Shift inventory management responsibility to World

this removes a bunch of problematic Position usages from Tile, as well as getting rid of a bunch of code duplication.
This commit is contained in:
Dylan K. Taylor 2024-12-06 16:14:41 +00:00
parent ce4d3aef9e
commit 40574be333
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
9 changed files with 48 additions and 66 deletions

View File

@ -276,6 +276,9 @@ class Campfire extends Transparent{
$this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
}
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
}else{
//make sure the visual state is updated when items are added
$this->position->getWorld()->setBlock($this->position, $this);
}
}

View File

@ -50,13 +50,6 @@ class Barrel extends Spawnable implements ContainerTile, Nameable{
$this->saveItems($nbt);
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
parent::close();
}
}
public function getInventory() : Inventory{
return $this->inventory;
}

View File

@ -27,7 +27,6 @@ use pocketmine\block\inventory\window\BrewingStandInventoryWindow;
use pocketmine\crafting\BrewingRecipe;
use pocketmine\event\block\BrewingFuelUseEvent;
use pocketmine\event\block\BrewItemEvent;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
@ -64,9 +63,6 @@ class BrewingStand extends Spawnable implements ContainerTile, Nameable{
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new SimpleInventory(5);
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(static function(Inventory $unused) use ($world, $pos) : void{
$world->scheduleDelayedBlockUpdate($pos, 1);
}));
}
public function readSaveData(CompoundTag $nbt) : void{
@ -105,14 +101,6 @@ class BrewingStand extends Spawnable implements ContainerTile, Nameable{
return "Brewing Stand";
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
parent::close();
}
}
public function getInventory() : Inventory{
return $this->inventory;
}

View File

