47    public const TAG_BURN_TIME = 
"BurnTime";
 
   48    public const TAG_COOK_TIME = 
"CookTime";
 
   49    public const TAG_MAX_TIME = 
"MaxTime";
 
   52    private int $remainingFuelTime = 0;
 
   53    private int $cookTime = 0;
 
   54    private int $maxFuelTime = 0;
 
   57        parent::__construct($world, $pos);
 
   58        $this->inventory = 
new FurnaceInventory($this->position, $this->getFurnaceType());
 
   59        $this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
 
   60            static function(
Inventory $unused) use ($world, $pos) : 
void{
 
   66    public function readSaveData(
CompoundTag $nbt) : 
void{
 
   67        $this->remainingFuelTime = max(0, $nbt->getShort(self::TAG_BURN_TIME, $this->remainingFuelTime));
 
   69        $this->cookTime = $nbt->getShort(self::TAG_COOK_TIME, $this->cookTime);
 
   70        if($this->remainingFuelTime === 0){
 
   74        $this->maxFuelTime = $nbt->getShort(self::TAG_MAX_TIME, $this->maxFuelTime);
 
   75        if($this->maxFuelTime === 0){
 
   76            $this->maxFuelTime = $this->remainingFuelTime;
 
   79        $this->loadName($nbt);
 
   80        $this->loadItems($nbt);
 
   82        if($this->remainingFuelTime > 0){
 
   83            $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
 
   88        $nbt->setShort(self::TAG_BURN_TIME, $this->remainingFuelTime);
 
   89        $nbt->
setShort(self::TAG_COOK_TIME, $this->cookTime);
 
   90        $nbt->
setShort(self::TAG_MAX_TIME, $this->maxFuelTime);
 
   91        $this->saveName($nbt);
 
   92        $this->saveItems($nbt);
 
 
   95    public function getDefaultName() : string{
 
   99    public function close() : void{
 
  101            $this->inventory->removeAllViewers();
 
  107    public function getInventory() : FurnaceInventory{
 
  108        return $this->inventory;
 
  111    public function getRealInventory() : FurnaceInventory{
 
  112        return $this->getInventory();
 
  115    protected function checkFuel(Item $fuel) : void{
 
  116        $ev = new FurnaceBurnEvent($this, $fuel, $fuel->getFuelTime());
 
  118        if($ev->isCancelled()){
 
  122        $this->maxFuelTime = $this->remainingFuelTime = $ev->getBurnTime();
 
  123        $this->onStartSmelting();
 
  125        if($this->remainingFuelTime > 0 && $ev->isBurning()){
 
  126            $this->inventory->setFuel($fuel->getFuelResidue());
 
  130    protected function onStartSmelting() : void{
 
  131        $block = $this->getBlock();
 
  132        if($block instanceof BlockFurnace && !$block->isLit()){
 
  133            $block->setLit(true);
 
  134            $this->position->getWorld()->setBlock($block->getPosition(), $block);
 
  138    protected function onStopSmelting() : void{
 
  139        $block = $this->getBlock();
 
  140        if($block instanceof BlockFurnace && $block->isLit()){
 
  141            $block->setLit(false);
 
  142            $this->position->getWorld()->setBlock($block->getPosition(), $block);
 
  146    abstract public function getFurnaceType() : FurnaceType;
 
  148    public function onUpdate() : bool{
 
  154        $this->timings->startTiming();
 
  156        $prevCookTime = $this->cookTime;
 
  157        $prevRemainingFuelTime = $this->remainingFuelTime;
 
  158        $prevMaxFuelTime = $this->maxFuelTime;
 
  162        $fuel = $this->inventory->getFuel();
 
  163        $raw = $this->inventory->getSmelting();
 
  164        $product = $this->inventory->getResult();
 
  166        $furnaceType = $this->getFurnaceType();
 
  167        $smelt = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($raw);
 
  168        $canSmelt = ($smelt instanceof FurnaceRecipe && $raw->getCount() > 0 && (($smelt->getResult()->canStackWith($product) && $product->getCount() < $product->getMaxStackSize()) || $product->isNull()));
 
  170        if($this->remainingFuelTime <= 0 && $canSmelt && $fuel->getFuelTime() > 0 && $fuel->getCount() > 0){
 
  171            $this->checkFuel($fuel);
 
  174        if($this->remainingFuelTime > 0){
 
  175            --$this->remainingFuelTime;
 
  177            if($smelt instanceof FurnaceRecipe && $canSmelt){
 
  180                if($this->cookTime >= $furnaceType->getCookDurationTicks()){
 
  181                    $product = $smelt->getResult()->setCount($product->getCount() + 1);
 
  183                    $ev = 
new FurnaceSmeltEvent($this, $raw, $product);
 
  186                    if(!$ev->isCancelled()){
 
  187                        $this->inventory->setResult($ev->getResult());
 
  189                        $this->inventory->setSmelting($raw);
 
  192                    $this->cookTime -= $furnaceType->getCookDurationTicks();
 
  194            }elseif($this->remainingFuelTime <= 0){
 
  195                $this->remainingFuelTime = $this->cookTime = $this->maxFuelTime = 0;
 
  201            $this->onStopSmelting();
 
  202            $this->remainingFuelTime = $this->cookTime = $this->maxFuelTime = 0;
 
  205        $viewers = array_map(fn(Player $p) => $p->getNetworkSession()->getInvManager(), $this->inventory->getViewers());
 
  206        foreach($viewers as $v){
 
  210            if($prevCookTime !== $this->cookTime){
 
  211                $v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_SMELT_PROGRESS, $this->cookTime);
 
  213            if($prevRemainingFuelTime !== $this->remainingFuelTime){
 
  214                $v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_REMAINING_FUEL_TIME, $this->remainingFuelTime);
 
  216            if($prevMaxFuelTime !== $this->maxFuelTime){
 
  217                $v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_MAX_FUEL_TIME, $this->maxFuelTime);
 
  221        $this->timings->stopTiming();