diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 4479faee103..380d080c5d8 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -52,6 +52,9 @@ public function getCandle() : Candle{ } public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($this->lit && $face !== Facing::UP){ + return true; + } if($this->onInteractCandle($item, $face, $clickVector, $player, $returnedItems)){ return true; } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 7803cea5c71..8240bf06346 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -266,6 +266,7 @@ private function register1to1ItemMappings() : void{ $this->map1to1Item(Ids::HONEY_BOTTLE, Items::HONEY_BOTTLE()); $this->map1to1Item(Ids::HONEYCOMB, Items::HONEYCOMB()); $this->map1to1Item(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE()); + $this->map1to1Item(Ids::ICE_BOMB, Items::ICE_BOMB()); $this->map1to1Item(Ids::INK_SAC, Items::INK_SAC()); $this->map1to1Item(Ids::IRON_AXE, Items::IRON_AXE()); $this->map1to1Item(Ids::IRON_BOOTS, Items::IRON_BOOTS()); diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 3d53233ab71..03d9c03e6be 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -43,6 +43,7 @@ use pocketmine\entity\projectile\Egg; use pocketmine\entity\projectile\EnderPearl; use pocketmine\entity\projectile\ExperienceBottle; +use pocketmine\entity\projectile\IceBomb; use pocketmine\entity\projectile\Snowball; use pocketmine\entity\projectile\SplashPotion; use pocketmine\item\Item; @@ -120,6 +121,10 @@ public function __construct(){ return new FallingBlock(Helper::parseLocation($nbt, $world), FallingBlock::parseBlockNBT(RuntimeBlockStateRegistry::getInstance(), $nbt), $nbt); }, ['FallingSand', 'minecraft:falling_block']); + $this->register(IceBomb::class, function(World $world, CompoundTag $nbt) : IceBomb{ + return new IceBomb(Helper::parseLocation($nbt, $world), null, $nbt); + }, ['minecraft:ice_bomb']); + $this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{ $itemTag = $nbt->getCompoundTag(ItemEntity::TAG_ITEM); if($itemTag === null){ diff --git a/src/entity/projectile/IceBomb.php b/src/entity/projectile/IceBomb.php new file mode 100644 index 00000000000..69a5d46a2c1 --- /dev/null +++ b/src/entity/projectile/IceBomb.php @@ -0,0 +1,86 @@ +getTypeId() === BlockTypeIds::WATER){ + $pos = $block->getPosition(); + + return AxisAlignedBB::one()->offset($pos->x, $pos->y, $pos->z)->calculateIntercept($start, $end); + } + + return parent::calculateInterceptWithBlock($block, $start, $end); + } + + protected function onHit(ProjectileHitEvent $event) : void{ + $world = $this->getWorld(); + $pos = $this->location; + + $world->addSound($pos, new IceBombHitSound()); + $itemBreakParticle = new ItemBreakParticle(VanillaItems::ICE_BOMB()); + for($i = 0; $i < 6; ++$i){ + $world->addParticle($pos, $itemBreakParticle); + } + } + + protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{ + parent::onHitBlock($blockHit, $hitResult); + + $pos = $blockHit->getPosition(); + $world = $pos->getWorld(); + $posX = $pos->getFloorX(); + $posY = $pos->getFloorY(); + $posZ = $pos->getFloorZ(); + + $ice = VanillaBlocks::ICE(); + for($x = $posX - 1; $x <= $posX + 1; $x++){ + for($y = $posY - 1; $y <= $posY + 1; $y++){ + for($z = $posZ - 1; $z <= $posZ + 1; $z++){ + if($world->getBlockAt($x, $y, $z)->getTypeId() === BlockTypeIds::WATER){ + $world->setBlockAt($x, $y, $z, $ice); + } + } + } + } + } +} diff --git a/src/item/IceBomb.php b/src/item/IceBomb.php new file mode 100644 index 00000000000..fbc9f24a2f6 --- /dev/null +++ b/src/item/IceBomb.php @@ -0,0 +1,48 @@ +register("honey_bottle", fn() => Items::HONEY_BOTTLE()); $result->register("host_armor_trim_smithing_template", fn() => Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("honeycomb", fn() => Items::HONEYCOMB()); + $result->register("ice_bomb", fn() => Items::ICE_BOMB()); $result->register("ink_sac", fn() => Items::INK_SAC()); $result->register("iron_axe", fn() => Items::IRON_AXE()); $result->register("iron_boots", fn() => Items::IRON_BOOTS()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 5899b63576f..dcf59daf6b1 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -192,6 +192,7 @@ * @method static Item HONEYCOMB() * @method static HoneyBottle HONEY_BOTTLE() * @method static Item HOST_ARMOR_TRIM_SMITHING_TEMPLATE() + * @method static IceBomb ICE_BOMB() * @method static Item INK_SAC() * @method static Axe IRON_AXE() * @method static Armor IRON_BOOTS() @@ -504,6 +505,7 @@ protected static function setup() : void{ self::register("heart_of_the_sea", fn(IID $id) => new Item($id, "Heart of the Sea")); self::register("honey_bottle", fn(IID $id) => new HoneyBottle($id, "Honey Bottle")); self::register("honeycomb", fn(IID $id) => new Item($id, "Honeycomb")); + self::register("ice_bomb", fn(IID $id) => new IceBomb($id, "Ice Bomb")); self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac")); self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot")); self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget")); diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index e4c303121f7..70c427aa175 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -592,7 +592,6 @@ public function syncContents(Inventory $inventory) : void{ $info = $this->trackItemStack($entry, $slot, $itemStack, null); $contents[] = new ItemStackWrapper($info->getStackId(), $itemStack); } - $clearSlotWrapper = new ItemStackWrapper(0, ItemStack::null()); if($entry->complexSlotMap !== null){ foreach($contents as $slotId => $info){ $packetSlot = $entry->complexSlotMap->mapCoreToNet($slotId) ?? null; diff --git a/src/scheduler/Task.php b/src/scheduler/Task.php index bde405a637d..db42b477ad0 100644 --- a/src/scheduler/Task.php +++ b/src/scheduler/Task.php @@ -26,8 +26,12 @@ use pocketmine\utils\Utils; abstract class Task{ + /** @phpstan-var TaskHandler|null */ private ?TaskHandler $taskHandler = null; + /** + * @phpstan-return TaskHandler|null + */ final public function getHandler() : ?TaskHandler{ return $this->taskHandler; } @@ -36,6 +40,9 @@ public function getName() : string{ return Utils::getNiceClassName($this); } + /** + * @phpstan-param TaskHandler|null $taskHandler + */ final public function setHandler(?TaskHandler $taskHandler) : void{ if($this->taskHandler === null || $taskHandler === null){ $this->taskHandler = $taskHandler; diff --git a/src/scheduler/TaskHandler.php b/src/scheduler/TaskHandler.php index b90c992d920..985344b6498 100644 --- a/src/scheduler/TaskHandler.php +++ b/src/scheduler/TaskHandler.php @@ -26,6 +26,9 @@ use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; +/** + * @template TTask of Task + */ class TaskHandler{ protected int $nextRun; @@ -36,6 +39,9 @@ class TaskHandler{ private string $taskName; private string $ownerName; + /** + * @phpstan-param TTask $task + */ public function __construct( protected Task $task, protected int $delay = -1, @@ -66,6 +72,9 @@ public function setNextRun(int $ticks) : void{ $this->nextRun = $ticks; } + /** + * @phpstan-return TTask + */ public function getTask() : Task{ return $this->task; } diff --git a/src/scheduler/TaskScheduler.php b/src/scheduler/TaskScheduler.php index f2b7c484678..3e2b089c6c5 100644 --- a/src/scheduler/TaskScheduler.php +++ b/src/scheduler/TaskScheduler.php @@ -33,12 +33,12 @@ class TaskScheduler{ private bool $enabled = true; - /** @phpstan-var ReversePriorityQueue */ + /** @phpstan-var ReversePriorityQueue> */ protected ReversePriorityQueue $queue; /** * @var ObjectSet|TaskHandler[] - * @phpstan-var ObjectSet + * @phpstan-var ObjectSet> */ protected ObjectSet $tasks; @@ -51,18 +51,42 @@ public function __construct( $this->tasks = new ObjectSet(); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleTask(Task $task) : TaskHandler{ return $this->addTask($task, -1, -1); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleDelayedTask(Task $task, int $delay) : TaskHandler{ return $this->addTask($task, $delay, -1); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleRepeatingTask(Task $task, int $period) : TaskHandler{ return $this->addTask($task, -1, $period); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleDelayedRepeatingTask(Task $task, int $delay, int $period) : TaskHandler{ return $this->addTask($task, $delay, $period); } @@ -77,10 +101,19 @@ public function cancelAllTasks() : void{ } } + /** + * @phpstan-param TaskHandler $task + */ public function isQueued(TaskHandler $task) : bool{ return $this->tasks->contains($task); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ private function addTask(Task $task, int $delay, int $period) : TaskHandler{ if(!$this->enabled){ throw new \LogicException("Tried to schedule task to disabled scheduler"); @@ -99,6 +132,11 @@ private function addTask(Task $task, int $delay, int $period) : TaskHandler{ return $this->handle(new TaskHandler($task, $delay, $period, $this->owner)); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TaskHandler $handler + * @phpstan-return TaskHandler + */ private function handle(TaskHandler $handler) : TaskHandler{ if($handler->isDelayed()){ $nextRun = $this->currentTick + $handler->getDelay(); @@ -128,7 +166,7 @@ public function mainThreadHeartbeat(int $currentTick) : void{ } $this->currentTick = $currentTick; while($this->isReady($this->currentTick)){ - /** @var TaskHandler $task */ + /** @phpstan-var TaskHandler $task */ $task = $this->queue->extract(); if($task->isCancelled()){ $this->tasks->remove($task); diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 563af69bff6..77f8efee646 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -30,6 +30,7 @@ use pocketmine\network\mcpe\protocol\ServerboundPacket; use pocketmine\player\Player; use pocketmine\scheduler\AsyncTask; +use pocketmine\scheduler\Task; use pocketmine\scheduler\TaskHandler; use function get_class; use function str_starts_with; @@ -192,6 +193,10 @@ public static function init() : void{ } + /** + * @template TTask of Task + * @phpstan-param TaskHandler $task + */ public static function getScheduledTaskTimings(TaskHandler $task, int $period) : TimingsHandler{ self::init(); $name = "Task: " . $task->getTaskName(); diff --git a/src/world/sound/IceBombHitSound.php b/src/world/sound/IceBombHitSound.php new file mode 100644 index 00000000000..7dcdeebf676 --- /dev/null +++ b/src/world/sound/IceBombHitSound.php @@ -0,0 +1,34 @@ +\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\.$#" + message: "#^Cannot call method getNextRun\\(\\) on array\\\\>\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\\\.$#" count: 1 path: ../../../src/scheduler/TaskScheduler.php