mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-01 07:39:57 +00:00
Buffer slot and content syncing until the end of the tick
we may receive multiple requests in one tick (e.g. crafting in a batch)
This commit is contained in:
parent
e8085e22a0
commit
ca6d51498f
@ -59,8 +59,11 @@ use pocketmine\network\PacketHandlingException;
|
|||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
use pocketmine\utils\ObjectSet;
|
use pocketmine\utils\ObjectSet;
|
||||||
|
use function array_keys;
|
||||||
use function array_search;
|
use function array_search;
|
||||||
|
use function count;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
|
use function implode;
|
||||||
use function is_int;
|
use function is_int;
|
||||||
use function max;
|
use function max;
|
||||||
use function spl_object_id;
|
use function spl_object_id;
|
||||||
@ -100,6 +103,8 @@ class InventoryManager{
|
|||||||
private int $nextItemStackId = 1;
|
private int $nextItemStackId = 1;
|
||||||
private ?int $currentItemStackRequestId = null;
|
private ?int $currentItemStackRequestId = null;
|
||||||
|
|
||||||
|
private bool $fullSyncRequested = false;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Player $player,
|
private Player $player,
|
||||||
private NetworkSession $session
|
private NetworkSession $session
|
||||||
@ -400,7 +405,7 @@ class InventoryManager{
|
|||||||
if($clientSideItem === null || !$clientSideItem->equals($currentItem)){
|
if($clientSideItem === null || !$clientSideItem->equals($currentItem)){
|
||||||
//no prediction or incorrect - do not associate this with the currently active itemstack request
|
//no prediction or incorrect - do not associate this with the currently active itemstack request
|
||||||
$this->trackItemStack($inventory, $slot, $currentItem, null);
|
$this->trackItemStack($inventory, $slot, $currentItem, null);
|
||||||
$this->syncSlot($inventory, $slot);
|
$inventoryEntry->pendingSyncs[$slot] = $slot;
|
||||||
}else{
|
}else{
|
||||||
//correctly predicted - associate the change with the currently active itemstack request
|
//correctly predicted - associate the change with the currently active itemstack request
|
||||||
$this->trackItemStack($inventory, $slot, $currentItem, $this->currentItemStackRequestId);
|
$this->trackItemStack($inventory, $slot, $currentItem, $this->currentItemStackRequestId);
|
||||||
@ -452,7 +457,7 @@ class InventoryManager{
|
|||||||
}
|
}
|
||||||
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper));
|
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper));
|
||||||
}
|
}
|
||||||
unset($entry->predictions[$slot]);
|
unset($entry->predictions[$slot], $entry->pendingSyncs[$slot]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncContents(Inventory $inventory) : void{
|
public function syncContents(Inventory $inventory) : void{
|
||||||
@ -464,6 +469,7 @@ class InventoryManager{
|
|||||||
}
|
}
|
||||||
if($windowId !== null){
|
if($windowId !== null){
|
||||||
$entry->predictions = [];
|
$entry->predictions = [];
|
||||||
|
$entry->pendingSyncs = [];
|
||||||
$contents = [];
|
$contents = [];
|
||||||
foreach($inventory->getContents(true) as $slot => $item){
|
foreach($inventory->getContents(true) as $slot => $item){
|
||||||
$itemStack = TypeConverter::getInstance()->coreItemStackToNet($item);
|
$itemStack = TypeConverter::getInstance()->coreItemStackToNet($item);
|
||||||
@ -494,6 +500,10 @@ class InventoryManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requestSyncAll() : void{
|
||||||
|
$this->fullSyncRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
public function syncMismatchedPredictedSlotChanges() : void{
|
public function syncMismatchedPredictedSlotChanges() : void{
|
||||||
foreach($this->inventories as $entry){
|
foreach($this->inventories as $entry){
|
||||||
$inventory = $entry->inventory;
|
$inventory = $entry->inventory;
|
||||||
@ -504,13 +514,33 @@ class InventoryManager{
|
|||||||
|
|
||||||
//any prediction that still exists at this point is a slot that was predicted to change but didn't
|
//any prediction that still exists at this point is a slot that was predicted to change but didn't
|
||||||
$this->session->getLogger()->debug("Detected prediction mismatch in inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " slot $slot");
|
$this->session->getLogger()->debug("Detected prediction mismatch in inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " slot $slot");
|
||||||
$this->syncSlot($inventory, $slot);
|
$entry->pendingSyncs[$slot] = $slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
$entry->predictions = [];
|
$entry->predictions = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function flushPendingUpdates() : void{
|
||||||
|
if($this->fullSyncRequested){
|
||||||
|
$this->fullSyncRequested = false;
|
||||||
|
$this->session->getLogger()->debug("Full inventory sync requested, sending contents of " . count($this->inventories) . " inventories");
|
||||||
|
$this->syncAll();
|
||||||
|
}else{
|
||||||
|
foreach($this->inventories as $entry){
|
||||||
|
if(count($entry->pendingSyncs) === 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$inventory = $entry->inventory;
|
||||||
|
$this->session->getLogger()->debug("Syncing slots " . implode(", ", array_keys($entry->pendingSyncs)) . " in inventory " . get_class($inventory) . "#" . spl_object_id($inventory));
|
||||||
|
foreach($entry->pendingSyncs as $slot){
|
||||||
|
$this->syncSlot($inventory, $slot);
|
||||||
|
}
|
||||||
|
$entry->pendingSyncs = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function syncData(Inventory $inventory, int $propertyId, int $value) : void{
|
public function syncData(Inventory $inventory, int $propertyId, int $value) : void{
|
||||||
$windowId = $this->getWindowId($inventory);
|
$windowId = $this->getWindowId($inventory);
|
||||||
if($windowId !== null){
|
if($windowId !== null){
|
||||||
|
@ -39,6 +39,12 @@ final class InventoryManagerEntry{
|
|||||||
*/
|
*/
|
||||||
public array $itemStackInfos = [];
|
public array $itemStackInfos = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int[]
|
||||||
|
* @phpstan-var array<int, int>
|
||||||
|
*/
|
||||||
|
public array $pendingSyncs = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public Inventory $inventory,
|
public Inventory $inventory,
|
||||||
public ?ComplexInventoryMapEntry $complexSlotMap = null
|
public ?ComplexInventoryMapEntry $complexSlotMap = null
|
||||||
|
@ -1219,6 +1219,7 @@ class NetworkSession{
|
|||||||
$attribute->markSynchronized();
|
$attribute->markSynchronized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->invManager?->flushPendingUpdates();
|
||||||
|
|
||||||
$this->flushSendBuffer();
|
$this->flushSendBuffer();
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,8 @@ use pocketmine\event\player\PlayerEditBookEvent;
|
|||||||
use pocketmine\inventory\transaction\action\DropItemAction;
|
use pocketmine\inventory\transaction\action\DropItemAction;
|
||||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||||
use pocketmine\inventory\transaction\TransactionBuilder;
|
use pocketmine\inventory\transaction\TransactionBuilder;
|
||||||
use pocketmine\inventory\transaction\TransactionException;
|
use pocketmine\inventory\transaction\TransactionCancelledException;
|
||||||
|
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
use pocketmine\item\WritableBook;
|
use pocketmine\item\WritableBook;
|
||||||
use pocketmine\item\WritableBookPage;
|
use pocketmine\item\WritableBookPage;
|
||||||
@ -335,7 +336,7 @@ class InGamePacketHandler extends PacketHandler{
|
|||||||
$result = $this->handleNormalTransaction($packet->trData, $packet->requestId);
|
$result = $this->handleNormalTransaction($packet->trData, $packet->requestId);
|
||||||
}elseif($packet->trData instanceof MismatchTransactionData){
|
}elseif($packet->trData instanceof MismatchTransactionData){
|
||||||
$this->session->getLogger()->debug("Mismatch transaction received");
|
$this->session->getLogger()->debug("Mismatch transaction received");
|
||||||
$this->inventoryManager->syncAll();
|
$this->inventoryManager->requestSyncAll();
|
||||||
$result = true;
|
$result = true;
|
||||||
}elseif($packet->trData instanceof UseItemTransactionData){
|
}elseif($packet->trData instanceof UseItemTransactionData){
|
||||||
$result = $this->handleUseItemTransaction($packet->trData);
|
$result = $this->handleUseItemTransaction($packet->trData);
|
||||||
@ -357,9 +358,14 @@ class InGamePacketHandler extends PacketHandler{
|
|||||||
$this->inventoryManager->addTransactionPredictedSlotChanges($transaction);
|
$this->inventoryManager->addTransactionPredictedSlotChanges($transaction);
|
||||||
try{
|
try{
|
||||||
$transaction->execute();
|
$transaction->execute();
|
||||||
}catch(TransactionException $e){
|
}catch(TransactionValidationException $e){
|
||||||
|
$this->inventoryManager->requestSyncAll();
|
||||||
$logger = $this->session->getLogger();
|
$logger = $this->session->getLogger();
|
||||||
$logger->debug("Failed to execute inventory transaction: " . $e->getMessage());
|
$logger->debug("Invalid inventory transaction $requestId: " . $e->getMessage());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}catch(TransactionCancelledException){
|
||||||
|
$this->session->getLogger()->debug("Inventory transaction $requestId cancelled by a plugin");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}finally{
|
}finally{
|
||||||
@ -538,6 +544,7 @@ class InGamePacketHandler extends PacketHandler{
|
|||||||
$result = false;
|
$result = false;
|
||||||
$this->session->getLogger()->debug("ItemStackRequest #" . $request->getRequestId() . " failed: " . $e->getMessage());
|
$this->session->getLogger()->debug("ItemStackRequest #" . $request->getRequestId() . " failed: " . $e->getMessage());
|
||||||
$this->session->getLogger()->debug(implode("\n", Utils::printableExceptionInfo($e)));
|
$this->session->getLogger()->debug(implode("\n", Utils::printableExceptionInfo($e)));
|
||||||
|
$this->inventoryManager->requestSyncAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$result){
|
if(!$result){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user