mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 08:17:34 +00:00
Merge branch 'stable' into next-minor
This commit is contained in:
commit
a9f2766a8b
@ -55,6 +55,25 @@ abstract class BaseInventory implements Inventory{
|
||||
return $this->maxStackSize;
|
||||
}
|
||||
|
||||
public function setMaxStackSize(int $size) : void{
|
||||
$this->maxStackSize = $size;
|
||||
}
|
||||
|
||||
abstract protected function internalSetItem(int $index, Item $item) : void;
|
||||
|
||||
public function setItem(int $index, Item $item) : void{
|
||||
if($item->isNull()){
|
||||
$item = VanillaItems::AIR();
|
||||
}else{
|
||||
$item = clone $item;
|
||||
}
|
||||
|
||||
$oldItem = $this->getItem($index);
|
||||
|
||||
$this->internalSetItem($index, $item);
|
||||
$this->onSlotChange($index, $oldItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
*/
|
||||
@ -85,21 +104,6 @@ abstract class BaseInventory implements Inventory{
|
||||
$this->onContentChange($oldContents);
|
||||
}
|
||||
|
||||
abstract protected function internalSetItem(int $index, Item $item) : void;
|
||||
|
||||
public function setItem(int $index, Item $item) : void{
|
||||
if($item->isNull()){
|
||||
$item = VanillaItems::AIR();
|
||||
}else{
|
||||
$item = clone $item;
|
||||
}
|
||||
|
||||
$oldItem = $this->getItem($index);
|
||||
|
||||
$this->internalSetItem($index, $item);
|
||||
$this->onSlotChange($index, $oldItem);
|
||||
}
|
||||
|
||||
public function contains(Item $item) : bool{
|
||||
$count = max(1, $item->getCount());
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
@ -128,18 +132,6 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
return $slots;
|
||||
}
|
||||
|
||||
public function remove(Item $item) : void{
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
$checkTags = $item->hasNamedTag();
|
||||
|
||||
foreach($this->getContents() as $index => $i){
|
||||
if($item->equals($i, $checkDamage, $checkTags)){
|
||||
$this->clear($index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function first(Item $item, bool $exact = false) : int{
|
||||
$count = $exact ? $item->getCount() : max(1, $item->getCount());
|
||||
$checkDamage = $exact || !$item->hasAnyDamageValue();
|
||||
@ -253,6 +245,17 @@ abstract class BaseInventory implements Inventory{
|
||||
return $slot;
|
||||
}
|
||||
|
||||
public function remove(Item $item) : void{
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
$checkTags = $item->hasNamedTag();
|
||||
|
||||
foreach($this->getContents() as $index => $i){
|
||||
if($item->equals($i, $checkDamage, $checkTags)){
|
||||
$this->clear($index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function removeItem(Item ...$slots) : array{
|
||||
/** @var Item[] $itemSlots */
|
||||
/** @var Item[] $slots */
|
||||
@ -323,10 +326,6 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
}
|
||||
|
||||
public function setMaxStackSize(int $size) : void{
|
||||
$this->maxStackSize = $size;
|
||||
}
|
||||
|
||||
public function onOpen(Player $who) : void{
|
||||
$this->viewers[spl_object_id($who)] = $who;
|
||||
}
|
||||
|
@ -46,6 +46,16 @@ interface Inventory{
|
||||
*/
|
||||
public function setItem(int $index, Item $item) : void;
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getContents(bool $includeEmpty = false) : array;
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
*/
|
||||
public function setContents(array $items) : void;
|
||||
|
||||
/**
|
||||
* Stores the given Items in the inventory. This will try to fill
|
||||
* existing stacks and empty slots as well as it can.
|
||||
@ -66,24 +76,6 @@ interface Inventory{
|
||||
*/
|
||||
public function getAddableItemQuantity(Item $item) : int;
|
||||
|
||||
/**
|
||||
* Removes the given Item from the inventory.
|
||||
* It will return the Items that couldn't be removed.
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function removeItem(Item ...$slots) : array;
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getContents(bool $includeEmpty = false) : array;
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
*/
|
||||
public function setContents(array $items) : void;
|
||||
|
||||
/**
|
||||
* Checks if the inventory contains any Item with the same material data.
|
||||
* It will check id, amount, and metadata (if not null)
|
||||
@ -121,6 +113,14 @@ interface Inventory{
|
||||
*/
|
||||
public function remove(Item $item) : void;
|
||||
|
||||
/**
|
||||
* Removes the given Item from the inventory.
|
||||
* It will return the Items that couldn't be removed.
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function removeItem(Item ...$slots) : array;
|
||||
|
||||
/**
|
||||
* Will clear a specific slot
|
||||
*/
|
||||
|
@ -52,6 +52,10 @@ class SimpleInventory extends BaseInventory{
|
||||
return $this->slots[$index] !== null ? clone $this->slots[$index] : VanillaItems::AIR();
|
||||
}
|
||||
|
||||
protected function internalSetItem(int $index, Item $item) : void{
|
||||
$this->slots[$index] = $item->isNull() ? null : $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
@ -78,8 +82,4 @@ class SimpleInventory extends BaseInventory{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function internalSetItem(int $index, Item $item) : void{
|
||||
$this->slots[$index] = $item->isNull() ? null : $item;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,9 @@ use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use function array_map;
|
||||
use function array_search;
|
||||
use function get_class;
|
||||
use function max;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* @phpstan-type ContainerOpenClosure \Closure(int $id, Inventory $inventory) : (list<ClientboundPacket>|null)
|
||||
@ -304,6 +306,28 @@ class InventoryManager{
|
||||
}
|
||||
}
|
||||
|
||||
public function syncMismatchedPredictedSlotChanges() : void{
|
||||
foreach($this->initiatedSlotChanges as $windowId => $slots){
|
||||
if(!isset($this->windowMap[$windowId])){
|
||||
continue;
|
||||
}
|
||||
$inventory = $this->windowMap[$windowId];
|
||||
|
||||
foreach($slots as $slot => $expectedItem){
|
||||
if(!$inventory->slotExists($slot)){
|
||||
continue; //TODO: size desync ???
|
||||
}
|
||||
$actualItem = $inventory->getItem($slot);
|
||||
if(!$actualItem->equalsExact($expectedItem)){
|
||||
$this->session->getLogger()->debug("Detected prediction mismatch in inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " slot $slot");
|
||||
$this->syncSlot($inventory, $slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->initiatedSlotChanges = [];
|
||||
}
|
||||
|
||||
public function syncData(Inventory $inventory, int $propertyId, int $value) : void{
|
||||
$windowId = $this->getWindowId($inventory);
|
||||
if($windowId !== null){
|
||||
|
@ -234,9 +234,13 @@ class InGamePacketHandler extends PacketHandler{
|
||||
$packetHandled = true;
|
||||
|
||||
$useItemTransaction = $packet->getItemInteractionData();
|
||||
if($useItemTransaction !== null && !$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
|
||||
$packetHandled = false;
|
||||
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
|
||||
if($useItemTransaction !== null){
|
||||
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
|
||||
$packetHandled = false;
|
||||
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
|
||||
}else{
|
||||
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
|
||||
}
|
||||
}
|
||||
|
||||
$blockActions = $packet->getBlockActions();
|
||||
@ -304,6 +308,8 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
if(!$result){
|
||||
$this->inventoryManager->syncAll();
|
||||
}else{
|
||||
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
@ -313,6 +313,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->setNameTag($this->username);
|
||||
}
|
||||
|
||||
private function callDummyItemHeldEvent() : void{
|
||||
$slot = $this->inventory->getHeldItemIndex();
|
||||
|
||||
$event = new PlayerItemHeldEvent($this, $this->inventory->getItem($slot), $slot);
|
||||
$event->call();
|
||||
//TODO: this event is actually cancellable, but cancelling it here has no meaningful result, so we
|
||||
//just ignore it. We fire this only because the content of the held slot changed, not because the
|
||||
//held slot index changed. We can't prevent that from here, and nor would it be sensible to.
|
||||
}
|
||||
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
parent::initEntity($nbt);
|
||||
$this->addDefaultWindows();
|
||||
@ -321,10 +331,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
function(Inventory $unused, int $slot) : void{
|
||||
if($slot === $this->inventory->getHeldItemIndex()){
|
||||
$this->setUsingItem(false);
|
||||
|
||||
$this->callDummyItemHeldEvent();
|
||||
}
|
||||
},
|
||||
function() : void{
|
||||
function(Inventory $unused, array $changedSlots) : void{
|
||||
$this->setUsingItem(false);
|
||||
$heldSlot = $this->inventory->getHeldItemIndex();
|
||||
if(isset($changedSlots[$heldSlot])){
|
||||
$this->callDummyItemHeldEvent();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user