Allow blocks to respond to the contents of their containers being updated

turns out relying on scheduled updates for this was a bad idea, since it causes a lot of unnecessary code to run every tick, as well as being problematic for campfire, which doesn't have any blockstates to compare against.
This commit is contained in:
Dylan K. Taylor 2024-12-06 17:46:11 +00:00
parent 40574be333
commit 4850bd5538
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
5 changed files with 53 additions and 18 deletions

View File

@ -36,6 +36,7 @@ use pocketmine\data\runtime\RuntimeDataSizeCalculator;
use pocketmine\data\runtime\RuntimeDataWriter;
use pocketmine\entity\Entity;
use pocketmine\entity\projectile\Projectile;
use pocketmine\inventory\Inventory;
use pocketmine\item\enchantment\AvailableEnchantmentRegistry;
use pocketmine\item\enchantment\ItemEnchantmentTagRegistry;
use pocketmine\item\enchantment\ItemEnchantmentTags;
@ -515,6 +516,15 @@ class Block{
}
/**
* Called by the World when a change is detected in a container's inventory at the block's position.
* Use this to do visual updates on the block if needed.
* Don't do any expensive logic in here. It will be called every time a slot of the inventory changes.
*/
public function onContainerUpdate(Inventory $inventory) : void{
}
/**
* Do actions when interacted by Item. Returns if it has done anything
*

View File

@ -28,6 +28,7 @@ use pocketmine\block\tile\BrewingStand as TileBrewingStand;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
@ -114,19 +115,22 @@ class BrewingStand extends Transparent{
if($brewing->onUpdate()){
$world->scheduleDelayedBlockUpdate($this->position, 1);
}
}
}
$changed = false;
foreach(BrewingStandSlot::cases() as $slot){
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
if($occupied !== $this->hasSlot($slot)){
$this->setSlot($slot, $occupied);
$changed = true;
}
public function onContainerUpdate(Inventory $inventory) : void{
$world = $this->position->getWorld();
$changed = false;
foreach(BrewingStandSlot::cases() as $slot){
$occupied = !$inventory->isSlotEmpty($slot->getSlotNumber());
if($occupied !== $this->hasSlot($slot)){
$this->setSlot($slot, $occupied);
$changed = true;
}
}
if($changed){
$world->setBlock($this->position, $this);
}
if($changed){
$world->setBlock($this->position, $this);
}
}
}

View File

@ -38,6 +38,7 @@ use pocketmine\entity\projectile\SplashPotion;
use pocketmine\event\block\CampfireCookEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\inventory\Inventory;
use pocketmine\item\Durable;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
@ -67,8 +68,6 @@ class Campfire extends Transparent{
LightableTrait::describeBlockOnlyState as encodeLitState;
}
private const UPDATE_INTERVAL_TICKS = 10;
/**
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
* has never been set in the world.
@ -250,7 +249,7 @@ class Campfire extends Transparent{
$furnaceType = $this->getFurnaceType();
$maxCookDuration = $furnaceType->getCookDurationTicks();
foreach($items as $slot => $item){
$this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + self::UPDATE_INTERVAL_TICKS));
$this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + 1));
if($this->getCookingTime($slot) >= $maxCookDuration){
$result =
($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ?
@ -269,19 +268,25 @@ class Campfire extends Transparent{
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult());
}
}
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileCampfire){
//TODO: we probably need to rethink how these are tracked
$tile->setCookingTimes($this->cookingTimes);
}
if(count($items) > 0){
$this->position->getWorld()->setBlock($this->position, $this);
}
if(mt_rand(1, 6) === 1){
$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);
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
}
}
public function onContainerUpdate(Inventory $inventory) : void{
$this->position->getWorld()->setBlock($this->position, $this); //update visual state
}
private function extinguish() : void{
$this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(false));
@ -290,6 +295,6 @@ class Campfire extends Transparent{
private function ignite() : void{
$this->position->getWorld()->addSound($this->position, new FlintSteelSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(true));
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
}
}

View File

@ -28,6 +28,7 @@ use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\inventory\Inventory;
use pocketmine\item\Book;
use pocketmine\item\EnchantedBook;
use pocketmine\item\Item;
@ -164,6 +165,20 @@ class ChiseledBookshelf extends Opaque{
return true;
}
public function onContainerUpdate(Inventory $inventory) : void{
$changed = false;
foreach(ChiseledBookshelfSlot::cases() as $case){
$hasItem = !$inventory->isSlotEmpty($case->value);
if($this->hasSlot($case) !== $hasItem){
$this->setSlot($case, $hasItem);
$changed = true;
}
}
if($changed){
$this->position->getWorld()->setBlock($this->position, $this);
}
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}

View File

@ -2834,6 +2834,7 @@ class World implements ChunkManager, InventoryListener{
private function notifyInventoryUpdate(Inventory $inventory) : void{
$blockPosition = $this->containerToBlockPositionMap[$inventory] ?? null;
if($blockPosition !== null){
$this->getBlock($blockPosition)->onContainerUpdate($inventory);
$this->scheduleDelayedBlockUpdate($blockPosition, 1);
}
}