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\data\runtime\RuntimeDataWriter;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\entity\projectile\Projectile; use pocketmine\entity\projectile\Projectile;
use pocketmine\inventory\Inventory;
use pocketmine\item\enchantment\AvailableEnchantmentRegistry; use pocketmine\item\enchantment\AvailableEnchantmentRegistry;
use pocketmine\item\enchantment\ItemEnchantmentTagRegistry; use pocketmine\item\enchantment\ItemEnchantmentTagRegistry;
use pocketmine\item\enchantment\ItemEnchantmentTags; 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 * 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\BrewingStandSlot;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Axis; use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
@ -114,19 +115,22 @@ class BrewingStand extends Transparent{
if($brewing->onUpdate()){ if($brewing->onUpdate()){
$world->scheduleDelayedBlockUpdate($this->position, 1); $world->scheduleDelayedBlockUpdate($this->position, 1);
} }
}
}
$changed = false; public function onContainerUpdate(Inventory $inventory) : void{
foreach(BrewingStandSlot::cases() as $slot){ $world = $this->position->getWorld();
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber()); $changed = false;
if($occupied !== $this->hasSlot($slot)){ foreach(BrewingStandSlot::cases() as $slot){
$this->setSlot($slot, $occupied); $occupied = !$inventory->isSlotEmpty($slot->getSlotNumber());
$changed = true; if($occupied !== $this->hasSlot($slot)){
} $this->setSlot($slot, $occupied);
$changed = true;
} }
}
if($changed){ if($changed){
$world->setBlock($this->position, $this); $world->setBlock($this->position, $this);
}
} }
} }
} }

View File

@ -38,6 +38,7 @@ use pocketmine\entity\projectile\SplashPotion;
use pocketmine\event\block\CampfireCookEvent; use pocketmine\event\block\CampfireCookEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\inventory\Inventory;
use pocketmine\item\Durable; use pocketmine\item\Durable;
use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item; use pocketmine\item\Item;
@ -67,8 +68,6 @@ class Campfire extends Transparent{
LightableTrait::describeBlockOnlyState as encodeLitState; 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 * @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. * has never been set in the world.
@ -250,7 +249,7 @@ class Campfire extends Transparent{
$furnaceType = $this->getFurnaceType(); $furnaceType = $this->getFurnaceType();
$maxCookDuration = $furnaceType->getCookDurationTicks(); $maxCookDuration = $furnaceType->getCookDurationTicks();
foreach($items as $slot => $item){ 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){ if($this->getCookingTime($slot) >= $maxCookDuration){
$result = $result =
($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ? ($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()); $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){ if(count($items) > 0){
$this->position->getWorld()->setBlock($this->position, $this); $this->position->getWorld()->setBlock($this->position, $this);
} }
if(mt_rand(1, 6) === 1){ if(mt_rand(1, 6) === 1){
$this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound()); $this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
} }
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS); $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
}else{
//make sure the visual state is updated when items are added
$this->position->getWorld()->setBlock($this->position, $this);
} }
} }
public function onContainerUpdate(Inventory $inventory) : void{
$this->position->getWorld()->setBlock($this->position, $this); //update visual state
}
private function extinguish() : void{ private function extinguish() : void{
$this->position->getWorld()->addSound($this->position, new FireExtinguishSound()); $this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(false)); $this->position->getWorld()->setBlock($this->position, $this->setLit(false));
@ -290,6 +295,6 @@ class Campfire extends Transparent{
private function ignite() : void{ private function ignite() : void{
$this->position->getWorld()->addSound($this->position, new FlintSteelSound()); $this->position->getWorld()->addSound($this->position, new FlintSteelSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(true)); $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\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\inventory\Inventory;
use pocketmine\item\Book; use pocketmine\item\Book;
use pocketmine\item\EnchantedBook; use pocketmine\item\EnchantedBook;
use pocketmine\item\Item; use pocketmine\item\Item;
@ -164,6 +165,20 @@ class ChiseledBookshelf extends Opaque{
return true; 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{ public function getDropsForCompatibleTool(Item $item) : array{
return []; return [];
} }

View File

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