diff --git a/changelogs/5.23.md b/changelogs/5.23.md new file mode 100644 index 00000000000..f5b08593d7f --- /dev/null +++ b/changelogs/5.23.md @@ -0,0 +1,107 @@ +# 5.23.0 +Released 5th December 2024. + +This is a minor feature release, including new gameplay features, internals improvements, API additions and +deprecations, and improvements to timings. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- `/timings` now supports collecting timings from async task workers. These new timings will be shown alongside `Full Server Tick` timings, but will not be counted in total load. +- Added `/xp` command. +- `start.sh` will now emit warnings when the server process exits with an unusual exit code. This helps to detect unexpected segmentation faults and other kinds of native errors. + +## Gameplay +- Added the following new items: + - End Crystal + - Goat Horn (all variants) + - Ice Bomb (from Education Edition) + - Recovery Compass +- Added the following enchantments: + - Frost Walker +- Sugarcane now self-destructs when there is no water adjacent to the base block. +- Added basic support for middle-clicking on entities to get their spawn eggs. +- Added sounds when drinking potions. +- Eating food is now allowed in creative mode and in peaceful difficulty. + +## API +### `pocketmine\block` +- Extracted `MultiAnyFacingTrait` and `MultiAnySupportTrait` from `GlowLichen` to enable reuse in other blocks. +- The following API methods have been deprecated: + - `Campfire->getInventory()` - this was added by mistake and can't be well-supported given the way that blocks work + +### `pocketmine\command` +- The following classes have been added: + - `ClosureCommand` - allows registering a closure to execute a command + +### `pocketmine\event` +- Added APIs to `PlayerInteractEvent` to allow toggling item and block interactions. + - This allows various customisations, such as allowing interactions when sneaking, selectively disabling item or block reactions, etc. + - If both item and block interactions are disabled, the event is **not** cancelled (blocks can still be placed). + - The following API methods have been added: + - `public PlayerInteractEvent->setUseBlock(bool $useBlock) : void` + - `public PlayerInteractEvent->setUseItem(bool $useItem) : void` + - `public PlayerInteractEvent->useBlock() : bool` - returns whether the block can respond to the interaction (toggling levers, opening/closing doors, etc). + - `public PlayerInteractEvent->useItem() : bool` - returns whether the item can respond to the interaction (spawn eggs, flint & steel, etc). +- The following new classes have been added: + - `player\PlayerEntityPickEvent` - called when a player middle-clicks on an entity + +### `pocketmine\inventory\transaction` +- The following API methods have been deprecated: + - `InventoryAction->onAddToTransaction()` + +### `pocketmine\permission` +- The following API methods have been deprecated: + - `PermissionManager->getPermissionSubscriptions()` + - `PermissionManager->subscribeToPermission()` + - `PermissionManager->unsubscribeFromAllPermissions()` + - `PermissionManager->unsubscribeFromPermission()` + +### `pocketmine\plugin` +- The following classes have been deprecated: + - `DiskResourceProvider` + - `ResourceProvider` + +### `pocketmine\promise` +- `Promise::all()` now accepts zero promises. This will return an already-resolved promise with an empty array. + +### `pocketmine\scheduler` +- Added PHPStan generic types to `TaskHandler` and related APIs in `TaskScheduler` and `Task`. +- The following API methods have been deprecated + - `AsyncTask->publishProgress()` + - `AsyncTask->onProgressUpdate()` + +### `pocketmine\timings` +- Timings can now notify other code when timings are enabled/disabled, reloaded, or collected. + - The intent of this is to facilitate timings usage on other threads, and have the results collected into a single timings report. + - Timings cannot directly control timings on other threads, so these callbacks allow plugins to use custom mechanisms to toggle, reset and collect timings. + - PocketMine-MP currently uses this to collect timings from async task workers. More internal threads may be supported in the future. +- The following API methods have been added: + - `public static TimingsHandler::getCollectCallbacks() : ObjectSet<\Closure() : list>>` - callbacks for (asynchronously) collecting timings (typically from other threads). The returned promises should be resolved with the result of `TimingsHandler::printCurrentThreadRecords()`. + - `public static TimingsHandler::getReloadCallbacks() : ObjectSet<\Closure() : void>` - callbacks called when timings are reset + - `public static TimingsHandler::getToggleCallbacks() : ObjectSet<\Closure(bool $enable) : void>` - callbacks called when timings are enabled/disabled + - `public static TimingsHandler::requestPrintTimings() : Promise>` - asynchronously collects timing results from all threads and assembles them into a single report +- The following API methods have been deprecated: + - `TimingsHandler::printTimings()` - this function cannot support async timings collection. Use `TimingsHandler::requestPrintTimings()` instead. + - `Timings::getAsyncTaskErrorTimings()` - internal method that is no longer needed +- The following constants have been deprecated: + - `Timings::GROUP_BREAKDOWN` - no longer used + +### `pocketmine\utils` +- The following API methods have been added: + - `public static Utils::getRandomFloat() : float` - returns a random float between 0 and 1. Drop-in replacement for `lcg_value()` in PHP 8.4. + +## Internals +- Blocks are now always synced with the client during a right-click-block interaction. This clears mispredictions on the client in case the new `PlayerInteractEvent` flags were customized by plugins. +- `VanillaBlocks` and `VanillaItems` now use reflection to lookup TypeId constants by registration name, instead of requiring TypeIds to be manually specified. + - While this is obviously a hack, it prevents incorrect constants from being used when adding new blocks, and guarantees that the names of constants in `BlockTypeIds` and `ItemTypeIds` will match their corresponding entries in `VanillaBlocks` and `VanillaItems` respectively. + - It also significantly improves readability of `VanillaBlocks` and `VanillaItems`, as well as eliminating ugly code like `WoodLikeBlockIdHelper`. + - In PM6, the team is exploring options to redesign `VanillaBlocks` and `VanillaItems` to eliminate the need for defining separate TypeIds entirely. +- `ConsoleReader` now uses socket support in `proc_open()` to transmit IPC messages to the server process. Previously, a temporary socket server was used, which was unreliable in some conditions. +- Event handler tests have been converted to PHPUnit tests by mocking `Server` and `Plugin` instances. Previously, these required integration tests for these dependencies. +- Fixed various deprecation warnings in PHP 8.4. +- `netresearch/jsonmapper` is now used at `5.0.0`. The PMMP fork of this library has been removed, as it is no longer needed. diff --git a/composer.json b/composer.json index 4a6ad5daf2d..e2ae641cac0 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", - "pocketmine/netresearch-jsonmapper": "~v4.4.999", + "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40", "pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", @@ -41,7 +41,7 @@ "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", - "pocketmine/locale-data": "~2.19.0", + "pocketmine/locale-data": "~2.22.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", diff --git a/composer.lock b/composer.lock index 89e7b3af434..1cf1415888b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "26d10b9381ab4e19684ca0b7a5a11a42", + "content-hash": "732102eca72dc1d29e7b67dfbce07653", "packages": [ { "name": "adhocore/json-comment", @@ -125,6 +125,57 @@ ], "time": "2023-11-29T23:19:16+00:00" }, + { + "name": "netresearch/jsonmapper", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0" + }, + "time": "2024-09-08T10:20:00+00:00" + }, { "name": "pocketmine/bedrock-block-upgrade-schema", "version": "5.0.0", @@ -420,16 +471,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.19.6", + "version": "2.22.0", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e" + "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e", - "reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e", + "url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d", + "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d", "shasum": "" }, "type": "library", @@ -437,9 +488,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.19.6" + "source": "https://github.com/pmmp/Language/tree/2.22.0" }, - "time": "2023-08-08T16:53:23+00:00" + "time": "2024-11-16T13:28:01+00:00" }, { "name": "pocketmine/log", @@ -565,60 +616,6 @@ }, "time": "2023-07-14T13:01:49+00:00" }, - { - "name": "pocketmine/netresearch-jsonmapper", - "version": "v4.4.999", - "source": { - "type": "git", - "url": "https://github.com/pmmp/netresearch-jsonmapper.git", - "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833", - "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=7.1" - }, - "replace": { - "netresearch/jsonmapper": "~4.2.0" - }, - "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", - "squizlabs/php_codesniffer": "~3.5" - }, - "type": "library", - "autoload": { - "psr-0": { - "JsonMapper": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@cweiske.de", - "homepage": "http://github.com/cweiske/jsonmapper/", - "role": "Developer" - } - ], - "description": "Fork of netresearch/jsonmapper with security fixes needed by pocketmine/pocketmine-mp", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999" - }, - "time": "2024-02-23T13:17:01+00:00" - }, { "name": "pocketmine/raklib", "version": "1.1.1", diff --git a/src/Server.php b/src/Server.php index 074088068b9..9003a7f9cfa 100644 --- a/src/Server.php +++ b/src/Server.php @@ -89,6 +89,8 @@ use pocketmine\promise\PromiseResolver; use pocketmine\resourcepacks\ResourcePackManager; use pocketmine\scheduler\AsyncPool; +use pocketmine\scheduler\TimingsCollectionTask; +use pocketmine\scheduler\TimingsControlTask; use pocketmine\snooze\SleeperHandler; use pocketmine\stats\SendUsageTask; use pocketmine\thread\log\AttachableThreadSafeLogger; @@ -894,7 +896,36 @@ public function __construct( $poolSize = max(1, (int) $poolSize); } + TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false)); + $this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND); + $this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper); + $this->asyncPool->addWorkerStartHook(function(int $i) : void{ + if(TimingsHandler::isEnabled()){ + $this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled(true), $i); + } + }); + TimingsHandler::getToggleCallbacks()->add(function(bool $enable) : void{ + foreach($this->asyncPool->getRunningWorkers() as $workerId){ + $this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled($enable), $workerId); + } + }); + TimingsHandler::getReloadCallbacks()->add(function() : void{ + foreach($this->asyncPool->getRunningWorkers() as $workerId){ + $this->asyncPool->submitTaskToWorker(TimingsControlTask::reload(), $workerId); + } + }); + TimingsHandler::getCollectCallbacks()->add(function() : array{ + $promises = []; + foreach($this->asyncPool->getRunningWorkers() as $workerId){ + $resolver = new PromiseResolver(); + $this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId); + + $promises[] = $resolver->getPromise(); + } + + return $promises; + }); $netCompressionThreshold = -1; if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){ @@ -968,9 +999,6 @@ public function __construct( ))); $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName()))); - TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false)); - $this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND); - DefaultPermissions::registerCorePermissions(); $this->commandMap = new SimpleCommandMap($this); diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 9c1fdf2302f..90c1a7df4fd 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.22.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.23.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 4b4afef6153..0a1a4707005 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -35,10 +35,10 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\utils\Utils; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\AnvilFallSound; use pocketmine\world\sound\Sound; -use function lcg_value; use function round; class Anvil extends Transparent implements Fallable{ @@ -97,7 +97,7 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo } public function onHitGround(FallingBlock $blockEntity) : bool{ - if(lcg_value() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){ + if(Utils::getRandomFloat() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){ if($this->damage !== self::VERY_DAMAGED){ $this->damage = $this->damage + 1; }else{ diff --git a/src/block/Campfire.php b/src/block/Campfire.php index ce759ee87f7..9f4c42a9ccc 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -69,6 +69,10 @@ class Campfire extends Transparent{ 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. + */ protected CampfireInventory $inventory; /** @@ -129,6 +133,10 @@ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)]; } + /** + * @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. + */ public function getInventory() : CampfireInventory{ return $this->inventory; } diff --git a/src/block/Farmland.php b/src/block/Farmland.php index a17a220f0b5..b7a2500a8b5 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -31,8 +31,8 @@ use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; +use pocketmine\utils\Utils; use function intdiv; -use function lcg_value; class Farmland extends Transparent{ public const MAX_WETNESS = 7; @@ -148,7 +148,7 @@ public function onRandomTick() : void{ } public function onEntityLand(Entity $entity) : ?float{ - if($entity instanceof Living && lcg_value() < $entity->getFallDistance() - 0.5){ + if($entity instanceof Living && Utils::getRandomFloat() < $entity->getFallDistance() - 0.5){ $ev = new EntityTrampleFarmlandEvent($entity, $this); $ev->call(); if(!$ev->isCancelled()){ diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index de66ccad769..d30e2539582 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -24,60 +24,20 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; -use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\world\BlockTransaction; use pocketmine\world\World; -use function array_key_first; use function count; use function shuffle; class GlowLichen extends Transparent{ - - /** @var int[] */ - protected array $faces = []; - - protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facingFlags($this->faces); - } - - /** @return int[] */ - public function getFaces() : array{ return $this->faces; } - - public function hasFace(int $face) : bool{ - return isset($this->faces[$face]); - } - - /** - * @param int[] $faces - * @return $this - */ - public function setFaces(array $faces) : self{ - $uniqueFaces = []; - foreach($faces as $face){ - Facing::validate($face); - $uniqueFaces[$face] = $face; - } - $this->faces = $uniqueFaces; - return $this; - } - - /** @return $this */ - public function setFace(int $face, bool $value) : self{ - Facing::validate($face); - if($value){ - $this->faces[$face] = $face; - }else{ - unset($this->faces[$face]); - } - return $this; - } + use MultiAnySupportTrait; public function getLightLevel() : int{ return 7; @@ -102,39 +62,11 @@ public function canBeReplaced() : bool{ return true; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - $this->faces = $blockReplace instanceof GlowLichen ? $blockReplace->faces : []; - $availableFaces = $this->getAvailableFaces(); - - if(count($availableFaces) === 0){ - return false; - } - - $opposite = Facing::opposite($face); - $placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces); - $this->faces[$placedFace] = $placedFace; - - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - - public function onNearbyBlockChange() : void{ - $changed = false; - - foreach($this->faces as $face){ - if($this->getAdjacentSupportType($face) !== SupportType::FULL){ - unset($this->faces[$face]); - $changed = true; - } - } - - if($changed){ - $world = $this->position->getWorld(); - if(count($this->faces) === 0){ - $world->useBreakOn($this->position); - }else{ - $world->setBlock($this->position, $this); - } - } + /** + * @return int[] + */ + protected function getInitialPlaceFaces(Block $blockReplace) : array{ + return $blockReplace instanceof GlowLichen ? $blockReplace->faces : []; } private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{ @@ -261,17 +193,4 @@ public function getFlameEncouragement() : int{ public function getFlammability() : int{ return 100; } - - /** - * @return array $faces - */ - private function getAvailableFaces() : array{ - $faces = []; - foreach(Facing::ALL as $face){ - if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){ - $faces[$face] = $face; - } - } - return $faces; - } } diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index b5b6093c497..c03806a3b55 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -31,13 +31,13 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\utils\Utils; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\ItemFrameAddItemSound; use pocketmine\world\sound\ItemFrameRemoveItemSound; use pocketmine\world\sound\ItemFrameRotateItemSound; use function is_infinite; use function is_nan; -use function lcg_value; class ItemFrame extends Flowable{ use AnyFacingTrait; @@ -154,7 +154,7 @@ public function onAttack(Item $item, int $face, ?Player $player = null) : bool{ return false; } $world = $this->position->getWorld(); - if(lcg_value() <= $this->itemDropChance){ + if(Utils::getRandomFloat() <= $this->itemDropChance){ $world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem); $world->addSound($this->position, new ItemFrameRemoveItemSound()); } @@ -185,7 +185,7 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo public function getDropsForCompatibleTool(Item $item) : array{ $drops = parent::getDropsForCompatibleTool($item); - if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){ + if($this->framedItem !== null && Utils::getRandomFloat() <= $this->itemDropChance){ $drops[] = clone $this->framedItem; } diff --git a/src/block/Liquid.php b/src/block/Liquid.php index 6404cf9081f..a37019d650d 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -33,9 +33,9 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; +use pocketmine\utils\Utils; use pocketmine\world\sound\FizzSound; use pocketmine\world\sound\Sound; -use function lcg_value; abstract class Liquid extends Transparent{ public const MAX_DECAY = 7; @@ -368,7 +368,7 @@ protected function checkForHarden() : bool{ protected function liquidCollide(Block $cause, Block $result) : bool{ if(BlockEventHelper::form($this, $result, $cause)){ - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); + $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.8)); } return true; } diff --git a/src/block/Magma.php b/src/block/Magma.php index d2f309325d4..7b3fa5229eb 100644 --- a/src/block/Magma.php +++ b/src/block/Magma.php @@ -39,7 +39,7 @@ public function hasEntityCollision() : bool{ } public function onEntityInside(Entity $entity) : bool{ - if($entity instanceof Living && !$entity->isSneaking()){ + if($entity instanceof Living && !$entity->isSneaking() && $entity->getFrostWalkerLevel() === 0){ $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1); $entity->attack($ev); } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 97b4aee9c19..2da2dc9b971 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -36,7 +36,9 @@ class Sugarcane extends Flowable{ use AgeableTrait; - use StaticSupportTrait; + use StaticSupportTrait { + onNearbyBlockChange as onSupportBlockChange; + } public const MAX_AGE = 15; @@ -97,7 +99,13 @@ public function ticksRandomly() : bool{ } public function onRandomTick() : void{ - if(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)){ + $down = $this->getSide(Facing::DOWN); + if(!$down->hasSameTypeId($this)){ + if(!$this->hasNearbyWater($down)){ + $this->position->getWorld()->useBreakOn($this->position, createParticles: true); + return; + } + if($this->age === self::MAX_AGE){ $this->grow($this->position); }else{ @@ -123,4 +131,23 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo return false; } + + private function hasNearbyWater(Block $down) : bool{ + foreach($down->getHorizontalSides() as $sideBlock){ + $blockId = $sideBlock->getTypeId(); + if($blockId === BlockTypeIds::WATER || $blockId === BlockTypeIds::FROSTED_ICE){ + return true; + } + } + return false; + } + + public function onNearbyBlockChange() : void{ + $down = $this->getSide(Facing::DOWN); + if(!$down->hasSameTypeId($this) && !$this->hasNearbyWater($down)){ + $this->position->getWorld()->useBreakOn($this->position, createParticles: true); + }else{ + $this->onSupportBlockChange(); + } + } } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 60540dfb832..54cf90a0c3b 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -26,7 +26,6 @@ use pocketmine\block\BlockBreakInfo as BreakInfo; use pocketmine\block\BlockIdentifier as BID; use pocketmine\block\BlockToolType as ToolType; -use pocketmine\block\BlockTypeIds as Ids; use pocketmine\block\BlockTypeInfo as Info; use pocketmine\block\BlockTypeTags as Tags; use pocketmine\block\tile\Banner as TileBanner; @@ -56,6 +55,7 @@ use pocketmine\block\tile\Note as TileNote; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; use pocketmine\block\tile\Smoker as TileSmoker; +use pocketmine\block\tile\Tile; use pocketmine\block\utils\AmethystTrait; use pocketmine\block\utils\LeavesType; use pocketmine\block\utils\SaplingType; @@ -64,9 +64,12 @@ use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\Item; use pocketmine\item\ToolTier; +use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use pocketmine\utils\CloningRegistryTrait; +use function is_int; use function mb_strtolower; +use function mb_strtoupper; use function strtolower; /** @@ -793,8 +796,28 @@ private function __construct(){ //NOOP } - protected static function register(string $name, Block $block) : void{ + /** + * @phpstan-template TBlock of Block + * @phpstan-param \Closure(BID) : TBlock $createBlock + * @phpstan-param class-string $tileClass + * @phpstan-return TBlock + */ + protected static function register(string $name, \Closure $createBlock, ?string $tileClass = null) : Block{ + //this sketchy hack allows us to avoid manually writing the constants inline + //since type IDs are generated from this class anyway, I'm OK with this hack + //nonetheless, we should try to get rid of it in a future major version (e.g by using string type IDs) + $reflect = new \ReflectionClass(BlockTypeIds::class); + $typeId = $reflect->getConstant(mb_strtoupper($name)); + if(!is_int($typeId)){ + //this allows registering new stuff without adding new type ID constants + //this reduces the number of mandatory steps to test new features in local development + \GlobalLogger::get()->error(self::class . ": No constant type ID found for $name, generating a new one"); + $typeId = BlockTypeIds::newId(); + } + $block = $createBlock(new BID($typeId, $tileClass)); self::_registryRegister($name, $block); + + return $block; } /** @@ -809,11 +832,12 @@ public static function getAll() : array{ } protected static function setup() : void{ + self::register("air", fn(BID $id) => new Air($id, "Air", new Info(BreakInfo::indestructible(-1.0)))); + $railBreakInfo = new Info(new BreakInfo(0.7)); - self::register("activator_rail", new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL), "Activator Rail", $railBreakInfo)); - self::register("air", new Air(new BID(Ids::AIR), "Air", new Info(BreakInfo::indestructible(-1.0)))); - self::register("anvil", new Anvil(new BID(Ids::ANVIL), "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); - self::register("bamboo", new Bamboo(new BID(Ids::BAMBOO), "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{ + self::register("activator_rail", fn(BID $id) => new ActivatorRail($id, "Activator Rail", $railBreakInfo)); + self::register("anvil", fn(BID $id) => new Anvil($id, "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); + self::register("bamboo", fn(BID $id) => new Bamboo($id, "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{ public function getBreakTime(Item $item) : float{ if($item->getBlockToolType() === ToolType::SWORD){ return 0.0; @@ -821,242 +845,242 @@ public function getBreakTime(Item $item) : float{ return parent::getBreakTime($item); } }, [Tags::POTTABLE_PLANTS]))); - self::register("bamboo_sapling", new BambooSapling(new BID(Ids::BAMBOO_SAPLING), "Bamboo Sapling", new Info(BreakInfo::instant()))); + self::register("bamboo_sapling", fn(BID $id) => new BambooSapling($id, "Bamboo Sapling", new Info(BreakInfo::instant()))); $bannerBreakInfo = new Info(BreakInfo::axe(1.0)); - self::register("banner", new FloorBanner(new BID(Ids::BANNER, TileBanner::class), "Banner", $bannerBreakInfo)); - self::register("wall_banner", new WallBanner(new BID(Ids::WALL_BANNER, TileBanner::class), "Wall Banner", $bannerBreakInfo)); - self::register("barrel", new Barrel(new BID(Ids::BARREL, TileBarrel::class), "Barrel", new Info(BreakInfo::axe(2.5)))); - self::register("barrier", new Transparent(new BID(Ids::BARRIER), "Barrier", new Info(BreakInfo::indestructible()))); - self::register("beacon", new Beacon(new BID(Ids::BEACON, TileBeacon::class), "Beacon", new Info(new BreakInfo(3.0)))); - self::register("bed", new Bed(new BID(Ids::BED, TileBed::class), "Bed Block", new Info(new BreakInfo(0.2)))); - self::register("bedrock", new Bedrock(new BID(Ids::BEDROCK), "Bedrock", new Info(BreakInfo::indestructible()))); - - self::register("beetroots", new Beetroot(new BID(Ids::BEETROOTS), "Beetroot Block", new Info(BreakInfo::instant()))); - self::register("bell", new Bell(new BID(Ids::BELL, TileBell::class), "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); - self::register("blue_ice", new BlueIce(new BID(Ids::BLUE_ICE), "Blue Ice", new Info(BreakInfo::pickaxe(2.8)))); - self::register("bone_block", new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD)))); - self::register("bookshelf", new Bookshelf(new BID(Ids::BOOKSHELF), "Bookshelf", new Info(BreakInfo::axe(1.5)))); - self::register("chiseled_bookshelf", new ChiseledBookshelf(new BID(Ids::CHISELED_BOOKSHELF, TileChiseledBookshelf::class), "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5)))); - self::register("brewing_stand", new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class); + self::register("wall_banner", fn(BID $id) => new WallBanner($id, "Wall Banner", $bannerBreakInfo), TileBanner::class); + self::register("barrel", fn(BID $id) => new Barrel($id, "Barrel", new Info(BreakInfo::axe(2.5))), TileBarrel::class); + self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible()))); + self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class); + self::register("bed", fn(BID $id) => new Bed($id, "Bed Block", new Info(new BreakInfo(0.2))), TileBed::class); + self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible()))); + + self::register("beetroots", fn(BID $id) => new Beetroot($id, "Beetroot Block", new Info(BreakInfo::instant()))); + self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))), TileBell::class); + self::register("blue_ice", fn(BID $id) => new BlueIce($id, "Blue Ice", new Info(BreakInfo::pickaxe(2.8)))); + self::register("bone_block", fn(BID $id) => new BoneBlock($id, "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD)))); + self::register("bookshelf", fn(BID $id) => new Bookshelf($id, "Bookshelf", new Info(BreakInfo::axe(1.5)))); + self::register("chiseled_bookshelf", fn(BID $id) => new ChiseledBookshelf($id, "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))), TileChiseledBookshelf::class); + self::register("brewing_stand", fn(BID $id) => new BrewingStand($id, "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))), TileBrewingStand::class); $bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("brick_stairs", new Stair(new BID(Ids::BRICK_STAIRS), "Brick Stairs", $bricksBreakInfo)); - self::register("bricks", new Opaque(new BID(Ids::BRICKS), "Bricks", $bricksBreakInfo)); + self::register("brick_stairs", fn(BID $id) => new Stair($id, "Brick Stairs", $bricksBreakInfo)); + self::register("bricks", fn(BID $id) => new Opaque($id, "Bricks", $bricksBreakInfo)); - self::register("brown_mushroom", new BrownMushroom(new BID(Ids::BROWN_MUSHROOM), "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); - self::register("cactus", new Cactus(new BID(Ids::CACTUS), "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS]))); - self::register("cake", new Cake(new BID(Ids::CAKE), "Cake", new Info(new BreakInfo(0.5)))); + self::register("brown_mushroom", fn(BID $id) => new BrownMushroom($id, "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); + self::register("cactus", fn(BID $id) => new Cactus($id, "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS]))); + self::register("cake", fn(BID $id) => new Cake($id, "Cake", new Info(new BreakInfo(0.5)))); $campfireBreakInfo = new Info(BreakInfo::axe(2.0)); - self::register("campfire", new Campfire(new BID(Ids::CAMPFIRE, TileCampfire::class), "Campfire", $campfireBreakInfo)); - self::register("soul_campfire", new SoulCampfire(new BID(Ids::SOUL_CAMPFIRE, TileCampfire::class), "Soul Campfire", $campfireBreakInfo)); + self::register("campfire", fn(BID $id) => new Campfire($id, "Campfire", $campfireBreakInfo), TileCampfire::class); + self::register("soul_campfire", fn(BID $id) => new SoulCampfire($id, "Soul Campfire", $campfireBreakInfo), TileCampfire::class); - self::register("carrots", new Carrot(new BID(Ids::CARROTS), "Carrot Block", new Info(BreakInfo::instant()))); + self::register("carrots", fn(BID $id) => new Carrot($id, "Carrot Block", new Info(BreakInfo::instant()))); $chestBreakInfo = new Info(BreakInfo::axe(2.5)); - self::register("chest", new Chest(new BID(Ids::CHEST, TileChest::class), "Chest", $chestBreakInfo)); - self::register("clay", new Clay(new BID(Ids::CLAY), "Clay Block", new Info(BreakInfo::shovel(0.6)))); - self::register("coal", new Coal(new BID(Ids::COAL), "Coal Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); + self::register("chest", fn(BID $id) => new Chest($id, "Chest", $chestBreakInfo), TileChest::class); + self::register("clay", fn(BID $id) => new Clay($id, "Clay Block", new Info(BreakInfo::shovel(0.6)))); + self::register("coal", fn(BID $id) => new Coal($id, "Coal Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); $cobblestoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("cobblestone", $cobblestone = new Opaque(new BID(Ids::COBBLESTONE), "Cobblestone", $cobblestoneBreakInfo)); - self::register("mossy_cobblestone", new Opaque(new BID(Ids::MOSSY_COBBLESTONE), "Mossy Cobblestone", $cobblestoneBreakInfo)); - self::register("cobblestone_stairs", new Stair(new BID(Ids::COBBLESTONE_STAIRS), "Cobblestone Stairs", $cobblestoneBreakInfo)); - self::register("mossy_cobblestone_stairs", new Stair(new BID(Ids::MOSSY_COBBLESTONE_STAIRS), "Mossy Cobblestone Stairs", $cobblestoneBreakInfo)); - - self::register("cobweb", new Cobweb(new BID(Ids::COBWEB), "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)))); - self::register("cocoa_pod", new CocoaBlock(new BID(Ids::COCOA_POD), "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0)))); - self::register("coral_block", new CoralBlock(new BID(Ids::CORAL_BLOCK), "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD)))); - self::register("daylight_sensor", new DaylightSensor(new BID(Ids::DAYLIGHT_SENSOR, TileDaylightSensor::class), "Daylight Sensor", new Info(BreakInfo::axe(0.2)))); - self::register("dead_bush", new DeadBush(new BID(Ids::DEAD_BUSH), "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); - self::register("detector_rail", new DetectorRail(new BID(Ids::DETECTOR_RAIL), "Detector Rail", $railBreakInfo)); - - self::register("diamond", new Opaque(new BID(Ids::DIAMOND), "Diamond Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); - self::register("dirt", new Dirt(new BID(Ids::DIRT), "Dirt", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); - self::register("sunflower", new DoublePlant(new BID(Ids::SUNFLOWER), "Sunflower", new Info(BreakInfo::instant()))); - self::register("lilac", new DoublePlant(new BID(Ids::LILAC), "Lilac", new Info(BreakInfo::instant()))); - self::register("rose_bush", new DoublePlant(new BID(Ids::ROSE_BUSH), "Rose Bush", new Info(BreakInfo::instant()))); - self::register("peony", new DoublePlant(new BID(Ids::PEONY), "Peony", new Info(BreakInfo::instant()))); - self::register("pink_petals", new PinkPetals(new BID(Ids::PINK_PETALS), "Pink Petals", new Info(BreakInfo::instant()))); - self::register("double_tallgrass", new DoubleTallGrass(new BID(Ids::DOUBLE_TALLGRASS), "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("large_fern", new DoubleTallGrass(new BID(Ids::LARGE_FERN), "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("pitcher_plant", new DoublePlant(new BID(Ids::PITCHER_PLANT), "Pitcher Plant", new Info(BreakInfo::instant()))); - self::register("pitcher_crop", new PitcherCrop(new BID(Ids::PITCHER_CROP), "Pitcher Crop", new Info(BreakInfo::instant()))); - self::register("double_pitcher_crop", new DoublePitcherCrop(new BID(Ids::DOUBLE_PITCHER_CROP), "Double Pitcher Crop", new Info(BreakInfo::instant()))); - self::register("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); - self::register("dried_kelp", new DriedKelp(new BID(Ids::DRIED_KELP), "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5)))); - self::register("emerald", new Opaque(new BID(Ids::EMERALD), "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); - self::register("enchanting_table", new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, TileEnchantingTable::class), "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); - self::register("end_portal_frame", new EndPortalFrame(new BID(Ids::END_PORTAL_FRAME), "End Portal Frame", new Info(BreakInfo::indestructible()))); - self::register("end_rod", new EndRod(new BID(Ids::END_ROD), "End Rod", new Info(BreakInfo::instant()))); - self::register("end_stone", new Opaque(new BID(Ids::END_STONE), "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); + $cobblestone = self::register("cobblestone", fn(BID $id) => new Opaque($id, "Cobblestone", $cobblestoneBreakInfo)); + self::register("mossy_cobblestone", fn(BID $id) => new Opaque($id, "Mossy Cobblestone", $cobblestoneBreakInfo)); + self::register("cobblestone_stairs", fn(BID $id) => new Stair($id, "Cobblestone Stairs", $cobblestoneBreakInfo)); + self::register("mossy_cobblestone_stairs", fn(BID $id) => new Stair($id, "Mossy Cobblestone Stairs", $cobblestoneBreakInfo)); + + self::register("cobweb", fn(BID $id) => new Cobweb($id, "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)))); + self::register("cocoa_pod", fn(BID $id) => new CocoaBlock($id, "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0)))); + self::register("coral_block", fn(BID $id) => new CoralBlock($id, "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD)))); + self::register("daylight_sensor", fn(BID $id) => new DaylightSensor($id, "Daylight Sensor", new Info(BreakInfo::axe(0.2))), TileDaylightSensor::class); + self::register("dead_bush", fn(BID $id) => new DeadBush($id, "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); + self::register("detector_rail", fn(BID $id) => new DetectorRail($id, "Detector Rail", $railBreakInfo)); + + self::register("diamond", fn(BID $id) => new Opaque($id, "Diamond Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); + self::register("dirt", fn(BID $id) => new Dirt($id, "Dirt", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); + self::register("sunflower", fn(BID $id) => new DoublePlant($id, "Sunflower", new Info(BreakInfo::instant()))); + self::register("lilac", fn(BID $id) => new DoublePlant($id, "Lilac", new Info(BreakInfo::instant()))); + self::register("rose_bush", fn(BID $id) => new DoublePlant($id, "Rose Bush", new Info(BreakInfo::instant()))); + self::register("peony", fn(BID $id) => new DoublePlant($id, "Peony", new Info(BreakInfo::instant()))); + self::register("pink_petals", fn(BID $id) => new PinkPetals($id, "Pink Petals", new Info(BreakInfo::instant()))); + self::register("double_tallgrass", fn(BID $id) => new DoubleTallGrass($id, "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("large_fern", fn(BID $id) => new DoubleTallGrass($id, "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("pitcher_plant", fn(BID $id) => new DoublePlant($id, "Pitcher Plant", new Info(BreakInfo::instant()))); + self::register("pitcher_crop", fn(BID $id) => new PitcherCrop($id, "Pitcher Crop", new Info(BreakInfo::instant()))); + self::register("double_pitcher_crop", fn(BID $id) => new DoublePitcherCrop($id, "Double Pitcher Crop", new Info(BreakInfo::instant()))); + self::register("dragon_egg", fn(BID $id) => new DragonEgg($id, "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); + self::register("dried_kelp", fn(BID $id) => new DriedKelp($id, "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5)))); + self::register("emerald", fn(BID $id) => new Opaque($id, "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); + self::register("enchanting_table", fn(BID $id) => new EnchantingTable($id, "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))), TileEnchantingTable::class); + self::register("end_portal_frame", fn(BID $id) => new EndPortalFrame($id, "End Portal Frame", new Info(BreakInfo::indestructible()))); + self::register("end_rod", fn(BID $id) => new EndRod($id, "End Rod", new Info(BreakInfo::instant()))); + self::register("end_stone", fn(BID $id) => new Opaque($id, "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); $endBrickBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0)); - self::register("end_stone_bricks", new Opaque(new BID(Ids::END_STONE_BRICKS), "End Stone Bricks", $endBrickBreakInfo)); - self::register("end_stone_brick_stairs", new Stair(new BID(Ids::END_STONE_BRICK_STAIRS), "End Stone Brick Stairs", $endBrickBreakInfo)); + self::register("end_stone_bricks", fn(BID $id) => new Opaque($id, "End Stone Bricks", $endBrickBreakInfo)); + self::register("end_stone_brick_stairs", fn(BID $id) => new Stair($id, "End Stone Brick Stairs", $endBrickBreakInfo)); - self::register("ender_chest", new EnderChest(new BID(Ids::ENDER_CHEST, TileEnderChest::class), "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0)))); - self::register("farmland", new Farmland(new BID(Ids::FARMLAND), "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); - self::register("fire", new Fire(new BID(Ids::FIRE), "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE]))); + self::register("ender_chest", fn(BID $id) => new EnderChest($id, "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0))), TileEnderChest::class); + self::register("farmland", fn(BID $id) => new Farmland($id, "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); + self::register("fire", fn(BID $id) => new Fire($id, "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE]))); $flowerTypeInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]); - self::register("dandelion", new Flower(new BID(Ids::DANDELION), "Dandelion", $flowerTypeInfo)); - self::register("poppy", new Flower(new BID(Ids::POPPY), "Poppy", $flowerTypeInfo)); - self::register("allium", new Flower(new BID(Ids::ALLIUM), "Allium", $flowerTypeInfo)); - self::register("azure_bluet", new Flower(new BID(Ids::AZURE_BLUET), "Azure Bluet", $flowerTypeInfo)); - self::register("blue_orchid", new Flower(new BID(Ids::BLUE_ORCHID), "Blue Orchid", $flowerTypeInfo)); - self::register("cornflower", new Flower(new BID(Ids::CORNFLOWER), "Cornflower", $flowerTypeInfo)); - self::register("lily_of_the_valley", new Flower(new BID(Ids::LILY_OF_THE_VALLEY), "Lily of the Valley", $flowerTypeInfo)); - self::register("orange_tulip", new Flower(new BID(Ids::ORANGE_TULIP), "Orange Tulip", $flowerTypeInfo)); - self::register("oxeye_daisy", new Flower(new BID(Ids::OXEYE_DAISY), "Oxeye Daisy", $flowerTypeInfo)); - self::register("pink_tulip", new Flower(new BID(Ids::PINK_TULIP), "Pink Tulip", $flowerTypeInfo)); - self::register("red_tulip", new Flower(new BID(Ids::RED_TULIP), "Red Tulip", $flowerTypeInfo)); - self::register("white_tulip", new Flower(new BID(Ids::WHITE_TULIP), "White Tulip", $flowerTypeInfo)); - self::register("torchflower", new Flower(new BID(Ids::TORCHFLOWER), "Torchflower", $flowerTypeInfo)); - self::register("torchflower_crop", new TorchflowerCrop(new BID(Ids::TORCHFLOWER_CROP), "Torchflower Crop", new Info(BreakInfo::instant()))); - self::register("flower_pot", new FlowerPot(new BID(Ids::FLOWER_POT, TileFlowerPot::class), "Flower Pot", new Info(BreakInfo::instant()))); - self::register("frosted_ice", new FrostedIce(new BID(Ids::FROSTED_ICE), "Frosted Ice", new Info(BreakInfo::pickaxe(2.5)))); - self::register("furnace", new Furnace(new BID(Ids::FURNACE, TileNormalFurnace::class), "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE)); - self::register("blast_furnace", new Furnace(new BID(Ids::BLAST_FURNACE, TileBlastFurnace::class), "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE)); - self::register("smoker", new Furnace(new BID(Ids::SMOKER, TileSmoker::class), "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER)); + self::register("dandelion", fn(BID $id) => new Flower($id, "Dandelion", $flowerTypeInfo)); + self::register("poppy", fn(BID $id) => new Flower($id, "Poppy", $flowerTypeInfo)); + self::register("allium", fn(BID $id) => new Flower($id, "Allium", $flowerTypeInfo)); + self::register("azure_bluet", fn(BID $id) => new Flower($id, "Azure Bluet", $flowerTypeInfo)); + self::register("blue_orchid", fn(BID $id) => new Flower($id, "Blue Orchid", $flowerTypeInfo)); + self::register("cornflower", fn(BID $id) => new Flower($id, "Cornflower", $flowerTypeInfo)); + self::register("lily_of_the_valley", fn(BID $id) => new Flower($id, "Lily of the Valley", $flowerTypeInfo)); + self::register("orange_tulip", fn(BID $id) => new Flower($id, "Orange Tulip", $flowerTypeInfo)); + self::register("oxeye_daisy", fn(BID $id) => new Flower($id, "Oxeye Daisy", $flowerTypeInfo)); + self::register("pink_tulip", fn(BID $id) => new Flower($id, "Pink Tulip", $flowerTypeInfo)); + self::register("red_tulip", fn(BID $id) => new Flower($id, "Red Tulip", $flowerTypeInfo)); + self::register("white_tulip", fn(BID $id) => new Flower($id, "White Tulip", $flowerTypeInfo)); + self::register("torchflower", fn(BID $id) => new Flower($id, "Torchflower", $flowerTypeInfo)); + self::register("torchflower_crop", fn(BID $id) => new TorchflowerCrop($id, "Torchflower Crop", new Info(BreakInfo::instant()))); + self::register("flower_pot", fn(BID $id) => new FlowerPot($id, "Flower Pot", new Info(BreakInfo::instant())), TileFlowerPot::class); + self::register("frosted_ice", fn(BID $id) => new FrostedIce($id, "Frosted Ice", new Info(BreakInfo::pickaxe(2.5)))); + self::register("furnace", fn(BID $id) => new Furnace($id, "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE), TileNormalFurnace::class); + self::register("blast_furnace", fn(BID $id) => new Furnace($id, "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE), TileBlastFurnace::class); + self::register("smoker", fn(BID $id) => new Furnace($id, "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER), TileSmoker::class); $glassBreakInfo = new Info(new BreakInfo(0.3)); - self::register("glass", new Glass(new BID(Ids::GLASS), "Glass", $glassBreakInfo)); - self::register("glass_pane", new GlassPane(new BID(Ids::GLASS_PANE), "Glass Pane", $glassBreakInfo)); - self::register("glowing_obsidian", new GlowingObsidian(new BID(Ids::GLOWING_OBSIDIAN), "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0)))); - self::register("glowstone", new Glowstone(new BID(Ids::GLOWSTONE), "Glowstone", new Info(BreakInfo::pickaxe(0.3)))); - self::register("glow_lichen", new GlowLichen(new BID(Ids::GLOW_LICHEN), "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2)))); - self::register("gold", new Opaque(new BID(Ids::GOLD), "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0)))); + self::register("glass", fn(BID $id) => new Glass($id, "Glass", $glassBreakInfo)); + self::register("glass_pane", fn(BID $id) => new GlassPane($id, "Glass Pane", $glassBreakInfo)); + self::register("glowing_obsidian", fn(BID $id) => new GlowingObsidian($id, "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0)))); + self::register("glowstone", fn(BID $id) => new Glowstone($id, "Glowstone", new Info(BreakInfo::pickaxe(0.3)))); + self::register("glow_lichen", fn(BID $id) => new GlowLichen($id, "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2)))); + self::register("gold", fn(BID $id) => new Opaque($id, "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0)))); $grassBreakInfo = BreakInfo::shovel(0.6); - self::register("grass", new Grass(new BID(Ids::GRASS), "Grass", new Info($grassBreakInfo, [Tags::DIRT]))); - self::register("grass_path", new GrassPath(new BID(Ids::GRASS_PATH), "Grass Path", new Info($grassBreakInfo))); - self::register("gravel", new Gravel(new BID(Ids::GRAVEL), "Gravel", new Info(BreakInfo::shovel(0.6)))); + self::register("grass", fn(BID $id) => new Grass($id, "Grass", new Info($grassBreakInfo, [Tags::DIRT]))); + self::register("grass_path", fn(BID $id) => new GrassPath($id, "Grass Path", new Info($grassBreakInfo))); + self::register("gravel", fn(BID $id) => new Gravel($id, "Gravel", new Info(BreakInfo::shovel(0.6)))); $hardenedClayBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)); - self::register("hardened_clay", new HardenedClay(new BID(Ids::HARDENED_CLAY), "Hardened Clay", $hardenedClayBreakInfo)); + self::register("hardened_clay", fn(BID $id) => new HardenedClay($id, "Hardened Clay", $hardenedClayBreakInfo)); $hardenedGlassBreakInfo = new Info(new BreakInfo(10.0)); - self::register("hardened_glass", new HardenedGlass(new BID(Ids::HARDENED_GLASS), "Hardened Glass", $hardenedGlassBreakInfo)); - self::register("hardened_glass_pane", new HardenedGlassPane(new BID(Ids::HARDENED_GLASS_PANE), "Hardened Glass Pane", $hardenedGlassBreakInfo)); - self::register("hay_bale", new HayBale(new BID(Ids::HAY_BALE), "Hay Bale", new Info(new BreakInfo(0.5)))); - self::register("hopper", new Hopper(new BID(Ids::HOPPER, TileHopper::class), "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0)))); - self::register("ice", new Ice(new BID(Ids::ICE), "Ice", new Info(BreakInfo::pickaxe(0.5)))); + self::register("hardened_glass", fn(BID $id) => new HardenedGlass($id, "Hardened Glass", $hardenedGlassBreakInfo)); + self::register("hardened_glass_pane", fn(BID $id) => new HardenedGlassPane($id, "Hardened Glass Pane", $hardenedGlassBreakInfo)); + self::register("hay_bale", fn(BID $id) => new HayBale($id, "Hay Bale", new Info(new BreakInfo(0.5)))); + self::register("hopper", fn(BID $id) => new Hopper($id, "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0))), TileHopper::class); + self::register("ice", fn(BID $id) => new Ice($id, "Ice", new Info(BreakInfo::pickaxe(0.5)))); $updateBlockBreakInfo = new Info(new BreakInfo(1.0)); - self::register("info_update", new Opaque(new BID(Ids::INFO_UPDATE), "update!", $updateBlockBreakInfo)); - self::register("info_update2", new Opaque(new BID(Ids::INFO_UPDATE2), "ate!upd", $updateBlockBreakInfo)); - self::register("invisible_bedrock", new Transparent(new BID(Ids::INVISIBLE_BEDROCK), "Invisible Bedrock", new Info(BreakInfo::indestructible()))); + self::register("info_update", fn(BID $id) => new Opaque($id, "update!", $updateBlockBreakInfo)); + self::register("info_update2", fn(BID $id) => new Opaque($id, "ate!upd", $updateBlockBreakInfo)); + self::register("invisible_bedrock", fn(BID $id) => new Transparent($id, "Invisible Bedrock", new Info(BreakInfo::indestructible()))); $ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE, 30.0)); - self::register("iron", new Opaque(new BID(Ids::IRON), "Iron Block", $ironBreakInfo)); - self::register("iron_bars", new Thin(new BID(Ids::IRON_BARS), "Iron Bars", $ironBreakInfo)); + self::register("iron", fn(BID $id) => new Opaque($id, "Iron Block", $ironBreakInfo)); + self::register("iron_bars", fn(BID $id) => new Thin($id, "Iron Bars", $ironBreakInfo)); $ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 25.0)); - self::register("iron_door", new Door(new BID(Ids::IRON_DOOR), "Iron Door", $ironDoorBreakInfo)); - self::register("iron_trapdoor", new Trapdoor(new BID(Ids::IRON_TRAPDOOR), "Iron Trapdoor", $ironDoorBreakInfo)); + self::register("iron_door", fn(BID $id) => new Door($id, "Iron Door", $ironDoorBreakInfo)); + self::register("iron_trapdoor", fn(BID $id) => new Trapdoor($id, "Iron Trapdoor", $ironDoorBreakInfo)); $itemFrameInfo = new Info(new BreakInfo(0.25)); - self::register("item_frame", new ItemFrame(new BID(Ids::ITEM_FRAME, TileItemFrame::class), "Item Frame", $itemFrameInfo)); - self::register("glowing_item_frame", new ItemFrame(new BID(Ids::GLOWING_ITEM_FRAME, TileGlowingItemFrame::class), "Glow Item Frame", $itemFrameInfo)); + self::register("item_frame", fn(BID $id) => new ItemFrame($id, "Item Frame", $itemFrameInfo), TileItemFrame::class); + self::register("glowing_item_frame", fn(BID $id) => new ItemFrame($id, "Glow Item Frame", $itemFrameInfo), TileGlowingItemFrame::class); - self::register("jukebox", new Jukebox(new BID(Ids::JUKEBOX, TileJukebox::class), "Jukebox", new Info(BreakInfo::axe(0.8)))); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not - self::register("ladder", new Ladder(new BID(Ids::LADDER), "Ladder", new Info(BreakInfo::axe(0.4)))); + self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(0.8))), TileJukebox::class); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not + self::register("ladder", fn(BID $id) => new Ladder($id, "Ladder", new Info(BreakInfo::axe(0.4)))); $lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)); - self::register("lantern", new Lantern(new BID(Ids::LANTERN), "Lantern", $lanternBreakInfo, 15)); - self::register("soul_lantern", new Lantern(new BID(Ids::SOUL_LANTERN), "Soul Lantern", $lanternBreakInfo, 10)); - - self::register("lapis_lazuli", new Opaque(new BID(Ids::LAPIS_LAZULI), "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE)))); - self::register("lava", new Lava(new BID(Ids::LAVA), "Lava", new Info(BreakInfo::indestructible(500.0)))); - self::register("lectern", new Lectern(new BID(Ids::LECTERN, TileLectern::class), "Lectern", new Info(BreakInfo::axe(2.0)))); - self::register("lever", new Lever(new BID(Ids::LEVER), "Lever", new Info(new BreakInfo(0.5)))); - self::register("magma", new Magma(new BID(Ids::MAGMA), "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); - self::register("melon", new Melon(new BID(Ids::MELON), "Melon Block", new Info(BreakInfo::axe(1.0)))); - self::register("melon_stem", new MelonStem(new BID(Ids::MELON_STEM), "Melon Stem", new Info(BreakInfo::instant()))); - self::register("monster_spawner", new MonsterSpawner(new BID(Ids::MONSTER_SPAWNER, TileMonsterSpawner::class), "Monster Spawner", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); - self::register("mycelium", new Mycelium(new BID(Ids::MYCELIUM), "Mycelium", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); + self::register("lantern", fn(BID $id) => new Lantern($id, "Lantern", $lanternBreakInfo, 15)); + self::register("soul_lantern", fn(BID $id) => new Lantern($id, "Soul Lantern", $lanternBreakInfo, 10)); + + self::register("lapis_lazuli", fn(BID $id) => new Opaque($id, "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE)))); + self::register("lava", fn(BID $id) => new Lava($id, "Lava", new Info(BreakInfo::indestructible(500.0)))); + self::register("lectern", fn(BID $id) => new Lectern($id, "Lectern", new Info(BreakInfo::axe(2.0))), TileLectern::class); + self::register("lever", fn(BID $id) => new Lever($id, "Lever", new Info(new BreakInfo(0.5)))); + self::register("magma", fn(BID $id) => new Magma($id, "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("melon", fn(BID $id) => new Melon($id, "Melon Block", new Info(BreakInfo::axe(1.0)))); + self::register("melon_stem", fn(BID $id) => new MelonStem($id, "Melon Stem", new Info(BreakInfo::instant()))); + self::register("monster_spawner", fn(BID $id) => new MonsterSpawner($id, "Monster Spawner", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))), TileMonsterSpawner::class); + self::register("mycelium", fn(BID $id) => new Mycelium($id, "Mycelium", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); $netherBrickBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("nether_bricks", new Opaque(new BID(Ids::NETHER_BRICKS), "Nether Bricks", $netherBrickBreakInfo)); - self::register("red_nether_bricks", new Opaque(new BID(Ids::RED_NETHER_BRICKS), "Red Nether Bricks", $netherBrickBreakInfo)); - self::register("nether_brick_fence", new Fence(new BID(Ids::NETHER_BRICK_FENCE), "Nether Brick Fence", $netherBrickBreakInfo)); - self::register("nether_brick_stairs", new Stair(new BID(Ids::NETHER_BRICK_STAIRS), "Nether Brick Stairs", $netherBrickBreakInfo)); - self::register("red_nether_brick_stairs", new Stair(new BID(Ids::RED_NETHER_BRICK_STAIRS), "Red Nether Brick Stairs", $netherBrickBreakInfo)); - self::register("chiseled_nether_bricks", new Opaque(new BID(Ids::CHISELED_NETHER_BRICKS), "Chiseled Nether Bricks", $netherBrickBreakInfo)); - self::register("cracked_nether_bricks", new Opaque(new BID(Ids::CRACKED_NETHER_BRICKS), "Cracked Nether Bricks", $netherBrickBreakInfo)); - - self::register("nether_portal", new NetherPortal(new BID(Ids::NETHER_PORTAL), "Nether Portal", new Info(BreakInfo::indestructible(0.0)))); - self::register("nether_reactor_core", new NetherReactor(new BID(Ids::NETHER_REACTOR_CORE), "Nether Reactor Core", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); - self::register("nether_wart_block", new Opaque(new BID(Ids::NETHER_WART_BLOCK), "Nether Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); - self::register("nether_wart", new NetherWartPlant(new BID(Ids::NETHER_WART), "Nether Wart", new Info(BreakInfo::instant()))); - self::register("netherrack", new Netherrack(new BID(Ids::NETHERRACK), "Netherrack", new Info(BreakInfo::pickaxe(0.4, ToolTier::WOOD)))); - self::register("note_block", new Note(new BID(Ids::NOTE_BLOCK, TileNote::class), "Note Block", new Info(BreakInfo::axe(0.8)))); - self::register("obsidian", new Opaque(new BID(Ids::OBSIDIAN), "Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in PC */, ToolTier::DIAMOND, 6000.0)))); - self::register("packed_ice", new PackedIce(new BID(Ids::PACKED_ICE), "Packed Ice", new Info(BreakInfo::pickaxe(0.5)))); - self::register("podzol", new Podzol(new BID(Ids::PODZOL), "Podzol", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); - self::register("potatoes", new Potato(new BID(Ids::POTATOES), "Potato Block", new Info(BreakInfo::instant()))); - self::register("powered_rail", new PoweredRail(new BID(Ids::POWERED_RAIL), "Powered Rail", $railBreakInfo)); + self::register("nether_bricks", fn(BID $id) => new Opaque($id, "Nether Bricks", $netherBrickBreakInfo)); + self::register("red_nether_bricks", fn(BID $id) => new Opaque($id, "Red Nether Bricks", $netherBrickBreakInfo)); + self::register("nether_brick_fence", fn(BID $id) => new Fence($id, "Nether Brick Fence", $netherBrickBreakInfo)); + self::register("nether_brick_stairs", fn(BID $id) => new Stair($id, "Nether Brick Stairs", $netherBrickBreakInfo)); + self::register("red_nether_brick_stairs", fn(BID $id) => new Stair($id, "Red Nether Brick Stairs", $netherBrickBreakInfo)); + self::register("chiseled_nether_bricks", fn(BID $id) => new Opaque($id, "Chiseled Nether Bricks", $netherBrickBreakInfo)); + self::register("cracked_nether_bricks", fn(BID $id) => new Opaque($id, "Cracked Nether Bricks", $netherBrickBreakInfo)); + + self::register("nether_portal", fn(BID $id) => new NetherPortal($id, "Nether Portal", new Info(BreakInfo::indestructible(0.0)))); + self::register("nether_reactor_core", fn(BID $id) => new NetherReactor($id, "Nether Reactor Core", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); + self::register("nether_wart_block", fn(BID $id) => new Opaque($id, "Nether Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); + self::register("nether_wart", fn(BID $id) => new NetherWartPlant($id, "Nether Wart", new Info(BreakInfo::instant()))); + self::register("netherrack", fn(BID $id) => new Netherrack($id, "Netherrack", new Info(BreakInfo::pickaxe(0.4, ToolTier::WOOD)))); + self::register("note_block", fn(BID $id) => new Note($id, "Note Block", new Info(BreakInfo::axe(0.8))), TileNote::class); + self::register("obsidian", fn(BID $id) => new Opaque($id, "Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in PC */, ToolTier::DIAMOND, 6000.0)))); + self::register("packed_ice", fn(BID $id) => new PackedIce($id, "Packed Ice", new Info(BreakInfo::pickaxe(0.5)))); + self::register("podzol", fn(BID $id) => new Podzol($id, "Podzol", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); + self::register("potatoes", fn(BID $id) => new Potato($id, "Potato Block", new Info(BreakInfo::instant()))); + self::register("powered_rail", fn(BID $id) => new PoweredRail($id, "Powered Rail", $railBreakInfo)); $prismarineBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("prismarine", new Opaque(new BID(Ids::PRISMARINE), "Prismarine", $prismarineBreakInfo)); - self::register("dark_prismarine", new Opaque(new BID(Ids::DARK_PRISMARINE), "Dark Prismarine", $prismarineBreakInfo)); - self::register("prismarine_bricks", new Opaque(new BID(Ids::PRISMARINE_BRICKS), "Prismarine Bricks", $prismarineBreakInfo)); - self::register("prismarine_bricks_stairs", new Stair(new BID(Ids::PRISMARINE_BRICKS_STAIRS), "Prismarine Bricks Stairs", $prismarineBreakInfo)); - self::register("dark_prismarine_stairs", new Stair(new BID(Ids::DARK_PRISMARINE_STAIRS), "Dark Prismarine Stairs", $prismarineBreakInfo)); - self::register("prismarine_stairs", new Stair(new BID(Ids::PRISMARINE_STAIRS), "Prismarine Stairs", $prismarineBreakInfo)); + self::register("prismarine", fn(BID $id) => new Opaque($id, "Prismarine", $prismarineBreakInfo)); + self::register("dark_prismarine", fn(BID $id) => new Opaque($id, "Dark Prismarine", $prismarineBreakInfo)); + self::register("prismarine_bricks", fn(BID $id) => new Opaque($id, "Prismarine Bricks", $prismarineBreakInfo)); + self::register("prismarine_bricks_stairs", fn(BID $id) => new Stair($id, "Prismarine Bricks Stairs", $prismarineBreakInfo)); + self::register("dark_prismarine_stairs", fn(BID $id) => new Stair($id, "Dark Prismarine Stairs", $prismarineBreakInfo)); + self::register("prismarine_stairs", fn(BID $id) => new Stair($id, "Prismarine Stairs", $prismarineBreakInfo)); $pumpkinBreakInfo = new Info(BreakInfo::axe(1.0)); - self::register("pumpkin", new Pumpkin(new BID(Ids::PUMPKIN), "Pumpkin", $pumpkinBreakInfo)); - self::register("carved_pumpkin", new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN), "Carved Pumpkin", new Info(BreakInfo::axe(1.0), enchantmentTags: [EnchantmentTags::MASK]))); - self::register("lit_pumpkin", new LitPumpkin(new BID(Ids::LIT_PUMPKIN), "Jack o'Lantern", $pumpkinBreakInfo)); + self::register("pumpkin", fn(BID $id) => new Pumpkin($id, "Pumpkin", $pumpkinBreakInfo)); + self::register("carved_pumpkin", fn(BID $id) => new CarvedPumpkin($id, "Carved Pumpkin", new Info(BreakInfo::axe(1.0), enchantmentTags: [EnchantmentTags::MASK]))); + self::register("lit_pumpkin", fn(BID $id) => new LitPumpkin($id, "Jack o'Lantern", $pumpkinBreakInfo)); - self::register("pumpkin_stem", new PumpkinStem(new BID(Ids::PUMPKIN_STEM), "Pumpkin Stem", new Info(BreakInfo::instant()))); + self::register("pumpkin_stem", fn(BID $id) => new PumpkinStem($id, "Pumpkin Stem", new Info(BreakInfo::instant()))); $purpurBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("purpur", new Opaque(new BID(Ids::PURPUR), "Purpur Block", $purpurBreakInfo)); - self::register("purpur_pillar", new SimplePillar(new BID(Ids::PURPUR_PILLAR), "Purpur Pillar", $purpurBreakInfo)); - self::register("purpur_stairs", new Stair(new BID(Ids::PURPUR_STAIRS), "Purpur Stairs", $purpurBreakInfo)); + self::register("purpur", fn(BID $id) => new Opaque($id, "Purpur Block", $purpurBreakInfo)); + self::register("purpur_pillar", fn(BID $id) => new SimplePillar($id, "Purpur Pillar", $purpurBreakInfo)); + self::register("purpur_stairs", fn(BID $id) => new Stair($id, "Purpur Stairs", $purpurBreakInfo)); $quartzBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); - self::register("quartz", new Opaque(new BID(Ids::QUARTZ), "Quartz Block", $quartzBreakInfo)); - self::register("chiseled_quartz", new SimplePillar(new BID(Ids::CHISELED_QUARTZ), "Chiseled Quartz Block", $quartzBreakInfo)); - self::register("quartz_pillar", new SimplePillar(new BID(Ids::QUARTZ_PILLAR), "Quartz Pillar", $quartzBreakInfo)); - self::register("smooth_quartz", new Opaque(new BID(Ids::SMOOTH_QUARTZ), "Smooth Quartz Block", $quartzBreakInfo)); - self::register("quartz_bricks", new Opaque(new BID(Ids::QUARTZ_BRICKS), "Quartz Bricks", $quartzBreakInfo)); - - self::register("quartz_stairs", new Stair(new BID(Ids::QUARTZ_STAIRS), "Quartz Stairs", $quartzBreakInfo)); - self::register("smooth_quartz_stairs", new Stair(new BID(Ids::SMOOTH_QUARTZ_STAIRS), "Smooth Quartz Stairs", $quartzBreakInfo)); - - self::register("rail", new Rail(new BID(Ids::RAIL), "Rail", $railBreakInfo)); - self::register("red_mushroom", new RedMushroom(new BID(Ids::RED_MUSHROOM), "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); - self::register("redstone", new Redstone(new BID(Ids::REDSTONE), "Redstone Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); - self::register("redstone_comparator", new RedstoneComparator(new BID(Ids::REDSTONE_COMPARATOR, TileComparator::class), "Redstone Comparator", new Info(BreakInfo::instant()))); - self::register("redstone_lamp", new RedstoneLamp(new BID(Ids::REDSTONE_LAMP), "Redstone Lamp", new Info(new BreakInfo(0.3)))); - self::register("redstone_repeater", new RedstoneRepeater(new BID(Ids::REDSTONE_REPEATER), "Redstone Repeater", new Info(BreakInfo::instant()))); - self::register("redstone_torch", new RedstoneTorch(new BID(Ids::REDSTONE_TORCH), "Redstone Torch", new Info(BreakInfo::instant()))); - self::register("redstone_wire", new RedstoneWire(new BID(Ids::REDSTONE_WIRE), "Redstone", new Info(BreakInfo::instant()))); - self::register("reserved6", new Reserved6(new BID(Ids::RESERVED6), "reserved6", new Info(BreakInfo::instant()))); + self::register("quartz", fn(BID $id) => new Opaque($id, "Quartz Block", $quartzBreakInfo)); + self::register("chiseled_quartz", fn(BID $id) => new SimplePillar($id, "Chiseled Quartz Block", $quartzBreakInfo)); + self::register("quartz_pillar", fn(BID $id) => new SimplePillar($id, "Quartz Pillar", $quartzBreakInfo)); + self::register("smooth_quartz", fn(BID $id) => new Opaque($id, "Smooth Quartz Block", $quartzBreakInfo)); + self::register("quartz_bricks", fn(BID $id) => new Opaque($id, "Quartz Bricks", $quartzBreakInfo)); + + self::register("quartz_stairs", fn(BID $id) => new Stair($id, "Quartz Stairs", $quartzBreakInfo)); + self::register("smooth_quartz_stairs", fn(BID $id) => new Stair($id, "Smooth Quartz Stairs", $quartzBreakInfo)); + + self::register("rail", fn(BID $id) => new Rail($id, "Rail", $railBreakInfo)); + self::register("red_mushroom", fn(BID $id) => new RedMushroom($id, "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); + self::register("redstone", fn(BID $id) => new Redstone($id, "Redstone Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); + self::register("redstone_comparator", fn(BID $id) => new RedstoneComparator($id, "Redstone Comparator", new Info(BreakInfo::instant())), TileComparator::class); + self::register("redstone_lamp", fn(BID $id) => new RedstoneLamp($id, "Redstone Lamp", new Info(new BreakInfo(0.3)))); + self::register("redstone_repeater", fn(BID $id) => new RedstoneRepeater($id, "Redstone Repeater", new Info(BreakInfo::instant()))); + self::register("redstone_torch", fn(BID $id) => new RedstoneTorch($id, "Redstone Torch", new Info(BreakInfo::instant()))); + self::register("redstone_wire", fn(BID $id) => new RedstoneWire($id, "Redstone", new Info(BreakInfo::instant()))); + self::register("reserved6", fn(BID $id) => new Reserved6($id, "reserved6", new Info(BreakInfo::instant()))); $sandTypeInfo = new Info(BreakInfo::shovel(0.5), [Tags::SAND]); - self::register("sand", new Sand(new BID(Ids::SAND), "Sand", $sandTypeInfo)); - self::register("red_sand", new Sand(new BID(Ids::RED_SAND), "Red Sand", $sandTypeInfo)); - - self::register("sea_lantern", new SeaLantern(new BID(Ids::SEA_LANTERN), "Sea Lantern", new Info(new BreakInfo(0.3)))); - self::register("sea_pickle", new SeaPickle(new BID(Ids::SEA_PICKLE), "Sea Pickle", new Info(BreakInfo::instant()))); - self::register("mob_head", new MobHead(new BID(Ids::MOB_HEAD, TileMobHead::class), "Mob Head", new Info(new BreakInfo(1.0), enchantmentTags: [EnchantmentTags::MASK]))); - self::register("slime", new Slime(new BID(Ids::SLIME), "Slime Block", new Info(BreakInfo::instant()))); - self::register("snow", new Snow(new BID(Ids::SNOW), "Snow Block", new Info(BreakInfo::shovel(0.2, ToolTier::WOOD)))); - self::register("snow_layer", new SnowLayer(new BID(Ids::SNOW_LAYER), "Snow Layer", new Info(BreakInfo::shovel(0.1, ToolTier::WOOD)))); - self::register("soul_sand", new SoulSand(new BID(Ids::SOUL_SAND), "Soul Sand", new Info(BreakInfo::shovel(0.5)))); - self::register("sponge", new Sponge(new BID(Ids::SPONGE), "Sponge", new Info(new BreakInfo(0.6, ToolType::HOE)))); + self::register("sand", fn(BID $id) => new Sand($id, "Sand", $sandTypeInfo)); + self::register("red_sand", fn(BID $id) => new Sand($id, "Red Sand", $sandTypeInfo)); + + self::register("sea_lantern", fn(BID $id) => new SeaLantern($id, "Sea Lantern", new Info(new BreakInfo(0.3)))); + self::register("sea_pickle", fn(BID $id) => new SeaPickle($id, "Sea Pickle", new Info(BreakInfo::instant()))); + self::register("mob_head", fn(BID $id) => new MobHead($id, "Mob Head", new Info(new BreakInfo(1.0), enchantmentTags: [EnchantmentTags::MASK])), TileMobHead::class); + self::register("slime", fn(BID $id) => new Slime($id, "Slime Block", new Info(BreakInfo::instant()))); + self::register("snow", fn(BID $id) => new Snow($id, "Snow Block", new Info(BreakInfo::shovel(0.2, ToolTier::WOOD)))); + self::register("snow_layer", fn(BID $id) => new SnowLayer($id, "Snow Layer", new Info(BreakInfo::shovel(0.1, ToolTier::WOOD)))); + self::register("soul_sand", fn(BID $id) => new SoulSand($id, "Soul Sand", new Info(BreakInfo::shovel(0.5)))); + self::register("sponge", fn(BID $id) => new Sponge($id, "Sponge", new Info(new BreakInfo(0.6, ToolType::HOE)))); $shulkerBoxBreakInfo = new Info(BreakInfo::pickaxe(2)); - self::register("shulker_box", new ShulkerBox(new BID(Ids::SHULKER_BOX, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo)); + self::register("shulker_box", fn(BID $id) => new ShulkerBox($id, "Shulker Box", $shulkerBoxBreakInfo), TileShulkerBox::class); $stoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register( + $stone = self::register( "stone", - $stone = new class(new BID(Ids::STONE), "Stone", $stoneBreakInfo) extends Opaque{ + fn(BID $id) => new class($id, "Stone", $stoneBreakInfo) extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return [VanillaBlocks::COBBLESTONE()->asItem()]; } @@ -1066,110 +1090,110 @@ public function isAffectedBySilkTouch() : bool{ } } ); - self::register("andesite", new Opaque(new BID(Ids::ANDESITE), "Andesite", $stoneBreakInfo)); - self::register("diorite", new Opaque(new BID(Ids::DIORITE), "Diorite", $stoneBreakInfo)); - self::register("granite", new Opaque(new BID(Ids::GRANITE), "Granite", $stoneBreakInfo)); - self::register("polished_andesite", new Opaque(new BID(Ids::POLISHED_ANDESITE), "Polished Andesite", $stoneBreakInfo)); - self::register("polished_diorite", new Opaque(new BID(Ids::POLISHED_DIORITE), "Polished Diorite", $stoneBreakInfo)); - self::register("polished_granite", new Opaque(new BID(Ids::POLISHED_GRANITE), "Polished Granite", $stoneBreakInfo)); - - self::register("stone_bricks", $stoneBrick = new Opaque(new BID(Ids::STONE_BRICKS), "Stone Bricks", $stoneBreakInfo)); - self::register("mossy_stone_bricks", $mossyStoneBrick = new Opaque(new BID(Ids::MOSSY_STONE_BRICKS), "Mossy Stone Bricks", $stoneBreakInfo)); - self::register("cracked_stone_bricks", $crackedStoneBrick = new Opaque(new BID(Ids::CRACKED_STONE_BRICKS), "Cracked Stone Bricks", $stoneBreakInfo)); - self::register("chiseled_stone_bricks", $chiseledStoneBrick = new Opaque(new BID(Ids::CHISELED_STONE_BRICKS), "Chiseled Stone Bricks", $stoneBreakInfo)); + self::register("andesite", fn(BID $id) => new Opaque($id, "Andesite", $stoneBreakInfo)); + self::register("diorite", fn(BID $id) => new Opaque($id, "Diorite", $stoneBreakInfo)); + self::register("granite", fn(BID $id) => new Opaque($id, "Granite", $stoneBreakInfo)); + self::register("polished_andesite", fn(BID $id) => new Opaque($id, "Polished Andesite", $stoneBreakInfo)); + self::register("polished_diorite", fn(BID $id) => new Opaque($id, "Polished Diorite", $stoneBreakInfo)); + self::register("polished_granite", fn(BID $id) => new Opaque($id, "Polished Granite", $stoneBreakInfo)); + + $stoneBrick = self::register("stone_bricks", fn(BID $id) => new Opaque($id, "Stone Bricks", $stoneBreakInfo)); + $mossyStoneBrick = self::register("mossy_stone_bricks", fn(BID $id) => new Opaque($id, "Mossy Stone Bricks", $stoneBreakInfo)); + $crackedStoneBrick = self::register("cracked_stone_bricks", fn(BID $id) => new Opaque($id, "Cracked Stone Bricks", $stoneBreakInfo)); + $chiseledStoneBrick = self::register("chiseled_stone_bricks", fn(BID $id) => new Opaque($id, "Chiseled Stone Bricks", $stoneBreakInfo)); $infestedStoneBreakInfo = new Info(BreakInfo::pickaxe(0.75)); - self::register("infested_stone", new InfestedStone(new BID(Ids::INFESTED_STONE), "Infested Stone", $infestedStoneBreakInfo, $stone)); - self::register("infested_stone_brick", new InfestedStone(new BID(Ids::INFESTED_STONE_BRICK), "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick)); - self::register("infested_cobblestone", new InfestedStone(new BID(Ids::INFESTED_COBBLESTONE), "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone)); - self::register("infested_mossy_stone_brick", new InfestedStone(new BID(Ids::INFESTED_MOSSY_STONE_BRICK), "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick)); - self::register("infested_cracked_stone_brick", new InfestedStone(new BID(Ids::INFESTED_CRACKED_STONE_BRICK), "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick)); - self::register("infested_chiseled_stone_brick", new InfestedStone(new BID(Ids::INFESTED_CHISELED_STONE_BRICK), "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick)); - - self::register("stone_stairs", new Stair(new BID(Ids::STONE_STAIRS), "Stone Stairs", $stoneBreakInfo)); - self::register("smooth_stone", new Opaque(new BID(Ids::SMOOTH_STONE), "Smooth Stone", $stoneBreakInfo)); - self::register("andesite_stairs", new Stair(new BID(Ids::ANDESITE_STAIRS), "Andesite Stairs", $stoneBreakInfo)); - self::register("diorite_stairs", new Stair(new BID(Ids::DIORITE_STAIRS), "Diorite Stairs", $stoneBreakInfo)); - self::register("granite_stairs", new Stair(new BID(Ids::GRANITE_STAIRS), "Granite Stairs", $stoneBreakInfo)); - self::register("polished_andesite_stairs", new Stair(new BID(Ids::POLISHED_ANDESITE_STAIRS), "Polished Andesite Stairs", $stoneBreakInfo)); - self::register("polished_diorite_stairs", new Stair(new BID(Ids::POLISHED_DIORITE_STAIRS), "Polished Diorite Stairs", $stoneBreakInfo)); - self::register("polished_granite_stairs", new Stair(new BID(Ids::POLISHED_GRANITE_STAIRS), "Polished Granite Stairs", $stoneBreakInfo)); - self::register("stone_brick_stairs", new Stair(new BID(Ids::STONE_BRICK_STAIRS), "Stone Brick Stairs", $stoneBreakInfo)); - self::register("mossy_stone_brick_stairs", new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS), "Mossy Stone Brick Stairs", $stoneBreakInfo)); - self::register("stone_button", new StoneButton(new BID(Ids::STONE_BUTTON), "Stone Button", new Info(BreakInfo::pickaxe(0.5)))); - self::register("stonecutter", new Stonecutter(new BID(Ids::STONECUTTER), "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); - self::register("stone_pressure_plate", new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE), "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("infested_stone", fn(BID $id) => new InfestedStone($id, "Infested Stone", $infestedStoneBreakInfo, $stone)); + self::register("infested_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick)); + self::register("infested_cobblestone", fn(BID $id) => new InfestedStone($id, "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone)); + self::register("infested_mossy_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick)); + self::register("infested_cracked_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick)); + self::register("infested_chiseled_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick)); + + self::register("stone_stairs", fn(BID $id) => new Stair($id, "Stone Stairs", $stoneBreakInfo)); + self::register("smooth_stone", fn(BID $id) => new Opaque($id, "Smooth Stone", $stoneBreakInfo)); + self::register("andesite_stairs", fn(BID $id) => new Stair($id, "Andesite Stairs", $stoneBreakInfo)); + self::register("diorite_stairs", fn(BID $id) => new Stair($id, "Diorite Stairs", $stoneBreakInfo)); + self::register("granite_stairs", fn(BID $id) => new Stair($id, "Granite Stairs", $stoneBreakInfo)); + self::register("polished_andesite_stairs", fn(BID $id) => new Stair($id, "Polished Andesite Stairs", $stoneBreakInfo)); + self::register("polished_diorite_stairs", fn(BID $id) => new Stair($id, "Polished Diorite Stairs", $stoneBreakInfo)); + self::register("polished_granite_stairs", fn(BID $id) => new Stair($id, "Polished Granite Stairs", $stoneBreakInfo)); + self::register("stone_brick_stairs", fn(BID $id) => new Stair($id, "Stone Brick Stairs", $stoneBreakInfo)); + self::register("mossy_stone_brick_stairs", fn(BID $id) => new Stair($id, "Mossy Stone Brick Stairs", $stoneBreakInfo)); + self::register("stone_button", fn(BID $id) => new StoneButton($id, "Stone Button", new Info(BreakInfo::pickaxe(0.5)))); + self::register("stonecutter", fn(BID $id) => new Stonecutter($id, "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); + self::register("stone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); //TODO: in the future this won't be the same for all the types $stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("brick_slab", new Slab(new BID(Ids::BRICK_SLAB), "Brick", $stoneSlabBreakInfo)); - self::register("cobblestone_slab", new Slab(new BID(Ids::COBBLESTONE_SLAB), "Cobblestone", $stoneSlabBreakInfo)); - self::register("fake_wooden_slab", new Slab(new BID(Ids::FAKE_WOODEN_SLAB), "Fake Wooden", $stoneSlabBreakInfo)); - self::register("nether_brick_slab", new Slab(new BID(Ids::NETHER_BRICK_SLAB), "Nether Brick", $stoneSlabBreakInfo)); - self::register("quartz_slab", new Slab(new BID(Ids::QUARTZ_SLAB), "Quartz", $stoneSlabBreakInfo)); - self::register("sandstone_slab", new Slab(new BID(Ids::SANDSTONE_SLAB), "Sandstone", $stoneSlabBreakInfo)); - self::register("smooth_stone_slab", new Slab(new BID(Ids::SMOOTH_STONE_SLAB), "Smooth Stone", $stoneSlabBreakInfo)); - self::register("stone_brick_slab", new Slab(new BID(Ids::STONE_BRICK_SLAB), "Stone Brick", $stoneSlabBreakInfo)); - self::register("dark_prismarine_slab", new Slab(new BID(Ids::DARK_PRISMARINE_SLAB), "Dark Prismarine", $stoneSlabBreakInfo)); - self::register("mossy_cobblestone_slab", new Slab(new BID(Ids::MOSSY_COBBLESTONE_SLAB), "Mossy Cobblestone", $stoneSlabBreakInfo)); - self::register("prismarine_slab", new Slab(new BID(Ids::PRISMARINE_SLAB), "Prismarine", $stoneSlabBreakInfo)); - self::register("prismarine_bricks_slab", new Slab(new BID(Ids::PRISMARINE_BRICKS_SLAB), "Prismarine Bricks", $stoneSlabBreakInfo)); - self::register("purpur_slab", new Slab(new BID(Ids::PURPUR_SLAB), "Purpur", $stoneSlabBreakInfo)); - self::register("red_nether_brick_slab", new Slab(new BID(Ids::RED_NETHER_BRICK_SLAB), "Red Nether Brick", $stoneSlabBreakInfo)); - self::register("red_sandstone_slab", new Slab(new BID(Ids::RED_SANDSTONE_SLAB), "Red Sandstone", $stoneSlabBreakInfo)); - self::register("smooth_sandstone_slab", new Slab(new BID(Ids::SMOOTH_SANDSTONE_SLAB), "Smooth Sandstone", $stoneSlabBreakInfo)); - self::register("andesite_slab", new Slab(new BID(Ids::ANDESITE_SLAB), "Andesite", $stoneSlabBreakInfo)); - self::register("diorite_slab", new Slab(new BID(Ids::DIORITE_SLAB), "Diorite", $stoneSlabBreakInfo)); - self::register("end_stone_brick_slab", new Slab(new BID(Ids::END_STONE_BRICK_SLAB), "End Stone Brick", $stoneSlabBreakInfo)); - self::register("granite_slab", new Slab(new BID(Ids::GRANITE_SLAB), "Granite", $stoneSlabBreakInfo)); - self::register("polished_andesite_slab", new Slab(new BID(Ids::POLISHED_ANDESITE_SLAB), "Polished Andesite", $stoneSlabBreakInfo)); - self::register("polished_diorite_slab", new Slab(new BID(Ids::POLISHED_DIORITE_SLAB), "Polished Diorite", $stoneSlabBreakInfo)); - self::register("polished_granite_slab", new Slab(new BID(Ids::POLISHED_GRANITE_SLAB), "Polished Granite", $stoneSlabBreakInfo)); - self::register("smooth_red_sandstone_slab", new Slab(new BID(Ids::SMOOTH_RED_SANDSTONE_SLAB), "Smooth Red Sandstone", $stoneSlabBreakInfo)); - self::register("cut_red_sandstone_slab", new Slab(new BID(Ids::CUT_RED_SANDSTONE_SLAB), "Cut Red Sandstone", $stoneSlabBreakInfo)); - self::register("cut_sandstone_slab", new Slab(new BID(Ids::CUT_SANDSTONE_SLAB), "Cut Sandstone", $stoneSlabBreakInfo)); - self::register("mossy_stone_brick_slab", new Slab(new BID(Ids::MOSSY_STONE_BRICK_SLAB), "Mossy Stone Brick", $stoneSlabBreakInfo)); - self::register("smooth_quartz_slab", new Slab(new BID(Ids::SMOOTH_QUARTZ_SLAB), "Smooth Quartz", $stoneSlabBreakInfo)); - self::register("stone_slab", new Slab(new BID(Ids::STONE_SLAB), "Stone", $stoneSlabBreakInfo)); - - self::register("legacy_stonecutter", new Opaque(new BID(Ids::LEGACY_STONECUTTER), "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)))); - self::register("sugarcane", new Sugarcane(new BID(Ids::SUGARCANE), "Sugarcane", new Info(BreakInfo::instant()))); - self::register("sweet_berry_bush", new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH), "Sweet Berry Bush", new Info(BreakInfo::instant()))); - self::register("tnt", new TNT(new BID(Ids::TNT), "TNT", new Info(BreakInfo::instant()))); - self::register("fern", new TallGrass(new BID(Ids::FERN), "Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); - self::register("tall_grass", new TallGrass(new BID(Ids::TALL_GRASS), "Tall Grass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - - self::register("blue_torch", new Torch(new BID(Ids::BLUE_TORCH), "Blue Torch", new Info(BreakInfo::instant()))); - self::register("purple_torch", new Torch(new BID(Ids::PURPLE_TORCH), "Purple Torch", new Info(BreakInfo::instant()))); - self::register("red_torch", new Torch(new BID(Ids::RED_TORCH), "Red Torch", new Info(BreakInfo::instant()))); - self::register("green_torch", new Torch(new BID(Ids::GREEN_TORCH), "Green Torch", new Info(BreakInfo::instant()))); - self::register("torch", new Torch(new BID(Ids::TORCH), "Torch", new Info(BreakInfo::instant()))); - - self::register("trapped_chest", new TrappedChest(new BID(Ids::TRAPPED_CHEST, TileChest::class), "Trapped Chest", $chestBreakInfo)); - self::register("tripwire", new Tripwire(new BID(Ids::TRIPWIRE), "Tripwire", new Info(BreakInfo::instant()))); - self::register("tripwire_hook", new TripwireHook(new BID(Ids::TRIPWIRE_HOOK), "Tripwire Hook", new Info(BreakInfo::instant()))); - self::register("underwater_torch", new UnderwaterTorch(new BID(Ids::UNDERWATER_TORCH), "Underwater Torch", new Info(BreakInfo::instant()))); - self::register("vines", new Vine(new BID(Ids::VINES), "Vines", new Info(BreakInfo::axe(0.2)))); - self::register("water", new Water(new BID(Ids::WATER), "Water", new Info(BreakInfo::indestructible(500.0)))); - self::register("lily_pad", new WaterLily(new BID(Ids::LILY_PAD), "Lily Pad", new Info(BreakInfo::instant()))); + self::register("brick_slab", fn(BID $id) => new Slab($id, "Brick", $stoneSlabBreakInfo)); + self::register("cobblestone_slab", fn(BID $id) => new Slab($id, "Cobblestone", $stoneSlabBreakInfo)); + self::register("fake_wooden_slab", fn(BID $id) => new Slab($id, "Fake Wooden", $stoneSlabBreakInfo)); + self::register("nether_brick_slab", fn(BID $id) => new Slab($id, "Nether Brick", $stoneSlabBreakInfo)); + self::register("quartz_slab", fn(BID $id) => new Slab($id, "Quartz", $stoneSlabBreakInfo)); + self::register("sandstone_slab", fn(BID $id) => new Slab($id, "Sandstone", $stoneSlabBreakInfo)); + self::register("smooth_stone_slab", fn(BID $id) => new Slab($id, "Smooth Stone", $stoneSlabBreakInfo)); + self::register("stone_brick_slab", fn(BID $id) => new Slab($id, "Stone Brick", $stoneSlabBreakInfo)); + self::register("dark_prismarine_slab", fn(BID $id) => new Slab($id, "Dark Prismarine", $stoneSlabBreakInfo)); + self::register("mossy_cobblestone_slab", fn(BID $id) => new Slab($id, "Mossy Cobblestone", $stoneSlabBreakInfo)); + self::register("prismarine_slab", fn(BID $id) => new Slab($id, "Prismarine", $stoneSlabBreakInfo)); + self::register("prismarine_bricks_slab", fn(BID $id) => new Slab($id, "Prismarine Bricks", $stoneSlabBreakInfo)); + self::register("purpur_slab", fn(BID $id) => new Slab($id, "Purpur", $stoneSlabBreakInfo)); + self::register("red_nether_brick_slab", fn(BID $id) => new Slab($id, "Red Nether Brick", $stoneSlabBreakInfo)); + self::register("red_sandstone_slab", fn(BID $id) => new Slab($id, "Red Sandstone", $stoneSlabBreakInfo)); + self::register("smooth_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Sandstone", $stoneSlabBreakInfo)); + self::register("andesite_slab", fn(BID $id) => new Slab($id, "Andesite", $stoneSlabBreakInfo)); + self::register("diorite_slab", fn(BID $id) => new Slab($id, "Diorite", $stoneSlabBreakInfo)); + self::register("end_stone_brick_slab", fn(BID $id) => new Slab($id, "End Stone Brick", $stoneSlabBreakInfo)); + self::register("granite_slab", fn(BID $id) => new Slab($id, "Granite", $stoneSlabBreakInfo)); + self::register("polished_andesite_slab", fn(BID $id) => new Slab($id, "Polished Andesite", $stoneSlabBreakInfo)); + self::register("polished_diorite_slab", fn(BID $id) => new Slab($id, "Polished Diorite", $stoneSlabBreakInfo)); + self::register("polished_granite_slab", fn(BID $id) => new Slab($id, "Polished Granite", $stoneSlabBreakInfo)); + self::register("smooth_red_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Red Sandstone", $stoneSlabBreakInfo)); + self::register("cut_red_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Red Sandstone", $stoneSlabBreakInfo)); + self::register("cut_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Sandstone", $stoneSlabBreakInfo)); + self::register("mossy_stone_brick_slab", fn(BID $id) => new Slab($id, "Mossy Stone Brick", $stoneSlabBreakInfo)); + self::register("smooth_quartz_slab", fn(BID $id) => new Slab($id, "Smooth Quartz", $stoneSlabBreakInfo)); + self::register("stone_slab", fn(BID $id) => new Slab($id, "Stone", $stoneSlabBreakInfo)); + + self::register("legacy_stonecutter", fn(BID $id) => new Opaque($id, "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)))); + self::register("sugarcane", fn(BID $id) => new Sugarcane($id, "Sugarcane", new Info(BreakInfo::instant()))); + self::register("sweet_berry_bush", fn(BID $id) => new SweetBerryBush($id, "Sweet Berry Bush", new Info(BreakInfo::instant()))); + self::register("tnt", fn(BID $id) => new TNT($id, "TNT", new Info(BreakInfo::instant()))); + self::register("fern", fn(BID $id) => new TallGrass($id, "Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); + self::register("tall_grass", fn(BID $id) => new TallGrass($id, "Tall Grass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + + self::register("blue_torch", fn(BID $id) => new Torch($id, "Blue Torch", new Info(BreakInfo::instant()))); + self::register("purple_torch", fn(BID $id) => new Torch($id, "Purple Torch", new Info(BreakInfo::instant()))); + self::register("red_torch", fn(BID $id) => new Torch($id, "Red Torch", new Info(BreakInfo::instant()))); + self::register("green_torch", fn(BID $id) => new Torch($id, "Green Torch", new Info(BreakInfo::instant()))); + self::register("torch", fn(BID $id) => new Torch($id, "Torch", new Info(BreakInfo::instant()))); + + self::register("trapped_chest", fn(BID $id) => new TrappedChest($id, "Trapped Chest", $chestBreakInfo), TileChest::class); + self::register("tripwire", fn(BID $id) => new Tripwire($id, "Tripwire", new Info(BreakInfo::instant()))); + self::register("tripwire_hook", fn(BID $id) => new TripwireHook($id, "Tripwire Hook", new Info(BreakInfo::instant()))); + self::register("underwater_torch", fn(BID $id) => new UnderwaterTorch($id, "Underwater Torch", new Info(BreakInfo::instant()))); + self::register("vines", fn(BID $id) => new Vine($id, "Vines", new Info(BreakInfo::axe(0.2)))); + self::register("water", fn(BID $id) => new Water($id, "Water", new Info(BreakInfo::indestructible(500.0)))); + self::register("lily_pad", fn(BID $id) => new WaterLily($id, "Lily Pad", new Info(BreakInfo::instant()))); $weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)); - self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy( - new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY), + self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlateHeavy( + $id, "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo, deactivationDelayTicks: 10, signalStrengthFactor: 0.1 )); - self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight( - new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT), + self::register("weighted_pressure_plate_light", fn(BID $id) => new WeightedPressurePlateLight( + $id, "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo, deactivationDelayTicks: 10, signalStrengthFactor: 1.0 )); - self::register("wheat", new Wheat(new BID(Ids::WHEAT), "Wheat Block", new Info(BreakInfo::instant()))); + self::register("wheat", fn(BID $id) => new Wheat($id, "Wheat Block", new Info(BreakInfo::instant()))); $leavesBreakInfo = new Info(new class(0.2, ToolType::HOE) extends BreakInfo{ public function getBreakTime(Item $item) : float{ @@ -1183,39 +1207,39 @@ public function getBreakTime(Item $item) : float{ foreach(SaplingType::cases() as $saplingType){ $name = $saplingType->getDisplayName(); - self::register(strtolower($saplingType->name) . "_sapling", new Sapling(WoodLikeBlockIdHelper::getSaplingIdentifier($saplingType), $name . " Sapling", $saplingTypeInfo, $saplingType)); + self::register(strtolower($saplingType->name) . "_sapling", fn(BID $id) => new Sapling($id, $name . " Sapling", $saplingTypeInfo, $saplingType)); } foreach(LeavesType::cases() as $leavesType){ $name = $leavesType->getDisplayName(); - self::register(strtolower($leavesType->name) . "_leaves", new Leaves(WoodLikeBlockIdHelper::getLeavesIdentifier($leavesType), $name . " Leaves", $leavesBreakInfo, $leavesType)); + self::register(strtolower($leavesType->name) . "_leaves", fn(BID $id) => new Leaves($id, $name . " Leaves", $leavesBreakInfo, $leavesType)); } $sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); - self::register("red_sandstone_stairs", new Stair(new BID(Ids::RED_SANDSTONE_STAIRS), "Red Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone_stairs", new Stair(new BID(Ids::SMOOTH_RED_SANDSTONE_STAIRS), "Smooth Red Sandstone Stairs", $sandstoneBreakInfo)); - self::register("red_sandstone", new Opaque(new BID(Ids::RED_SANDSTONE), "Red Sandstone", $sandstoneBreakInfo)); - self::register("chiseled_red_sandstone", new Opaque(new BID(Ids::CHISELED_RED_SANDSTONE), "Chiseled Red Sandstone", $sandstoneBreakInfo)); - self::register("cut_red_sandstone", new Opaque(new BID(Ids::CUT_RED_SANDSTONE), "Cut Red Sandstone", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone", new Opaque(new BID(Ids::SMOOTH_RED_SANDSTONE), "Smooth Red Sandstone", $sandstoneBreakInfo)); - - self::register("sandstone_stairs", new Stair(new BID(Ids::SANDSTONE_STAIRS), "Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_sandstone_stairs", new Stair(new BID(Ids::SMOOTH_SANDSTONE_STAIRS), "Smooth Sandstone Stairs", $sandstoneBreakInfo)); - self::register("sandstone", new Opaque(new BID(Ids::SANDSTONE), "Sandstone", $sandstoneBreakInfo)); - self::register("chiseled_sandstone", new Opaque(new BID(Ids::CHISELED_SANDSTONE), "Chiseled Sandstone", $sandstoneBreakInfo)); - self::register("cut_sandstone", new Opaque(new BID(Ids::CUT_SANDSTONE), "Cut Sandstone", $sandstoneBreakInfo)); - self::register("smooth_sandstone", new Opaque(new BID(Ids::SMOOTH_SANDSTONE), "Smooth Sandstone", $sandstoneBreakInfo)); - - self::register("glazed_terracotta", new GlazedTerracotta(new BID(Ids::GLAZED_TERRACOTTA), "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD)))); - self::register("dyed_shulker_box", new DyedShulkerBox(new BID(Ids::DYED_SHULKER_BOX, TileShulkerBox::class), "Dyed Shulker Box", $shulkerBoxBreakInfo)); - self::register("stained_glass", new StainedGlass(new BID(Ids::STAINED_GLASS), "Stained Glass", $glassBreakInfo)); - self::register("stained_glass_pane", new StainedGlassPane(new BID(Ids::STAINED_GLASS_PANE), "Stained Glass Pane", $glassBreakInfo)); - self::register("stained_clay", new StainedHardenedClay(new BID(Ids::STAINED_CLAY), "Stained Clay", $hardenedClayBreakInfo)); - self::register("stained_hardened_glass", new StainedHardenedGlass(new BID(Ids::STAINED_HARDENED_GLASS), "Stained Hardened Glass", $hardenedGlassBreakInfo)); - self::register("stained_hardened_glass_pane", new StainedHardenedGlassPane(new BID(Ids::STAINED_HARDENED_GLASS_PANE), "Stained Hardened Glass Pane", $hardenedGlassBreakInfo)); - self::register("carpet", new Carpet(new BID(Ids::CARPET), "Carpet", new Info(new BreakInfo(0.1)))); - self::register("concrete", new Concrete(new BID(Ids::CONCRETE), "Concrete", new Info(BreakInfo::pickaxe(1.8, ToolTier::WOOD)))); - self::register("concrete_powder", new ConcretePowder(new BID(Ids::CONCRETE_POWDER), "Concrete Powder", new Info(BreakInfo::shovel(0.5)))); - self::register("wool", new Wool(new BID(Ids::WOOL), "Wool", new Info(new class(0.8, ToolType::SHEARS) extends BreakInfo{ + self::register("red_sandstone_stairs", fn(BID $id) => new Stair($id, "Red Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Red Sandstone Stairs", $sandstoneBreakInfo)); + self::register("red_sandstone", fn(BID $id) => new Opaque($id, "Red Sandstone", $sandstoneBreakInfo)); + self::register("chiseled_red_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Red Sandstone", $sandstoneBreakInfo)); + self::register("cut_red_sandstone", fn(BID $id) => new Opaque($id, "Cut Red Sandstone", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone", fn(BID $id) => new Opaque($id, "Smooth Red Sandstone", $sandstoneBreakInfo)); + + self::register("sandstone_stairs", fn(BID $id) => new Stair($id, "Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Sandstone Stairs", $sandstoneBreakInfo)); + self::register("sandstone", fn(BID $id) => new Opaque($id, "Sandstone", $sandstoneBreakInfo)); + self::register("chiseled_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Sandstone", $sandstoneBreakInfo)); + self::register("cut_sandstone", fn(BID $id) => new Opaque($id, "Cut Sandstone", $sandstoneBreakInfo)); + self::register("smooth_sandstone", fn(BID $id) => new Opaque($id, "Smooth Sandstone", $sandstoneBreakInfo)); + + self::register("glazed_terracotta", fn(BID $id) => new GlazedTerracotta($id, "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD)))); + self::register("dyed_shulker_box", fn(BID $id) => new DyedShulkerBox($id, "Dyed Shulker Box", $shulkerBoxBreakInfo), TileShulkerBox::class); + self::register("stained_glass", fn(BID $id) => new StainedGlass($id, "Stained Glass", $glassBreakInfo)); + self::register("stained_glass_pane", fn(BID $id) => new StainedGlassPane($id, "Stained Glass Pane", $glassBreakInfo)); + self::register("stained_clay", fn(BID $id) => new StainedHardenedClay($id, "Stained Clay", $hardenedClayBreakInfo)); + self::register("stained_hardened_glass", fn(BID $id) => new StainedHardenedGlass($id, "Stained Hardened Glass", $hardenedGlassBreakInfo)); + self::register("stained_hardened_glass_pane", fn(BID $id) => new StainedHardenedGlassPane($id, "Stained Hardened Glass Pane", $hardenedGlassBreakInfo)); + self::register("carpet", fn(BID $id) => new Carpet($id, "Carpet", new Info(new BreakInfo(0.1)))); + self::register("concrete", fn(BID $id) => new Concrete($id, "Concrete", new Info(BreakInfo::pickaxe(1.8, ToolTier::WOOD)))); + self::register("concrete_powder", fn(BID $id) => new ConcretePowder($id, "Concrete Powder", new Info(BreakInfo::shovel(0.5)))); + self::register("wool", fn(BID $id) => new Wool($id, "Wool", new Info(new class(0.8, ToolType::SHEARS) extends BreakInfo{ public function getBreakTime(Item $item) : float{ $time = parent::getBreakTime($item); if($item->getBlockToolType() === ToolType::SHEARS){ @@ -1228,54 +1252,54 @@ public function getBreakTime(Item $item) : float{ //TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap $wallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("cobblestone_wall", new Wall(new BID(Ids::COBBLESTONE_WALL), "Cobblestone Wall", $wallBreakInfo)); - self::register("andesite_wall", new Wall(new BID(Ids::ANDESITE_WALL), "Andesite Wall", $wallBreakInfo)); - self::register("brick_wall", new Wall(new BID(Ids::BRICK_WALL), "Brick Wall", $wallBreakInfo)); - self::register("diorite_wall", new Wall(new BID(Ids::DIORITE_WALL), "Diorite Wall", $wallBreakInfo)); - self::register("end_stone_brick_wall", new Wall(new BID(Ids::END_STONE_BRICK_WALL), "End Stone Brick Wall", $wallBreakInfo)); - self::register("granite_wall", new Wall(new BID(Ids::GRANITE_WALL), "Granite Wall", $wallBreakInfo)); - self::register("mossy_stone_brick_wall", new Wall(new BID(Ids::MOSSY_STONE_BRICK_WALL), "Mossy Stone Brick Wall", $wallBreakInfo)); - self::register("mossy_cobblestone_wall", new Wall(new BID(Ids::MOSSY_COBBLESTONE_WALL), "Mossy Cobblestone Wall", $wallBreakInfo)); - self::register("nether_brick_wall", new Wall(new BID(Ids::NETHER_BRICK_WALL), "Nether Brick Wall", $wallBreakInfo)); - self::register("prismarine_wall", new Wall(new BID(Ids::PRISMARINE_WALL), "Prismarine Wall", $wallBreakInfo)); - self::register("red_nether_brick_wall", new Wall(new BID(Ids::RED_NETHER_BRICK_WALL), "Red Nether Brick Wall", $wallBreakInfo)); - self::register("red_sandstone_wall", new Wall(new BID(Ids::RED_SANDSTONE_WALL), "Red Sandstone Wall", $wallBreakInfo)); - self::register("sandstone_wall", new Wall(new BID(Ids::SANDSTONE_WALL), "Sandstone Wall", $wallBreakInfo)); - self::register("stone_brick_wall", new Wall(new BID(Ids::STONE_BRICK_WALL), "Stone Brick Wall", $wallBreakInfo)); + self::register("cobblestone_wall", fn(BID $id) => new Wall($id, "Cobblestone Wall", $wallBreakInfo)); + self::register("andesite_wall", fn(BID $id) => new Wall($id, "Andesite Wall", $wallBreakInfo)); + self::register("brick_wall", fn(BID $id) => new Wall($id, "Brick Wall", $wallBreakInfo)); + self::register("diorite_wall", fn(BID $id) => new Wall($id, "Diorite Wall", $wallBreakInfo)); + self::register("end_stone_brick_wall", fn(BID $id) => new Wall($id, "End Stone Brick Wall", $wallBreakInfo)); + self::register("granite_wall", fn(BID $id) => new Wall($id, "Granite Wall", $wallBreakInfo)); + self::register("mossy_stone_brick_wall", fn(BID $id) => new Wall($id, "Mossy Stone Brick Wall", $wallBreakInfo)); + self::register("mossy_cobblestone_wall", fn(BID $id) => new Wall($id, "Mossy Cobblestone Wall", $wallBreakInfo)); + self::register("nether_brick_wall", fn(BID $id) => new Wall($id, "Nether Brick Wall", $wallBreakInfo)); + self::register("prismarine_wall", fn(BID $id) => new Wall($id, "Prismarine Wall", $wallBreakInfo)); + self::register("red_nether_brick_wall", fn(BID $id) => new Wall($id, "Red Nether Brick Wall", $wallBreakInfo)); + self::register("red_sandstone_wall", fn(BID $id) => new Wall($id, "Red Sandstone Wall", $wallBreakInfo)); + self::register("sandstone_wall", fn(BID $id) => new Wall($id, "Sandstone Wall", $wallBreakInfo)); + self::register("stone_brick_wall", fn(BID $id) => new Wall($id, "Stone Brick Wall", $wallBreakInfo)); self::registerElements(); $chemistryTableBreakInfo = new Info(BreakInfo::pickaxe(2.5, ToolTier::WOOD)); - self::register("compound_creator", new ChemistryTable(new BID(Ids::COMPOUND_CREATOR), "Compound Creator", $chemistryTableBreakInfo)); - self::register("element_constructor", new ChemistryTable(new BID(Ids::ELEMENT_CONSTRUCTOR), "Element Constructor", $chemistryTableBreakInfo)); - self::register("lab_table", new ChemistryTable(new BID(Ids::LAB_TABLE), "Lab Table", $chemistryTableBreakInfo)); - self::register("material_reducer", new ChemistryTable(new BID(Ids::MATERIAL_REDUCER), "Material Reducer", $chemistryTableBreakInfo)); + self::register("compound_creator", fn(BID $id) => new ChemistryTable($id, "Compound Creator", $chemistryTableBreakInfo)); + self::register("element_constructor", fn(BID $id) => new ChemistryTable($id, "Element Constructor", $chemistryTableBreakInfo)); + self::register("lab_table", fn(BID $id) => new ChemistryTable($id, "Lab Table", $chemistryTableBreakInfo)); + self::register("material_reducer", fn(BID $id) => new ChemistryTable($id, "Material Reducer", $chemistryTableBreakInfo)); - self::register("chemical_heat", new ChemicalHeat(new BID(Ids::CHEMICAL_HEAT), "Heat Block", $chemistryTableBreakInfo)); + self::register("chemical_heat", fn(BID $id) => new ChemicalHeat($id, "Heat Block", $chemistryTableBreakInfo)); self::registerMushroomBlocks(); - self::register("coral", new Coral( - new BID(Ids::CORAL), + self::register("coral", fn(BID $id) => new Coral( + $id, "Coral", new Info(BreakInfo::instant()), )); - self::register("coral_fan", new FloorCoralFan( - new BID(Ids::CORAL_FAN), + self::register("coral_fan", fn(BID $id) => new FloorCoralFan( + $id, "Coral Fan", new Info(BreakInfo::instant()), )); - self::register("wall_coral_fan", new WallCoralFan( - new BID(Ids::WALL_CORAL_FAN), + self::register("wall_coral_fan", fn(BID $id) => new WallCoralFan( + $id, "Wall Coral Fan", new Info(BreakInfo::instant()), )); - self::register("mangrove_roots", new MangroveRoots(new BID(Ids::MANGROVE_ROOTS), "Mangrove Roots", new Info(BreakInfo::axe(0.7)))); - self::register("muddy_mangrove_roots", new SimplePillar(new BID(Ids::MUDDY_MANGROVE_ROOTS), "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD]))); - self::register("froglight", new Froglight(new BID(Ids::FROGLIGHT), "Froglight", new Info(new BreakInfo(0.3)))); - self::register("sculk", new Sculk(new BID(Ids::SCULK), "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE)))); - self::register("reinforced_deepslate", new class(new BID(Ids::REINFORCED_DEEPSLATE), "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 3600.0))) extends Opaque{ + self::register("mangrove_roots", fn(BID $id) => new MangroveRoots($id, "Mangrove Roots", new Info(BreakInfo::axe(0.7)))); + self::register("muddy_mangrove_roots", fn(BID $id) => new SimplePillar($id, "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD]))); + self::register("froglight", fn(BID $id) => new Froglight($id, "Froglight", new Info(new BreakInfo(0.3)))); + self::register("sculk", fn(BID $id) => new Sculk($id, "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE)))); + self::register("reinforced_deepslate", fn(BID $id) => new class($id, "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 3600.0))) extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return []; } @@ -1308,300 +1332,311 @@ private static function registerWoodenBlocks() : void{ $name = $woodType->getDisplayName(); $idName = fn(string $suffix) => strtolower($woodType->name) . "_" . $suffix; - self::register($idName(mb_strtolower($woodType->getStandardLogSuffix() ?? "log", 'US-ASCII')), new Wood(WoodLikeBlockIdHelper::getLogIdentifier($woodType), $name . " " . ($woodType->getStandardLogSuffix() ?? "Log"), $logBreakInfo, $woodType)); - self::register($idName(mb_strtolower($woodType->getAllSidedLogSuffix() ?? "wood", 'US-ASCII')), new Wood(WoodLikeBlockIdHelper::getAllSidedLogIdentifier($woodType), $name . " " . ($woodType->getAllSidedLogSuffix() ?? "Wood"), $logBreakInfo, $woodType)); - - self::register($idName("planks"), new Planks(WoodLikeBlockIdHelper::getPlanksIdentifier($woodType), $name . " Planks", $planksBreakInfo, $woodType)); - self::register($idName("fence"), new WoodenFence(WoodLikeBlockIdHelper::getFenceIdentifier($woodType), $name . " Fence", $planksBreakInfo, $woodType)); - self::register($idName("slab"), new WoodenSlab(WoodLikeBlockIdHelper::getSlabIdentifier($woodType), $name, $planksBreakInfo, $woodType)); - - self::register($idName("fence_gate"), new FenceGate(WoodLikeBlockIdHelper::getFenceGateIdentifier($woodType), $name . " Fence Gate", $planksBreakInfo, $woodType)); - self::register($idName("stairs"), new WoodenStairs(WoodLikeBlockIdHelper::getStairsIdentifier($woodType), $name . " Stairs", $planksBreakInfo, $woodType)); - self::register($idName("door"), new WoodenDoor(WoodLikeBlockIdHelper::getDoorIdentifier($woodType), $name . " Door", $woodenDoorBreakInfo, $woodType)); - - self::register($idName("button"), new WoodenButton(WoodLikeBlockIdHelper::getButtonIdentifier($woodType), $name . " Button", $woodenButtonBreakInfo, $woodType)); - self::register($idName("pressure_plate"), new WoodenPressurePlate(WoodLikeBlockIdHelper::getPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20)); - self::register($idName("trapdoor"), new WoodenTrapdoor(WoodLikeBlockIdHelper::getTrapdoorIdentifier($woodType), $name . " Trapdoor", $woodenDoorBreakInfo, $woodType)); - - [$floorSignId, $wallSignId, $signAsItem] = WoodLikeBlockIdHelper::getSignInfo($woodType); - self::register($idName("sign"), new FloorSign($floorSignId, $name . " Sign", $signBreakInfo, $woodType, $signAsItem)); - self::register($idName("wall_sign"), new WallSign($wallSignId, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem)); + self::register($idName(mb_strtolower($woodType->getStandardLogSuffix() ?? "log", 'US-ASCII')), fn(BID $id) => new Wood($id, $name . " " . ($woodType->getStandardLogSuffix() ?? "Log"), $logBreakInfo, $woodType)); + self::register($idName(mb_strtolower($woodType->getAllSidedLogSuffix() ?? "wood", 'US-ASCII')), fn(BID $id) => new Wood($id, $name . " " . ($woodType->getAllSidedLogSuffix() ?? "Wood"), $logBreakInfo, $woodType)); + + self::register($idName("planks"), fn(BID $id) => new Planks($id, $name . " Planks", $planksBreakInfo, $woodType)); + self::register($idName("fence"), fn(BID $id) => new WoodenFence($id, $name . " Fence", $planksBreakInfo, $woodType)); + self::register($idName("slab"), fn(BID $id) => new WoodenSlab($id, $name, $planksBreakInfo, $woodType)); + + self::register($idName("fence_gate"), fn(BID $id) => new FenceGate($id, $name . " Fence Gate", $planksBreakInfo, $woodType)); + self::register($idName("stairs"), fn(BID $id) => new WoodenStairs($id, $name . " Stairs", $planksBreakInfo, $woodType)); + self::register($idName("door"), fn(BID $id) => new WoodenDoor($id, $name . " Door", $woodenDoorBreakInfo, $woodType)); + + self::register($idName("button"), fn(BID $id) => new WoodenButton($id, $name . " Button", $woodenButtonBreakInfo, $woodType)); + self::register($idName("pressure_plate"), fn(BID $id) => new WoodenPressurePlate($id, $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20)); + self::register($idName("trapdoor"), fn(BID $id) => new WoodenTrapdoor($id, $name . " Trapdoor", $woodenDoorBreakInfo, $woodType)); + + $signAsItem = match($woodType){ + WoodType::OAK => VanillaItems::OAK_SIGN(...), + WoodType::SPRUCE => VanillaItems::SPRUCE_SIGN(...), + WoodType::BIRCH => VanillaItems::BIRCH_SIGN(...), + WoodType::JUNGLE => VanillaItems::JUNGLE_SIGN(...), + WoodType::ACACIA => VanillaItems::ACACIA_SIGN(...), + WoodType::DARK_OAK => VanillaItems::DARK_OAK_SIGN(...), + WoodType::MANGROVE => VanillaItems::MANGROVE_SIGN(...), + WoodType::CRIMSON => VanillaItems::CRIMSON_SIGN(...), + WoodType::WARPED => VanillaItems::WARPED_SIGN(...), + WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...), + }; + self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem)); + self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem)); } } private static function registerMushroomBlocks() : void{ $mushroomBlockBreakInfo = new Info(BreakInfo::axe(0.2)); - self::register("brown_mushroom_block", new BrownMushroomBlock(new BID(Ids::BROWN_MUSHROOM_BLOCK), "Brown Mushroom Block", $mushroomBlockBreakInfo)); - self::register("red_mushroom_block", new RedMushroomBlock(new BID(Ids::RED_MUSHROOM_BLOCK), "Red Mushroom Block", $mushroomBlockBreakInfo)); + self::register("brown_mushroom_block", fn(BID $id) => new BrownMushroomBlock($id, "Brown Mushroom Block", $mushroomBlockBreakInfo)); + self::register("red_mushroom_block", fn(BID $id) => new RedMushroomBlock($id, "Red Mushroom Block", $mushroomBlockBreakInfo)); //finally, the stems - self::register("mushroom_stem", new MushroomStem(new BID(Ids::MUSHROOM_STEM), "Mushroom Stem", $mushroomBlockBreakInfo)); - self::register("all_sided_mushroom_stem", new MushroomStem(new BID(Ids::ALL_SIDED_MUSHROOM_STEM), "All Sided Mushroom Stem", $mushroomBlockBreakInfo)); + self::register("mushroom_stem", fn(BID $id) => new MushroomStem($id, "Mushroom Stem", $mushroomBlockBreakInfo)); + self::register("all_sided_mushroom_stem", fn(BID $id) => new MushroomStem($id, "All Sided Mushroom Stem", $mushroomBlockBreakInfo)); } private static function registerElements() : void{ $instaBreak = new Info(BreakInfo::instant()); - self::register("element_zero", new Opaque(new BID(Ids::ELEMENT_ZERO), "???", $instaBreak)); - - $register = fn(string $name, int $id, string $displayName, string $symbol, int $atomicWeight, int $group) => - self::register("element_$name", new Element(new BID($id), $displayName, $instaBreak, $symbol, $atomicWeight, $group)); - - $register("hydrogen", Ids::ELEMENT_HYDROGEN, "Hydrogen", "h", 1, 5); - $register("helium", Ids::ELEMENT_HELIUM, "Helium", "he", 2, 7); - $register("lithium", Ids::ELEMENT_LITHIUM, "Lithium", "li", 3, 0); - $register("beryllium", Ids::ELEMENT_BERYLLIUM, "Beryllium", "be", 4, 1); - $register("boron", Ids::ELEMENT_BORON, "Boron", "b", 5, 4); - $register("carbon", Ids::ELEMENT_CARBON, "Carbon", "c", 6, 5); - $register("nitrogen", Ids::ELEMENT_NITROGEN, "Nitrogen", "n", 7, 5); - $register("oxygen", Ids::ELEMENT_OXYGEN, "Oxygen", "o", 8, 5); - $register("fluorine", Ids::ELEMENT_FLUORINE, "Fluorine", "f", 9, 6); - $register("neon", Ids::ELEMENT_NEON, "Neon", "ne", 10, 7); - $register("sodium", Ids::ELEMENT_SODIUM, "Sodium", "na", 11, 0); - $register("magnesium", Ids::ELEMENT_MAGNESIUM, "Magnesium", "mg", 12, 1); - $register("aluminum", Ids::ELEMENT_ALUMINUM, "Aluminum", "al", 13, 3); - $register("silicon", Ids::ELEMENT_SILICON, "Silicon", "si", 14, 4); - $register("phosphorus", Ids::ELEMENT_PHOSPHORUS, "Phosphorus", "p", 15, 5); - $register("sulfur", Ids::ELEMENT_SULFUR, "Sulfur", "s", 16, 5); - $register("chlorine", Ids::ELEMENT_CHLORINE, "Chlorine", "cl", 17, 6); - $register("argon", Ids::ELEMENT_ARGON, "Argon", "ar", 18, 7); - $register("potassium", Ids::ELEMENT_POTASSIUM, "Potassium", "k", 19, 0); - $register("calcium", Ids::ELEMENT_CALCIUM, "Calcium", "ca", 20, 1); - $register("scandium", Ids::ELEMENT_SCANDIUM, "Scandium", "sc", 21, 2); - $register("titanium", Ids::ELEMENT_TITANIUM, "Titanium", "ti", 22, 2); - $register("vanadium", Ids::ELEMENT_VANADIUM, "Vanadium", "v", 23, 2); - $register("chromium", Ids::ELEMENT_CHROMIUM, "Chromium", "cr", 24, 2); - $register("manganese", Ids::ELEMENT_MANGANESE, "Manganese", "mn", 25, 2); - $register("iron", Ids::ELEMENT_IRON, "Iron", "fe", 26, 2); - $register("cobalt", Ids::ELEMENT_COBALT, "Cobalt", "co", 27, 2); - $register("nickel", Ids::ELEMENT_NICKEL, "Nickel", "ni", 28, 2); - $register("copper", Ids::ELEMENT_COPPER, "Copper", "cu", 29, 2); - $register("zinc", Ids::ELEMENT_ZINC, "Zinc", "zn", 30, 2); - $register("gallium", Ids::ELEMENT_GALLIUM, "Gallium", "ga", 31, 3); - $register("germanium", Ids::ELEMENT_GERMANIUM, "Germanium", "ge", 32, 4); - $register("arsenic", Ids::ELEMENT_ARSENIC, "Arsenic", "as", 33, 4); - $register("selenium", Ids::ELEMENT_SELENIUM, "Selenium", "se", 34, 5); - $register("bromine", Ids::ELEMENT_BROMINE, "Bromine", "br", 35, 6); - $register("krypton", Ids::ELEMENT_KRYPTON, "Krypton", "kr", 36, 7); - $register("rubidium", Ids::ELEMENT_RUBIDIUM, "Rubidium", "rb", 37, 0); - $register("strontium", Ids::ELEMENT_STRONTIUM, "Strontium", "sr", 38, 1); - $register("yttrium", Ids::ELEMENT_YTTRIUM, "Yttrium", "y", 39, 2); - $register("zirconium", Ids::ELEMENT_ZIRCONIUM, "Zirconium", "zr", 40, 2); - $register("niobium", Ids::ELEMENT_NIOBIUM, "Niobium", "nb", 41, 2); - $register("molybdenum", Ids::ELEMENT_MOLYBDENUM, "Molybdenum", "mo", 42, 2); - $register("technetium", Ids::ELEMENT_TECHNETIUM, "Technetium", "tc", 43, 2); - $register("ruthenium", Ids::ELEMENT_RUTHENIUM, "Ruthenium", "ru", 44, 2); - $register("rhodium", Ids::ELEMENT_RHODIUM, "Rhodium", "rh", 45, 2); - $register("palladium", Ids::ELEMENT_PALLADIUM, "Palladium", "pd", 46, 2); - $register("silver", Ids::ELEMENT_SILVER, "Silver", "ag", 47, 2); - $register("cadmium", Ids::ELEMENT_CADMIUM, "Cadmium", "cd", 48, 2); - $register("indium", Ids::ELEMENT_INDIUM, "Indium", "in", 49, 3); - $register("tin", Ids::ELEMENT_TIN, "Tin", "sn", 50, 3); - $register("antimony", Ids::ELEMENT_ANTIMONY, "Antimony", "sb", 51, 4); - $register("tellurium", Ids::ELEMENT_TELLURIUM, "Tellurium", "te", 52, 4); - $register("iodine", Ids::ELEMENT_IODINE, "Iodine", "i", 53, 6); - $register("xenon", Ids::ELEMENT_XENON, "Xenon", "xe", 54, 7); - $register("cesium", Ids::ELEMENT_CESIUM, "Cesium", "cs", 55, 0); - $register("barium", Ids::ELEMENT_BARIUM, "Barium", "ba", 56, 1); - $register("lanthanum", Ids::ELEMENT_LANTHANUM, "Lanthanum", "la", 57, 8); - $register("cerium", Ids::ELEMENT_CERIUM, "Cerium", "ce", 58, 8); - $register("praseodymium", Ids::ELEMENT_PRASEODYMIUM, "Praseodymium", "pr", 59, 8); - $register("neodymium", Ids::ELEMENT_NEODYMIUM, "Neodymium", "nd", 60, 8); - $register("promethium", Ids::ELEMENT_PROMETHIUM, "Promethium", "pm", 61, 8); - $register("samarium", Ids::ELEMENT_SAMARIUM, "Samarium", "sm", 62, 8); - $register("europium", Ids::ELEMENT_EUROPIUM, "Europium", "eu", 63, 8); - $register("gadolinium", Ids::ELEMENT_GADOLINIUM, "Gadolinium", "gd", 64, 8); - $register("terbium", Ids::ELEMENT_TERBIUM, "Terbium", "tb", 65, 8); - $register("dysprosium", Ids::ELEMENT_DYSPROSIUM, "Dysprosium", "dy", 66, 8); - $register("holmium", Ids::ELEMENT_HOLMIUM, "Holmium", "ho", 67, 8); - $register("erbium", Ids::ELEMENT_ERBIUM, "Erbium", "er", 68, 8); - $register("thulium", Ids::ELEMENT_THULIUM, "Thulium", "tm", 69, 8); - $register("ytterbium", Ids::ELEMENT_YTTERBIUM, "Ytterbium", "yb", 70, 8); - $register("lutetium", Ids::ELEMENT_LUTETIUM, "Lutetium", "lu", 71, 8); - $register("hafnium", Ids::ELEMENT_HAFNIUM, "Hafnium", "hf", 72, 2); - $register("tantalum", Ids::ELEMENT_TANTALUM, "Tantalum", "ta", 73, 2); - $register("tungsten", Ids::ELEMENT_TUNGSTEN, "Tungsten", "w", 74, 2); - $register("rhenium", Ids::ELEMENT_RHENIUM, "Rhenium", "re", 75, 2); - $register("osmium", Ids::ELEMENT_OSMIUM, "Osmium", "os", 76, 2); - $register("iridium", Ids::ELEMENT_IRIDIUM, "Iridium", "ir", 77, 2); - $register("platinum", Ids::ELEMENT_PLATINUM, "Platinum", "pt", 78, 2); - $register("gold", Ids::ELEMENT_GOLD, "Gold", "au", 79, 2); - $register("mercury", Ids::ELEMENT_MERCURY, "Mercury", "hg", 80, 2); - $register("thallium", Ids::ELEMENT_THALLIUM, "Thallium", "tl", 81, 3); - $register("lead", Ids::ELEMENT_LEAD, "Lead", "pb", 82, 3); - $register("bismuth", Ids::ELEMENT_BISMUTH, "Bismuth", "bi", 83, 3); - $register("polonium", Ids::ELEMENT_POLONIUM, "Polonium", "po", 84, 4); - $register("astatine", Ids::ELEMENT_ASTATINE, "Astatine", "at", 85, 6); - $register("radon", Ids::ELEMENT_RADON, "Radon", "rn", 86, 7); - $register("francium", Ids::ELEMENT_FRANCIUM, "Francium", "fr", 87, 0); - $register("radium", Ids::ELEMENT_RADIUM, "Radium", "ra", 88, 1); - $register("actinium", Ids::ELEMENT_ACTINIUM, "Actinium", "ac", 89, 9); - $register("thorium", Ids::ELEMENT_THORIUM, "Thorium", "th", 90, 9); - $register("protactinium", Ids::ELEMENT_PROTACTINIUM, "Protactinium", "pa", 91, 9); - $register("uranium", Ids::ELEMENT_URANIUM, "Uranium", "u", 92, 9); - $register("neptunium", Ids::ELEMENT_NEPTUNIUM, "Neptunium", "np", 93, 9); - $register("plutonium", Ids::ELEMENT_PLUTONIUM, "Plutonium", "pu", 94, 9); - $register("americium", Ids::ELEMENT_AMERICIUM, "Americium", "am", 95, 9); - $register("curium", Ids::ELEMENT_CURIUM, "Curium", "cm", 96, 9); - $register("berkelium", Ids::ELEMENT_BERKELIUM, "Berkelium", "bk", 97, 9); - $register("californium", Ids::ELEMENT_CALIFORNIUM, "Californium", "cf", 98, 9); - $register("einsteinium", Ids::ELEMENT_EINSTEINIUM, "Einsteinium", "es", 99, 9); - $register("fermium", Ids::ELEMENT_FERMIUM, "Fermium", "fm", 100, 9); - $register("mendelevium", Ids::ELEMENT_MENDELEVIUM, "Mendelevium", "md", 101, 9); - $register("nobelium", Ids::ELEMENT_NOBELIUM, "Nobelium", "no", 102, 9); - $register("lawrencium", Ids::ELEMENT_LAWRENCIUM, "Lawrencium", "lr", 103, 9); - $register("rutherfordium", Ids::ELEMENT_RUTHERFORDIUM, "Rutherfordium", "rf", 104, 2); - $register("dubnium", Ids::ELEMENT_DUBNIUM, "Dubnium", "db", 105, 2); - $register("seaborgium", Ids::ELEMENT_SEABORGIUM, "Seaborgium", "sg", 106, 2); - $register("bohrium", Ids::ELEMENT_BOHRIUM, "Bohrium", "bh", 107, 2); - $register("hassium", Ids::ELEMENT_HASSIUM, "Hassium", "hs", 108, 2); - $register("meitnerium", Ids::ELEMENT_MEITNERIUM, "Meitnerium", "mt", 109, 2); - $register("darmstadtium", Ids::ELEMENT_DARMSTADTIUM, "Darmstadtium", "ds", 110, 2); - $register("roentgenium", Ids::ELEMENT_ROENTGENIUM, "Roentgenium", "rg", 111, 2); - $register("copernicium", Ids::ELEMENT_COPERNICIUM, "Copernicium", "cn", 112, 2); - $register("nihonium", Ids::ELEMENT_NIHONIUM, "Nihonium", "nh", 113, 3); - $register("flerovium", Ids::ELEMENT_FLEROVIUM, "Flerovium", "fl", 114, 3); - $register("moscovium", Ids::ELEMENT_MOSCOVIUM, "Moscovium", "mc", 115, 3); - $register("livermorium", Ids::ELEMENT_LIVERMORIUM, "Livermorium", "lv", 116, 3); - $register("tennessine", Ids::ELEMENT_TENNESSINE, "Tennessine", "ts", 117, 6); - $register("oganesson", Ids::ELEMENT_OGANESSON, "Oganesson", "og", 118, 7); + self::register("element_zero", fn(BID $id) => new Opaque($id, "???", $instaBreak)); + + $register = fn(string $name, string $displayName, string $symbol, int $atomicWeight, int $group) => + self::register("element_$name", fn(BID $id) => new Element($id, $displayName, $instaBreak, $symbol, $atomicWeight, $group)); + + $register("hydrogen", "Hydrogen", "h", 1, 5); + $register("helium", "Helium", "he", 2, 7); + $register("lithium", "Lithium", "li", 3, 0); + $register("beryllium", "Beryllium", "be", 4, 1); + $register("boron", "Boron", "b", 5, 4); + $register("carbon", "Carbon", "c", 6, 5); + $register("nitrogen", "Nitrogen", "n", 7, 5); + $register("oxygen", "Oxygen", "o", 8, 5); + $register("fluorine", "Fluorine", "f", 9, 6); + $register("neon", "Neon", "ne", 10, 7); + $register("sodium", "Sodium", "na", 11, 0); + $register("magnesium", "Magnesium", "mg", 12, 1); + $register("aluminum", "Aluminum", "al", 13, 3); + $register("silicon", "Silicon", "si", 14, 4); + $register("phosphorus", "Phosphorus", "p", 15, 5); + $register("sulfur", "Sulfur", "s", 16, 5); + $register("chlorine", "Chlorine", "cl", 17, 6); + $register("argon", "Argon", "ar", 18, 7); + $register("potassium", "Potassium", "k", 19, 0); + $register("calcium", "Calcium", "ca", 20, 1); + $register("scandium", "Scandium", "sc", 21, 2); + $register("titanium", "Titanium", "ti", 22, 2); + $register("vanadium", "Vanadium", "v", 23, 2); + $register("chromium", "Chromium", "cr", 24, 2); + $register("manganese", "Manganese", "mn", 25, 2); + $register("iron", "Iron", "fe", 26, 2); + $register("cobalt", "Cobalt", "co", 27, 2); + $register("nickel", "Nickel", "ni", 28, 2); + $register("copper", "Copper", "cu", 29, 2); + $register("zinc", "Zinc", "zn", 30, 2); + $register("gallium", "Gallium", "ga", 31, 3); + $register("germanium", "Germanium", "ge", 32, 4); + $register("arsenic", "Arsenic", "as", 33, 4); + $register("selenium", "Selenium", "se", 34, 5); + $register("bromine", "Bromine", "br", 35, 6); + $register("krypton", "Krypton", "kr", 36, 7); + $register("rubidium", "Rubidium", "rb", 37, 0); + $register("strontium", "Strontium", "sr", 38, 1); + $register("yttrium", "Yttrium", "y", 39, 2); + $register("zirconium", "Zirconium", "zr", 40, 2); + $register("niobium", "Niobium", "nb", 41, 2); + $register("molybdenum", "Molybdenum", "mo", 42, 2); + $register("technetium", "Technetium", "tc", 43, 2); + $register("ruthenium", "Ruthenium", "ru", 44, 2); + $register("rhodium", "Rhodium", "rh", 45, 2); + $register("palladium", "Palladium", "pd", 46, 2); + $register("silver", "Silver", "ag", 47, 2); + $register("cadmium", "Cadmium", "cd", 48, 2); + $register("indium", "Indium", "in", 49, 3); + $register("tin", "Tin", "sn", 50, 3); + $register("antimony", "Antimony", "sb", 51, 4); + $register("tellurium", "Tellurium", "te", 52, 4); + $register("iodine", "Iodine", "i", 53, 6); + $register("xenon", "Xenon", "xe", 54, 7); + $register("cesium", "Cesium", "cs", 55, 0); + $register("barium", "Barium", "ba", 56, 1); + $register("lanthanum", "Lanthanum", "la", 57, 8); + $register("cerium", "Cerium", "ce", 58, 8); + $register("praseodymium", "Praseodymium", "pr", 59, 8); + $register("neodymium", "Neodymium", "nd", 60, 8); + $register("promethium", "Promethium", "pm", 61, 8); + $register("samarium", "Samarium", "sm", 62, 8); + $register("europium", "Europium", "eu", 63, 8); + $register("gadolinium", "Gadolinium", "gd", 64, 8); + $register("terbium", "Terbium", "tb", 65, 8); + $register("dysprosium", "Dysprosium", "dy", 66, 8); + $register("holmium", "Holmium", "ho", 67, 8); + $register("erbium", "Erbium", "er", 68, 8); + $register("thulium", "Thulium", "tm", 69, 8); + $register("ytterbium", "Ytterbium", "yb", 70, 8); + $register("lutetium", "Lutetium", "lu", 71, 8); + $register("hafnium", "Hafnium", "hf", 72, 2); + $register("tantalum", "Tantalum", "ta", 73, 2); + $register("tungsten", "Tungsten", "w", 74, 2); + $register("rhenium", "Rhenium", "re", 75, 2); + $register("osmium", "Osmium", "os", 76, 2); + $register("iridium", "Iridium", "ir", 77, 2); + $register("platinum", "Platinum", "pt", 78, 2); + $register("gold", "Gold", "au", 79, 2); + $register("mercury", "Mercury", "hg", 80, 2); + $register("thallium", "Thallium", "tl", 81, 3); + $register("lead", "Lead", "pb", 82, 3); + $register("bismuth", "Bismuth", "bi", 83, 3); + $register("polonium", "Polonium", "po", 84, 4); + $register("astatine", "Astatine", "at", 85, 6); + $register("radon", "Radon", "rn", 86, 7); + $register("francium", "Francium", "fr", 87, 0); + $register("radium", "Radium", "ra", 88, 1); + $register("actinium", "Actinium", "ac", 89, 9); + $register("thorium", "Thorium", "th", 90, 9); + $register("protactinium", "Protactinium", "pa", 91, 9); + $register("uranium", "Uranium", "u", 92, 9); + $register("neptunium", "Neptunium", "np", 93, 9); + $register("plutonium", "Plutonium", "pu", 94, 9); + $register("americium", "Americium", "am", 95, 9); + $register("curium", "Curium", "cm", 96, 9); + $register("berkelium", "Berkelium", "bk", 97, 9); + $register("californium", "Californium", "cf", 98, 9); + $register("einsteinium", "Einsteinium", "es", 99, 9); + $register("fermium", "Fermium", "fm", 100, 9); + $register("mendelevium", "Mendelevium", "md", 101, 9); + $register("nobelium", "Nobelium", "no", 102, 9); + $register("lawrencium", "Lawrencium", "lr", 103, 9); + $register("rutherfordium", "Rutherfordium", "rf", 104, 2); + $register("dubnium", "Dubnium", "db", 105, 2); + $register("seaborgium", "Seaborgium", "sg", 106, 2); + $register("bohrium", "Bohrium", "bh", 107, 2); + $register("hassium", "Hassium", "hs", 108, 2); + $register("meitnerium", "Meitnerium", "mt", 109, 2); + $register("darmstadtium", "Darmstadtium", "ds", 110, 2); + $register("roentgenium", "Roentgenium", "rg", 111, 2); + $register("copernicium", "Copernicium", "cn", 112, 2); + $register("nihonium", "Nihonium", "nh", 113, 3); + $register("flerovium", "Flerovium", "fl", 114, 3); + $register("moscovium", "Moscovium", "mc", 115, 3); + $register("livermorium", "Livermorium", "lv", 116, 3); + $register("tennessine", "Tennessine", "ts", 117, 6); + $register("oganesson", "Oganesson", "og", 118, 7); } private static function registerOres() : void{ $stoneOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(3.0, $toolTier)); - self::register("coal_ore", new CoalOre(new BID(Ids::COAL_ORE), "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD))); - self::register("copper_ore", new CopperOre(new BID(Ids::COPPER_ORE), "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE))); - self::register("diamond_ore", new DiamondOre(new BID(Ids::DIAMOND_ORE), "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON))); - self::register("emerald_ore", new EmeraldOre(new BID(Ids::EMERALD_ORE), "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON))); - self::register("gold_ore", new GoldOre(new BID(Ids::GOLD_ORE), "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON))); - self::register("iron_ore", new IronOre(new BID(Ids::IRON_ORE), "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE))); - self::register("lapis_lazuli_ore", new LapisOre(new BID(Ids::LAPIS_LAZULI_ORE), "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE))); - self::register("redstone_ore", new RedstoneOre(new BID(Ids::REDSTONE_ORE), "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("coal_ore", fn(BID $id) => new CoalOre($id, "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD))); + self::register("copper_ore", fn(BID $id) => new CopperOre($id, "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE))); + self::register("diamond_ore", fn(BID $id) => new DiamondOre($id, "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("emerald_ore", fn(BID $id) => new EmeraldOre($id, "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("gold_ore", fn(BID $id) => new GoldOre($id, "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("iron_ore", fn(BID $id) => new IronOre($id, "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE))); + self::register("lapis_lazuli_ore", fn(BID $id) => new LapisOre($id, "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE))); + self::register("redstone_ore", fn(BID $id) => new RedstoneOre($id, "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON))); $deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier)); - self::register("deepslate_coal_ore", new CoalOre(new BID(Ids::DEEPSLATE_COAL_ORE), "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD))); - self::register("deepslate_copper_ore", new CopperOre(new BID(Ids::DEEPSLATE_COPPER_ORE), "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE))); - self::register("deepslate_diamond_ore", new DiamondOre(new BID(Ids::DEEPSLATE_DIAMOND_ORE), "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON))); - self::register("deepslate_emerald_ore", new EmeraldOre(new BID(Ids::DEEPSLATE_EMERALD_ORE), "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON))); - self::register("deepslate_gold_ore", new GoldOre(new BID(Ids::DEEPSLATE_GOLD_ORE), "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON))); - self::register("deepslate_iron_ore", new IronOre(new BID(Ids::DEEPSLATE_IRON_ORE), "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE))); - self::register("deepslate_lapis_lazuli_ore", new LapisOre(new BID(Ids::DEEPSLATE_LAPIS_LAZULI_ORE), "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE))); - self::register("deepslate_redstone_ore", new RedstoneOre(new BID(Ids::DEEPSLATE_REDSTONE_ORE), "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_coal_ore", fn(BID $id) => new CoalOre($id, "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD))); + self::register("deepslate_copper_ore", fn(BID $id) => new CopperOre($id, "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE))); + self::register("deepslate_diamond_ore", fn(BID $id) => new DiamondOre($id, "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_emerald_ore", fn(BID $id) => new EmeraldOre($id, "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_gold_ore", fn(BID $id) => new GoldOre($id, "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_iron_ore", fn(BID $id) => new IronOre($id, "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE))); + self::register("deepslate_lapis_lazuli_ore", fn(BID $id) => new LapisOre($id, "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE))); + self::register("deepslate_redstone_ore", fn(BID $id) => new RedstoneOre($id, "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON))); $netherrackOreBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)); - self::register("nether_quartz_ore", new NetherQuartzOre(new BID(Ids::NETHER_QUARTZ_ORE), "Nether Quartz Ore", $netherrackOreBreakInfo)); - self::register("nether_gold_ore", new NetherGoldOre(new BID(Ids::NETHER_GOLD_ORE), "Nether Gold Ore", $netherrackOreBreakInfo)); + self::register("nether_quartz_ore", fn(BID $id) => new NetherQuartzOre($id, "Nether Quartz Ore", $netherrackOreBreakInfo)); + self::register("nether_gold_ore", fn(BID $id) => new NetherGoldOre($id, "Nether Gold Ore", $netherrackOreBreakInfo)); } private static function registerCraftingTables() : void{ //TODO: this is the same for all wooden crafting blocks $craftingBlockBreakInfo = new Info(BreakInfo::axe(2.5)); - self::register("cartography_table", new CartographyTable(new BID(Ids::CARTOGRAPHY_TABLE), "Cartography Table", $craftingBlockBreakInfo)); - self::register("crafting_table", new CraftingTable(new BID(Ids::CRAFTING_TABLE), "Crafting Table", $craftingBlockBreakInfo)); - self::register("fletching_table", new FletchingTable(new BID(Ids::FLETCHING_TABLE), "Fletching Table", $craftingBlockBreakInfo)); - self::register("loom", new Loom(new BID(Ids::LOOM), "Loom", $craftingBlockBreakInfo)); - self::register("smithing_table", new SmithingTable(new BID(Ids::SMITHING_TABLE), "Smithing Table", $craftingBlockBreakInfo)); + self::register("cartography_table", fn(BID $id) => new CartographyTable($id, "Cartography Table", $craftingBlockBreakInfo)); + self::register("crafting_table", fn(BID $id) => new CraftingTable($id, "Crafting Table", $craftingBlockBreakInfo)); + self::register("fletching_table", fn(BID $id) => new FletchingTable($id, "Fletching Table", $craftingBlockBreakInfo)); + self::register("loom", fn(BID $id) => new Loom($id, "Loom", $craftingBlockBreakInfo)); + self::register("smithing_table", fn(BID $id) => new SmithingTable($id, "Smithing Table", $craftingBlockBreakInfo)); } private static function registerChorusBlocks() : void{ $chorusBlockBreakInfo = new Info(BreakInfo::axe(0.4)); - self::register("chorus_plant", new ChorusPlant(new BID(Ids::CHORUS_PLANT), "Chorus Plant", $chorusBlockBreakInfo)); - self::register("chorus_flower", new ChorusFlower(new BID(Ids::CHORUS_FLOWER), "Chorus Flower", $chorusBlockBreakInfo)); + self::register("chorus_plant", fn(BID $id) => new ChorusPlant($id, "Chorus Plant", $chorusBlockBreakInfo)); + self::register("chorus_flower", fn(BID $id) => new ChorusFlower($id, "Chorus Flower", $chorusBlockBreakInfo)); } private static function registerBlocksR13() : void{ - self::register("light", new Light(new BID(Ids::LIGHT), "Light Block", new Info(BreakInfo::indestructible()))); - self::register("wither_rose", new WitherRose(new BID(Ids::WITHER_ROSE), "Wither Rose", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); + self::register("light", fn(BID $id) => new Light($id, "Light Block", new Info(BreakInfo::indestructible()))); + self::register("wither_rose", fn(BID $id) => new WitherRose($id, "Wither Rose", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); } private static function registerBlocksR14() : void{ - self::register("honeycomb", new Opaque(new BID(Ids::HONEYCOMB), "Honeycomb Block", new Info(new BreakInfo(0.6)))); + self::register("honeycomb", fn(BID $id) => new Opaque($id, "Honeycomb Block", new Info(new BreakInfo(0.6)))); } private static function registerBlocksR16() : void{ //for some reason, slabs have weird hardness like the legacy ones $slabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("ancient_debris", new class(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ + self::register("ancient_debris", fn(BID $id) => new class($id, "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 3600.0)); - self::register("netherite", new class(new BID(Ids::NETHERITE), "Netherite Block", $netheriteBreakInfo) extends Opaque{ + self::register("netherite", fn(BID $id) => new class($id, "Netherite Block", $netheriteBreakInfo) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); $basaltBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)); - self::register("basalt", new SimplePillar(new BID(Ids::BASALT), "Basalt", $basaltBreakInfo)); - self::register("polished_basalt", new SimplePillar(new BID(Ids::POLISHED_BASALT), "Polished Basalt", $basaltBreakInfo)); - self::register("smooth_basalt", new Opaque(new BID(Ids::SMOOTH_BASALT), "Smooth Basalt", $basaltBreakInfo)); + self::register("basalt", fn(BID $id) => new SimplePillar($id, "Basalt", $basaltBreakInfo)); + self::register("polished_basalt", fn(BID $id) => new SimplePillar($id, "Polished Basalt", $basaltBreakInfo)); + self::register("smooth_basalt", fn(BID $id) => new Opaque($id, "Smooth Basalt", $basaltBreakInfo)); $blackstoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("blackstone", new Opaque(new BID(Ids::BLACKSTONE), "Blackstone", $blackstoneBreakInfo)); - self::register("blackstone_slab", new Slab(new BID(Ids::BLACKSTONE_SLAB), "Blackstone", $slabBreakInfo)); - self::register("blackstone_stairs", new Stair(new BID(Ids::BLACKSTONE_STAIRS), "Blackstone Stairs", $blackstoneBreakInfo)); - self::register("blackstone_wall", new Wall(new BID(Ids::BLACKSTONE_WALL), "Blackstone Wall", $blackstoneBreakInfo)); + self::register("blackstone", fn(BID $id) => new Opaque($id, "Blackstone", $blackstoneBreakInfo)); + self::register("blackstone_slab", fn(BID $id) => new Slab($id, "Blackstone", $slabBreakInfo)); + self::register("blackstone_stairs", fn(BID $id) => new Stair($id, "Blackstone Stairs", $blackstoneBreakInfo)); + self::register("blackstone_wall", fn(BID $id) => new Wall($id, "Blackstone Wall", $blackstoneBreakInfo)); - self::register("gilded_blackstone", new GildedBlackstone(new BID(Ids::GILDED_BLACKSTONE), "Gilded Blackstone", $blackstoneBreakInfo)); + self::register("gilded_blackstone", fn(BID $id) => new GildedBlackstone($id, "Gilded Blackstone", $blackstoneBreakInfo)); //TODO: polished blackstone ought to have 2.0 hardness (as per java) but it's 1.5 in Bedrock (probably parity bug) $prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : ""); - self::register("polished_blackstone", new Opaque(new BID(Ids::POLISHED_BLACKSTONE), $prefix(""), $blackstoneBreakInfo)); - self::register("polished_blackstone_button", new StoneButton(new BID(Ids::POLISHED_BLACKSTONE_BUTTON), $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); - self::register("polished_blackstone_pressure_plate", new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)), 20)); - self::register("polished_blackstone_slab", new Slab(new BID(Ids::POLISHED_BLACKSTONE_SLAB), $prefix(""), $slabBreakInfo)); - self::register("polished_blackstone_stairs", new Stair(new BID(Ids::POLISHED_BLACKSTONE_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo)); - self::register("polished_blackstone_wall", new Wall(new BID(Ids::POLISHED_BLACKSTONE_WALL), $prefix("Wall"), $blackstoneBreakInfo)); - self::register("chiseled_polished_blackstone", new Opaque(new BID(Ids::CHISELED_POLISHED_BLACKSTONE), "Chiseled Polished Blackstone", $blackstoneBreakInfo)); + self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $blackstoneBreakInfo)); + self::register("polished_blackstone_button", fn(BID $id) => new StoneButton($id, $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); + self::register("polished_blackstone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)), 20)); + self::register("polished_blackstone_slab", fn(BID $id) => new Slab($id, $prefix(""), $slabBreakInfo)); + self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); + self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); + self::register("chiseled_polished_blackstone", fn(BID $id) => new Opaque($id, "Chiseled Polished Blackstone", $blackstoneBreakInfo)); $prefix = fn(string $thing) => "Polished Blackstone Brick" . ($thing !== "" ? " $thing" : ""); - self::register("polished_blackstone_bricks", new Opaque(new BID(Ids::POLISHED_BLACKSTONE_BRICKS), "Polished Blackstone Bricks", $blackstoneBreakInfo)); - self::register("polished_blackstone_brick_slab", new Slab(new BID(Ids::POLISHED_BLACKSTONE_BRICK_SLAB), "Polished Blackstone Brick", $slabBreakInfo)); - self::register("polished_blackstone_brick_stairs", new Stair(new BID(Ids::POLISHED_BLACKSTONE_BRICK_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo)); - self::register("polished_blackstone_brick_wall", new Wall(new BID(Ids::POLISHED_BLACKSTONE_BRICK_WALL), $prefix("Wall"), $blackstoneBreakInfo)); - self::register("cracked_polished_blackstone_bricks", new Opaque(new BID(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS), "Cracked Polished Blackstone Bricks", $blackstoneBreakInfo)); + self::register("polished_blackstone_bricks", fn(BID $id) => new Opaque($id, "Polished Blackstone Bricks", $blackstoneBreakInfo)); + self::register("polished_blackstone_brick_slab", fn(BID $id) => new Slab($id, "Polished Blackstone Brick", $slabBreakInfo)); + self::register("polished_blackstone_brick_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); + self::register("polished_blackstone_brick_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); + self::register("cracked_polished_blackstone_bricks", fn(BID $id) => new Opaque($id, "Cracked Polished Blackstone Bricks", $blackstoneBreakInfo)); - self::register("soul_torch", new Torch(new BID(Ids::SOUL_TORCH), "Soul Torch", new Info(BreakInfo::instant()))); - self::register("soul_fire", new SoulFire(new BID(Ids::SOUL_FIRE), "Soul Fire", new Info(BreakInfo::instant(), [Tags::FIRE]))); + self::register("soul_torch", fn(BID $id) => new Torch($id, "Soul Torch", new Info(BreakInfo::instant()))); + self::register("soul_fire", fn(BID $id) => new SoulFire($id, "Soul Fire", new Info(BreakInfo::instant(), [Tags::FIRE]))); //TODO: soul soul ought to have 0.5 hardness (as per java) but it's 1.0 in Bedrock (probably parity bug) - self::register("soul_soil", new Opaque(new BID(Ids::SOUL_SOIL), "Soul Soil", new Info(BreakInfo::shovel(1.0)))); + self::register("soul_soil", fn(BID $id) => new Opaque($id, "Soul Soil", new Info(BreakInfo::shovel(1.0)))); - self::register("shroomlight", new class(new BID(Ids::SHROOMLIGHT), "Shroomlight", new Info(new BreakInfo(1.0, ToolType::HOE))) extends Opaque{ + self::register("shroomlight", fn(BID $id) => new class($id, "Shroomlight", new Info(new BreakInfo(1.0, ToolType::HOE))) extends Opaque{ public function getLightLevel() : int{ return 15; } }); - self::register("warped_wart_block", new Opaque(new BID(Ids::WARPED_WART_BLOCK), "Warped Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); - self::register("crying_obsidian", new class(new BID(Ids::CRYING_OBSIDIAN), "Crying Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in Java */, ToolTier::DIAMOND, 6000.0))) extends Opaque{ + self::register("warped_wart_block", fn(BID $id) => new Opaque($id, "Warped Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); + self::register("crying_obsidian", fn(BID $id) => new class($id, "Crying Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in Java */, ToolTier::DIAMOND, 6000.0))) extends Opaque{ public function getLightLevel() : int{ return 10;} }); - self::register("twisting_vines", new NetherVines(new BID(Ids::TWISTING_VINES), "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP)); - self::register("weeping_vines", new NetherVines(new BID(Ids::WEEPING_VINES), "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN)); + self::register("twisting_vines", fn(BID $id) => new NetherVines($id, "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP)); + self::register("weeping_vines", fn(BID $id) => new NetherVines($id, "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN)); $netherRootsInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]); - self::register("crimson_roots", new NetherRoots(new BID(Ids::CRIMSON_ROOTS), "Crimson Roots", $netherRootsInfo)); - self::register("warped_roots", new NetherRoots(new BID(Ids::WARPED_ROOTS), "Warped Roots", $netherRootsInfo)); + self::register("crimson_roots", fn(BID $id) => new NetherRoots($id, "Crimson Roots", $netherRootsInfo)); + self::register("warped_roots", fn(BID $id) => new NetherRoots($id, "Warped Roots", $netherRootsInfo)); - self::register("chain", new Chain(new BID(Ids::CHAIN), "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); + self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); } private static function registerBlocksR17() : void{ //in java this can be acquired using any tool - seems to be a parity issue in bedrock $amethystInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD)); - self::register("amethyst", new class(new BID(Ids::AMETHYST), "Amethyst", $amethystInfo) extends Opaque{ + self::register("amethyst", fn(BID $id) => new class($id, "Amethyst", $amethystInfo) extends Opaque{ use AmethystTrait; }); - self::register("budding_amethyst", new BuddingAmethyst(new BID(Ids::BUDDING_AMETHYST), "Budding Amethyst", $amethystInfo)); - self::register("amethyst_cluster", new AmethystCluster(new BID(Ids::AMETHYST_CLUSTER), "Amethyst Cluster", $amethystInfo)); + self::register("budding_amethyst", fn(BID $id) => new BuddingAmethyst($id, "Budding Amethyst", $amethystInfo)); + self::register("amethyst_cluster", fn(BID $id) => new AmethystCluster($id, "Amethyst Cluster", $amethystInfo)); - self::register("calcite", new Opaque(new BID(Ids::CALCITE), "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD)))); + self::register("calcite", fn(BID $id) => new Opaque($id, "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD)))); - self::register("raw_copper", new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); - self::register("raw_gold", new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0)))); - self::register("raw_iron", new Opaque(new BID(Ids::RAW_IRON), "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); + self::register("raw_copper", fn(BID $id) => new Opaque($id, "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); + self::register("raw_gold", fn(BID $id) => new Opaque($id, "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0)))); + self::register("raw_iron", fn(BID $id) => new Opaque($id, "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); $deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 18.0)); - self::register("deepslate", new class(new BID(Ids::DEEPSLATE), "Deepslate", $deepslateBreakInfo) extends SimplePillar{ + self::register("deepslate", fn(BID $id) => new class($id, "Deepslate", $deepslateBreakInfo) extends SimplePillar{ public function getDropsForCompatibleTool(Item $item) : array{ return [VanillaBlocks::COBBLED_DEEPSLATE()->asItem()]; } @@ -1612,113 +1647,113 @@ public function isAffectedBySilkTouch() : bool{ }); //TODO: parity issue here - in Java this has a hardness of 3.0, but in bedrock it's 3.5 - self::register("chiseled_deepslate", new Opaque(new BID(Ids::CHISELED_DEEPSLATE), "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)))); + self::register("chiseled_deepslate", fn(BID $id) => new Opaque($id, "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)))); $deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("deepslate_bricks", new Opaque(new BID(Ids::DEEPSLATE_BRICKS), "Deepslate Bricks", $deepslateBrickBreakInfo)); - self::register("deepslate_brick_slab", new Slab(new BID(Ids::DEEPSLATE_BRICK_SLAB), "Deepslate Brick", $deepslateBrickBreakInfo)); - self::register("deepslate_brick_stairs", new Stair(new BID(Ids::DEEPSLATE_BRICK_STAIRS), "Deepslate Brick Stairs", $deepslateBrickBreakInfo)); - self::register("deepslate_brick_wall", new Wall(new BID(Ids::DEEPSLATE_BRICK_WALL), "Deepslate Brick Wall", $deepslateBrickBreakInfo)); - self::register("cracked_deepslate_bricks", new Opaque(new BID(Ids::CRACKED_DEEPSLATE_BRICKS), "Cracked Deepslate Bricks", $deepslateBrickBreakInfo)); + self::register("deepslate_bricks", fn(BID $id) => new Opaque($id, "Deepslate Bricks", $deepslateBrickBreakInfo)); + self::register("deepslate_brick_slab", fn(BID $id) => new Slab($id, "Deepslate Brick", $deepslateBrickBreakInfo)); + self::register("deepslate_brick_stairs", fn(BID $id) => new Stair($id, "Deepslate Brick Stairs", $deepslateBrickBreakInfo)); + self::register("deepslate_brick_wall", fn(BID $id) => new Wall($id, "Deepslate Brick Wall", $deepslateBrickBreakInfo)); + self::register("cracked_deepslate_bricks", fn(BID $id) => new Opaque($id, "Cracked Deepslate Bricks", $deepslateBrickBreakInfo)); $deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("deepslate_tiles", new Opaque(new BID(Ids::DEEPSLATE_TILES), "Deepslate Tiles", $deepslateTilesBreakInfo)); - self::register("deepslate_tile_slab", new Slab(new BID(Ids::DEEPSLATE_TILE_SLAB), "Deepslate Tile", $deepslateTilesBreakInfo)); - self::register("deepslate_tile_stairs", new Stair(new BID(Ids::DEEPSLATE_TILE_STAIRS), "Deepslate Tile Stairs", $deepslateTilesBreakInfo)); - self::register("deepslate_tile_wall", new Wall(new BID(Ids::DEEPSLATE_TILE_WALL), "Deepslate Tile Wall", $deepslateTilesBreakInfo)); - self::register("cracked_deepslate_tiles", new Opaque(new BID(Ids::CRACKED_DEEPSLATE_TILES), "Cracked Deepslate Tiles", $deepslateTilesBreakInfo)); + self::register("deepslate_tiles", fn(BID $id) => new Opaque($id, "Deepslate Tiles", $deepslateTilesBreakInfo)); + self::register("deepslate_tile_slab", fn(BID $id) => new Slab($id, "Deepslate Tile", $deepslateTilesBreakInfo)); + self::register("deepslate_tile_stairs", fn(BID $id) => new Stair($id, "Deepslate Tile Stairs", $deepslateTilesBreakInfo)); + self::register("deepslate_tile_wall", fn(BID $id) => new Wall($id, "Deepslate Tile Wall", $deepslateTilesBreakInfo)); + self::register("cracked_deepslate_tiles", fn(BID $id) => new Opaque($id, "Cracked Deepslate Tiles", $deepslateTilesBreakInfo)); $cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("cobbled_deepslate", new Opaque(new BID(Ids::COBBLED_DEEPSLATE), "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); - self::register("cobbled_deepslate_slab", new Slab(new BID(Ids::COBBLED_DEEPSLATE_SLAB), "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); - self::register("cobbled_deepslate_stairs", new Stair(new BID(Ids::COBBLED_DEEPSLATE_STAIRS), "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo)); - self::register("cobbled_deepslate_wall", new Wall(new BID(Ids::COBBLED_DEEPSLATE_WALL), "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate", fn(BID $id) => new Opaque($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate_slab", fn(BID $id) => new Slab($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate_stairs", fn(BID $id) => new Stair($id, "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate_wall", fn(BID $id) => new Wall($id, "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo)); $polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("polished_deepslate", new Opaque(new BID(Ids::POLISHED_DEEPSLATE), "Polished Deepslate", $polishedDeepslateBreakInfo)); - self::register("polished_deepslate_slab", new Slab(new BID(Ids::POLISHED_DEEPSLATE_SLAB), "Polished Deepslate", $polishedDeepslateBreakInfo)); - self::register("polished_deepslate_stairs", new Stair(new BID(Ids::POLISHED_DEEPSLATE_STAIRS), "Polished Deepslate Stairs", $polishedDeepslateBreakInfo)); - self::register("polished_deepslate_wall", new Wall(new BID(Ids::POLISHED_DEEPSLATE_WALL), "Polished Deepslate Wall", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate", fn(BID $id) => new Opaque($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate_slab", fn(BID $id) => new Slab($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate_stairs", fn(BID $id) => new Stair($id, "Polished Deepslate Stairs", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate_wall", fn(BID $id) => new Wall($id, "Polished Deepslate Wall", $polishedDeepslateBreakInfo)); - self::register("tinted_glass", new TintedGlass(new BID(Ids::TINTED_GLASS), "Tinted Glass", new Info(new BreakInfo(0.3)))); + self::register("tinted_glass", fn(BID $id) => new TintedGlass($id, "Tinted Glass", new Info(new BreakInfo(0.3)))); //blast resistance should be 30 if we were matched with java :( $copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 18.0)); - self::register("lightning_rod", new LightningRod(new BID(Ids::LIGHTNING_ROD), "Lightning Rod", $copperBreakInfo)); + self::register("lightning_rod", fn(BID $id) => new LightningRod($id, "Lightning Rod", $copperBreakInfo)); - self::register("copper", new Copper(new BID(Ids::COPPER), "Copper Block", $copperBreakInfo)); - self::register("chiseled_copper", new Copper(new BID(Ids::CHISELED_COPPER), "Chiseled Copper", $copperBreakInfo)); - self::register("copper_grate", new CopperGrate(new BID(Ids::COPPER_GRATE), "Copper Grate", $copperBreakInfo)); - self::register("cut_copper", new Copper(new BID(Ids::CUT_COPPER), "Cut Copper Block", $copperBreakInfo)); - self::register("cut_copper_slab", new CopperSlab(new BID(Ids::CUT_COPPER_SLAB), "Cut Copper Slab", $copperBreakInfo)); - self::register("cut_copper_stairs", new CopperStairs(new BID(Ids::CUT_COPPER_STAIRS), "Cut Copper Stairs", $copperBreakInfo)); - self::register("copper_bulb", new CopperBulb(new BID(Ids::COPPER_BULB), "Copper Bulb", $copperBreakInfo)); + self::register("copper", fn(BID $id) => new Copper($id, "Copper Block", $copperBreakInfo)); + self::register("chiseled_copper", fn(BID $id) => new Copper($id, "Chiseled Copper", $copperBreakInfo)); + self::register("copper_grate", fn(BID $id) => new CopperGrate($id, "Copper Grate", $copperBreakInfo)); + self::register("cut_copper", fn(BID $id) => new Copper($id, "Cut Copper Block", $copperBreakInfo)); + self::register("cut_copper_slab", fn(BID $id) => new CopperSlab($id, "Cut Copper Slab", $copperBreakInfo)); + self::register("cut_copper_stairs", fn(BID $id) => new CopperStairs($id, "Cut Copper Stairs", $copperBreakInfo)); + self::register("copper_bulb", fn(BID $id) => new CopperBulb($id, "Copper Bulb", $copperBreakInfo)); $copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)); - self::register("copper_door", new CopperDoor(new BID(Ids::COPPER_DOOR), "Copper Door", $copperDoorBreakInfo)); - self::register("copper_trapdoor", new CopperTrapdoor(new BID(Ids::COPPER_TRAPDOOR), "Copper Trapdoor", $copperDoorBreakInfo)); + self::register("copper_door", fn(BID $id) => new CopperDoor($id, "Copper Door", $copperDoorBreakInfo)); + self::register("copper_trapdoor", fn(BID $id) => new CopperTrapdoor($id, "Copper Trapdoor", $copperDoorBreakInfo)); $candleBreakInfo = new Info(new BreakInfo(0.1)); - self::register("candle", new Candle(new BID(Ids::CANDLE), "Candle", $candleBreakInfo)); - self::register("dyed_candle", new DyedCandle(new BID(Ids::DYED_CANDLE), "Dyed Candle", $candleBreakInfo)); + self::register("candle", fn(BID $id) => new Candle($id, "Candle", $candleBreakInfo)); + self::register("dyed_candle", fn(BID $id) => new DyedCandle($id, "Dyed Candle", $candleBreakInfo)); //TODO: duplicated break info :( $cakeBreakInfo = new Info(new BreakInfo(0.5)); - self::register("cake_with_candle", new CakeWithCandle(new BID(Ids::CAKE_WITH_CANDLE), "Cake With Candle", $cakeBreakInfo)); - self::register("cake_with_dyed_candle", new CakeWithDyedCandle(new BID(Ids::CAKE_WITH_DYED_CANDLE), "Cake With Dyed Candle", $cakeBreakInfo)); + self::register("cake_with_candle", fn(BID $id) => new CakeWithCandle($id, "Cake With Candle", $cakeBreakInfo)); + self::register("cake_with_dyed_candle", fn(BID $id) => new CakeWithDyedCandle($id, "Cake With Dyed Candle", $cakeBreakInfo)); - self::register("hanging_roots", new HangingRoots(new BID(Ids::HANGING_ROOTS), "Hanging Roots", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("hanging_roots", fn(BID $id) => new HangingRoots($id, "Hanging Roots", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("cave_vines", new CaveVines(new BID(Ids::CAVE_VINES), "Cave Vines", new Info(BreakInfo::instant()))); + self::register("cave_vines", fn(BID $id) => new CaveVines($id, "Cave Vines", new Info(BreakInfo::instant()))); - self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1)))); - self::register("big_dripleaf_head", new BigDripleafHead(new BID(Ids::BIG_DRIPLEAF_HEAD), "Big Dripleaf", new Info(BreakInfo::instant()))); - self::register("big_dripleaf_stem", new BigDripleafStem(new BID(Ids::BIG_DRIPLEAF_STEM), "Big Dripleaf Stem", new Info(BreakInfo::instant()))); + self::register("small_dripleaf", fn(BID $id) => new SmallDripleaf($id, "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1)))); + self::register("big_dripleaf_head", fn(BID $id) => new BigDripleafHead($id, "Big Dripleaf", new Info(BreakInfo::instant()))); + self::register("big_dripleaf_stem", fn(BID $id) => new BigDripleafStem($id, "Big Dripleaf Stem", new Info(BreakInfo::instant()))); } private static function registerBlocksR18() : void{ - self::register("spore_blossom", new SporeBlossom(new BID(Ids::SPORE_BLOSSOM), "Spore Blossom", new Info(BreakInfo::instant()))); + self::register("spore_blossom", fn(BID $id) => new SporeBlossom($id, "Spore Blossom", new Info(BreakInfo::instant()))); } private static function registerMudBlocks() : void{ - self::register("mud", new Opaque(new BID(Ids::MUD), "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD]))); - self::register("packed_mud", new Opaque(new BID(Ids::PACKED_MUD), "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0)))); + self::register("mud", fn(BID $id) => new Opaque($id, "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD]))); + self::register("packed_mud", fn(BID $id) => new Opaque($id, "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0)))); $mudBricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("mud_bricks", new Opaque(new BID(Ids::MUD_BRICKS), "Mud Bricks", $mudBricksBreakInfo)); - self::register("mud_brick_slab", new Slab(new BID(Ids::MUD_BRICK_SLAB), "Mud Brick", $mudBricksBreakInfo)); - self::register("mud_brick_stairs", new Stair(new BID(Ids::MUD_BRICK_STAIRS), "Mud Brick Stairs", $mudBricksBreakInfo)); - self::register("mud_brick_wall", new Wall(new BID(Ids::MUD_BRICK_WALL), "Mud Brick Wall", $mudBricksBreakInfo)); + self::register("mud_bricks", fn(BID $id) => new Opaque($id, "Mud Bricks", $mudBricksBreakInfo)); + self::register("mud_brick_slab", fn(BID $id) => new Slab($id, "Mud Brick", $mudBricksBreakInfo)); + self::register("mud_brick_stairs", fn(BID $id) => new Stair($id, "Mud Brick Stairs", $mudBricksBreakInfo)); + self::register("mud_brick_wall", fn(BID $id) => new Wall($id, "Mud Brick Wall", $mudBricksBreakInfo)); } private static function registerTuffBlocks() : void{ $tuffBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", $tuffBreakInfo)); - self::register("tuff_slab", new Slab(new BID(Ids::TUFF_SLAB), "Tuff", $tuffBreakInfo)); - self::register("tuff_stairs", new Stair(new BID(Ids::TUFF_STAIRS), "Tuff Stairs", $tuffBreakInfo)); - self::register("tuff_wall", new Wall(new BID(Ids::TUFF_WALL), "Tuff Wall", $tuffBreakInfo)); - self::register("chiseled_tuff", new Opaque(new BID(Ids::CHISELED_TUFF), "Chiseled Tuff", $tuffBreakInfo)); - - self::register("tuff_bricks", new Opaque(new BID(Ids::TUFF_BRICKS), "Tuff Bricks", $tuffBreakInfo)); - self::register("tuff_brick_slab", new Slab(new BID(Ids::TUFF_BRICK_SLAB), "Tuff Brick", $tuffBreakInfo)); - self::register("tuff_brick_stairs", new Stair(new BID(Ids::TUFF_BRICK_STAIRS), "Tuff Brick Stairs", $tuffBreakInfo)); - self::register("tuff_brick_wall", new Wall(new BID(Ids::TUFF_BRICK_WALL), "Tuff Brick Wall", $tuffBreakInfo)); - self::register("chiseled_tuff_bricks", new Opaque(new BID(Ids::CHISELED_TUFF_BRICKS), "Chiseled Tuff Bricks", $tuffBreakInfo)); - - self::register("polished_tuff", new Opaque(new BID(Ids::POLISHED_TUFF), "Polished Tuff", $tuffBreakInfo)); - self::register("polished_tuff_slab", new Slab(new BID(Ids::POLISHED_TUFF_SLAB), "Polished Tuff", $tuffBreakInfo)); - self::register("polished_tuff_stairs", new Stair(new BID(Ids::POLISHED_TUFF_STAIRS), "Polished Tuff Stairs", $tuffBreakInfo)); - self::register("polished_tuff_wall", new Wall(new BID(Ids::POLISHED_TUFF_WALL), "Polished Tuff Wall", $tuffBreakInfo)); + self::register("tuff", fn(BID $id) => new Opaque($id, "Tuff", $tuffBreakInfo)); + self::register("tuff_slab", fn(BID $id) => new Slab($id, "Tuff", $tuffBreakInfo)); + self::register("tuff_stairs", fn(BID $id) => new Stair($id, "Tuff Stairs", $tuffBreakInfo)); + self::register("tuff_wall", fn(BID $id) => new Wall($id, "Tuff Wall", $tuffBreakInfo)); + self::register("chiseled_tuff", fn(BID $id) => new Opaque($id, "Chiseled Tuff", $tuffBreakInfo)); + + self::register("tuff_bricks", fn(BID $id) => new Opaque($id, "Tuff Bricks", $tuffBreakInfo)); + self::register("tuff_brick_slab", fn(BID $id) => new Slab($id, "Tuff Brick", $tuffBreakInfo)); + self::register("tuff_brick_stairs", fn(BID $id) => new Stair($id, "Tuff Brick Stairs", $tuffBreakInfo)); + self::register("tuff_brick_wall", fn(BID $id) => new Wall($id, "Tuff Brick Wall", $tuffBreakInfo)); + self::register("chiseled_tuff_bricks", fn(BID $id) => new Opaque($id, "Chiseled Tuff Bricks", $tuffBreakInfo)); + + self::register("polished_tuff", fn(BID $id) => new Opaque($id, "Polished Tuff", $tuffBreakInfo)); + self::register("polished_tuff_slab", fn(BID $id) => new Slab($id, "Polished Tuff", $tuffBreakInfo)); + self::register("polished_tuff_stairs", fn(BID $id) => new Stair($id, "Polished Tuff Stairs", $tuffBreakInfo)); + self::register("polished_tuff_wall", fn(BID $id) => new Wall($id, "Polished Tuff Wall", $tuffBreakInfo)); } private static function registerCauldronBlocks() : void{ $cauldronBreakInfo = new Info(BreakInfo::pickaxe(2, ToolTier::WOOD)); - self::register("cauldron", new Cauldron(new BID(Ids::CAULDRON, TileCauldron::class), "Cauldron", $cauldronBreakInfo)); - self::register("water_cauldron", new WaterCauldron(new BID(Ids::WATER_CAULDRON, TileCauldron::class), "Water Cauldron", $cauldronBreakInfo)); - self::register("lava_cauldron", new LavaCauldron(new BID(Ids::LAVA_CAULDRON, TileCauldron::class), "Lava Cauldron", $cauldronBreakInfo)); - self::register("potion_cauldron", new PotionCauldron(new BID(Ids::POTION_CAULDRON, TileCauldron::class), "Potion Cauldron", $cauldronBreakInfo)); + self::register("cauldron", fn(BID $id) => new Cauldron($id, "Cauldron", $cauldronBreakInfo), TileCauldron::class); + self::register("water_cauldron", fn(BID $id) => new WaterCauldron($id, "Water Cauldron", $cauldronBreakInfo), TileCauldron::class); + self::register("lava_cauldron", fn(BID $id) => new LavaCauldron($id, "Lava Cauldron", $cauldronBreakInfo), TileCauldron::class); + self::register("potion_cauldron", fn(BID $id) => new PotionCauldron($id, "Potion Cauldron", $cauldronBreakInfo), TileCauldron::class); } } diff --git a/src/block/WoodLikeBlockIdHelper.php b/src/block/WoodLikeBlockIdHelper.php deleted file mode 100644 index 88fdff3a6ec..00000000000 --- a/src/block/WoodLikeBlockIdHelper.php +++ /dev/null @@ -1,263 +0,0 @@ - Ids::OAK_PLANKS, - WoodType::SPRUCE => Ids::SPRUCE_PLANKS, - WoodType::BIRCH => Ids::BIRCH_PLANKS, - WoodType::JUNGLE => Ids::JUNGLE_PLANKS, - WoodType::ACACIA => Ids::ACACIA_PLANKS, - WoodType::DARK_OAK => Ids::DARK_OAK_PLANKS, - WoodType::MANGROVE => Ids::MANGROVE_PLANKS, - WoodType::CRIMSON => Ids::CRIMSON_PLANKS, - WoodType::WARPED => Ids::WARPED_PLANKS, - WoodType::CHERRY => Ids::CHERRY_PLANKS, - }); - } - - public static function getFenceIdentifier(WoodType $type) : BID{ - return new BID(match($type){ - WoodType::OAK => Ids::OAK_FENCE, - WoodType::SPRUCE => Ids::SPRUCE_FENCE, - WoodType::BIRCH => Ids::BIRCH_FENCE, - WoodType::JUNGLE => Ids::JUNGLE_FENCE, - WoodType::ACACIA => Ids::ACACIA_FENCE, - WoodType::DARK_OAK => Ids::DARK_OAK_FENCE, - WoodType::MANGROVE => Ids::MANGROVE_FENCE, - WoodType::CRIMSON => Ids::CRIMSON_FENCE, - WoodType::WARPED => Ids::WARPED_FENCE, - WoodType::CHERRY => Ids::CHERRY_FENCE, - }); - } - - public static function getSlabIdentifier(WoodType $type) : BID{ - return new BID(match($type){ - WoodType::OAK => Ids::OAK_SLAB, - WoodType::SPRUCE => Ids::SPRUCE_SLAB, - WoodType::BIRCH => Ids::BIRCH_SLAB, - WoodType::JUNGLE => Ids::JUNGLE_SLAB, - WoodType::ACACIA => Ids::ACACIA_SLAB, - WoodType::DARK_OAK => Ids::DARK_OAK_SLAB, - WoodType::MANGROVE => Ids::MANGROVE_SLAB, - WoodType::CRIMSON => Ids::CRIMSON_SLAB, - WoodType::WARPED => Ids::WARPED_SLAB, - WoodType::CHERRY => Ids::CHERRY_SLAB, - }); - } - - public static function getLogIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_LOG, - WoodType::SPRUCE => Ids::SPRUCE_LOG, - WoodType::BIRCH => Ids::BIRCH_LOG, - WoodType::JUNGLE => Ids::JUNGLE_LOG, - WoodType::ACACIA => Ids::ACACIA_LOG, - WoodType::DARK_OAK => Ids::DARK_OAK_LOG, - WoodType::MANGROVE => Ids::MANGROVE_LOG, - WoodType::CRIMSON => Ids::CRIMSON_STEM, - WoodType::WARPED => Ids::WARPED_STEM, - WoodType::CHERRY => Ids::CHERRY_LOG, - }); - } - - public static function getAllSidedLogIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_WOOD, - WoodType::SPRUCE => Ids::SPRUCE_WOOD, - WoodType::BIRCH => Ids::BIRCH_WOOD, - WoodType::JUNGLE => Ids::JUNGLE_WOOD, - WoodType::ACACIA => Ids::ACACIA_WOOD, - WoodType::DARK_OAK => Ids::DARK_OAK_WOOD, - WoodType::MANGROVE => Ids::MANGROVE_WOOD, - WoodType::CRIMSON => Ids::CRIMSON_HYPHAE, - WoodType::WARPED => Ids::WARPED_HYPHAE, - WoodType::CHERRY => Ids::CHERRY_WOOD, - }); - } - - public static function getLeavesIdentifier(LeavesType $leavesType) : BID{ - return new BID(match($leavesType){ - LeavesType::OAK => Ids::OAK_LEAVES, - LeavesType::SPRUCE => Ids::SPRUCE_LEAVES, - LeavesType::BIRCH => Ids::BIRCH_LEAVES, - LeavesType::JUNGLE => Ids::JUNGLE_LEAVES, - LeavesType::ACACIA => Ids::ACACIA_LEAVES, - LeavesType::DARK_OAK => Ids::DARK_OAK_LEAVES, - LeavesType::MANGROVE => Ids::MANGROVE_LEAVES, - LeavesType::AZALEA => Ids::AZALEA_LEAVES, - LeavesType::FLOWERING_AZALEA => Ids::FLOWERING_AZALEA_LEAVES, - LeavesType::CHERRY => Ids::CHERRY_LEAVES, - }); - } - - public static function getSaplingIdentifier(SaplingType $treeType) : BID{ - return new BID(match($treeType){ - SaplingType::OAK => Ids::OAK_SAPLING, - SaplingType::SPRUCE => Ids::SPRUCE_SAPLING, - SaplingType::BIRCH => Ids::BIRCH_SAPLING, - SaplingType::JUNGLE => Ids::JUNGLE_SAPLING, - SaplingType::ACACIA => Ids::ACACIA_SAPLING, - SaplingType::DARK_OAK => Ids::DARK_OAK_SAPLING, - }); - } - - /** - * @return BID[]|\Closure[] - * @phpstan-return array{BID, BID, \Closure() : \pocketmine\item\Item} - */ - public static function getSignInfo(WoodType $treeType) : array{ - $make = fn(int $floorId, int $wallId, \Closure $getItem) => [ - new BID($floorId, TileSign::class), - new BID($wallId, TileSign::class), - $getItem - ]; - return match($treeType){ - WoodType::OAK => $make(Ids::OAK_SIGN, Ids::OAK_WALL_SIGN, fn() => VanillaItems::OAK_SIGN()), - WoodType::SPRUCE => $make(Ids::SPRUCE_SIGN, Ids::SPRUCE_WALL_SIGN, fn() => VanillaItems::SPRUCE_SIGN()), - WoodType::BIRCH => $make(Ids::BIRCH_SIGN, Ids::BIRCH_WALL_SIGN, fn() => VanillaItems::BIRCH_SIGN()), - WoodType::JUNGLE => $make(Ids::JUNGLE_SIGN, Ids::JUNGLE_WALL_SIGN, fn() => VanillaItems::JUNGLE_SIGN()), - WoodType::ACACIA => $make(Ids::ACACIA_SIGN, Ids::ACACIA_WALL_SIGN, fn() => VanillaItems::ACACIA_SIGN()), - WoodType::DARK_OAK => $make(Ids::DARK_OAK_SIGN, Ids::DARK_OAK_WALL_SIGN, fn() => VanillaItems::DARK_OAK_SIGN()), - WoodType::MANGROVE => $make(Ids::MANGROVE_SIGN, Ids::MANGROVE_WALL_SIGN, fn() => VanillaItems::MANGROVE_SIGN()), - WoodType::CRIMSON => $make(Ids::CRIMSON_SIGN, Ids::CRIMSON_WALL_SIGN, fn() => VanillaItems::CRIMSON_SIGN()), - WoodType::WARPED => $make(Ids::WARPED_SIGN, Ids::WARPED_WALL_SIGN, fn() => VanillaItems::WARPED_SIGN()), - WoodType::CHERRY => $make(Ids::CHERRY_SIGN, Ids::CHERRY_WALL_SIGN, fn() => VanillaItems::CHERRY_SIGN()), - }; - } - - public static function getTrapdoorIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_TRAPDOOR, - WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR, - WoodType::BIRCH => Ids::BIRCH_TRAPDOOR, - WoodType::JUNGLE => Ids::JUNGLE_TRAPDOOR, - WoodType::ACACIA => Ids::ACACIA_TRAPDOOR, - WoodType::DARK_OAK => Ids::DARK_OAK_TRAPDOOR, - WoodType::MANGROVE => Ids::MANGROVE_TRAPDOOR, - WoodType::CRIMSON => Ids::CRIMSON_TRAPDOOR, - WoodType::WARPED => Ids::WARPED_TRAPDOOR, - WoodType::CHERRY => Ids::CHERRY_TRAPDOOR, - }); - } - - public static function getButtonIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_BUTTON, - WoodType::SPRUCE => Ids::SPRUCE_BUTTON, - WoodType::BIRCH => Ids::BIRCH_BUTTON, - WoodType::JUNGLE => Ids::JUNGLE_BUTTON, - WoodType::ACACIA => Ids::ACACIA_BUTTON, - WoodType::DARK_OAK => Ids::DARK_OAK_BUTTON, - WoodType::MANGROVE => Ids::MANGROVE_BUTTON, - WoodType::CRIMSON => Ids::CRIMSON_BUTTON, - WoodType::WARPED => Ids::WARPED_BUTTON, - WoodType::CHERRY => Ids::CHERRY_BUTTON, - }); - } - - public static function getPressurePlateIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_PRESSURE_PLATE, - WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE, - WoodType::BIRCH => Ids::BIRCH_PRESSURE_PLATE, - WoodType::JUNGLE => Ids::JUNGLE_PRESSURE_PLATE, - WoodType::ACACIA => Ids::ACACIA_PRESSURE_PLATE, - WoodType::DARK_OAK => Ids::DARK_OAK_PRESSURE_PLATE, - WoodType::MANGROVE => Ids::MANGROVE_PRESSURE_PLATE, - WoodType::CRIMSON => Ids::CRIMSON_PRESSURE_PLATE, - WoodType::WARPED => Ids::WARPED_PRESSURE_PLATE, - WoodType::CHERRY => Ids::CHERRY_PRESSURE_PLATE, - }); - } - - public static function getDoorIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_DOOR, - WoodType::SPRUCE => Ids::SPRUCE_DOOR, - WoodType::BIRCH => Ids::BIRCH_DOOR, - WoodType::JUNGLE => Ids::JUNGLE_DOOR, - WoodType::ACACIA => Ids::ACACIA_DOOR, - WoodType::DARK_OAK => Ids::DARK_OAK_DOOR, - WoodType::MANGROVE => Ids::MANGROVE_DOOR, - WoodType::CRIMSON => Ids::CRIMSON_DOOR, - WoodType::WARPED => Ids::WARPED_DOOR, - WoodType::CHERRY => Ids::CHERRY_DOOR, - }); - } - - public static function getFenceGateIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_FENCE_GATE, - WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE, - WoodType::BIRCH => Ids::BIRCH_FENCE_GATE, - WoodType::JUNGLE => Ids::JUNGLE_FENCE_GATE, - WoodType::ACACIA => Ids::ACACIA_FENCE_GATE, - WoodType::DARK_OAK => Ids::DARK_OAK_FENCE_GATE, - WoodType::MANGROVE => Ids::MANGROVE_FENCE_GATE, - WoodType::CRIMSON => Ids::CRIMSON_FENCE_GATE, - WoodType::WARPED => Ids::WARPED_FENCE_GATE, - WoodType::CHERRY => Ids::CHERRY_FENCE_GATE, - }); - } - - public static function getStairsIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_STAIRS, - WoodType::SPRUCE => Ids::SPRUCE_STAIRS, - WoodType::BIRCH => Ids::BIRCH_STAIRS, - WoodType::JUNGLE => Ids::JUNGLE_STAIRS, - WoodType::ACACIA => Ids::ACACIA_STAIRS, - WoodType::DARK_OAK => Ids::DARK_OAK_STAIRS, - WoodType::MANGROVE => Ids::MANGROVE_STAIRS, - WoodType::CRIMSON => Ids::CRIMSON_STAIRS, - WoodType::WARPED => Ids::WARPED_STAIRS, - WoodType::CHERRY => Ids::CHERRY_STAIRS, - }); - } -} diff --git a/src/block/utils/MultiAnyFacingTrait.php b/src/block/utils/MultiAnyFacingTrait.php new file mode 100644 index 00000000000..66f26d98009 --- /dev/null +++ b/src/block/utils/MultiAnyFacingTrait.php @@ -0,0 +1,72 @@ +facingFlags($this->faces); + } + + /** @return int[] */ + public function getFaces() : array{ return $this->faces; } + + public function hasFace(int $face) : bool{ + return isset($this->faces[$face]); + } + + /** + * @param int[] $faces + * @return $this + */ + public function setFaces(array $faces) : self{ + $uniqueFaces = []; + foreach($faces as $face){ + Facing::validate($face); + $uniqueFaces[$face] = $face; + } + $this->faces = $uniqueFaces; + return $this; + } + + /** @return $this */ + public function setFace(int $face, bool $value) : self{ + Facing::validate($face); + if($value){ + $this->faces[$face] = $face; + }else{ + unset($this->faces[$face]); + } + return $this; + } +} diff --git a/src/block/utils/MultiAnySupportTrait.php b/src/block/utils/MultiAnySupportTrait.php new file mode 100644 index 00000000000..ae1da7befe1 --- /dev/null +++ b/src/block/utils/MultiAnySupportTrait.php @@ -0,0 +1,96 @@ +faces = $this->getInitialPlaceFaces($blockReplace); + $availableFaces = $this->getAvailableFaces(); + + if(count($availableFaces) === 0){ + return false; + } + + $opposite = Facing::opposite($face); + $placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces); + $this->faces[$placedFace] = $placedFace; + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function onNearbyBlockChange() : void{ + $changed = false; + + foreach($this->faces as $face){ + if($this->getAdjacentSupportType($face) !== SupportType::FULL){ + unset($this->faces[$face]); + $changed = true; + } + } + + if($changed){ + $world = $this->position->getWorld(); + if(count($this->faces) === 0){ + $world->useBreakOn($this->position); + }else{ + $world->setBlock($this->position, $this); + } + } + } + + /** + * @return array $faces + */ + private function getAvailableFaces() : array{ + $faces = []; + foreach(Facing::ALL as $face){ + if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){ + $faces[$face] = $face; + } + } + return $faces; + } +} diff --git a/src/command/ClosureCommand.php b/src/command/ClosureCommand.php new file mode 100644 index 00000000000..289c82853ec --- /dev/null +++ b/src/command/ClosureCommand.php @@ -0,0 +1,60 @@ + $args) : mixed + */ +final class ClosureCommand extends Command{ + /** @phpstan-var Execute */ + private \Closure $execute; + + /** + * @param string[] $permissions + * @phpstan-param Execute $execute + */ + public function __construct( + string $name, + \Closure $execute, + array $permissions, + Translatable|string $description = "", + Translatable|string|null $usageMessage = null, + array $aliases = [] + ){ + Utils::validateCallableSignature( + fn(CommandSender $sender, Command $command, string $commandLabel, array $args) : mixed => 1, + $execute, + ); + $this->execute = $execute; + parent::__construct($name, $description, $usageMessage, $aliases); + $this->setPermissions($permissions); + } + + public function execute(CommandSender $sender, string $commandLabel, array $args){ + return ($this->execute)($sender, $this, $commandLabel, $args); + } +} diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php index 06367994a04..e0d8e6565e8 100644 --- a/src/command/SimpleCommandMap.php +++ b/src/command/SimpleCommandMap.php @@ -64,6 +64,7 @@ use pocketmine\command\defaults\VanillaCommand; use pocketmine\command\defaults\VersionCommand; use pocketmine\command\defaults\WhitelistCommand; +use pocketmine\command\defaults\XpCommand; use pocketmine\command\utils\CommandStringHelper; use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\lang\KnownTranslationFactory; @@ -132,7 +133,8 @@ private function setDefaultCommands() : void{ new TitleCommand(), new TransferServerCommand(), new VersionCommand(), - new WhitelistCommand() + new WhitelistCommand(), + new XpCommand(), ]); } diff --git a/src/command/defaults/TimingsCommand.php b/src/command/defaults/TimingsCommand.php index 3c0701ea423..ec26f3efe09 100644 --- a/src/command/defaults/TimingsCommand.php +++ b/src/command/defaults/TimingsCommand.php @@ -26,28 +26,28 @@ use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; +use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; use pocketmine\scheduler\BulkCurlTask; use pocketmine\scheduler\BulkCurlTaskOperation; use pocketmine\timings\TimingsHandler; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\InternetException; use pocketmine\utils\InternetRequestResult; -use pocketmine\utils\Utils; use pocketmine\YmlServerProperties; use Symfony\Component\Filesystem\Path; use function count; use function fclose; use function file_exists; use function fopen; -use function fseek; use function fwrite; use function http_build_query; +use function implode; use function is_array; use function json_decode; use function mkdir; -use function stream_get_contents; use function strtolower; use const CURLOPT_AUTOREFERER; use const CURLOPT_FOLLOWLOCATION; @@ -101,82 +101,91 @@ public function execute(CommandSender $sender, string $commandLabel, array $args TimingsHandler::reload(); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset()); }elseif($mode === "merged" || $mode === "report" || $paste){ - $timings = ""; - if($paste){ - $fileTimings = Utils::assumeNotFalse(fopen("php://temp", "r+b"), "Opening php://temp should never fail"); - }else{ - $index = 0; - $timingFolder = Path::join($sender->getServer()->getDataPath(), "timings"); - - if(!file_exists($timingFolder)){ - mkdir($timingFolder, 0777); - } - $timings = Path::join($timingFolder, "timings.txt"); - while(file_exists($timings)){ - $timings = Path::join($timingFolder, "timings" . (++$index) . ".txt"); - } - - $fileTimings = fopen($timings, "a+b"); - } - $lines = TimingsHandler::printTimings(); - foreach($lines as $line){ - fwrite($fileTimings, $line . PHP_EOL); - } - - if($paste){ - fseek($fileTimings, 0); - $data = [ - "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(), - "data" => $content = stream_get_contents($fileTimings) - ]; - fclose($fileTimings); - - $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); - - $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( - [new BulkCurlTaskOperation( - "https://$host?upload=true", - 10, - [], - [ - CURLOPT_HTTPHEADER => [ - "User-Agent: $agent", - "Content-Type: application/x-www-form-urlencoded" - ], - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => http_build_query($data), - CURLOPT_AUTOREFERER => false, - CURLOPT_FOLLOWLOCATION => false - ] - )], - function(array $results) use ($sender, $host) : void{ - /** @phpstan-var array $results */ - if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender - return; - } - $result = $results[0]; - if($result instanceof InternetException){ - $sender->getServer()->getLogger()->logException($result); - return; - } - $response = json_decode($result->getBody(), true); - if(is_array($response) && isset($response["id"])){ - Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead( - "https://" . $host . "/?id=" . $response["id"])); - }else{ - $sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody()); - Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError()); - } - } - )); - }else{ - fclose($fileTimings); - Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings)); - } + $timingsPromise = TimingsHandler::requestPrintTimings(); + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect()); + $timingsPromise->onCompletion( + fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender), + fn() => throw new AssumptionFailedError("This promise is not expected to be rejected") + ); }else{ throw new InvalidCommandSyntaxException(); } return true; } + + /** + * @param string[] $lines + * @phpstan-param list $lines + */ + private function createReportFile(array $lines, CommandSender $sender) : void{ + $index = 0; + $timingFolder = Path::join($sender->getServer()->getDataPath(), "timings"); + + if(!file_exists($timingFolder)){ + mkdir($timingFolder, 0777); + } + $timings = Path::join($timingFolder, "timings.txt"); + while(file_exists($timings)){ + $timings = Path::join($timingFolder, "timings" . (++$index) . ".txt"); + } + + $fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b")); + foreach($lines as $line){ + fwrite($fileTimings, $line . PHP_EOL); + } + fclose($fileTimings); + + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings)); + } + + /** + * @param string[] $lines + * @phpstan-param list $lines + */ + private function uploadReport(array $lines, CommandSender $sender) : void{ + $data = [ + "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(), + "data" => implode("\n", $lines) + ]; + + $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); + + $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( + [new BulkCurlTaskOperation( + "https://$host?upload=true", + 10, + [], + [ + CURLOPT_HTTPHEADER => [ + "User-Agent: $agent", + "Content-Type: application/x-www-form-urlencoded" + ], + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query($data), + CURLOPT_AUTOREFERER => false, + CURLOPT_FOLLOWLOCATION => false + ] + )], + function(array $results) use ($sender, $host) : void{ + /** @phpstan-var array $results */ + if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender + return; + } + $result = $results[0]; + if($result instanceof InternetException){ + $sender->getServer()->getLogger()->logException($result); + return; + } + $response = json_decode($result->getBody(), true); + if(is_array($response) && isset($response["id"])){ + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead( + "https://" . $host . "/?id=" . $response["id"])); + }else{ + $sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody()); + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError()); + } + } + )); + } } diff --git a/src/command/defaults/XpCommand.php b/src/command/defaults/XpCommand.php new file mode 100644 index 00000000000..cb035236574 --- /dev/null +++ b/src/command/defaults/XpCommand.php @@ -0,0 +1,89 @@ +setPermissions([ + DefaultPermissionNames::COMMAND_XP_SELF, + DefaultPermissionNames::COMMAND_XP_OTHER + ]); + } + + public function execute(CommandSender $sender, string $commandLabel, array $args){ + if(count($args) < 1){ + throw new InvalidCommandSyntaxException(); + } + + $player = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER); + if($player === null){ + return true; + } + + $xpManager = $player->getXpManager(); + if(str_ends_with($args[0], "L")){ + $xpLevelAttr = $player->getAttributeMap()->get(Attribute::EXPERIENCE_LEVEL) ?? throw new AssumptionFailedError(); + $maxXpLevel = (int) $xpLevelAttr->getMaxValue(); + $currentXpLevel = $xpManager->getXpLevel(); + $xpLevels = $this->getInteger($sender, substr($args[0], 0, -1), -$currentXpLevel, $maxXpLevel - $currentXpLevel); + if($xpLevels >= 0){ + $xpManager->addXpLevels($xpLevels, false); + $sender->sendMessage(KnownTranslationFactory::commands_xp_success_levels((string) $xpLevels, $player->getName())); + }else{ + $xpLevels = abs($xpLevels); + $xpManager->subtractXpLevels($xpLevels); + $sender->sendMessage(KnownTranslationFactory::commands_xp_success_negative_levels((string) $xpLevels, $player->getName())); + } + }else{ + $xp = $this->getInteger($sender, $args[0], max: Limits::INT32_MAX); + if($xp < 0){ + $sender->sendMessage(KnownTranslationFactory::commands_xp_failure_widthdrawXp()->prefix(TextFormat::RED)); + }else{ + $xpManager->addXp($xp, false); + $sender->sendMessage(KnownTranslationFactory::commands_xp_success((string) $xp, $player->getName())); + } + } + + return true; + } +} diff --git a/src/console/ConsoleReaderChildProcess.php b/src/console/ConsoleReaderChildProcess.php index 0d5a30fdcc0..c0c4f0048f1 100644 --- a/src/console/ConsoleReaderChildProcess.php +++ b/src/console/ConsoleReaderChildProcess.php @@ -29,23 +29,21 @@ use function cli_set_process_title; use function count; use function dirname; -use function feof; use function fwrite; -use function stream_socket_client; +use function is_numeric; +use const PHP_EOL; +use const STDOUT; -require dirname(__DIR__, 2) . '/vendor/autoload.php'; - -if(count($argv) !== 2){ - die("Please provide a server to connect to"); +if(count($argv) !== 2 || !is_numeric($argv[1])){ + echo "Usage: " . $argv[0] . " " . PHP_EOL; + exit(1); } +$commandTokenSeed = (int) $argv[1]; + +require dirname(__DIR__, 2) . '/vendor/autoload.php'; + @cli_set_process_title('PocketMine-MP Console Reader'); -$errCode = null; -$errMessage = null; -$socket = stream_socket_client($argv[1], $errCode, $errMessage, 15.0); -if($socket === false){ - throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage"); -} /** @phpstan-var ThreadSafeArray $channel */ $channel = new ThreadSafeArray(); @@ -75,15 +73,15 @@ public function run() : void{ }; $thread->start(NativeThread::INHERIT_NONE); -while(!feof($socket)){ +while(true){ $line = $channel->synchronized(function() use ($channel) : ?string{ if(count($channel) === 0){ $channel->wait(1_000_000); } - $line = $channel->shift(); - return $line; + return $channel->shift(); }); - if(@fwrite($socket, ($line ?? "") . "\n") === false){ + $message = $line !== null ? ConsoleReaderChildProcessUtils::createMessage($line, $commandTokenSeed) : ""; + if(@fwrite(STDOUT, $message . "\n") === false){ //Always send even if there's no line, to check if the parent is alive //If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return //false even though the connection is actually broken. However, fwrite() will fail. diff --git a/src/console/ConsoleReaderChildProcessDaemon.php b/src/console/ConsoleReaderChildProcessDaemon.php index 138559f06e9..f7300b7a520 100644 --- a/src/console/ConsoleReaderChildProcessDaemon.php +++ b/src/console/ConsoleReaderChildProcessDaemon.php @@ -29,19 +29,16 @@ use function base64_encode; use function fgets; use function fopen; +use function mt_rand; use function preg_replace; use function proc_close; use function proc_open; use function proc_terminate; +use function rtrim; use function sprintf; use function stream_select; -use function stream_socket_accept; -use function stream_socket_get_name; -use function stream_socket_server; -use function stream_socket_shutdown; use function trim; use const PHP_BINARY; -use const STREAM_SHUT_RDWR; /** * This pile of shit exists because PHP on Windows is broken, and can't handle stream_select() on stdin or pipes @@ -58,44 +55,44 @@ * communication. */ final class ConsoleReaderChildProcessDaemon{ + public const TOKEN_DELIMITER = ":"; + public const TOKEN_HASH_ALGO = "xxh3"; + private \PrefixedLogger $logger; /** @var resource */ private $subprocess; /** @var resource */ private $socket; + private int $commandTokenSeed; public function __construct( \Logger $logger ){ $this->logger = new \PrefixedLogger($logger, "Console Reader Daemon"); + $this->commandTokenSeed = mt_rand(); $this->prepareSubprocess(); } private function prepareSubprocess() : void{ - $server = stream_socket_server("tcp://127.0.0.1:0"); - if($server === false){ - throw new \RuntimeException("Failed to open console reader socket server"); - } - $address = Utils::assumeNotFalse(stream_socket_get_name($server, false), "stream_socket_get_name() shouldn't return false here"); - //Windows sucks, and likes to corrupt UTF-8 file paths when they travel to the subprocess, so we base64 encode //the path to avoid the problem. This is an abysmally shitty hack, but here we are :( $sub = Utils::assumeNotFalse(proc_open( - [PHP_BINARY, '-dopcache.enable_cli=0', '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address], [ + PHP_BINARY, + '-dopcache.enable_cli=0', + '-r', + sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), + (string) $this->commandTokenSeed + ], + [ + 1 => ['socket'], 2 => fopen("php://stderr", "w"), ], $pipes ), "Something has gone horribly wrong"); - $client = stream_socket_accept($server, 15); - if($client === false){ - throw new AssumptionFailedError("stream_socket_accept() returned false"); - } - stream_socket_shutdown($server, STREAM_SHUT_RDWR); - $this->subprocess = $sub; - $this->socket = $client; + $this->socket = $pipes[1]; } private function shutdownSubprocess() : void{ @@ -104,7 +101,6 @@ private function shutdownSubprocess() : void{ //the first place). proc_terminate($this->subprocess); proc_close($this->subprocess); - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); } public function readLine() : ?string{ @@ -112,13 +108,27 @@ public function readLine() : ?string{ $w = null; $e = null; if(stream_select($r, $w, $e, 0, 0) === 1){ - $command = fgets($this->socket); - if($command === false){ + $line = fgets($this->socket); + if($line === false){ $this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)"); $this->shutdownSubprocess(); $this->prepareSubprocess(); return null; } + $line = rtrim($line, "\n"); + + if($line === ""){ + //keepalive + return null; + } + + $command = ConsoleReaderChildProcessUtils::parseMessage($line, $this->commandTokenSeed); + if($command === null){ + //this is not a command - it may be some kind of error output from the subprocess + //write it directly to the console + $this->logger->warning("Unexpected output from child process: $line"); + return null; + } $command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); $command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); diff --git a/src/console/ConsoleReaderChildProcessUtils.php b/src/console/ConsoleReaderChildProcessUtils.php new file mode 100644 index 00000000000..661e9b0f7bb --- /dev/null +++ b/src/console/ConsoleReaderChildProcessUtils.php @@ -0,0 +1,71 @@ + $counter]); + $counter++; + return $line . self::TOKEN_DELIMITER . $token; + } + + /** + * Extracts a command from an IPC message from the console reader subprocess. + * Returns the user's input command, or null if this isn't a user input. + */ + public static function parseMessage(string $message, int &$counter) : ?string{ + $delimiterPos = strrpos($message, self::TOKEN_DELIMITER); + if($delimiterPos !== false){ + $left = substr($message, 0, $delimiterPos); + $right = substr($message, $delimiterPos + strlen(self::TOKEN_DELIMITER)); + $expectedToken = hash(self::TOKEN_HASH_ALGO, $left, options: ['seed' => $counter]); + + if($expectedToken === $right){ + $counter++; + return $left; + } + } + + return null; + } +} diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php index e3d652b19ce..90a10dc20a9 100644 --- a/src/data/bedrock/EnchantmentIdMap.php +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -66,5 +66,7 @@ private function __construct(){ $this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING()); $this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK()); + + $this->register(EnchantmentIds::FROST_WALKER, VanillaEnchantments::FROST_WALKER()); } } diff --git a/src/data/bedrock/GoatHornTypeIdMap.php b/src/data/bedrock/GoatHornTypeIdMap.php new file mode 100644 index 00000000000..0510a09cee7 --- /dev/null +++ b/src/data/bedrock/GoatHornTypeIdMap.php @@ -0,0 +1,48 @@ + */ + use IntSaveIdMapTrait; + + private function __construct(){ + foreach(GoatHornType::cases() as $case){ + $this->register(match($case){ + GoatHornType::PONDER => GoatHornTypeIds::PONDER, + GoatHornType::SING => GoatHornTypeIds::SING, + GoatHornType::SEEK => GoatHornTypeIds::SEEK, + GoatHornType::FEEL => GoatHornTypeIds::FEEL, + GoatHornType::ADMIRE => GoatHornTypeIds::ADMIRE, + GoatHornType::CALL => GoatHornTypeIds::CALL, + GoatHornType::YEARN => GoatHornTypeIds::YEARN, + GoatHornType::DREAM => GoatHornTypeIds::DREAM + }, $case); + } + } +} diff --git a/src/data/bedrock/GoatHornTypeIds.php b/src/data/bedrock/GoatHornTypeIds.php new file mode 100644 index 00000000000..048d246fe0a --- /dev/null +++ b/src/data/bedrock/GoatHornTypeIds.php @@ -0,0 +1,35 @@ +map1to1Item(Ids::EMERALD, Items::EMERALD()); $this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK()); $this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE()); + $this->map1to1Item(Ids::END_CRYSTAL, Items::END_CRYSTAL()); $this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL()); $this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE()); $this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); @@ -263,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()); @@ -348,6 +352,7 @@ private function register1to1ItemMappings() : void{ $this->map1to1Item(Ids::RAW_COPPER, Items::RAW_COPPER()); $this->map1to1Item(Ids::RAW_GOLD, Items::RAW_GOLD()); $this->map1to1Item(Ids::RAW_IRON, Items::RAW_IRON()); + $this->map1to1Item(Ids::RECOVERY_COMPASS, Items::RECOVERY_COMPASS()); $this->map1to1Item(Ids::REDSTONE, Items::REDSTONE_DUST()); $this->map1to1Item(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::ROTTEN_FLESH, Items::ROTTEN_FLESH()); @@ -483,6 +488,14 @@ function(Banner $item, int $meta) : void{ }, fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor()) ); + $this->map1to1ItemWithMeta( + Ids::GOAT_HORN, + Items::GOAT_HORN(), + function(GoatHorn $item, int $meta) : void{ + $item->setHornType(GoatHornTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown goat horn type ID $meta")); + }, + fn(GoatHorn $item) => GoatHornTypeIdMap::getInstance()->toId($item->getHornType()) + ); $this->map1to1ItemWithMeta( Ids::MEDICINE, Items::MEDICINE(), diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 3d791ebca63..9bd0de9eace 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntityTeleportEvent; +use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector2; @@ -74,7 +75,6 @@ use function floor; use function fmod; use function get_class; -use function lcg_value; use function sin; use function spl_object_id; use const M_PI_2; @@ -909,7 +909,7 @@ protected function checkObstruction(float $x, float $y, float $z) : bool{ return false; } - $force = lcg_value() * 0.2 + 0.1; + $force = Utils::getRandomFloat() * 0.2 + 0.1; $this->motion = match($direction){ Facing::WEST => $this->motion->withComponents(-$force, null, null), @@ -1563,6 +1563,13 @@ public function despawnFromAll() : void{ $this->hasSpawned = []; } + /** + * Returns the item that players will equip when middle-clicking on this entity. + */ + public function getPickedItem() : ?Item{ + return null; + } + /** * Flags the entity to be removed from the world on the next tick. */ diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index d8d189cffc1..03d9c03e6be 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -32,6 +32,7 @@ use pocketmine\data\bedrock\PotionTypeIds; use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\EntityDataHelper as Helper; +use pocketmine\entity\object\EndCrystal; use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\FallingBlock; use pocketmine\entity\object\ItemEntity; @@ -42,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; @@ -92,6 +94,10 @@ public function __construct(){ return new Egg(Helper::parseLocation($nbt, $world), null, $nbt); }, ['Egg', 'minecraft:egg']); + $this->register(EndCrystal::class, function(World $world, CompoundTag $nbt) : EndCrystal{ + return new EndCrystal(Helper::parseLocation($nbt, $world), $nbt); + }, ['EnderCrystal', 'minecraft:ender_crystal']); + $this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{ return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt); }, ['ThrownEnderpearl', 'minecraft:ender_pearl']); @@ -115,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/FoodSource.php b/src/entity/FoodSource.php index 98478b4a1da..028c7678382 100644 --- a/src/entity/FoodSource.php +++ b/src/entity/FoodSource.php @@ -34,6 +34,7 @@ public function getSaturationRestore() : float; /** * Returns whether a Human eating this FoodSource must have a non-full hunger bar. + * This is ignored in creative mode and in peaceful difficulty. */ public function requiresHunger() : bool; } diff --git a/src/entity/Human.php b/src/entity/Human.php index f2c4c7a7405..833196651c7 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -68,6 +68,7 @@ use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\player\Player; use pocketmine\world\sound\TotemUseSound; +use pocketmine\world\World; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; use function array_fill; @@ -189,8 +190,16 @@ public function getHungerManager() : HungerManager{ return $this->hungerManager; } + /** + * Returns whether the Human can eat food. This may return a different result than {@link HungerManager::isHungry()}, + * as HungerManager only handles the hunger bar. + */ + public function canEat() : bool{ + return $this->hungerManager->isHungry() || $this->getWorld()->getDifficulty() === World::DIFFICULTY_PEACEFUL; + } + public function consumeObject(Consumable $consumable) : bool{ - if($consumable instanceof FoodSource && $consumable->requiresHunger() && !$this->hungerManager->isHungry()){ + if($consumable instanceof FoodSource && $consumable->requiresHunger() && !$this->canEat()){ return false; } diff --git a/src/entity/HungerManager.php b/src/entity/HungerManager.php index a3185589187..7e3b40e745f 100644 --- a/src/entity/HungerManager.php +++ b/src/entity/HungerManager.php @@ -88,7 +88,8 @@ public function addFood(float $amount) : void{ } /** - * Returns whether this Human may consume objects requiring hunger. + * Returns whether the food level is below the maximum. + * This doesn't decide if the entity can eat food. Use {@link Human::canEat()} for that. */ public function isHungry() : bool{ return $this->getFood() < $this->getMaxFood(); diff --git a/src/entity/Living.php b/src/entity/Living.php index 81f46424f17..852344784a6 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -25,6 +25,8 @@ use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; +use pocketmine\block\VanillaBlocks; +use pocketmine\block\Water; use pocketmine\data\bedrock\EffectIdMap; use pocketmine\entity\animation\DeathAnimation; use pocketmine\entity\animation\HurtAnimation; @@ -44,6 +46,7 @@ use pocketmine\item\enchantment\Enchantment; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; +use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\math\VoxelRayTrace; use pocketmine\nbt\tag\CompoundTag; @@ -58,18 +61,19 @@ use pocketmine\player\Player; use pocketmine\timings\Timings; use pocketmine\utils\Binary; +use pocketmine\utils\Utils; use pocketmine\world\sound\BurpSound; use pocketmine\world\sound\EntityLandSound; use pocketmine\world\sound\EntityLongFallSound; use pocketmine\world\sound\EntityShortFallSound; use pocketmine\world\sound\ItemBreakSound; +use function abs; use function array_shift; use function atan2; use function ceil; use function count; use function floor; use function ksort; -use function lcg_value; use function max; use function min; use function mt_getrandmax; @@ -128,6 +132,8 @@ abstract class Living extends Entity{ protected bool $gliding = false; protected bool $swimming = false; + private ?int $frostWalkerLevel = null; + protected function getInitialDragMultiplier() : float{ return 0.02; } protected function getInitialGravity() : float{ return 0.08; } @@ -151,6 +157,14 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->getViewers(), fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this) ))); + $this->armorInventory->getListeners()->add(new CallbackInventoryListener( + onSlotChange: function(Inventory $inventory, int $slot) : void{ + if($slot === ArmorInventory::SLOT_FEET){ + $this->frostWalkerLevel = null; + } + }, + onContentChange: function() : void{ $this->frostWalkerLevel = null; } + )); $health = $this->getMaxHealth(); @@ -490,7 +504,7 @@ protected function applyPostDamageEffects(EntityDamageEvent $source) : void{ $helmet = $this->armorInventory->getHelmet(); if($helmet instanceof Armor){ $finalDamage = $source->getFinalDamage(); - $this->damageItem($helmet, (int) round($finalDamage * 4 + lcg_value() * $finalDamage * 2)); + $this->damageItem($helmet, (int) round($finalDamage * 4 + Utils::getRandomFloat() * $finalDamage * 2)); $this->armorInventory->setHelmet($helmet); } } @@ -687,6 +701,47 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ return $hasUpdate; } + protected function move(float $dx, float $dy, float $dz) : void{ + $oldX = $this->location->x; + $oldZ = $this->location->z; + + parent::move($dx, $dy, $dz); + + $frostWalkerLevel = $this->getFrostWalkerLevel(); + if($frostWalkerLevel > 0 && (abs($this->location->x - $oldX) > self::MOTION_THRESHOLD || abs($this->location->z - $oldZ) > self::MOTION_THRESHOLD)){ + $this->applyFrostWalker($frostWalkerLevel); + } + } + + protected function applyFrostWalker(int $level) : void{ + $radius = $level + 2; + $world = $this->getWorld(); + + $baseX = $this->location->getFloorX(); + $y = $this->location->getFloorY() - 1; + $baseZ = $this->location->getFloorZ(); + + $frostedIce = VanillaBlocks::FROSTED_ICE(); + for($x = $baseX - $radius; $x <= $baseX + $radius; $x++){ + for($z = $baseZ - $radius; $z <= $baseZ + $radius; $z++){ + $block = $world->getBlockAt($x, $y, $z); + if( + !$block instanceof Water || + !$block->isSource() || + $world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR || + count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0 + ){ + continue; + } + $world->setBlockAt($x, $y, $z, $frostedIce); + } + } + } + + public function getFrostWalkerLevel() : int{ + return $this->frostWalkerLevel ??= $this->armorInventory->getBoots()->getEnchantmentLevel(VanillaEnchantments::FROST_WALKER()); + } + /** * Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water. */ @@ -697,7 +752,7 @@ protected function doAirSupplyTick(int $tickDiff) : bool{ $this->setBreathing(false); if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(VanillaEnchantments::RESPIRATION())) <= 0 || - lcg_value() <= (1 / ($respirationLevel + 1)) + Utils::getRandomFloat() <= (1 / ($respirationLevel + 1)) ){ $ticks -= $tickDiff; if($ticks <= -20){ diff --git a/src/entity/Squid.php b/src/entity/Squid.php index 75c50061b98..a0f3fef48b9 100644 --- a/src/entity/Squid.php +++ b/src/entity/Squid.php @@ -26,6 +26,7 @@ use pocketmine\entity\animation\SquidInkCloudAnimation; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; @@ -124,4 +125,8 @@ public function getDrops() : array{ VanillaItems::INK_SAC()->setCount(mt_rand(1, 3)) ]; } + + public function getPickedItem() : ?Item{ + return VanillaItems::SQUID_SPAWN_EGG(); + } } diff --git a/src/entity/Villager.php b/src/entity/Villager.php index 376401a5df2..fee65b01437 100644 --- a/src/entity/Villager.php +++ b/src/entity/Villager.php @@ -23,6 +23,8 @@ namespace pocketmine\entity; +use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; @@ -87,6 +89,10 @@ public function isBaby() : bool{ return $this->baby; } + public function getPickedItem() : ?Item{ + return VanillaItems::VILLAGER_SPAWN_EGG(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); $properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); diff --git a/src/entity/Zombie.php b/src/entity/Zombie.php index 18fc2207e7f..159a2dd2586 100644 --- a/src/entity/Zombie.php +++ b/src/entity/Zombie.php @@ -23,6 +23,7 @@ namespace pocketmine\entity; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use function mt_rand; @@ -65,4 +66,8 @@ public function getXpDropAmount() : int{ //TODO: check for equipment and whether it's a baby return 5; } + + public function getPickedItem() : ?Item{ + return VanillaItems::ZOMBIE_SPAWN_EGG(); + } } diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php new file mode 100644 index 00000000000..afaeb676938 --- /dev/null +++ b/src/entity/object/EndCrystal.php @@ -0,0 +1,146 @@ +showBase; + } + + public function setShowBase(bool $showBase) : void{ + $this->showBase = $showBase; + $this->networkPropertiesDirty = true; + } + + public function getBeamTarget() : ?Vector3{ + return $this->beamTarget; + } + + public function setBeamTarget(?Vector3 $beamTarget) : void{ + $this->beamTarget = $beamTarget; + $this->networkPropertiesDirty = true; + } + + public function attack(EntityDamageEvent $source) : void{ + parent::attack($source); + if( + $source->getCause() !== EntityDamageEvent::CAUSE_VOID && + !$this->isFlaggedForDespawn() && + !$source->isCancelled() + ){ + $this->flagForDespawn(); + $this->explode(); + } + } + + protected function initEntity(CompoundTag $nbt) : void{ + parent::initEntity($nbt); + + $this->setMaxHealth(1); + $this->setHealth(1); + + $this->setShowBase($nbt->getByte(self::TAG_SHOWBASE, 0) === 1); + + if( + ($beamXTag = $nbt->getTag(self::TAG_BLOCKTARGET_X)) instanceof IntTag && + ($beamYTag = $nbt->getTag(self::TAG_BLOCKTARGET_Y)) instanceof IntTag && + ($beamZTag = $nbt->getTag(self::TAG_BLOCKTARGET_Z)) instanceof IntTag + ){ + $this->setBeamTarget(new Vector3($beamXTag->getValue(), $beamYTag->getValue(), $beamZTag->getValue())); + } + } + + public function saveNBT() : CompoundTag{ + $nbt = parent::saveNBT(); + + $nbt->setByte(self::TAG_SHOWBASE, $this->showBase ? 1 : 0); + if($this->beamTarget !== null){ + $nbt->setInt(self::TAG_BLOCKTARGET_X, $this->beamTarget->getFloorX()); + $nbt->setInt(self::TAG_BLOCKTARGET_Y, $this->beamTarget->getFloorY()); + $nbt->setInt(self::TAG_BLOCKTARGET_Z, $this->beamTarget->getFloorZ()); + } + return $nbt; + } + + public function explode() : void{ + $ev = new EntityPreExplodeEvent($this, 6); + $ev->call(); + if(!$ev->isCancelled()){ + $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this); + if($ev->isBlockBreaking()){ + $explosion->explodeA(); + } + $explosion->explodeB(); + } + } + + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ + parent::syncNetworkData($properties); + + $properties->setGenericFlag(EntityMetadataFlags::SHOWBASE, $this->showBase); + $properties->setBlockPos(EntityMetadataProperties::BLOCK_TARGET, BlockPosition::fromVector3($this->beamTarget ?? Vector3::zero())); + } +} diff --git a/src/entity/object/FallingBlock.php b/src/entity/object/FallingBlock.php index 9d8af893470..66d4049f88a 100644 --- a/src/entity/object/FallingBlock.php +++ b/src/entity/object/FallingBlock.php @@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityBlockChangeEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; @@ -194,6 +195,10 @@ public function saveNBT() : CompoundTag{ return $nbt; } + public function getPickedItem() : ?Item{ + return $this->block->asItem(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); diff --git a/src/entity/object/Painting.php b/src/entity/object/Painting.php index f6449883c7d..641d040510d 100644 --- a/src/entity/object/Painting.php +++ b/src/entity/object/Painting.php @@ -28,6 +28,7 @@ use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Location; use pocketmine\event\entity\EntityDamageByEntityEvent; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; @@ -165,6 +166,10 @@ protected function sendSpawnPacket(Player $player) : void{ )); } + public function getPickedItem() : ?Item{ + return VanillaItems::PAINTING(); + } + /** * Returns the painting motive (which image is displayed on the painting) */ diff --git a/src/entity/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index ec621adfb80..af3c9792280 100644 --- a/src/entity/object/PrimedTNT.php +++ b/src/entity/object/PrimedTNT.php @@ -23,11 +23,13 @@ namespace pocketmine\entity\object; +use pocketmine\block\VanillaBlocks; use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Explosive; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityPreExplodeEvent; +use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; @@ -127,6 +129,10 @@ public function explode() : void{ } } + public function getPickedItem() : ?Item{ + return VanillaBlocks::TNT()->setWorksUnderwater($this->worksUnderwater)->asItem(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); 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/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 55950b6a6d1..0abc274b5dd 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -28,6 +28,7 @@ use pocketmine\entity\Entity; use pocketmine\entity\Living; use pocketmine\entity\Location; +use pocketmine\entity\object\EndCrystal; use pocketmine\event\entity\EntityCombustByEntityEvent; use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; @@ -96,7 +97,7 @@ protected function initEntity(CompoundTag $nbt) : void{ } public function canCollideWith(Entity $entity) : bool{ - return $entity instanceof Living && !$this->onGround; + return ($entity instanceof Living || $entity instanceof EndCrystal) && !$this->onGround; } public function canBeCollidedWith() : bool{ diff --git a/src/event/player/PlayerEntityPickEvent.php b/src/event/player/PlayerEntityPickEvent.php new file mode 100644 index 00000000000..3c742d6c489 --- /dev/null +++ b/src/event/player/PlayerEntityPickEvent.php @@ -0,0 +1,53 @@ +player = $player; + } + + public function getEntity() : Entity{ + return $this->entityClicked; + } + + public function getResultItem() : Item{ + return $this->resultItem; + } +} diff --git a/src/event/player/PlayerInteractEvent.php b/src/event/player/PlayerInteractEvent.php index 6119d5e99cb..46daf70813d 100644 --- a/src/event/player/PlayerInteractEvent.php +++ b/src/event/player/PlayerInteractEvent.php @@ -42,6 +42,9 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{ protected Vector3 $touchVector; + protected bool $useItem = true; + protected bool $useBlock = true; + public function __construct( Player $player, protected Item $item, @@ -73,4 +76,28 @@ public function getTouchVector() : Vector3{ public function getFace() : int{ return $this->blockFace; } + + /** + * Returns whether the item may react to the interaction. If disabled, items such as spawn eggs will not activate. + * This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking. + */ + public function useItem() : bool{ return $this->useItem; } + + /** + * Sets whether the used item may react to the interaction. If false, items such as spawn eggs will not activate. + * This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking. + */ + public function setUseItem(bool $useItem) : void{ $this->useItem = $useItem; } + + /** + * Returns whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not + * respond, containers will not open, etc. + */ + public function useBlock() : bool{ return $this->useBlock; } + + /** + * Sets whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not + * respond, containers will not open, etc. + */ + public function setUseBlock(bool $useBlock) : void{ $this->useBlock = $useBlock; } } diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php index b3465a8dfb1..47290e40151 100644 --- a/src/inventory/transaction/InventoryTransaction.php +++ b/src/inventory/transaction/InventoryTransaction.php @@ -111,6 +111,9 @@ public function addAction(InventoryAction $action) : void{ if(!isset($this->actions[$hash = spl_object_id($action)])){ $this->actions[$hash] = $action; $action->onAddToTransaction($this); + if($action instanceof SlotChangeAction && !isset($this->inventories[$inventoryId = spl_object_id($action->getInventory())])){ + $this->inventories[$inventoryId] = $action->getInventory(); + } }else{ throw new \InvalidArgumentException("Tried to add the same action to a transaction twice"); } @@ -129,16 +132,6 @@ private function shuffleActions() : void{ $this->actions = $actions; } - /** - * @internal This method should not be used by plugins, it's used to add tracked inventories for InventoryActions - * involving inventories. - */ - public function addInventory(Inventory $inventory) : void{ - if(!isset($this->inventories[$hash = spl_object_id($inventory)])){ - $this->inventories[$hash] = $inventory; - } - } - /** * @param Item[] $needItems * @param Item[] $haveItems diff --git a/src/inventory/transaction/action/InventoryAction.php b/src/inventory/transaction/action/InventoryAction.php index fff3d22b809..2f0db083c9f 100644 --- a/src/inventory/transaction/action/InventoryAction.php +++ b/src/inventory/transaction/action/InventoryAction.php @@ -60,6 +60,7 @@ abstract public function validate(Player $source) : void; /** * Called when the action is added to the specified InventoryTransaction. + * @deprecated */ public function onAddToTransaction(InventoryTransaction $transaction) : void{ diff --git a/src/inventory/transaction/action/SlotChangeAction.php b/src/inventory/transaction/action/SlotChangeAction.php index 68c3dba1b32..3c9b8e5cf79 100644 --- a/src/inventory/transaction/action/SlotChangeAction.php +++ b/src/inventory/transaction/action/SlotChangeAction.php @@ -25,7 +25,6 @@ use pocketmine\inventory\Inventory; use pocketmine\inventory\SlotValidatedInventory; -use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\Item; use pocketmine\player\Player; @@ -85,13 +84,6 @@ public function validate(Player $source) : void{ } } - /** - * Adds this action's target inventory to the transaction's inventory list. - */ - public function onAddToTransaction(InventoryTransaction $transaction) : void{ - $transaction->addInventory($this->inventory); - } - /** * Sets the item into the target inventory. */ diff --git a/src/item/Armor.php b/src/item/Armor.php index 417c57f75ca..63a8003adc9 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -33,7 +33,7 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\player\Player; use pocketmine\utils\Binary; -use function lcg_value; +use pocketmine\utils\Utils; use function mt_rand; class Armor extends Durable{ @@ -129,7 +129,7 @@ protected function getUnbreakingDamageReduction(int $amount) : int{ $chance = 1 / ($unbreakingLevel + 1); for($i = 0; $i < $amount; ++$i){ - if(mt_rand(1, 100) > 60 && lcg_value() > $chance){ //unbreaking only applies to armor 40% of the time at best + if(mt_rand(1, 100) > 60 && Utils::getRandomFloat() > $chance){ //unbreaking only applies to armor 40% of the time at best $negated++; } } diff --git a/src/item/Durable.php b/src/item/Durable.php index f110f6ea517..069a01202ed 100644 --- a/src/item/Durable.php +++ b/src/item/Durable.php @@ -25,7 +25,7 @@ use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\nbt\tag\CompoundTag; -use function lcg_value; +use pocketmine\utils\Utils; use function min; abstract class Durable extends Item{ @@ -87,7 +87,7 @@ protected function getUnbreakingDamageReduction(int $amount) : int{ $chance = 1 / ($unbreakingLevel + 1); for($i = 0; $i < $amount; ++$i){ - if(lcg_value() > $chance){ + if(Utils::getRandomFloat() > $chance){ $negated++; } } diff --git a/src/item/EndCrystal.php b/src/item/EndCrystal.php new file mode 100644 index 00000000000..320d657e6df --- /dev/null +++ b/src/item/EndCrystal.php @@ -0,0 +1,59 @@ +getTypeId() === BlockTypeIds::OBSIDIAN || $blockClicked->getTypeId() === BlockTypeIds::BEDROCK){ + $pos = $blockClicked->getPosition(); + $world = $pos->getWorld(); + $bb = AxisAlignedBB::one() + ->offset($pos->getX(), $pos->getY(), $pos->getZ()) + ->extend(Facing::UP, 1); + if( + count($world->getNearbyEntities($bb)) === 0 && + $blockClicked->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR && + $blockClicked->getSide(Facing::UP, 2)->getTypeId() === BlockTypeIds::AIR + ){ + $crystal = new EntityEndCrystal(Location::fromObject($pos->add(0.5, 1, 0.5), $world)); + $crystal->spawnToAll(); + + $this->pop(); + return ItemUseResult::SUCCESS; + } + } + return ItemUseResult::NONE; + } +} diff --git a/src/item/Food.php b/src/item/Food.php index 1950c4b14ae..d01ce9e18d8 100644 --- a/src/item/Food.php +++ b/src/item/Food.php @@ -44,6 +44,6 @@ public function onConsume(Living $consumer) : void{ } public function canStartUsingItem(Player $player) : bool{ - return !$this->requiresHunger() || $player->getHungerManager()->isHungry(); + return !$this->requiresHunger() || $player->canEat(); } } diff --git a/src/item/GoatHorn.php b/src/item/GoatHorn.php new file mode 100644 index 00000000000..088701e3986 --- /dev/null +++ b/src/item/GoatHorn.php @@ -0,0 +1,71 @@ +enum($this->goatHornType); + } + + public function getHornType() : GoatHornType{ return $this->goatHornType; } + + /** + * @return $this + */ + public function setHornType(GoatHornType $type) : self{ + $this->goatHornType = $type; + return $this; + } + + public function getMaxStackSize() : int{ + return 1; + } + + public function getCooldownTicks() : int{ + return 140; + } + + public function getCooldownTag() : ?string{ + return ItemCooldownTags::GOAT_HORN; + } + + public function canStartUsingItem(Player $player) : bool{ + return true; + } + + public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{ + $position = $player->getPosition(); + $position->getWorld()->addSound($position, new GoatHornSound($this->goatHornType)); + + return ItemUseResult::SUCCESS; + } +} diff --git a/src/item/GoatHornType.php b/src/item/GoatHornType.php new file mode 100644 index 00000000000..6c0c3b2f7fc --- /dev/null +++ b/src/item/GoatHornType.php @@ -0,0 +1,36 @@ +broadcastSound(new BottleEmptySound()); } public function getAdditionalEffects() : array{ diff --git a/src/item/RottenFlesh.php b/src/item/RottenFlesh.php index 4cecc67fcd3..2ea3ee9559c 100644 --- a/src/item/RottenFlesh.php +++ b/src/item/RottenFlesh.php @@ -25,7 +25,7 @@ use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\VanillaEffects; -use function lcg_value; +use pocketmine\utils\Utils; class RottenFlesh extends Food{ @@ -38,7 +38,7 @@ public function getSaturationRestore() : float{ } public function getAdditionalEffects() : array{ - if(lcg_value() <= 0.8){ + if(Utils::getRandomFloat() <= 0.8){ return [ new EffectInstance(VanillaEffects::HUNGER(), 600) ]; diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php index 51dcceebd2a..ab4f0e14983 100644 --- a/src/item/SpawnEgg.php +++ b/src/item/SpawnEgg.php @@ -27,15 +27,15 @@ use pocketmine\entity\Entity; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\utils\Utils; use pocketmine\world\World; -use function lcg_value; abstract class SpawnEgg extends Item{ abstract protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity; public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ - $entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), lcg_value() * 360, 0); + $entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), Utils::getRandomFloat() * 360, 0); if($this->hasCustomName()){ $entity->setNameTag($this->getCustomName()); diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index ee0f1f5c520..09c93d5d925 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1184,6 +1184,13 @@ private static function registerDynamicItems(self $result) : void{ $result->register($prefix("dye"), fn() => Items::DYE()->setColor($color)); } + + foreach(GoatHornType::cases() as $goatHornType){ + $prefix = fn(string $name) => strtolower($goatHornType->name) . "_" . $name; + + $result->register($prefix("goat_horn"), fn() => Items::GOAT_HORN()->setHornType($goatHornType)); + } + foreach(SuspiciousStewType::cases() as $suspiciousStewType){ $prefix = fn(string $name) => strtolower($suspiciousStewType->name) . "_" . $name; @@ -1323,6 +1330,7 @@ private static function registerItems(self $result) : void{ $result->register("enchanted_book", fn() => Items::ENCHANTED_BOOK()); $result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE()); + $result->register("end_crystal", fn() => Items::END_CRYSTAL()); $result->register("ender_pearl", fn() => Items::ENDER_PEARL()); $result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE()); $result->register("eye_armor_trim_smithing_template", fn() => Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); @@ -1341,6 +1349,7 @@ private static function registerItems(self $result) : void{ $result->register("glow_berries", fn() => Items::GLOW_BERRIES()); $result->register("glow_ink_sac", fn() => Items::GLOW_INK_SAC()); $result->register("glowstone_dust", fn() => Items::GLOWSTONE_DUST()); + $result->register("goat_horn", fn() => Items::GOAT_HORN()); $result->register("gold_axe", fn() => Items::GOLDEN_AXE()); $result->register("gold_boots", fn() => Items::GOLDEN_BOOTS()); $result->register("gold_chestplate", fn() => Items::GOLDEN_CHESTPLATE()); @@ -1369,6 +1378,7 @@ private static function registerItems(self $result) : void{ $result->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()); @@ -1471,6 +1481,7 @@ private static function registerItems(self $result) : void{ $result->register("record_strad", fn() => Items::RECORD_STRAD()); $result->register("record_wait", fn() => Items::RECORD_WAIT()); $result->register("record_ward", fn() => Items::RECORD_WARD()); + $result->register("recovery_compass", fn() => Items::RECOVERY_COMPASS()); $result->register("redstone", fn() => Items::REDSTONE_DUST()); $result->register("redstone_dust", fn() => Items::REDSTONE_DUST()); $result->register("rib_armor_trim_smithing_template", fn() => Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 5115ee48a8b..6768ed8f07e 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -33,11 +33,12 @@ use pocketmine\inventory\ArmorInventory; use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\ItemIdentifier as IID; -use pocketmine\item\ItemTypeIds as Ids; use pocketmine\item\VanillaArmorMaterials as ArmorMaterials; use pocketmine\math\Vector3; use pocketmine\utils\CloningRegistryTrait; use pocketmine\world\World; +use function is_int; +use function mb_strtoupper; use function strtolower; /** @@ -157,6 +158,7 @@ * @method static EnchantedBook ENCHANTED_BOOK() * @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE() * @method static EnderPearl ENDER_PEARL() + * @method static EndCrystal END_CRYSTAL() * @method static ExperienceBottle EXPERIENCE_BOTTLE() * @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static Item FEATHER() @@ -171,6 +173,7 @@ * @method static Item GLOWSTONE_DUST() * @method static GlowBerries GLOW_BERRIES() * @method static Item GLOW_INK_SAC() + * @method static GoatHorn GOAT_HORN() * @method static GoldenApple GOLDEN_APPLE() * @method static Axe GOLDEN_AXE() * @method static Armor GOLDEN_BOOTS() @@ -189,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() @@ -280,6 +284,7 @@ * @method static Record RECORD_STRAD() * @method static Record RECORD_WAIT() * @method static Record RECORD_WARD() + * @method static Item RECOVERY_COMPASS() * @method static Redstone REDSTONE_DUST() * @method static Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static RottenFlesh ROTTEN_FLESH() @@ -339,8 +344,29 @@ private function __construct(){ //NOOP } - protected static function register(string $name, Item $item) : void{ + /** + * @phpstan-template TItem of Item + * @phpstan-param \Closure(IID) : TItem $createItem + * @phpstan-return TItem + */ + protected static function register(string $name, \Closure $createItem) : Item{ + //this sketchy hack allows us to avoid manually writing the constants inline + //since type IDs are generated from this class anyway, I'm OK with this hack + //nonetheless, we should try to get rid of it in a future major version (e.g by using string type IDs) + $reflect = new \ReflectionClass(ItemTypeIds::class); + $typeId = $reflect->getConstant(mb_strtoupper($name)); + if(!is_int($typeId)){ + //this allows registering new stuff without adding new type ID constants + //this reduces the number of mandatory steps to test new features in local development + \GlobalLogger::get()->error(self::class . ": No constant type ID found for $name, generating a new one"); + $typeId = ItemTypeIds::newId(); + } + + $item = $createItem(new IID($typeId)); + self::_registryRegister($name, $item); + + return $item; } /** @@ -360,242 +386,240 @@ protected static function setup() : void{ self::registerTierToolItems(); self::registerSmithingTemplates(); - self::register("air", Blocks::AIR()->asItem()->setCount(0)); + //this doesn't use the regular register() because it doesn't have an item typeID + //in the future we'll probably want to dissociate this from the air block and make a proper null item + self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0)); - self::register("acacia_sign", new ItemBlockWallOrFloor(new IID(Ids::ACACIA_SIGN), Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); - self::register("amethyst_shard", new Item(new IID(Ids::AMETHYST_SHARD), "Amethyst Shard")); - self::register("apple", new Apple(new IID(Ids::APPLE), "Apple")); - self::register("arrow", new Arrow(new IID(Ids::ARROW), "Arrow")); - self::register("baked_potato", new BakedPotato(new IID(Ids::BAKED_POTATO), "Baked Potato")); - self::register("bamboo", new Bamboo(new IID(Ids::BAMBOO), "Bamboo")); - self::register("banner", new Banner(new IID(Ids::BANNER), Blocks::BANNER(), Blocks::WALL_BANNER())); - self::register("beetroot", new Beetroot(new IID(Ids::BEETROOT), "Beetroot")); - self::register("beetroot_seeds", new BeetrootSeeds(new IID(Ids::BEETROOT_SEEDS), "Beetroot Seeds")); - self::register("beetroot_soup", new BeetrootSoup(new IID(Ids::BEETROOT_SOUP), "Beetroot Soup")); - self::register("birch_sign", new ItemBlockWallOrFloor(new IID(Ids::BIRCH_SIGN), Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); - self::register("blaze_powder", new Item(new IID(Ids::BLAZE_POWDER), "Blaze Powder")); - self::register("blaze_rod", new BlazeRod(new IID(Ids::BLAZE_ROD), "Blaze Rod")); - self::register("bleach", new Item(new IID(Ids::BLEACH), "Bleach")); - self::register("bone", new Item(new IID(Ids::BONE), "Bone")); - self::register("bone_meal", new Fertilizer(new IID(Ids::BONE_MEAL), "Bone Meal")); - self::register("book", new Book(new IID(Ids::BOOK), "Book", [EnchantmentTags::ALL])); - self::register("bow", new Bow(new IID(Ids::BOW), "Bow", [EnchantmentTags::BOW])); - self::register("bowl", new Bowl(new IID(Ids::BOWL), "Bowl")); - self::register("bread", new Bread(new IID(Ids::BREAD), "Bread")); - self::register("brick", new Item(new IID(Ids::BRICK), "Brick")); - self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket")); - self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot")); - self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal")); - self::register("cherry_sign", new ItemBlockWallOrFloor(new IID(Ids::CHERRY_SIGN), Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); - self::register("chemical_aluminium_oxide", new Item(new IID(Ids::CHEMICAL_ALUMINIUM_OXIDE), "Aluminium Oxide")); - self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia")); - self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate")); - self::register("chemical_benzene", new Item(new IID(Ids::CHEMICAL_BENZENE), "Benzene")); - self::register("chemical_boron_trioxide", new Item(new IID(Ids::CHEMICAL_BORON_TRIOXIDE), "Boron Trioxide")); - self::register("chemical_calcium_bromide", new Item(new IID(Ids::CHEMICAL_CALCIUM_BROMIDE), "Calcium Bromide")); - self::register("chemical_calcium_chloride", new Item(new IID(Ids::CHEMICAL_CALCIUM_CHLORIDE), "Calcium Chloride")); - self::register("chemical_cerium_chloride", new Item(new IID(Ids::CHEMICAL_CERIUM_CHLORIDE), "Cerium Chloride")); - self::register("chemical_charcoal", new Item(new IID(Ids::CHEMICAL_CHARCOAL), "Charcoal")); - self::register("chemical_crude_oil", new Item(new IID(Ids::CHEMICAL_CRUDE_OIL), "Crude Oil")); - self::register("chemical_glue", new Item(new IID(Ids::CHEMICAL_GLUE), "Glue")); - self::register("chemical_hydrogen_peroxide", new Item(new IID(Ids::CHEMICAL_HYDROGEN_PEROXIDE), "Hydrogen Peroxide")); - self::register("chemical_hypochlorite", new Item(new IID(Ids::CHEMICAL_HYPOCHLORITE), "Hypochlorite")); - self::register("chemical_ink", new Item(new IID(Ids::CHEMICAL_INK), "Ink")); - self::register("chemical_iron_sulphide", new Item(new IID(Ids::CHEMICAL_IRON_SULPHIDE), "Iron Sulphide")); - self::register("chemical_latex", new Item(new IID(Ids::CHEMICAL_LATEX), "Latex")); - self::register("chemical_lithium_hydride", new Item(new IID(Ids::CHEMICAL_LITHIUM_HYDRIDE), "Lithium Hydride")); - self::register("chemical_luminol", new Item(new IID(Ids::CHEMICAL_LUMINOL), "Luminol")); - self::register("chemical_magnesium_nitrate", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_NITRATE), "Magnesium Nitrate")); - self::register("chemical_magnesium_oxide", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_OXIDE), "Magnesium Oxide")); - self::register("chemical_magnesium_salts", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_SALTS), "Magnesium Salts")); - self::register("chemical_mercuric_chloride", new Item(new IID(Ids::CHEMICAL_MERCURIC_CHLORIDE), "Mercuric Chloride")); - self::register("chemical_polyethylene", new Item(new IID(Ids::CHEMICAL_POLYETHYLENE), "Polyethylene")); - self::register("chemical_potassium_chloride", new Item(new IID(Ids::CHEMICAL_POTASSIUM_CHLORIDE), "Potassium Chloride")); - self::register("chemical_potassium_iodide", new Item(new IID(Ids::CHEMICAL_POTASSIUM_IODIDE), "Potassium Iodide")); - self::register("chemical_rubbish", new Item(new IID(Ids::CHEMICAL_RUBBISH), "Rubbish")); - self::register("chemical_salt", new Item(new IID(Ids::CHEMICAL_SALT), "Salt")); - self::register("chemical_soap", new Item(new IID(Ids::CHEMICAL_SOAP), "Soap")); - self::register("chemical_sodium_acetate", new Item(new IID(Ids::CHEMICAL_SODIUM_ACETATE), "Sodium Acetate")); - self::register("chemical_sodium_fluoride", new Item(new IID(Ids::CHEMICAL_SODIUM_FLUORIDE), "Sodium Fluoride")); - self::register("chemical_sodium_hydride", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDRIDE), "Sodium Hydride")); - self::register("chemical_sodium_hydroxide", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDROXIDE), "Sodium Hydroxide")); - self::register("chemical_sodium_hypochlorite", new Item(new IID(Ids::CHEMICAL_SODIUM_HYPOCHLORITE), "Sodium Hypochlorite")); - self::register("chemical_sodium_oxide", new Item(new IID(Ids::CHEMICAL_SODIUM_OXIDE), "Sodium Oxide")); - self::register("chemical_sugar", new Item(new IID(Ids::CHEMICAL_SUGAR), "Sugar")); - self::register("chemical_sulphate", new Item(new IID(Ids::CHEMICAL_SULPHATE), "Sulphate")); - self::register("chemical_tungsten_chloride", new Item(new IID(Ids::CHEMICAL_TUNGSTEN_CHLORIDE), "Tungsten Chloride")); - self::register("chemical_water", new Item(new IID(Ids::CHEMICAL_WATER), "Water")); - self::register("chorus_fruit", new ChorusFruit(new IID(Ids::CHORUS_FRUIT), "Chorus Fruit")); - self::register("clay", new Item(new IID(Ids::CLAY), "Clay")); - self::register("clock", new Clock(new IID(Ids::CLOCK), "Clock")); - self::register("clownfish", new Clownfish(new IID(Ids::CLOWNFISH), "Clownfish")); - self::register("coal", new Coal(new IID(Ids::COAL), "Coal")); - self::register("cocoa_beans", new CocoaBeans(new IID(Ids::COCOA_BEANS), "Cocoa Beans")); - self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass", [EnchantmentTags::COMPASS])); - self::register("cooked_chicken", new CookedChicken(new IID(Ids::COOKED_CHICKEN), "Cooked Chicken")); - self::register("cooked_fish", new CookedFish(new IID(Ids::COOKED_FISH), "Cooked Fish")); - self::register("cooked_mutton", new CookedMutton(new IID(Ids::COOKED_MUTTON), "Cooked Mutton")); - self::register("cooked_porkchop", new CookedPorkchop(new IID(Ids::COOKED_PORKCHOP), "Cooked Porkchop")); - self::register("cooked_rabbit", new CookedRabbit(new IID(Ids::COOKED_RABBIT), "Cooked Rabbit")); - self::register("cooked_salmon", new CookedSalmon(new IID(Ids::COOKED_SALMON), "Cooked Salmon")); - self::register("cookie", new Cookie(new IID(Ids::COOKIE), "Cookie")); - self::register("copper_ingot", new Item(new IID(Ids::COPPER_INGOT), "Copper Ingot")); - self::register("coral_fan", new CoralFan(new IID(Ids::CORAL_FAN))); - self::register("crimson_sign", new ItemBlockWallOrFloor(new IID(Ids::CRIMSON_SIGN), Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); - self::register("dark_oak_sign", new ItemBlockWallOrFloor(new IID(Ids::DARK_OAK_SIGN), Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN())); - self::register("diamond", new Item(new IID(Ids::DIAMOND), "Diamond")); - self::register("disc_fragment_5", new Item(new IID(Ids::DISC_FRAGMENT_5), "Disc Fragment (5)")); - self::register("dragon_breath", new Item(new IID(Ids::DRAGON_BREATH), "Dragon's Breath")); - self::register("dried_kelp", new DriedKelp(new IID(Ids::DRIED_KELP), "Dried Kelp")); + self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); + self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard")); + self::register("apple", fn(IID $id) => new Apple($id, "Apple")); + self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow")); + self::register("baked_potato", fn(IID $id) => new BakedPotato($id, "Baked Potato")); + self::register("bamboo", fn(IID $id) => new Bamboo($id, "Bamboo")); + self::register("banner", fn(IID $id) => new Banner($id, Blocks::BANNER(), Blocks::WALL_BANNER())); + self::register("beetroot", fn(IID $id) => new Beetroot($id, "Beetroot")); + self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds")); + self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup")); + self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); + self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder")); + self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod")); + self::register("bleach", fn(IID $id) => new Item($id, "Bleach")); + self::register("bone", fn(IID $id) => new Item($id, "Bone")); + self::register("bone_meal", fn(IID $id) => new Fertilizer($id, "Bone Meal")); + self::register("book", fn(IID $id) => new Book($id, "Book", [EnchantmentTags::ALL])); + self::register("bow", fn(IID $id) => new Bow($id, "Bow", [EnchantmentTags::BOW])); + self::register("bowl", fn(IID $id) => new Bowl($id, "Bowl")); + self::register("bread", fn(IID $id) => new Bread($id, "Bread")); + self::register("brick", fn(IID $id) => new Item($id, "Brick")); + self::register("bucket", fn(IID $id) => new Bucket($id, "Bucket")); + self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot")); + self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal")); + self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); + self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide")); + self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia")); + self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate")); + self::register("chemical_benzene", fn(IID $id) => new Item($id, "Benzene")); + self::register("chemical_boron_trioxide", fn(IID $id) => new Item($id, "Boron Trioxide")); + self::register("chemical_calcium_bromide", fn(IID $id) => new Item($id, "Calcium Bromide")); + self::register("chemical_calcium_chloride", fn(IID $id) => new Item($id, "Calcium Chloride")); + self::register("chemical_cerium_chloride", fn(IID $id) => new Item($id, "Cerium Chloride")); + self::register("chemical_charcoal", fn(IID $id) => new Item($id, "Charcoal")); + self::register("chemical_crude_oil", fn(IID $id) => new Item($id, "Crude Oil")); + self::register("chemical_glue", fn(IID $id) => new Item($id, "Glue")); + self::register("chemical_hydrogen_peroxide", fn(IID $id) => new Item($id, "Hydrogen Peroxide")); + self::register("chemical_hypochlorite", fn(IID $id) => new Item($id, "Hypochlorite")); + self::register("chemical_ink", fn(IID $id) => new Item($id, "Ink")); + self::register("chemical_iron_sulphide", fn(IID $id) => new Item($id, "Iron Sulphide")); + self::register("chemical_latex", fn(IID $id) => new Item($id, "Latex")); + self::register("chemical_lithium_hydride", fn(IID $id) => new Item($id, "Lithium Hydride")); + self::register("chemical_luminol", fn(IID $id) => new Item($id, "Luminol")); + self::register("chemical_magnesium_nitrate", fn(IID $id) => new Item($id, "Magnesium Nitrate")); + self::register("chemical_magnesium_oxide", fn(IID $id) => new Item($id, "Magnesium Oxide")); + self::register("chemical_magnesium_salts", fn(IID $id) => new Item($id, "Magnesium Salts")); + self::register("chemical_mercuric_chloride", fn(IID $id) => new Item($id, "Mercuric Chloride")); + self::register("chemical_polyethylene", fn(IID $id) => new Item($id, "Polyethylene")); + self::register("chemical_potassium_chloride", fn(IID $id) => new Item($id, "Potassium Chloride")); + self::register("chemical_potassium_iodide", fn(IID $id) => new Item($id, "Potassium Iodide")); + self::register("chemical_rubbish", fn(IID $id) => new Item($id, "Rubbish")); + self::register("chemical_salt", fn(IID $id) => new Item($id, "Salt")); + self::register("chemical_soap", fn(IID $id) => new Item($id, "Soap")); + self::register("chemical_sodium_acetate", fn(IID $id) => new Item($id, "Sodium Acetate")); + self::register("chemical_sodium_fluoride", fn(IID $id) => new Item($id, "Sodium Fluoride")); + self::register("chemical_sodium_hydride", fn(IID $id) => new Item($id, "Sodium Hydride")); + self::register("chemical_sodium_hydroxide", fn(IID $id) => new Item($id, "Sodium Hydroxide")); + self::register("chemical_sodium_hypochlorite", fn(IID $id) => new Item($id, "Sodium Hypochlorite")); + self::register("chemical_sodium_oxide", fn(IID $id) => new Item($id, "Sodium Oxide")); + self::register("chemical_sugar", fn(IID $id) => new Item($id, "Sugar")); + self::register("chemical_sulphate", fn(IID $id) => new Item($id, "Sulphate")); + self::register("chemical_tungsten_chloride", fn(IID $id) => new Item($id, "Tungsten Chloride")); + self::register("chemical_water", fn(IID $id) => new Item($id, "Water")); + self::register("chorus_fruit", fn(IID $id) => new ChorusFruit($id, "Chorus Fruit")); + self::register("clay", fn(IID $id) => new Item($id, "Clay")); + self::register("clock", fn(IID $id) => new Clock($id, "Clock")); + self::register("clownfish", fn(IID $id) => new Clownfish($id, "Clownfish")); + self::register("coal", fn(IID $id) => new Coal($id, "Coal")); + self::register("cocoa_beans", fn(IID $id) => new CocoaBeans($id, "Cocoa Beans")); + self::register("compass", fn(IID $id) => new Compass($id, "Compass", [EnchantmentTags::COMPASS])); + self::register("cooked_chicken", fn(IID $id) => new CookedChicken($id, "Cooked Chicken")); + self::register("cooked_fish", fn(IID $id) => new CookedFish($id, "Cooked Fish")); + self::register("cooked_mutton", fn(IID $id) => new CookedMutton($id, "Cooked Mutton")); + self::register("cooked_porkchop", fn(IID $id) => new CookedPorkchop($id, "Cooked Porkchop")); + self::register("cooked_rabbit", fn(IID $id) => new CookedRabbit($id, "Cooked Rabbit")); + self::register("cooked_salmon", fn(IID $id) => new CookedSalmon($id, "Cooked Salmon")); + self::register("cookie", fn(IID $id) => new Cookie($id, "Cookie")); + self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot")); + self::register("coral_fan", fn(IID $id) => new CoralFan($id)); + self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); + self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN())); + self::register("diamond", fn(IID $id) => new Item($id, "Diamond")); + self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)")); + self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath")); + self::register("dried_kelp", fn(IID $id) => new DriedKelp($id, "Dried Kelp")); //TODO: add interface to dye-colour objects - self::register("dye", new Dye(new IID(Ids::DYE), "Dye")); - self::register("echo_shard", new Item(new IID(Ids::ECHO_SHARD), "Echo Shard")); - self::register("egg", new Egg(new IID(Ids::EGG), "Egg")); - self::register("emerald", new Item(new IID(Ids::EMERALD), "Emerald")); - self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book", [EnchantmentTags::ALL])); - self::register("enchanted_golden_apple", new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE), "Enchanted Golden Apple")); - self::register("ender_pearl", new EnderPearl(new IID(Ids::ENDER_PEARL), "Ender Pearl")); - self::register("experience_bottle", new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE), "Bottle o' Enchanting")); - self::register("feather", new Item(new IID(Ids::FEATHER), "Feather")); - self::register("fermented_spider_eye", new Item(new IID(Ids::FERMENTED_SPIDER_EYE), "Fermented Spider Eye")); - self::register("fire_charge", new FireCharge(new IID(Ids::FIRE_CHARGE), "Fire Charge")); - self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod", [EnchantmentTags::FISHING_ROD])); - self::register("flint", new Item(new IID(Ids::FLINT), "Flint")); - self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL])); - self::register("ghast_tear", new Item(new IID(Ids::GHAST_TEAR), "Ghast Tear")); - self::register("glass_bottle", new GlassBottle(new IID(Ids::GLASS_BOTTLE), "Glass Bottle")); - self::register("glistering_melon", new Item(new IID(Ids::GLISTERING_MELON), "Glistering Melon")); - self::register("glow_berries", new GlowBerries(new IID(Ids::GLOW_BERRIES), "Glow Berries")); - self::register("glow_ink_sac", new Item(new IID(Ids::GLOW_INK_SAC), "Glow Ink Sac")); - self::register("glowstone_dust", new Item(new IID(Ids::GLOWSTONE_DUST), "Glowstone Dust")); - self::register("gold_ingot", new Item(new IID(Ids::GOLD_INGOT), "Gold Ingot")); - self::register("gold_nugget", new Item(new IID(Ids::GOLD_NUGGET), "Gold Nugget")); - self::register("golden_apple", new GoldenApple(new IID(Ids::GOLDEN_APPLE), "Golden Apple")); - self::register("golden_carrot", new GoldenCarrot(new IID(Ids::GOLDEN_CARROT), "Golden Carrot")); - self::register("gunpowder", new Item(new IID(Ids::GUNPOWDER), "Gunpowder")); - self::register("heart_of_the_sea", new Item(new IID(Ids::HEART_OF_THE_SEA), "Heart of the Sea")); - self::register("honey_bottle", new HoneyBottle(new IID(Ids::HONEY_BOTTLE), "Honey Bottle")); - self::register("honeycomb", new Item(new IID(Ids::HONEYCOMB), "Honeycomb")); - self::register("ink_sac", new Item(new IID(Ids::INK_SAC), "Ink Sac")); - self::register("iron_ingot", new Item(new IID(Ids::IRON_INGOT), "Iron Ingot")); - self::register("iron_nugget", new Item(new IID(Ids::IRON_NUGGET), "Iron Nugget")); - self::register("jungle_sign", new ItemBlockWallOrFloor(new IID(Ids::JUNGLE_SIGN), Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); - self::register("lapis_lazuli", new Item(new IID(Ids::LAPIS_LAZULI), "Lapis Lazuli")); - self::register("lava_bucket", new LiquidBucket(new IID(Ids::LAVA_BUCKET), "Lava Bucket", Blocks::LAVA())); - self::register("leather", new Item(new IID(Ids::LEATHER), "Leather")); - self::register("magma_cream", new Item(new IID(Ids::MAGMA_CREAM), "Magma Cream")); - self::register("mangrove_sign", new ItemBlockWallOrFloor(new IID(Ids::MANGROVE_SIGN), Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); - self::register("medicine", new Medicine(new IID(Ids::MEDICINE), "Medicine")); - self::register("melon", new Melon(new IID(Ids::MELON), "Melon")); - self::register("melon_seeds", new MelonSeeds(new IID(Ids::MELON_SEEDS), "Melon Seeds")); - self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket")); - self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart")); - self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew")); - self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag")); - self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell")); - self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick")); - self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz")); - self::register("nether_star", new Item(new IID(Ids::NETHER_STAR), "Nether Star")); - self::register("netherite_ingot", new class(new IID(Ids::NETHERITE_INGOT), "Netherite Ingot") extends Item{ + self::register("dye", fn(IID $id) => new Dye($id, "Dye")); + self::register("echo_shard", fn(IID $id) => new Item($id, "Echo Shard")); + self::register("egg", fn(IID $id) => new Egg($id, "Egg")); + self::register("emerald", fn(IID $id) => new Item($id, "Emerald")); + self::register("enchanted_book", fn(IID $id) => new EnchantedBook($id, "Enchanted Book", [EnchantmentTags::ALL])); + self::register("enchanted_golden_apple", fn(IID $id) => new GoldenAppleEnchanted($id, "Enchanted Golden Apple")); + self::register("end_crystal", fn(IID $id) => new EndCrystal($id, "End Crystal")); + self::register("ender_pearl", fn(IID $id) => new EnderPearl($id, "Ender Pearl")); + self::register("experience_bottle", fn(IID $id) => new ExperienceBottle($id, "Bottle o' Enchanting")); + self::register("feather", fn(IID $id) => new Item($id, "Feather")); + self::register("fermented_spider_eye", fn(IID $id) => new Item($id, "Fermented Spider Eye")); + self::register("fire_charge", fn(IID $id) => new FireCharge($id, "Fire Charge")); + self::register("fishing_rod", fn(IID $id) => new FishingRod($id, "Fishing Rod", [EnchantmentTags::FISHING_ROD])); + self::register("flint", fn(IID $id) => new Item($id, "Flint")); + self::register("flint_and_steel", fn(IID $id) => new FlintSteel($id, "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL])); + self::register("ghast_tear", fn(IID $id) => new Item($id, "Ghast Tear")); + self::register("glass_bottle", fn(IID $id) => new GlassBottle($id, "Glass Bottle")); + self::register("glistering_melon", fn(IID $id) => new Item($id, "Glistering Melon")); + self::register("glow_berries", fn(IID $id) => new GlowBerries($id, "Glow Berries")); + self::register("glow_ink_sac", fn(IID $id) => new Item($id, "Glow Ink Sac")); + self::register("glowstone_dust", fn(IID $id) => new Item($id, "Glowstone Dust")); + self::register("goat_horn", fn(IID $id) => new GoatHorn($id, "Goat Horn")); + self::register("gold_ingot", fn(IID $id) => new Item($id, "Gold Ingot")); + self::register("gold_nugget", fn(IID $id) => new Item($id, "Gold Nugget")); + self::register("golden_apple", fn(IID $id) => new GoldenApple($id, "Golden Apple")); + self::register("golden_carrot", fn(IID $id) => new GoldenCarrot($id, "Golden Carrot")); + self::register("gunpowder", fn(IID $id) => new Item($id, "Gunpowder")); + 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")); + self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); + self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli")); + self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA())); + self::register("leather", fn(IID $id) => new Item($id, "Leather")); + self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream")); + self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); + self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine")); + self::register("melon", fn(IID $id) => new Melon($id, "Melon")); + self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds")); + self::register("milk_bucket", fn(IID $id) => new MilkBucket($id, "Milk Bucket")); + self::register("minecart", fn(IID $id) => new Minecart($id, "Minecart")); + self::register("mushroom_stew", fn(IID $id) => new MushroomStew($id, "Mushroom Stew")); + self::register("name_tag", fn(IID $id) => new NameTag($id, "Name Tag")); + self::register("nautilus_shell", fn(IID $id) => new Item($id, "Nautilus Shell")); + self::register("nether_brick", fn(IID $id) => new Item($id, "Nether Brick")); + self::register("nether_quartz", fn(IID $id) => new Item($id, "Nether Quartz")); + self::register("nether_star", fn(IID $id) => new Item($id, "Nether Star")); + self::register("netherite_ingot", fn(IID $id) => new class($id, "Netherite Ingot") extends Item{ public function isFireProof() : bool{ return true; } }); - self::register("netherite_scrap", new class(new IID(Ids::NETHERITE_SCRAP), "Netherite Scrap") extends Item{ + self::register("netherite_scrap", fn(IID $id) => new class($id, "Netherite Scrap") extends Item{ public function isFireProof() : bool{ return true; } }); - self::register("oak_sign", new ItemBlockWallOrFloor(new IID(Ids::OAK_SIGN), Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); - self::register("painting", new PaintingItem(new IID(Ids::PAINTING), "Painting")); - self::register("paper", new Item(new IID(Ids::PAPER), "Paper")); - self::register("phantom_membrane", new Item(new IID(Ids::PHANTOM_MEMBRANE), "Phantom Membrane")); - self::register("pitcher_pod", new PitcherPod(new IID(Ids::PITCHER_POD), "Pitcher Pod")); - self::register("poisonous_potato", new PoisonousPotato(new IID(Ids::POISONOUS_POTATO), "Poisonous Potato")); - self::register("popped_chorus_fruit", new Item(new IID(Ids::POPPED_CHORUS_FRUIT), "Popped Chorus Fruit")); - self::register("potato", new Potato(new IID(Ids::POTATO), "Potato")); - self::register("potion", new Potion(new IID(Ids::POTION), "Potion")); - self::register("prismarine_crystals", new Item(new IID(Ids::PRISMARINE_CRYSTALS), "Prismarine Crystals")); - self::register("prismarine_shard", new Item(new IID(Ids::PRISMARINE_SHARD), "Prismarine Shard")); - self::register("pufferfish", new Pufferfish(new IID(Ids::PUFFERFISH), "Pufferfish")); - self::register("pumpkin_pie", new PumpkinPie(new IID(Ids::PUMPKIN_PIE), "Pumpkin Pie")); - self::register("pumpkin_seeds", new PumpkinSeeds(new IID(Ids::PUMPKIN_SEEDS), "Pumpkin Seeds")); - self::register("rabbit_foot", new Item(new IID(Ids::RABBIT_FOOT), "Rabbit's Foot")); - self::register("rabbit_hide", new Item(new IID(Ids::RABBIT_HIDE), "Rabbit Hide")); - self::register("rabbit_stew", new RabbitStew(new IID(Ids::RABBIT_STEW), "Rabbit Stew")); - self::register("raw_beef", new RawBeef(new IID(Ids::RAW_BEEF), "Raw Beef")); - self::register("raw_chicken", new RawChicken(new IID(Ids::RAW_CHICKEN), "Raw Chicken")); - self::register("raw_copper", new Item(new IID(Ids::RAW_COPPER), "Raw Copper")); - self::register("raw_fish", new RawFish(new IID(Ids::RAW_FISH), "Raw Fish")); - self::register("raw_gold", new Item(new IID(Ids::RAW_GOLD), "Raw Gold")); - self::register("raw_iron", new Item(new IID(Ids::RAW_IRON), "Raw Iron")); - self::register("raw_mutton", new RawMutton(new IID(Ids::RAW_MUTTON), "Raw Mutton")); - self::register("raw_porkchop", new RawPorkchop(new IID(Ids::RAW_PORKCHOP), "Raw Porkchop")); - self::register("raw_rabbit", new RawRabbit(new IID(Ids::RAW_RABBIT), "Raw Rabbit")); - self::register("raw_salmon", new RawSalmon(new IID(Ids::RAW_SALMON), "Raw Salmon")); - self::register("record_11", new Record(new IID(Ids::RECORD_11), RecordType::DISK_11, "Record 11")); - self::register("record_13", new Record(new IID(Ids::RECORD_13), RecordType::DISK_13, "Record 13")); - self::register("record_5", new Record(new IID(Ids::RECORD_5), RecordType::DISK_5, "Record 5")); - self::register("record_blocks", new Record(new IID(Ids::RECORD_BLOCKS), RecordType::DISK_BLOCKS, "Record Blocks")); - self::register("record_cat", new Record(new IID(Ids::RECORD_CAT), RecordType::DISK_CAT, "Record Cat")); - self::register("record_chirp", new Record(new IID(Ids::RECORD_CHIRP), RecordType::DISK_CHIRP, "Record Chirp")); - self::register("record_far", new Record(new IID(Ids::RECORD_FAR), RecordType::DISK_FAR, "Record Far")); - self::register("record_mall", new Record(new IID(Ids::RECORD_MALL), RecordType::DISK_MALL, "Record Mall")); - self::register("record_mellohi", new Record(new IID(Ids::RECORD_MELLOHI), RecordType::DISK_MELLOHI, "Record Mellohi")); - self::register("record_otherside", new Record(new IID(Ids::RECORD_OTHERSIDE), RecordType::DISK_OTHERSIDE, "Record Otherside")); - self::register("record_pigstep", new Record(new IID(Ids::RECORD_PIGSTEP), RecordType::DISK_PIGSTEP, "Record Pigstep")); - self::register("record_stal", new Record(new IID(Ids::RECORD_STAL), RecordType::DISK_STAL, "Record Stal")); - self::register("record_strad", new Record(new IID(Ids::RECORD_STRAD), RecordType::DISK_STRAD, "Record Strad")); - self::register("record_wait", new Record(new IID(Ids::RECORD_WAIT), RecordType::DISK_WAIT, "Record Wait")); - self::register("record_ward", new Record(new IID(Ids::RECORD_WARD), RecordType::DISK_WARD, "Record Ward")); - self::register("redstone_dust", new Redstone(new IID(Ids::REDSTONE_DUST), "Redstone")); - self::register("rotten_flesh", new RottenFlesh(new IID(Ids::ROTTEN_FLESH), "Rotten Flesh")); - self::register("scute", new Item(new IID(Ids::SCUTE), "Scute")); - self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears", [EnchantmentTags::SHEARS])); - self::register("shulker_shell", new Item(new IID(Ids::SHULKER_SHELL), "Shulker Shell")); - self::register("slimeball", new Item(new IID(Ids::SLIMEBALL), "Slimeball")); - self::register("snowball", new Snowball(new IID(Ids::SNOWBALL), "Snowball")); - self::register("spider_eye", new SpiderEye(new IID(Ids::SPIDER_EYE), "Spider Eye")); - self::register("splash_potion", new SplashPotion(new IID(Ids::SPLASH_POTION), "Splash Potion")); - self::register("spruce_sign", new ItemBlockWallOrFloor(new IID(Ids::SPRUCE_SIGN), Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); - self::register("spyglass", new Spyglass(new IID(Ids::SPYGLASS), "Spyglass")); - self::register("steak", new Steak(new IID(Ids::STEAK), "Steak")); - self::register("stick", new Stick(new IID(Ids::STICK), "Stick")); - self::register("string", new StringItem(new IID(Ids::STRING), "String")); - self::register("sugar", new Item(new IID(Ids::SUGAR), "Sugar")); - self::register("suspicious_stew", new SuspiciousStew(new IID(Ids::SUSPICIOUS_STEW), "Suspicious Stew")); - self::register("sweet_berries", new SweetBerries(new IID(Ids::SWEET_BERRIES), "Sweet Berries")); - self::register("torchflower_seeds", new TorchflowerSeeds(new IID(Ids::TORCHFLOWER_SEEDS), "Torchflower Seeds")); - self::register("totem", new Totem(new IID(Ids::TOTEM), "Totem of Undying")); - self::register("warped_sign", new ItemBlockWallOrFloor(new IID(Ids::WARPED_SIGN), Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); - self::register("water_bucket", new LiquidBucket(new IID(Ids::WATER_BUCKET), "Water Bucket", Blocks::WATER())); - self::register("wheat", new Item(new IID(Ids::WHEAT), "Wheat")); - self::register("wheat_seeds", new WheatSeeds(new IID(Ids::WHEAT_SEEDS), "Wheat Seeds")); - self::register("writable_book", new WritableBook(new IID(Ids::WRITABLE_BOOK), "Book & Quill")); - self::register("written_book", new WrittenBook(new IID(Ids::WRITTEN_BOOK), "Written Book")); + self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); + self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); + self::register("paper", fn(IID $id) => new Item($id, "Paper")); + self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane")); + self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod")); + self::register("poisonous_potato", fn(IID $id) => new PoisonousPotato($id, "Poisonous Potato")); + self::register("popped_chorus_fruit", fn(IID $id) => new Item($id, "Popped Chorus Fruit")); + self::register("potato", fn(IID $id) => new Potato($id, "Potato")); + self::register("potion", fn(IID $id) => new Potion($id, "Potion")); + self::register("prismarine_crystals", fn(IID $id) => new Item($id, "Prismarine Crystals")); + self::register("prismarine_shard", fn(IID $id) => new Item($id, "Prismarine Shard")); + self::register("pufferfish", fn(IID $id) => new Pufferfish($id, "Pufferfish")); + self::register("pumpkin_pie", fn(IID $id) => new PumpkinPie($id, "Pumpkin Pie")); + self::register("pumpkin_seeds", fn(IID $id) => new PumpkinSeeds($id, "Pumpkin Seeds")); + self::register("rabbit_foot", fn(IID $id) => new Item($id, "Rabbit's Foot")); + self::register("rabbit_hide", fn(IID $id) => new Item($id, "Rabbit Hide")); + self::register("rabbit_stew", fn(IID $id) => new RabbitStew($id, "Rabbit Stew")); + self::register("raw_beef", fn(IID $id) => new RawBeef($id, "Raw Beef")); + self::register("raw_chicken", fn(IID $id) => new RawChicken($id, "Raw Chicken")); + self::register("raw_copper", fn(IID $id) => new Item($id, "Raw Copper")); + self::register("raw_fish", fn(IID $id) => new RawFish($id, "Raw Fish")); + self::register("raw_gold", fn(IID $id) => new Item($id, "Raw Gold")); + self::register("raw_iron", fn(IID $id) => new Item($id, "Raw Iron")); + self::register("raw_mutton", fn(IID $id) => new RawMutton($id, "Raw Mutton")); + self::register("raw_porkchop", fn(IID $id) => new RawPorkchop($id, "Raw Porkchop")); + self::register("raw_rabbit", fn(IID $id) => new RawRabbit($id, "Raw Rabbit")); + self::register("raw_salmon", fn(IID $id) => new RawSalmon($id, "Raw Salmon")); + self::register("record_11", fn(IID $id) => new Record($id, RecordType::DISK_11, "Record 11")); + self::register("record_13", fn(IID $id) => new Record($id, RecordType::DISK_13, "Record 13")); + self::register("record_5", fn(IID $id) => new Record($id, RecordType::DISK_5, "Record 5")); + self::register("record_blocks", fn(IID $id) => new Record($id, RecordType::DISK_BLOCKS, "Record Blocks")); + self::register("record_cat", fn(IID $id) => new Record($id, RecordType::DISK_CAT, "Record Cat")); + self::register("record_chirp", fn(IID $id) => new Record($id, RecordType::DISK_CHIRP, "Record Chirp")); + self::register("record_far", fn(IID $id) => new Record($id, RecordType::DISK_FAR, "Record Far")); + self::register("record_mall", fn(IID $id) => new Record($id, RecordType::DISK_MALL, "Record Mall")); + self::register("record_mellohi", fn(IID $id) => new Record($id, RecordType::DISK_MELLOHI, "Record Mellohi")); + self::register("record_otherside", fn(IID $id) => new Record($id, RecordType::DISK_OTHERSIDE, "Record Otherside")); + self::register("record_pigstep", fn(IID $id) => new Record($id, RecordType::DISK_PIGSTEP, "Record Pigstep")); + self::register("record_stal", fn(IID $id) => new Record($id, RecordType::DISK_STAL, "Record Stal")); + self::register("record_strad", fn(IID $id) => new Record($id, RecordType::DISK_STRAD, "Record Strad")); + self::register("record_wait", fn(IID $id) => new Record($id, RecordType::DISK_WAIT, "Record Wait")); + self::register("record_ward", fn(IID $id) => new Record($id, RecordType::DISK_WARD, "Record Ward")); + self::register("recovery_compass", fn(IID $id) => new Item($id, "Recovery Compass")); + self::register("redstone_dust", fn(IID $id) => new Redstone($id, "Redstone")); + self::register("rotten_flesh", fn(IID $id) => new RottenFlesh($id, "Rotten Flesh")); + self::register("scute", fn(IID $id) => new Item($id, "Scute")); + self::register("shears", fn(IID $id) => new Shears($id, "Shears", [EnchantmentTags::SHEARS])); + self::register("shulker_shell", fn(IID $id) => new Item($id, "Shulker Shell")); + self::register("slimeball", fn(IID $id) => new Item($id, "Slimeball")); + self::register("snowball", fn(IID $id) => new Snowball($id, "Snowball")); + self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye")); + self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion")); + self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); + self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass")); + self::register("steak", fn(IID $id) => new Steak($id, "Steak")); + self::register("stick", fn(IID $id) => new Stick($id, "Stick")); + self::register("string", fn(IID $id) => new StringItem($id, "String")); + self::register("sugar", fn(IID $id) => new Item($id, "Sugar")); + self::register("suspicious_stew", fn(IID $id) => new SuspiciousStew($id, "Suspicious Stew")); + self::register("sweet_berries", fn(IID $id) => new SweetBerries($id, "Sweet Berries")); + self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds")); + self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying")); + self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); + self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER())); + self::register("wheat", fn(IID $id) => new Item($id, "Wheat")); + self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds")); + self::register("writable_book", fn(IID $id) => new WritableBook($id, "Book & Quill")); + self::register("written_book", fn(IID $id) => new WrittenBook($id, "Written Book")); foreach(BoatType::cases() as $type){ //boat type is static, because different types of wood may have different properties - self::register(strtolower($type->name) . "_boat", new Boat(new IID(match($type){ - BoatType::OAK => Ids::OAK_BOAT, - BoatType::SPRUCE => Ids::SPRUCE_BOAT, - BoatType::BIRCH => Ids::BIRCH_BOAT, - BoatType::JUNGLE => Ids::JUNGLE_BOAT, - BoatType::ACACIA => Ids::ACACIA_BOAT, - BoatType::DARK_OAK => Ids::DARK_OAK_BOAT, - BoatType::MANGROVE => Ids::MANGROVE_BOAT, - }), $type->getDisplayName() . " Boat", $type)); + self::register(strtolower($type->name) . "_boat", fn(IID $id) => new Boat($id, $type->getDisplayName() . " Boat", $type)); } } private static function registerSpawnEggs() : void{ - self::register("zombie_spawn_egg", new class(new IID(Ids::ZOMBIE_SPAWN_EGG), "Zombie Spawn Egg") extends SpawnEgg{ + self::register("zombie_spawn_egg", fn(IID $id) => new class($id, "Zombie Spawn Egg") extends SpawnEgg{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{ return new Zombie(Location::fromObject($pos, $world, $yaw, $pitch)); } }); - self::register("squid_spawn_egg", new class(new IID(Ids::SQUID_SPAWN_EGG), "Squid Spawn Egg") extends SpawnEgg{ + self::register("squid_spawn_egg", fn(IID $id) => new class($id, "Squid Spawn Egg") extends SpawnEgg{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{ return new Squid(Location::fromObject($pos, $world, $yaw, $pitch)); } }); - self::register("villager_spawn_egg", new class(new IID(Ids::VILLAGER_SPAWN_EGG), "Villager Spawn Egg") extends SpawnEgg{ + self::register("villager_spawn_egg", fn(IID $id) => new class($id, "Villager Spawn Egg") extends SpawnEgg{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{ return new Villager(Location::fromObject($pos, $world, $yaw, $pitch)); } @@ -603,87 +627,87 @@ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $p } private static function registerTierToolItems() : void{ - self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE])); - self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD, [EnchantmentTags::AXE])); - self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON, [EnchantmentTags::AXE])); - self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE, [EnchantmentTags::AXE])); - self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE, [EnchantmentTags::AXE])); - self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD, [EnchantmentTags::AXE])); - self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND, [EnchantmentTags::HOE])); - self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD, [EnchantmentTags::HOE])); - self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON, [EnchantmentTags::HOE])); - self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE, [EnchantmentTags::HOE])); - self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE, [EnchantmentTags::HOE])); - self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD, [EnchantmentTags::HOE])); - self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND, [EnchantmentTags::PICKAXE])); - self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD, [EnchantmentTags::PICKAXE])); - self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON, [EnchantmentTags::PICKAXE])); - self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE, [EnchantmentTags::PICKAXE])); - self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE, [EnchantmentTags::PICKAXE])); - self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD, [EnchantmentTags::PICKAXE])); - self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND, [EnchantmentTags::SHOVEL])); - self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD, [EnchantmentTags::SHOVEL])); - self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON, [EnchantmentTags::SHOVEL])); - self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE, [EnchantmentTags::SHOVEL])); - self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE, [EnchantmentTags::SHOVEL])); - self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD, [EnchantmentTags::SHOVEL])); - self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND, [EnchantmentTags::SWORD])); - self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD, [EnchantmentTags::SWORD])); - self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON, [EnchantmentTags::SWORD])); - self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE, [EnchantmentTags::SWORD])); - self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE, [EnchantmentTags::SWORD])); - self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD])); + self::register("diamond_axe", fn(IID $id) => new Axe($id, "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE])); + self::register("golden_axe", fn(IID $id) => new Axe($id, "Golden Axe", ToolTier::GOLD, [EnchantmentTags::AXE])); + self::register("iron_axe", fn(IID $id) => new Axe($id, "Iron Axe", ToolTier::IRON, [EnchantmentTags::AXE])); + self::register("netherite_axe", fn(IID $id) => new Axe($id, "Netherite Axe", ToolTier::NETHERITE, [EnchantmentTags::AXE])); + self::register("stone_axe", fn(IID $id) => new Axe($id, "Stone Axe", ToolTier::STONE, [EnchantmentTags::AXE])); + self::register("wooden_axe", fn(IID $id) => new Axe($id, "Wooden Axe", ToolTier::WOOD, [EnchantmentTags::AXE])); + self::register("diamond_hoe", fn(IID $id) => new Hoe($id, "Diamond Hoe", ToolTier::DIAMOND, [EnchantmentTags::HOE])); + self::register("golden_hoe", fn(IID $id) => new Hoe($id, "Golden Hoe", ToolTier::GOLD, [EnchantmentTags::HOE])); + self::register("iron_hoe", fn(IID $id) => new Hoe($id, "Iron Hoe", ToolTier::IRON, [EnchantmentTags::HOE])); + self::register("netherite_hoe", fn(IID $id) => new Hoe($id, "Netherite Hoe", ToolTier::NETHERITE, [EnchantmentTags::HOE])); + self::register("stone_hoe", fn(IID $id) => new Hoe($id, "Stone Hoe", ToolTier::STONE, [EnchantmentTags::HOE])); + self::register("wooden_hoe", fn(IID $id) => new Hoe($id, "Wooden Hoe", ToolTier::WOOD, [EnchantmentTags::HOE])); + self::register("diamond_pickaxe", fn(IID $id) => new Pickaxe($id, "Diamond Pickaxe", ToolTier::DIAMOND, [EnchantmentTags::PICKAXE])); + self::register("golden_pickaxe", fn(IID $id) => new Pickaxe($id, "Golden Pickaxe", ToolTier::GOLD, [EnchantmentTags::PICKAXE])); + self::register("iron_pickaxe", fn(IID $id) => new Pickaxe($id, "Iron Pickaxe", ToolTier::IRON, [EnchantmentTags::PICKAXE])); + self::register("netherite_pickaxe", fn(IID $id) => new Pickaxe($id, "Netherite Pickaxe", ToolTier::NETHERITE, [EnchantmentTags::PICKAXE])); + self::register("stone_pickaxe", fn(IID $id) => new Pickaxe($id, "Stone Pickaxe", ToolTier::STONE, [EnchantmentTags::PICKAXE])); + self::register("wooden_pickaxe", fn(IID $id) => new Pickaxe($id, "Wooden Pickaxe", ToolTier::WOOD, [EnchantmentTags::PICKAXE])); + self::register("diamond_shovel", fn(IID $id) => new Shovel($id, "Diamond Shovel", ToolTier::DIAMOND, [EnchantmentTags::SHOVEL])); + self::register("golden_shovel", fn(IID $id) => new Shovel($id, "Golden Shovel", ToolTier::GOLD, [EnchantmentTags::SHOVEL])); + self::register("iron_shovel", fn(IID $id) => new Shovel($id, "Iron Shovel", ToolTier::IRON, [EnchantmentTags::SHOVEL])); + self::register("netherite_shovel", fn(IID $id) => new Shovel($id, "Netherite Shovel", ToolTier::NETHERITE, [EnchantmentTags::SHOVEL])); + self::register("stone_shovel", fn(IID $id) => new Shovel($id, "Stone Shovel", ToolTier::STONE, [EnchantmentTags::SHOVEL])); + self::register("wooden_shovel", fn(IID $id) => new Shovel($id, "Wooden Shovel", ToolTier::WOOD, [EnchantmentTags::SHOVEL])); + self::register("diamond_sword", fn(IID $id) => new Sword($id, "Diamond Sword", ToolTier::DIAMOND, [EnchantmentTags::SWORD])); + self::register("golden_sword", fn(IID $id) => new Sword($id, "Golden Sword", ToolTier::GOLD, [EnchantmentTags::SWORD])); + self::register("iron_sword", fn(IID $id) => new Sword($id, "Iron Sword", ToolTier::IRON, [EnchantmentTags::SWORD])); + self::register("netherite_sword", fn(IID $id) => new Sword($id, "Netherite Sword", ToolTier::NETHERITE, [EnchantmentTags::SWORD])); + self::register("stone_sword", fn(IID $id) => new Sword($id, "Stone Sword", ToolTier::STONE, [EnchantmentTags::SWORD])); + self::register("wooden_sword", fn(IID $id) => new Sword($id, "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD])); } private static function registerArmorItems() : void{ - self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS])); - self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS])); - self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS])); - self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS])); - self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS])); - self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS])); + self::register("chainmail_boots", fn(IID $id) => new Armor($id, "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS])); + self::register("diamond_boots", fn(IID $id) => new Armor($id, "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS])); + self::register("golden_boots", fn(IID $id) => new Armor($id, "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS])); + self::register("iron_boots", fn(IID $id) => new Armor($id, "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS])); + self::register("leather_boots", fn(IID $id) => new Armor($id, "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS])); + self::register("netherite_boots", fn(IID $id) => new Armor($id, "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS])); - self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE])); - self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE])); - self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE])); - self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE])); - self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE])); - self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE])); + self::register("chainmail_chestplate", fn(IID $id) => new Armor($id, "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE])); + self::register("diamond_chestplate", fn(IID $id) => new Armor($id, "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE])); + self::register("golden_chestplate", fn(IID $id) => new Armor($id, "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE])); + self::register("iron_chestplate", fn(IID $id) => new Armor($id, "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE])); + self::register("leather_tunic", fn(IID $id) => new Armor($id, "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE])); + self::register("netherite_chestplate", fn(IID $id) => new Armor($id, "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE])); - self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET])); - self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET])); - self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET])); - self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET])); - self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET])); - self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET])); - self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET])); + self::register("chainmail_helmet", fn(IID $id) => new Armor($id, "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET])); + self::register("diamond_helmet", fn(IID $id) => new Armor($id, "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET])); + self::register("golden_helmet", fn(IID $id) => new Armor($id, "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET])); + self::register("iron_helmet", fn(IID $id) => new Armor($id, "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET])); + self::register("leather_cap", fn(IID $id) => new Armor($id, "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET])); + self::register("netherite_helmet", fn(IID $id) => new Armor($id, "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET])); + self::register("turtle_helmet", fn(IID $id) => new TurtleHelmet($id, "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET])); - self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS])); - self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS])); - self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS])); - self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS])); - self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS])); - self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS])); + self::register("chainmail_leggings", fn(IID $id) => new Armor($id, "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS])); + self::register("diamond_leggings", fn(IID $id) => new Armor($id, "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS])); + self::register("golden_leggings", fn(IID $id) => new Armor($id, "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS])); + self::register("iron_leggings", fn(IID $id) => new Armor($id, "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS])); + self::register("leather_pants", fn(IID $id) => new Armor($id, "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS])); + self::register("netherite_leggings", fn(IID $id) => new Armor($id, "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS])); } private static function registerSmithingTemplates() : void{ - self::register("netherite_upgrade_smithing_template", new Item(new IID(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE), "Netherite Upgrade Smithing Template")); - self::register("coast_armor_trim_smithing_template", new Item(new IID(Ids::COAST_ARMOR_TRIM_SMITHING_TEMPLATE), "Coast Armor Trim Smithing Template")); - self::register("dune_armor_trim_smithing_template", new Item(new IID(Ids::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE), "Dune Armor Trim Smithing Template")); - self::register("eye_armor_trim_smithing_template", new Item(new IID(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE), "Eye Armor Trim Smithing Template")); - self::register("host_armor_trim_smithing_template", new Item(new IID(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE), "Host Armor Trim Smithing Template")); - self::register("raiser_armor_trim_smithing_template", new Item(new IID(Ids::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE), "Raiser Armor Trim Smithing Template")); - self::register("rib_armor_trim_smithing_template", new Item(new IID(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE), "Rib Armor Trim Smithing Template")); - self::register("sentry_armor_trim_smithing_template", new Item(new IID(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE), "Sentry Armor Trim Smithing Template")); - self::register("shaper_armor_trim_smithing_template", new Item(new IID(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE), "Shaper Armor Trim Smithing Template")); - self::register("silence_armor_trim_smithing_template", new Item(new IID(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE), "Silence Armor Trim Smithing Template")); - self::register("snout_armor_trim_smithing_template", new Item(new IID(Ids::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE), "Snout Armor Trim Smithing Template")); - self::register("spire_armor_trim_smithing_template", new Item(new IID(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE), "Spire Armor Trim Smithing Template")); - self::register("tide_armor_trim_smithing_template", new Item(new IID(Ids::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE), "Tide Armor Trim Smithing Template")); - self::register("vex_armor_trim_smithing_template", new Item(new IID(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE), "Vex Armor Trim Smithing Template")); - self::register("ward_armor_trim_smithing_template", new Item(new IID(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE), "Ward Armor Trim Smithing Template")); - self::register("wayfinder_armor_trim_smithing_template", new Item(new IID(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE), "Wayfinder Armor Trim Smithing Template")); - self::register("wild_armor_trim_smithing_template", new Item(new IID(Ids::WILD_ARMOR_TRIM_SMITHING_TEMPLATE), "Wild Armor Trim Smithing Template")); + self::register("netherite_upgrade_smithing_template", fn(IID $id) => new Item($id, "Netherite Upgrade Smithing Template")); + self::register("coast_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Coast Armor Trim Smithing Template")); + self::register("dune_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Dune Armor Trim Smithing Template")); + self::register("eye_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Eye Armor Trim Smithing Template")); + self::register("host_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Host Armor Trim Smithing Template")); + self::register("raiser_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Raiser Armor Trim Smithing Template")); + self::register("rib_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Rib Armor Trim Smithing Template")); + self::register("sentry_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Sentry Armor Trim Smithing Template")); + self::register("shaper_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Shaper Armor Trim Smithing Template")); + self::register("silence_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Silence Armor Trim Smithing Template")); + self::register("snout_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Snout Armor Trim Smithing Template")); + self::register("spire_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Spire Armor Trim Smithing Template")); + self::register("tide_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Tide Armor Trim Smithing Template")); + self::register("vex_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Vex Armor Trim Smithing Template")); + self::register("ward_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Ward Armor Trim Smithing Template")); + self::register("wayfinder_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Wayfinder Armor Trim Smithing Template")); + self::register("wild_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Wild Armor Trim Smithing Template")); } } diff --git a/src/item/enchantment/AvailableEnchantmentRegistry.php b/src/item/enchantment/AvailableEnchantmentRegistry.php index cae94c666f5..eed7bff5273 100644 --- a/src/item/enchantment/AvailableEnchantmentRegistry.php +++ b/src/item/enchantment/AvailableEnchantmentRegistry.php @@ -57,6 +57,7 @@ private function __construct(){ $this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]); $this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []); $this->register(Enchantments::AQUA_AFFINITY(), [Tags::HELMET], []); + $this->register(Enchantments::FROST_WALKER(), [/* no primary items */], [Tags::BOOTS]); $this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []); $this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []); $this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []); diff --git a/src/item/enchantment/StringToEnchantmentParser.php b/src/item/enchantment/StringToEnchantmentParser.php index 47a750ff27d..b6763e49125 100644 --- a/src/item/enchantment/StringToEnchantmentParser.php +++ b/src/item/enchantment/StringToEnchantmentParser.php @@ -44,6 +44,7 @@ private static function make() : self{ $result->register("fire_protection", fn() => VanillaEnchantments::FIRE_PROTECTION()); $result->register("flame", fn() => VanillaEnchantments::FLAME()); $result->register("fortune", fn() => VanillaEnchantments::FORTUNE()); + $result->register("frost_walker", fn() => VanillaEnchantments::FROST_WALKER()); $result->register("infinity", fn() => VanillaEnchantments::INFINITY()); $result->register("knockback", fn() => VanillaEnchantments::KNOCKBACK()); $result->register("mending", fn() => VanillaEnchantments::MENDING()); diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index 19ce3971633..1132dc9c662 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -41,6 +41,7 @@ * @method static ProtectionEnchantment FIRE_PROTECTION() * @method static Enchantment FLAME() * @method static Enchantment FORTUNE() + * @method static Enchantment FROST_WALKER() * @method static Enchantment INFINITY() * @method static KnockbackEnchantment KNOCKBACK() * @method static Enchantment MENDING() @@ -145,6 +146,16 @@ protected static function setup() : void{ fn(int $level) : int => 10 * $level, 30 )); + + self::register("FROST_WALKER", new Enchantment( + KnownTranslationFactory::enchantment_frostwalker(), + Rarity::RARE, + 0, + 0, + 2, + fn(int $level) : int => 10 * $level, + 15 + )); self::register("AQUA_AFFINITY", new Enchantment( KnownTranslationFactory::enchantment_waterWorker(), Rarity::RARE, diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index ea8c2952e5e..f0b70ffd1a0 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -603,6 +603,31 @@ public static function commands_whitelist_usage() : Translatable{ return new Translatable(KnownTranslationKeys::COMMANDS_WHITELIST_USAGE, []); } + public static function commands_xp_failure_widthdrawXp() : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_FAILURE_WIDTHDRAWXP, []); + } + + public static function commands_xp_success(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS, [ + 0 => $param0, + 1 => $param1, + ]); + } + + public static function commands_xp_success_levels(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS_LEVELS, [ + 0 => $param0, + 1 => $param1, + ]); + } + + public static function commands_xp_success_negative_levels(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS_NEGATIVE_LEVELS, [ + 0 => $param0, + 1 => $param1, + ]); + } + public static function death_attack_anvil(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ANVIL, [ 0 => $param0, @@ -667,6 +692,12 @@ public static function death_attack_fireworks(Translatable|string $param0) : Tra ]); } + public static function death_attack_flyIntoWall(Translatable|string $param0) : Translatable{ + return new Translatable(KnownTranslationKeys::DEATH_ATTACK_FLYINTOWALL, [ + 0 => $param0, + ]); + } + public static function death_attack_generic(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [ 0 => $param0, @@ -1025,6 +1056,14 @@ public static function item_record_chirp_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_CHIRP_DESC, []); } + public static function item_record_creator_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_CREATOR_DESC, []); + } + + public static function item_record_creator_music_box_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_CREATOR_MUSIC_BOX_DESC, []); + } + public static function item_record_far_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_FAR_DESC, []); } @@ -1045,6 +1084,14 @@ public static function item_record_pigstep_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_PIGSTEP_DESC, []); } + public static function item_record_precipice_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_PRECIPICE_DESC, []); + } + + public static function item_record_relic_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_RELIC_DESC, []); + } + public static function item_record_stal_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_STAL_DESC, []); } @@ -1394,6 +1441,10 @@ public static function pocketmine_command_timings_alreadyEnabled() : Translatabl return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED, []); } + public static function pocketmine_command_timings_collect() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_COLLECT, []); + } + public static function pocketmine_command_timings_description() : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_DESCRIPTION, []); } @@ -1536,6 +1587,14 @@ public static function pocketmine_command_whitelist_description() : Translatable return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_WHITELIST_DESCRIPTION, []); } + public static function pocketmine_command_xp_description() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_XP_DESCRIPTION, []); + } + + public static function pocketmine_command_xp_usage() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_XP_USAGE, []); + } + public static function pocketmine_crash_archive(Translatable|string $param0, Translatable|string $param1) : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_CRASH_ARCHIVE, [ 0 => $param0, @@ -2056,6 +2115,14 @@ public static function pocketmine_permission_command_whitelist_remove() : Transl return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE, []); } + public static function pocketmine_permission_command_xp_other() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_XP_OTHER, []); + } + + public static function pocketmine_permission_command_xp_self() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_XP_SELF, []); + } + public static function pocketmine_permission_group_console() : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_CONSOLE, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index c8345273060..1c1fef20f5b 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -137,6 +137,10 @@ final class KnownTranslationKeys{ public const COMMANDS_WHITELIST_REMOVE_SUCCESS = "commands.whitelist.remove.success"; public const COMMANDS_WHITELIST_REMOVE_USAGE = "commands.whitelist.remove.usage"; public const COMMANDS_WHITELIST_USAGE = "commands.whitelist.usage"; + public const COMMANDS_XP_FAILURE_WIDTHDRAWXP = "commands.xp.failure.widthdrawXp"; + public const COMMANDS_XP_SUCCESS = "commands.xp.success"; + public const COMMANDS_XP_SUCCESS_LEVELS = "commands.xp.success.levels"; + public const COMMANDS_XP_SUCCESS_NEGATIVE_LEVELS = "commands.xp.success.negative.levels"; public const DEATH_ATTACK_ANVIL = "death.attack.anvil"; public const DEATH_ATTACK_ARROW = "death.attack.arrow"; public const DEATH_ATTACK_ARROW_ITEM = "death.attack.arrow.item"; @@ -147,6 +151,7 @@ final class KnownTranslationKeys{ public const DEATH_ATTACK_FALL = "death.attack.fall"; public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock"; public const DEATH_ATTACK_FIREWORKS = "death.attack.fireworks"; + public const DEATH_ATTACK_FLYINTOWALL = "death.attack.flyIntoWall"; public const DEATH_ATTACK_GENERIC = "death.attack.generic"; public const DEATH_ATTACK_INFIRE = "death.attack.inFire"; public const DEATH_ATTACK_INWALL = "death.attack.inWall"; @@ -227,11 +232,15 @@ final class KnownTranslationKeys{ public const ITEM_RECORD_BLOCKS_DESC = "item.record_blocks.desc"; public const ITEM_RECORD_CAT_DESC = "item.record_cat.desc"; public const ITEM_RECORD_CHIRP_DESC = "item.record_chirp.desc"; + public const ITEM_RECORD_CREATOR_DESC = "item.record_creator.desc"; + public const ITEM_RECORD_CREATOR_MUSIC_BOX_DESC = "item.record_creator_music_box.desc"; public const ITEM_RECORD_FAR_DESC = "item.record_far.desc"; public const ITEM_RECORD_MALL_DESC = "item.record_mall.desc"; public const ITEM_RECORD_MELLOHI_DESC = "item.record_mellohi.desc"; public const ITEM_RECORD_OTHERSIDE_DESC = "item.record_otherside.desc"; public const ITEM_RECORD_PIGSTEP_DESC = "item.record_pigstep.desc"; + public const ITEM_RECORD_PRECIPICE_DESC = "item.record_precipice.desc"; + public const ITEM_RECORD_RELIC_DESC = "item.record_relic.desc"; public const ITEM_RECORD_STAL_DESC = "item.record_stal.desc"; public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc"; public const ITEM_RECORD_WAIT_DESC = "item.record_wait.desc"; @@ -306,6 +315,7 @@ final class KnownTranslationKeys{ public const POCKETMINE_COMMAND_TIME_DESCRIPTION = "pocketmine.command.time.description"; public const POCKETMINE_COMMAND_TIME_USAGE = "pocketmine.command.time.usage"; public const POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED = "pocketmine.command.timings.alreadyEnabled"; + public const POCKETMINE_COMMAND_TIMINGS_COLLECT = "pocketmine.command.timings.collect"; public const POCKETMINE_COMMAND_TIMINGS_DESCRIPTION = "pocketmine.command.timings.description"; public const POCKETMINE_COMMAND_TIMINGS_DISABLE = "pocketmine.command.timings.disable"; public const POCKETMINE_COMMAND_TIMINGS_ENABLE = "pocketmine.command.timings.enable"; @@ -336,6 +346,8 @@ final class KnownTranslationKeys{ public const POCKETMINE_COMMAND_VERSION_SERVERSOFTWAREVERSION = "pocketmine.command.version.serverSoftwareVersion"; public const POCKETMINE_COMMAND_VERSION_USAGE = "pocketmine.command.version.usage"; public const POCKETMINE_COMMAND_WHITELIST_DESCRIPTION = "pocketmine.command.whitelist.description"; + public const POCKETMINE_COMMAND_XP_DESCRIPTION = "pocketmine.command.xp.description"; + public const POCKETMINE_COMMAND_XP_USAGE = "pocketmine.command.xp.usage"; public const POCKETMINE_CRASH_ARCHIVE = "pocketmine.crash.archive"; public const POCKETMINE_CRASH_CREATE = "pocketmine.crash.create"; public const POCKETMINE_CRASH_ERROR = "pocketmine.crash.error"; @@ -449,6 +461,8 @@ final class KnownTranslationKeys{ public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_LIST = "pocketmine.permission.command.whitelist.list"; public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_RELOAD = "pocketmine.permission.command.whitelist.reload"; public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE = "pocketmine.permission.command.whitelist.remove"; + public const POCKETMINE_PERMISSION_COMMAND_XP_OTHER = "pocketmine.permission.command.xp.other"; + public const POCKETMINE_PERMISSION_COMMAND_XP_SELF = "pocketmine.permission.command.xp.self"; public const POCKETMINE_PERMISSION_GROUP_CONSOLE = "pocketmine.permission.group.console"; public const POCKETMINE_PERMISSION_GROUP_OPERATOR = "pocketmine.permission.group.operator"; public const POCKETMINE_PERMISSION_GROUP_USER = "pocketmine.permission.group.user"; diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 8a42371e694..88b4ba1a053 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -497,15 +497,18 @@ private function handleUseItemTransaction(UseItemTransactionData $data) : bool{ $blockPos = $data->getBlockPosition(); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); - if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){ - $this->onFailedBlockAction($vBlockPos, $data->getFace()); - } + $this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos); + //always sync this in case plugins caused a different result than the client expected + //we *could* try to enhance detection of plugin-altered behaviour, but this would require propagating + //more information up the stack. For now I think this is good enough. + //if only the client would tell us what blocks it thinks changed... + $this->syncBlocksNearby($vBlockPos, $data->getFace()); return true; case UseItemTransactionData::ACTION_BREAK_BLOCK: $blockPos = $data->getBlockPosition(); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); if(!$this->player->breakBlock($vBlockPos)){ - $this->onFailedBlockAction($vBlockPos, null); + $this->syncBlocksNearby($vBlockPos, null); } return true; case UseItemTransactionData::ACTION_CLICK_AIR: @@ -533,9 +536,9 @@ private static function validateFacing(int $facing) : void{ } /** - * Internal function used to execute rollbacks when an action fails on a block. + * Syncs blocks nearby to ensure that the client and server agree on the world's blocks after a block interaction. */ - private function onFailedBlockAction(Vector3 $blockPos, ?int $face) : void{ + private function syncBlocksNearby(Vector3 $blockPos, ?int $face) : void{ if($blockPos->distanceSquared($this->player->getLocation()) < 10000){ $blocks = $blockPos->sidesArray(); if($face !== null){ @@ -672,7 +675,7 @@ public function handleBlockPickRequest(BlockPickRequestPacket $packet) : bool{ } public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{ - return false; //TODO + return $this->player->pickEntity($packet->actorUniqueId); } public function handlePlayerAction(PlayerActionPacket $packet) : bool{ @@ -686,7 +689,7 @@ private function handlePlayerActionFromData(int $action, BlockPosition $blockPos case PlayerAction::START_BREAK: self::validateFacing($face); if(!$this->player->attackBlock($pos, $face)){ - $this->onFailedBlockAction($pos, $face); + $this->syncBlocksNearby($pos, $face); } break; @@ -1002,7 +1005,7 @@ public function handleLecternUpdate(LecternUpdatePacket $packet) : bool{ $lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ()); if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){ if(!$lectern->onPageTurn($packet->page)){ - $this->onFailedBlockAction($lectern->getPosition(), null); + $this->syncBlocksNearby($lectern->getPosition(), null); } return true; } diff --git a/src/permission/DefaultPermissionNames.php b/src/permission/DefaultPermissionNames.php index fab532e2869..a916e813c05 100644 --- a/src/permission/DefaultPermissionNames.php +++ b/src/permission/DefaultPermissionNames.php @@ -84,6 +84,8 @@ final class DefaultPermissionNames{ public const COMMAND_WHITELIST_LIST = "pocketmine.command.whitelist.list"; public const COMMAND_WHITELIST_RELOAD = "pocketmine.command.whitelist.reload"; public const COMMAND_WHITELIST_REMOVE = "pocketmine.command.whitelist.remove"; + public const COMMAND_XP_OTHER = "pocketmine.command.xp.other"; + public const COMMAND_XP_SELF = "pocketmine.command.xp.self"; public const GROUP_CONSOLE = "pocketmine.group.console"; public const GROUP_OPERATOR = "pocketmine.group.operator"; public const GROUP_USER = "pocketmine.group.user"; diff --git a/src/permission/DefaultPermissions.php b/src/permission/DefaultPermissions.php index c72765af6bc..1030e9ff8cc 100644 --- a/src/permission/DefaultPermissions.php +++ b/src/permission/DefaultPermissions.php @@ -112,5 +112,7 @@ public static function registerCorePermissions() : void{ self::registerPermission(new Permission(Names::COMMAND_WHITELIST_LIST, l10n::pocketmine_permission_command_whitelist_list()), [$operatorRoot]); self::registerPermission(new Permission(Names::COMMAND_WHITELIST_RELOAD, l10n::pocketmine_permission_command_whitelist_reload()), [$operatorRoot]); self::registerPermission(new Permission(Names::COMMAND_WHITELIST_REMOVE, l10n::pocketmine_permission_command_whitelist_remove()), [$operatorRoot]); + self::registerPermission(new Permission(Names::COMMAND_XP_OTHER, l10n::pocketmine_permission_command_xp_other()), [$operatorRoot]); + self::registerPermission(new Permission(Names::COMMAND_XP_SELF, l10n::pocketmine_permission_command_xp_self()), [$operatorRoot]); } } diff --git a/src/permission/PermissionManager.php b/src/permission/PermissionManager.php index f2b02e8e5ea..c9e37f5e946 100644 --- a/src/permission/PermissionManager.php +++ b/src/permission/PermissionManager.php @@ -23,6 +23,7 @@ namespace pocketmine\permission; +use pocketmine\Server; use pocketmine\utils\Utils; use function count; use function spl_object_id; @@ -71,6 +72,10 @@ public function removePermission(Permission|string $permission) : void{ } } + /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::subscribeToBroadcastChannel() + */ public function subscribeToPermission(string $permission, PermissibleInternal $permissible) : void{ if(!isset($this->permSubs[$permission])){ $this->permSubs[$permission] = []; @@ -78,6 +83,10 @@ public function subscribeToPermission(string $permission, PermissibleInternal $p $this->permSubs[$permission][spl_object_id($permissible)] = $permissible; } + /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::unsubscribeFromBroadcastChannel() + */ public function unsubscribeFromPermission(string $permission, PermissibleInternal $permissible) : void{ if(isset($this->permSubs[$permission][spl_object_id($permissible)])){ if(count($this->permSubs[$permission]) === 1){ @@ -88,6 +97,10 @@ public function unsubscribeFromPermission(string $permission, PermissibleInterna } } + /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::unsubscribeFromAllBroadcastChannels() + */ public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{ foreach(Utils::promoteKeys($this->permSubs) as $permission => $subs){ if(count($subs) === 1 && isset($subs[spl_object_id($permissible)])){ @@ -99,6 +112,8 @@ public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) } /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::getBroadcastChannelSubscribers() * @return PermissibleInternal[] */ public function getPermissionSubscriptions(string $permission) : array{ diff --git a/src/player/Player.php b/src/player/Player.php index d203fe6a949..bf911a2ff26 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -57,6 +57,7 @@ use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEntityInteractEvent; +use pocketmine\event\player\PlayerEntityPickEvent; use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerInteractEvent; @@ -109,6 +110,7 @@ use pocketmine\network\mcpe\protocol\MovePlayerPacket; use pocketmine\network\mcpe\protocol\SetActorMotionPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; +use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; @@ -190,6 +192,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ private const TAG_SPAWN_X = "SpawnX"; //TAG_Int private const TAG_SPAWN_Y = "SpawnY"; //TAG_Int private const TAG_SPAWN_Z = "SpawnZ"; //TAG_Int + private const TAG_DEATH_WORLD = "DeathLevel"; //TAG_String + private const TAG_DEATH_X = "DeathPositionX"; //TAG_Int + private const TAG_DEATH_Y = "DeathPositionY"; //TAG_Int + private const TAG_DEATH_Z = "DeathPositionZ"; //TAG_Int public const TAG_LEVEL = "Level"; //TAG_String public const TAG_LAST_KNOWN_XUID = "LastKnownXUID"; //TAG_String @@ -272,6 +278,8 @@ public static function isValidUserName(?string $name) : bool{ private bool $respawnLocked = false; + private ?Position $deathPosition = null; + //TODO: Abilities protected bool $autoJump = true; protected bool $allowFlight = false; @@ -390,6 +398,9 @@ function() : void{ if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_SPAWN_WORLD, ""))) instanceof World){ $this->spawnPosition = new Position($nbt->getInt(self::TAG_SPAWN_X), $nbt->getInt(self::TAG_SPAWN_Y), $nbt->getInt(self::TAG_SPAWN_Z), $world); } + if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_DEATH_WORLD, ""))) instanceof World){ + $this->deathPosition = new Position($nbt->getInt(self::TAG_DEATH_X), $nbt->getInt(self::TAG_DEATH_Y), $nbt->getInt(self::TAG_DEATH_Z), $world); + } } public function getLeaveMessage() : Translatable|string{ @@ -1031,6 +1042,30 @@ public function doChunkRequests() : void{ } } + public function getDeathPosition() : ?Position{ + if($this->deathPosition !== null && !$this->deathPosition->isValid()){ + $this->deathPosition = null; + } + return $this->deathPosition; + } + + /** + * @param Vector3|Position|null $pos + */ + public function setDeathPosition(?Vector3 $pos) : void{ + if($pos !== null){ + if($pos instanceof Position && $pos->world !== null){ + $world = $pos->world; + }else{ + $world = $this->getWorld(); + } + $this->deathPosition = new Position($pos->x, $pos->y, $pos->z, $world); + }else{ + $this->deathPosition = null; + } + $this->networkPropertiesDirty = true; + } + /** * @return Position */ @@ -1470,6 +1505,10 @@ public function onUpdate(int $currentTick) : bool{ return true; } + public function canEat() : bool{ + return $this->isCreative() || parent::canEat(); + } + public function canBreathe() : bool{ return $this->isCreative() || parent::canBreathe(); } @@ -1709,29 +1748,58 @@ public function pickBlock(Vector3 $pos, bool $addTileNBT) : bool{ $ev->call(); if(!$ev->isCancelled()){ - if($existingSlot !== -1){ - if($existingSlot < $this->inventory->getHotbarSize()){ - $this->inventory->setHeldItemIndex($existingSlot); - }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); - } - }else{ - $firstEmpty = $this->inventory->firstEmpty(); - if($firstEmpty === -1){ //full inventory - $this->inventory->setItemInHand($item); - }elseif($firstEmpty < $this->inventory->getHotbarSize()){ - $this->inventory->setItem($firstEmpty, $item); - $this->inventory->setHeldItemIndex($firstEmpty); - }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); - $this->inventory->setItemInHand($item); - } - } + $this->equipOrAddPickedItem($existingSlot, $item); + } + + return true; + } + + public function pickEntity(int $entityId) : bool{ + $entity = $this->getWorld()->getEntity($entityId); + if($entity === null){ + return true; + } + + $item = $entity->getPickedItem(); + if($item === null){ + return true; + } + + $ev = new PlayerEntityPickEvent($this, $entity, $item); + $existingSlot = $this->inventory->first($item); + if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){ + $ev->cancel(); + } + $ev->call(); + + if(!$ev->isCancelled()){ + $this->equipOrAddPickedItem($existingSlot, $item); } return true; } + private function equipOrAddPickedItem(int $existingSlot, Item $item) : void{ + if($existingSlot !== -1){ + if($existingSlot < $this->inventory->getHotbarSize()){ + $this->inventory->setHeldItemIndex($existingSlot); + }else{ + $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); + } + }else{ + $firstEmpty = $this->inventory->firstEmpty(); + if($firstEmpty === -1){ //full inventory + $this->inventory->setItemInHand($item); + }elseif($firstEmpty < $this->inventory->getHotbarSize()){ + $this->inventory->setItem($firstEmpty, $item); + $this->inventory->setHeldItemIndex($firstEmpty); + }else{ + $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); + $this->inventory->setItemInHand($item); + } + } + } + /** * Performs a left-click (attack) action on the block. * @@ -2302,6 +2370,7 @@ protected function destroyCycles() : void{ unset($this->cursorInventory); unset($this->craftingGrid); $this->spawnPosition = null; + $this->deathPosition = null; $this->blockBreakHandler = null; parent::destroyCycles(); } @@ -2343,6 +2412,13 @@ public function getSaveData() : CompoundTag{ $nbt->setInt(self::TAG_SPAWN_Z, $spawn->getFloorZ()); } + if($this->deathPosition !== null && $this->deathPosition->isValid()){ + $nbt->setString(self::TAG_DEATH_WORLD, $this->deathPosition->getWorld()->getFolderName()); + $nbt->setInt(self::TAG_DEATH_X, $this->deathPosition->getFloorX()); + $nbt->setInt(self::TAG_DEATH_Y, $this->deathPosition->getFloorY()); + $nbt->setInt(self::TAG_DEATH_Z, $this->deathPosition->getFloorZ()); + } + $nbt->setInt(self::TAG_GAME_MODE, GameModeIdMap::getInstance()->toId($this->gamemode)); $nbt->setLong(self::TAG_FIRST_PLAYED, $this->firstPlayed); $nbt->setLong(self::TAG_LAST_PLAYED, (int) floor(microtime(true) * 1000)); @@ -2362,6 +2438,8 @@ protected function onDeath() : void{ //main inventory and drops the rest on the ground. $this->removeCurrentWindow(); + $this->setDeathPosition($this->getPosition()); + $ev = new PlayerDeathEvent($this, $this->getDrops(), $this->getXpDropAmount(), null); $ev->call(); @@ -2490,6 +2568,17 @@ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ $properties->setPlayerFlag(PlayerMetadataFlags::SLEEP, $this->sleeping !== null); $properties->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $this->sleeping !== null ? BlockPosition::fromVector3($this->sleeping) : new BlockPosition(0, 0, 0)); + + if($this->deathPosition !== null && $this->deathPosition->world === $this->location->world){ + $properties->setBlockPos(EntityMetadataProperties::PLAYER_DEATH_POSITION, BlockPosition::fromVector3($this->deathPosition)); + //TODO: this should be updated when dimensions are implemented + $properties->setInt(EntityMetadataProperties::PLAYER_DEATH_DIMENSION, DimensionIds::OVERWORLD); + $properties->setByte(EntityMetadataProperties::PLAYER_HAS_DIED, 1); + }else{ + $properties->setBlockPos(EntityMetadataProperties::PLAYER_DEATH_POSITION, new BlockPosition(0, 0, 0)); + $properties->setInt(EntityMetadataProperties::PLAYER_DEATH_DIMENSION, DimensionIds::OVERWORLD); + $properties->setByte(EntityMetadataProperties::PLAYER_HAS_DIED, 0); + } } public function sendData(?array $targets, ?array $data = null) : void{ diff --git a/src/plugin/DiskResourceProvider.php b/src/plugin/DiskResourceProvider.php index efdc9cd1724..9649f565f13 100644 --- a/src/plugin/DiskResourceProvider.php +++ b/src/plugin/DiskResourceProvider.php @@ -36,6 +36,8 @@ /** * Provides resources from the given plugin directory on disk. The path may be prefixed with a specific access protocol * to enable special types of access. + * + * @deprecated */ class DiskResourceProvider implements ResourceProvider{ private string $file; diff --git a/src/plugin/ResourceProvider.php b/src/plugin/ResourceProvider.php index 3594d7eee88..5ff8db882a8 100644 --- a/src/plugin/ResourceProvider.php +++ b/src/plugin/ResourceProvider.php @@ -23,6 +23,9 @@ namespace pocketmine\plugin; +/** + * @deprecated + */ interface ResourceProvider{ /** * Gets an embedded resource on the plugin file. diff --git a/src/promise/Promise.php b/src/promise/Promise.php index cd8fcc4f1c9..c09037158bf 100644 --- a/src/promise/Promise.php +++ b/src/promise/Promise.php @@ -70,16 +70,17 @@ public function isResolved() : bool{ * * @phpstan-template TPromiseValue * @phpstan-template TKey of array-key - * @phpstan-param non-empty-array> $promises + * @phpstan-param array> $promises * * @phpstan-return Promise> */ public static function all(array $promises) : Promise{ - if(count($promises) === 0){ - throw new \InvalidArgumentException("At least one promise must be provided"); - } /** @phpstan-var PromiseResolver> $resolver */ $resolver = new PromiseResolver(); + if(count($promises) === 0){ + $resolver->resolve([]); + return $resolver->getPromise(); + } $values = []; $toResolve = count($promises); $continue = true; diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index b0b64347adc..1175b6fc4f9 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -27,6 +27,7 @@ use pmmp\thread\ThreadSafe; use pmmp\thread\ThreadSafeArray; use pocketmine\thread\NonThreadSafeValue; +use pocketmine\timings\Timings; use function array_key_exists; use function igbinary_serialize; use function igbinary_unserialize; @@ -67,7 +68,10 @@ abstract class AsyncTask extends Runnable{ */ private static array $threadLocalStorage = []; - /** @phpstan-var ThreadSafeArray|null */ + /** + * @phpstan-var ThreadSafeArray|null + * @deprecated + */ private ?ThreadSafeArray $progressUpdates = null; private ThreadSafe|string|int|bool|null|float $result = null; @@ -78,7 +82,14 @@ abstract class AsyncTask extends Runnable{ public function run() : void{ $this->result = null; - $this->onRun(); + $timings = Timings::getAsyncTaskRunTimings($this); + $timings->startTiming(); + + try{ + $this->onRun(); + }finally{ + $timings->stopTiming(); + } $this->finished = true; AsyncWorker::getNotifier()->wakeupSleeper(); @@ -153,6 +164,8 @@ public function onCompletion() : void{ } /** + * @deprecated + * * Call this method from {@link AsyncTask::onRun} (AsyncTask execution thread) to schedule a call to * {@link AsyncTask::onProgressUpdate} from the main thread with the given progress parameter. * @@ -167,6 +180,7 @@ public function publishProgress(mixed $progress) : void{ } /** + * @deprecated * @internal Only call from AsyncPool.php on the main thread */ public function checkProgressUpdates() : void{ @@ -179,6 +193,8 @@ public function checkProgressUpdates() : void{ } /** + * @deprecated + * * Called from the main thread after {@link AsyncTask::publishProgress} is called. * All {@link AsyncTask::publishProgress} calls should result in {@link AsyncTask::onProgressUpdate} calls before * {@link AsyncTask::onCompletion} is called. 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/scheduler/TimingsCollectionTask.php b/src/scheduler/TimingsCollectionTask.php new file mode 100644 index 00000000000..25cd41166c2 --- /dev/null +++ b/src/scheduler/TimingsCollectionTask.php @@ -0,0 +1,60 @@ +> + */ +final class TimingsCollectionTask extends AsyncTask{ + private const TLS_KEY_RESOLVER = "resolver"; + + /** + * @phpstan-param PromiseResolver> $promiseResolver + */ + public function __construct(PromiseResolver $promiseResolver){ + $this->storeLocal(self::TLS_KEY_RESOLVER, $promiseResolver); + } + + public function onRun() : void{ + $this->setResult(TimingsHandler::printCurrentThreadRecords()); + } + + public function onCompletion() : void{ + /** + * @var string[] $result + * @phpstan-var list $result + */ + $result = $this->getResult(); + /** + * @var PromiseResolver $promiseResolver + * @phpstan-var PromiseResolver> $promiseResolver + */ + $promiseResolver = $this->fetchLocal(self::TLS_KEY_RESOLVER); + + $promiseResolver->resolve($result); + } +} diff --git a/src/scheduler/TimingsControlTask.php b/src/scheduler/TimingsControlTask.php new file mode 100644 index 00000000000..51e906e6bcb --- /dev/null +++ b/src/scheduler/TimingsControlTask.php @@ -0,0 +1,60 @@ +operation === self::ENABLE){ + TimingsHandler::setEnabled(true); + \GlobalLogger::get()->debug("Enabled timings"); + }elseif($this->operation === self::DISABLE){ + TimingsHandler::setEnabled(false); + \GlobalLogger::get()->debug("Disabled timings"); + }elseif($this->operation === self::RELOAD){ + TimingsHandler::reload(); + \GlobalLogger::get()->debug("Reset timings"); + }else{ + throw new \InvalidArgumentException("Invalid operation $this->operation"); + } + } +} diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 563af69bff6..e717000233f 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -30,12 +30,14 @@ 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; abstract class Timings{ public const GROUP_MINECRAFT = "Minecraft"; + /** @deprecated No longer used */ public const GROUP_BREAKDOWN = "Minecraft - Breakdown"; private static bool $initialized = false; @@ -123,11 +125,16 @@ abstract class Timings{ /** @var TimingsHandler[] */ private static array $asyncTaskProgressUpdate = []; + /** @var TimingsHandler[] */ private static array $asyncTaskCompletion = []; /** @var TimingsHandler[] */ private static array $asyncTaskError = []; + private static TimingsHandler $asyncTaskWorkers; + /** @var TimingsHandler[] */ + private static array $asyncTaskRun = []; + public static function init() : void{ if(self::$initialized){ return; @@ -187,11 +194,17 @@ public static function init() : void{ self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync); self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync); + self::$asyncTaskWorkers = new TimingsHandler("Async Task Workers"); + self::$playerCommand = new TimingsHandler("Player Command"); self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache"); } + /** + * @template TTask of Task + * @phpstan-param TaskHandler $task + */ public static function getScheduledTaskTimings(TaskHandler $task, int $period) : TimingsHandler{ self::init(); $name = "Task: " . $task->getTaskName(); @@ -339,6 +352,9 @@ public static function getAsyncTaskCompletionTimings(AsyncTask $task, string $gr return self::$asyncTaskCompletion[$taskClass]; } + /** + * @deprecated No longer used + */ public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ $taskClass = $task::class; if(!isset(self::$asyncTaskError[$taskClass])){ @@ -352,4 +368,18 @@ public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = return self::$asyncTaskError[$taskClass]; } + + public static function getAsyncTaskRunTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ + $taskClass = $task::class; + if(!isset(self::$asyncTaskRun[$taskClass])){ + self::init(); + self::$asyncTaskRun[$taskClass] = new TimingsHandler( + "AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Run", + self::$asyncTaskWorkers, + $group + ); + } + + return self::$asyncTaskRun[$taskClass]; + } } diff --git a/src/timings/TimingsHandler.php b/src/timings/TimingsHandler.php index 574dd6d2b88..95f7dbacc6d 100644 --- a/src/timings/TimingsHandler.php +++ b/src/timings/TimingsHandler.php @@ -23,20 +23,66 @@ namespace pocketmine\timings; +use pmmp\thread\Thread as NativeThread; +use pocketmine\promise\Promise; +use pocketmine\promise\PromiseResolver; use pocketmine\Server; +use pocketmine\utils\ObjectSet; use pocketmine\utils\Utils; +use function array_merge; +use function array_push; use function hrtime; use function implode; use function spl_object_id; +/** + * @phpstan-type CollectPromise Promise> + */ class TimingsHandler{ - private const FORMAT_VERSION = 2; //peak timings fix + private const FORMAT_VERSION = 3; //thread timings collection private static bool $enabled = false; private static int $timingStart = 0; - /** @return string[] */ - public static function printTimings() : array{ + /** @phpstan-var ObjectSet<\Closure(bool $enable) : void> */ + private static ?ObjectSet $toggleCallbacks = null; + /** @phpstan-var ObjectSet<\Closure() : void> */ + private static ?ObjectSet $reloadCallbacks = null; + /** @phpstan-var ObjectSet<\Closure() : list> */ + private static ?ObjectSet $collectCallbacks = null; + + /** + * @phpstan-template T of object + * @phpstan-param ?ObjectSet $where + * @phpstan-param-out ObjectSet $where + * @phpstan-return ObjectSet + */ + private static function lazyGetSet(?ObjectSet &$where) : ObjectSet{ + //workaround for phpstan bug - allows us to ignore 1 error instead of 6 without suppressing other errors + return $where ??= new ObjectSet(); + } + + /** + * @phpstan-return ObjectSet<\Closure(bool $enable) : void> + */ + public static function getToggleCallbacks() : ObjectSet{ return self::lazyGetSet(self::$toggleCallbacks); } + + /** + * @phpstan-return ObjectSet<\Closure() : void> + */ + public static function getReloadCallbacks() : ObjectSet{ return self::lazyGetSet(self::$reloadCallbacks); } + + /** + * @phpstan-return ObjectSet<\Closure() : list> + */ + public static function getCollectCallbacks() : ObjectSet{ return self::lazyGetSet(self::$collectCallbacks); } + + /** + * @return string[] + * @phpstan-return list + */ + public static function printCurrentThreadRecords() : array{ + $threadId = NativeThread::getCurrentThread()?->getThreadId(); $groups = []; foreach(TimingsRecord::getAll() as $timings){ @@ -49,7 +95,7 @@ public static function printTimings() : array{ $avg = $time / $count; - $group = $timings->getGroup(); + $group = $timings->getGroup() . ($threadId !== null ? " ThreadId: $threadId" : ""); $groups[$group][] = implode(" ", [ $timings->getName(), "Time: $time", @@ -72,6 +118,15 @@ public static function printTimings() : array{ } } + return $result; + } + + /** + * @return string[] + */ + private static function printFooter() : array{ + $result = []; + $result[] = "# Version " . Server::getInstance()->getVersion(); $result[] = "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion(); @@ -79,29 +134,95 @@ public static function printTimings() : array{ $sampleTime = hrtime(true) - self::$timingStart; $result[] = "Sample time $sampleTime (" . ($sampleTime / 1000000000) . "s)"; + return $result; } + /** + * @deprecated This only collects timings from the main thread. Collecting timings from all threads is an async + * operation, so it can't be done synchronously. + * + * @return string[] + */ + public static function printTimings() : array{ + $records = self::printCurrentThreadRecords(); + $footer = self::printFooter(); + + return [...$records, ...$footer]; + } + + /** + * Collects timings asynchronously, allowing timings from multiple threads to be aggregated into a single report. + * + * NOTE: You need to add a callback to collectCallbacks if you want to include timings from other threads. They + * won't be automatically collected if you don't, since the main thread has no way to access them. + * + * This is an asynchronous operation, and the result is returned as a promise. + * The caller must add a callback to the returned promise to get the complete timings report. + * + * @phpstan-return Promise> + */ + public static function requestPrintTimings() : Promise{ + $thisThreadRecords = self::printCurrentThreadRecords(); + + $otherThreadRecordPromises = []; + if(self::$collectCallbacks !== null){ + foreach(self::$collectCallbacks as $callback){ + $callbackPromises = $callback(); + array_push($otherThreadRecordPromises, ...$callbackPromises); + } + } + + $resolver = new PromiseResolver(); + Promise::all($otherThreadRecordPromises)->onCompletion( + function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{ + $resolver->resolve([...$thisThreadRecords, ...array_merge(...$promisedRecords), ...self::printFooter()]); + }, + function() : void{ + throw new \AssertionError("This promise is not expected to be rejected"); + } + ); + + return $resolver->getPromise(); + } + public static function isEnabled() : bool{ return self::$enabled; } public static function setEnabled(bool $enable = true) : void{ + if($enable === self::$enabled){ + return; + } self::$enabled = $enable; - self::reload(); + self::internalReload(); + if(self::$toggleCallbacks !== null){ + foreach(self::$toggleCallbacks as $callback){ + $callback($enable); + } + } } public static function getStartTime() : float{ return self::$timingStart; } - public static function reload() : void{ + private static function internalReload() : void{ TimingsRecord::reset(); if(self::$enabled){ self::$timingStart = hrtime(true); } } + public static function reload() : void{ + self::internalReload(); + if(self::$reloadCallbacks !== null){ + foreach(self::$reloadCallbacks as $callback){ + $callback(); + } + } + } + public static function tick(bool $measure = true) : void{ if(self::$enabled){ TimingsRecord::tick($measure); diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 38f523ad4db..c8be174d6a1 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -67,6 +67,8 @@ use function is_object; use function is_string; use function mb_check_encoding; +use function mt_getrandmax; +use function mt_rand; use function ob_end_clean; use function ob_get_contents; use function ob_start; @@ -688,4 +690,12 @@ function_exists('opcache_get_status') && //jit not available return null; } + + /** + * Returns a random float between 0.0 and 1.0 + * Drop-in replacement for lcg_value() + */ + public static function getRandomFloat() : float{ + return mt_rand() / mt_getrandmax(); + } } diff --git a/src/world/World.php b/src/world/World.php index 187f7ab8b89..ff65377c094 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -83,6 +83,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Limits; use pocketmine\utils\ReversePriorityQueue; +use pocketmine\utils\Utils; use pocketmine\world\biome\Biome; use pocketmine\world\biome\BiomeRegistry; use pocketmine\world\format\Chunk; @@ -120,7 +121,6 @@ use function gettype; use function is_a; use function is_object; -use function lcg_value; use function max; use function microtime; use function min; @@ -1998,10 +1998,10 @@ public function dropItem(Vector3 $source, Item $item, ?Vector3 $motion = null, i return null; } - $itemEntity = new ItemEntity(Location::fromObject($source, $this, lcg_value() * 360, 0), $item); + $itemEntity = new ItemEntity(Location::fromObject($source, $this, Utils::getRandomFloat() * 360, 0), $item); $itemEntity->setPickupDelay($delay); - $itemEntity->setMotion($motion ?? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1)); + $itemEntity->setMotion($motion ?? new Vector3(Utils::getRandomFloat() * 0.2 - 0.1, 0.2, Utils::getRandomFloat() * 0.2 - 0.1)); $itemEntity->spawnToAll(); return $itemEntity; @@ -2018,9 +2018,9 @@ public function dropExperience(Vector3 $pos, int $amount) : array{ $orbs = []; foreach(ExperienceOrb::splitIntoOrbSizes($amount) as $split){ - $orb = new ExperienceOrb(Location::fromObject($pos, $this, lcg_value() * 360, 0), $split); + $orb = new ExperienceOrb(Location::fromObject($pos, $this, Utils::getRandomFloat() * 360, 0), $split); - $orb->setMotion(new Vector3((lcg_value() * 0.2 - 0.1) * 2, lcg_value() * 0.4, (lcg_value() * 0.2 - 0.1) * 2)); + $orb->setMotion(new Vector3((Utils::getRandomFloat() * 0.2 - 0.1) * 2, Utils::getRandomFloat() * 0.4, (Utils::getRandomFloat() * 0.2 - 0.1) * 2)); $orb->spawnToAll(); $orbs[] = $orb; @@ -2173,19 +2173,25 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli if($player !== null){ $ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK); + if($player->isSneaking()){ + $ev->setUseItem(false); + $ev->setUseBlock($item->isNull()); //opening doors is still possible when sneaking if using an empty hand + } if($player->isSpectator()){ $ev->cancel(); //set it to cancelled so plugins can bypass this } $ev->call(); if(!$ev->isCancelled()){ - if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ + if($ev->useBlock() && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ return true; } - $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems); - if($result !== ItemUseResult::NONE){ - return $result === ItemUseResult::SUCCESS; + if($ev->useItem()){ + $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems); + if($result !== ItemUseResult::NONE){ + return $result === ItemUseResult::SUCCESS; + } } }else{ return false; diff --git a/src/world/sound/BottleEmptySound.php b/src/world/sound/BottleEmptySound.php new file mode 100644 index 00000000000..bb2a0be2ad1 --- /dev/null +++ b/src/world/sound/BottleEmptySound.php @@ -0,0 +1,35 @@ +goatHornType){ + GoatHornType::PONDER => LevelSoundEvent::HORN_CALL0, + GoatHornType::SING => LevelSoundEvent::HORN_CALL1, + GoatHornType::SEEK => LevelSoundEvent::HORN_CALL2, + GoatHornType::FEEL => LevelSoundEvent::HORN_CALL3, + GoatHornType::ADMIRE => LevelSoundEvent::HORN_CALL4, + GoatHornType::CALL => LevelSoundEvent::HORN_CALL5, + GoatHornType::YEARN => LevelSoundEvent::HORN_CALL6, + GoatHornType::DREAM => LevelSoundEvent::HORN_CALL7 + }, $pos, false)]; + } +} 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 diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 02c94d2d68e..ea5e1c62a70 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -20,6 +20,56 @@ parameters: count: 1 path: ../../../src/block/DoubleTallGrass.php + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:ACACIA_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:BIRCH_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CHERRY_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CRIMSON_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:DARK_OAK_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:JUNGLE_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:MANGROVE_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:OAK_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:SPRUCE_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:WARPED_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#" count: 1 @@ -40,6 +90,11 @@ parameters: count: 1 path: ../../../src/plugin/PluginManager.php + - + message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\ but returns pocketmine\\\\utils\\\\ObjectSet\\\\.$#" + count: 1 + path: ../../../src/timings/TimingsHandler.php + - message: "#^Casting to int something that's already int\\.$#" count: 1 diff --git a/tests/phpunit/block/BlockTypeIdsTest.php b/tests/phpunit/block/BlockTypeIdsTest.php index 32c8d490295..ce21a89ab68 100644 --- a/tests/phpunit/block/BlockTypeIdsTest.php +++ b/tests/phpunit/block/BlockTypeIdsTest.php @@ -51,6 +51,7 @@ public function testVanillaBlocksParity() : void{ foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){ $expected = $block->getTypeId(); $actual = $reflect->getConstant($name); + self::assertNotFalse($actual, "VanillaBlocks::$name() does not have a BlockTypeIds constant"); self::assertSame($expected, $actual, "VanillaBlocks::$name() does not match BlockTypeIds::$name"); } } diff --git a/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php new file mode 100644 index 00000000000..31ae2e27a4e --- /dev/null +++ b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php @@ -0,0 +1,92 @@ + + */ + public static function commandStringProvider() : \Generator{ + yield ["stop"]; + yield ["pocketmine:status"]; + yield [str_repeat("s", 1000)]; + yield ["time set day"]; + yield ["give \"Steve\" golden_apple"]; + } + + /** + * @dataProvider commandStringProvider + */ + public function testCreateParseSymmetry(string $input) : void{ + $counterCreate = $counterParse = mt_rand(); + $message = ConsoleReaderChildProcessUtils::createMessage($input, $counterCreate); + $parsedInput = ConsoleReaderChildProcessUtils::parseMessage($message, $counterParse); + + self::assertSame($input, $parsedInput); + } + + public function testCreateMessage() : void{ + $counter = 0; + + ConsoleReaderChildProcessUtils::createMessage("", $counter); + self::assertSame(1, $counter, "createMessage should always bump the counter"); + } + + /** + * @phpstan-return \Generator + */ + public static function parseMessageProvider() : \Generator{ + $counter = 0; + yield [ConsoleReaderChildProcessUtils::createMessage("", $counter), true]; + + yield ["", false]; //keepalive message, doesn't bump counter + + $counter = 1; + yield [ConsoleReaderChildProcessUtils::createMessage("", $counter), false]; //mismatched counter + + $counter = 0; + yield ["a" . ConsoleReaderChildProcessUtils::TOKEN_DELIMITER . "b", false]; //message with delimiter but not a valid IPC message + } + + /** + * @dataProvider parseMessageProvider + */ + public static function testParseMessage(string $message, bool $valid) : void{ + $counter = $oldCounter = 0; + + $input = ConsoleReaderChildProcessUtils::parseMessage($message, $counter); + if(!$valid){ + self::assertNull($input, "Result should be null on invalid message"); + self::assertSame($oldCounter, $counter, "Counter shouldn't be bumped on invalid message"); + }else{ + self::assertNotNull($input, "This was a valid message, expected a result"); + self::assertSame($oldCounter + 1, $counter, "Counter should be bumped on valid message parse"); + } + } +} diff --git a/tests/phpunit/event/EventTest.php b/tests/phpunit/event/EventTest.php new file mode 100644 index 00000000000..7410cc3bb02 --- /dev/null +++ b/tests/phpunit/event/EventTest.php @@ -0,0 +1,79 @@ +unregisterAll(); + + //TODO: this is a really bad hack and could break any time if PluginManager decides to access its Server field + //we really need to make it possible to register events without a Plugin or Server context + $mockServer = $this->createMock(Server::class); + $this->mockPlugin = self::createStub(Plugin::class); + $this->mockPlugin->method('isEnabled')->willReturn(true); + + $this->pluginManager = new PluginManager($mockServer, null); + } + + public static function tearDownAfterClass() : void{ + HandlerListManager::global()->unregisterAll(); + } + + public function testHandlerInheritance() : void{ + $expectedOrder = [ + TestGrandchildEvent::class, + TestChildEvent::class, + TestParentEvent::class + ]; + $actualOrder = []; + + foreach($expectedOrder as $class){ + $this->pluginManager->registerEvent( + $class, + function(TestParentEvent $event) use (&$actualOrder, $class) : void{ + $actualOrder[] = $class; + }, + EventPriority::NORMAL, + $this->mockPlugin + ); + } + + $event = new TestGrandchildEvent(); + $event->call(); + + self::assertSame($expectedOrder, $actualOrder, "Expected event handlers to be called from most specific to least specific"); + } +} diff --git a/tests/phpunit/event/HandlerListManagerTest.php b/tests/phpunit/event/HandlerListManagerTest.php index edff36639dd..c61043dab88 100644 --- a/tests/phpunit/event/HandlerListManagerTest.php +++ b/tests/phpunit/event/HandlerListManagerTest.php @@ -24,6 +24,12 @@ namespace pocketmine\event; use PHPUnit\Framework\TestCase; +use pocketmine\event\fixtures\TestAbstractAllowHandleEvent; +use pocketmine\event\fixtures\TestAbstractEvent; +use pocketmine\event\fixtures\TestConcreteEvent; +use pocketmine\event\fixtures\TestConcreteExtendsAbstractEvent; +use pocketmine\event\fixtures\TestConcreteExtendsAllowHandleEvent; +use pocketmine\event\fixtures\TestConcreteExtendsConcreteEvent; class HandlerListManagerTest extends TestCase{ diff --git a/tests/phpunit/event/TestAbstractAllowHandleEvent.php b/tests/phpunit/event/fixtures/TestAbstractAllowHandleEvent.php similarity index 92% rename from tests/phpunit/event/TestAbstractAllowHandleEvent.php rename to tests/phpunit/event/fixtures/TestAbstractAllowHandleEvent.php index 1bac06bbb0e..3831698098e 100644 --- a/tests/phpunit/event/TestAbstractAllowHandleEvent.php +++ b/tests/phpunit/event/fixtures/TestAbstractAllowHandleEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; /** * @allowHandle diff --git a/tests/phpunit/event/TestAbstractEvent.php b/tests/phpunit/event/fixtures/TestAbstractEvent.php similarity index 92% rename from tests/phpunit/event/TestAbstractEvent.php rename to tests/phpunit/event/fixtures/TestAbstractEvent.php index 92a95363e30..b48d8f5269a 100644 --- a/tests/phpunit/event/TestAbstractEvent.php +++ b/tests/phpunit/event/fixtures/TestAbstractEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; abstract class TestAbstractEvent extends Event{ diff --git a/tests/plugins/TesterPlugin/src/event/ChildEvent.php b/tests/phpunit/event/fixtures/TestChildEvent.php similarity index 89% rename from tests/plugins/TesterPlugin/src/event/ChildEvent.php rename to tests/phpunit/event/fixtures/TestChildEvent.php index b71d2627fce..569a2c069ab 100644 --- a/tests/plugins/TesterPlugin/src/event/ChildEvent.php +++ b/tests/phpunit/event/fixtures/TestChildEvent.php @@ -21,8 +21,8 @@ declare(strict_types=1); -namespace pmmp\TesterPlugin\event; +namespace pocketmine\event\fixtures; -class ChildEvent extends ParentEvent{ +class TestChildEvent extends TestParentEvent{ } diff --git a/tests/phpunit/event/TestConcreteEvent.php b/tests/phpunit/event/fixtures/TestConcreteEvent.php similarity index 92% rename from tests/phpunit/event/TestConcreteEvent.php rename to tests/phpunit/event/fixtures/TestConcreteEvent.php index 8b159df91a6..cf744eb2ced 100644 --- a/tests/phpunit/event/TestConcreteEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; class TestConcreteEvent extends Event{ diff --git a/tests/phpunit/event/TestConcreteExtendsAbstractEvent.php b/tests/phpunit/event/fixtures/TestConcreteExtendsAbstractEvent.php similarity index 95% rename from tests/phpunit/event/TestConcreteExtendsAbstractEvent.php rename to tests/phpunit/event/fixtures/TestConcreteExtendsAbstractEvent.php index 3f0fa572fef..54ec3dbb128 100644 --- a/tests/phpunit/event/TestConcreteExtendsAbstractEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteExtendsAbstractEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestConcreteExtendsAbstractEvent extends TestAbstractEvent{ diff --git a/tests/phpunit/event/TestConcreteExtendsAllowHandleEvent.php b/tests/phpunit/event/fixtures/TestConcreteExtendsAllowHandleEvent.php similarity index 95% rename from tests/phpunit/event/TestConcreteExtendsAllowHandleEvent.php rename to tests/phpunit/event/fixtures/TestConcreteExtendsAllowHandleEvent.php index e4146512116..362ce693b50 100644 --- a/tests/phpunit/event/TestConcreteExtendsAllowHandleEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteExtendsAllowHandleEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestConcreteExtendsAllowHandleEvent extends TestAbstractAllowHandleEvent{ diff --git a/tests/phpunit/event/TestConcreteExtendsConcreteEvent.php b/tests/phpunit/event/fixtures/TestConcreteExtendsConcreteEvent.php similarity index 95% rename from tests/phpunit/event/TestConcreteExtendsConcreteEvent.php rename to tests/phpunit/event/fixtures/TestConcreteExtendsConcreteEvent.php index cc955893515..3fd3a6cf0e0 100644 --- a/tests/phpunit/event/TestConcreteExtendsConcreteEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteExtendsConcreteEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestConcreteExtendsConcreteEvent extends TestConcreteEvent{ diff --git a/tests/plugins/TesterPlugin/src/event/ParentEvent.php b/tests/phpunit/event/fixtures/TestGrandchildEvent.php similarity index 89% rename from tests/plugins/TesterPlugin/src/event/ParentEvent.php rename to tests/phpunit/event/fixtures/TestGrandchildEvent.php index 68f7df6300e..bfe50f9f3d4 100644 --- a/tests/plugins/TesterPlugin/src/event/ParentEvent.php +++ b/tests/phpunit/event/fixtures/TestGrandchildEvent.php @@ -21,8 +21,8 @@ declare(strict_types=1); -namespace pmmp\TesterPlugin\event; +namespace pocketmine\event\fixtures; -class ParentEvent extends \pocketmine\event\Event{ +class TestGrandchildEvent extends TestChildEvent{ } diff --git a/tests/plugins/TesterPlugin/src/event/GrandchildEvent.php b/tests/phpunit/event/fixtures/TestParentEvent.php similarity index 87% rename from tests/plugins/TesterPlugin/src/event/GrandchildEvent.php rename to tests/phpunit/event/fixtures/TestParentEvent.php index 40c37c56794..c204422722b 100644 --- a/tests/plugins/TesterPlugin/src/event/GrandchildEvent.php +++ b/tests/phpunit/event/fixtures/TestParentEvent.php @@ -21,8 +21,10 @@ declare(strict_types=1); -namespace pmmp\TesterPlugin\event; +namespace pocketmine\event\fixtures; -class GrandchildEvent extends ChildEvent{ +use pocketmine\event\Event; + +class TestParentEvent extends Event{ } diff --git a/tests/phpunit/item/ItemTypeIdsTest.php b/tests/phpunit/item/ItemTypeIdsTest.php index 7ac8485fccd..7336780b33f 100644 --- a/tests/phpunit/item/ItemTypeIdsTest.php +++ b/tests/phpunit/item/ItemTypeIdsTest.php @@ -54,6 +54,7 @@ public function testVanillaItemsParity() : void{ } $expected = $item->getTypeId(); $actual = $reflect->getConstant($name); + self::assertNotFalse($actual, "VanillaItems::$name() does not have an ItemTypeIds constant"); self::assertSame($expected, $actual, "VanillaItems::$name() type ID does not match ItemTypeIds::$name"); } } diff --git a/tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php b/tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php deleted file mode 100644 index efe20f8d8f9..00000000000 --- a/tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php +++ /dev/null @@ -1,88 +0,0 @@ -getPlugin(); - $plugin->getServer()->getPluginManager()->registerEvent( - ParentEvent::class, - function(ParentEvent $event) : void{ - $this->callOrder[] = ParentEvent::class; - }, - EventPriority::NORMAL, - $plugin - ); - $plugin->getServer()->getPluginManager()->registerEvent( - ChildEvent::class, - function(ChildEvent $event) : void{ - $this->callOrder[] = ChildEvent::class; - }, - EventPriority::NORMAL, - $plugin - ); - $plugin->getServer()->getPluginManager()->registerEvent( - GrandchildEvent::class, - function(GrandchildEvent $event) : void{ - $this->callOrder[] = GrandchildEvent::class; - }, - EventPriority::NORMAL, - $plugin - ); - - $event = new GrandchildEvent(); - $event->call(); - - if($this->callOrder === self::EXPECTED_ORDER){ - $this->setResult(Test::RESULT_OK); - }else{ - $plugin->getLogger()->error("Expected order: " . implode(", ", self::EXPECTED_ORDER) . ", got: " . implode(", ", $this->callOrder)); - $this->setResult(Test::RESULT_FAILED); - } - } -} diff --git a/tests/plugins/TesterPlugin/src/Main.php b/tests/plugins/TesterPlugin/src/Main.php index 26d3441f4a6..08b59dbacb6 100644 --- a/tests/plugins/TesterPlugin/src/Main.php +++ b/tests/plugins/TesterPlugin/src/Main.php @@ -57,7 +57,7 @@ public function onEnable() : void{ }), 10); $this->waitingTests = [ - new EventHandlerInheritanceTest($this), + //Add test objects here ]; }