@ -23,10 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\Campfire as BlockCampfire;
use pocketmine\block\inventory\CampfireInventory;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
@ -54,14 +51,6 @@ class Campfire extends Spawnable implements ContainerTile{
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new CampfireInventory();
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
static function(Inventory $unused) use ($world, $pos) : void{
$block = $world->getBlock($pos);
if($block instanceof BlockCampfire){
$world->setBlock($pos, $block);
}
})
);
}
public function getInventory() : CampfireInventory{

View File

@ -94,8 +94,6 @@ class Chest extends Spawnable implements ContainerTile, Nameable{
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
if($this->doubleInventory !== null){
if($this->isPaired() && $this->position->getWorld()->isChunkLoaded($this->pairX >> Chunk::COORD_BIT_SIZE, $this->pairZ >> Chunk::COORD_BIT_SIZE)){
$this->doubleInventory->removeAllViewers();

View File

@ -29,7 +29,6 @@ use pocketmine\crafting\FurnaceRecipe;
use pocketmine\crafting\FurnaceType;
use pocketmine\event\inventory\FurnaceBurnEvent;
use pocketmine\event\inventory\FurnaceSmeltEvent;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
@ -57,11 +56,6 @@ abstract class Furnace extends Spawnable implements ContainerTile, Nameable{
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new SimpleInventory(3);
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
static function(Inventory $unused) use ($world, $pos) : void{
$world->scheduleDelayedBlockUpdate($pos, 1);
})
);
}
public function readSaveData(CompoundTag $nbt) : void{
@ -79,10 +73,6 @@ abstract class Furnace extends Spawnable implements ContainerTile, Nameable{
$this->loadName($nbt);
$this->loadItems($nbt);
if($this->remainingFuelTime > 0){
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
@ -97,14 +87,6 @@ abstract class Furnace extends Spawnable implements ContainerTile, Nameable{
return "Furnace";
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
parent::close();
}
}
public function getInventory() : Inventory{
return $this->inventory;
}

View File

@ -58,14 +58,6 @@ class Hopper extends Spawnable implements ContainerTile, Nameable{
$nbt->setInt(self::TAG_TRANSFER_COOLDOWN, $this->transferCooldown);
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
parent::close();
}
}
public function getDefaultName() : string{
return "Hopper";
}

View File

@ -80,13 +80,6 @@ class ShulkerBox extends Spawnable implements ContainerTile, Nameable{
}
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
parent::close();
}
}
protected function onBlockDestroyedHook() : void{
//NOOP override of ContainerTrait - shulker boxes retain their contents when destroyed
}

View File

@ -30,6 +30,7 @@ use pocketmine\block\Air;
use pocketmine\block\Block;
use pocketmine\block\BlockTypeIds;
use pocketmine\block\RuntimeBlockStateRegistry;
use pocketmine\block\tile\ContainerTile;
use pocketmine\block\tile\Spawnable;
use pocketmine\block\tile\Tile;
use pocketmine\block\tile\TileFactory;
@ -57,6 +58,8 @@ use pocketmine\event\world\WorldDisplayNameChangeEvent;
use pocketmine\event\world\WorldParticleEvent;
use pocketmine\event\world\WorldSaveEvent;
use pocketmine\event\world\WorldSoundEvent;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\InventoryListener;
use pocketmine\item\Item;
use pocketmine\item\ItemUseResult;
use pocketmine\item\LegacyStringToItemParser;
@ -144,7 +147,7 @@ use const PHP_INT_MIN;
* @phpstan-type BlockPosHash int
* @phpstan-type ChunkBlockPosHash int
*/
class World implements ChunkManager{
class World implements ChunkManager, InventoryListener{
private static int $worldIdCounter = 1;
@ -282,6 +285,12 @@ class World implements ChunkManager{
*/
private array $chunks = [];
/**
* @var Vector3[]|\WeakMap
* @phpstan-var \WeakMap<Inventory, Vector3>
*/
private \WeakMap $containerToBlockPositionMap;
/**
* @var Vector3[][] chunkHash => [relativeBlockHash => Vector3]
* @phpstan-var array<ChunkPosHash, array<ChunkBlockPosHash, Vector3>>
@ -506,6 +515,8 @@ class World implements ChunkManager{
}
});
$this->containerToBlockPositionMap = new \WeakMap();
$this->scheduledBlockUpdateQueue = new ReversePriorityQueue();
$this->scheduledBlockUpdateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
@ -2787,6 +2798,10 @@ class World implements ChunkManager{
//delegate tile ticking to the corresponding block
$this->scheduleDelayedBlockUpdate($pos->asVector3(), 1);
if($tile instanceof ContainerTile){
$tile->getInventory()->getListeners()->add($this);
$this->containerToBlockPositionMap[$tile->getInventory()] = $pos->asVector3();
}
}
/**
@ -2805,11 +2820,40 @@ class World implements ChunkManager{
if(isset($this->chunks[$hash = World::chunkHash($chunkX, $chunkZ)])){
$this->chunks[$hash]->removeTile($tile);
}
if($tile instanceof ContainerTile){
$inventory = $tile->getInventory();
$inventory->removeAllViewers();
$inventory->getListeners()->remove($this);
unset($this->containerToBlockPositionMap[$inventory]);
}
foreach($this->getChunkListeners($chunkX, $chunkZ) as $listener){
$listener->onBlockChanged($pos->asVector3());
}
}
private function notifyInventoryUpdate(Inventory $inventory) : void{
$blockPosition = $this->containerToBlockPositionMap[$inventory] ?? null;
if($blockPosition !== null){
$this->scheduleDelayedBlockUpdate($blockPosition, 1);
}
}
/**
* @internal
* @see InventoryListener
*/
public function onSlotChange(Inventory $inventory, int $slot, Item $oldItem) : void{
$this->notifyInventoryUpdate($inventory);
}
/**
* @internal
* @see InventoryListener
*/
public function onContentChange(Inventory $inventory, array $oldContents) : void{
$this->notifyInventoryUpdate($inventory);
}
public function isChunkInUse(int $x, int $z) : bool{
return isset($this->chunkLoaders[$index = World::chunkHash($x, $z)]) && count($this->chunkLoaders[$index]) > 0;
}