diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81a0eeb78..f9046017c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -158,6 +158,9 @@ jobs: - name: Regenerate BedrockData available files constants run: php build/generate-bedrockdata-path-consts.php + - name: Regenerate YmlServerProperties constants + run: php build/generate-pocketmine-yml-property-consts.php + - name: Verify code is unchanged run: | git diff diff --git a/build/generate-pocketmine-yml-property-consts.php b/build/generate-pocketmine-yml-property-consts.php new file mode 100644 index 000000000..90342a75f --- /dev/null +++ b/build/generate-pocketmine-yml-property-consts.php @@ -0,0 +1,111 @@ + $constants + * @phpstan-param-out array $constants + */ +function collectProperties(string $prefix, array $properties, array &$constants) : void{ + foreach($properties as $propertyName => $property){ + $fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName; + + $constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName)); + $constants[$constName] = $fullPropertyName; + + if(is_array($property)){ + collectProperties($fullPropertyName, $property, $constants); + } + } +} + +collectProperties("", $defaultConfig, $constants); +ksort($constants, SORT_STRING); + +$file = fopen(dirname(__DIR__) . '/src/YmlServerProperties.php', 'wb'); +if($file === false){ + fwrite(STDERR, "Failed to open output file\n"); + exit(1); +} +fwrite($file, " $propertyName){ + fwrite($file, "\tpublic const $constName = '$propertyName';\n"); +} +fwrite($file, "}\n"); + +fclose($file); + +echo "Done. Don't forget to run CS fixup after generating code.\n"; diff --git a/changelogs/5.5-beta.md b/changelogs/5.5-beta.md new file mode 100644 index 000000000..84153c279 --- /dev/null +++ b/changelogs/5.5-beta.md @@ -0,0 +1,156 @@ +# 5.5.0-BETA1 +Released 23rd August 2023. + +**For Minecraft: Bedrock Edition 1.20.10** + +This is a minor feature release, including performance improvements, new API methods, and new gameplay features. + +**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. + +## Dependencies +- Updated `pocketmine/math` dependency to [`1.0.0`](https://github.com/pmmp/Math/releases/tag/1.0.0). +- Updated `pocketmine/nbt` dependency to [`1.0.0`](https://github.com/pmmp/NBT/releases/tag/1.0.0). + +## Performance +- Some events are now no longer fired if no handlers are registered. + - This improves performance by avoiding unnecessary object allocations and function calls. + - Events such as `DataPacketReceiveEvent`, `DataPacketSendEvent` and `PlayerMoveEvent` are optimized away almost completely by this change, offering some much-needed performance gains. +- Significantly improved performance of small moving entities, such as dropped items. + - This was achieved by a combination of changes, which together improved observed performance with 2000 item entities moving in water by 30-40%. + - The benefit of this will be most noticeable in SkyBlock servers, where large cactus farms can generate thousands of dropped items. +- `World->getCollisionBoxes()` now uses an improved search method, which reduces the work done by the function by almost 90% for small entities. + - This improves performance of collision detection for small entities, such as dropped items. + +## Gameplay +### General +- Implemented enchanting using an enchanting table (yes, finally!) + - Thanks to [@S3v3Nice](https://github.com/S3v3Nice) for investing lots of time and effort into developing this. + - Since this feature is quite complex, it's possible there may be bugs. Please be vigilant and report any issues you find. + +### Blocks +- The following new blocks have been implemented: + - Pink Petals +- Pressure plates are now functional, in the sense that they react when entities stand on them and perform the correct logic. + - Note that since redstone is not yet implemented, pressure plates do not activate any redstone devices, similar to buttons and levers. +- Signs can now be edited by right-clicking them. +- Signs can now be waxed using a honeycomb, which prevents them from being edited. + +### Items +- The following new items have been implemented: + - Enchanted Book + +## API +### `pocketmine\block` +- The following new API methods have been added: + - `public Block->getEnchantmentTags() : list` returns a list of strings indicating which types of enchantment can be applied to the block when in item form + - `public BlockTypeInfo->getEnchantmentTags() : list` + - `protected PressurePlate->getActivationBox() : AxisAlignedBB` - returns the AABB entities must intersect with in order to activate the pressure plate (not the same as the visual shape) + - `protected PressurePlate->hasOutputSignal() : bool` - returns whether the pressure plate has an output signal - this should be implemented by subclasses + - `protected PressurePlate->calculatePlateState() : array{Block, ?bool}` - returns the state the pressure plate will change to if the given list of entities are standing on it, and a bool indicating whether the plate activated or deactivated this tick + - `protected PressurePlate->filterIrrelevantEntities(list $entities) : list` - returns the given list filtered of entities that don't affect the plate's state (e.g. dropped items don't affect stone pressure plates) + - `public BaseSign->isWaxed() : bool` + - `public BaseSign->setWaxed(bool $waxed) : $this` + - `public inventory\EnchantInventory->getInput() : Item` + - `public inventory\EnchantInventory->getLapis() : Item` + - `public inventory\EnchantInventory->getOutput(int $optionId) : ?Item` - returns the item that would be produced if the input item was enchanted with the selected option, or `null` if the option is invalid + - `public inventory\EnchantInventory->getOption(int $optionId) : EnchantOption` - returns the enchanting option at the given index +- The following API methods have signature changes: + - `BlockTypeInfo->__construct()` now accepts an optional `list $enchantmentTags` parameter + - `PressurePlate->__construct()` now accepts an optional `int $deactivationDelayTicks` parameter + - `WeightedPressurePlate->__construct()` now accepts optional `int $deactivationDelayTicks` and `float $signalStrengthFactor` parameters + - `SimplePressurePlate->__construct()` now accepts an optional `int $deactivationDelayTicks` parameter +- The following new classes have been added: + - `PinkPetals` + - `utils\BlockEventHelper` - provides helper methods for calling block-related events +- The following classes have been deprecated: + - `WeightedPressurePlateLight` + - `WeightedPressurePlateHeavy` + +### `pocketmine\entity` +- The following new API methods have been added: + - `public Human->getEnchantmentSeed() : int` - returns the current seed used to randomize options shown on the enchanting table for this human + - `public Human->setEnchantmentSeed(int $seed) : void` + - `public Human->regenerateEnchantmentSeed() : void` - returns a new randomly generated seed which can be set with `setEnchantmentSeed()` + +### `pocketmine\event` +- The following new classes have been added: + - `block\FarmlandHydrationChangeEvent` - called when farmland is hydrated or dehydrated + - `block\PressurePlateUpdateEvent` - called when a pressure plate is activated or changes its power output + - `player\PlayerEnchantingOptionsRequestEvent` - called when a player puts an item to be enchanted into an enchanting table, to allow plugins to modify the enchanting options shown + - `player\PlayerItemEnchantEvent` - called when a player enchants an item in an enchanting table + - `world\WorldDifficultyChangeEvent` - called when a world's difficulty is changed +- The following new API methods have been added: + - `public static Event::hasHandlers() : bool` - returns whether the event class has any registered handlers - used like `SomeEvent::hasHandlers()` + - `public HandlerListManager->getHandlersFor(class-string $event) : list` - returns a list of all registered listeners for the given event class, using cache if available + +### `pocketmine\inventory\transaction` +- The following new classes have been added: + - `EnchantingTransaction` - used when a player enchants an item in an enchanting table + +### `pocketmine\item` +- The following new API methods have been added: + - `public Armor->getMaterial() : ArmorMaterial` - returns an object containing properties shared by all items of the same armor material + - `public ArmorTypeInfo->getMaterial() : ArmorMaterial` + - `public Item->getEnchantability() : int` - returns the enchantability value of the item - higher values increase the chance of more powerful enchantments being offered by an enchanting table + - `public Item->getEnchantmentTags() : list` - returns a list of strings indicating which types of enchantment can be applied to the item + - `public ToolTier->getEnchantability() : int` +- The following API methods have signature changes: + - `Item->__construct()` now accepts an optional `list $enchantmentTags` parameter + - `ArmorTypeInfo->__construct()` now accepts an optional `?ArmorMaterial $material` parameter +- The following new classes have been added: + - `ArmorMaterial` - container for shared armor properties + - `VanillaArmorMaterials` - all vanilla armor materials + - `EnchantedBook` - represents an enchanted book item + +### `pocketmine\item\enchantment` +- The following new classes have been added: + - `AvailableEnchantmentRegistry` - enchantments to be displayed on the enchanting table are selected from here - custom enchantments may be added + - `EnchantingHelper` - static class containing various helper methods for enchanting tables + - `EnchantingOption` - represents an option on the enchanting table menu + - `IncompatibleEnchantmentGroups` - list of constants naming groups of enchantments that are incompatible with each other - custom enchantments may be added using these group names to make them incompatible with existing enchantments in the same group + - `IncompatibleEnchantmentRegistry` - manages which enchantments are considered incompatible with each other - custom enchantments may be added using existing group names to make them incompatible with existing enchantments in the same group, or to entirely new groups + - `ItemEnchantmentTagRegistry` - manages item enchantment compatibility tags and which tags include which other tags + - `ItemEnchantmentTags` - list of constants naming item types for enchantment compatibility checks +- The following classes have been deprecated + - `ItemFlags` +- The following API methods have been added: + - `public Enchantment->isCompatibleWith(Enchantment $other) : bool` + - `public Enchantment->getMinEnchantingPower()` - returns the minimum enchanting power (derived from enchantability and number of bookshelves) needed to allow this enchantment to show on the enchanting table with a given level + - `public Enchantment->getMaxEnchantingPower()` - upper limit of enchanting power for this enchantment to be offered on the enchanting table with a given level +- The following API methods have signature changes: + - `Enchantment->__construct()` now accepts optional `(\Closure(int $level) : int)|null $minEnchantingPower` and `int $enchantingPowerRange` parameters + - `Enchantment->__construct()` parameters `$primaryItemFlags` and `$secondaryItemFlags` are now deprecated and no longer used + - `ProtectionEnchantment->__construct()` has extra parameters to reflect `Enchantment->__construct()` changes +- The following API methods have been deprecated: + - `Enchantment->getPrimaryItemFlags()` - use API methods provided by `AvailableEnchantmentRegistry` instead + - `Enchantment->getSecondaryItemFlags()` - use API methods provided by `AvailableEnchantmentRegistry` instead + - `Enchantment->hasPrimaryItemType()` + - `Enchantment->hasSecondaryItemType()` + +### `pocketmine\plugin` +- The following new API methods have been added: + - `public PluginBase->getResourcePath(string $filename) : string` - returns a URI to an embedded resource file that can be used with `file_get_contents()` and similar functions + - `public PluginBase->getResourceFolder() : string` - returns a URI to the plugin's folder of embedded resources +- The following API methods have been deprecated: + - `PluginBase->getResource()` - prefer using `getResourcePath()` with `file_get_contents()` or other PHP built-in functions instead + +### `pocketmine\resourcepacks` +- The following new API methods have been added: + - `public ResourcePackManager->setResourcePacksRequired(bool $value) : void` - sets whether players must accept resource packs in order to join + +### `pocketmine\world\generator` +- The following new API methods have been added: + - `public GeneratorManager->addAlias(string $name, string $alias) : void` - allows registering a generator alias without copying the generator registration parameters + +### `pocketmine\world\sound` +- The following new classes have been added: +- `PressurePlateActivateSound` +- `PressurePlateDeactivateSound` + +### `pocketmine\utils` +- The following new API methods have been added: + - `public StringToTParser->registerAlias(string $existing, string $alias) : void` - allows registering a string alias without copying registration parameters diff --git a/composer.json b/composer.json index 75f68f89d..5f59f64d5 100644 --- a/composer.json +++ b/composer.json @@ -43,8 +43,8 @@ "pocketmine/errorhandler": "^0.6.0", "pocketmine/locale-data": "~2.19.0", "pocketmine/log": "^0.4.0", - "pocketmine/math": "^0.4.0", - "pocketmine/nbt": "^0.3.2", + "pocketmine/math": "~1.0.0", + "pocketmine/nbt": "~1.0.0", "pocketmine/raklib": "^0.15.0", "pocketmine/raklib-ipc": "^0.2.0", "pocketmine/snooze": "^0.5.0", diff --git a/composer.lock b/composer.lock index 52700d9fd..f412fa9d7 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": "27b30e0ed071ba0e6733545a695c3586", + "content-hash": "9237955fd97ba7c1697d80314fa9ad6f", "packages": [ { "name": "adhocore/json-comment", @@ -480,16 +480,16 @@ }, { "name": "pocketmine/math", - "version": "0.4.3", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/Math.git", - "reference": "47a243d320b01c8099d65309967934c188111549" + "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Math/zipball/47a243d320b01c8099d65309967934c188111549", - "reference": "47a243d320b01c8099d65309967934c188111549", + "url": "https://api.github.com/repos/pmmp/Math/zipball/dc132d93595b32e9f210d78b3c8d43c662a5edbf", + "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf", "shasum": "" }, "require": { @@ -498,7 +498,7 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "1.8.2", + "phpstan/phpstan": "~1.10.3", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^8.5 || ^9.5" }, @@ -515,22 +515,22 @@ "description": "PHP library containing math related code used in PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Math/issues", - "source": "https://github.com/pmmp/Math/tree/0.4.3" + "source": "https://github.com/pmmp/Math/tree/1.0.0" }, - "time": "2022-08-25T18:43:37+00:00" + "time": "2023-08-03T12:56:33+00:00" }, { "name": "pocketmine/nbt", - "version": "0.3.4", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/NBT.git", - "reference": "62c02464c6708b2467c1e1a2af01af09d5114eda" + "reference": "20540271cb59e04672cb163dca73366f207974f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/NBT/zipball/62c02464c6708b2467c1e1a2af01af09d5114eda", - "reference": "62c02464c6708b2467c1e1a2af01af09d5114eda", + "url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1", + "reference": "20540271cb59e04672cb163dca73366f207974f1", "shasum": "" }, "require": { @@ -540,7 +540,7 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "1.10.3", + "phpstan/phpstan": "1.10.25", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5" }, @@ -557,9 +557,9 @@ "description": "PHP library for working with Named Binary Tags", "support": { "issues": "https://github.com/pmmp/NBT/issues", - "source": "https://github.com/pmmp/NBT/tree/0.3.4" + "source": "https://github.com/pmmp/NBT/tree/1.0.0" }, - "time": "2023-04-10T11:31:20+00:00" + "time": "2023-07-14T13:01:49+00:00" }, { "name": "pocketmine/netresearch-jsonmapper", diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 0e8502ff8..053498e7e 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -30,6 +30,7 @@ use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\timings\Timings; use pocketmine\utils\Process; use pocketmine\utils\Utils; +use pocketmine\YmlServerProperties as Yml; use Symfony\Component\Filesystem\Path; use function arsort; use function count; @@ -109,7 +110,7 @@ class MemoryManager{ } private function init(ServerConfigGroup $config) : void{ - $this->memoryLimit = $config->getPropertyInt("memory.main-limit", 0) * 1024 * 1024; + $this->memoryLimit = $config->getPropertyInt(Yml::MEMORY_MAIN_LIMIT, 0) * 1024 * 1024; $defaultMemory = 1024; @@ -127,7 +128,7 @@ class MemoryManager{ } } - $hardLimit = $config->getPropertyInt("memory.main-hard-limit", $defaultMemory); + $hardLimit = $config->getPropertyInt(Yml::MEMORY_MAIN_HARD_LIMIT, $defaultMemory); if($hardLimit <= 0){ ini_set("memory_limit", '-1'); @@ -135,22 +136,22 @@ class MemoryManager{ ini_set("memory_limit", $hardLimit . "M"); } - $this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024; - $this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE); - $this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true); - $this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE); + $this->globalMemoryLimit = $config->getPropertyInt(Yml::MEMORY_GLOBAL_LIMIT, 0) * 1024 * 1024; + $this->checkRate = $config->getPropertyInt(Yml::MEMORY_CHECK_RATE, self::DEFAULT_CHECK_RATE); + $this->continuousTrigger = $config->getPropertyBool(Yml::MEMORY_CONTINUOUS_TRIGGER, true); + $this->continuousTriggerRate = $config->getPropertyInt(Yml::MEMORY_CONTINUOUS_TRIGGER_RATE, self::DEFAULT_CONTINUOUS_TRIGGER_RATE); - $this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEFAULT_TICKS_PER_GC); - $this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true); - $this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true); + $this->garbageCollectionPeriod = $config->getPropertyInt(Yml::MEMORY_GARBAGE_COLLECTION_PERIOD, self::DEFAULT_TICKS_PER_GC); + $this->garbageCollectionTrigger = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER, true); + $this->garbageCollectionAsync = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER, true); - $this->lowMemChunkRadiusOverride = $config->getPropertyInt("memory.max-chunks.chunk-radius", 4); - $this->lowMemChunkGC = $config->getPropertyBool("memory.max-chunks.trigger-chunk-collect", true); + $this->lowMemChunkRadiusOverride = $config->getPropertyInt(Yml::MEMORY_MAX_CHUNKS_CHUNK_RADIUS, 4); + $this->lowMemChunkGC = $config->getPropertyBool(Yml::MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT, true); - $this->lowMemDisableChunkCache = $config->getPropertyBool("memory.world-caches.disable-chunk-cache", true); - $this->lowMemClearWorldCache = $config->getPropertyBool("memory.world-caches.low-memory-trigger", true); + $this->lowMemDisableChunkCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE, true); + $this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true); - $this->dumpWorkers = $config->getPropertyBool("memory.memory-dump.dump-async-worker", true); + $this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true); gc_enable(); } diff --git a/src/Server.php b/src/Server.php index fb8a2bb6e..2f1e9a53b 100644 --- a/src/Server.php +++ b/src/Server.php @@ -119,6 +119,7 @@ use pocketmine\world\Position; use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; use pocketmine\world\WorldManager; +use pocketmine\YmlServerProperties as Yml; use Ramsey\Uuid\UuidInterface; use Symfony\Component\Filesystem\Path; use function array_fill; @@ -356,15 +357,15 @@ class Server{ } public function getPort() : int{ - return $this->configGroup->getConfigInt("server-port", self::DEFAULT_PORT_IPV4); + return $this->configGroup->getConfigInt(ServerProperties::SERVER_PORT_IPV4, self::DEFAULT_PORT_IPV4); } public function getPortV6() : int{ - return $this->configGroup->getConfigInt("server-portv6", self::DEFAULT_PORT_IPV6); + return $this->configGroup->getConfigInt(ServerProperties::SERVER_PORT_IPV6, self::DEFAULT_PORT_IPV6); } public function getViewDistance() : int{ - return max(2, $this->configGroup->getConfigInt("view-distance", self::DEFAULT_MAX_VIEW_DISTANCE)); + return max(2, $this->configGroup->getConfigInt(ServerProperties::VIEW_DISTANCE, self::DEFAULT_MAX_VIEW_DISTANCE)); } /** @@ -375,12 +376,12 @@ class Server{ } public function getIp() : string{ - $str = $this->configGroup->getConfigString("server-ip"); + $str = $this->configGroup->getConfigString(ServerProperties::SERVER_IPV4); return $str !== "" ? $str : "0.0.0.0"; } public function getIpV6() : string{ - $str = $this->configGroup->getConfigString("server-ipv6"); + $str = $this->configGroup->getConfigString(ServerProperties::SERVER_IPV6); return $str !== "" ? $str : "::"; } @@ -389,30 +390,30 @@ class Server{ } public function getGamemode() : GameMode{ - return GameMode::fromString($this->configGroup->getConfigString("gamemode", GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL(); + return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE, GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL(); } public function getForceGamemode() : bool{ - return $this->configGroup->getConfigBool("force-gamemode", false); + return $this->configGroup->getConfigBool(ServerProperties::FORCE_GAME_MODE, false); } /** * Returns Server global difficulty. Note that this may be overridden in individual worlds. */ public function getDifficulty() : int{ - return $this->configGroup->getConfigInt("difficulty", World::DIFFICULTY_NORMAL); + return $this->configGroup->getConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_NORMAL); } public function hasWhitelist() : bool{ - return $this->configGroup->getConfigBool("white-list", false); + return $this->configGroup->getConfigBool(ServerProperties::WHITELIST, false); } public function isHardcore() : bool{ - return $this->configGroup->getConfigBool("hardcore", false); + return $this->configGroup->getConfigBool(ServerProperties::HARDCORE, false); } public function getMotd() : string{ - return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME); + return $this->configGroup->getConfigString(ServerProperties::MOTD, self::DEFAULT_SERVER_NAME); } public function getLoader() : ThreadSafeClassLoader{ @@ -495,7 +496,7 @@ class Server{ } public function shouldSavePlayerData() : bool{ - return $this->configGroup->getPropertyBool("player.save-player-data", true); + return $this->configGroup->getPropertyBool(Yml::PLAYER_SAVE_PLAYER_DATA, true); } public function getOfflinePlayer(string $name) : Player|OfflinePlayer|null{ @@ -735,7 +736,7 @@ class Server{ * @return string[][] */ public function getCommandAliases() : array{ - $section = $this->configGroup->getProperty("aliases"); + $section = $this->configGroup->getProperty(YmlServerProperties::ALIASES); $result = []; if(is_array($section)){ foreach($section as $key => $value){ @@ -810,36 +811,36 @@ class Server{ $this->configGroup = new ServerConfigGroup( new Config($pocketmineYmlPath, Config::YAML, []), new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES, [ - "motd" => self::DEFAULT_SERVER_NAME, - "server-port" => self::DEFAULT_PORT_IPV4, - "server-portv6" => self::DEFAULT_PORT_IPV6, - "enable-ipv6" => true, - "white-list" => false, - "max-players" => self::DEFAULT_MAX_PLAYERS, - "gamemode" => GameMode::SURVIVAL()->name(), - "force-gamemode" => false, - "hardcore" => false, - "pvp" => true, - "difficulty" => World::DIFFICULTY_NORMAL, - "generator-settings" => "", - "level-name" => "world", - "level-seed" => "", - "level-type" => "DEFAULT", - "enable-query" => true, - "auto-save" => true, - "view-distance" => self::DEFAULT_MAX_VIEW_DISTANCE, - "xbox-auth" => true, - "language" => "eng" + ServerProperties::MOTD => self::DEFAULT_SERVER_NAME, + ServerProperties::SERVER_PORT_IPV4 => self::DEFAULT_PORT_IPV4, + ServerProperties::SERVER_PORT_IPV6 => self::DEFAULT_PORT_IPV6, + ServerProperties::ENABLE_IPV6 => true, + ServerProperties::WHITELIST => false, + ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS, + ServerProperties::GAME_MODE => GameMode::SURVIVAL()->name(), + ServerProperties::FORCE_GAME_MODE => false, + ServerProperties::HARDCORE => false, + ServerProperties::PVP => true, + ServerProperties::DIFFICULTY => World::DIFFICULTY_NORMAL, + ServerProperties::DEFAULT_WORLD_GENERATOR_SETTINGS => "", + ServerProperties::DEFAULT_WORLD_NAME => "world", + ServerProperties::DEFAULT_WORLD_SEED => "", + ServerProperties::DEFAULT_WORLD_GENERATOR => "DEFAULT", + ServerProperties::ENABLE_QUERY => true, + ServerProperties::AUTO_SAVE => true, + ServerProperties::VIEW_DISTANCE => self::DEFAULT_MAX_VIEW_DISTANCE, + ServerProperties::XBOX_AUTH => true, + ServerProperties::LANGUAGE => "eng" ]) ); - $debugLogLevel = $this->configGroup->getPropertyInt("debug.level", 1); + $debugLogLevel = $this->configGroup->getPropertyInt(Yml::DEBUG_LEVEL, 1); if($this->logger instanceof MainLogger){ $this->logger->setLogDebug($debugLogLevel > 1); } - $this->forceLanguage = $this->configGroup->getPropertyBool("settings.force-language", false); - $selectedLang = $this->configGroup->getConfigString("language", $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE)); + $this->forceLanguage = $this->configGroup->getPropertyBool(Yml::SETTINGS_FORCE_LANGUAGE, false); + $selectedLang = $this->configGroup->getConfigString(ServerProperties::LANGUAGE, $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE)); try{ $this->language = new Language($selectedLang); }catch(LanguageNotFoundException $e){ @@ -855,11 +856,11 @@ class Server{ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::language_selected($this->getLanguage()->getName(), $this->getLanguage()->getLang()))); if(VersionInfo::IS_DEVELOPMENT_BUILD){ - if(!$this->configGroup->getPropertyBool("settings.enable-dev-builds", false)){ + if(!$this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_DEV_BUILDS, false)){ $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error1(VersionInfo::NAME))); $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error2())); $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error3())); - $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4("settings.enable-dev-builds"))); + $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4(YmlServerProperties::SETTINGS_ENABLE_DEV_BUILDS))); $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error5("https://github.com/pmmp/PocketMine-MP/releases"))); $this->forceShutdownExit(); @@ -877,7 +878,7 @@ class Server{ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_start(TextFormat::AQUA . $this->getVersion() . TextFormat::RESET))); - if(($poolSize = $this->configGroup->getPropertyString("settings.async-workers", "auto")) === "auto"){ + if(($poolSize = $this->configGroup->getPropertyString(Yml::SETTINGS_ASYNC_WORKERS, "auto")) === "auto"){ $poolSize = 2; $processors = Utils::getCoreCount() - 2; @@ -888,32 +889,32 @@ class Server{ $poolSize = max(1, (int) $poolSize); } - $this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt("memory.async-worker-hard-limit", 256)), $this->autoloader, $this->logger, $this->tickSleeper); + $this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper); $netCompressionThreshold = -1; - if($this->configGroup->getPropertyInt("network.batch-threshold", 256) >= 0){ - $netCompressionThreshold = $this->configGroup->getPropertyInt("network.batch-threshold", 256); + if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){ + $netCompressionThreshold = $this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256); } if($netCompressionThreshold < 0){ $netCompressionThreshold = null; } - $netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6); + $netCompressionLevel = $this->configGroup->getPropertyInt(Yml::NETWORK_COMPRESSION_LEVEL, 6); if($netCompressionLevel < 1 || $netCompressionLevel > 9){ $this->logger->warning("Invalid network compression level $netCompressionLevel set, setting to default 6"); $netCompressionLevel = 6; } ZlibCompressor::setInstance(new ZlibCompressor($netCompressionLevel, $netCompressionThreshold, ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE)); - $this->networkCompressionAsync = $this->configGroup->getPropertyBool("network.async-compression", true); + $this->networkCompressionAsync = $this->configGroup->getPropertyBool(Yml::NETWORK_ASYNC_COMPRESSION, true); $this->networkCompressionAsyncThreshold = max( - $this->configGroup->getPropertyInt("network.async-compression-threshold", self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD), + $this->configGroup->getPropertyInt(Yml::NETWORK_ASYNC_COMPRESSION_THRESHOLD, self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD), $netCompressionThreshold ?? self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD ); - EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool("network.enable-encryption", true); + EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool(Yml::NETWORK_ENABLE_ENCRYPTION, true); - $this->doTitleTick = $this->configGroup->getPropertyBool("console.title-tick", true) && Terminal::hasFormattingCodes(); + $this->doTitleTick = $this->configGroup->getPropertyBool(Yml::CONSOLE_TITLE_TICK, true) && Terminal::hasFormattingCodes(); $this->operators = new Config(Path::join($this->dataPath, "ops.txt"), Config::ENUM); $this->whitelist = new Config(Path::join($this->dataPath, "white-list.txt"), Config::ENUM); @@ -931,9 +932,9 @@ class Server{ $this->banByIP = new BanList($bannedIpsTxt); $this->banByIP->load(); - $this->maxPlayers = $this->configGroup->getConfigInt("max-players", self::DEFAULT_MAX_PLAYERS); + $this->maxPlayers = $this->configGroup->getConfigInt(ServerProperties::MAX_PLAYERS, self::DEFAULT_MAX_PLAYERS); - $this->onlineMode = $this->configGroup->getConfigBool("xbox-auth", true); + $this->onlineMode = $this->configGroup->getConfigBool(ServerProperties::XBOX_AUTH, true); if($this->onlineMode){ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_auth_enabled())); }else{ @@ -942,8 +943,8 @@ class Server{ $this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled())); } - if($this->configGroup->getConfigBool("hardcore", false) && $this->getDifficulty() < World::DIFFICULTY_HARD){ - $this->configGroup->setConfigInt("difficulty", World::DIFFICULTY_HARD); + if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){ + $this->configGroup->setConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_HARD); } @cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion()); @@ -962,8 +963,8 @@ class Server{ ))); $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName()))); - TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false)); - $this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND); + 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(); @@ -985,13 +986,13 @@ class Server{ $this->forceShutdownExit(); return; } - $this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool("plugins.legacy-data-dir", true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist); + $this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist); $this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader)); $this->pluginManager->registerInterface(new ScriptPluginLoader()); $providerManager = new WorldProviderManager(); if( - ($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null && + ($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString(Yml::LEVEL_SETTINGS_DEFAULT_FORMAT, ""))) !== null && $format instanceof WritableWorldProviderManagerEntry ){ $providerManager->setDefault($format); @@ -1000,10 +1001,10 @@ class Server{ } $this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager); - $this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave())); - $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval())); + $this->worldManager->setAutoSave($this->configGroup->getConfigBool(ServerProperties::AUTO_SAVE, $this->worldManager->getAutoSave())); + $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt(Yml::TICKS_PER_AUTOSAVE, $this->worldManager->getAutoSaveInterval())); - $this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io")); + $this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString(Yml::AUTO_UPDATER_HOST, "update.pmmp.io")); $this->queryInfo = new QueryInfo($this); @@ -1040,7 +1041,7 @@ class Server{ return; } - if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){ + if($this->configGroup->getPropertyBool(Yml::ANONYMOUS_STATISTICS_ENABLED, true)){ $this->sendUsageTicker = self::TICKS_PER_STATS_REPORT; $this->sendUsage(SendUsageTask::TYPE_OPEN); } @@ -1056,7 +1057,7 @@ class Server{ $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); //TODO: move console parts to a separate component - if($this->configGroup->getPropertyBool("console.enable-input", true)){ + if($this->configGroup->getPropertyBool(Yml::CONSOLE_ENABLE_INPUT, true)){ $this->console = new ConsoleReaderChildProcessDaemon($this->logger); } @@ -1091,7 +1092,7 @@ class Server{ $anyWorldFailedToLoad = false; - foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){ + foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){ if($options === null){ $options = []; }elseif(!is_array($options)){ @@ -1135,11 +1136,11 @@ class Server{ } if($this->worldManager->getDefaultWorld() === null){ - $default = $this->configGroup->getConfigString("level-name", "world"); + $default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); if(trim($default) == ""){ $this->getLogger()->warning("level-name cannot be null, using default"); $default = "world"; - $this->configGroup->setConfigString("level-name", "world"); + $this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); } if(!$this->worldManager->loadWorld($default, true)){ if($this->worldManager->isWorldGenerated($default)){ @@ -1147,8 +1148,8 @@ class Server{ return false; } - $generatorName = $this->configGroup->getConfigString("level-type"); - $generatorOptions = $this->configGroup->getConfigString("generator-settings"); + $generatorName = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_GENERATOR); + $generatorOptions = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_GENERATOR_SETTINGS); $generatorClass = $getGenerator($generatorName, $generatorOptions, $default); if($generatorClass === null){ @@ -1158,7 +1159,7 @@ class Server{ $creationOptions = WorldCreationOptions::create() ->setGeneratorClass($generatorClass) ->setGeneratorOptions($generatorOptions); - $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed")); + $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_SEED)); if($convertedSeed !== null){ $creationOptions->setSeed($convertedSeed); } @@ -1212,7 +1213,7 @@ class Server{ } private function startupPrepareNetworkInterfaces() : bool{ - $useQuery = $this->configGroup->getConfigBool("enable-query", true); + $useQuery = $this->configGroup->getConfigBool(ServerProperties::ENABLE_QUERY, true); $typeConverter = TypeConverter::getInstance(); $packetSerializerContext = new PacketSerializerContext($typeConverter->getItemTypeDictionary()); @@ -1222,7 +1223,7 @@ class Server{ if( !$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) || ( - $this->configGroup->getConfigBool("enable-ipv6", true) && + $this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) && !$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) ) ){ @@ -1237,7 +1238,7 @@ class Server{ $this->network->blockAddress($entry->getName(), -1); } - if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){ + if($this->configGroup->getPropertyBool(Yml::NETWORK_UPNP_FORWARDING, false)){ $this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort())); } @@ -1457,7 +1458,7 @@ class Server{ } if(isset($this->network)){ - $this->network->getSessionManager()->close($this->configGroup->getPropertyString("settings.shutdown-message", "Server closed")); + $this->network->getSessionManager()->close($this->configGroup->getPropertyString(YmlServerProperties::SETTINGS_SHUTDOWN_MESSAGE, "Server closed")); } if(isset($this->worldManager)){ @@ -1594,7 +1595,7 @@ class Server{ $this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath))); - if($this->configGroup->getPropertyBool("auto-report.enabled", true)){ + if($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_ENABLED, true)){ $report = true; $stamp = Path::join($this->getDataPath(), "crashdumps", ".last_crash"); @@ -1615,7 +1616,7 @@ class Server{ } if($report){ - $url = ($this->configGroup->getPropertyBool("auto-report.use-https", true) ? "https" : "http") . "://" . $this->configGroup->getPropertyString("auto-report.host", "crash.pmmp.io") . "/submit/api"; + $url = ($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_USE_HTTPS, true) ? "https" : "http") . "://" . $this->configGroup->getPropertyString(Yml::AUTO_REPORT_HOST, "crash.pmmp.io") . "/submit/api"; $postUrlError = "Unknown error"; $reply = Internet::postURL($url, [ "report" => "yes", @@ -1726,7 +1727,7 @@ class Server{ } public function sendUsage(int $type = SendUsageTask::TYPE_STATUS) : void{ - if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){ + if($this->configGroup->getPropertyBool(Yml::ANONYMOUS_STATISTICS_ENABLED, true)){ $this->asyncPool->submitTask(new SendUsageTask($this, $type, $this->uniquePlayers)); } $this->uniquePlayers = []; diff --git a/src/ServerProperties.php b/src/ServerProperties.php new file mode 100644 index 000000000..aad26d242 --- /dev/null +++ b/src/ServerProperties.php @@ -0,0 +1,54 @@ +dead && !$this->isCoveredWithWater()){ - $ev = new BlockDeathEvent($this, (clone $this)->setDead(true)); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::die($this, (clone $this)->setDead(true)); } } diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 7f65b59f4..d7a22f3b8 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -49,6 +49,8 @@ abstract class BaseSign extends Transparent{ use WoodTypeTrait; protected SignText $text; + private bool $waxed = false; + protected ?int $editorEntityRuntimeId = null; /** @var \Closure() : Item */ @@ -69,6 +71,7 @@ abstract class BaseSign extends Transparent{ $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileSign){ $this->text = $tile->getText(); + $this->waxed = $tile->isWaxed(); $this->editorEntityRuntimeId = $tile->getEditorEntityRuntimeId(); } @@ -80,6 +83,7 @@ abstract class BaseSign extends Transparent{ $tile = $this->position->getWorld()->getTile($this->position); assert($tile instanceof TileSign); $tile->setText($this->text); + $tile->setWaxed($this->waxed); $tile->setEditorEntityRuntimeId($this->editorEntityRuntimeId); } @@ -147,10 +151,26 @@ abstract class BaseSign extends Transparent{ return false; } + private function wax(Player $player, Item $item) : bool{ + if($this->waxed){ + return false; + } + + $this->waxed = true; + $this->position->getWorld()->setBlock($this->position, $this); + $item->pop(); + + return true; + } + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player === null){ return false; } + if($this->waxed){ + return true; + } + $dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){ ItemTypeIds::BONE_MEAL => DyeColor::WHITE(), ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(), @@ -159,20 +179,25 @@ abstract class BaseSign extends Transparent{ }; if($dyeColor !== null){ $color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue(); - if($color->toARGB() === $this->text->getBaseColor()->toARGB()){ - return false; - } - - if($this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)){ + if( + $color->toARGB() !== $this->text->getBaseColor()->toARGB() && + $this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item) + ){ $this->position->getWorld()->addSound($this->position, new DyeUseSound()); return true; } - }elseif($item->getTypeId() === ItemTypeIds::INK_SAC){ - return $this->changeSignGlowingState(false, $player, $item); - }elseif($item->getTypeId() === ItemTypeIds::GLOW_INK_SAC){ - return $this->changeSignGlowingState(true, $player, $item); + }elseif(match($item->getTypeId()){ + ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item), + ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item), + ItemTypeIds::HONEYCOMB => $this->wax($player, $item), + default => false + }){ + return true; } - return false; + + $player->openSignEditor($this->position); + + return true; } /** @@ -188,6 +213,17 @@ abstract class BaseSign extends Transparent{ return $this; } + /** + * Returns whether the sign has been waxed using a honeycomb. If true, the sign cannot be edited by a player. + */ + public function isWaxed() : bool{ return $this->waxed; } + + /** @return $this */ + public function setWaxed(bool $waxed) : self{ + $this->waxed = $waxed; + return $this; + } + /** * Sets the runtime entity ID of the player editing this sign. Only this player will be able to edit the sign. * This is used to prevent multiple players from editing the same sign at the same time, and to prevent players @@ -217,8 +253,8 @@ abstract class BaseSign extends Transparent{ } $ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) : string{ return TextFormat::clean($line, false); - }, $text->getLines()))); - if($this->editorEntityRuntimeId === null || $this->editorEntityRuntimeId !== $author->getId()){ + }, $text->getLines()), $this->text->getBaseColor(), $this->text->isGlowing())); + if($this->waxed || $this->editorEntityRuntimeId !== $author->getId()){ $ev->cancel(); } $ev->call(); diff --git a/src/block/Block.php b/src/block/Block.php index 0e045792f..9f61982ff 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -36,6 +36,9 @@ use pocketmine\data\runtime\RuntimeDataSizeCalculator; use pocketmine\data\runtime\RuntimeDataWriter; use pocketmine\entity\Entity; use pocketmine\entity\projectile\Projectile; +use pocketmine\item\enchantment\AvailableEnchantmentRegistry; +use pocketmine\item\enchantment\ItemEnchantmentTagRegistry; +use pocketmine\item\enchantment\ItemEnchantmentTags; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\ItemBlock; @@ -422,6 +425,19 @@ class Block{ return $this->typeInfo->getBreakInfo(); } + /** + * Returns tags that represent the type of item being enchanted and are used to determine + * what enchantments can be applied to the item of this block during in-game enchanting (enchanting table, anvil, fishing, etc.). + * @see ItemEnchantmentTags + * @see ItemEnchantmentTagRegistry + * @see AvailableEnchantmentRegistry + * + * @return string[] + */ + public function getEnchantmentTags() : array{ + return $this->typeInfo->getEnchantmentTags(); + } + /** * Do the actions needed so the block is broken with the Item * diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index fe2101e1f..dc86490c3 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -736,8 +736,9 @@ final class BlockTypeIds{ public const SMALL_DRIPLEAF = 10706; public const BIG_DRIPLEAF_HEAD = 10707; public const BIG_DRIPLEAF_STEM = 10708; + public const PINK_PETALS = 10709; - public const FIRST_UNUSED_BLOCK_ID = 10709; + public const FIRST_UNUSED_BLOCK_ID = 10710; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/BlockTypeInfo.php b/src/block/BlockTypeInfo.php index eb6b89ad1..a1424ef3c 100644 --- a/src/block/BlockTypeInfo.php +++ b/src/block/BlockTypeInfo.php @@ -35,10 +35,12 @@ final class BlockTypeInfo{ /** * @param string[] $typeTags + * @param string[] $enchantmentTags */ public function __construct( private BlockBreakInfo $breakInfo, - array $typeTags = [] + array $typeTags = [], + private array $enchantmentTags = [] ){ $this->typeTags = array_fill_keys($typeTags, true); } @@ -49,4 +51,17 @@ final class BlockTypeInfo{ public function getTypeTags() : array{ return array_keys($this->typeTags); } public function hasTypeTag(string $tag) : bool{ return isset($this->typeTags[$tag]); } + + /** + * Returns tags that represent the type of item being enchanted and are used to determine + * what enchantments can be applied to the item of this block during in-game enchanting (enchanting table, anvil, fishing, etc.). + * @see ItemEnchantmentTags + * @see ItemEnchantmentTagRegistry + * @see AvailableEnchantmentRegistry + * + * @return string[] + */ + public function getEnchantmentTags() : array{ + return $this->enchantmentTags; + } } diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 8fff294f6..0176d17bd 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\item\Item; @@ -111,12 +111,7 @@ class Cactus extends Transparent{ } $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); if($b->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($b, VanillaBlocks::CACTUS()); - $ev->call(); - if($ev->isCancelled()){ - break; - } - $world->setBlock($b->position, $ev->getNewState()); + BlockEventHelper::grow($b, VanillaBlocks::CACTUS(), null); }else{ break; } diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php index e56b8b720..6ff934881 100644 --- a/src/block/CaveVines.php +++ b/src/block/CaveVines.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -114,16 +114,12 @@ class CaveVines extends Flowable{ return true; } if($item instanceof Fertilizer){ - $ev = new BlockGrowEvent($this, (clone $this) + $newState = (clone $this) ->setBerries(true) - ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)) - ); - $ev->call(); - if($ev->isCancelled()){ - return false; + ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)); + if(BlockEventHelper::grow($this, $newState, $player)){ + $item->pop(); } - $item->pop(); - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); return true; } return false; @@ -141,16 +137,10 @@ class CaveVines extends Flowable{ if($world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ())){ $block = $world->getBlock($growthPos); if($block->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($block, VanillaBlocks::CAVE_VINES() + $newState = VanillaBlocks::CAVE_VINES() ->setAge($this->age + 1) - ->setBerries(mt_rand(1, 9) === 1) - ); - - $ev->call(); - - if(!$ev->isCancelled()){ - $world->setBlock($growthPos, $ev->getNewState()); - } + ->setBerries(mt_rand(1, 9) === 1); + BlockEventHelper::grow($block, $newState, null); } } } diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index aafce3169..69f94eaf6 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -123,12 +123,7 @@ class CocoaBlock extends Transparent{ if($this->age < self::MAX_AGE){ $block = clone $this; $block->age++; - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - return true; - } + return BlockEventHelper::grow($this, $block, $player); } return false; } diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php index 02fa31adb..635d3ff2c 100644 --- a/src/block/ConcretePowder.php +++ b/src/block/ConcretePowder.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; -use pocketmine\event\block\BlockFormEvent; use pocketmine\math\Facing; class ConcretePowder extends Opaque implements Fallable{ @@ -43,11 +43,7 @@ class ConcretePowder extends Opaque implements Fallable{ public function onNearbyBlockChange() : void{ if(($water = $this->getAdjacentWater()) !== null){ - $ev = new BlockFormEvent($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); }else{ $this->startFalling(); } diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php index c41c90182..5ce58f413 100644 --- a/src/block/CoralBlock.php +++ b/src/block/CoralBlock.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; -use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; use function mt_rand; @@ -55,11 +55,7 @@ final class CoralBlock extends Opaque{ } } if(!$hasWater){ - $ev = new BlockDeathEvent($this, (clone $this)->setDead(true)); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::die($this, (clone $this)->setDead(true)); } } } diff --git a/src/block/Crops.php b/src/block/Crops.php index 8949e663b..d6e84c424 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -68,11 +68,7 @@ abstract class Crops extends Flowable{ if($block->age > self::MAX_AGE){ $block->age = self::MAX_AGE; } - - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + if(BlockEventHelper::grow($this, $block, $player)){ $item->pop(); } @@ -96,11 +92,7 @@ abstract class Crops extends Flowable{ if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } } diff --git a/src/block/Farmland.php b/src/block/Farmland.php index 6fcdcd8bc..2dd795eec 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\entity\Living; +use pocketmine\event\block\FarmlandHydrationChangeEvent; use pocketmine\event\entity\EntityTrampleFarmlandEvent; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -73,14 +74,22 @@ class Farmland extends Transparent{ $world = $this->position->getWorld(); if(!$this->canHydrate()){ if($this->wetness > 0){ - $this->wetness--; - $world->setBlock($this->position, $this, false); + $event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1); + $event->call(); + if(!$event->isCancelled()){ + $this->wetness = $event->getNewHydration(); + $world->setBlock($this->position, $this, false); + } }else{ $world->setBlock($this->position, VanillaBlocks::DIRT()); } }elseif($this->wetness < self::MAX_WETNESS){ - $this->wetness = self::MAX_WETNESS; - $world->setBlock($this->position, $this, false); + $event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS); + $event->call(); + if(!$event->isCancelled()){ + $this->wetness = $event->getNewHydration(); + $world->setBlock($this->position, $this, false); + } } } diff --git a/src/block/Fire.php b/src/block/Fire.php index 0a3b2e3e3..11378b82b 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\event\block\BlockBurnEvent; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\math\Facing; use pocketmine\world\format\Chunk; use pocketmine\world\World; @@ -145,9 +145,13 @@ class Fire extends BaseFire{ private function burnBlock(Block $block, int $chanceBound) : void{ if(mt_rand(0, $chanceBound) < $block->getFlammability()){ - $ev = new BlockBurnEvent($block, $this); - $ev->call(); - if(!$ev->isCancelled()){ + $cancelled = false; + if(BlockBurnEvent::hasHandlers()){ + $ev = new BlockBurnEvent($block, $this); + $ev->call(); + $cancelled = $ev->isCancelled(); + } + if(!$cancelled){ $block->onIncinerate(); $world = $this->position->getWorld(); @@ -225,13 +229,6 @@ class Fire extends BaseFire{ } private function spreadBlock(Block $block, Block $newState) : bool{ - $ev = new BlockSpreadEvent($block, $this, $newState); - $ev->call(); - if(!$ev->isCancelled()){ - $block->position->getWorld()->setBlock($block->position, $ev->getNewState()); - return true; - } - - return false; + return BlockEventHelper::spread($block, $newState, $this); } } diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php index 38c552a95..039fe49f3 100644 --- a/src/block/FrostedIce.php +++ b/src/block/FrostedIce.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockMeltEvent; use function mt_rand; class FrostedIce extends Ice{ @@ -97,11 +97,7 @@ class FrostedIce extends Ice{ private function tryMelt() : bool{ $world = $this->position->getWorld(); if($this->age >= self::MAX_AGE){ - $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::WATER()); return true; } diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index 39ce512a6..84dec29ce 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -166,14 +166,7 @@ class GlowLichen extends Transparent{ return false; } - $ev = new BlockSpreadEvent($replacedBlock, $this, $replacementBlock); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($replacedBlock->getPosition(), $ev->getNewState()); - return true; - } - - return false; + return BlockEventHelper::spread($replacedBlock, $replacementBlock, $this); } /** diff --git a/src/block/Grass.php b/src/block/Grass.php index 54975d046..709dc6a9d 100644 --- a/src/block/Grass.php +++ b/src/block/Grass.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\DirtType; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Hoe; use pocketmine\item\Item; @@ -58,11 +58,7 @@ class Grass extends Opaque{ $lightAbove = $world->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z); if($lightAbove < 4 && $world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){ //grass dies - $ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState(), false); - } + BlockEventHelper::spread($this, VanillaBlocks::DIRT(), $this); }elseif($lightAbove >= 9){ //try grass spread for($i = 0; $i < 4; ++$i){ @@ -80,11 +76,7 @@ class Grass extends Opaque{ continue; } - $ev = new BlockSpreadEvent($b, $this, VanillaBlocks::GRASS()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($b->position, $ev->getNewState(), false); - } + BlockEventHelper::spread($b, VanillaBlocks::GRASS(), $this); } } } diff --git a/src/block/Ice.php b/src/block/Ice.php index 41f60e3f9..ca0302f90 100644 --- a/src/block/Ice.php +++ b/src/block/Ice.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\event\block\BlockMeltEvent; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\player\Player; @@ -53,11 +53,7 @@ class Ice extends Transparent{ public function onRandomTick() : void{ $world = $this->position->getWorld(); if($world->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){ - $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::WATER()); } } diff --git a/src/block/Leaves.php b/src/block/Leaves.php index 235190454..b1839dca6 100644 --- a/src/block/Leaves.php +++ b/src/block/Leaves.php @@ -116,10 +116,15 @@ class Leaves extends Transparent{ public function onRandomTick() : void{ if(!$this->noDecay && $this->checkDecay){ - $ev = new LeavesDecayEvent($this); - $ev->call(); + $cancelled = false; + if(LeavesDecayEvent::hasHandlers()){ + $ev = new LeavesDecayEvent($this); + $ev->call(); + $cancelled = $ev->isCancelled(); + } + $world = $this->position->getWorld(); - if($ev->isCancelled() || $this->findLog($this->position)){ + if($cancelled || $this->findLog($this->position)){ $this->checkDecay = false; $world->setBlock($this->position, $this, false); }else{ diff --git a/src/block/Liquid.php b/src/block/Liquid.php index a56f666a2..e102540ec 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MinimumCostFlowCalculator; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockFormEvent; use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -363,12 +363,8 @@ abstract class Liquid extends Transparent{ } protected function liquidCollide(Block $cause, Block $result) : bool{ - $ev = new BlockFormEvent($this, $result, $cause); - $ev->call(); - if(!$ev->isCancelled()){ - $world = $this->position->getWorld(); - $world->setBlock($this->position, $ev->getNewState()); - $world->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); + 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)); } return true; } diff --git a/src/block/MobHead.php b/src/block/MobHead.php index 1b3f2d88f..96cd1cf34 100644 --- a/src/block/MobHead.php +++ b/src/block/MobHead.php @@ -113,14 +113,15 @@ class MobHead extends Flowable{ * @return AxisAlignedBB[] */ protected function recalculateCollisionBoxes() : array{ - $collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5); - return match($this->facing){ - Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)], - Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)], - Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)], - Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)], - default => [$collisionBox] - }; + $collisionBox = AxisAlignedBB::one() + ->contract(0.25, 0, 0.25) + ->trim(Facing::UP, 0.5); + if($this->facing !== Facing::UP){ + $collisionBox = $collisionBox + ->offsetTowards(Facing::opposite($this->facing), 0.25) + ->offsetTowards(Facing::UP, 0.25); + } + return [$collisionBox]; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ diff --git a/src/block/Mycelium.php b/src/block/Mycelium.php index c87f89367..08b0b3e75 100644 --- a/src/block/Mycelium.php +++ b/src/block/Mycelium.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\DirtType; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use function mt_rand; @@ -54,11 +54,7 @@ class Mycelium extends Opaque{ $block = $world->getBlockAt($x, $y, $z); if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){ if($block->getSide(Facing::UP) instanceof Transparent){ - $ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($block->position, $ev->getNewState()); - } + BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this); } } } diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php index 6a8fe1f7a..d7e587441 100644 --- a/src/block/NetherWartPlant.php +++ b/src/block/NetherWartPlant.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -76,11 +76,7 @@ class NetherWartPlant extends Flowable{ if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing $block = clone $this; $block->age++; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php new file mode 100644 index 000000000..9271e7ddc --- /dev/null +++ b/src/block/PinkPetals.php @@ -0,0 +1,119 @@ +horizontalFacing($this->facing); + $w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count); + } + + public function getCount() : int{ + return $this->count; + } + + /** @return $this */ + public function setCount(int $count) : self{ + if($count < self::MIN_COUNT || $count > self::MAX_COUNT){ + throw new \InvalidArgumentException("Count must be in range " . self::MIN_COUNT . " ... " . self::MAX_COUNT); + } + $this->count = $count; + return $this; + } + + private function canBeSupportedAt(Block $block) : bool{ + $supportBlock = $block->getSide(Facing::DOWN); + //TODO: Moss block + return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD); + } + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedAt($this)){ + $this->position->getWorld()->useBreakOn($this->position); + } + } + + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(!$this->canBeSupportedAt($this)){ + return false; + } + if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){ + $this->count = $blockReplace->getCount() + 1; + $this->facing = $blockReplace->getFacing(); + }elseif($player !== null){ + $this->facing = Facing::opposite($player->getHorizontalFacing()); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($item instanceof Fertilizer){ + $grew = false; + if($this->count < self::MAX_COUNT){ + $grew = BlockEventHelper::grow($this, (clone $this)->setCount($this->count + 1), $player); + }else{ + $this->position->getWorld()->dropItem($this->position->add(0, 0.5, 0), $this->asItem()); + $grew = true; + } + if($grew){ + $item->pop(); + return true; + } + } + return false; + } + + public function getFlameEncouragement() : int{ + return 60; + } + + public function getFlammability() : int{ + return 100; + } + + public function getDropsForCompatibleTool(Item $item) : array{ + return [$this->asItem()->setCount($this->count)]; + } +} diff --git a/src/block/PressurePlate.php b/src/block/PressurePlate.php index 4df0bf927..d67433a75 100644 --- a/src/block/PressurePlate.php +++ b/src/block/PressurePlate.php @@ -24,14 +24,33 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\SupportType; +use pocketmine\entity\Entity; +use pocketmine\event\block\PressurePlateUpdateEvent; use pocketmine\item\Item; +use pocketmine\math\Axis; +use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; +use pocketmine\world\sound\PressurePlateActivateSound; +use pocketmine\world\sound\PressurePlateDeactivateSound; +use function count; abstract class PressurePlate extends Transparent{ + private readonly int $deactivationDelayTicks; + + public function __construct( + BlockIdentifier $idInfo, + string $name, + BlockTypeInfo $typeInfo, + int $deactivationDelayTicks = 20 //TODO: make this mandatory in PM6 + ){ + parent::__construct($idInfo, $name, $typeInfo); + $this->deactivationDelayTicks = $deactivationDelayTicks; + } + public function isSolid() : bool{ return false; } @@ -61,5 +80,89 @@ abstract class PressurePlate extends Transparent{ } } - //TODO + public function hasEntityCollision() : bool{ + return true; + } + + public function onEntityInside(Entity $entity) : bool{ + if(!$this->hasOutputSignal()){ + $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 0); + } + return true; + } + + /** + * Returns the AABB that entities must intersect to activate the pressure plate. + * Note that this is not the same as the collision box (pressure plate doesn't have one), nor the visual bounding + * box. The activation area has a height of 0.25 blocks. + */ + protected function getActivationBox() : AxisAlignedBB{ + return AxisAlignedBB::one() + ->squash(Axis::X, 1 / 8) + ->squash(Axis::Z, 1 / 8) + ->trim(Facing::UP, 3 / 4) + ->offset($this->position->x, $this->position->y, $this->position->z); + } + + /** + * TODO: make this abstract in PM6 + */ + protected function hasOutputSignal() : bool{ + return false; + } + + /** + * TODO: make this abstract in PM6 + * + * @param Entity[] $entities + * + * @return mixed[] + * @phpstan-return array{Block, ?bool} + */ + protected function calculatePlateState(array $entities) : array{ + return [$this, null]; + } + + /** + * Filters entities which don't affect the pressure plate state from the given list. + * + * @param Entity[] $entities + * @return Entity[] + */ + protected function filterIrrelevantEntities(array $entities) : array{ + return $entities; + } + + public function onScheduledUpdate() : void{ + $world = $this->position->getWorld(); + + $intersectionAABB = $this->getActivationBox(); + $activatingEntities = $this->filterIrrelevantEntities($world->getNearbyEntities($intersectionAABB)); + + //if an irrelevant entity is inside the full cube space of the pressure plate but not activating the plate, + //it will cause scheduled updates on the plate every tick. We don't want to fire events in this case if the + //plate is already deactivated. + if(count($activatingEntities) > 0 || $this->hasOutputSignal()){ + [$newState, $pressedChange] = $this->calculatePlateState($activatingEntities); + + //always call this, in case there are new entities on the plate + if(PressurePlateUpdateEvent::hasHandlers()){ + $ev = new PressurePlateUpdateEvent($this, $newState, $activatingEntities); + $ev->call(); + $newState = $ev->isCancelled() ? null : $ev->getNewState(); + } + if($newState !== null){ + $world->setBlock($this->position, $newState); + if($pressedChange !== null){ + $world->addSound($this->position, $pressedChange ? + new PressurePlateActivateSound($this) : + new PressurePlateDeactivateSound($this) + ); + } + } + if($pressedChange ?? $this->hasOutputSignal()){ + $world->scheduleDelayedBlockUpdate($this->position, $this->deactivationDelayTicks); + } + } + } } diff --git a/src/block/SimplePressurePlate.php b/src/block/SimplePressurePlate.php index e4278410d..3429b9b5d 100644 --- a/src/block/SimplePressurePlate.php +++ b/src/block/SimplePressurePlate.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\data\runtime\RuntimeDataDescriber; +use function count; abstract class SimplePressurePlate extends PressurePlate{ protected bool $pressed = false; @@ -39,4 +40,19 @@ abstract class SimplePressurePlate extends PressurePlate{ $this->pressed = $pressed; return $this; } + + protected function hasOutputSignal() : bool{ + return $this->pressed; + } + + protected function calculatePlateState(array $entities) : array{ + $newPressed = count($entities) > 0; + if($newPressed === $this->pressed){ + return [$this, null]; + } + return [ + (clone $this)->setPressed($newPressed), + $newPressed + ]; + } } diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php index f561c8ff5..05fc88421 100644 --- a/src/block/SnowLayer.php +++ b/src/block/SnowLayer.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockMeltEvent; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; @@ -105,11 +105,7 @@ class SnowLayer extends Flowable implements Fallable{ public function onRandomTick() : void{ $world = $this->position->getWorld(); if($world->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){ - $ev = new BlockMeltEvent($this, VanillaBlocks::AIR()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::AIR()); } } diff --git a/src/block/Stem.php b/src/block/Stem.php index 252b28aeb..7223572dd 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use function array_rand; @@ -64,11 +64,7 @@ abstract class Stem extends Crops{ if($this->age < self::MAX_AGE){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); }else{ $grow = $this->getPlant(); foreach(Facing::HORIZONTAL as $side){ @@ -80,12 +76,7 @@ abstract class Stem extends Crops{ $facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]; $side = $this->getSide($facing); if($side->getTypeId() === BlockTypeIds::AIR && $side->getSide(Facing::DOWN)->hasTypeTag(BlockTypeTags::DIRT)){ - $ev = new BlockGrowEvent($side, $grow); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $this->setFacing($facing)); - $world->setBlock($side->position, $ev->getNewState()); - } + BlockEventHelper::grow($side, $grow, null); } } } diff --git a/src/block/StonePressurePlate.php b/src/block/StonePressurePlate.php index 626e6d885..5ddc5a599 100644 --- a/src/block/StonePressurePlate.php +++ b/src/block/StonePressurePlate.php @@ -23,6 +23,13 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\entity\Entity; +use pocketmine\entity\Living; +use function array_filter; + class StonePressurePlate extends SimplePressurePlate{ + protected function filterIrrelevantEntities(array $entities) : array{ + return array_filter($entities, fn(Entity $e) => $e instanceof Living); //TODO: armor stands should activate stone plates too + } } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 4cc5989a7..3757e4577 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -60,13 +60,11 @@ class Sugarcane extends Flowable{ } $b = $world->getBlockAt($pos->x, $pos->y + $y, $pos->z); if($b->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($b, VanillaBlocks::SUGARCANE(), $player); - $ev->call(); - if($ev->isCancelled()){ + if(BlockEventHelper::grow($b, VanillaBlocks::SUGARCANE(), $player)){ + $grew = true; + }else{ break; } - $world->setBlock($b->position, $ev->getNewState()); - $grew = true; }elseif(!$b->hasSameTypeId($this)){ break; } diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index 5ead39390..ef1169df5 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\entity\Living; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; @@ -87,15 +87,9 @@ class SweetBerryBush extends Flowable{ if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ $block = clone $this; $block->age++; - - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); + if(BlockEventHelper::grow($this, $block, $player)){ $item->pop(); } - }elseif(($dropAmount = $this->getBerryDropAmount()) > 0){ $world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); $world->dropItem($this->position, $this->asItem()->setCount($dropAmount)); @@ -133,11 +127,7 @@ class SweetBerryBush extends Flowable{ if($this->age < self::STAGE_MATURE && mt_rand(0, 2) === 1){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 9f2996abd..a41c3985b 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -59,6 +59,7 @@ use pocketmine\block\utils\SaplingType; use pocketmine\block\utils\WoodType; use pocketmine\crafting\FurnaceType; use pocketmine\entity\projectile\Projectile; +use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\Item; use pocketmine\item\ToolTier; use pocketmine\math\Facing; @@ -573,6 +574,7 @@ use function mb_strtolower; * @method static PackedIce PACKED_ICE() * @method static Opaque PACKED_MUD() * @method static DoublePlant PEONY() + * @method static PinkPetals PINK_PETALS() * @method static Flower PINK_TULIP() * @method static Podzol PODZOL() * @method static Opaque POLISHED_ANDESITE() @@ -840,6 +842,7 @@ final class VanillaBlocks{ 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("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD())))); @@ -964,7 +967,7 @@ final class VanillaBlocks{ $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", $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_stem", new PumpkinStem(new BID(Ids::PUMPKIN_STEM), "Pumpkin Stem", new Info(BreakInfo::instant()))); @@ -1000,7 +1003,7 @@ final class VanillaBlocks{ 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)))); + 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())))); @@ -1111,8 +1114,20 @@ final class VanillaBlocks{ self::register("lily_pad", new WaterLily(new BID(Ids::LILY_PAD), "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), "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo)); - self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight(new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT), "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo)); + self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy( + new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY), + "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), + "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()))); $leavesBreakInfo = new Info(new class(0.2, ToolType::HOE) extends BreakInfo{ @@ -1263,7 +1278,7 @@ final class VanillaBlocks{ 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)); + 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); @@ -1488,7 +1503,7 @@ final class VanillaBlocks{ $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())))); + 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)); diff --git a/src/block/WeightedPressurePlate.php b/src/block/WeightedPressurePlate.php index bdfae5082..726b31f6b 100644 --- a/src/block/WeightedPressurePlate.php +++ b/src/block/WeightedPressurePlate.php @@ -24,7 +24,40 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; +use function ceil; +use function count; +use function max; +use function min; -abstract class WeightedPressurePlate extends PressurePlate{ +class WeightedPressurePlate extends PressurePlate{ use AnalogRedstoneSignalEmitterTrait; + + private readonly float $signalStrengthFactor; + + /** + * @param float $signalStrengthFactor Number of entities on the plate is divided by this value to get signal strength + */ + public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $deactivationDelayTicks, float $signalStrengthFactor = 1.0){ + parent::__construct($idInfo, $name, $typeInfo, $deactivationDelayTicks); + $this->signalStrengthFactor = $signalStrengthFactor; + } + + protected function hasOutputSignal() : bool{ + return $this->signalStrength > 0; + } + + protected function calculatePlateState(array $entities) : array{ + $newSignalStrength = min(15, max(0, + (int) ceil(count($entities) * $this->signalStrengthFactor) + )); + if($newSignalStrength === $this->signalStrength){ + return [$this, null]; + } + $wasActive = $this->signalStrength !== 0; + $isActive = $newSignalStrength !== 0; + return [ + (clone $this)->setOutputSignalStrength($newSignalStrength), + $wasActive !== $isActive ? $isActive : null + ]; + } } diff --git a/src/block/WeightedPressurePlateHeavy.php b/src/block/WeightedPressurePlateHeavy.php index 390297436..9a8d1c31b 100644 --- a/src/block/WeightedPressurePlateHeavy.php +++ b/src/block/WeightedPressurePlateHeavy.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +/** + * @deprecated + */ class WeightedPressurePlateHeavy extends WeightedPressurePlate{ } diff --git a/src/block/WeightedPressurePlateLight.php b/src/block/WeightedPressurePlateLight.php index 458c07e1a..85c13d438 100644 --- a/src/block/WeightedPressurePlateLight.php +++ b/src/block/WeightedPressurePlateLight.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +/** + * @deprecated + */ class WeightedPressurePlateLight extends WeightedPressurePlate{ } diff --git a/src/block/WoodenPressurePlate.php b/src/block/WoodenPressurePlate.php index baaf44c2c..a629c2f1c 100644 --- a/src/block/WoodenPressurePlate.php +++ b/src/block/WoodenPressurePlate.php @@ -23,11 +23,23 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodTypeTrait; class WoodenPressurePlate extends SimplePressurePlate{ use WoodTypeTrait; + public function __construct( + BlockIdentifier $idInfo, + string $name, + BlockTypeInfo $typeInfo, + WoodType $woodType, + int $deactivationDelayTicks = 20 //TODO: make this mandatory in PM6 + ){ + $this->woodType = $woodType; + parent::__construct($idInfo, $name, $typeInfo, $deactivationDelayTicks); + } + public function getFuelTime() : int{ return 300; } diff --git a/src/block/inventory/EnchantInventory.php b/src/block/inventory/EnchantInventory.php index 2c682d134..b726dbedf 100644 --- a/src/block/inventory/EnchantInventory.php +++ b/src/block/inventory/EnchantInventory.php @@ -23,9 +23,15 @@ declare(strict_types=1); namespace pocketmine\block\inventory; +use pocketmine\event\player\PlayerEnchantingOptionsRequestEvent; use pocketmine\inventory\SimpleInventory; use pocketmine\inventory\TemporaryInventory; +use pocketmine\item\enchantment\EnchantingHelper as Helper; +use pocketmine\item\enchantment\EnchantingOption; +use pocketmine\item\Item; use pocketmine\world\Position; +use function array_values; +use function count; class EnchantInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{ use BlockInventoryTrait; @@ -33,8 +39,47 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor public const SLOT_INPUT = 0; public const SLOT_LAPIS = 1; + /** @var EnchantingOption[] $options */ + private array $options = []; + public function __construct(Position $holder){ $this->holder = $holder; parent::__construct(2); } + + protected function onSlotChange(int $index, Item $before) : void{ + if($index === self::SLOT_INPUT){ + foreach($this->viewers as $viewer){ + $this->options = []; + $item = $this->getInput(); + $options = Helper::generateOptions($this->holder, $item, $viewer->getEnchantmentSeed()); + + $event = new PlayerEnchantingOptionsRequestEvent($viewer, $this, $options); + $event->call(); + if(!$event->isCancelled() && count($event->getOptions()) > 0){ + $this->options = array_values($event->getOptions()); + $viewer->getNetworkSession()->getInvManager()?->syncEnchantingTableOptions($this->options); + } + } + } + + parent::onSlotChange($index, $before); + } + + public function getInput() : Item{ + return $this->getItem(self::SLOT_INPUT); + } + + public function getLapis() : Item{ + return $this->getItem(self::SLOT_LAPIS); + } + + public function getOutput(int $optionId) : ?Item{ + $option = $this->getOption($optionId); + return $option === null ? null : Helper::enchantItem($this->getInput(), $option->getEnchantments()); + } + + public function getOption(int $optionId) : ?EnchantingOption{ + return $this->options[$optionId] ?? null; + } } diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index 047c54703..d5d314ee3 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -68,6 +68,8 @@ class Sign extends Spawnable{ } protected SignText $text; + private bool $waxed = false; + protected ?int $editorEntityRuntimeId = null; public function __construct(World $world, Vector3 $pos){ @@ -101,6 +103,7 @@ class Sign extends Spawnable{ } $this->text = new SignText($text); } + $this->waxed = $nbt->getByte(self::TAG_WAXED, 0) !== 0; } protected function writeSaveData(CompoundTag $nbt) : void{ @@ -113,6 +116,7 @@ class Sign extends Spawnable{ $nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())); $nbt->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0); $nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1); + $nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0); } public function getText() : SignText{ @@ -123,6 +127,10 @@ class Sign extends Spawnable{ $this->text = $text; } + public function isWaxed() : bool{ return $this->waxed; } + + public function setWaxed(bool $waxed) : void{ $this->waxed = $waxed; } + /** * Returns the entity runtime ID of the player who placed this sign. Only the player whose entity ID matches this * one may edit the sign text. @@ -153,7 +161,7 @@ class Sign extends Spawnable{ ->setByte(self::TAG_GLOWING_TEXT, 0) ->setByte(self::TAG_PERSIST_FORMATTING, 1) ); - $nbt->setByte(self::TAG_WAXED, 0); + $nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0); $nbt->setLong(self::TAG_LOCKED_FOR_EDITING_BY, $this->editorEntityRuntimeId ?? -1); } } diff --git a/src/block/utils/BlockEventHelper.php b/src/block/utils/BlockEventHelper.php new file mode 100644 index 000000000..fc9b06d13 --- /dev/null +++ b/src/block/utils/BlockEventHelper.php @@ -0,0 +1,115 @@ +call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function spread(Block $oldState, Block $newState, Block $source) : bool{ + if(BlockSpreadEvent::hasHandlers()){ + $ev = new BlockSpreadEvent($oldState, $source, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function form(Block $oldState, Block $newState, Block $cause) : bool{ + if(BlockFormEvent::hasHandlers()){ + $ev = new BlockFormEvent($oldState, $newState, $cause); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function melt(Block $oldState, Block $newState) : bool{ + if(BlockMeltEvent::hasHandlers()){ + $ev = new BlockMeltEvent($oldState, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function die(Block $oldState, Block $newState) : bool{ + if(BlockDeathEvent::hasHandlers()){ + $ev = new BlockDeathEvent($oldState, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } +} diff --git a/src/command/defaults/DefaultGamemodeCommand.php b/src/command/defaults/DefaultGamemodeCommand.php index 3860b9e34..bac98e678 100644 --- a/src/command/defaults/DefaultGamemodeCommand.php +++ b/src/command/defaults/DefaultGamemodeCommand.php @@ -28,6 +28,7 @@ use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\GameMode; +use pocketmine\ServerProperties; use function count; class DefaultGamemodeCommand extends VanillaCommand{ @@ -52,7 +53,7 @@ class DefaultGamemodeCommand extends VanillaCommand{ return true; } - $sender->getServer()->getConfigGroup()->setConfigString("gamemode", $gameMode->name()); + $sender->getServer()->getConfigGroup()->setConfigString(ServerProperties::GAME_MODE, $gameMode->name()); $sender->sendMessage(KnownTranslationFactory::commands_defaultgamemode_success($gameMode->getTranslatableName())); return true; } diff --git a/src/command/defaults/DifficultyCommand.php b/src/command/defaults/DifficultyCommand.php index 98eb3be3b..dee75e025 100644 --- a/src/command/defaults/DifficultyCommand.php +++ b/src/command/defaults/DifficultyCommand.php @@ -28,6 +28,7 @@ use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; +use pocketmine\ServerProperties; use pocketmine\world\World; use function count; @@ -54,7 +55,7 @@ class DifficultyCommand extends VanillaCommand{ } if($difficulty !== -1){ - $sender->getServer()->getConfigGroup()->setConfigInt("difficulty", $difficulty); + $sender->getServer()->getConfigGroup()->setConfigInt(ServerProperties::DIFFICULTY, $difficulty); //TODO: add per-world support foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){ diff --git a/src/command/defaults/EnchantCommand.php b/src/command/defaults/EnchantCommand.php index 583bd59ec..191a146b0 100644 --- a/src/command/defaults/EnchantCommand.php +++ b/src/command/defaults/EnchantCommand.php @@ -25,6 +25,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; +use pocketmine\item\enchantment\EnchantingHelper; use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\enchantment\StringToEnchantmentParser; use pocketmine\lang\KnownTranslationFactory; @@ -76,8 +77,9 @@ class EnchantCommand extends VanillaCommand{ } } - $item->addEnchantment(new EnchantmentInstance($enchantment, $level)); - $player->getInventory()->setItemInHand($item); + //this is necessary to deal with enchanted books, which are a different item type than regular books + $enchantedItem = EnchantingHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]); + $player->getInventory()->setItemInHand($enchantedItem); self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName())); return true; diff --git a/src/command/defaults/TimingsCommand.php b/src/command/defaults/TimingsCommand.php index b5ca18628..3c0701ea4 100644 --- a/src/command/defaults/TimingsCommand.php +++ b/src/command/defaults/TimingsCommand.php @@ -35,6 +35,7 @@ use pocketmine\timings\TimingsHandler; 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; @@ -130,7 +131,7 @@ class TimingsCommand extends VanillaCommand{ ]; fclose($fileTimings); - $host = $sender->getServer()->getConfigGroup()->getPropertyString("timings.host", "timings.pmmp.io"); + $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( [new BulkCurlTaskOperation( diff --git a/src/command/defaults/WhitelistCommand.php b/src/command/defaults/WhitelistCommand.php index 65860aefa..fdf01ff56 100644 --- a/src/command/defaults/WhitelistCommand.php +++ b/src/command/defaults/WhitelistCommand.php @@ -30,6 +30,7 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; use pocketmine\Server; +use pocketmine\ServerProperties; use function count; use function implode; use function sort; @@ -71,7 +72,7 @@ class WhitelistCommand extends VanillaCommand{ case "on": if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_ENABLE)){ $server = $sender->getServer(); - $server->getConfigGroup()->setConfigBool("white-list", true); + $server->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, true); $this->kickNonWhitelistedPlayers($server); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_enabled()); } @@ -79,7 +80,7 @@ class WhitelistCommand extends VanillaCommand{ return true; case "off": if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_DISABLE)){ - $sender->getServer()->getConfigGroup()->setConfigBool("white-list", false); + $sender->getServer()->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, false); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_disabled()); } diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php index 741a88ca6..b0e58fd16 100644 --- a/src/crash/CrashDump.php +++ b/src/crash/CrashDump.php @@ -34,6 +34,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use pocketmine\VersionInfo; +use pocketmine\YmlServerProperties; use Symfony\Component\Filesystem\Path; use function array_map; use function base64_encode; @@ -152,7 +153,7 @@ class CrashDump{ private function extraData() : void{ global $argv; - if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_SETTINGS, true)){ $this->data->parameters = (array) $argv; if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){ $this->data->serverDotProperties = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties) ?? throw new AssumptionFailedError("Pattern is valid"); @@ -170,7 +171,7 @@ class CrashDump{ $this->data->jit_mode = Utils::getOpcacheJitMode(); - if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_PHPINFO, true)){ ob_start(); phpinfo(); $this->data->phpinfo = ob_get_contents(); // @phpstan-ignore-line @@ -226,7 +227,7 @@ class CrashDump{ } } - if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) && file_exists($error["fullFile"])){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_CODE, true) && file_exists($error["fullFile"])){ $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES); if($file !== false){ for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 && isset($file[$l]); ++$l){ diff --git a/src/data/bedrock/DyeColorIdMap.php b/src/data/bedrock/DyeColorIdMap.php index 35db72c3e..0d10edad5 100644 --- a/src/data/bedrock/DyeColorIdMap.php +++ b/src/data/bedrock/DyeColorIdMap.php @@ -29,18 +29,10 @@ use pocketmine\utils\SingletonTrait; final class DyeColorIdMap{ use SingletonTrait; - - /** - * @var DyeColor[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait { + register as registerInt; + } /** * @var DyeColor[] @@ -74,16 +66,11 @@ final class DyeColorIdMap{ } private function register(int $id, string $itemId, DyeColor $color) : void{ - $this->idToEnum[$id] = $color; - $this->enumToId[$color->id()] = $id; + $this->registerInt($id, $color); $this->itemIdToEnum[$itemId] = $color; $this->enumToItemId[$color->id()] = $itemId; } - public function toId(DyeColor $color) : int{ - return $this->enumToId[$color->id()]; //TODO: is it possible for this to be missing? - } - public function toInvertedId(DyeColor $color) : int{ return ~$this->toId($color) & 0xf; } @@ -92,10 +79,6 @@ final class DyeColorIdMap{ return $this->enumToItemId[$color->id()]; } - public function fromId(int $id) : ?DyeColor{ - return $this->idToEnum[$id] ?? null; - } - public function fromInvertedId(int $id) : ?DyeColor{ return $this->fromId(~$id & 0xf); } diff --git a/src/data/bedrock/EffectIdMap.php b/src/data/bedrock/EffectIdMap.php index 6dce86d9b..cadd8c397 100644 --- a/src/data/bedrock/EffectIdMap.php +++ b/src/data/bedrock/EffectIdMap.php @@ -26,23 +26,11 @@ namespace pocketmine\data\bedrock; use pocketmine\entity\effect\Effect; use pocketmine\entity\effect\VanillaEffects; use pocketmine\utils\SingletonTrait; -use function array_key_exists; -use function spl_object_id; final class EffectIdMap{ use SingletonTrait; - - /** - * @var Effect[] - * @phpstan-var array - */ - private array $idToEffect = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $effectToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(EffectIds::SPEED, VanillaEffects::SPEED()); @@ -76,24 +64,4 @@ final class EffectIdMap{ //TODO: VILLAGE_HERO $this->register(EffectIds::DARKNESS, VanillaEffects::DARKNESS()); } - - //TODO: not a big fan of the code duplication here :( - - public function register(int $mcpeId, Effect $effect) : void{ - $this->idToEffect[$mcpeId] = $effect; - $this->effectToId[spl_object_id($effect)] = $mcpeId; - } - - public function fromId(int $id) : ?Effect{ - //we might not have all the effect IDs registered - return $this->idToEffect[$id] ?? null; - } - - public function toId(Effect $effect) : int{ - if(!array_key_exists(spl_object_id($effect), $this->effectToId)){ - //this should never happen, so we treat it as an exceptional condition - throw new \InvalidArgumentException("Effect does not have a mapped ID"); - } - return $this->effectToId[spl_object_id($effect)]; - } } diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php index 1d974ed6e..ae2460cfe 100644 --- a/src/data/bedrock/EnchantmentIdMap.php +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -26,25 +26,14 @@ namespace pocketmine\data\bedrock; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\utils\SingletonTrait; -use function array_key_exists; -use function spl_object_id; /** * Handles translation of internal enchantment types to and from Minecraft: Bedrock IDs. */ final class EnchantmentIdMap{ use SingletonTrait; - - /** - * @var Enchantment[] - * @phpstan-var array - */ - private array $idToEnch = []; - /** - * @var int[] - * @phpstan-var array - */ - private array $enchToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(EnchantmentIds::PROTECTION, VanillaEnchantments::PROTECTION()); @@ -77,22 +66,4 @@ final class EnchantmentIdMap{ $this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK()); } - - public function register(int $mcpeId, Enchantment $enchantment) : void{ - $this->idToEnch[$mcpeId] = $enchantment; - $this->enchToId[spl_object_id($enchantment)] = $mcpeId; - } - - public function fromId(int $id) : ?Enchantment{ - //we might not have all the enchantment IDs registered - return $this->idToEnch[$id] ?? null; - } - - public function toId(Enchantment $enchantment) : int{ - if(!array_key_exists(spl_object_id($enchantment), $this->enchToId)){ - //this should never happen, so we treat it as an exceptional condition - throw new \InvalidArgumentException("Enchantment does not have a mapped ID"); - } - return $this->enchToId[spl_object_id($enchantment)]; - } } diff --git a/src/data/bedrock/IntSaveIdMapTrait.php b/src/data/bedrock/IntSaveIdMapTrait.php new file mode 100644 index 000000000..cf1631fd5 --- /dev/null +++ b/src/data/bedrock/IntSaveIdMapTrait.php @@ -0,0 +1,81 @@ + + */ + private array $idToEnum = []; + + /** + * @var int[] + * @phpstan-var array + */ + private array $enumToId = []; + + /** + * @phpstan-param TObject $enum + */ + protected function getRuntimeId(object $enum) : int{ + //this is fine for enums and non-cloning object registries + return spl_object_id($enum); + } + + /** + * @phpstan-param TObject $enum + */ + public function register(int $saveId, object $enum) : void{ + $this->idToEnum[$saveId] = $enum; + $this->enumToId[$this->getRuntimeId($enum)] = $saveId; + } + + /** + * @phpstan-return TObject|null + */ + public function fromId(int $id) : ?object{ + //we might not have all the effect IDs registered + return $this->idToEnum[$id] ?? null; + } + + /** + * @phpstan-param TObject $enum + */ + public function toId(object $enum) : int{ + $runtimeId = $this->getRuntimeId($enum); + if(!array_key_exists($runtimeId, $this->enumToId)){ + //this should never happen, so we treat it as an exceptional condition + throw new \InvalidArgumentException("Object does not have a mapped save ID"); + } + return $this->enumToId[$runtimeId]; + } +} diff --git a/src/data/bedrock/MedicineTypeIdMap.php b/src/data/bedrock/MedicineTypeIdMap.php index a85dbb7a8..2d9deb380 100644 --- a/src/data/bedrock/MedicineTypeIdMap.php +++ b/src/data/bedrock/MedicineTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class MedicineTypeIdMap{ use SingletonTrait; - - /** - * @var MedicineType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE()); @@ -47,20 +37,4 @@ final class MedicineTypeIdMap{ $this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS()); $this->register(MedicineTypeIds::TONIC, MedicineType::TONIC()); } - - private function register(int $id, MedicineType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?MedicineType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(MedicineType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/MobHeadTypeIdMap.php b/src/data/bedrock/MobHeadTypeIdMap.php index 99213fa46..b99b1097b 100644 --- a/src/data/bedrock/MobHeadTypeIdMap.php +++ b/src/data/bedrock/MobHeadTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class MobHeadTypeIdMap{ use SingletonTrait; - - /** - * @var MobHeadType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(0, MobHeadType::SKELETON()); @@ -50,20 +40,4 @@ final class MobHeadTypeIdMap{ $this->register(5, MobHeadType::DRAGON()); $this->register(6, MobHeadType::PIGLIN()); } - - private function register(int $id, MobHeadType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?MobHeadType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(MobHeadType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php index 2eec17549..927b52c76 100644 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ b/src/data/bedrock/MushroomBlockTypeIdMap.php @@ -26,21 +26,11 @@ namespace pocketmine\data\bedrock; use pocketmine\block\utils\MushroomBlockType; use pocketmine\data\bedrock\block\BlockLegacyMetadata as LegacyMeta; use pocketmine\utils\SingletonTrait; -use function array_key_exists; final class MushroomBlockTypeIdMap{ use SingletonTrait; - - /** - * @var MushroomBlockType[] - * @phpstan-var array - */ - private array $idToEnum = []; - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; public function __construct(){ $this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES()); @@ -55,20 +45,4 @@ final class MushroomBlockTypeIdMap{ $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST()); $this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP()); } - - public function register(int $id, MushroomBlockType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?MushroomBlockType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(MushroomBlockType $type) : int{ - if(!array_key_exists($type->id(), $this->enumToId)){ - throw new \InvalidArgumentException("Mushroom block type does not have a mapped ID"); //this should never happen - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/NoteInstrumentIdMap.php b/src/data/bedrock/NoteInstrumentIdMap.php index 0b8a43735..22d7e5cb1 100644 --- a/src/data/bedrock/NoteInstrumentIdMap.php +++ b/src/data/bedrock/NoteInstrumentIdMap.php @@ -28,18 +28,8 @@ use pocketmine\world\sound\NoteInstrument; final class NoteInstrumentIdMap{ use SingletonTrait; - - /** - * @var NoteInstrument[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(0, NoteInstrument::PIANO()); @@ -59,20 +49,4 @@ final class NoteInstrumentIdMap{ $this->register(14, NoteInstrument::BANJO()); $this->register(15, NoteInstrument::PLING()); } - - private function register(int $id, NoteInstrument $instrument) : void{ - $this->idToEnum[$id] = $instrument; - $this->enumToId[$instrument->id()] = $id; - } - - public function fromId(int $id) : ?NoteInstrument{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(NoteInstrument $instrument) : int{ - if(!isset($this->enumToId[$instrument->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$instrument->id()]; - } } diff --git a/src/data/bedrock/PotionTypeIdMap.php b/src/data/bedrock/PotionTypeIdMap.php index 8194f24a2..3e9858217 100644 --- a/src/data/bedrock/PotionTypeIdMap.php +++ b/src/data/bedrock/PotionTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class PotionTypeIdMap{ use SingletonTrait; - - /** - * @var PotionType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(PotionTypeIds::WATER, PotionType::WATER()); @@ -86,20 +76,4 @@ final class PotionTypeIdMap{ $this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING()); $this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS()); } - - private function register(int $id, PotionType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?PotionType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(PotionType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/SuspiciousStewTypeIdMap.php b/src/data/bedrock/SuspiciousStewTypeIdMap.php index 1dc86abf1..37d121517 100644 --- a/src/data/bedrock/SuspiciousStewTypeIdMap.php +++ b/src/data/bedrock/SuspiciousStewTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class SuspiciousStewTypeIdMap{ use SingletonTrait; - - /** - * @var SuspiciousStewType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY()); @@ -53,20 +43,4 @@ final class SuspiciousStewTypeIdMap{ $this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY()); $this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE()); } - - private function register(int $id, SuspiciousStewType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?SuspiciousStewType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(SuspiciousStewType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 8ec9a1ddc..6ffc1c55d 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -100,6 +100,7 @@ use pocketmine\block\MobHead; use pocketmine\block\NetherPortal; use pocketmine\block\NetherVines; use pocketmine\block\NetherWartPlant; +use pocketmine\block\PinkPetals; use pocketmine\block\Potato; use pocketmine\block\PoweredRail; use pocketmine\block\PumpkinStem; @@ -1364,6 +1365,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE)); $this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE)); $this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT))); + $this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{ + return Writer::create(Ids::PINK_PETALS) + ->writeLegacyHorizontalFacing($block->getFacing()) + ->writeInt(StateNames::GROWTH, $block->getCount() - 1); + }); $this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK)); $this->map(Blocks::POLISHED_ANDESITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_ANDESITE_SMOOTH)); $this->map(Blocks::POLISHED_ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE)); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 08c9b5914..f6b8699a9 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -28,6 +28,7 @@ use pocketmine\block\Block; use pocketmine\block\CaveVines; use pocketmine\block\ChorusFlower; use pocketmine\block\Light; +use pocketmine\block\PinkPetals; use pocketmine\block\Slab; use pocketmine\block\Stair; use pocketmine\block\SweetBerryBush; @@ -1175,6 +1176,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED())); $this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED())); $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT())->setAxis($in->readPillarAxis())); + $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ + //Pink petals only uses 0-3, but GROWTH state can go up to 7 + $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); + return Blocks::PINK_PETALS() + ->setFacing($in->readLegacyHorizontalFacing()) + ->setCount(min($growth + 1, PinkPetals::MAX_COUNT)); + }); $this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS()); $this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{ return Blocks::POLISHED_BASALT() diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 6f557ddb1..ccf430b9c 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -223,6 +223,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::ECHO_SHARD, Items::ECHO_SHARD()); $this->map1to1Item(Ids::EGG, Items::EGG()); $this->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::ENDER_PEARL, Items::ENDER_PEARL()); $this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE()); diff --git a/src/entity/Human.php b/src/entity/Human.php index ae1aa2464..f2c4c7a74 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -37,6 +37,7 @@ use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerEnderInventory; use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\PlayerOffHandInventory; +use pocketmine\item\enchantment\EnchantingHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\Totem; @@ -66,7 +67,6 @@ use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\player\Player; -use pocketmine\utils\Limits; use pocketmine\world\sound\TotemUseSound; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; @@ -76,7 +76,6 @@ use function array_key_exists; use function array_merge; use function array_values; use function min; -use function random_int; class Human extends Living implements ProjectileSource, InventoryHolder{ @@ -211,6 +210,18 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ return $this->xpManager; } + public function getEnchantmentSeed() : int{ + return $this->xpSeed; + } + + public function setEnchantmentSeed(int $seed) : void{ + $this->xpSeed = $seed; + } + + public function regenerateEnchantmentSeed() : void{ + $this->xpSeed = EnchantingHelper::generateSeed(); + } + public function getXpDropAmount() : int{ //this causes some XP to be lost on death when above level 1 (by design), dropping at most enough points for //about 7.5 levels of XP. @@ -334,7 +345,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){ $this->xpSeed = $xpSeedTag->getValue(); }else{ - $this->xpSeed = random_int(Limits::INT32_MIN, Limits::INT32_MAX); + $this->xpSeed = EnchantingHelper::generateSeed(); } } diff --git a/src/entity/HungerManager.php b/src/entity/HungerManager.php index 304dcd30b..a31855891 100644 --- a/src/entity/HungerManager.php +++ b/src/entity/HungerManager.php @@ -133,14 +133,18 @@ class HungerManager{ if(!$this->enabled){ return 0; } - $ev = new PlayerExhaustEvent($this->entity, $amount, $cause); - $ev->call(); - if($ev->isCancelled()){ - return 0.0; + $evAmount = $amount; + if(PlayerExhaustEvent::hasHandlers()){ + $ev = new PlayerExhaustEvent($this->entity, $amount, $cause); + $ev->call(); + if($ev->isCancelled()){ + return 0.0; + } + $evAmount = $ev->getAmount(); } $exhaustion = $this->getExhaustion(); - $exhaustion += $ev->getAmount(); + $exhaustion += $evAmount; while($exhaustion >= 4.0){ $exhaustion -= 4.0; @@ -159,7 +163,7 @@ class HungerManager{ } $this->setExhaustion($exhaustion); - return $ev->getAmount(); + return $evAmount; } public function getFoodTickTimer() : int{ diff --git a/src/event/Event.php b/src/event/Event.php index 5488285b2..21b8ae36a 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace pocketmine\event; use pocketmine\timings\Timings; +use function count; use function get_class; abstract class Event{ @@ -54,11 +55,11 @@ abstract class Event{ $timings = Timings::getEventTimings($this); $timings->startTiming(); - $handlerList = HandlerListManager::global()->getListFor(get_class($this)); + $handlers = HandlerListManager::global()->getHandlersFor(static::class); ++self::$eventCallDepth; try{ - foreach($handlerList->getListenerList() as $registration){ + foreach($handlers as $registration){ $registration->callEvent($this); } }finally{ @@ -66,4 +67,14 @@ abstract class Event{ $timings->stopTiming(); } } + + /** + * Returns whether the current class context has any registered global handlers. + * This can be used in hot code paths to avoid unnecessary event object creation. + * + * Usage: SomeEventClass::hasHandlers() + */ + public static function hasHandlers() : bool{ + return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0; + } } diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php index 37811e959..74eedf3a4 100644 --- a/src/event/HandlerList.php +++ b/src/event/HandlerList.php @@ -33,8 +33,6 @@ class HandlerList{ /** @var RegisteredListener[][] */ private array $handlerSlots = []; - private RegisteredListenerCache $handlerCache; - /** @var RegisteredListenerCache[] */ private array $affectedHandlerCaches = []; @@ -44,9 +42,9 @@ class HandlerList{ */ public function __construct( private string $class, - private ?HandlerList $parentList + private ?HandlerList $parentList, + private RegisteredListenerCache $handlerCache = new RegisteredListenerCache() ){ - $this->handlerCache = new RegisteredListenerCache(); for($list = $this; $list !== null; $list = $list->parentList){ $list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache; } diff --git a/src/event/HandlerListManager.php b/src/event/HandlerListManager.php index ab94674cf..047632f54 100644 --- a/src/event/HandlerListManager.php +++ b/src/event/HandlerListManager.php @@ -36,6 +36,11 @@ class HandlerListManager{ /** @var HandlerList[] classname => HandlerList */ private array $allLists = []; + /** + * @var RegisteredListenerCache[] event class name => cache + * @phpstan-var array, RegisteredListenerCache> + */ + private array $handlerCaches = []; /** * Unregisters all the listeners @@ -98,7 +103,25 @@ class HandlerListManager{ } $parent = self::resolveNearestHandleableParent($class); - return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null); + $cache = new RegisteredListenerCache(); + $this->handlerCaches[$event] = $cache; + return $this->allLists[$event] = new HandlerList( + $event, + parentList: $parent !== null ? $this->getListFor($parent->getName()) : null, + handlerCache: $cache + ); + } + + /** + * @phpstan-template TEvent of Event + * @phpstan-param class-string $event + * + * @return RegisteredListener[] + */ + public function getHandlersFor(string $event) : array{ + $cache = $this->handlerCaches[$event] ?? null; + //getListFor() will populate the cache for the next call + return $cache?->list ?? $this->getListFor($event)->getListenerList(); } /** diff --git a/src/event/block/FarmlandHydrationChangeEvent.php b/src/event/block/FarmlandHydrationChangeEvent.php new file mode 100644 index 000000000..4df5c1a4e --- /dev/null +++ b/src/event/block/FarmlandHydrationChangeEvent.php @@ -0,0 +1,59 @@ +oldHydration; + } + + public function getNewHydration() : int{ + return $this->newHydration; + } + + public function setNewHydration(int $hydration) : void{ + if($hydration < 0 || $hydration > Farmland::MAX_WETNESS){ + throw new \InvalidArgumentException("Hydration must be in range 0 ... " . Farmland::MAX_WETNESS); + } + $this->newHydration = $hydration; + } +} diff --git a/src/event/block/PressurePlateUpdateEvent.php b/src/event/block/PressurePlateUpdateEvent.php new file mode 100644 index 000000000..e6f2e92ba --- /dev/null +++ b/src/event/block/PressurePlateUpdateEvent.php @@ -0,0 +1,52 @@ +activatingEntities; } +} diff --git a/src/event/player/PlayerEnchantingOptionsRequestEvent.php b/src/event/player/PlayerEnchantingOptionsRequestEvent.php new file mode 100644 index 000000000..833185f76 --- /dev/null +++ b/src/event/player/PlayerEnchantingOptionsRequestEvent.php @@ -0,0 +1,75 @@ +player = $player; + } + + public function getInventory() : EnchantInventory{ + return $this->inventory; + } + + /** + * @return EnchantingOption[] + */ + public function getOptions() : array{ + return $this->options; + } + + /** + * @param EnchantingOption[] $options + */ + public function setOptions(array $options) : void{ + Utils::validateArrayValueType($options, function(EnchantingOption $_) : void{ }); + if(($optionCount = count($options)) > 3){ + throw new \LogicException("The maximum number of options for an enchanting table is 3, but $optionCount have been passed"); + } + + $this->options = $options; + } +} diff --git a/src/event/player/PlayerItemEnchantEvent.php b/src/event/player/PlayerItemEnchantEvent.php new file mode 100644 index 000000000..76151384d --- /dev/null +++ b/src/event/player/PlayerItemEnchantEvent.php @@ -0,0 +1,85 @@ +player = $player; + } + + /** + * Returns the inventory transaction involved in this enchant event. + */ + public function getTransaction() : EnchantingTransaction{ + return $this->transaction; + } + + /** + * Returns the enchantment option used. + */ + public function getOption() : EnchantingOption{ + return $this->option; + } + + /** + * Returns the item to be enchanted. + */ + public function getInputItem() : Item{ + return clone $this->inputItem; + } + + /** + * Returns the enchanted item. + */ + public function getOutputItem() : Item{ + return clone $this->outputItem; + } + + /** + * Returns the number of XP levels and lapis that will be subtracted after enchanting + * if the player is not in creative mode. + */ + public function getCost() : int{ + return $this->cost; + } +} diff --git a/src/event/world/WorldDifficultyChangeEvent.php b/src/event/world/WorldDifficultyChangeEvent.php new file mode 100644 index 000000000..90cee376a --- /dev/null +++ b/src/event/world/WorldDifficultyChangeEvent.php @@ -0,0 +1,44 @@ +oldDifficulty; } + + public function getNewDifficulty() : int{ return $this->newDifficulty; } +} diff --git a/src/inventory/transaction/EnchantingTransaction.php b/src/inventory/transaction/EnchantingTransaction.php new file mode 100644 index 000000000..d00df97e2 --- /dev/null +++ b/src/inventory/transaction/EnchantingTransaction.php @@ -0,0 +1,134 @@ +inputItem === null || $this->outputItem === null){ + throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before validating output"); + } + + $enchantedInput = EnchantingHelper::enchantItem($this->inputItem, $this->option->getEnchantments()); + if(!$this->outputItem->equalsExact($enchantedInput)){ + throw new TransactionValidationException("Invalid output item"); + } + } + + private function validateFiniteResources(int $lapisSpent) : void{ + if($lapisSpent !== $this->cost){ + throw new TransactionValidationException("Expected the amount of lapis lazuli spent to be $this->cost, but received $lapisSpent"); + } + + $xpLevel = $this->source->getXpManager()->getXpLevel(); + $requiredXpLevel = $this->option->getRequiredXpLevel(); + + if($xpLevel < $requiredXpLevel){ + throw new TransactionValidationException("Player's XP level $xpLevel is less than the required XP level $requiredXpLevel"); + } + //XP level cost is intentionally not checked here, as the required level may be lower than the cost, allowing + //the option to be used with less XP than the cost - in this case, as much XP as possible will be deducted. + } + + public function validate() : void{ + if(count($this->actions) < 1){ + throw new TransactionValidationException("Transaction must have at least one action to be executable"); + } + + /** @var Item[] $inputs */ + $inputs = []; + /** @var Item[] $outputs */ + $outputs = []; + $this->matchItems($outputs, $inputs); + + $lapisSpent = 0; + foreach($inputs as $input){ + if($input->getTypeId() === ItemTypeIds::LAPIS_LAZULI){ + $lapisSpent = $input->getCount(); + }else{ + if($this->inputItem !== null){ + throw new TransactionValidationException("Received more than 1 items to enchant"); + } + $this->inputItem = $input; + } + } + + if($this->inputItem === null){ + throw new TransactionValidationException("No item to enchant received"); + } + + if(($outputCount = count($outputs)) !== 1){ + throw new TransactionValidationException("Expected 1 output item, but received $outputCount"); + } + $this->outputItem = $outputs[0]; + + $this->validateOutput(); + + if($this->source->hasFiniteResources()){ + $this->validateFiniteResources($lapisSpent); + } + } + + public function execute() : void{ + parent::execute(); + + if($this->source->hasFiniteResources()){ + //If the required XP level is less than the XP cost, the option can be selected with less XP than the cost. + //In this case, as much XP as possible will be taken. + $this->source->getXpManager()->subtractXpLevels(min($this->cost, $this->source->getXpManager()->getXpLevel())); + } + $this->source->regenerateEnchantmentSeed(); + } + + protected function callExecuteEvent() : bool{ + if($this->inputItem === null || $this->outputItem === null){ + throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before executing the event"); + } + + $event = new PlayerItemEnchantEvent($this->source, $this, $this->option, $this->inputItem, $this->outputItem, $this->cost); + $event->call(); + return !$event->isCancelled(); + } +} diff --git a/src/item/Armor.php b/src/item/Armor.php index 6fb538cd6..e52732caf 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -44,8 +44,11 @@ class Armor extends Durable{ protected ?Color $customColor = null; - public function __construct(ItemIdentifier $identifier, string $name, ArmorTypeInfo $info){ - parent::__construct($identifier, $name); + /** + * @param string[] $enchantmentTags + */ + public function __construct(ItemIdentifier $identifier, string $name, ArmorTypeInfo $info, array $enchantmentTags = []){ + parent::__construct($identifier, $name, $enchantmentTags); $this->armorInfo = $info; } @@ -72,6 +75,14 @@ class Armor extends Durable{ return $this->armorInfo->isFireProof(); } + public function getMaterial() : ArmorMaterial{ + return $this->armorInfo->getMaterial(); + } + + public function getEnchantability() : int{ + return $this->armorInfo->getMaterial()->getEnchantability(); + } + /** * Returns the dyed colour of this armour piece. This generally only applies to leather armour. */ diff --git a/src/item/ArmorMaterial.php b/src/item/ArmorMaterial.php new file mode 100644 index 000000000..c7915bacc --- /dev/null +++ b/src/item/ArmorMaterial.php @@ -0,0 +1,42 @@ +enchantability; + } +} diff --git a/src/item/ArmorTypeInfo.php b/src/item/ArmorTypeInfo.php index 580b73df3..dbb4ed06d 100644 --- a/src/item/ArmorTypeInfo.php +++ b/src/item/ArmorTypeInfo.php @@ -24,13 +24,18 @@ declare(strict_types=1); namespace pocketmine\item; class ArmorTypeInfo{ + private ArmorMaterial $material; + public function __construct( private int $defensePoints, private int $maxDurability, private int $armorSlot, private int $toughness = 0, - private bool $fireProof = false - ){} + private bool $fireProof = false, + ?ArmorMaterial $material = null + ){ + $this->material = $material ?? VanillaArmorMaterials::LEATHER(); + } public function getDefensePoints() : int{ return $this->defensePoints; @@ -51,4 +56,8 @@ class ArmorTypeInfo{ public function isFireProof() : bool{ return $this->fireProof; } + + public function getMaterial() : ArmorMaterial{ + return $this->material; + } } diff --git a/src/item/EnchantedBook.php b/src/item/EnchantedBook.php new file mode 100644 index 000000000..5660de6f6 --- /dev/null +++ b/src/item/EnchantedBook.php @@ -0,0 +1,30 @@ +nbt = new CompoundTag(); } @@ -455,6 +458,29 @@ class Item implements \JsonSerializable{ return $this->name; } + /** + * Returns tags that represent the type of item being enchanted and are used to determine + * what enchantments can be applied to this item during in-game enchanting (enchanting table, anvil, fishing, etc.). + * @see ItemEnchantmentTags + * @see ItemEnchantmentTagRegistry + * @see AvailableEnchantmentRegistry + * + * @return string[] + */ + public function getEnchantmentTags() : array{ + return $this->enchantmentTags; + } + + /** + * Returns the value that defines how enchantable the item is. + * + * The higher an item's enchantability is, the more likely it will be to gain high-level enchantments + * or multiple enchantments upon being enchanted in an enchanting table. + */ + public function getEnchantability() : int{ + return 1; + } + final public function canBePlaced() : bool{ return $this->getBlock()->canBePlaced(); } diff --git a/src/item/ItemBlock.php b/src/item/ItemBlock.php index fbbe2efeb..11bcb58d3 100644 --- a/src/item/ItemBlock.php +++ b/src/item/ItemBlock.php @@ -36,7 +36,7 @@ final class ItemBlock extends Item{ public function __construct( private Block $block ){ - parent::__construct(ItemIdentifier::fromBlock($block), $block->getName()); + parent::__construct(ItemIdentifier::fromBlock($block), $block->getName(), $block->getEnchantmentTags()); } protected function describeState(RuntimeDataDescriber $w) : void{ diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index f37426c56..823339766 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -303,8 +303,9 @@ final class ItemTypeIds{ public const MANGROVE_BOAT = 20264; public const GLOW_BERRIES = 20265; public const CHERRY_SIGN = 20266; + public const ENCHANTED_BOOK = 20267; - public const FIRST_UNUSED_ITEM_ID = 20267; + public const FIRST_UNUSED_ITEM_ID = 20268; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index ea9923c21..d482e4bef 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -856,6 +856,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("packed_ice", fn() => Blocks::PACKED_ICE()); $result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD()); $result->registerBlock("peony", fn() => Blocks::PEONY()); + $result->registerBlock("pink_petals", fn() => Blocks::PINK_PETALS()); $result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP()); $result->registerBlock("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN())); $result->registerBlock("plank", fn() => Blocks::OAK_PLANKS()); @@ -1276,6 +1277,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("egg", fn() => Items::EGG()); $result->register("elixir", fn() => Items::MEDICINE()->setType(MedicineType::ELIXIR())); $result->register("emerald", fn() => Items::EMERALD()); + $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("ender_pearl", fn() => Items::ENDER_PEARL()); diff --git a/src/item/TieredTool.php b/src/item/TieredTool.php index e7d4f2201..dc00aebcf 100644 --- a/src/item/TieredTool.php +++ b/src/item/TieredTool.php @@ -26,8 +26,11 @@ namespace pocketmine\item; abstract class TieredTool extends Tool{ protected ToolTier $tier; - public function __construct(ItemIdentifier $identifier, string $name, ToolTier $tier){ - parent::__construct($identifier, $name); + /** + * @param string[] $enchantmentTags + */ + public function __construct(ItemIdentifier $identifier, string $name, ToolTier $tier, array $enchantmentTags = []){ + parent::__construct($identifier, $name, $enchantmentTags); $this->tier = $tier; } @@ -43,6 +46,10 @@ abstract class TieredTool extends Tool{ return $this->tier->getBaseEfficiency(); } + public function getEnchantability() : int{ + return $this->tier->getEnchantability(); + } + public function getFuelTime() : int{ if($this->tier->equals(ToolTier::WOOD())){ return 200; diff --git a/src/item/ToolTier.php b/src/item/ToolTier.php index 231e233c3..4ca910c0b 100644 --- a/src/item/ToolTier.php +++ b/src/item/ToolTier.php @@ -45,12 +45,12 @@ final class ToolTier{ protected static function setup() : void{ self::registerAll( - new self("wood", 1, 60, 5, 2), - new self("gold", 2, 33, 5, 12), - new self("stone", 3, 132, 6, 4), - new self("iron", 4, 251, 7, 6), - new self("diamond", 5, 1562, 8, 8), - new self("netherite", 6, 2032, 9, 9) + new self("wood", 1, 60, 5, 2, 15), + new self("gold", 2, 33, 5, 12, 22), + new self("stone", 3, 132, 6, 4, 5), + new self("iron", 4, 251, 7, 6, 14), + new self("diamond", 5, 1562, 8, 8, 10), + new self("netherite", 6, 2032, 9, 9, 15) ); } @@ -59,7 +59,8 @@ final class ToolTier{ private int $harvestLevel, private int $maxDurability, private int $baseAttackPoints, - private int $baseEfficiency + private int $baseEfficiency, + private int $enchantability ){ $this->Enum___construct($name); } @@ -79,4 +80,14 @@ final class ToolTier{ public function getBaseEfficiency() : int{ return $this->baseEfficiency; } + + /** + * Returns the value that defines how enchantable the item is. + * + * The higher an item's enchantability is, the more likely it will be to gain high-level enchantments + * or multiple enchantments upon being enchanted in an enchanting table. + */ + public function getEnchantability() : int{ + return $this->enchantability; + } } diff --git a/src/item/VanillaArmorMaterials.php b/src/item/VanillaArmorMaterials.php new file mode 100644 index 000000000..ab2909bce --- /dev/null +++ b/src/item/VanillaArmorMaterials.php @@ -0,0 +1,73 @@ + + */ + public static function getAll() : array{ + // phpstan doesn't support generic traits yet :( + /** @var ArmorMaterial[] $result */ + $result = self::_registryGetAll(); + return $result; + } + + protected static function setup() : void{ + self::register("leather", new ArmorMaterial(15)); + self::register("chainmail", new ArmorMaterial(12)); + self::register("iron", new ArmorMaterial(9)); + self::register("turtle", new ArmorMaterial(9)); + self::register("gold", new ArmorMaterial(25)); + self::register("diamond", new ArmorMaterial(10)); + self::register("netherite", new ArmorMaterial(15)); + } +} diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index b7c32ebc1..a5d50c9db 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\utils\RecordType; -use pocketmine\block\VanillaBlocks; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\entity\Entity; use pocketmine\entity\Location; @@ -32,8 +31,10 @@ use pocketmine\entity\Squid; use pocketmine\entity\Villager; use pocketmine\entity\Zombie; 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\AssumptionFailedError; use pocketmine\utils\CloningRegistryTrait; @@ -151,6 +152,7 @@ use pocketmine\world\World; * @method static Item ECHO_SHARD() * @method static Egg EGG() * @method static Item EMERALD() + * @method static EnchantedBook ENCHANTED_BOOK() * @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE() * @method static EnderPearl ENDER_PEARL() * @method static ExperienceBottle EXPERIENCE_BOTTLE() @@ -337,7 +339,7 @@ final class VanillaItems{ self::registerSpawnEggs(); self::registerTierToolItems(); - self::register("air", VanillaBlocks::AIR()->asItem()->setCount(0)); + self::register("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")); @@ -355,8 +357,8 @@ final class VanillaItems{ 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")); - self::register("bow", new Bow(new IID(Ids::BOW), "Bow")); + 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")); @@ -408,7 +410,7 @@ final class VanillaItems{ 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")); + 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")); @@ -429,15 +431,16 @@ final class VanillaItems{ 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")); + 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")); + 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")); @@ -521,7 +524,7 @@ final class VanillaItems{ 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")); + 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")); @@ -577,67 +580,67 @@ final class VanillaItems{ } private static function registerTierToolItems() : void{ - self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND())); - self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD())); - self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON())); - self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE())); - self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE())); - self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD())); - self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND())); - self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD())); - self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON())); - self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE())); - self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE())); - self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD())); - self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND())); - self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD())); - self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON())); - self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE())); - self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE())); - self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD())); - self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND())); - self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD())); - self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON())); - self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE())); - self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE())); - self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD())); - self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND())); - self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD())); - self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON())); - self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE())); - self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE())); - self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD())); + 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])); } 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))); - self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2))); - self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET))); - self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET))); - self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET))); - self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true))); + 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_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST))); - self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2))); - self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST))); - self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST))); - self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST))); - self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true))); + 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_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD))); - self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2))); - self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD))); - self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD))); - self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD))); - self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true))); - self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD))); + 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_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS))); - self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2))); - self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS))); - self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS))); - self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS))); - self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true))); + 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])); } } diff --git a/src/item/enchantment/AvailableEnchantmentRegistry.php b/src/item/enchantment/AvailableEnchantmentRegistry.php new file mode 100644 index 000000000..2c6f421ed --- /dev/null +++ b/src/item/enchantment/AvailableEnchantmentRegistry.php @@ -0,0 +1,211 @@ +register(Enchantments::PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::FIRE_PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::FEATHER_FALLING(), [Tags::BOOTS], []); + $this->register(Enchantments::BLAST_PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::PROJECTILE_PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]); + $this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []); + $this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []); + $this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []); + $this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []); + $this->register(Enchantments::EFFICIENCY(), [Tags::BLOCK_TOOLS], [Tags::SHEARS]); + $this->register(Enchantments::FORTUNE(), [Tags::BLOCK_TOOLS], []); + $this->register(Enchantments::SILK_TOUCH(), [Tags::BLOCK_TOOLS], [Tags::SHEARS]); + $this->register( + Enchantments::UNBREAKING(), + [Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD], + [Tags::SHEARS, Tags::FLINT_AND_STEEL, Tags::SHIELD, Tags::CARROT_ON_STICK, Tags::ELYTRA, Tags::BRUSH] + ); + $this->register(Enchantments::POWER(), [Tags::BOW], []); + $this->register(Enchantments::PUNCH(), [Tags::BOW], []); + $this->register(Enchantments::FLAME(), [Tags::BOW], []); + $this->register(Enchantments::INFINITY(), [Tags::BOW], []); + $this->register( + Enchantments::MENDING(), + [], + [Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD, + Tags::SHEARS, Tags::FLINT_AND_STEEL, Tags::SHIELD, Tags::CARROT_ON_STICK, Tags::ELYTRA, Tags::BRUSH] + ); + $this->register(Enchantments::VANISHING(), [], [Tags::ALL]); + $this->register(Enchantments::SWIFT_SNEAK(), [], [Tags::LEGGINGS]); + } + + /** + * @param string[] $primaryItemTags + * @param string[] $secondaryItemTags + */ + public function register(Enchantment $enchantment, array $primaryItemTags, array $secondaryItemTags) : void{ + $this->enchantments[spl_object_id($enchantment)] = $enchantment; + $this->setPrimaryItemTags($enchantment, $primaryItemTags); + $this->setSecondaryItemTags($enchantment, $secondaryItemTags); + } + + public function unregister(Enchantment $enchantment) : void{ + unset($this->enchantments[spl_object_id($enchantment)]); + unset($this->primaryItemTags[spl_object_id($enchantment)]); + unset($this->secondaryItemTags[spl_object_id($enchantment)]); + } + + public function unregisterAll() : void{ + $this->enchantments = []; + $this->primaryItemTags = []; + $this->secondaryItemTags = []; + } + + public function isRegistered(Enchantment $enchantment) : bool{ + return isset($this->enchantments[spl_object_id($enchantment)]); + } + + /** + * Returns primary compatibility tags for the specified enchantment. + * + * An item matching at least one of these tags (or its descendents) can be: + * - Offered this enchantment in an enchanting table + * - Enchanted by any means allowed by secondary tags + * + * @return string[] + */ + public function getPrimaryItemTags(Enchantment $enchantment) : array{ + return $this->primaryItemTags[spl_object_id($enchantment)] ?? []; + } + + /** + * @param string[] $tags + */ + public function setPrimaryItemTags(Enchantment $enchantment, array $tags) : void{ + if(!$this->isRegistered($enchantment)){ + throw new \LogicException("Cannot set primary item tags for non-registered enchantment"); + } + $this->primaryItemTags[spl_object_id($enchantment)] = array_values($tags); + } + + /** + * Returns secondary compatibility tags for the specified enchantment. + * + * An item matching at least one of these tags (or its descendents) can be: + * - Combined with an enchanted book with this enchantment in an anvil + * - Obtained as loot with this enchantment, e.g. fishing, treasure chests, mob equipment, etc. + * + * @return string[] + */ + public function getSecondaryItemTags(Enchantment $enchantment) : array{ + return $this->secondaryItemTags[spl_object_id($enchantment)] ?? []; + } + + /** + * @param string[] $tags + */ + public function setSecondaryItemTags(Enchantment $enchantment, array $tags) : void{ + if(!$this->isRegistered($enchantment)){ + throw new \LogicException("Cannot set secondary item tags for non-registered enchantment"); + } + $this->secondaryItemTags[spl_object_id($enchantment)] = array_values($tags); + } + + /** + * Returns enchantments that can be applied to the specified item in an enchanting table (primary only). + * + * @return Enchantment[] + */ + public function getPrimaryEnchantmentsForItem(Item $item) : array{ + $itemTags = $item->getEnchantmentTags(); + if(count($itemTags) === 0 || $item->hasEnchantments()){ + return []; + } + + return array_filter( + $this->enchantments, + fn(Enchantment $e) => TagRegistry::getInstance()->isTagArrayIntersection($this->getPrimaryItemTags($e), $itemTags) + ); + } + + /** + * Returns all available enchantments compatible with the item. + * + * Warning: not suitable for obtaining enchantments for an enchanting table + * (use {@link AvailableEnchantmentRegistry::getPrimaryEnchantmentsForItem()} for that). + * + * @return Enchantment[] + */ + public function getAllEnchantmentsForItem(Item $item) : array{ + if(count($item->getEnchantmentTags()) === 0){ + return []; + } + + return array_filter( + $this->enchantments, + fn(Enchantment $enchantment) => $this->isAvailableForItem($enchantment, $item) + ); + } + + /** + * Returns whether the specified enchantment can be applied to the particular item. + * + * Warning: not suitable for checking the availability of enchantment for an enchanting table. + */ + public function isAvailableForItem(Enchantment $enchantment, Item $item) : bool{ + $itemTags = $item->getEnchantmentTags(); + $tagRegistry = TagRegistry::getInstance(); + + return $tagRegistry->isTagArrayIntersection($this->getPrimaryItemTags($enchantment), $itemTags) || + $tagRegistry->isTagArrayIntersection($this->getSecondaryItemTags($enchantment), $itemTags); + } + + /** + * @return Enchantment[] + */ + public function getAll() : array{ + return $this->enchantments; + } +} diff --git a/src/item/enchantment/EnchantingHelper.php b/src/item/enchantment/EnchantingHelper.php new file mode 100644 index 000000000..fb4e8f27c --- /dev/null +++ b/src/item/enchantment/EnchantingHelper.php @@ -0,0 +1,233 @@ +getTypeId() === ItemTypeIds::BOOK ? Items::ENCHANTED_BOOK() : clone $item; + + foreach($enchantments as $enchantment){ + $resultItem->addEnchantment($enchantment); + } + + return $resultItem; + } + + /** + * @return EnchantingOption[] + */ + public static function generateOptions(Position $tablePos, Item $input, int $seed) : array{ + if($input->isNull() || $input->hasEnchantments()){ + return []; + } + + $random = new Random($seed); + + $bookshelfCount = self::countBookshelves($tablePos); + $baseRequiredLevel = $random->nextRange(1, 8) + ($bookshelfCount >> 1) + $random->nextRange(0, $bookshelfCount); + $topRequiredLevel = (int) floor(max($baseRequiredLevel / 3, 1)); + $middleRequiredLevel = (int) floor($baseRequiredLevel * 2 / 3 + 1); + $bottomRequiredLevel = max($baseRequiredLevel, $bookshelfCount * 2); + + return [ + self::createOption($random, $input, $topRequiredLevel), + self::createOption($random, $input, $middleRequiredLevel), + self::createOption($random, $input, $bottomRequiredLevel), + ]; + } + + private static function countBookshelves(Position $tablePos) : int{ + $bookshelfCount = 0; + $world = $tablePos->getWorld(); + + for($x = -2; $x <= 2; $x++){ + for($z = -2; $z <= 2; $z++){ + // We only check blocks at a distance of 2 blocks from the enchanting table + if(abs($x) !== 2 && abs($z) !== 2){ + continue; + } + + // Ensure the space between the bookshelf stack at this X/Z and the enchanting table is empty + for($y = 0; $y <= 1; $y++){ + // Calculate the coordinates of the space between the bookshelf and the enchanting table + $spaceX = max(min($x, 1), -1); + $spaceZ = max(min($z, 1), -1); + $spaceBlock = $world->getBlock($tablePos->add($spaceX, $y, $spaceZ)); + if($spaceBlock->getTypeId() !== BlockTypeIds::AIR){ + continue 2; + } + } + + // Finally, check the number of bookshelves at the current position + for($y = 0; $y <= 1; $y++){ + $block = $world->getBlock($tablePos->add($x, $y, $z)); + if($block->getTypeId() === BlockTypeIds::BOOKSHELF){ + $bookshelfCount++; + if($bookshelfCount === self::MAX_BOOKSHELF_COUNT){ + return $bookshelfCount; + } + } + } + } + } + + return $bookshelfCount; + } + + private static function createOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantingOption{ + $enchantingPower = $requiredXpLevel; + + $enchantability = $inputItem->getEnchantability(); + $enchantingPower = $enchantingPower + $random->nextRange(0, $enchantability >> 2) + $random->nextRange(0, $enchantability >> 2) + 1; + // Random bonus for enchanting power between 0.85 and 1.15 + $bonus = 1 + ($random->nextFloat() + $random->nextFloat() - 1) * 0.15; + $enchantingPower = (int) round($enchantingPower * $bonus); + + $resultEnchantments = []; + $availableEnchantments = self::getAvailableEnchantments($enchantingPower, $inputItem); + + $lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments); + if($lastEnchantment !== null){ + $resultEnchantments[] = $lastEnchantment; + + // With probability (power + 1) / 50, continue adding enchantments + while($random->nextFloat() <= ($enchantingPower + 1) / 50){ + // Remove from the list of available enchantments anything that conflicts + // with previously-chosen enchantments + $availableEnchantments = array_filter( + $availableEnchantments, + function(EnchantmentInstance $e) use ($lastEnchantment){ + return $e->getType() !== $lastEnchantment->getType() && + $e->getType()->isCompatibleWith($lastEnchantment->getType()); + } + ); + + $lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments); + if($lastEnchantment === null){ + break; + } + + $resultEnchantments[] = $lastEnchantment; + $enchantingPower >>= 1; + } + } + + return new EnchantingOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments); + } + + /** + * @return EnchantmentInstance[] + */ + private static function getAvailableEnchantments(int $enchantingPower, Item $item) : array{ + $list = []; + + foreach(EnchantmentRegistry::getInstance()->getPrimaryEnchantmentsForItem($item) as $enchantment){ + for($lvl = $enchantment->getMaxLevel(); $lvl > 0; $lvl--){ + if($enchantingPower >= $enchantment->getMinEnchantingPower($lvl) && + $enchantingPower <= $enchantment->getMaxEnchantingPower($lvl) + ){ + $list[] = new EnchantmentInstance($enchantment, $lvl); + break; + } + } + } + + return $list; + } + + /** + * @param EnchantmentInstance[] $enchantments + */ + private static function getRandomWeightedEnchantment(Random $random, array $enchantments) : ?EnchantmentInstance{ + if(count($enchantments) === 0){ + return null; + } + + $totalWeight = 0; + foreach($enchantments as $enchantment){ + $totalWeight += $enchantment->getType()->getRarity(); + } + + $result = null; + $randomWeight = $random->nextRange(1, $totalWeight); + + foreach($enchantments as $enchantment){ + $randomWeight -= $enchantment->getType()->getRarity(); + + if($randomWeight <= 0){ + $result = $enchantment; + break; + } + } + + return $result; + } + + private static function getRandomOptionName(Random $random) : string{ + $name = ""; + for($i = $random->nextRange(5, 15); $i > 0; $i--){ + $name .= chr($random->nextRange(ord("a"), ord("z"))); + } + + return $name; + } +} diff --git a/src/item/enchantment/EnchantingOption.php b/src/item/enchantment/EnchantingOption.php new file mode 100644 index 000000000..2bedb0cc4 --- /dev/null +++ b/src/item/enchantment/EnchantingOption.php @@ -0,0 +1,66 @@ +requiredXpLevel; + } + + /** + * Returns the name that will be translated to the 'Standard Galactic Alphabet' client-side. + * This can be any arbitrary text string, since the vanilla client cannot read the text anyway. + * Example: 'bless creature range free'. + */ + public function getDisplayName() : string{ + return $this->displayName; + } + + /** + * Returns the enchantments that will be applied to the item when this option is clicked. + * + * @return EnchantmentInstance[] + */ + public function getEnchantments() : array{ + return $this->enchantments; + } +} diff --git a/src/item/enchantment/Enchantment.php b/src/item/enchantment/Enchantment.php index c53dfab7c..948f4648b 100644 --- a/src/item/enchantment/Enchantment.php +++ b/src/item/enchantment/Enchantment.php @@ -23,9 +23,13 @@ declare(strict_types=1); namespace pocketmine\item\enchantment; +use DaveRandom\CallbackValidator\CallbackType; +use DaveRandom\CallbackValidator\ParameterType; +use DaveRandom\CallbackValidator\ReturnType; use pocketmine\lang\Translatable; use pocketmine\utils\NotCloneable; use pocketmine\utils\NotSerializable; +use pocketmine\utils\Utils; /** * Manages enchantment type data. @@ -34,13 +38,32 @@ class Enchantment{ use NotCloneable; use NotSerializable; + /** @var \Closure(int $level) : int $minEnchantingPower */ + private \Closure $minEnchantingPower; + + /** + * @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower + * + * @param int $primaryItemFlags @deprecated + * @param int $secondaryItemFlags @deprecated + * @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange) + */ public function __construct( private Translatable|string $name, private int $rarity, private int $primaryItemFlags, private int $secondaryItemFlags, - private int $maxLevel - ){} + private int $maxLevel, + ?\Closure $minEnchantingPower = null, + private int $enchantingPowerRange = 50 + ){ + $this->minEnchantingPower = $minEnchantingPower ?? fn(int $level) : int => 1; + + Utils::validateCallableSignature(new CallbackType( + new ReturnType("int"), + new ParameterType("level", "int") + ), $this->minEnchantingPower); + } /** * Returns a translation key for this enchantment's name. @@ -58,6 +81,9 @@ class Enchantment{ /** * Returns a bitset indicating what item types can have this item applied from an enchanting table. + * + * @deprecated + * @see AvailableEnchantmentRegistry::getPrimaryItemTags() */ public function getPrimaryItemFlags() : int{ return $this->primaryItemFlags; @@ -66,6 +92,9 @@ class Enchantment{ /** * Returns a bitset indicating what item types cannot have this item applied from an enchanting table, but can from * an anvil. + * + * @deprecated + * @see AvailableEnchantmentRegistry::getSecondaryItemTags() */ public function getSecondaryItemFlags() : int{ return $this->secondaryItemFlags; @@ -73,6 +102,9 @@ class Enchantment{ /** * Returns whether this enchantment can apply to the item type from an enchanting table. + * + * @deprecated + * @see AvailableEnchantmentRegistry */ public function hasPrimaryItemType(int $flag) : bool{ return ($this->primaryItemFlags & $flag) !== 0; @@ -80,6 +112,9 @@ class Enchantment{ /** * Returns whether this enchantment can apply to the item type from an anvil, if it is not a primary item. + * + * @deprecated + * @see AvailableEnchantmentRegistry */ public function hasSecondaryItemType(int $flag) : bool{ return ($this->secondaryItemFlags & $flag) !== 0; @@ -92,5 +127,34 @@ class Enchantment{ return $this->maxLevel; } - //TODO: methods for min/max XP cost bounds based on enchantment level (not needed yet - enchanting is client-side) + /** + * Returns whether this enchantment can be applied to the item along with the given enchantment. + */ + public function isCompatibleWith(Enchantment $other) : bool{ + return IncompatibleEnchantmentRegistry::getInstance()->areCompatible($this, $other); + } + + /** + * Returns the minimum enchanting power value required for the particular level of the enchantment + * to be available in an enchanting table. + * + * Enchanting power is a random value based on the number of bookshelves around an enchanting table + * and the enchantability of the item being enchanted. It is only used when determining the available + * enchantments for the enchantment options. + */ + public function getMinEnchantingPower(int $level) : int{ + return ($this->minEnchantingPower)($level); + } + + /** + * Returns the maximum enchanting power value allowed for the particular level of the enchantment + * to be available in an enchanting table. + * + * Enchanting power is a random value based on the number of bookshelves around an enchanting table + * and the enchantability of the item being enchanted. It is only used when determining the available + * enchantments for the enchantment options. + */ + public function getMaxEnchantingPower(int $level) : int{ + return $this->getMinEnchantingPower($level) + $this->enchantingPowerRange; + } } diff --git a/src/item/enchantment/IncompatibleEnchantmentGroups.php b/src/item/enchantment/IncompatibleEnchantmentGroups.php new file mode 100644 index 000000000..74574562c --- /dev/null +++ b/src/item/enchantment/IncompatibleEnchantmentGroups.php @@ -0,0 +1,34 @@ +> + * @var true[][] + */ + private array $incompatibilityMap = []; + + private function __construct(){ + $this->register(Groups::PROTECTION, [Enchantments::PROTECTION(), Enchantments::FIRE_PROTECTION(), Enchantments::BLAST_PROTECTION(), Enchantments::PROJECTILE_PROTECTION()]); + $this->register(Groups::BOW_INFINITE, [Enchantments::INFINITY(), Enchantments::MENDING()]); + $this->register(Groups::BLOCK_DROPS, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]); + } + + /** + * Register incompatibility for an enchantment group. + * + * All enchantments belonging to the same group are incompatible with each other, + * i.e. they cannot be added together on the same item. + * + * @param Enchantment[] $enchantments + */ + public function register(string $tag, array $enchantments) : void{ + foreach($enchantments as $enchantment){ + $this->incompatibilityMap[spl_object_id($enchantment)][$tag] = true; + } + } + + /** + * Unregister incompatibility for some enchantments of a particular group. + * + * @param Enchantment[] $enchantments + */ + public function unregister(string $tag, array $enchantments) : void{ + foreach($enchantments as $enchantment){ + unset($this->incompatibilityMap[spl_object_id($enchantment)][$tag]); + } + } + + /** + * Unregister incompatibility for all enchantments of a particular group. + */ + public function unregisterAll(string $tag) : void{ + foreach($this->incompatibilityMap as $id => $tags){ + unset($this->incompatibilityMap[$id][$tag]); + } + } + + /** + * Returns whether two enchantments can be applied to the same item. + */ + public function areCompatible(Enchantment $first, Enchantment $second) : bool{ + $firstIncompatibilities = $this->incompatibilityMap[spl_object_id($first)] ?? []; + $secondIncompatibilities = $this->incompatibilityMap[spl_object_id($second)] ?? []; + return count(array_intersect_key($firstIncompatibilities, $secondIncompatibilities)) === 0; + } +} diff --git a/src/item/enchantment/ItemEnchantmentTagRegistry.php b/src/item/enchantment/ItemEnchantmentTagRegistry.php new file mode 100644 index 000000000..210cd8e86 --- /dev/null +++ b/src/item/enchantment/ItemEnchantmentTagRegistry.php @@ -0,0 +1,190 @@ +> + * @var string[][] + */ + private array $tagMap = []; + + private function __construct(){ + $this->register(Tags::ARMOR, [Tags::HELMET, Tags::CHESTPLATE, Tags::LEGGINGS, Tags::BOOTS]); + $this->register(Tags::SHIELD); + $this->register(Tags::SWORD); + $this->register(Tags::TRIDENT); + $this->register(Tags::BOW); + $this->register(Tags::CROSSBOW); + $this->register(Tags::SHEARS); + $this->register(Tags::FLINT_AND_STEEL); + $this->register(Tags::BLOCK_TOOLS, [Tags::AXE, Tags::PICKAXE, Tags::SHOVEL, Tags::HOE]); + $this->register(Tags::FISHING_ROD); + $this->register(Tags::CARROT_ON_STICK); + $this->register(Tags::COMPASS); + $this->register(Tags::MASK); + $this->register(Tags::ELYTRA); + $this->register(Tags::BRUSH); + $this->register(Tags::WEAPONS, [ + Tags::SWORD, + Tags::TRIDENT, + Tags::BOW, + Tags::CROSSBOW, + Tags::BLOCK_TOOLS, + ]); + } + + /** + * Register tag and its nested tags. + * + * @param string[] $nestedTags + */ + public function register(string $tag, array $nestedTags = []) : void{ + $this->assertNotInternalTag($tag); + + foreach($nestedTags as $nestedTag){ + if(!isset($this->tagMap[$nestedTag])){ + $this->register($nestedTag); + } + $this->tagMap[$tag][] = $nestedTag; + } + + if(!isset($this->tagMap[$tag])){ + $this->tagMap[$tag] = []; + $this->tagMap[Tags::ALL][] = $tag; + } + } + + public function unregister(string $tag) : void{ + if(!isset($this->tagMap[$tag])){ + return; + } + $this->assertNotInternalTag($tag); + + unset($this->tagMap[$tag]); + + foreach(Utils::stringifyKeys($this->tagMap) as $key => $nestedTags){ + if(($nestedKey = array_search($tag, $nestedTags, true)) !== false){ + unset($this->tagMap[$key][$nestedKey]); + } + } + } + + /** + * Remove specified nested tags. + * + * @param string[] $nestedTags + */ + public function removeNested(string $tag, array $nestedTags) : void{ + $this->assertNotInternalTag($tag); + $this->tagMap[$tag] = array_diff($this->tagMap[$tag], $nestedTags); + } + + /** + * Returns nested tags of a particular tag. + * + * @return string[] + */ + public function getNested(string $tag) : array{ + return $this->tagMap[$tag] ?? []; + } + + /** + * @param string[] $firstTags + * @param string[] $secondTags + */ + public function isTagArrayIntersection(array $firstTags, array $secondTags) : bool{ + if(count($firstTags) === 0 || count($secondTags) === 0){ + return false; + } + + $firstLeafTags = $this->getLeafTagsForArray($firstTags); + $secondLeafTags = $this->getLeafTagsForArray($secondTags); + + return count(array_intersect($firstLeafTags, $secondLeafTags)) !== 0; + } + + /** + * Returns all tags that are recursively nested within each tag in the array and do not have any nested tags. + * + * @param string[] $tags + * + * @return string[] + */ + private function getLeafTagsForArray(array $tags) : array{ + $leafTagArrays = []; + foreach($tags as $tag){ + $leafTagArrays[] = $this->getLeafTags($tag); + } + return array_unique(array_merge(...$leafTagArrays)); + } + + /** + * Returns all tags that are recursively nested within the given tag and do not have any nested tags. + * + * @return string[] + */ + private function getLeafTags(string $tag) : array{ + $result = []; + $tagsToHandle = [$tag]; + + while(count($tagsToHandle) !== 0){ + $currentTag = array_shift($tagsToHandle); + $nestedTags = $this->getNested($currentTag); + + if(count($nestedTags) === 0){ + $result[] = $currentTag; + }else{ + $tagsToHandle = array_merge($tagsToHandle, $nestedTags); + } + } + + return $result; + } + + private function assertNotInternalTag(string $tag) : void{ + if($tag === Tags::ALL){ + throw new \InvalidArgumentException( + "Cannot perform any operations on the internal item enchantment tag '$tag'" + ); + } + } +} diff --git a/src/item/enchantment/ItemEnchantmentTags.php b/src/item/enchantment/ItemEnchantmentTags.php new file mode 100644 index 000000000..50abf8db3 --- /dev/null +++ b/src/item/enchantment/ItemEnchantmentTags.php @@ -0,0 +1,57 @@ +typeModifier = $typeModifier; if($applicableDamageTypes !== null){ diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index ac2f5c57e..779098c77 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -59,47 +59,224 @@ final class VanillaEnchantments{ use RegistryTrait; protected static function setup() : void{ - self::register("PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_all(), Rarity::COMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 0.75, null)); - self::register("FIRE_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_fire(), Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.25, [ - EntityDamageEvent::CAUSE_FIRE, - EntityDamageEvent::CAUSE_FIRE_TICK, - EntityDamageEvent::CAUSE_LAVA - //TODO: check fireballs - ])); - self::register("FEATHER_FALLING", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_fall(), Rarity::UNCOMMON, ItemFlags::FEET, ItemFlags::NONE, 4, 2.5, [ - EntityDamageEvent::CAUSE_FALL - ])); - self::register("BLAST_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_explosion(), Rarity::RARE, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.5, [ - EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, - EntityDamageEvent::CAUSE_ENTITY_EXPLOSION - ])); - self::register("PROJECTILE_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_projectile(), Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.5, [ - EntityDamageEvent::CAUSE_PROJECTILE - ])); - self::register("THORNS", new Enchantment(KnownTranslationFactory::enchantment_thorns(), Rarity::MYTHIC, ItemFlags::TORSO, ItemFlags::HEAD | ItemFlags::LEGS | ItemFlags::FEET, 3)); - self::register("RESPIRATION", new Enchantment(KnownTranslationFactory::enchantment_oxygen(), Rarity::RARE, ItemFlags::HEAD, ItemFlags::NONE, 3)); + self::register("PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_all(), + Rarity::COMMON, + 0, + 0, + 4, + 0.75, + null, + fn(int $level) : int => 11 * ($level - 1) + 1, + 20 + )); + self::register("FIRE_PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_fire(), + Rarity::UNCOMMON, + 0, + 0, + 4, + 1.25, + [ + EntityDamageEvent::CAUSE_FIRE, + EntityDamageEvent::CAUSE_FIRE_TICK, + EntityDamageEvent::CAUSE_LAVA + //TODO: check fireballs + ], + fn(int $level) : int => 8 * ($level - 1) + 10, + 12 + )); + self::register("FEATHER_FALLING", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_fall(), + Rarity::UNCOMMON, + 0, + 0, + 4, + 2.5, + [ + EntityDamageEvent::CAUSE_FALL + ], + fn(int $level) : int => 6 * ($level - 1) + 5, + 10 + )); + self::register("BLAST_PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_explosion(), + Rarity::RARE, + 0, + 0, + 4, + 1.5, + [ + EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, + EntityDamageEvent::CAUSE_ENTITY_EXPLOSION + ], + fn(int $level) : int => 8 * ($level - 1) + 5, + 12 + )); + self::register("PROJECTILE_PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_projectile(), + Rarity::UNCOMMON, + 0, + 0, + 4, + 1.5, + [ + EntityDamageEvent::CAUSE_PROJECTILE + ], + fn(int $level) : int => 6 * ($level - 1) + 3, + 15 + )); + self::register("THORNS", new Enchantment( + KnownTranslationFactory::enchantment_thorns(), + Rarity::MYTHIC, + 0, + 0, + 3, + fn(int $level) : int => 20 * ($level - 1) + 10, + 50 + )); + self::register("RESPIRATION", new Enchantment( + KnownTranslationFactory::enchantment_oxygen(), + Rarity::RARE, + 0, + 0, + 3, + fn(int $level) : int => 10 * $level, + 30 + )); - self::register("SHARPNESS", new SharpnessEnchantment(KnownTranslationFactory::enchantment_damage_all(), Rarity::COMMON, ItemFlags::SWORD, ItemFlags::AXE, 5)); - //TODO: smite, bane of arthropods (these don't make sense now because their applicable mobs don't exist yet) + self::register("SHARPNESS", new SharpnessEnchantment( + KnownTranslationFactory::enchantment_damage_all(), + Rarity::COMMON, + 0, + 0, + 5, + fn(int $level) : int => 11 * ($level - 1) + 1, + 20 + )); + self::register("KNOCKBACK", new KnockbackEnchantment( + KnownTranslationFactory::enchantment_knockback(), + Rarity::UNCOMMON, + 0, + 0, + 2, + fn(int $level) : int => 20 * ($level - 1) + 5, + 50 + )); + self::register("FIRE_ASPECT", new FireAspectEnchantment( + KnownTranslationFactory::enchantment_fire(), + Rarity::RARE, + 0, + 0, + 2, + fn(int $level) : int => 20 * ($level - 1) + 10, + 50 + )); + //TODO: smite, bane of arthropods, looting (these don't make sense now because their applicable mobs don't exist yet) - self::register("KNOCKBACK", new KnockbackEnchantment(KnownTranslationFactory::enchantment_knockback(), Rarity::UNCOMMON, ItemFlags::SWORD, ItemFlags::NONE, 2)); - self::register("FIRE_ASPECT", new FireAspectEnchantment(KnownTranslationFactory::enchantment_fire(), Rarity::RARE, ItemFlags::SWORD, ItemFlags::NONE, 2)); + self::register("EFFICIENCY", new Enchantment( + KnownTranslationFactory::enchantment_digging(), + Rarity::COMMON, + 0, + 0, + 5, + fn(int $level) : int => 10 * ($level - 1) + 1, + 50 + )); + self::register("FORTUNE", new Enchantment( + KnownTranslationFactory::enchantment_lootBonusDigger(), + Rarity::RARE, + 0, + 0, + 3, + fn(int $level) : int => 9 * ($level - 1) + 15, + 50 + )); + self::register("SILK_TOUCH", new Enchantment( + KnownTranslationFactory::enchantment_untouching(), + Rarity::MYTHIC, + 0, + 0, + 1, + fn(int $level) : int => 15, + 50 + )); + self::register("UNBREAKING", new Enchantment( + KnownTranslationFactory::enchantment_durability(), + Rarity::UNCOMMON, + 0, + 0, + 3, + fn(int $level) : int => 8 * ($level - 1) + 5, + 50 + )); - self::register("EFFICIENCY", new Enchantment(KnownTranslationFactory::enchantment_digging(), Rarity::COMMON, ItemFlags::DIG, ItemFlags::SHEARS, 5)); - self::register("FORTUNE", new Enchantment(KnownTranslationFactory::enchantment_lootBonusDigger(), Rarity::RARE, ItemFlags::DIG, ItemFlags::NONE, 3)); - self::register("SILK_TOUCH", new Enchantment(KnownTranslationFactory::enchantment_untouching(), Rarity::MYTHIC, ItemFlags::DIG, ItemFlags::SHEARS, 1)); - self::register("UNBREAKING", new Enchantment(KnownTranslationFactory::enchantment_durability(), Rarity::UNCOMMON, ItemFlags::DIG | ItemFlags::ARMOR | ItemFlags::FISHING_ROD | ItemFlags::BOW, ItemFlags::TOOL | ItemFlags::CARROT_STICK | ItemFlags::ELYTRA, 3)); + self::register("POWER", new Enchantment( + KnownTranslationFactory::enchantment_arrowDamage(), + Rarity::COMMON, + 0, + 0, + 5, + fn(int $level) : int => 10 * ($level - 1) + 1, + 15 + )); + self::register("PUNCH", new Enchantment( + KnownTranslationFactory::enchantment_arrowKnockback(), + Rarity::RARE, + 0, + 0, + 2, + fn(int $level) : int => 20 * ($level - 1) + 12, + 25 + )); + self::register("FLAME", new Enchantment( + KnownTranslationFactory::enchantment_arrowFire(), + Rarity::RARE, + 0, + 0, + 1, + fn(int $level) : int => 20, + 30 + )); + self::register("INFINITY", new Enchantment( + KnownTranslationFactory::enchantment_arrowInfinite(), + Rarity::MYTHIC, + 0, + 0, + 1, + fn(int $level) : int => 20, + 30 + )); - self::register("POWER", new Enchantment(KnownTranslationFactory::enchantment_arrowDamage(), Rarity::COMMON, ItemFlags::BOW, ItemFlags::NONE, 5)); - self::register("PUNCH", new Enchantment(KnownTranslationFactory::enchantment_arrowKnockback(), Rarity::RARE, ItemFlags::BOW, ItemFlags::NONE, 2)); - self::register("FLAME", new Enchantment(KnownTranslationFactory::enchantment_arrowFire(), Rarity::RARE, ItemFlags::BOW, ItemFlags::NONE, 1)); - self::register("INFINITY", new Enchantment(KnownTranslationFactory::enchantment_arrowInfinite(), Rarity::MYTHIC, ItemFlags::BOW, ItemFlags::NONE, 1)); + self::register("MENDING", new Enchantment( + KnownTranslationFactory::enchantment_mending(), + Rarity::RARE, + 0, + 0, + 1, + fn(int $level) : int => 25, + 50 + )); - self::register("MENDING", new Enchantment(KnownTranslationFactory::enchantment_mending(), Rarity::RARE, ItemFlags::NONE, ItemFlags::ALL, 1)); + self::register("VANISHING", new Enchantment( + KnownTranslationFactory::enchantment_curse_vanishing(), + Rarity::MYTHIC, + 0, + 0, + 1, + fn(int $level) : int => 25, + 25 + )); - self::register("VANISHING", new Enchantment(KnownTranslationFactory::enchantment_curse_vanishing(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::ALL, 1)); - - self::register("SWIFT_SNEAK", new Enchantment(KnownTranslationFactory::enchantment_swift_sneak(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::LEGS, 3)); + self::register("SWIFT_SNEAK", new Enchantment( + KnownTranslationFactory::enchantment_swift_sneak(), + Rarity::MYTHIC, + 0, + 0, + 3, + fn(int $level) : int => 10 * $level, + 5 + )); } protected static function register(string $name, Enchantment $member) : void{ diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 2ae125d43..ebbd71146 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -35,9 +35,12 @@ use pocketmine\block\inventory\LoomInventory; use pocketmine\block\inventory\SmithingTableInventory; use pocketmine\block\inventory\StonecutterInventory; use pocketmine\crafting\FurnaceType; +use pocketmine\data\bedrock\EnchantmentIdMap; use pocketmine\inventory\Inventory; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\inventory\transaction\InventoryTransaction; +use pocketmine\item\enchantment\EnchantingOption; +use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\network\mcpe\cache\CreativeInventoryCache; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; @@ -46,7 +49,10 @@ use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; use pocketmine\network\mcpe\protocol\InventoryContentPacket; use pocketmine\network\mcpe\protocol\InventorySlotPacket; use pocketmine\network\mcpe\protocol\MobEquipmentPacket; +use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; +use pocketmine\network\mcpe\protocol\types\Enchant; +use pocketmine\network\mcpe\protocol\types\EnchantOption as ProtocolEnchantOption; use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; @@ -59,6 +65,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\ObjectSet; use function array_fill_keys; use function array_keys; +use function array_map; use function array_search; use function count; use function get_class; @@ -104,6 +111,12 @@ class InventoryManager{ private bool $fullSyncRequested = false; + /** @var int[] network recipe ID => enchanting table option index */ + private array $enchantingTableOptions = []; + //TODO: this should be based on the total number of crafting recipes - if there are ever 100k recipes, this will + //conflict with regular recipes + private int $nextEnchantingTableOptionId = 100000; + public function __construct( private Player $player, private NetworkSession $session @@ -383,6 +396,7 @@ class InventoryManager{ throw new AssumptionFailedError("We should not have opened a new window while a window was waiting to be closed"); } $this->pendingCloseWindowId = $this->lastInventoryNetworkId; + $this->enchantingTableOptions = []; } } @@ -630,6 +644,39 @@ class InventoryManager{ $this->session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache($this->player->getCreativeInventory())); } + /** + * @param EnchantingOption[] $options + */ + public function syncEnchantingTableOptions(array $options) : void{ + $protocolOptions = []; + + foreach($options as $index => $option){ + $optionId = $this->nextEnchantingTableOptionId++; + $this->enchantingTableOptions[$optionId] = $index; + + $protocolEnchantments = array_map( + fn(EnchantmentInstance $e) => new Enchant(EnchantmentIdMap::getInstance()->toId($e->getType()), $e->getLevel()), + $option->getEnchantments() + ); + // We don't pay attention to the $slotFlags, $heldActivatedEnchantments and $selfActivatedEnchantments + // as everything works fine without them (perhaps these values are used somehow in the BDS). + $protocolOptions[] = new ProtocolEnchantOption( + $option->getRequiredXpLevel(), + 0, $protocolEnchantments, + [], + [], + $option->getDisplayName(), + $optionId + ); + } + + $this->session->sendDataPacket(PlayerEnchantOptionsPacket::create($protocolOptions)); + } + + public function getEnchantingTableOptionIndex(int $recipeId) : ?int{ + return $this->enchantingTableOptions[$recipeId] ?? null; + } + private function newItemStackId() : int{ return $this->nextItemStackId++; } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index dbd857b0f..5b65ca1b5 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -108,6 +108,7 @@ use pocketmine\utils\BinaryStream; use pocketmine\utils\ObjectSet; use pocketmine\utils\TextFormat; use pocketmine\world\Position; +use pocketmine\YmlServerProperties; use function array_map; use function array_values; use function base64_encode; @@ -406,10 +407,12 @@ class NetworkSession{ $timings->startTiming(); try{ - $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketDecodeEvent::hasHandlers()){ + $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); + $ev->call(); + if($ev->isCancelled()){ + return; + } } $decodeTimings = Timings::getDecodeDataPacketTimings($packet); @@ -429,19 +432,22 @@ class NetworkSession{ $decodeTimings->stopTiming(); } - $ev = new DataPacketReceiveEvent($this, $packet); - $ev->call(); - if(!$ev->isCancelled()){ - $handlerTimings = Timings::getHandleDataPacketTimings($packet); - $handlerTimings->startTiming(); - try{ - if($this->handler === null || !$packet->handle($this->handler)){ - $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); - } - }finally{ - $handlerTimings->stopTiming(); + if(DataPacketReceiveEvent::hasHandlers()){ + $ev = new DataPacketReceiveEvent($this, $packet); + $ev->call(); + if($ev->isCancelled()){ + return; } } + $handlerTimings = Timings::getHandleDataPacketTimings($packet); + $handlerTimings->startTiming(); + try{ + if($this->handler === null || !$packet->handle($this->handler)){ + $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); + } + }finally{ + $handlerTimings->stopTiming(); + } }finally{ $timings->stopTiming(); } @@ -459,12 +465,16 @@ class NetworkSession{ $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); try{ - $ev = new DataPacketSendEvent([$this], [$packet]); - $ev->call(); - if($ev->isCancelled()){ - return false; + if(DataPacketSendEvent::hasHandlers()){ + $ev = new DataPacketSendEvent([$this], [$packet]); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $packets = $ev->getPackets(); + }else{ + $packets = [$packet]; } - $packets = $ev->getPackets(); foreach($packets as $evPacket){ $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket)); @@ -731,7 +741,7 @@ class NetworkSession{ } $this->logger->debug("Xbox Live authenticated: " . ($this->authenticated ? "YES" : "NO")); - $checkXUID = $this->server->getConfigGroup()->getPropertyBool("player.verify-xuid", true); + $checkXUID = $this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::PLAYER_VERIFY_XUID, true); $myXUID = $this->info instanceof XboxLivePlayerInfo ? $this->info->getXuid() : ""; $kickForXUIDMismatch = function(string $xuid) use ($checkXUID, $myXUID) : bool{ if($checkXUID && $myXUID !== $xuid){ diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index 9de3d214a..c200859fd 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -44,12 +44,14 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{ public function broadcastPackets(array $recipients, array $packets) : void{ //TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative //implementation that doesn't fire events - $ev = new DataPacketSendEvent($recipients, $packets); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketSendEvent::hasHandlers()){ + $ev = new DataPacketSendEvent($recipients, $packets); + $ev->call(); + if($ev->isCancelled()){ + return; + } + $packets = $ev->getPackets(); } - $packets = $ev->getPackets(); $compressors = []; diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 10787f84b..a36ae9f40 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -23,11 +23,13 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\handler; +use pocketmine\block\inventory\EnchantInventory; use pocketmine\inventory\Inventory; use pocketmine\inventory\transaction\action\CreateItemAction; use pocketmine\inventory\transaction\action\DestroyItemAction; use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\CraftingTransaction; +use pocketmine\inventory\transaction\EnchantingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionBuilderInventory; @@ -287,7 +289,7 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ private function assertDoingCrafting() : void{ - if(!$this->specialTransaction instanceof CraftingTransaction){ + if(!$this->specialTransaction instanceof CraftingTransaction && !$this->specialTransaction instanceof EnchantingTransaction){ if($this->specialTransaction === null){ throw new ItemStackRequestProcessException("Expected CraftRecipe or CraftRecipeAuto action to precede this action"); }else{ @@ -333,7 +335,16 @@ class ItemStackRequestExecutor{ $this->setNextCreatedItem($item, true); }elseif($action instanceof CraftRecipeStackRequestAction){ - $this->beginCrafting($action->getRecipeId(), 1); + $window = $this->player->getCurrentWindow(); + if($window instanceof EnchantInventory){ + $optionId = $this->inventoryManager->getEnchantingTableOptionIndex($action->getRecipeId()); + if($optionId !== null && ($option = $window->getOption($optionId)) !== null){ + $this->specialTransaction = new EnchantingTransaction($this->player, $option, $optionId + 1); + $this->setNextCreatedItem($window->getOutput($optionId)); + } + }else{ + $this->beginCrafting($action->getRecipeId(), 1); + } }elseif($action instanceof CraftRecipeAutoStackRequestAction){ $this->beginCrafting($action->getRecipeId(), $action->getRepetitions()); }elseif($action instanceof CraftingConsumeInputStackRequestAction){ diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php index 4bf8ffb15..759a992e8 100644 --- a/src/network/mcpe/raklib/RakLibInterface.php +++ b/src/network/mcpe/raklib/RakLibInterface.php @@ -42,6 +42,7 @@ use pocketmine\Server; use pocketmine\thread\ThreadCrashException; use pocketmine\timings\Timings; use pocketmine\utils\Utils; +use pocketmine\YmlServerProperties; use raklib\generic\DisconnectReason; use raklib\generic\SocketException; use raklib\protocol\EncapsulatedPacket; @@ -125,7 +126,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ $threadToMainBuffer, new InternetAddress($ip, $port, $ipV6 ? 6 : 4), $this->rakServerId, - $this->server->getConfigGroup()->getPropertyInt("network.max-mtu-size", 1492), + $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::NETWORK_MAX_MTU_SIZE, 1492), self::MCPE_RAKNET_PROTOCOL_VERSION, $sleeperEntry ); diff --git a/src/network/query/QueryInfo.php b/src/network/query/QueryInfo.php index 2d36ac7e6..cdb1c66db 100644 --- a/src/network/query/QueryInfo.php +++ b/src/network/query/QueryInfo.php @@ -29,6 +29,7 @@ use pocketmine\plugin\Plugin; use pocketmine\Server; use pocketmine\utils\Binary; use pocketmine\utils\Utils; +use pocketmine\YmlServerProperties; use function array_map; use function chr; use function count; @@ -66,7 +67,7 @@ final class QueryInfo{ public function __construct(Server $server){ $this->serverName = $server->getMotd(); - $this->listPlugins = $server->getConfigGroup()->getPropertyBool("settings.query-plugins", true); + $this->listPlugins = $server->getConfigGroup()->getPropertyBool(YmlServerProperties::SETTINGS_QUERY_PLUGINS, true); $this->plugins = $server->getPluginManager()->getPlugins(); $this->players = array_map(fn(Player $p) => $p->getName(), $server->getOnlinePlayers()); diff --git a/src/player/Player.php b/src/player/Player.php index f62485242..2059613ad 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -119,6 +119,7 @@ use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissibleDelegateTrait; use pocketmine\player\chat\StandardChatFormatter; use pocketmine\Server; +use pocketmine\ServerProperties; use pocketmine\timings\Timings; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat; @@ -134,6 +135,7 @@ use pocketmine\world\sound\FireExtinguishSound; use pocketmine\world\sound\ItemBreakSound; use pocketmine\world\sound\Sound; use pocketmine\world\World; +use pocketmine\YmlServerProperties; use Ramsey\Uuid\UuidInterface; use function abs; use function array_filter; @@ -317,8 +319,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $rootPermissions[DefaultPermissions::ROOT_OPERATOR] = true; } $this->perm = new PermissibleBase($rootPermissions); - $this->chunksPerTick = $this->server->getConfigGroup()->getPropertyInt("chunk-sending.per-tick", 4); - $this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt("chunk-sending.spawn-radius", 4) ** 2) * M_PI); + $this->chunksPerTick = $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_PER_TICK, 4); + $this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4) ** 2) * M_PI); $this->chunkSelector = new ChunkSelector(); $this->chunkLoader = new class implements ChunkLoader{}; @@ -583,7 +585,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->viewDistance = $newViewDistance; - $this->spawnThreshold = (int) (min($this->viewDistance, $this->server->getConfigGroup()->getPropertyInt("chunk-sending.spawn-radius", 4)) ** 2 * M_PI); + $this->spawnThreshold = (int) (min($this->viewDistance, $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4)) ** 2 * M_PI); $this->nextChunkOrderRun = 0; @@ -1330,18 +1332,20 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $deltaAngle = abs($this->lastLocation->yaw - $to->yaw) + abs($this->lastLocation->pitch - $to->pitch); if($delta > 0.0001 || $deltaAngle > 1.0){ - $ev = new PlayerMoveEvent($this, $from, $to); + if(PlayerMoveEvent::hasHandlers()){ + $ev = new PlayerMoveEvent($this, $from, $to); - $ev->call(); + $ev->call(); - if($ev->isCancelled()){ - $this->revertMovement($from); - return; - } + if($ev->isCancelled()){ + $this->revertMovement($from); + return; + } - if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination - $this->teleport($ev->getTo()); - return; + if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination + $this->teleport($ev->getTo()); + return; + } } $this->lastLocation = $to; @@ -1842,7 +1846,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if(!$this->canInteract($entity->getLocation(), self::MAX_REACH_DISTANCE_ENTITY_INTERACTION)){ $this->logger->debug("Cancelled attack of entity " . $entity->getId() . " due to not currently being interactable"); $ev->cancel(); - }elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool("pvp"))){ + }elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool(ServerProperties::PVP))){ $ev->cancel(); } diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php index 23a98b943..1b537a11c 100644 --- a/src/plugin/PluginBase.php +++ b/src/plugin/PluginBase.php @@ -30,19 +30,16 @@ use pocketmine\command\PluginCommand; use pocketmine\lang\KnownTranslationFactory; use pocketmine\scheduler\TaskScheduler; use pocketmine\Server; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Config; use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; +use function copy; use function count; use function dirname; -use function fclose; use function file_exists; -use function fopen; use function mkdir; use function rtrim; use function str_contains; -use function stream_copy_to_stream; use function strtolower; use function trim; use const DIRECTORY_SEPARATOR; @@ -50,6 +47,8 @@ use const DIRECTORY_SEPARATOR; abstract class PluginBase implements Plugin, CommandExecutor{ private bool $isEnabled = false; + private string $resourceFolder; + private ?Config $config = null; private string $configFile; @@ -67,6 +66,8 @@ abstract class PluginBase implements Plugin, CommandExecutor{ $this->dataFolder = rtrim($dataFolder, "/" . DIRECTORY_SEPARATOR) . "/"; //TODO: this is accessed externally via reflection, not unused $this->file = rtrim($file, "/" . DIRECTORY_SEPARATOR) . "/"; + $this->resourceFolder = Path::join($this->file, "resources") . "/"; + $this->configFile = Path::join($this->dataFolder, "config.yml"); $prefix = $this->getDescription()->getPrefix(); @@ -209,6 +210,27 @@ abstract class PluginBase implements Plugin, CommandExecutor{ } /** + * Returns the path to the folder where the plugin's embedded resource files are usually located. + * Note: This is NOT the same as the data folder. The files in this folder should be considered read-only. + */ + public function getResourceFolder() : string{ + return $this->resourceFolder; + } + + /** + * Returns the full path to a data file in the plugin's resources folder. + * This path can be used with standard PHP functions like fopen() or file_get_contents(). + * + * Note: Any path returned by this function should be considered READ-ONLY. + */ + public function getResourcePath(string $filename) : string{ + return Path::join($this->getResourceFolder(), $filename); + } + + /** + * @deprecated Prefer using standard PHP functions with {@link PluginBase::getResourcePath()}, like + * file_get_contents() or fopen(). + * * Gets an embedded resource on the plugin file. * WARNING: You must close the resource given using fclose() * @@ -226,26 +248,21 @@ abstract class PluginBase implements Plugin, CommandExecutor{ return false; } - $out = Path::join($this->dataFolder, $filename); - if(file_exists($out) && !$replace){ + $source = Path::join($this->resourceFolder, $filename); + if(!file_exists($source)){ return false; } - if(($resource = $this->getResource($filename)) === null){ + $destination = Path::join($this->dataFolder, $filename); + if(file_exists($destination) && !$replace){ return false; } - if(!file_exists(dirname($out))){ - mkdir(dirname($out), 0755, true); + if(!file_exists(dirname($destination))){ + mkdir(dirname($destination), 0755, true); } - $fp = fopen($out, "wb"); - if($fp === false) throw new AssumptionFailedError("fopen() should not fail with wb flags"); - - $ret = stream_copy_to_stream($resource, $fp) > 0; - fclose($fp); - fclose($resource); - return $ret; + return copy($source, $destination); } /** diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 6429ac06b..2df6750de 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -154,6 +154,13 @@ class ResourcePackManager{ return $this->serverForceResources; } + /** + * Sets whether players must accept resource packs in order to join. + */ + public function setResourcePacksRequired(bool $value) : void{ + $this->serverForceResources = $value; + } + /** * Returns an array of resource packs in use, sorted in order of priority. * @return ResourcePack[] diff --git a/src/stats/SendUsageTask.php b/src/stats/SendUsageTask.php index 08c17c73a..d218774b5 100644 --- a/src/stats/SendUsageTask.php +++ b/src/stats/SendUsageTask.php @@ -31,6 +31,7 @@ use pocketmine\utils\Internet; use pocketmine\utils\Process; use pocketmine\utils\Utils; use pocketmine\VersionInfo; +use pocketmine\YmlServerProperties; use Ramsey\Uuid\Uuid; use function array_map; use function array_values; @@ -57,7 +58,7 @@ class SendUsageTask extends AsyncTask{ * @phpstan-param array $playerList */ public function __construct(Server $server, int $type, array $playerList = []){ - $endpoint = "http://" . $server->getConfigGroup()->getPropertyString("anonymous-statistics.host", "stats.pocketmine.net") . "/"; + $endpoint = "http://" . $server->getConfigGroup()->getPropertyString(YmlServerProperties::ANONYMOUS_STATISTICS_HOST, "stats.pocketmine.net") . "/"; $data = []; $data["uniqueServerId"] = $server->getServerUniqueId()->toString(); diff --git a/src/updater/UpdateChecker.php b/src/updater/UpdateChecker.php index ddb0f4d66..4a3ee3444 100644 --- a/src/updater/UpdateChecker.php +++ b/src/updater/UpdateChecker.php @@ -27,6 +27,7 @@ use pocketmine\event\server\UpdateNotifyEvent; use pocketmine\Server; use pocketmine\utils\VersionString; use pocketmine\VersionInfo; +use pocketmine\YmlServerProperties; use function date; use function strtolower; use function ucfirst; @@ -43,7 +44,7 @@ class UpdateChecker{ $this->logger = new \PrefixedLogger($server->getLogger(), "Update Checker"); $this->endpoint = "http://$endpoint/api/"; - if($server->getConfigGroup()->getPropertyBool("auto-updater.enabled", true)){ + if($server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_UPDATER_ENABLED, true)){ $this->doCheck(); } } @@ -59,7 +60,7 @@ class UpdateChecker{ $this->checkUpdate($updateInfo); if($this->hasUpdate()){ (new UpdateNotifyEvent($this))->call(); - if($this->server->getConfigGroup()->getPropertyBool("auto-updater.on-update.warn-console", true)){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_UPDATER_ON_UPDATE_WARN_CONSOLE, true)){ $this->showConsoleUpdate(); } }else{ @@ -157,7 +158,7 @@ class UpdateChecker{ * Returns the channel used for update checking (stable, beta, dev) */ public function getChannel() : string{ - return strtolower($this->server->getConfigGroup()->getPropertyString("auto-updater.preferred-channel", "stable")); + return strtolower($this->server->getConfigGroup()->getPropertyString(YmlServerProperties::AUTO_UPDATER_PREFERRED_CHANNEL, "stable")); } /** diff --git a/src/utils/StringToTParser.php b/src/utils/StringToTParser.php index 0166e2a28..d98153ac1 100644 --- a/src/utils/StringToTParser.php +++ b/src/utils/StringToTParser.php @@ -58,6 +58,21 @@ abstract class StringToTParser{ $this->callbackMap[$this->reprocess($alias)] = $callback; } + /** + * Registers a new alias for an existing known alias. + */ + public function registerAlias(string $existing, string $alias) : void{ + $existingKey = $this->reprocess($existing); + if(!isset($this->callbackMap[$existingKey])){ + throw new \InvalidArgumentException("Cannot register new alias for unknown existing alias \"$existing\""); + } + $newKey = $this->reprocess($alias); + if(isset($this->callbackMap[$newKey])){ + throw new \InvalidArgumentException("Alias \"$newKey\" is already registered"); + } + $this->callbackMap[$newKey] = $this->callbackMap[$existingKey]; + } + /** * Tries to parse the specified string into a corresponding instance of T. * @phpstan-return T|null diff --git a/src/world/World.php b/src/world/World.php index b09304742..903cc60e2 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -52,6 +52,7 @@ use pocketmine\event\world\ChunkLoadEvent; use pocketmine\event\world\ChunkPopulateEvent; use pocketmine\event\world\ChunkUnloadEvent; use pocketmine\event\world\SpawnChangeEvent; +use pocketmine\event\world\WorldDifficultyChangeEvent; use pocketmine\event\world\WorldDisplayNameChangeEvent; use pocketmine\event\world\WorldParticleEvent; use pocketmine\event\world\WorldSaveEvent; @@ -103,6 +104,7 @@ use pocketmine\world\particle\Particle; use pocketmine\world\sound\BlockPlaceSound; use pocketmine\world\sound\Sound; use pocketmine\world\utils\SubChunkExplorer; +use pocketmine\YmlServerProperties; use function abs; use function array_filter; use function array_key_exists; @@ -200,6 +202,11 @@ class World implements ChunkManager{ * @phpstan-var array> */ private array $blockCache = []; + /** + * @var AxisAlignedBB[][][] chunkHash => [relativeBlockHash => AxisAlignedBB[]] + * @phpstan-var array>> + */ + private array $blockCollisionBoxCache = []; private int $sendTimeTicker = 0; @@ -507,14 +514,14 @@ class World implements ChunkManager{ $this->time = $this->provider->getWorldData()->getTime(); $cfg = $this->server->getConfigGroup(); - $this->chunkTickRadius = min($this->server->getViewDistance(), max(0, $cfg->getPropertyInt("chunk-ticking.tick-radius", 4))); + $this->chunkTickRadius = min($this->server->getViewDistance(), max(0, $cfg->getPropertyInt(YmlServerProperties::CHUNK_TICKING_TICK_RADIUS, 4))); if($cfg->getPropertyInt("chunk-ticking.per-tick", 40) <= 0){ //TODO: this needs l10n $this->logger->warning("\"chunk-ticking.per-tick\" setting is deprecated, but you've used it to disable chunk ticking. Set \"chunk-ticking.tick-radius\" to 0 in \"pocketmine.yml\" instead."); $this->chunkTickRadius = 0; } - $this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt("chunk-ticking.blocks-per-subchunk-per-tick", self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK); - $this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt("chunk-generation.population-queue-size", 2); + $this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt(YmlServerProperties::CHUNK_TICKING_BLOCKS_PER_SUBCHUNK_PER_TICK, self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK); + $this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2); $this->initRandomTickBlocksFromConfig($cfg); @@ -535,7 +542,7 @@ class World implements ChunkManager{ private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{ $dontTickBlocks = []; $parser = StringToItemParser::getInstance(); - foreach($cfg->getProperty("chunk-ticking.disable-block-ticking", []) as $name){ + foreach($cfg->getProperty(YmlServerProperties::CHUNK_TICKING_DISABLE_BLOCK_TICKING, []) as $name){ $name = (string) $name; $item = $parser->parse($name); if($item !== null){ @@ -646,6 +653,7 @@ class World implements ChunkManager{ $this->provider->close(); $this->blockCache = []; + $this->blockCollisionBoxCache = []; $this->unloaded = true; } @@ -687,16 +695,19 @@ class World implements ChunkManager{ */ public function addSound(Vector3 $pos, Sound $sound, ?array $players = null) : void{ $players ??= $this->getViewersForPosition($pos); - $ev = new WorldSoundEvent($this, $sound, $pos, $players); - $ev->call(); + if(WorldSoundEvent::hasHandlers()){ + $ev = new WorldSoundEvent($this, $sound, $pos, $players); + $ev->call(); + if($ev->isCancelled()){ + return; + } - if($ev->isCancelled()){ - return; + $sound = $ev->getSound(); + $players = $ev->getRecipients(); } - $pk = $ev->getSound()->encode($pos); - $players = $ev->getRecipients(); + $pk = $sound->encode($pos); if(count($pk) > 0){ if($players === $this->getViewersForPosition($pos)){ foreach($pk as $e){ @@ -713,23 +724,26 @@ class World implements ChunkManager{ */ public function addParticle(Vector3 $pos, Particle $particle, ?array $players = null) : void{ $players ??= $this->getViewersForPosition($pos); - $ev = new WorldParticleEvent($this, $particle, $pos, $players); - $ev->call(); + if(WorldParticleEvent::hasHandlers()){ + $ev = new WorldParticleEvent($this, $particle, $pos, $players); + $ev->call(); + if($ev->isCancelled()){ + return; + } - if($ev->isCancelled()){ - return; + $particle = $ev->getParticle(); + $players = $ev->getRecipients(); } - $pk = $ev->getParticle()->encode($pos); - $players = $ev->getRecipients(); + $pk = $particle->encode($pos); if(count($pk) > 0){ if($players === $this->getViewersForPosition($pos)){ foreach($pk as $e){ $this->broadcastPacketToViewers($pos, $e); } }else{ - NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk); + NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $players), $pk); } } } @@ -965,14 +979,17 @@ class World implements ChunkManager{ $block = $replacement; } - $ev = new BlockUpdateEvent($block); - $ev->call(); - if(!$ev->isCancelled()){ - foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){ - $entity->onNearbyBlockChange(); + if(BlockUpdateEvent::hasHandlers()){ + $ev = new BlockUpdateEvent($block); + $ev->call(); + if($ev->isCancelled()){ + continue; } - $block->onNearbyBlockChange(); } + foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){ + $entity->onNearbyBlockChange(); + } + $block->onNearbyBlockChange(); } $this->timings->scheduledBlockUpdates->stopTiming(); @@ -1117,6 +1134,7 @@ class World implements ChunkManager{ public function clearCache(bool $force = false) : void{ if($force){ $this->blockCache = []; + $this->blockCollisionBoxCache = []; }else{ $count = 0; foreach($this->blockCache as $list){ @@ -1126,6 +1144,16 @@ class World implements ChunkManager{ break; } } + + $count = 0; + foreach($this->blockCollisionBoxCache as $list){ + $count += count($list); + if($count > 2048){ + //TODO: Is this really the best logic? + $this->blockCollisionBoxCache = []; + break; + } + } } } @@ -1491,25 +1519,53 @@ class World implements ChunkManager{ return $collides; } + /** + * Returns a list of all block AABBs which overlap the full block area at the given coordinates. + * This checks a padding of 1 block around the coordinates to account for oversized AABBs of blocks like fences. + * Larger AABBs (>= 2 blocks on any axis) are not accounted for. + * + * @return AxisAlignedBB[] + */ + private function getCollisionBoxesForCell(int $x, int $y, int $z) : array{ + $block = $this->getBlockAt($x, $y, $z); + $boxes = $block->getCollisionBoxes(); + + $cellBB = AxisAlignedBB::one()->offset($x, $y, $z); + foreach(Facing::ALL as $facing){ + $extraBoxes = $block->getSide($facing)->getCollisionBoxes(); + foreach($extraBoxes as $extraBox){ + if($extraBox->intersectsWith($cellBB)){ + $boxes[] = $extraBox; + } + } + } + + return $boxes; + } + /** * @return AxisAlignedBB[] * @phpstan-return list */ public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{ - $minX = (int) floor($bb->minX - 1); - $minY = (int) floor($bb->minY - 1); - $minZ = (int) floor($bb->minZ - 1); - $maxX = (int) floor($bb->maxX + 1); - $maxY = (int) floor($bb->maxY + 1); - $maxZ = (int) floor($bb->maxZ + 1); + $minX = (int) floor($bb->minX); + $minY = (int) floor($bb->minY); + $minZ = (int) floor($bb->minZ); + $maxX = (int) floor($bb->maxX); + $maxY = (int) floor($bb->maxY); + $maxZ = (int) floor($bb->maxZ); $collides = []; for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ + $chunkPosHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE); for($y = $minY; $y <= $maxY; ++$y){ - $block = $this->getBlockAt($x, $y, $z); - foreach($block->getCollisionBoxes() as $blockBB){ + $relativeBlockHash = World::chunkBlockHash($x, $y, $z); + + $boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getCollisionBoxesForCell($x, $y, $z); + + foreach($boxes as $blockBB){ if($blockBB->intersectsWith($bb)){ $collides[] = $blockBB; } @@ -1682,14 +1738,10 @@ class World implements ChunkManager{ */ private function getHighestAdjacentLight(int $x, int $y, int $z, \Closure $lightGetter) : int{ $max = 0; - foreach([ - [$x + 1, $y, $z], - [$x - 1, $y, $z], - [$x, $y + 1, $z], - [$x, $y - 1, $z], - [$x, $y, $z + 1], - [$x, $y, $z - 1] - ] as [$x1, $y1, $z1]){ + foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){ + $x1 = $x + $offsetX; + $y1 = $y + $offsetY; + $z1 = $z + $offsetZ; if( !$this->isInWorld($x1, $y1, $z1) || ($chunk = $this->getChunk($x1 >> Chunk::COORD_BIT_SIZE, $z1 >> Chunk::COORD_BIT_SIZE)) === null || @@ -1858,6 +1910,14 @@ class World implements ChunkManager{ $relativeBlockHash = World::chunkBlockHash($x, $y, $z); unset($this->blockCache[$chunkHash][$relativeBlockHash]); + unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]); + //blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the + //caches for those blocks as well + foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){ + $sideChunkPosHash = World::chunkHash(($x + $offsetX) >> Chunk::COORD_BIT_SIZE, ($z + $offsetZ) >> Chunk::COORD_BIT_SIZE); + $sideChunkBlockHash = World::chunkBlockHash($x + $offsetX, $y + $offsetY, $z + $offsetZ); + unset($this->blockCollisionBoxCache[$sideChunkPosHash][$sideChunkBlockHash]); + } if(!isset($this->changedBlocks[$chunkHash])){ $this->changedBlocks[$chunkHash] = []; @@ -2454,6 +2514,7 @@ class World implements ChunkManager{ $this->chunks[$chunkHash] = $chunk; unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); $chunk->setTerrainDirty(); $this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking @@ -2463,7 +2524,9 @@ class World implements ChunkManager{ } if($oldChunk === null){ - (new ChunkLoadEvent($this, $chunkX, $chunkZ, $chunk, true))->call(); + if(ChunkLoadEvent::hasHandlers()){ + (new ChunkLoadEvent($this, $chunkX, $chunkZ, $chunk, true))->call(); + } foreach($this->getChunkListeners($chunkX, $chunkZ) as $listener){ $listener->onChunkLoaded($chunkX, $chunkZ, $chunk); @@ -2733,10 +2796,13 @@ class World implements ChunkManager{ } $this->chunks[$chunkHash] = $chunk; unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); $this->initChunk($x, $z, $chunkData); - (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); + if(ChunkLoadEvent::hasHandlers()){ + (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); + } if(!$this->isChunkInUse($x, $z)){ $this->logger->debug("Newly loaded chunk $x $z has no loaders registered, will be unloaded at next available opportunity"); @@ -2849,12 +2915,14 @@ class World implements ChunkManager{ $chunk = $this->chunks[$chunkHash] ?? null; if($chunk !== null){ - $ev = new ChunkUnloadEvent($this, $x, $z, $chunk); - $ev->call(); - if($ev->isCancelled()){ - $this->timings->doChunkUnload->stopTiming(); + if(ChunkUnloadEvent::hasHandlers()){ + $ev = new ChunkUnloadEvent($this, $x, $z, $chunk); + $ev->call(); + if($ev->isCancelled()){ + $this->timings->doChunkUnload->stopTiming(); - return false; + return false; + } } if($trySave && $this->getAutoSave()){ @@ -2887,6 +2955,7 @@ class World implements ChunkManager{ unset($this->chunks[$chunkHash]); unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); unset($this->registeredTickingChunks[$chunkHash]); $this->markTickingChunkForRecheck($x, $z); @@ -3075,6 +3144,7 @@ class World implements ChunkManager{ if($difficulty < 0 || $difficulty > 3){ throw new \InvalidArgumentException("Invalid difficulty level $difficulty"); } + (new WorldDifficultyChangeEvent($this, $this->getDifficulty(), $difficulty))->call(); $this->provider->getWorldData()->setDifficulty($difficulty); foreach($this->players as $player){ @@ -3309,7 +3379,9 @@ class World implements ChunkManager{ } if(($oldChunk === null || !$oldChunk->isPopulated()) && $chunk->isPopulated()){ - (new ChunkPopulateEvent($this, $x, $z, $chunk))->call(); + if(ChunkPopulateEvent::hasHandlers()){ + (new ChunkPopulateEvent($this, $x, $z, $chunk))->call(); + } foreach($this->getChunkListeners($x, $z) as $listener){ $listener->onChunkPopulated($x, $z, $chunk); diff --git a/src/world/generator/GeneratorManager.php b/src/world/generator/GeneratorManager.php index 180450b72..291ea91de 100644 --- a/src/world/generator/GeneratorManager.php +++ b/src/world/generator/GeneratorManager.php @@ -52,9 +52,9 @@ final class GeneratorManager{ } }); $this->addGenerator(Normal::class, "normal", fn() => null); - $this->addGenerator(Normal::class, "default", fn() => null); - $this->addGenerator(Nether::class, "hell", fn() => null); + $this->addAlias("normal", "default"); $this->addGenerator(Nether::class, "nether", fn() => null); + $this->addAlias("nether", "hell"); } /** @@ -80,6 +80,22 @@ final class GeneratorManager{ $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator); } + /** + * Aliases an already-registered generator name to another name. Useful if you want to map a generator name to an + * existing generator without having to replicate the parameters. + */ + public function addAlias(string $name, string $alias) : void{ + $name = strtolower($name); + $alias = strtolower($alias); + if(!isset($this->list[$name])){ + throw new \InvalidArgumentException("Alias \"$name\" is not assigned"); + } + if(isset($this->list[$alias])){ + throw new \InvalidArgumentException("Alias \"$alias\" is already assigned"); + } + $this->list[$alias] = $this->list[$name]; + } + /** * Returns a list of names for registered generators. * diff --git a/src/world/light/LightUpdate.php b/src/world/light/LightUpdate.php index 1a82bdcef..58ac0a9dc 100644 --- a/src/world/light/LightUpdate.php +++ b/src/world/light/LightUpdate.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\light; +use pocketmine\math\Facing; use pocketmine\world\format\LightArray; use pocketmine\world\format\SubChunk; use pocketmine\world\utils\SubChunkExplorer; @@ -33,15 +34,6 @@ use function max; //TODO: make light updates asynchronous abstract class LightUpdate{ - private const ADJACENTS = [ - [ 1, 0, 0], - [-1, 0, 0], - [ 0, 1, 0], - [ 0, -1, 0], - [ 0, 0, 1], - [ 0, 0, -1] - ]; - public const BASE_LIGHT_FILTER = 1; /** @@ -78,7 +70,7 @@ abstract class LightUpdate{ protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{ $adjacent = 0; - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ if(($adjacent = max($adjacent, $this->getEffectiveLight($x + $ox, $y + $oy, $z + $oz))) === 15){ break; } @@ -123,7 +115,7 @@ abstract class LightUpdate{ $touched++; [$x, $y, $z, $oldAdjacentLight] = $context->removalQueue->dequeue(); - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; @@ -163,7 +155,7 @@ abstract class LightUpdate{ continue; } - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; diff --git a/src/world/sound/PressurePlateActivateSound.php b/src/world/sound/PressurePlateActivateSound.php new file mode 100644 index 000000000..fac24e285 --- /dev/null +++ b/src/world/sound/PressurePlateActivateSound.php @@ -0,0 +1,46 @@ +getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()) + )]; + } +} diff --git a/src/world/sound/PressurePlateDeactivateSound.php b/src/world/sound/PressurePlateDeactivateSound.php new file mode 100644 index 000000000..895bb8b8a --- /dev/null +++ b/src/world/sound/PressurePlateDeactivateSound.php @@ -0,0 +1,46 @@ +getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()) + )]; + } +} diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 2e9fa0555..c12010564 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -1 +1 @@ -{"knownStates":{"???":[2624010],"Acacia Button":[2560272,2560273,2560274,2560275,2560276,2560277,2560280,2560281,2560282,2560283,2560284,2560285],"Acacia Door":[2560512,2560513,2560514,2560515,2560516,2560517,2560518,2560519,2560520,2560521,2560522,2560523,2560524,2560525,2560526,2560527,2560528,2560529,2560530,2560531,2560532,2560533,2560534,2560535,2560536,2560537,2560538,2560539,2560540,2560541,2560542,2560543],"Acacia Fence":[2560787],"Acacia Fence Gate":[2561040,2561041,2561042,2561043,2561044,2561045,2561046,2561047,2561048,2561049,2561050,2561051,2561052,2561053,2561054,2561055],"Acacia Leaves":[2561300,2561301,2561302,2561303],"Acacia Log":[2561554,2561555,2561556,2561557,2561558,2561559],"Acacia Planks":[2561815],"Acacia Pressure Plate":[2562072,2562073],"Acacia Sapling":[2562328,2562329],"Acacia Sign":[2562576,2562577,2562578,2562579,2562580,2562581,2562582,2562583,2562584,2562585,2562586,2562587,2562588,2562589,2562590,2562591],"Acacia Slab":[2562841,2562842,2562843],"Acacia Stairs":[2563096,2563097,2563098,2563099,2563100,2563101,2563102,2563103],"Acacia Trapdoor":[2563344,2563345,2563346,2563347,2563348,2563349,2563350,2563351,2563352,2563353,2563354,2563355,2563356,2563357,2563358,2563359],"Acacia Wall Sign":[2563612,2563613,2563614,2563615],"Acacia Wood":[2563866,2563867,2563868,2563869,2563870,2563871],"Actinium":[2594197],"Activator Rail":[2564128,2564129,2564130,2564131,2564132,2564133,2564136,2564137,2564138,2564139,2564140,2564141],"Air":[2560016],"All Sided Mushroom Stem":[2564385],"Allium":[2564642],"Aluminum":[2594454],"Americium":[2594711],"Amethyst":[2698284],"Ancient Debris":[2698541],"Andesite":[2564899],"Andesite Slab":[2565156,2565157,2565158],"Andesite Stairs":[2565408,2565409,2565410,2565411,2565412,2565413,2565414,2565415],"Andesite Wall":[2565632,2565633,2565634,2565635,2565636,2565637,2565638,2565639,2565640,2565641,2565642,2565643,2565644,2565645,2565646,2565647,2565648,2565649,2565650,2565651,2565652,2565653,2565654,2565655,2565656,2565657,2565658,2565659,2565660,2565661,2565662,2565663,2565664,2565665,2565666,2565667,2565668,2565669,2565670,2565671,2565672,2565673,2565674,2565675,2565676,2565677,2565678,2565679,2565680,2565681,2565682,2565683,2565684,2565685,2565686,2565687,2565688,2565689,2565690,2565691,2565692,2565693,2565694,2565695,2565728,2565729,2565730,2565731,2565732,2565733,2565734,2565735,2565736,2565737,2565738,2565739,2565740,2565741,2565742,2565743,2565750,2565760,2565761,2565762,2565763,2565764,2565765,2565766,2565767,2565768,2565769,2565770,2565771,2565772,2565773,2565774,2565775,2565776,2565777,2565778,2565779,2565780,2565781,2565782,2565783,2565784,2565785,2565786,2565787,2565788,2565789,2565790,2565791,2565792,2565793,2565794,2565795,2565796,2565797,2565798,2565799,2565800,2565801,2565802,2565803,2565804,2565805,2565806,2565807,2565808,2565809,2565810,2565811,2565812,2565813,2565814,2565815,2565816,2565817,2565818,2565819,2565820,2565821,2565822,2565823,2565856,2565857,2565858,2565859,2565860,2565861,2565862,2565863,2565864,2565865,2565866,2565867,2565868,2565869,2565870,2565871,2565878],"Antimony":[2594968],"Anvil":[2565921,2565922,2565923,2565925,2565926,2565927,2565929,2565930,2565931,2565933,2565934,2565935],"Argon":[2595225],"Arsenic":[2595482],"Astatine":[2595739],"Azalea Leaves":[2735804,2735805,2735806,2735807],"Azure Bluet":[2566184],"Bamboo":[2566432,2566433,2566435,2566436,2566437,2566439,2566440,2566441,2566443,2566444,2566445,2566447],"Bamboo Sapling":[2566698,2566699],"Banner":[2566912,2566913,2566914,2566915,2566916,2566917,2566918,2566919,2566920,2566921,2566922,2566923,2566924,2566925,2566926,2566927,2566928,2566929,2566930,2566931,2566932,2566933,2566934,2566935,2566936,2566937,2566938,2566939,2566940,2566941,2566942,2566943,2566944,2566945,2566946,2566947,2566948,2566949,2566950,2566951,2566952,2566953,2566954,2566955,2566956,2566957,2566958,2566959,2566960,2566961,2566962,2566963,2566964,2566965,2566966,2566967,2566968,2566969,2566970,2566971,2566972,2566973,2566974,2566975,2566976,2566977,2566978,2566979,2566980,2566981,2566982,2566983,2566984,2566985,2566986,2566987,2566988,2566989,2566990,2566991,2566992,2566993,2566994,2566995,2566996,2566997,2566998,2566999,2567000,2567001,2567002,2567003,2567004,2567005,2567006,2567007,2567008,2567009,2567010,2567011,2567012,2567013,2567014,2567015,2567016,2567017,2567018,2567019,2567020,2567021,2567022,2567023,2567024,2567025,2567026,2567027,2567028,2567029,2567030,2567031,2567032,2567033,2567034,2567035,2567036,2567037,2567038,2567039,2567040,2567041,2567042,2567043,2567044,2567045,2567046,2567047,2567048,2567049,2567050,2567051,2567052,2567053,2567054,2567055,2567056,2567057,2567058,2567059,2567060,2567061,2567062,2567063,2567064,2567065,2567066,2567067,2567068,2567069,2567070,2567071,2567072,2567073,2567074,2567075,2567076,2567077,2567078,2567079,2567080,2567081,2567082,2567083,2567084,2567085,2567086,2567087,2567088,2567089,2567090,2567091,2567092,2567093,2567094,2567095,2567096,2567097,2567098,2567099,2567100,2567101,2567102,2567103,2567104,2567105,2567106,2567107,2567108,2567109,2567110,2567111,2567112,2567113,2567114,2567115,2567116,2567117,2567118,2567119,2567120,2567121,2567122,2567123,2567124,2567125,2567126,2567127,2567128,2567129,2567130,2567131,2567132,2567133,2567134,2567135,2567136,2567137,2567138,2567139,2567140,2567141,2567142,2567143,2567144,2567145,2567146,2567147,2567148,2567149,2567150,2567151,2567152,2567153,2567154,2567155,2567156,2567157,2567158,2567159,2567160,2567161,2567162,2567163,2567164,2567165,2567166,2567167],"Barium":[2595996],"Barrel":[2567200,2567201,2567204,2567205,2567206,2567207,2567208,2567209,2567212,2567213,2567214,2567215],"Barrier":[2567469],"Basalt":[2698796,2698798,2698799],"Beacon":[2567726],"Bed Block":[2567936,2567937,2567938,2567939,2567940,2567941,2567942,2567943,2567944,2567945,2567946,2567947,2567948,2567949,2567950,2567951,2567952,2567953,2567954,2567955,2567956,2567957,2567958,2567959,2567960,2567961,2567962,2567963,2567964,2567965,2567966,2567967,2567968,2567969,2567970,2567971,2567972,2567973,2567974,2567975,2567976,2567977,2567978,2567979,2567980,2567981,2567982,2567983,2567984,2567985,2567986,2567987,2567988,2567989,2567990,2567991,2567992,2567993,2567994,2567995,2567996,2567997,2567998,2567999,2568000,2568001,2568002,2568003,2568004,2568005,2568006,2568007,2568008,2568009,2568010,2568011,2568012,2568013,2568014,2568015,2568016,2568017,2568018,2568019,2568020,2568021,2568022,2568023,2568024,2568025,2568026,2568027,2568028,2568029,2568030,2568031,2568032,2568033,2568034,2568035,2568036,2568037,2568038,2568039,2568040,2568041,2568042,2568043,2568044,2568045,2568046,2568047,2568048,2568049,2568050,2568051,2568052,2568053,2568054,2568055,2568056,2568057,2568058,2568059,2568060,2568061,2568062,2568063,2568064,2568065,2568066,2568067,2568068,2568069,2568070,2568071,2568072,2568073,2568074,2568075,2568076,2568077,2568078,2568079,2568080,2568081,2568082,2568083,2568084,2568085,2568086,2568087,2568088,2568089,2568090,2568091,2568092,2568093,2568094,2568095,2568096,2568097,2568098,2568099,2568100,2568101,2568102,2568103,2568104,2568105,2568106,2568107,2568108,2568109,2568110,2568111,2568112,2568113,2568114,2568115,2568116,2568117,2568118,2568119,2568120,2568121,2568122,2568123,2568124,2568125,2568126,2568127,2568128,2568129,2568130,2568131,2568132,2568133,2568134,2568135,2568136,2568137,2568138,2568139,2568140,2568141,2568142,2568143,2568144,2568145,2568146,2568147,2568148,2568149,2568150,2568151,2568152,2568153,2568154,2568155,2568156,2568157,2568158,2568159,2568160,2568161,2568162,2568163,2568164,2568165,2568166,2568167,2568168,2568169,2568170,2568171,2568172,2568173,2568174,2568175,2568176,2568177,2568178,2568179,2568180,2568181,2568182,2568183,2568184,2568185,2568186,2568187,2568188,2568189,2568190,2568191],"Bedrock":[2568240,2568241],"Beetroot Block":[2568496,2568497,2568498,2568499,2568500,2568501,2568502,2568503],"Bell":[2568752,2568753,2568754,2568755,2568756,2568757,2568758,2568759,2568760,2568761,2568762,2568763,2568764,2568765,2568766,2568767],"Berkelium":[2596253],"Beryllium":[2596510],"Big Dripleaf":[2741200,2741201,2741202,2741203,2741204,2741205,2741206,2741207,2741208,2741209,2741210,2741211,2741212,2741213,2741214,2741215],"Big Dripleaf Stem":[2741460,2741461,2741462,2741463],"Birch Button":[2569008,2569009,2569010,2569011,2569014,2569015,2569016,2569017,2569018,2569019,2569022,2569023],"Birch Door":[2569248,2569249,2569250,2569251,2569252,2569253,2569254,2569255,2569256,2569257,2569258,2569259,2569260,2569261,2569262,2569263,2569264,2569265,2569266,2569267,2569268,2569269,2569270,2569271,2569272,2569273,2569274,2569275,2569276,2569277,2569278,2569279],"Birch Fence":[2569525],"Birch Fence Gate":[2569776,2569777,2569778,2569779,2569780,2569781,2569782,2569783,2569784,2569785,2569786,2569787,2569788,2569789,2569790,2569791],"Birch Leaves":[2570036,2570037,2570038,2570039],"Birch Log":[2570296,2570297,2570298,2570299,2570300,2570301],"Birch Planks":[2570553],"Birch Pressure Plate":[2570810,2570811],"Birch Sapling":[2571066,2571067],"Birch Sign":[2571312,2571313,2571314,2571315,2571316,2571317,2571318,2571319,2571320,2571321,2571322,2571323,2571324,2571325,2571326,2571327],"Birch Slab":[2571580,2571581,2571583],"Birch Stairs":[2571832,2571833,2571834,2571835,2571836,2571837,2571838,2571839],"Birch Trapdoor":[2572080,2572081,2572082,2572083,2572084,2572085,2572086,2572087,2572088,2572089,2572090,2572091,2572092,2572093,2572094,2572095],"Birch Wall Sign":[2572352,2572353,2572354,2572355],"Birch Wood":[2572608,2572609,2572610,2572611,2572612,2572613],"Bismuth":[2596767],"Blackstone":[2699569],"Blackstone Slab":[2699824,2699826,2699827],"Blackstone Stairs":[2700080,2700081,2700082,2700083,2700084,2700085,2700086,2700087],"Blackstone Wall":[2700288,2700289,2700290,2700291,2700292,2700293,2700294,2700295,2700296,2700297,2700298,2700299,2700300,2700301,2700302,2700303,2700304,2700305,2700306,2700307,2700308,2700309,2700310,2700311,2700312,2700313,2700314,2700315,2700316,2700317,2700318,2700319,2700320,2700321,2700322,2700323,2700324,2700325,2700326,2700327,2700328,2700329,2700330,2700331,2700332,2700333,2700334,2700335,2700336,2700337,2700338,2700339,2700340,2700341,2700342,2700343,2700344,2700345,2700346,2700347,2700348,2700349,2700350,2700351,2700388,2700400,2700401,2700402,2700403,2700404,2700405,2700406,2700407,2700408,2700409,2700410,2700411,2700412,2700413,2700414,2700415,2700416,2700417,2700418,2700419,2700420,2700421,2700422,2700423,2700424,2700425,2700426,2700427,2700428,2700429,2700430,2700431,2700432,2700433,2700434,2700435,2700436,2700437,2700438,2700439,2700440,2700441,2700442,2700443,2700444,2700445,2700446,2700447,2700448,2700449,2700450,2700451,2700452,2700453,2700454,2700455,2700456,2700457,2700458,2700459,2700460,2700461,2700462,2700463,2700464,2700465,2700466,2700467,2700468,2700469,2700470,2700471,2700472,2700473,2700474,2700475,2700476,2700477,2700478,2700479,2700516,2700528,2700529,2700530,2700531,2700532,2700533,2700534,2700535,2700536,2700537,2700538,2700539,2700540,2700541,2700542,2700543],"Blast Furnace":[2573120,2573121,2573122,2573123,2573124,2573125,2573126,2573127],"Blue Ice":[2573637],"Blue Orchid":[2573894],"Blue Torch":[2574146,2574147,2574148,2574149,2574150],"Bohrium":[2597024],"Bone Block":[2574408,2574409,2574410],"Bookshelf":[2574665],"Boron":[2597281],"Brewing Stand":[2574920,2574921,2574922,2574923,2574924,2574925,2574926,2574927],"Brick Slab":[2575177,2575178,2575179],"Brick Stairs":[2575432,2575433,2575434,2575435,2575436,2575437,2575438,2575439],"Brick Wall":[2575616,2575617,2575618,2575619,2575620,2575621,2575622,2575623,2575624,2575625,2575626,2575627,2575628,2575629,2575630,2575631,2575645,2575680,2575681,2575682,2575683,2575684,2575685,2575686,2575687,2575688,2575689,2575690,2575691,2575692,2575693,2575694,2575695,2575696,2575697,2575698,2575699,2575700,2575701,2575702,2575703,2575704,2575705,2575706,2575707,2575708,2575709,2575710,2575711,2575712,2575713,2575714,2575715,2575716,2575717,2575718,2575719,2575720,2575721,2575722,2575723,2575724,2575725,2575726,2575727,2575728,2575729,2575730,2575731,2575732,2575733,2575734,2575735,2575736,2575737,2575738,2575739,2575740,2575741,2575742,2575743,2575744,2575745,2575746,2575747,2575748,2575749,2575750,2575751,2575752,2575753,2575754,2575755,2575756,2575757,2575758,2575759,2575773,2575808,2575809,2575810,2575811,2575812,2575813,2575814,2575815,2575816,2575817,2575818,2575819,2575820,2575821,2575822,2575823,2575824,2575825,2575826,2575827,2575828,2575829,2575830,2575831,2575832,2575833,2575834,2575835,2575836,2575837,2575838,2575839,2575840,2575841,2575842,2575843,2575844,2575845,2575846,2575847,2575848,2575849,2575850,2575851,2575852,2575853,2575854,2575855,2575856,2575857,2575858,2575859,2575860,2575861,2575862,2575863,2575864,2575865,2575866,2575867,2575868,2575869,2575870,2575871],"Bricks":[2575950],"Bromine":[2597538],"Brown Mushroom":[2576464],"Brown Mushroom Block":[2576720,2576721,2576722,2576723,2576724,2576725,2576726,2576727,2576728,2576729,2576731],"Cactus":[2576976,2576977,2576978,2576979,2576980,2576981,2576982,2576983,2576984,2576985,2576986,2576987,2576988,2576989,2576990,2576991],"Cadmium":[2597795],"Cake":[2577232,2577233,2577234,2577235,2577237,2577238,2577239],"Cake With Candle":[2729638,2729639],"Cake With Dyed Candle":[2729888,2729889,2729890,2729891,2729892,2729893,2729894,2729895,2729896,2729897,2729898,2729899,2729900,2729901,2729902,2729903,2729904,2729905,2729906,2729907,2729908,2729909,2729910,2729911,2729912,2729913,2729914,2729915,2729916,2729917,2729918,2729919],"Calcite":[2704709],"Calcium":[2598052],"Californium":[2598309],"Candle":[2729120,2729121,2729122,2729123,2729124,2729125,2729126,2729127],"Carbon":[2598566],"Carpet":[2577488,2577489,2577490,2577491,2577492,2577493,2577494,2577495,2577496,2577497,2577498,2577499,2577500,2577501,2577502,2577503],"Carrot Block":[2577744,2577745,2577746,2577747,2577748,2577749,2577750,2577751],"Cartography Table":[2730666],"Carved Pumpkin":[2578004,2578005,2578006,2578007],"Cauldron":[2731694],"Cave Vines":[2736512,2736513,2736514,2736515,2736516,2736517,2736518,2736519,2736520,2736521,2736522,2736523,2736524,2736525,2736526,2736527,2736528,2736529,2736530,2736531,2736532,2736533,2736534,2736535,2736536,2736537,2736544,2736545,2736546,2736547,2736548,2736549,2736550,2736551,2736552,2736553,2736554,2736555,2736556,2736557,2736558,2736559,2736560,2736561,2736562,2736563,2736564,2736565,2736566,2736567,2736568,2736569,2736576,2736577,2736578,2736579,2736580,2736581,2736582,2736583,2736584,2736585,2736586,2736587,2736588,2736589,2736590,2736591,2736592,2736593,2736594,2736595,2736596,2736597,2736598,2736599,2736600,2736601,2736608,2736609,2736610,2736611,2736612,2736613,2736614,2736615,2736616,2736617,2736618,2736619,2736620,2736621,2736622,2736623,2736624,2736625,2736626,2736627,2736628,2736629,2736630,2736631,2736632,2736633],"Cerium":[2598823],"Cesium":[2599080],"Chain":[2734776,2734778,2734779],"Cherry Button":[2737088,2737089,2737090,2737091,2737094,2737095,2737096,2737097,2737098,2737099,2737102,2737103],"Cherry Door":[2737344,2737345,2737346,2737347,2737348,2737349,2737350,2737351,2737352,2737353,2737354,2737355,2737356,2737357,2737358,2737359,2737360,2737361,2737362,2737363,2737364,2737365,2737366,2737367,2737368,2737369,2737370,2737371,2737372,2737373,2737374,2737375],"Cherry Fence":[2737605],"Cherry Fence Gate":[2737856,2737857,2737858,2737859,2737860,2737861,2737862,2737863,2737864,2737865,2737866,2737867,2737868,2737869,2737870,2737871],"Cherry Leaves":[2738116,2738117,2738118,2738119],"Cherry Log":[2738376,2738377,2738378,2738379,2738380,2738381],"Cherry Planks":[2738633],"Cherry Pressure Plate":[2738890,2738891],"Cherry Sign":[2739392,2739393,2739394,2739395,2739396,2739397,2739398,2739399,2739400,2739401,2739402,2739403,2739404,2739405,2739406,2739407],"Cherry Slab":[2739660,2739661,2739663],"Cherry Stairs":[2739912,2739913,2739914,2739915,2739916,2739917,2739918,2739919],"Cherry Trapdoor":[2740160,2740161,2740162,2740163,2740164,2740165,2740166,2740167,2740168,2740169,2740170,2740171,2740172,2740173,2740174,2740175],"Cherry Wall Sign":[2740432,2740433,2740434,2740435],"Cherry Wood":[2740688,2740689,2740690,2740691,2740692,2740693],"Chest":[2578520,2578521,2578522,2578523],"Chiseled Deepslate":[2710106],"Chiseled Nether Bricks":[2710363],"Chiseled Polished Blackstone":[2702139],"Chiseled Quartz Block":[2578776,2578777,2578779],"Chiseled Red Sandstone":[2579034],"Chiseled Sandstone":[2579291],"Chiseled Stone Bricks":[2579548],"Chlorine":[2599337],"Chorus Flower":[2732976,2732977,2732978,2732979,2732982,2732983],"Chorus Plant":[2733236],"Chromium":[2599594],"Clay Block":[2579805],"Coal Block":[2580062],"Coal Ore":[2580319],"Cobalt":[2599851],"Cobbled Deepslate":[2707793],"Cobbled Deepslate Slab":[2708048,2708050,2708051],"Cobbled Deepslate Stairs":[2708304,2708305,2708306,2708307,2708308,2708309,2708310,2708311],"Cobbled Deepslate Wall":[2708484,2708496,2708497,2708498,2708499,2708500,2708501,2708502,2708503,2708504,2708505,2708506,2708507,2708508,2708509,2708510,2708511,2708544,2708545,2708546,2708547,2708548,2708549,2708550,2708551,2708552,2708553,2708554,2708555,2708556,2708557,2708558,2708559,2708560,2708561,2708562,2708563,2708564,2708565,2708566,2708567,2708568,2708569,2708570,2708571,2708572,2708573,2708574,2708575,2708576,2708577,2708578,2708579,2708580,2708581,2708582,2708583,2708584,2708585,2708586,2708587,2708588,2708589,2708590,2708591,2708592,2708593,2708594,2708595,2708596,2708597,2708598,2708599,2708600,2708601,2708602,2708603,2708604,2708605,2708606,2708607,2708612,2708624,2708625,2708626,2708627,2708628,2708629,2708630,2708631,2708632,2708633,2708634,2708635,2708636,2708637,2708638,2708639,2708672,2708673,2708674,2708675,2708676,2708677,2708678,2708679,2708680,2708681,2708682,2708683,2708684,2708685,2708686,2708687,2708688,2708689,2708690,2708691,2708692,2708693,2708694,2708695,2708696,2708697,2708698,2708699,2708700,2708701,2708702,2708703,2708704,2708705,2708706,2708707,2708708,2708709,2708710,2708711,2708712,2708713,2708714,2708715,2708716,2708717,2708718,2708719,2708720,2708721,2708722,2708723,2708724,2708725,2708726,2708727,2708728,2708729,2708730,2708731,2708732,2708733,2708734,2708735],"Cobblestone":[2580576],"Cobblestone Slab":[2580832,2580833,2580835],"Cobblestone Stairs":[2581088,2581089,2581090,2581091,2581092,2581093,2581094,2581095],"Cobblestone Wall":[2581280,2581281,2581282,2581283,2581284,2581285,2581286,2581287,2581288,2581289,2581290,2581291,2581292,2581293,2581294,2581295,2581299,2581312,2581313,2581314,2581315,2581316,2581317,2581318,2581319,2581320,2581321,2581322,2581323,2581324,2581325,2581326,2581327,2581328,2581329,2581330,2581331,2581332,2581333,2581334,2581335,2581336,2581337,2581338,2581339,2581340,2581341,2581342,2581343,2581344,2581345,2581346,2581347,2581348,2581349,2581350,2581351,2581352,2581353,2581354,2581355,2581356,2581357,2581358,2581359,2581360,2581361,2581362,2581363,2581364,2581365,2581366,2581367,2581368,2581369,2581370,2581371,2581372,2581373,2581374,2581375,2581408,2581409,2581410,2581411,2581412,2581413,2581414,2581415,2581416,2581417,2581418,2581419,2581420,2581421,2581422,2581423,2581427,2581440,2581441,2581442,2581443,2581444,2581445,2581446,2581447,2581448,2581449,2581450,2581451,2581452,2581453,2581454,2581455,2581456,2581457,2581458,2581459,2581460,2581461,2581462,2581463,2581464,2581465,2581466,2581467,2581468,2581469,2581470,2581471,2581472,2581473,2581474,2581475,2581476,2581477,2581478,2581479,2581480,2581481,2581482,2581483,2581484,2581485,2581486,2581487,2581488,2581489,2581490,2581491,2581492,2581493,2581494,2581495,2581496,2581497,2581498,2581499,2581500,2581501,2581502,2581503],"Cobweb":[2581604],"Cocoa Block":[2581856,2581857,2581858,2581859,2581860,2581861,2581862,2581863,2581868,2581869,2581870,2581871],"Compound Creator":[2582116,2582117,2582118,2582119],"Concrete":[2582368,2582369,2582370,2582371,2582372,2582373,2582374,2582375,2582376,2582377,2582378,2582379,2582380,2582381,2582382,2582383],"Concrete Powder":[2582624,2582625,2582626,2582627,2582628,2582629,2582630,2582631,2582632,2582633,2582634,2582635,2582636,2582637,2582638,2582639],"Copernicium":[2600365],"Copper":[2600622],"Copper Block":[2728096,2728097,2728098,2728099,2728100,2728101,2728102,2728103],"Copper Ore":[2725012],"Coral":[2582880,2582881,2582882,2582883,2582885,2582888,2582889,2582890,2582891,2582893],"Coral Block":[2583136,2583137,2583138,2583139,2583142,2583144,2583145,2583146,2583147,2583150],"Coral Fan":[2583392,2583393,2583394,2583395,2583399,2583400,2583401,2583402,2583403,2583407,2583408,2583409,2583410,2583411,2583415,2583416,2583417,2583418,2583419,2583423],"Cornflower":[2583660],"Cracked Deepslate Bricks":[2706251],"Cracked Deepslate Tiles":[2707536],"Cracked Nether Bricks":[2710620],"Cracked Polished Blackstone Bricks":[2703424],"Cracked Stone Bricks":[2583917],"Crafting Table":[2584174],"Crimson Button":[2717298,2717299,2717300,2717301,2717302,2717303,2717306,2717307,2717308,2717309,2717310,2717311],"Crimson Door":[2718816,2718817,2718818,2718819,2718820,2718821,2718822,2718823,2718824,2718825,2718826,2718827,2718828,2718829,2718830,2718831,2718832,2718833,2718834,2718835,2718836,2718837,2718838,2718839,2718840,2718841,2718842,2718843,2718844,2718845,2718846,2718847],"Crimson Fence":[2713447],"Crimson Fence Gate":[2719600,2719601,2719602,2719603,2719604,2719605,2719606,2719607,2719608,2719609,2719610,2719611,2719612,2719613,2719614,2719615],"Crimson Hyphae":[2715760,2715761,2715762,2715763,2715764,2715765],"Crimson Planks":[2712676],"Crimson Pressure Plate":[2718072,2718073],"Crimson Sign":[2721152,2721153,2721154,2721155,2721156,2721157,2721158,2721159,2721160,2721161,2721162,2721163,2721164,2721165,2721166,2721167],"Crimson Slab":[2714216,2714218,2714219],"Crimson Stairs":[2720384,2720385,2720386,2720387,2720388,2720389,2720390,2720391],"Crimson Stem":[2714984,2714985,2714988,2714989,2714990,2714991],"Crimson Trapdoor":[2716528,2716529,2716530,2716531,2716532,2716533,2716534,2716535,2716536,2716537,2716538,2716539,2716540,2716541,2716542,2716543],"Crimson Wall Sign":[2721928,2721929,2721930,2721931],"Crying Obsidian":[2727325],"Curium":[2600879],"Cut Copper Block":[2728352,2728353,2728354,2728355,2728356,2728357,2728358,2728359],"Cut Copper Slab Slab":[2728608,2728609,2728610,2728611,2728612,2728613,2728614,2728615,2728616,2728617,2728618,2728619,2728620,2728621,2728622,2728623,2728624,2728625,2728626,2728627,2728628,2728629,2728630,2728631],"Cut Copper Stairs":[2728832,2728833,2728834,2728835,2728836,2728837,2728838,2728839,2728840,2728841,2728842,2728843,2728844,2728845,2728846,2728847,2728848,2728849,2728850,2728851,2728852,2728853,2728854,2728855,2728856,2728857,2728858,2728859,2728860,2728861,2728862,2728863,2728864,2728865,2728866,2728867,2728868,2728869,2728870,2728871,2728872,2728873,2728874,2728875,2728876,2728877,2728878,2728879,2728880,2728881,2728882,2728883,2728884,2728885,2728886,2728887,2728888,2728889,2728890,2728891,2728892,2728893,2728894,2728895],"Cut Red Sandstone":[2584431],"Cut Red Sandstone Slab":[2584688,2584689,2584690],"Cut Sandstone":[2584945],"Cut Sandstone Slab":[2585200,2585202,2585203],"Dandelion":[2585716],"Dark Oak Button":[2585968,2585969,2585972,2585973,2585974,2585975,2585976,2585977,2585980,2585981,2585982,2585983],"Dark Oak Door":[2586208,2586209,2586210,2586211,2586212,2586213,2586214,2586215,2586216,2586217,2586218,2586219,2586220,2586221,2586222,2586223,2586224,2586225,2586226,2586227,2586228,2586229,2586230,2586231,2586232,2586233,2586234,2586235,2586236,2586237,2586238,2586239],"Dark Oak Fence":[2586487],"Dark Oak Fence Gate":[2586736,2586737,2586738,2586739,2586740,2586741,2586742,2586743,2586744,2586745,2586746,2586747,2586748,2586749,2586750,2586751],"Dark Oak Leaves":[2587000,2587001,2587002,2587003],"Dark Oak Log":[2587256,2587257,2587258,2587259,2587262,2587263],"Dark Oak Planks":[2587515],"Dark Oak Pressure Plate":[2587772,2587773],"Dark Oak Sapling":[2588028,2588029],"Dark Oak Sign":[2588272,2588273,2588274,2588275,2588276,2588277,2588278,2588279,2588280,2588281,2588282,2588283,2588284,2588285,2588286,2588287],"Dark Oak Slab":[2588541,2588542,2588543],"Dark Oak Stairs":[2588800,2588801,2588802,2588803,2588804,2588805,2588806,2588807],"Dark Oak Trapdoor":[2589056,2589057,2589058,2589059,2589060,2589061,2589062,2589063,2589064,2589065,2589066,2589067,2589068,2589069,2589070,2589071],"Dark Oak Wall Sign":[2589312,2589313,2589314,2589315],"Dark Oak Wood":[2589568,2589569,2589570,2589571,2589574,2589575],"Dark Prismarine":[2589828],"Dark Prismarine Slab":[2590084,2590085,2590087],"Dark Prismarine Stairs":[2590336,2590337,2590338,2590339,2590340,2590341,2590342,2590343],"Darmstadtium":[2601136],"Daylight Sensor":[2590592,2590593,2590594,2590595,2590596,2590597,2590598,2590599,2590600,2590601,2590602,2590603,2590604,2590605,2590606,2590607,2590608,2590609,2590610,2590611,2590612,2590613,2590614,2590615,2590616,2590617,2590618,2590619,2590620,2590621,2590622,2590623],"Dead Bush":[2590856],"Deepslate":[2704964,2704966,2704967],"Deepslate Brick Slab":[2705480,2705481,2705482],"Deepslate Brick Stairs":[2705736,2705737,2705738,2705739,2705740,2705741,2705742,2705743],"Deepslate Brick Wall":[2705920,2705921,2705922,2705923,2705924,2705925,2705926,2705927,2705928,2705929,2705930,2705931,2705932,2705933,2705934,2705935,2705946,2705984,2705985,2705986,2705987,2705988,2705989,2705990,2705991,2705992,2705993,2705994,2705995,2705996,2705997,2705998,2705999,2706000,2706001,2706002,2706003,2706004,2706005,2706006,2706007,2706008,2706009,2706010,2706011,2706012,2706013,2706014,2706015,2706016,2706017,2706018,2706019,2706020,2706021,2706022,2706023,2706024,2706025,2706026,2706027,2706028,2706029,2706030,2706031,2706032,2706033,2706034,2706035,2706036,2706037,2706038,2706039,2706040,2706041,2706042,2706043,2706044,2706045,2706046,2706047,2706048,2706049,2706050,2706051,2706052,2706053,2706054,2706055,2706056,2706057,2706058,2706059,2706060,2706061,2706062,2706063,2706074,2706112,2706113,2706114,2706115,2706116,2706117,2706118,2706119,2706120,2706121,2706122,2706123,2706124,2706125,2706126,2706127,2706128,2706129,2706130,2706131,2706132,2706133,2706134,2706135,2706136,2706137,2706138,2706139,2706140,2706141,2706142,2706143,2706144,2706145,2706146,2706147,2706148,2706149,2706150,2706151,2706152,2706153,2706154,2706155,2706156,2706157,2706158,2706159,2706160,2706161,2706162,2706163,2706164,2706165,2706166,2706167,2706168,2706169,2706170,2706171,2706172,2706173,2706174,2706175],"Deepslate Bricks":[2705223],"Deepslate Coal Ore":[2722956],"Deepslate Copper Ore":[2724755],"Deepslate Diamond Ore":[2723213],"Deepslate Emerald Ore":[2723470],"Deepslate Gold Ore":[2724498],"Deepslate Iron Ore":[2724241],"Deepslate Lapis Lazuli Ore":[2723727],"Deepslate Redstone Ore":[2723984,2723985],"Deepslate Tile Slab":[2706764,2706765,2706767],"Deepslate Tile Stairs":[2707016,2707017,2707018,2707019,2707020,2707021,2707022,2707023],"Deepslate Tile Wall":[2707200,2707201,2707202,2707203,2707204,2707205,2707206,2707207,2707208,2707209,2707210,2707211,2707212,2707213,2707214,2707215,2707231,2707264,2707265,2707266,2707267,2707268,2707269,2707270,2707271,2707272,2707273,2707274,2707275,2707276,2707277,2707278,2707279,2707280,2707281,2707282,2707283,2707284,2707285,2707286,2707287,2707288,2707289,2707290,2707291,2707292,2707293,2707294,2707295,2707296,2707297,2707298,2707299,2707300,2707301,2707302,2707303,2707304,2707305,2707306,2707307,2707308,2707309,2707310,2707311,2707312,2707313,2707314,2707315,2707316,2707317,2707318,2707319,2707320,2707321,2707322,2707323,2707324,2707325,2707326,2707327,2707328,2707329,2707330,2707331,2707332,2707333,2707334,2707335,2707336,2707337,2707338,2707339,2707340,2707341,2707342,2707343,2707359,2707392,2707393,2707394,2707395,2707396,2707397,2707398,2707399,2707400,2707401,2707402,2707403,2707404,2707405,2707406,2707407,2707408,2707409,2707410,2707411,2707412,2707413,2707414,2707415,2707416,2707417,2707418,2707419,2707420,2707421,2707422,2707423,2707424,2707425,2707426,2707427,2707428,2707429,2707430,2707431,2707432,2707433,2707434,2707435,2707436,2707437,2707438,2707439,2707440,2707441,2707442,2707443,2707444,2707445,2707446,2707447,2707448,2707449,2707450,2707451,2707452,2707453,2707454,2707455],"Deepslate Tiles":[2706508],"Detector Rail":[2591104,2591105,2591106,2591107,2591108,2591109,2591112,2591113,2591114,2591115,2591116,2591117],"Diamond Block":[2591370],"Diamond Ore":[2591627],"Diorite":[2591884],"Diorite Slab":[2592140,2592141,2592143],"Diorite Stairs":[2592392,2592393,2592394,2592395,2592396,2592397,2592398,2592399],"Diorite Wall":[2592512,2592513,2592514,2592515,2592516,2592517,2592518,2592519,2592520,2592521,2592522,2592523,2592524,2592525,2592526,2592527,2592528,2592529,2592530,2592531,2592532,2592533,2592534,2592535,2592536,2592537,2592538,2592539,2592540,2592541,2592542,2592543,2592544,2592545,2592546,2592547,2592548,2592549,2592550,2592551,2592552,2592553,2592554,2592555,2592556,2592557,2592558,2592559,2592560,2592561,2592562,2592563,2592564,2592565,2592566,2592567,2592568,2592569,2592570,2592571,2592572,2592573,2592574,2592575,2592576,2592577,2592578,2592579,2592580,2592581,2592582,2592583,2592584,2592585,2592586,2592587,2592588,2592589,2592590,2592591,2592607,2592640,2592641,2592642,2592643,2592644,2592645,2592646,2592647,2592648,2592649,2592650,2592651,2592652,2592653,2592654,2592655,2592656,2592657,2592658,2592659,2592660,2592661,2592662,2592663,2592664,2592665,2592666,2592667,2592668,2592669,2592670,2592671,2592672,2592673,2592674,2592675,2592676,2592677,2592678,2592679,2592680,2592681,2592682,2592683,2592684,2592685,2592686,2592687,2592688,2592689,2592690,2592691,2592692,2592693,2592694,2592695,2592696,2592697,2592698,2592699,2592700,2592701,2592702,2592703,2592704,2592705,2592706,2592707,2592708,2592709,2592710,2592711,2592712,2592713,2592714,2592715,2592716,2592717,2592718,2592719,2592735],"Dirt":[2592912,2592913,2592914],"Double Tallgrass":[2593168,2593169],"Dragon Egg":[2593426],"Dried Kelp Block":[2593683],"Dubnium":[2601393],"Dyed Candle":[2729344,2729345,2729346,2729347,2729348,2729349,2729350,2729351,2729352,2729353,2729354,2729355,2729356,2729357,2729358,2729359,2729360,2729361,2729362,2729363,2729364,2729365,2729366,2729367,2729368,2729369,2729370,2729371,2729372,2729373,2729374,2729375,2729376,2729377,2729378,2729379,2729380,2729381,2729382,2729383,2729384,2729385,2729386,2729387,2729388,2729389,2729390,2729391,2729392,2729393,2729394,2729395,2729396,2729397,2729398,2729399,2729400,2729401,2729402,2729403,2729404,2729405,2729406,2729407,2729408,2729409,2729410,2729411,2729412,2729413,2729414,2729415,2729416,2729417,2729418,2729419,2729420,2729421,2729422,2729423,2729424,2729425,2729426,2729427,2729428,2729429,2729430,2729431,2729432,2729433,2729434,2729435,2729436,2729437,2729438,2729439,2729440,2729441,2729442,2729443,2729444,2729445,2729446,2729447,2729448,2729449,2729450,2729451,2729452,2729453,2729454,2729455,2729456,2729457,2729458,2729459,2729460,2729461,2729462,2729463,2729464,2729465,2729466,2729467,2729468,2729469,2729470,2729471],"Dyed Shulker Box":[2593936,2593937,2593938,2593939,2593940,2593941,2593942,2593943,2593944,2593945,2593946,2593947,2593948,2593949,2593950,2593951],"Dysprosium":[2601650],"Einsteinium":[2601907],"Element Constructor":[2600108,2600109,2600110,2600111],"Emerald Block":[2624781],"Emerald Ore":[2625038],"Enchanting Table":[2625295],"End Portal Frame":[2625552,2625553,2625554,2625555,2625556,2625557,2625558,2625559],"End Rod":[2625808,2625809,2625810,2625811,2625812,2625813],"End Stone":[2626066],"End Stone Brick Slab":[2626321,2626322,2626323],"End Stone Brick Stairs":[2626576,2626577,2626578,2626579,2626580,2626581,2626582,2626583],"End Stone Brick Wall":[2626816,2626817,2626818,2626819,2626820,2626821,2626822,2626823,2626824,2626825,2626826,2626827,2626828,2626829,2626830,2626831,2626832,2626833,2626834,2626835,2626836,2626837,2626838,2626839,2626840,2626841,2626842,2626843,2626844,2626845,2626846,2626847,2626848,2626849,2626850,2626851,2626852,2626853,2626854,2626855,2626856,2626857,2626858,2626859,2626860,2626861,2626862,2626863,2626864,2626865,2626866,2626867,2626868,2626869,2626870,2626871,2626872,2626873,2626874,2626875,2626876,2626877,2626878,2626879,2626885,2626896,2626897,2626898,2626899,2626900,2626901,2626902,2626903,2626904,2626905,2626906,2626907,2626908,2626909,2626910,2626911,2626944,2626945,2626946,2626947,2626948,2626949,2626950,2626951,2626952,2626953,2626954,2626955,2626956,2626957,2626958,2626959,2626960,2626961,2626962,2626963,2626964,2626965,2626966,2626967,2626968,2626969,2626970,2626971,2626972,2626973,2626974,2626975,2626976,2626977,2626978,2626979,2626980,2626981,2626982,2626983,2626984,2626985,2626986,2626987,2626988,2626989,2626990,2626991,2626992,2626993,2626994,2626995,2626996,2626997,2626998,2626999,2627000,2627001,2627002,2627003,2627004,2627005,2627006,2627007,2627013,2627024,2627025,2627026,2627027,2627028,2627029,2627030,2627031,2627032,2627033,2627034,2627035,2627036,2627037,2627038,2627039],"End Stone Bricks":[2627094],"Ender Chest":[2627348,2627349,2627350,2627351],"Erbium":[2602164],"Europium":[2602421],"Fake Wooden Slab":[2627608,2627609,2627610],"Farmland":[2627864,2627865,2627866,2627867,2627868,2627869,2627870,2627871],"Fermium":[2602678],"Fern":[2628122],"Fire Block":[2628368,2628369,2628370,2628371,2628372,2628373,2628374,2628375,2628376,2628377,2628378,2628379,2628380,2628381,2628382,2628383],"Flerovium":[2602935],"Fletching Table":[2628636],"Flower Pot":[2628893],"Flowering Azalea Leaves":[2736060,2736061,2736062,2736063],"Fluorine":[2603192],"Francium":[2603449],"Froglight":[2734001,2734002,2734003,2734005,2734006,2734007,2734013,2734014,2734015],"Frosted Ice":[2629148,2629149,2629150,2629151],"Furnace":[2629400,2629401,2629402,2629403,2629404,2629405,2629406,2629407],"Gadolinium":[2603706],"Gallium":[2603963],"Germanium":[2604220],"Gilded Blackstone":[2727582],"Glass":[2629664],"Glass Pane":[2629921],"Glazed Terracotta":[2697984,2697985,2697986,2697987,2697988,2697989,2697990,2697991,2697992,2697993,2697994,2697995,2697996,2697997,2697998,2697999,2698000,2698001,2698002,2698003,2698004,2698005,2698006,2698007,2698008,2698009,2698010,2698011,2698012,2698013,2698014,2698015,2698016,2698017,2698018,2698019,2698020,2698021,2698022,2698023,2698024,2698025,2698026,2698027,2698028,2698029,2698030,2698031,2698032,2698033,2698034,2698035,2698036,2698037,2698038,2698039,2698040,2698041,2698042,2698043,2698044,2698045,2698046,2698047],"Glow Item Frame":[2735280,2735281,2735284,2735285,2735286,2735287,2735288,2735289,2735292,2735293,2735294,2735295],"Glow Lichen":[2736832,2736833,2736834,2736835,2736836,2736837,2736838,2736839,2736840,2736841,2736842,2736843,2736844,2736845,2736846,2736847,2736848,2736849,2736850,2736851,2736852,2736853,2736854,2736855,2736856,2736857,2736858,2736859,2736860,2736861,2736862,2736863,2736864,2736865,2736866,2736867,2736868,2736869,2736870,2736871,2736872,2736873,2736874,2736875,2736876,2736877,2736878,2736879,2736880,2736881,2736882,2736883,2736884,2736885,2736886,2736887,2736888,2736889,2736890,2736891,2736892,2736893,2736894,2736895],"Glowing Obsidian":[2630178],"Glowstone":[2630435],"Gold":[2604477],"Gold Block":[2630692],"Gold Ore":[2630949],"Granite":[2631206],"Granite Slab":[2631461,2631462,2631463],"Granite Stairs":[2631720,2631721,2631722,2631723,2631724,2631725,2631726,2631727],"Granite Wall":[2631936,2631937,2631938,2631939,2631940,2631941,2631942,2631943,2631944,2631945,2631946,2631947,2631948,2631949,2631950,2631951,2631952,2631953,2631954,2631955,2631956,2631957,2631958,2631959,2631960,2631961,2631962,2631963,2631964,2631965,2631966,2631967,2631968,2631969,2631970,2631971,2631972,2631973,2631974,2631975,2631976,2631977,2631978,2631979,2631980,2631981,2631982,2631983,2631984,2631985,2631986,2631987,2631988,2631989,2631990,2631991,2631992,2631993,2631994,2631995,2631996,2631997,2631998,2631999,2632032,2632033,2632034,2632035,2632036,2632037,2632038,2632039,2632040,2632041,2632042,2632043,2632044,2632045,2632046,2632047,2632057,2632064,2632065,2632066,2632067,2632068,2632069,2632070,2632071,2632072,2632073,2632074,2632075,2632076,2632077,2632078,2632079,2632080,2632081,2632082,2632083,2632084,2632085,2632086,2632087,2632088,2632089,2632090,2632091,2632092,2632093,2632094,2632095,2632096,2632097,2632098,2632099,2632100,2632101,2632102,2632103,2632104,2632105,2632106,2632107,2632108,2632109,2632110,2632111,2632112,2632113,2632114,2632115,2632116,2632117,2632118,2632119,2632120,2632121,2632122,2632123,2632124,2632125,2632126,2632127,2632160,2632161,2632162,2632163,2632164,2632165,2632166,2632167,2632168,2632169,2632170,2632171,2632172,2632173,2632174,2632175,2632185],"Grass":[2632234],"Grass Path":[2632491],"Gravel":[2632748],"Green Torch":[2633514,2633515,2633516,2633517,2633518],"Hafnium":[2604734],"Hanging Roots":[2730409],"Hardened Clay":[2633776],"Hardened Glass":[2634033],"Hardened Glass Pane":[2634290],"Hassium":[2604991],"Hay Bale":[2634545,2634546,2634547],"Heat Block":[2578263],"Helium":[2605248],"Holmium":[2605505],"Honeycomb Block":[2722699],"Hopper":[2634800,2634801,2634804,2634806,2634807,2634808,2634809,2634812,2634814,2634815],"Hydrogen":[2605762],"Ice":[2635061],"Indium":[2606019],"Infested Chiseled Stone Brick":[2635318],"Infested Cobblestone":[2635575],"Infested Cracked Stone Brick":[2635832],"Infested Mossy Stone Brick":[2636089],"Infested Stone":[2636346],"Infested Stone Brick":[2636603],"Invisible Bedrock":[2637374],"Iodine":[2606276],"Iridium":[2606533],"Iron":[2606790],"Iron Bars":[2637888],"Iron Block":[2637631],"Iron Door":[2638144,2638145,2638146,2638147,2638148,2638149,2638150,2638151,2638152,2638153,2638154,2638155,2638156,2638157,2638158,2638159,2638160,2638161,2638162,2638163,2638164,2638165,2638166,2638167,2638168,2638169,2638170,2638171,2638172,2638173,2638174,2638175],"Iron Ore":[2638402],"Iron Trapdoor":[2638656,2638657,2638658,2638659,2638660,2638661,2638662,2638663,2638664,2638665,2638666,2638667,2638668,2638669,2638670,2638671],"Item Frame":[2638912,2638913,2638916,2638917,2638918,2638919,2638920,2638921,2638924,2638925,2638926,2638927],"Jack o'Lantern":[2647396,2647397,2647398,2647399],"Jukebox":[2639173],"Jungle Button":[2639426,2639427,2639428,2639429,2639430,2639431,2639434,2639435,2639436,2639437,2639438,2639439],"Jungle Door":[2639680,2639681,2639682,2639683,2639684,2639685,2639686,2639687,2639688,2639689,2639690,2639691,2639692,2639693,2639694,2639695,2639696,2639697,2639698,2639699,2639700,2639701,2639702,2639703,2639704,2639705,2639706,2639707,2639708,2639709,2639710,2639711],"Jungle Fence":[2639944],"Jungle Fence Gate":[2640192,2640193,2640194,2640195,2640196,2640197,2640198,2640199,2640200,2640201,2640202,2640203,2640204,2640205,2640206,2640207],"Jungle Leaves":[2640456,2640457,2640458,2640459],"Jungle Log":[2640712,2640713,2640714,2640715,2640718,2640719],"Jungle Planks":[2640972],"Jungle Pressure Plate":[2641228,2641229],"Jungle Sapling":[2641486,2641487],"Jungle Sign":[2641728,2641729,2641730,2641731,2641732,2641733,2641734,2641735,2641736,2641737,2641738,2641739,2641740,2641741,2641742,2641743],"Jungle Slab":[2642000,2642001,2642002],"Jungle Stairs":[2642256,2642257,2642258,2642259,2642260,2642261,2642262,2642263],"Jungle Trapdoor":[2642512,2642513,2642514,2642515,2642516,2642517,2642518,2642519,2642520,2642521,2642522,2642523,2642524,2642525,2642526,2642527],"Jungle Wall Sign":[2642768,2642769,2642770,2642771],"Jungle Wood":[2643024,2643025,2643028,2643029,2643030,2643031],"Krypton":[2607047],"Lab Table":[2643284,2643285,2643286,2643287],"Ladder":[2643540,2643541,2643542,2643543],"Lantern":[2643798,2643799],"Lanthanum":[2607304],"Lapis Lazuli Block":[2644056],"Lapis Lazuli Ore":[2644313],"Large Fern":[2644570,2644571],"Lava":[2644800,2644801,2644802,2644803,2644804,2644805,2644806,2644807,2644808,2644809,2644810,2644811,2644812,2644813,2644814,2644815,2644816,2644817,2644818,2644819,2644820,2644821,2644822,2644823,2644824,2644825,2644826,2644827,2644828,2644829,2644830,2644831],"Lava Cauldron":[2732208,2732209,2732210,2732211,2732212,2732213],"Lawrencium":[2607561],"Lead":[2607818],"Lectern":[2645080,2645081,2645082,2645083,2645084,2645085,2645086,2645087],"Legacy Stonecutter":[2645341],"Lever":[2645584,2645585,2645586,2645587,2645588,2645589,2645590,2645591,2645592,2645593,2645594,2645595,2645596,2645597,2645598,2645599],"Light Block":[2703680,2703681,2703682,2703683,2703684,2703685,2703686,2703687,2703688,2703689,2703690,2703691,2703692,2703693,2703694,2703695],"Lightning Rod":[2727834,2727835,2727836,2727837,2727838,2727839],"Lilac":[2646368,2646369],"Lily Pad":[2646883],"Lily of the Valley":[2646626],"Lithium":[2608075],"Livermorium":[2608332],"Loom":[2647652,2647653,2647654,2647655],"Lutetium":[2608589],"Magma Block":[2648168],"Magnesium":[2608846],"Manganese":[2609103],"Mangrove Button":[2717040,2717041,2717044,2717045,2717046,2717047,2717048,2717049,2717052,2717053,2717054,2717055],"Mangrove Door":[2718560,2718561,2718562,2718563,2718564,2718565,2718566,2718567,2718568,2718569,2718570,2718571,2718572,2718573,2718574,2718575,2718576,2718577,2718578,2718579,2718580,2718581,2718582,2718583,2718584,2718585,2718586,2718587,2718588,2718589,2718590,2718591],"Mangrove Fence":[2713190],"Mangrove Fence Gate":[2719344,2719345,2719346,2719347,2719348,2719349,2719350,2719351,2719352,2719353,2719354,2719355,2719356,2719357,2719358,2719359],"Mangrove Leaves":[2735548,2735549,2735550,2735551],"Mangrove Log":[2714728,2714729,2714732,2714733,2714734,2714735],"Mangrove Planks":[2712419],"Mangrove Pressure Plate":[2717816,2717817],"Mangrove Roots":[2733493],"Mangrove Sign":[2720896,2720897,2720898,2720899,2720900,2720901,2720902,2720903,2720904,2720905,2720906,2720907,2720908,2720909,2720910,2720911],"Mangrove Slab":[2713960,2713961,2713963],"Mangrove Stairs":[2720128,2720129,2720130,2720131,2720132,2720133,2720134,2720135],"Mangrove Trapdoor":[2716272,2716273,2716274,2716275,2716276,2716277,2716278,2716279,2716280,2716281,2716282,2716283,2716284,2716285,2716286,2716287],"Mangrove Wall Sign":[2721668,2721669,2721670,2721671],"Mangrove Wood":[2715498,2715499,2715500,2715501,2715502,2715503],"Material Reducer":[2648424,2648425,2648426,2648427],"Meitnerium":[2609360],"Melon Block":[2648682],"Melon Stem":[2648896,2648897,2648898,2648899,2648900,2648901,2648902,2648903,2648904,2648905,2648906,2648907,2648908,2648909,2648910,2648911,2648928,2648929,2648930,2648931,2648932,2648933,2648934,2648935,2648944,2648945,2648946,2648947,2648948,2648949,2648950,2648951,2648952,2648953,2648954,2648955,2648956,2648957,2648958,2648959],"Mendelevium":[2609617],"Mercury":[2609874],"Mob Head":[2649152,2649153,2649154,2649156,2649157,2649158,2649159,2649160,2649161,2649162,2649164,2649165,2649166,2649167,2649184,2649185,2649186,2649188,2649189,2649190,2649191,2649200,2649201,2649202,2649204,2649205,2649206,2649207,2649208,2649209,2649210,2649212,2649213,2649214,2649215],"Molybdenum":[2610131],"Monster Spawner":[2649453],"Moscovium":[2610388],"Mossy Cobblestone":[2649710],"Mossy Cobblestone Slab":[2649965,2649966,2649967],"Mossy Cobblestone Stairs":[2650224,2650225,2650226,2650227,2650228,2650229,2650230,2650231],"Mossy Cobblestone Wall":[2650401,2650416,2650417,2650418,2650419,2650420,2650421,2650422,2650423,2650424,2650425,2650426,2650427,2650428,2650429,2650430,2650431,2650432,2650433,2650434,2650435,2650436,2650437,2650438,2650439,2650440,2650441,2650442,2650443,2650444,2650445,2650446,2650447,2650448,2650449,2650450,2650451,2650452,2650453,2650454,2650455,2650456,2650457,2650458,2650459,2650460,2650461,2650462,2650463,2650464,2650465,2650466,2650467,2650468,2650469,2650470,2650471,2650472,2650473,2650474,2650475,2650476,2650477,2650478,2650479,2650480,2650481,2650482,2650483,2650484,2650485,2650486,2650487,2650488,2650489,2650490,2650491,2650492,2650493,2650494,2650495,2650529,2650544,2650545,2650546,2650547,2650548,2650549,2650550,2650551,2650552,2650553,2650554,2650555,2650556,2650557,2650558,2650559,2650560,2650561,2650562,2650563,2650564,2650565,2650566,2650567,2650568,2650569,2650570,2650571,2650572,2650573,2650574,2650575,2650576,2650577,2650578,2650579,2650580,2650581,2650582,2650583,2650584,2650585,2650586,2650587,2650588,2650589,2650590,2650591,2650592,2650593,2650594,2650595,2650596,2650597,2650598,2650599,2650600,2650601,2650602,2650603,2650604,2650605,2650606,2650607,2650608,2650609,2650610,2650611,2650612,2650613,2650614,2650615,2650616,2650617,2650618,2650619,2650620,2650621,2650622,2650623],"Mossy Stone Brick Slab":[2650736,2650738,2650739],"Mossy Stone Brick Stairs":[2650992,2650993,2650994,2650995,2650996,2650997,2650998,2650999],"Mossy Stone Brick Wall":[2651172,2651184,2651185,2651186,2651187,2651188,2651189,2651190,2651191,2651192,2651193,2651194,2651195,2651196,2651197,2651198,2651199,2651200,2651201,2651202,2651203,2651204,2651205,2651206,2651207,2651208,2651209,2651210,2651211,2651212,2651213,2651214,2651215,2651216,2651217,2651218,2651219,2651220,2651221,2651222,2651223,2651224,2651225,2651226,2651227,2651228,2651229,2651230,2651231,2651232,2651233,2651234,2651235,2651236,2651237,2651238,2651239,2651240,2651241,2651242,2651243,2651244,2651245,2651246,2651247,2651248,2651249,2651250,2651251,2651252,2651253,2651254,2651255,2651256,2651257,2651258,2651259,2651260,2651261,2651262,2651263,2651300,2651312,2651313,2651314,2651315,2651316,2651317,2651318,2651319,2651320,2651321,2651322,2651323,2651324,2651325,2651326,2651327,2651328,2651329,2651330,2651331,2651332,2651333,2651334,2651335,2651336,2651337,2651338,2651339,2651340,2651341,2651342,2651343,2651344,2651345,2651346,2651347,2651348,2651349,2651350,2651351,2651352,2651353,2651354,2651355,2651356,2651357,2651358,2651359,2651360,2651361,2651362,2651363,2651364,2651365,2651366,2651367,2651368,2651369,2651370,2651371,2651372,2651373,2651374,2651375,2651376,2651377,2651378,2651379,2651380,2651381,2651382,2651383,2651384,2651385,2651386,2651387,2651388,2651389,2651390,2651391],"Mossy Stone Bricks":[2651509],"Mud":[2725526],"Mud Brick Slab":[2726040,2726041,2726042],"Mud Brick Stairs":[2726296,2726297,2726298,2726299,2726300,2726301,2726302,2726303],"Mud Brick Wall":[2726400,2726401,2726402,2726403,2726404,2726405,2726406,2726407,2726408,2726409,2726410,2726411,2726412,2726413,2726414,2726415,2726416,2726417,2726418,2726419,2726420,2726421,2726422,2726423,2726424,2726425,2726426,2726427,2726428,2726429,2726430,2726431,2726432,2726433,2726434,2726435,2726436,2726437,2726438,2726439,2726440,2726441,2726442,2726443,2726444,2726445,2726446,2726447,2726448,2726449,2726450,2726451,2726452,2726453,2726454,2726455,2726456,2726457,2726458,2726459,2726460,2726461,2726462,2726463,2726474,2726480,2726481,2726482,2726483,2726484,2726485,2726486,2726487,2726488,2726489,2726490,2726491,2726492,2726493,2726494,2726495,2726528,2726529,2726530,2726531,2726532,2726533,2726534,2726535,2726536,2726537,2726538,2726539,2726540,2726541,2726542,2726543,2726544,2726545,2726546,2726547,2726548,2726549,2726550,2726551,2726552,2726553,2726554,2726555,2726556,2726557,2726558,2726559,2726560,2726561,2726562,2726563,2726564,2726565,2726566,2726567,2726568,2726569,2726570,2726571,2726572,2726573,2726574,2726575,2726576,2726577,2726578,2726579,2726580,2726581,2726582,2726583,2726584,2726585,2726586,2726587,2726588,2726589,2726590,2726591,2726602,2726608,2726609,2726610,2726611,2726612,2726613,2726614,2726615,2726616,2726617,2726618,2726619,2726620,2726621,2726622,2726623],"Mud Bricks":[2725783],"Muddy Mangrove Roots":[2733748,2733750,2733751],"Mushroom Stem":[2651766],"Mycelium":[2652023],"Neodymium":[2610645],"Neon":[2610902],"Neptunium":[2611159],"Nether Brick Fence":[2652280],"Nether Brick Slab":[2652536,2652537,2652539],"Nether Brick Stairs":[2652792,2652793,2652794,2652795,2652796,2652797,2652798,2652799],"Nether Brick Wall":[2652971,2652976,2652977,2652978,2652979,2652980,2652981,2652982,2652983,2652984,2652985,2652986,2652987,2652988,2652989,2652990,2652991,2652992,2652993,2652994,2652995,2652996,2652997,2652998,2652999,2653000,2653001,2653002,2653003,2653004,2653005,2653006,2653007,2653008,2653009,2653010,2653011,2653012,2653013,2653014,2653015,2653016,2653017,2653018,2653019,2653020,2653021,2653022,2653023,2653024,2653025,2653026,2653027,2653028,2653029,2653030,2653031,2653032,2653033,2653034,2653035,2653036,2653037,2653038,2653039,2653040,2653041,2653042,2653043,2653044,2653045,2653046,2653047,2653048,2653049,2653050,2653051,2653052,2653053,2653054,2653055,2653099,2653104,2653105,2653106,2653107,2653108,2653109,2653110,2653111,2653112,2653113,2653114,2653115,2653116,2653117,2653118,2653119,2653120,2653121,2653122,2653123,2653124,2653125,2653126,2653127,2653128,2653129,2653130,2653131,2653132,2653133,2653134,2653135,2653136,2653137,2653138,2653139,2653140,2653141,2653142,2653143,2653144,2653145,2653146,2653147,2653148,2653149,2653150,2653151,2653152,2653153,2653154,2653155,2653156,2653157,2653158,2653159,2653160,2653161,2653162,2653163,2653164,2653165,2653166,2653167,2653168,2653169,2653170,2653171,2653172,2653173,2653174,2653175,2653176,2653177,2653178,2653179,2653180,2653181,2653182,2653183],"Nether Bricks":[2653308],"Nether Gold Ore":[2725269],"Nether Portal":[2653564,2653565],"Nether Quartz Ore":[2653822],"Nether Reactor Core":[2654079],"Nether Wart":[2654336,2654337,2654338,2654339],"Nether Wart Block":[2654593],"Netherite Block":[2731180],"Netherrack":[2654850],"Nickel":[2611416],"Nihonium":[2611673],"Niobium":[2611930],"Nitrogen":[2612187],"Nobelium":[2612444],"Note Block":[2655107],"Oak Button":[2655360,2655361,2655364,2655365,2655366,2655367,2655368,2655369,2655372,2655373,2655374,2655375],"Oak Door":[2655616,2655617,2655618,2655619,2655620,2655621,2655622,2655623,2655624,2655625,2655626,2655627,2655628,2655629,2655630,2655631,2655632,2655633,2655634,2655635,2655636,2655637,2655638,2655639,2655640,2655641,2655642,2655643,2655644,2655645,2655646,2655647],"Oak Fence":[2655878],"Oak Fence Gate":[2656128,2656129,2656130,2656131,2656132,2656133,2656134,2656135,2656136,2656137,2656138,2656139,2656140,2656141,2656142,2656143],"Oak Leaves":[2656392,2656393,2656394,2656395],"Oak Log":[2656648,2656649,2656650,2656651,2656652,2656653],"Oak Planks":[2656906],"Oak Pressure Plate":[2657162,2657163],"Oak Sapling":[2657420,2657421],"Oak Sign":[2657664,2657665,2657666,2657667,2657668,2657669,2657670,2657671,2657672,2657673,2657674,2657675,2657676,2657677,2657678,2657679],"Oak Slab":[2657932,2657934,2657935],"Oak Stairs":[2658184,2658185,2658186,2658187,2658188,2658189,2658190,2658191],"Oak Trapdoor":[2658448,2658449,2658450,2658451,2658452,2658453,2658454,2658455,2658456,2658457,2658458,2658459,2658460,2658461,2658462,2658463],"Oak Wall Sign":[2658704,2658705,2658706,2658707],"Oak Wood":[2658960,2658961,2658962,2658963,2658966,2658967],"Obsidian":[2659219],"Oganesson":[2612701],"Orange Tulip":[2659733],"Osmium":[2612958],"Oxeye Daisy":[2659990],"Oxygen":[2613215],"Packed Ice":[2660247],"Packed Mud":[2726811],"Palladium":[2613472],"Peony":[2660504,2660505],"Phosphorus":[2613729],"Pink Tulip":[2661018],"Platinum":[2613986],"Plutonium":[2614243],"Podzol":[2661275],"Polished Andesite":[2661532],"Polished Andesite Slab":[2661788,2661789,2661791],"Polished Andesite Stairs":[2662040,2662041,2662042,2662043,2662044,2662045,2662046,2662047],"Polished Basalt":[2699053,2699054,2699055],"Polished Blackstone":[2700597],"Polished Blackstone Brick Slab":[2702652,2702653,2702655],"Polished Blackstone Brick Stairs":[2702904,2702905,2702906,2702907,2702908,2702909,2702910,2702911],"Polished Blackstone Brick Wall":[2703104,2703105,2703106,2703107,2703108,2703109,2703110,2703111,2703112,2703113,2703114,2703115,2703116,2703117,2703118,2703119,2703120,2703121,2703122,2703123,2703124,2703125,2703126,2703127,2703128,2703129,2703130,2703131,2703132,2703133,2703134,2703135,2703136,2703137,2703138,2703139,2703140,2703141,2703142,2703143,2703144,2703145,2703146,2703147,2703148,2703149,2703150,2703151,2703152,2703153,2703154,2703155,2703156,2703157,2703158,2703159,2703160,2703161,2703162,2703163,2703164,2703165,2703166,2703167,2703215,2703216,2703217,2703218,2703219,2703220,2703221,2703222,2703223,2703224,2703225,2703226,2703227,2703228,2703229,2703230,2703231,2703232,2703233,2703234,2703235,2703236,2703237,2703238,2703239,2703240,2703241,2703242,2703243,2703244,2703245,2703246,2703247,2703248,2703249,2703250,2703251,2703252,2703253,2703254,2703255,2703256,2703257,2703258,2703259,2703260,2703261,2703262,2703263,2703264,2703265,2703266,2703267,2703268,2703269,2703270,2703271,2703272,2703273,2703274,2703275,2703276,2703277,2703278,2703279,2703280,2703281,2703282,2703283,2703284,2703285,2703286,2703287,2703288,2703289,2703290,2703291,2703292,2703293,2703294,2703295,2703343,2703344,2703345,2703346,2703347,2703348,2703349,2703350,2703351,2703352,2703353,2703354,2703355,2703356,2703357,2703358,2703359],"Polished Blackstone Bricks":[2702396],"Polished Blackstone Button":[2700850,2700851,2700852,2700853,2700854,2700855,2700858,2700859,2700860,2700861,2700862,2700863],"Polished Blackstone Pressure Plate":[2701110,2701111],"Polished Blackstone Slab":[2701368,2701369,2701370],"Polished Blackstone Stairs":[2701624,2701625,2701626,2701627,2701628,2701629,2701630,2701631],"Polished Blackstone Wall":[2701824,2701825,2701826,2701827,2701828,2701829,2701830,2701831,2701832,2701833,2701834,2701835,2701836,2701837,2701838,2701839,2701840,2701841,2701842,2701843,2701844,2701845,2701846,2701847,2701848,2701849,2701850,2701851,2701852,2701853,2701854,2701855,2701856,2701857,2701858,2701859,2701860,2701861,2701862,2701863,2701864,2701865,2701866,2701867,2701868,2701869,2701870,2701871,2701872,2701873,2701874,2701875,2701876,2701877,2701878,2701879,2701880,2701881,2701882,2701883,2701884,2701885,2701886,2701887,2701930,2701936,2701937,2701938,2701939,2701940,2701941,2701942,2701943,2701944,2701945,2701946,2701947,2701948,2701949,2701950,2701951,2701952,2701953,2701954,2701955,2701956,2701957,2701958,2701959,2701960,2701961,2701962,2701963,2701964,2701965,2701966,2701967,2701968,2701969,2701970,2701971,2701972,2701973,2701974,2701975,2701976,2701977,2701978,2701979,2701980,2701981,2701982,2701983,2701984,2701985,2701986,2701987,2701988,2701989,2701990,2701991,2701992,2701993,2701994,2701995,2701996,2701997,2701998,2701999,2702000,2702001,2702002,2702003,2702004,2702005,2702006,2702007,2702008,2702009,2702010,2702011,2702012,2702013,2702014,2702015,2702058,2702064,2702065,2702066,2702067,2702068,2702069,2702070,2702071,2702072,2702073,2702074,2702075,2702076,2702077,2702078,2702079],"Polished Deepslate":[2708821],"Polished Deepslate Slab":[2709076,2709078,2709079],"Polished Deepslate Stairs":[2709328,2709329,2709330,2709331,2709332,2709333,2709334,2709335],"Polished Deepslate Wall":[2709512,2709520,2709521,2709522,2709523,2709524,2709525,2709526,2709527,2709528,2709529,2709530,2709531,2709532,2709533,2709534,2709535,2709568,2709569,2709570,2709571,2709572,2709573,2709574,2709575,2709576,2709577,2709578,2709579,2709580,2709581,2709582,2709583,2709584,2709585,2709586,2709587,2709588,2709589,2709590,2709591,2709592,2709593,2709594,2709595,2709596,2709597,2709598,2709599,2709600,2709601,2709602,2709603,2709604,2709605,2709606,2709607,2709608,2709609,2709610,2709611,2709612,2709613,2709614,2709615,2709616,2709617,2709618,2709619,2709620,2709621,2709622,2709623,2709624,2709625,2709626,2709627,2709628,2709629,2709630,2709631,2709640,2709648,2709649,2709650,2709651,2709652,2709653,2709654,2709655,2709656,2709657,2709658,2709659,2709660,2709661,2709662,2709663,2709696,2709697,2709698,2709699,2709700,2709701,2709702,2709703,2709704,2709705,2709706,2709707,2709708,2709709,2709710,2709711,2709712,2709713,2709714,2709715,2709716,2709717,2709718,2709719,2709720,2709721,2709722,2709723,2709724,2709725,2709726,2709727,2709728,2709729,2709730,2709731,2709732,2709733,2709734,2709735,2709736,2709737,2709738,2709739,2709740,2709741,2709742,2709743,2709744,2709745,2709746,2709747,2709748,2709749,2709750,2709751,2709752,2709753,2709754,2709755,2709756,2709757,2709758,2709759],"Polished Diorite":[2662303],"Polished Diorite Slab":[2662560,2662561,2662562],"Polished Diorite Stairs":[2662816,2662817,2662818,2662819,2662820,2662821,2662822,2662823],"Polished Granite":[2663074],"Polished Granite Slab":[2663329,2663330,2663331],"Polished Granite Stairs":[2663584,2663585,2663586,2663587,2663588,2663589,2663590,2663591],"Polonium":[2614500],"Poppy":[2663845],"Potassium":[2614757],"Potato Block":[2664096,2664097,2664098,2664099,2664100,2664101,2664102,2664103],"Potion Cauldron":[2732464,2732465,2732466,2732467,2732468,2732469],"Powered Rail":[2664354,2664355,2664356,2664357,2664358,2664359,2664362,2664363,2664364,2664365,2664366,2664367],"Praseodymium":[2615014],"Prismarine":[2664616],"Prismarine Bricks":[2664873],"Prismarine Bricks Slab":[2665128,2665130,2665131],"Prismarine Bricks Stairs":[2665384,2665385,2665386,2665387,2665388,2665389,2665390,2665391],"Prismarine Slab":[2665644,2665645,2665646],"Prismarine Stairs":[2665896,2665897,2665898,2665899,2665900,2665901,2665902,2665903],"Prismarine Wall":[2665984,2665985,2665986,2665987,2665988,2665989,2665990,2665991,2665992,2665993,2665994,2665995,2665996,2665997,2665998,2665999,2666000,2666001,2666002,2666003,2666004,2666005,2666006,2666007,2666008,2666009,2666010,2666011,2666012,2666013,2666014,2666015,2666016,2666017,2666018,2666019,2666020,2666021,2666022,2666023,2666024,2666025,2666026,2666027,2666028,2666029,2666030,2666031,2666032,2666033,2666034,2666035,2666036,2666037,2666038,2666039,2666040,2666041,2666042,2666043,2666044,2666045,2666046,2666047,2666080,2666081,2666082,2666083,2666084,2666085,2666086,2666087,2666088,2666089,2666090,2666091,2666092,2666093,2666094,2666095,2666110,2666112,2666113,2666114,2666115,2666116,2666117,2666118,2666119,2666120,2666121,2666122,2666123,2666124,2666125,2666126,2666127,2666128,2666129,2666130,2666131,2666132,2666133,2666134,2666135,2666136,2666137,2666138,2666139,2666140,2666141,2666142,2666143,2666144,2666145,2666146,2666147,2666148,2666149,2666150,2666151,2666152,2666153,2666154,2666155,2666156,2666157,2666158,2666159,2666160,2666161,2666162,2666163,2666164,2666165,2666166,2666167,2666168,2666169,2666170,2666171,2666172,2666173,2666174,2666175,2666208,2666209,2666210,2666211,2666212,2666213,2666214,2666215,2666216,2666217,2666218,2666219,2666220,2666221,2666222,2666223,2666238],"Promethium":[2615271],"Protactinium":[2615528],"Pumpkin":[2666415],"Pumpkin Stem":[2666640,2666641,2666642,2666643,2666644,2666645,2666646,2666647,2666648,2666649,2666650,2666651,2666652,2666653,2666654,2666655,2666656,2666657,2666658,2666659,2666660,2666661,2666662,2666663,2666664,2666665,2666666,2666667,2666668,2666669,2666670,2666671,2666680,2666681,2666682,2666683,2666684,2666685,2666686,2666687],"Purple Torch":[2667184,2667185,2667187,2667190,2667191],"Purpur Block":[2667443],"Purpur Pillar":[2667700,2667701,2667702],"Purpur Slab":[2667956,2667957,2667959],"Purpur Stairs":[2668208,2668209,2668210,2668211,2668212,2668213,2668214,2668215],"Quartz Block":[2668471],"Quartz Bricks":[2709849],"Quartz Pillar":[2668728,2668729,2668730],"Quartz Slab":[2668984,2668985,2668987],"Quartz Stairs":[2669240,2669241,2669242,2669243,2669244,2669245,2669246,2669247],"Radium":[2615785],"Radon":[2616042],"Rail":[2669490,2669491,2669496,2669497,2669498,2669499,2669500,2669501,2669502,2669503],"Raw Copper Block":[2703938],"Raw Gold Block":[2704195],"Raw Iron Block":[2704452],"Red Mushroom":[2670013],"Red Mushroom Block":[2670260,2670262,2670263,2670264,2670265,2670266,2670267,2670268,2670269,2670270,2670271],"Red Nether Brick Slab":[2670525,2670526,2670527],"Red Nether Brick Stairs":[2670784,2670785,2670786,2670787,2670788,2670789,2670790,2670791],"Red Nether Brick Wall":[2670848,2670849,2670850,2670851,2670852,2670853,2670854,2670855,2670856,2670857,2670858,2670859,2670860,2670861,2670862,2670863,2670865,2670912,2670913,2670914,2670915,2670916,2670917,2670918,2670919,2670920,2670921,2670922,2670923,2670924,2670925,2670926,2670927,2670928,2670929,2670930,2670931,2670932,2670933,2670934,2670935,2670936,2670937,2670938,2670939,2670940,2670941,2670942,2670943,2670944,2670945,2670946,2670947,2670948,2670949,2670950,2670951,2670952,2670953,2670954,2670955,2670956,2670957,2670958,2670959,2670960,2670961,2670962,2670963,2670964,2670965,2670966,2670967,2670968,2670969,2670970,2670971,2670972,2670973,2670974,2670975,2670976,2670977,2670978,2670979,2670980,2670981,2670982,2670983,2670984,2670985,2670986,2670987,2670988,2670989,2670990,2670991,2670993,2671040,2671041,2671042,2671043,2671044,2671045,2671046,2671047,2671048,2671049,2671050,2671051,2671052,2671053,2671054,2671055,2671056,2671057,2671058,2671059,2671060,2671061,2671062,2671063,2671064,2671065,2671066,2671067,2671068,2671069,2671070,2671071,2671072,2671073,2671074,2671075,2671076,2671077,2671078,2671079,2671080,2671081,2671082,2671083,2671084,2671085,2671086,2671087,2671088,2671089,2671090,2671091,2671092,2671093,2671094,2671095,2671096,2671097,2671098,2671099,2671100,2671101,2671102,2671103],"Red Nether Bricks":[2671298],"Red Sand":[2671555],"Red Sandstone":[2671812],"Red Sandstone Slab":[2672068,2672069,2672071],"Red Sandstone Stairs":[2672320,2672321,2672322,2672323,2672324,2672325,2672326,2672327],"Red Sandstone Wall":[2672384,2672385,2672386,2672387,2672388,2672389,2672390,2672391,2672392,2672393,2672394,2672395,2672396,2672397,2672398,2672399,2672407,2672448,2672449,2672450,2672451,2672452,2672453,2672454,2672455,2672456,2672457,2672458,2672459,2672460,2672461,2672462,2672463,2672464,2672465,2672466,2672467,2672468,2672469,2672470,2672471,2672472,2672473,2672474,2672475,2672476,2672477,2672478,2672479,2672480,2672481,2672482,2672483,2672484,2672485,2672486,2672487,2672488,2672489,2672490,2672491,2672492,2672493,2672494,2672495,2672496,2672497,2672498,2672499,2672500,2672501,2672502,2672503,2672504,2672505,2672506,2672507,2672508,2672509,2672510,2672511,2672512,2672513,2672514,2672515,2672516,2672517,2672518,2672519,2672520,2672521,2672522,2672523,2672524,2672525,2672526,2672527,2672535,2672576,2672577,2672578,2672579,2672580,2672581,2672582,2672583,2672584,2672585,2672586,2672587,2672588,2672589,2672590,2672591,2672592,2672593,2672594,2672595,2672596,2672597,2672598,2672599,2672600,2672601,2672602,2672603,2672604,2672605,2672606,2672607,2672608,2672609,2672610,2672611,2672612,2672613,2672614,2672615,2672616,2672617,2672618,2672619,2672620,2672621,2672622,2672623,2672624,2672625,2672626,2672627,2672628,2672629,2672630,2672631,2672632,2672633,2672634,2672635,2672636,2672637,2672638,2672639],"Red Torch":[2672841,2672842,2672843,2672844,2672845],"Red Tulip":[2673097],"Redstone":[2674896,2674897,2674898,2674899,2674900,2674901,2674902,2674903,2674904,2674905,2674906,2674907,2674908,2674909,2674910,2674911],"Redstone Block":[2673354],"Redstone Comparator":[2673600,2673601,2673602,2673603,2673604,2673605,2673606,2673607,2673608,2673609,2673610,2673611,2673612,2673613,2673614,2673615],"Redstone Lamp":[2673868,2673869],"Redstone Ore":[2674124,2674125],"Redstone Repeater":[2674368,2674369,2674370,2674371,2674372,2674373,2674374,2674375,2674376,2674377,2674378,2674379,2674380,2674381,2674382,2674383,2674384,2674385,2674386,2674387,2674388,2674389,2674390,2674391,2674392,2674393,2674394,2674395,2674396,2674397,2674398,2674399],"Redstone Torch":[2674626,2674627,2674628,2674629,2674630,2674634,2674635,2674636,2674637,2674638],"Reinforced Deepslate":[2736320],"Rhenium":[2616299],"Rhodium":[2616556],"Roentgenium":[2616813],"Rose Bush":[2675410,2675411],"Rubidium":[2617070],"Ruthenium":[2617327],"Rutherfordium":[2617584],"Samarium":[2617841],"Sand":[2675667],"Sandstone":[2675924],"Sandstone Slab":[2676180,2676181,2676183],"Sandstone Stairs":[2676432,2676433,2676434,2676435,2676436,2676437,2676438,2676439],"Sandstone Wall":[2676487,2676496,2676497,2676498,2676499,2676500,2676501,2676502,2676503,2676504,2676505,2676506,2676507,2676508,2676509,2676510,2676511,2676544,2676545,2676546,2676547,2676548,2676549,2676550,2676551,2676552,2676553,2676554,2676555,2676556,2676557,2676558,2676559,2676560,2676561,2676562,2676563,2676564,2676565,2676566,2676567,2676568,2676569,2676570,2676571,2676572,2676573,2676574,2676575,2676576,2676577,2676578,2676579,2676580,2676581,2676582,2676583,2676584,2676585,2676586,2676587,2676588,2676589,2676590,2676591,2676592,2676593,2676594,2676595,2676596,2676597,2676598,2676599,2676600,2676601,2676602,2676603,2676604,2676605,2676606,2676607,2676615,2676624,2676625,2676626,2676627,2676628,2676629,2676630,2676631,2676632,2676633,2676634,2676635,2676636,2676637,2676638,2676639,2676672,2676673,2676674,2676675,2676676,2676677,2676678,2676679,2676680,2676681,2676682,2676683,2676684,2676685,2676686,2676687,2676688,2676689,2676690,2676691,2676692,2676693,2676694,2676695,2676696,2676697,2676698,2676699,2676700,2676701,2676702,2676703,2676704,2676705,2676706,2676707,2676708,2676709,2676710,2676711,2676712,2676713,2676714,2676715,2676716,2676717,2676718,2676719,2676720,2676721,2676722,2676723,2676724,2676725,2676726,2676727,2676728,2676729,2676730,2676731,2676732,2676733,2676734,2676735],"Scandium":[2618098],"Sculk":[2735035],"Sea Lantern":[2676952],"Sea Pickle":[2677208,2677209,2677210,2677211,2677212,2677213,2677214,2677215],"Seaborgium":[2618355],"Selenium":[2618612],"Shroomlight":[2712162],"Shulker Box":[2677466],"Silicon":[2618869],"Silver":[2619126],"Slime Block":[2677723],"Small Dripleaf":[2740944,2740945,2740946,2740947,2740948,2740949,2740950,2740951],"Smithing Table":[2730923],"Smoker":[2677976,2677977,2677978,2677979,2677980,2677981,2677982,2677983],"Smooth Basalt":[2699312],"Smooth Quartz Block":[2678237],"Smooth Quartz Slab":[2678492,2678494,2678495],"Smooth Quartz Stairs":[2678744,2678745,2678746,2678747,2678748,2678749,2678750,2678751],"Smooth Red Sandstone":[2679008],"Smooth Red Sandstone Slab":[2679264,2679265,2679267],"Smooth Red Sandstone Stairs":[2679520,2679521,2679522,2679523,2679524,2679525,2679526,2679527],"Smooth Sandstone":[2679779],"Smooth Sandstone Slab":[2680036,2680037,2680038],"Smooth Sandstone Stairs":[2680288,2680289,2680290,2680291,2680292,2680293,2680294,2680295],"Smooth Stone":[2680550],"Smooth Stone Slab":[2680805,2680806,2680807],"Snow Block":[2681064],"Snow Layer":[2681320,2681321,2681322,2681323,2681324,2681325,2681326,2681327],"Sodium":[2619383],"Soul Fire":[2711905],"Soul Lantern":[2711390,2711391],"Soul Sand":[2681578],"Soul Soil":[2711648],"Soul Torch":[2711130,2711131,2711132,2711133,2711135],"Sponge":[2681834,2681835],"Spore Blossom":[2731437],"Spruce Button":[2682080,2682081,2682084,2682085,2682086,2682087,2682088,2682089,2682092,2682093,2682094,2682095],"Spruce Door":[2682336,2682337,2682338,2682339,2682340,2682341,2682342,2682343,2682344,2682345,2682346,2682347,2682348,2682349,2682350,2682351,2682352,2682353,2682354,2682355,2682356,2682357,2682358,2682359,2682360,2682361,2682362,2682363,2682364,2682365,2682366,2682367],"Spruce Fence":[2682606],"Spruce Fence Gate":[2682848,2682849,2682850,2682851,2682852,2682853,2682854,2682855,2682856,2682857,2682858,2682859,2682860,2682861,2682862,2682863],"Spruce Leaves":[2683120,2683121,2683122,2683123],"Spruce Log":[2683376,2683377,2683378,2683379,2683380,2683381],"Spruce Planks":[2683634],"Spruce Pressure Plate":[2683890,2683891],"Spruce Sapling":[2684148,2684149],"Spruce Sign":[2684400,2684401,2684402,2684403,2684404,2684405,2684406,2684407,2684408,2684409,2684410,2684411,2684412,2684413,2684414,2684415],"Spruce Slab":[2684660,2684662,2684663],"Spruce Stairs":[2684912,2684913,2684914,2684915,2684916,2684917,2684918,2684919],"Spruce Trapdoor":[2685168,2685169,2685170,2685171,2685172,2685173,2685174,2685175,2685176,2685177,2685178,2685179,2685180,2685181,2685182,2685183],"Spruce Wall Sign":[2685432,2685433,2685434,2685435],"Spruce Wood":[2685688,2685689,2685690,2685691,2685694,2685695],"Stained Clay":[2685936,2685937,2685938,2685939,2685940,2685941,2685942,2685943,2685944,2685945,2685946,2685947,2685948,2685949,2685950,2685951],"Stained Glass":[2686192,2686193,2686194,2686195,2686196,2686197,2686198,2686199,2686200,2686201,2686202,2686203,2686204,2686205,2686206,2686207],"Stained Glass Pane":[2686448,2686449,2686450,2686451,2686452,2686453,2686454,2686455,2686456,2686457,2686458,2686459,2686460,2686461,2686462,2686463],"Stained Hardened Glass":[2686704,2686705,2686706,2686707,2686708,2686709,2686710,2686711,2686712,2686713,2686714,2686715,2686716,2686717,2686718,2686719],"Stained Hardened Glass Pane":[2686960,2686961,2686962,2686963,2686964,2686965,2686966,2686967,2686968,2686969,2686970,2686971,2686972,2686973,2686974,2686975],"Stone":[2686976],"Stone Brick Slab":[2687232,2687233,2687235],"Stone Brick Stairs":[2687488,2687489,2687490,2687491,2687492,2687493,2687494,2687495],"Stone Brick Wall":[2687744,2687745,2687746,2687747,2687748,2687749,2687750,2687751,2687752,2687753,2687754,2687755,2687756,2687757,2687758,2687759,2687760,2687761,2687762,2687763,2687764,2687765,2687766,2687767,2687768,2687769,2687770,2687771,2687772,2687773,2687774,2687775,2687776,2687777,2687778,2687779,2687780,2687781,2687782,2687783,2687784,2687785,2687786,2687787,2687788,2687789,2687790,2687791,2687792,2687793,2687794,2687795,2687796,2687797,2687798,2687799,2687800,2687801,2687802,2687803,2687804,2687805,2687806,2687807,2687808,2687809,2687810,2687811,2687812,2687813,2687814,2687815,2687816,2687817,2687818,2687819,2687820,2687821,2687822,2687823,2687827,2687872,2687873,2687874,2687875,2687876,2687877,2687878,2687879,2687880,2687881,2687882,2687883,2687884,2687885,2687886,2687887,2687888,2687889,2687890,2687891,2687892,2687893,2687894,2687895,2687896,2687897,2687898,2687899,2687900,2687901,2687902,2687903,2687904,2687905,2687906,2687907,2687908,2687909,2687910,2687911,2687912,2687913,2687914,2687915,2687916,2687917,2687918,2687919,2687920,2687921,2687922,2687923,2687924,2687925,2687926,2687927,2687928,2687929,2687930,2687931,2687932,2687933,2687934,2687935,2687936,2687937,2687938,2687939,2687940,2687941,2687942,2687943,2687944,2687945,2687946,2687947,2687948,2687949,2687950,2687951,2687955],"Stone Bricks":[2688004],"Stone Button":[2688256,2688257,2688260,2688261,2688262,2688263,2688264,2688265,2688268,2688269,2688270,2688271],"Stone Pressure Plate":[2688518,2688519],"Stone Slab":[2688773,2688774,2688775],"Stone Stairs":[2689032,2689033,2689034,2689035,2689036,2689037,2689038,2689039],"Stonecutter":[2689288,2689289,2689290,2689291],"Strontium":[2619640],"Sugarcane":[2692624,2692625,2692626,2692627,2692628,2692629,2692630,2692631,2692632,2692633,2692634,2692635,2692636,2692637,2692638,2692639],"Sulfur":[2619897],"Sunflower":[2692886,2692887],"Sweet Berry Bush":[2693144,2693145,2693146,2693147],"TNT":[2693656,2693657,2693658,2693659],"Tall Grass":[2693401],"Tantalum":[2620154],"Technetium":[2620411],"Tellurium":[2620668],"Tennessine":[2620925],"Terbium":[2621182],"Thallium":[2621439],"Thorium":[2621440],"Thulium":[2621697],"Tin":[2621954],"Tinted Glass":[2722442],"Titanium":[2622211],"Torch":[2693912,2693913,2693914,2693918,2693919],"Trapped Chest":[2694172,2694173,2694174,2694175],"Tripwire":[2694416,2694417,2694418,2694419,2694420,2694421,2694422,2694423,2694424,2694425,2694426,2694427,2694428,2694429,2694430,2694431],"Tripwire Hook":[2694672,2694673,2694674,2694675,2694676,2694677,2694678,2694679,2694680,2694681,2694682,2694683,2694684,2694685,2694686,2694687],"Tuff":[2710877],"Tungsten":[2622468],"Twisting Vines":[2734240,2734241,2734248,2734249,2734250,2734251,2734252,2734253,2734254,2734255,2734256,2734257,2734258,2734259,2734260,2734261,2734262,2734263,2734264,2734265,2734266,2734267,2734268,2734269,2734270,2734271],"Underwater Torch":[2694938,2694939,2694940,2694941,2694942],"Uranium":[2622725],"Vanadium":[2622982],"Vines":[2695200,2695201,2695202,2695203,2695204,2695205,2695206,2695207,2695208,2695209,2695210,2695211,2695212,2695213,2695214,2695215],"Wall Banner":[2695424,2695425,2695426,2695427,2695428,2695429,2695430,2695431,2695432,2695433,2695434,2695435,2695436,2695437,2695438,2695439,2695440,2695441,2695442,2695443,2695444,2695445,2695446,2695447,2695448,2695449,2695450,2695451,2695452,2695453,2695454,2695455,2695456,2695457,2695458,2695459,2695460,2695461,2695462,2695463,2695464,2695465,2695466,2695467,2695468,2695469,2695470,2695471,2695472,2695473,2695474,2695475,2695476,2695477,2695478,2695479,2695480,2695481,2695482,2695483,2695484,2695485,2695486,2695487],"Wall Coral Fan":[2695680,2695681,2695682,2695683,2695686,2695688,2695689,2695690,2695691,2695694,2695696,2695697,2695698,2695699,2695702,2695704,2695705,2695706,2695707,2695710,2695712,2695713,2695714,2695715,2695718,2695720,2695721,2695722,2695723,2695726,2695728,2695729,2695730,2695731,2695734,2695736,2695737,2695738,2695739,2695742],"Warped Button":[2717554,2717555,2717556,2717557,2717558,2717559,2717562,2717563,2717564,2717565,2717566,2717567],"Warped Door":[2719072,2719073,2719074,2719075,2719076,2719077,2719078,2719079,2719080,2719081,2719082,2719083,2719084,2719085,2719086,2719087,2719088,2719089,2719090,2719091,2719092,2719093,2719094,2719095,2719096,2719097,2719098,2719099,2719100,2719101,2719102,2719103],"Warped Fence":[2713704],"Warped Fence Gate":[2719872,2719873,2719874,2719875,2719876,2719877,2719878,2719879,2719880,2719881,2719882,2719883,2719884,2719885,2719886,2719887],"Warped Hyphae":[2716016,2716017,2716018,2716019,2716020,2716021],"Warped Planks":[2712933],"Warped Pressure Plate":[2718330,2718331],"Warped Sign":[2721408,2721409,2721410,2721411,2721412,2721413,2721414,2721415,2721416,2721417,2721418,2721419,2721420,2721421,2721422,2721423],"Warped Slab":[2714473,2714474,2714475],"Warped Stairs":[2720640,2720641,2720642,2720643,2720644,2720645,2720646,2720647],"Warped Stem":[2715242,2715243,2715244,2715245,2715246,2715247],"Warped Trapdoor":[2716784,2716785,2716786,2716787,2716788,2716789,2716790,2716791,2716792,2716793,2716794,2716795,2716796,2716797,2716798,2716799],"Warped Wall Sign":[2722184,2722185,2722186,2722187],"Warped Wart Block":[2727068],"Water":[2695968,2695969,2695970,2695971,2695972,2695973,2695974,2695975,2695976,2695977,2695978,2695979,2695980,2695981,2695982,2695983,2695984,2695985,2695986,2695987,2695988,2695989,2695990,2695991,2695992,2695993,2695994,2695995,2695996,2695997,2695998,2695999],"Water Cauldron":[2731946,2731947,2731948,2731949,2731950,2731951],"Weeping Vines":[2734496,2734497,2734504,2734505,2734506,2734507,2734508,2734509,2734510,2734511,2734512,2734513,2734514,2734515,2734516,2734517,2734518,2734519,2734520,2734521,2734522,2734523,2734524,2734525,2734526,2734527],"Weighted Pressure Plate Heavy":[2696224,2696225,2696226,2696227,2696228,2696229,2696230,2696231,2696232,2696233,2696234,2696235,2696236,2696237,2696238,2696239],"Weighted Pressure Plate Light":[2696480,2696481,2696482,2696483,2696484,2696485,2696486,2696487,2696488,2696489,2696490,2696491,2696492,2696493,2696494,2696495],"Wheat Block":[2696736,2696737,2696738,2696739,2696740,2696741,2696742,2696743],"White Tulip":[2697256],"Wither Rose":[2730152],"Wool":[2697504,2697505,2697506,2697507,2697508,2697509,2697510,2697511,2697512,2697513,2697514,2697515,2697516,2697517,2697518,2697519],"Xenon":[2623239],"Ytterbium":[2623496],"Yttrium":[2623753],"Zinc":[2624267],"Zirconium":[2624524],"ate!upd":[2637117],"reserved6":[2675153],"update!":[2636860]},"stateDataBits":8} \ No newline at end of file +{"knownStates":{"???":[2624010],"Acacia Button":[2560272,2560273,2560274,2560275,2560276,2560277,2560280,2560281,2560282,2560283,2560284,2560285],"Acacia Door":[2560512,2560513,2560514,2560515,2560516,2560517,2560518,2560519,2560520,2560521,2560522,2560523,2560524,2560525,2560526,2560527,2560528,2560529,2560530,2560531,2560532,2560533,2560534,2560535,2560536,2560537,2560538,2560539,2560540,2560541,2560542,2560543],"Acacia Fence":[2560787],"Acacia Fence Gate":[2561040,2561041,2561042,2561043,2561044,2561045,2561046,2561047,2561048,2561049,2561050,2561051,2561052,2561053,2561054,2561055],"Acacia Leaves":[2561300,2561301,2561302,2561303],"Acacia Log":[2561554,2561555,2561556,2561557,2561558,2561559],"Acacia Planks":[2561815],"Acacia Pressure Plate":[2562072,2562073],"Acacia Sapling":[2562328,2562329],"Acacia Sign":[2562576,2562577,2562578,2562579,2562580,2562581,2562582,2562583,2562584,2562585,2562586,2562587,2562588,2562589,2562590,2562591],"Acacia Slab":[2562841,2562842,2562843],"Acacia Stairs":[2563096,2563097,2563098,2563099,2563100,2563101,2563102,2563103],"Acacia Trapdoor":[2563344,2563345,2563346,2563347,2563348,2563349,2563350,2563351,2563352,2563353,2563354,2563355,2563356,2563357,2563358,2563359],"Acacia Wall Sign":[2563612,2563613,2563614,2563615],"Acacia Wood":[2563866,2563867,2563868,2563869,2563870,2563871],"Actinium":[2594197],"Activator Rail":[2564128,2564129,2564130,2564131,2564132,2564133,2564136,2564137,2564138,2564139,2564140,2564141],"Air":[2560016],"All Sided Mushroom Stem":[2564385],"Allium":[2564642],"Aluminum":[2594454],"Americium":[2594711],"Amethyst":[2698284],"Ancient Debris":[2698541],"Andesite":[2564899],"Andesite Slab":[2565156,2565157,2565158],"Andesite Stairs":[2565408,2565409,2565410,2565411,2565412,2565413,2565414,2565415],"Andesite Wall":[2565632,2565633,2565634,2565635,2565636,2565637,2565638,2565639,2565640,2565641,2565642,2565643,2565644,2565645,2565646,2565647,2565648,2565649,2565650,2565651,2565652,2565653,2565654,2565655,2565656,2565657,2565658,2565659,2565660,2565661,2565662,2565663,2565664,2565665,2565666,2565667,2565668,2565669,2565670,2565671,2565672,2565673,2565674,2565675,2565676,2565677,2565678,2565679,2565680,2565681,2565682,2565683,2565684,2565685,2565686,2565687,2565688,2565689,2565690,2565691,2565692,2565693,2565694,2565695,2565728,2565729,2565730,2565731,2565732,2565733,2565734,2565735,2565736,2565737,2565738,2565739,2565740,2565741,2565742,2565743,2565750,2565760,2565761,2565762,2565763,2565764,2565765,2565766,2565767,2565768,2565769,2565770,2565771,2565772,2565773,2565774,2565775,2565776,2565777,2565778,2565779,2565780,2565781,2565782,2565783,2565784,2565785,2565786,2565787,2565788,2565789,2565790,2565791,2565792,2565793,2565794,2565795,2565796,2565797,2565798,2565799,2565800,2565801,2565802,2565803,2565804,2565805,2565806,2565807,2565808,2565809,2565810,2565811,2565812,2565813,2565814,2565815,2565816,2565817,2565818,2565819,2565820,2565821,2565822,2565823,2565856,2565857,2565858,2565859,2565860,2565861,2565862,2565863,2565864,2565865,2565866,2565867,2565868,2565869,2565870,2565871,2565878],"Antimony":[2594968],"Anvil":[2565921,2565922,2565923,2565925,2565926,2565927,2565929,2565930,2565931,2565933,2565934,2565935],"Argon":[2595225],"Arsenic":[2595482],"Astatine":[2595739],"Azalea Leaves":[2735804,2735805,2735806,2735807],"Azure Bluet":[2566184],"Bamboo":[2566432,2566433,2566435,2566436,2566437,2566439,2566440,2566441,2566443,2566444,2566445,2566447],"Bamboo Sapling":[2566698,2566699],"Banner":[2566912,2566913,2566914,2566915,2566916,2566917,2566918,2566919,2566920,2566921,2566922,2566923,2566924,2566925,2566926,2566927,2566928,2566929,2566930,2566931,2566932,2566933,2566934,2566935,2566936,2566937,2566938,2566939,2566940,2566941,2566942,2566943,2566944,2566945,2566946,2566947,2566948,2566949,2566950,2566951,2566952,2566953,2566954,2566955,2566956,2566957,2566958,2566959,2566960,2566961,2566962,2566963,2566964,2566965,2566966,2566967,2566968,2566969,2566970,2566971,2566972,2566973,2566974,2566975,2566976,2566977,2566978,2566979,2566980,2566981,2566982,2566983,2566984,2566985,2566986,2566987,2566988,2566989,2566990,2566991,2566992,2566993,2566994,2566995,2566996,2566997,2566998,2566999,2567000,2567001,2567002,2567003,2567004,2567005,2567006,2567007,2567008,2567009,2567010,2567011,2567012,2567013,2567014,2567015,2567016,2567017,2567018,2567019,2567020,2567021,2567022,2567023,2567024,2567025,2567026,2567027,2567028,2567029,2567030,2567031,2567032,2567033,2567034,2567035,2567036,2567037,2567038,2567039,2567040,2567041,2567042,2567043,2567044,2567045,2567046,2567047,2567048,2567049,2567050,2567051,2567052,2567053,2567054,2567055,2567056,2567057,2567058,2567059,2567060,2567061,2567062,2567063,2567064,2567065,2567066,2567067,2567068,2567069,2567070,2567071,2567072,2567073,2567074,2567075,2567076,2567077,2567078,2567079,2567080,2567081,2567082,2567083,2567084,2567085,2567086,2567087,2567088,2567089,2567090,2567091,2567092,2567093,2567094,2567095,2567096,2567097,2567098,2567099,2567100,2567101,2567102,2567103,2567104,2567105,2567106,2567107,2567108,2567109,2567110,2567111,2567112,2567113,2567114,2567115,2567116,2567117,2567118,2567119,2567120,2567121,2567122,2567123,2567124,2567125,2567126,2567127,2567128,2567129,2567130,2567131,2567132,2567133,2567134,2567135,2567136,2567137,2567138,2567139,2567140,2567141,2567142,2567143,2567144,2567145,2567146,2567147,2567148,2567149,2567150,2567151,2567152,2567153,2567154,2567155,2567156,2567157,2567158,2567159,2567160,2567161,2567162,2567163,2567164,2567165,2567166,2567167],"Barium":[2595996],"Barrel":[2567200,2567201,2567204,2567205,2567206,2567207,2567208,2567209,2567212,2567213,2567214,2567215],"Barrier":[2567469],"Basalt":[2698796,2698798,2698799],"Beacon":[2567726],"Bed Block":[2567936,2567937,2567938,2567939,2567940,2567941,2567942,2567943,2567944,2567945,2567946,2567947,2567948,2567949,2567950,2567951,2567952,2567953,2567954,2567955,2567956,2567957,2567958,2567959,2567960,2567961,2567962,2567963,2567964,2567965,2567966,2567967,2567968,2567969,2567970,2567971,2567972,2567973,2567974,2567975,2567976,2567977,2567978,2567979,2567980,2567981,2567982,2567983,2567984,2567985,2567986,2567987,2567988,2567989,2567990,2567991,2567992,2567993,2567994,2567995,2567996,2567997,2567998,2567999,2568000,2568001,2568002,2568003,2568004,2568005,2568006,2568007,2568008,2568009,2568010,2568011,2568012,2568013,2568014,2568015,2568016,2568017,2568018,2568019,2568020,2568021,2568022,2568023,2568024,2568025,2568026,2568027,2568028,2568029,2568030,2568031,2568032,2568033,2568034,2568035,2568036,2568037,2568038,2568039,2568040,2568041,2568042,2568043,2568044,2568045,2568046,2568047,2568048,2568049,2568050,2568051,2568052,2568053,2568054,2568055,2568056,2568057,2568058,2568059,2568060,2568061,2568062,2568063,2568064,2568065,2568066,2568067,2568068,2568069,2568070,2568071,2568072,2568073,2568074,2568075,2568076,2568077,2568078,2568079,2568080,2568081,2568082,2568083,2568084,2568085,2568086,2568087,2568088,2568089,2568090,2568091,2568092,2568093,2568094,2568095,2568096,2568097,2568098,2568099,2568100,2568101,2568102,2568103,2568104,2568105,2568106,2568107,2568108,2568109,2568110,2568111,2568112,2568113,2568114,2568115,2568116,2568117,2568118,2568119,2568120,2568121,2568122,2568123,2568124,2568125,2568126,2568127,2568128,2568129,2568130,2568131,2568132,2568133,2568134,2568135,2568136,2568137,2568138,2568139,2568140,2568141,2568142,2568143,2568144,2568145,2568146,2568147,2568148,2568149,2568150,2568151,2568152,2568153,2568154,2568155,2568156,2568157,2568158,2568159,2568160,2568161,2568162,2568163,2568164,2568165,2568166,2568167,2568168,2568169,2568170,2568171,2568172,2568173,2568174,2568175,2568176,2568177,2568178,2568179,2568180,2568181,2568182,2568183,2568184,2568185,2568186,2568187,2568188,2568189,2568190,2568191],"Bedrock":[2568240,2568241],"Beetroot Block":[2568496,2568497,2568498,2568499,2568500,2568501,2568502,2568503],"Bell":[2568752,2568753,2568754,2568755,2568756,2568757,2568758,2568759,2568760,2568761,2568762,2568763,2568764,2568765,2568766,2568767],"Berkelium":[2596253],"Beryllium":[2596510],"Big Dripleaf":[2741200,2741201,2741202,2741203,2741204,2741205,2741206,2741207,2741208,2741209,2741210,2741211,2741212,2741213,2741214,2741215],"Big Dripleaf Stem":[2741460,2741461,2741462,2741463],"Birch Button":[2569008,2569009,2569010,2569011,2569014,2569015,2569016,2569017,2569018,2569019,2569022,2569023],"Birch Door":[2569248,2569249,2569250,2569251,2569252,2569253,2569254,2569255,2569256,2569257,2569258,2569259,2569260,2569261,2569262,2569263,2569264,2569265,2569266,2569267,2569268,2569269,2569270,2569271,2569272,2569273,2569274,2569275,2569276,2569277,2569278,2569279],"Birch Fence":[2569525],"Birch Fence Gate":[2569776,2569777,2569778,2569779,2569780,2569781,2569782,2569783,2569784,2569785,2569786,2569787,2569788,2569789,2569790,2569791],"Birch Leaves":[2570036,2570037,2570038,2570039],"Birch Log":[2570296,2570297,2570298,2570299,2570300,2570301],"Birch Planks":[2570553],"Birch Pressure Plate":[2570810,2570811],"Birch Sapling":[2571066,2571067],"Birch Sign":[2571312,2571313,2571314,2571315,2571316,2571317,2571318,2571319,2571320,2571321,2571322,2571323,2571324,2571325,2571326,2571327],"Birch Slab":[2571580,2571581,2571583],"Birch Stairs":[2571832,2571833,2571834,2571835,2571836,2571837,2571838,2571839],"Birch Trapdoor":[2572080,2572081,2572082,2572083,2572084,2572085,2572086,2572087,2572088,2572089,2572090,2572091,2572092,2572093,2572094,2572095],"Birch Wall Sign":[2572352,2572353,2572354,2572355],"Birch Wood":[2572608,2572609,2572610,2572611,2572612,2572613],"Bismuth":[2596767],"Blackstone":[2699569],"Blackstone Slab":[2699824,2699826,2699827],"Blackstone Stairs":[2700080,2700081,2700082,2700083,2700084,2700085,2700086,2700087],"Blackstone Wall":[2700288,2700289,2700290,2700291,2700292,2700293,2700294,2700295,2700296,2700297,2700298,2700299,2700300,2700301,2700302,2700303,2700304,2700305,2700306,2700307,2700308,2700309,2700310,2700311,2700312,2700313,2700314,2700315,2700316,2700317,2700318,2700319,2700320,2700321,2700322,2700323,2700324,2700325,2700326,2700327,2700328,2700329,2700330,2700331,2700332,2700333,2700334,2700335,2700336,2700337,2700338,2700339,2700340,2700341,2700342,2700343,2700344,2700345,2700346,2700347,2700348,2700349,2700350,2700351,2700388,2700400,2700401,2700402,2700403,2700404,2700405,2700406,2700407,2700408,2700409,2700410,2700411,2700412,2700413,2700414,2700415,2700416,2700417,2700418,2700419,2700420,2700421,2700422,2700423,2700424,2700425,2700426,2700427,2700428,2700429,2700430,2700431,2700432,2700433,2700434,2700435,2700436,2700437,2700438,2700439,2700440,2700441,2700442,2700443,2700444,2700445,2700446,2700447,2700448,2700449,2700450,2700451,2700452,2700453,2700454,2700455,2700456,2700457,2700458,2700459,2700460,2700461,2700462,2700463,2700464,2700465,2700466,2700467,2700468,2700469,2700470,2700471,2700472,2700473,2700474,2700475,2700476,2700477,2700478,2700479,2700516,2700528,2700529,2700530,2700531,2700532,2700533,2700534,2700535,2700536,2700537,2700538,2700539,2700540,2700541,2700542,2700543],"Blast Furnace":[2573120,2573121,2573122,2573123,2573124,2573125,2573126,2573127],"Blue Ice":[2573637],"Blue Orchid":[2573894],"Blue Torch":[2574146,2574147,2574148,2574149,2574150],"Bohrium":[2597024],"Bone Block":[2574408,2574409,2574410],"Bookshelf":[2574665],"Boron":[2597281],"Brewing Stand":[2574920,2574921,2574922,2574923,2574924,2574925,2574926,2574927],"Brick Slab":[2575177,2575178,2575179],"Brick Stairs":[2575432,2575433,2575434,2575435,2575436,2575437,2575438,2575439],"Brick Wall":[2575616,2575617,2575618,2575619,2575620,2575621,2575622,2575623,2575624,2575625,2575626,2575627,2575628,2575629,2575630,2575631,2575645,2575680,2575681,2575682,2575683,2575684,2575685,2575686,2575687,2575688,2575689,2575690,2575691,2575692,2575693,2575694,2575695,2575696,2575697,2575698,2575699,2575700,2575701,2575702,2575703,2575704,2575705,2575706,2575707,2575708,2575709,2575710,2575711,2575712,2575713,2575714,2575715,2575716,2575717,2575718,2575719,2575720,2575721,2575722,2575723,2575724,2575725,2575726,2575727,2575728,2575729,2575730,2575731,2575732,2575733,2575734,2575735,2575736,2575737,2575738,2575739,2575740,2575741,2575742,2575743,2575744,2575745,2575746,2575747,2575748,2575749,2575750,2575751,2575752,2575753,2575754,2575755,2575756,2575757,2575758,2575759,2575773,2575808,2575809,2575810,2575811,2575812,2575813,2575814,2575815,2575816,2575817,2575818,2575819,2575820,2575821,2575822,2575823,2575824,2575825,2575826,2575827,2575828,2575829,2575830,2575831,2575832,2575833,2575834,2575835,2575836,2575837,2575838,2575839,2575840,2575841,2575842,2575843,2575844,2575845,2575846,2575847,2575848,2575849,2575850,2575851,2575852,2575853,2575854,2575855,2575856,2575857,2575858,2575859,2575860,2575861,2575862,2575863,2575864,2575865,2575866,2575867,2575868,2575869,2575870,2575871],"Bricks":[2575950],"Bromine":[2597538],"Brown Mushroom":[2576464],"Brown Mushroom Block":[2576720,2576721,2576722,2576723,2576724,2576725,2576726,2576727,2576728,2576729,2576731],"Cactus":[2576976,2576977,2576978,2576979,2576980,2576981,2576982,2576983,2576984,2576985,2576986,2576987,2576988,2576989,2576990,2576991],"Cadmium":[2597795],"Cake":[2577232,2577233,2577234,2577235,2577237,2577238,2577239],"Cake With Candle":[2729638,2729639],"Cake With Dyed Candle":[2729888,2729889,2729890,2729891,2729892,2729893,2729894,2729895,2729896,2729897,2729898,2729899,2729900,2729901,2729902,2729903,2729904,2729905,2729906,2729907,2729908,2729909,2729910,2729911,2729912,2729913,2729914,2729915,2729916,2729917,2729918,2729919],"Calcite":[2704709],"Calcium":[2598052],"Californium":[2598309],"Candle":[2729120,2729121,2729122,2729123,2729124,2729125,2729126,2729127],"Carbon":[2598566],"Carpet":[2577488,2577489,2577490,2577491,2577492,2577493,2577494,2577495,2577496,2577497,2577498,2577499,2577500,2577501,2577502,2577503],"Carrot Block":[2577744,2577745,2577746,2577747,2577748,2577749,2577750,2577751],"Cartography Table":[2730666],"Carved Pumpkin":[2578004,2578005,2578006,2578007],"Cauldron":[2731694],"Cave Vines":[2736512,2736513,2736514,2736515,2736516,2736517,2736518,2736519,2736520,2736521,2736522,2736523,2736524,2736525,2736526,2736527,2736528,2736529,2736530,2736531,2736532,2736533,2736534,2736535,2736536,2736537,2736544,2736545,2736546,2736547,2736548,2736549,2736550,2736551,2736552,2736553,2736554,2736555,2736556,2736557,2736558,2736559,2736560,2736561,2736562,2736563,2736564,2736565,2736566,2736567,2736568,2736569,2736576,2736577,2736578,2736579,2736580,2736581,2736582,2736583,2736584,2736585,2736586,2736587,2736588,2736589,2736590,2736591,2736592,2736593,2736594,2736595,2736596,2736597,2736598,2736599,2736600,2736601,2736608,2736609,2736610,2736611,2736612,2736613,2736614,2736615,2736616,2736617,2736618,2736619,2736620,2736621,2736622,2736623,2736624,2736625,2736626,2736627,2736628,2736629,2736630,2736631,2736632,2736633],"Cerium":[2598823],"Cesium":[2599080],"Chain":[2734776,2734778,2734779],"Cherry Button":[2737088,2737089,2737090,2737091,2737094,2737095,2737096,2737097,2737098,2737099,2737102,2737103],"Cherry Door":[2737344,2737345,2737346,2737347,2737348,2737349,2737350,2737351,2737352,2737353,2737354,2737355,2737356,2737357,2737358,2737359,2737360,2737361,2737362,2737363,2737364,2737365,2737366,2737367,2737368,2737369,2737370,2737371,2737372,2737373,2737374,2737375],"Cherry Fence":[2737605],"Cherry Fence Gate":[2737856,2737857,2737858,2737859,2737860,2737861,2737862,2737863,2737864,2737865,2737866,2737867,2737868,2737869,2737870,2737871],"Cherry Leaves":[2738116,2738117,2738118,2738119],"Cherry Log":[2738376,2738377,2738378,2738379,2738380,2738381],"Cherry Planks":[2738633],"Cherry Pressure Plate":[2738890,2738891],"Cherry Sign":[2739392,2739393,2739394,2739395,2739396,2739397,2739398,2739399,2739400,2739401,2739402,2739403,2739404,2739405,2739406,2739407],"Cherry Slab":[2739660,2739661,2739663],"Cherry Stairs":[2739912,2739913,2739914,2739915,2739916,2739917,2739918,2739919],"Cherry Trapdoor":[2740160,2740161,2740162,2740163,2740164,2740165,2740166,2740167,2740168,2740169,2740170,2740171,2740172,2740173,2740174,2740175],"Cherry Wall Sign":[2740432,2740433,2740434,2740435],"Cherry Wood":[2740688,2740689,2740690,2740691,2740692,2740693],"Chest":[2578520,2578521,2578522,2578523],"Chiseled Deepslate":[2710106],"Chiseled Nether Bricks":[2710363],"Chiseled Polished Blackstone":[2702139],"Chiseled Quartz Block":[2578776,2578777,2578779],"Chiseled Red Sandstone":[2579034],"Chiseled Sandstone":[2579291],"Chiseled Stone Bricks":[2579548],"Chlorine":[2599337],"Chorus Flower":[2732976,2732977,2732978,2732979,2732982,2732983],"Chorus Plant":[2733236],"Chromium":[2599594],"Clay Block":[2579805],"Coal Block":[2580062],"Coal Ore":[2580319],"Cobalt":[2599851],"Cobbled Deepslate":[2707793],"Cobbled Deepslate Slab":[2708048,2708050,2708051],"Cobbled Deepslate Stairs":[2708304,2708305,2708306,2708307,2708308,2708309,2708310,2708311],"Cobbled Deepslate Wall":[2708484,2708496,2708497,2708498,2708499,2708500,2708501,2708502,2708503,2708504,2708505,2708506,2708507,2708508,2708509,2708510,2708511,2708544,2708545,2708546,2708547,2708548,2708549,2708550,2708551,2708552,2708553,2708554,2708555,2708556,2708557,2708558,2708559,2708560,2708561,2708562,2708563,2708564,2708565,2708566,2708567,2708568,2708569,2708570,2708571,2708572,2708573,2708574,2708575,2708576,2708577,2708578,2708579,2708580,2708581,2708582,2708583,2708584,2708585,2708586,2708587,2708588,2708589,2708590,2708591,2708592,2708593,2708594,2708595,2708596,2708597,2708598,2708599,2708600,2708601,2708602,2708603,2708604,2708605,2708606,2708607,2708612,2708624,2708625,2708626,2708627,2708628,2708629,2708630,2708631,2708632,2708633,2708634,2708635,2708636,2708637,2708638,2708639,2708672,2708673,2708674,2708675,2708676,2708677,2708678,2708679,2708680,2708681,2708682,2708683,2708684,2708685,2708686,2708687,2708688,2708689,2708690,2708691,2708692,2708693,2708694,2708695,2708696,2708697,2708698,2708699,2708700,2708701,2708702,2708703,2708704,2708705,2708706,2708707,2708708,2708709,2708710,2708711,2708712,2708713,2708714,2708715,2708716,2708717,2708718,2708719,2708720,2708721,2708722,2708723,2708724,2708725,2708726,2708727,2708728,2708729,2708730,2708731,2708732,2708733,2708734,2708735],"Cobblestone":[2580576],"Cobblestone Slab":[2580832,2580833,2580835],"Cobblestone Stairs":[2581088,2581089,2581090,2581091,2581092,2581093,2581094,2581095],"Cobblestone Wall":[2581280,2581281,2581282,2581283,2581284,2581285,2581286,2581287,2581288,2581289,2581290,2581291,2581292,2581293,2581294,2581295,2581299,2581312,2581313,2581314,2581315,2581316,2581317,2581318,2581319,2581320,2581321,2581322,2581323,2581324,2581325,2581326,2581327,2581328,2581329,2581330,2581331,2581332,2581333,2581334,2581335,2581336,2581337,2581338,2581339,2581340,2581341,2581342,2581343,2581344,2581345,2581346,2581347,2581348,2581349,2581350,2581351,2581352,2581353,2581354,2581355,2581356,2581357,2581358,2581359,2581360,2581361,2581362,2581363,2581364,2581365,2581366,2581367,2581368,2581369,2581370,2581371,2581372,2581373,2581374,2581375,2581408,2581409,2581410,2581411,2581412,2581413,2581414,2581415,2581416,2581417,2581418,2581419,2581420,2581421,2581422,2581423,2581427,2581440,2581441,2581442,2581443,2581444,2581445,2581446,2581447,2581448,2581449,2581450,2581451,2581452,2581453,2581454,2581455,2581456,2581457,2581458,2581459,2581460,2581461,2581462,2581463,2581464,2581465,2581466,2581467,2581468,2581469,2581470,2581471,2581472,2581473,2581474,2581475,2581476,2581477,2581478,2581479,2581480,2581481,2581482,2581483,2581484,2581485,2581486,2581487,2581488,2581489,2581490,2581491,2581492,2581493,2581494,2581495,2581496,2581497,2581498,2581499,2581500,2581501,2581502,2581503],"Cobweb":[2581604],"Cocoa Block":[2581856,2581857,2581858,2581859,2581860,2581861,2581862,2581863,2581868,2581869,2581870,2581871],"Compound Creator":[2582116,2582117,2582118,2582119],"Concrete":[2582368,2582369,2582370,2582371,2582372,2582373,2582374,2582375,2582376,2582377,2582378,2582379,2582380,2582381,2582382,2582383],"Concrete Powder":[2582624,2582625,2582626,2582627,2582628,2582629,2582630,2582631,2582632,2582633,2582634,2582635,2582636,2582637,2582638,2582639],"Copernicium":[2600365],"Copper":[2600622],"Copper Block":[2728096,2728097,2728098,2728099,2728100,2728101,2728102,2728103],"Copper Ore":[2725012],"Coral":[2582880,2582881,2582882,2582883,2582885,2582888,2582889,2582890,2582891,2582893],"Coral Block":[2583136,2583137,2583138,2583139,2583142,2583144,2583145,2583146,2583147,2583150],"Coral Fan":[2583392,2583393,2583394,2583395,2583399,2583400,2583401,2583402,2583403,2583407,2583408,2583409,2583410,2583411,2583415,2583416,2583417,2583418,2583419,2583423],"Cornflower":[2583660],"Cracked Deepslate Bricks":[2706251],"Cracked Deepslate Tiles":[2707536],"Cracked Nether Bricks":[2710620],"Cracked Polished Blackstone Bricks":[2703424],"Cracked Stone Bricks":[2583917],"Crafting Table":[2584174],"Crimson Button":[2717298,2717299,2717300,2717301,2717302,2717303,2717306,2717307,2717308,2717309,2717310,2717311],"Crimson Door":[2718816,2718817,2718818,2718819,2718820,2718821,2718822,2718823,2718824,2718825,2718826,2718827,2718828,2718829,2718830,2718831,2718832,2718833,2718834,2718835,2718836,2718837,2718838,2718839,2718840,2718841,2718842,2718843,2718844,2718845,2718846,2718847],"Crimson Fence":[2713447],"Crimson Fence Gate":[2719600,2719601,2719602,2719603,2719604,2719605,2719606,2719607,2719608,2719609,2719610,2719611,2719612,2719613,2719614,2719615],"Crimson Hyphae":[2715760,2715761,2715762,2715763,2715764,2715765],"Crimson Planks":[2712676],"Crimson Pressure Plate":[2718072,2718073],"Crimson Sign":[2721152,2721153,2721154,2721155,2721156,2721157,2721158,2721159,2721160,2721161,2721162,2721163,2721164,2721165,2721166,2721167],"Crimson Slab":[2714216,2714218,2714219],"Crimson Stairs":[2720384,2720385,2720386,2720387,2720388,2720389,2720390,2720391],"Crimson Stem":[2714984,2714985,2714988,2714989,2714990,2714991],"Crimson Trapdoor":[2716528,2716529,2716530,2716531,2716532,2716533,2716534,2716535,2716536,2716537,2716538,2716539,2716540,2716541,2716542,2716543],"Crimson Wall Sign":[2721928,2721929,2721930,2721931],"Crying Obsidian":[2727325],"Curium":[2600879],"Cut Copper Block":[2728352,2728353,2728354,2728355,2728356,2728357,2728358,2728359],"Cut Copper Slab Slab":[2728608,2728609,2728610,2728611,2728612,2728613,2728614,2728615,2728616,2728617,2728618,2728619,2728620,2728621,2728622,2728623,2728624,2728625,2728626,2728627,2728628,2728629,2728630,2728631],"Cut Copper Stairs":[2728832,2728833,2728834,2728835,2728836,2728837,2728838,2728839,2728840,2728841,2728842,2728843,2728844,2728845,2728846,2728847,2728848,2728849,2728850,2728851,2728852,2728853,2728854,2728855,2728856,2728857,2728858,2728859,2728860,2728861,2728862,2728863,2728864,2728865,2728866,2728867,2728868,2728869,2728870,2728871,2728872,2728873,2728874,2728875,2728876,2728877,2728878,2728879,2728880,2728881,2728882,2728883,2728884,2728885,2728886,2728887,2728888,2728889,2728890,2728891,2728892,2728893,2728894,2728895],"Cut Red Sandstone":[2584431],"Cut Red Sandstone Slab":[2584688,2584689,2584690],"Cut Sandstone":[2584945],"Cut Sandstone Slab":[2585200,2585202,2585203],"Dandelion":[2585716],"Dark Oak Button":[2585968,2585969,2585972,2585973,2585974,2585975,2585976,2585977,2585980,2585981,2585982,2585983],"Dark Oak Door":[2586208,2586209,2586210,2586211,2586212,2586213,2586214,2586215,2586216,2586217,2586218,2586219,2586220,2586221,2586222,2586223,2586224,2586225,2586226,2586227,2586228,2586229,2586230,2586231,2586232,2586233,2586234,2586235,2586236,2586237,2586238,2586239],"Dark Oak Fence":[2586487],"Dark Oak Fence Gate":[2586736,2586737,2586738,2586739,2586740,2586741,2586742,2586743,2586744,2586745,2586746,2586747,2586748,2586749,2586750,2586751],"Dark Oak Leaves":[2587000,2587001,2587002,2587003],"Dark Oak Log":[2587256,2587257,2587258,2587259,2587262,2587263],"Dark Oak Planks":[2587515],"Dark Oak Pressure Plate":[2587772,2587773],"Dark Oak Sapling":[2588028,2588029],"Dark Oak Sign":[2588272,2588273,2588274,2588275,2588276,2588277,2588278,2588279,2588280,2588281,2588282,2588283,2588284,2588285,2588286,2588287],"Dark Oak Slab":[2588541,2588542,2588543],"Dark Oak Stairs":[2588800,2588801,2588802,2588803,2588804,2588805,2588806,2588807],"Dark Oak Trapdoor":[2589056,2589057,2589058,2589059,2589060,2589061,2589062,2589063,2589064,2589065,2589066,2589067,2589068,2589069,2589070,2589071],"Dark Oak Wall Sign":[2589312,2589313,2589314,2589315],"Dark Oak Wood":[2589568,2589569,2589570,2589571,2589574,2589575],"Dark Prismarine":[2589828],"Dark Prismarine Slab":[2590084,2590085,2590087],"Dark Prismarine Stairs":[2590336,2590337,2590338,2590339,2590340,2590341,2590342,2590343],"Darmstadtium":[2601136],"Daylight Sensor":[2590592,2590593,2590594,2590595,2590596,2590597,2590598,2590599,2590600,2590601,2590602,2590603,2590604,2590605,2590606,2590607,2590608,2590609,2590610,2590611,2590612,2590613,2590614,2590615,2590616,2590617,2590618,2590619,2590620,2590621,2590622,2590623],"Dead Bush":[2590856],"Deepslate":[2704964,2704966,2704967],"Deepslate Brick Slab":[2705480,2705481,2705482],"Deepslate Brick Stairs":[2705736,2705737,2705738,2705739,2705740,2705741,2705742,2705743],"Deepslate Brick Wall":[2705920,2705921,2705922,2705923,2705924,2705925,2705926,2705927,2705928,2705929,2705930,2705931,2705932,2705933,2705934,2705935,2705946,2705984,2705985,2705986,2705987,2705988,2705989,2705990,2705991,2705992,2705993,2705994,2705995,2705996,2705997,2705998,2705999,2706000,2706001,2706002,2706003,2706004,2706005,2706006,2706007,2706008,2706009,2706010,2706011,2706012,2706013,2706014,2706015,2706016,2706017,2706018,2706019,2706020,2706021,2706022,2706023,2706024,2706025,2706026,2706027,2706028,2706029,2706030,2706031,2706032,2706033,2706034,2706035,2706036,2706037,2706038,2706039,2706040,2706041,2706042,2706043,2706044,2706045,2706046,2706047,2706048,2706049,2706050,2706051,2706052,2706053,2706054,2706055,2706056,2706057,2706058,2706059,2706060,2706061,2706062,2706063,2706074,2706112,2706113,2706114,2706115,2706116,2706117,2706118,2706119,2706120,2706121,2706122,2706123,2706124,2706125,2706126,2706127,2706128,2706129,2706130,2706131,2706132,2706133,2706134,2706135,2706136,2706137,2706138,2706139,2706140,2706141,2706142,2706143,2706144,2706145,2706146,2706147,2706148,2706149,2706150,2706151,2706152,2706153,2706154,2706155,2706156,2706157,2706158,2706159,2706160,2706161,2706162,2706163,2706164,2706165,2706166,2706167,2706168,2706169,2706170,2706171,2706172,2706173,2706174,2706175],"Deepslate Bricks":[2705223],"Deepslate Coal Ore":[2722956],"Deepslate Copper Ore":[2724755],"Deepslate Diamond Ore":[2723213],"Deepslate Emerald Ore":[2723470],"Deepslate Gold Ore":[2724498],"Deepslate Iron Ore":[2724241],"Deepslate Lapis Lazuli Ore":[2723727],"Deepslate Redstone Ore":[2723984,2723985],"Deepslate Tile Slab":[2706764,2706765,2706767],"Deepslate Tile Stairs":[2707016,2707017,2707018,2707019,2707020,2707021,2707022,2707023],"Deepslate Tile Wall":[2707200,2707201,2707202,2707203,2707204,2707205,2707206,2707207,2707208,2707209,2707210,2707211,2707212,2707213,2707214,2707215,2707231,2707264,2707265,2707266,2707267,2707268,2707269,2707270,2707271,2707272,2707273,2707274,2707275,2707276,2707277,2707278,2707279,2707280,2707281,2707282,2707283,2707284,2707285,2707286,2707287,2707288,2707289,2707290,2707291,2707292,2707293,2707294,2707295,2707296,2707297,2707298,2707299,2707300,2707301,2707302,2707303,2707304,2707305,2707306,2707307,2707308,2707309,2707310,2707311,2707312,2707313,2707314,2707315,2707316,2707317,2707318,2707319,2707320,2707321,2707322,2707323,2707324,2707325,2707326,2707327,2707328,2707329,2707330,2707331,2707332,2707333,2707334,2707335,2707336,2707337,2707338,2707339,2707340,2707341,2707342,2707343,2707359,2707392,2707393,2707394,2707395,2707396,2707397,2707398,2707399,2707400,2707401,2707402,2707403,2707404,2707405,2707406,2707407,2707408,2707409,2707410,2707411,2707412,2707413,2707414,2707415,2707416,2707417,2707418,2707419,2707420,2707421,2707422,2707423,2707424,2707425,2707426,2707427,2707428,2707429,2707430,2707431,2707432,2707433,2707434,2707435,2707436,2707437,2707438,2707439,2707440,2707441,2707442,2707443,2707444,2707445,2707446,2707447,2707448,2707449,2707450,2707451,2707452,2707453,2707454,2707455],"Deepslate Tiles":[2706508],"Detector Rail":[2591104,2591105,2591106,2591107,2591108,2591109,2591112,2591113,2591114,2591115,2591116,2591117],"Diamond Block":[2591370],"Diamond Ore":[2591627],"Diorite":[2591884],"Diorite Slab":[2592140,2592141,2592143],"Diorite Stairs":[2592392,2592393,2592394,2592395,2592396,2592397,2592398,2592399],"Diorite Wall":[2592512,2592513,2592514,2592515,2592516,2592517,2592518,2592519,2592520,2592521,2592522,2592523,2592524,2592525,2592526,2592527,2592528,2592529,2592530,2592531,2592532,2592533,2592534,2592535,2592536,2592537,2592538,2592539,2592540,2592541,2592542,2592543,2592544,2592545,2592546,2592547,2592548,2592549,2592550,2592551,2592552,2592553,2592554,2592555,2592556,2592557,2592558,2592559,2592560,2592561,2592562,2592563,2592564,2592565,2592566,2592567,2592568,2592569,2592570,2592571,2592572,2592573,2592574,2592575,2592576,2592577,2592578,2592579,2592580,2592581,2592582,2592583,2592584,2592585,2592586,2592587,2592588,2592589,2592590,2592591,2592607,2592640,2592641,2592642,2592643,2592644,2592645,2592646,2592647,2592648,2592649,2592650,2592651,2592652,2592653,2592654,2592655,2592656,2592657,2592658,2592659,2592660,2592661,2592662,2592663,2592664,2592665,2592666,2592667,2592668,2592669,2592670,2592671,2592672,2592673,2592674,2592675,2592676,2592677,2592678,2592679,2592680,2592681,2592682,2592683,2592684,2592685,2592686,2592687,2592688,2592689,2592690,2592691,2592692,2592693,2592694,2592695,2592696,2592697,2592698,2592699,2592700,2592701,2592702,2592703,2592704,2592705,2592706,2592707,2592708,2592709,2592710,2592711,2592712,2592713,2592714,2592715,2592716,2592717,2592718,2592719,2592735],"Dirt":[2592912,2592913,2592914],"Double Tallgrass":[2593168,2593169],"Dragon Egg":[2593426],"Dried Kelp Block":[2593683],"Dubnium":[2601393],"Dyed Candle":[2729344,2729345,2729346,2729347,2729348,2729349,2729350,2729351,2729352,2729353,2729354,2729355,2729356,2729357,2729358,2729359,2729360,2729361,2729362,2729363,2729364,2729365,2729366,2729367,2729368,2729369,2729370,2729371,2729372,2729373,2729374,2729375,2729376,2729377,2729378,2729379,2729380,2729381,2729382,2729383,2729384,2729385,2729386,2729387,2729388,2729389,2729390,2729391,2729392,2729393,2729394,2729395,2729396,2729397,2729398,2729399,2729400,2729401,2729402,2729403,2729404,2729405,2729406,2729407,2729408,2729409,2729410,2729411,2729412,2729413,2729414,2729415,2729416,2729417,2729418,2729419,2729420,2729421,2729422,2729423,2729424,2729425,2729426,2729427,2729428,2729429,2729430,2729431,2729432,2729433,2729434,2729435,2729436,2729437,2729438,2729439,2729440,2729441,2729442,2729443,2729444,2729445,2729446,2729447,2729448,2729449,2729450,2729451,2729452,2729453,2729454,2729455,2729456,2729457,2729458,2729459,2729460,2729461,2729462,2729463,2729464,2729465,2729466,2729467,2729468,2729469,2729470,2729471],"Dyed Shulker Box":[2593936,2593937,2593938,2593939,2593940,2593941,2593942,2593943,2593944,2593945,2593946,2593947,2593948,2593949,2593950,2593951],"Dysprosium":[2601650],"Einsteinium":[2601907],"Element Constructor":[2600108,2600109,2600110,2600111],"Emerald Block":[2624781],"Emerald Ore":[2625038],"Enchanting Table":[2625295],"End Portal Frame":[2625552,2625553,2625554,2625555,2625556,2625557,2625558,2625559],"End Rod":[2625808,2625809,2625810,2625811,2625812,2625813],"End Stone":[2626066],"End Stone Brick Slab":[2626321,2626322,2626323],"End Stone Brick Stairs":[2626576,2626577,2626578,2626579,2626580,2626581,2626582,2626583],"End Stone Brick Wall":[2626816,2626817,2626818,2626819,2626820,2626821,2626822,2626823,2626824,2626825,2626826,2626827,2626828,2626829,2626830,2626831,2626832,2626833,2626834,2626835,2626836,2626837,2626838,2626839,2626840,2626841,2626842,2626843,2626844,2626845,2626846,2626847,2626848,2626849,2626850,2626851,2626852,2626853,2626854,2626855,2626856,2626857,2626858,2626859,2626860,2626861,2626862,2626863,2626864,2626865,2626866,2626867,2626868,2626869,2626870,2626871,2626872,2626873,2626874,2626875,2626876,2626877,2626878,2626879,2626885,2626896,2626897,2626898,2626899,2626900,2626901,2626902,2626903,2626904,2626905,2626906,2626907,2626908,2626909,2626910,2626911,2626944,2626945,2626946,2626947,2626948,2626949,2626950,2626951,2626952,2626953,2626954,2626955,2626956,2626957,2626958,2626959,2626960,2626961,2626962,2626963,2626964,2626965,2626966,2626967,2626968,2626969,2626970,2626971,2626972,2626973,2626974,2626975,2626976,2626977,2626978,2626979,2626980,2626981,2626982,2626983,2626984,2626985,2626986,2626987,2626988,2626989,2626990,2626991,2626992,2626993,2626994,2626995,2626996,2626997,2626998,2626999,2627000,2627001,2627002,2627003,2627004,2627005,2627006,2627007,2627013,2627024,2627025,2627026,2627027,2627028,2627029,2627030,2627031,2627032,2627033,2627034,2627035,2627036,2627037,2627038,2627039],"End Stone Bricks":[2627094],"Ender Chest":[2627348,2627349,2627350,2627351],"Erbium":[2602164],"Europium":[2602421],"Fake Wooden Slab":[2627608,2627609,2627610],"Farmland":[2627864,2627865,2627866,2627867,2627868,2627869,2627870,2627871],"Fermium":[2602678],"Fern":[2628122],"Fire Block":[2628368,2628369,2628370,2628371,2628372,2628373,2628374,2628375,2628376,2628377,2628378,2628379,2628380,2628381,2628382,2628383],"Flerovium":[2602935],"Fletching Table":[2628636],"Flower Pot":[2628893],"Flowering Azalea Leaves":[2736060,2736061,2736062,2736063],"Fluorine":[2603192],"Francium":[2603449],"Froglight":[2734001,2734002,2734003,2734005,2734006,2734007,2734013,2734014,2734015],"Frosted Ice":[2629148,2629149,2629150,2629151],"Furnace":[2629400,2629401,2629402,2629403,2629404,2629405,2629406,2629407],"Gadolinium":[2603706],"Gallium":[2603963],"Germanium":[2604220],"Gilded Blackstone":[2727582],"Glass":[2629664],"Glass Pane":[2629921],"Glazed Terracotta":[2697984,2697985,2697986,2697987,2697988,2697989,2697990,2697991,2697992,2697993,2697994,2697995,2697996,2697997,2697998,2697999,2698000,2698001,2698002,2698003,2698004,2698005,2698006,2698007,2698008,2698009,2698010,2698011,2698012,2698013,2698014,2698015,2698016,2698017,2698018,2698019,2698020,2698021,2698022,2698023,2698024,2698025,2698026,2698027,2698028,2698029,2698030,2698031,2698032,2698033,2698034,2698035,2698036,2698037,2698038,2698039,2698040,2698041,2698042,2698043,2698044,2698045,2698046,2698047],"Glow Item Frame":[2735280,2735281,2735284,2735285,2735286,2735287,2735288,2735289,2735292,2735293,2735294,2735295],"Glow Lichen":[2736832,2736833,2736834,2736835,2736836,2736837,2736838,2736839,2736840,2736841,2736842,2736843,2736844,2736845,2736846,2736847,2736848,2736849,2736850,2736851,2736852,2736853,2736854,2736855,2736856,2736857,2736858,2736859,2736860,2736861,2736862,2736863,2736864,2736865,2736866,2736867,2736868,2736869,2736870,2736871,2736872,2736873,2736874,2736875,2736876,2736877,2736878,2736879,2736880,2736881,2736882,2736883,2736884,2736885,2736886,2736887,2736888,2736889,2736890,2736891,2736892,2736893,2736894,2736895],"Glowing Obsidian":[2630178],"Glowstone":[2630435],"Gold":[2604477],"Gold Block":[2630692],"Gold Ore":[2630949],"Granite":[2631206],"Granite Slab":[2631461,2631462,2631463],"Granite Stairs":[2631720,2631721,2631722,2631723,2631724,2631725,2631726,2631727],"Granite Wall":[2631936,2631937,2631938,2631939,2631940,2631941,2631942,2631943,2631944,2631945,2631946,2631947,2631948,2631949,2631950,2631951,2631952,2631953,2631954,2631955,2631956,2631957,2631958,2631959,2631960,2631961,2631962,2631963,2631964,2631965,2631966,2631967,2631968,2631969,2631970,2631971,2631972,2631973,2631974,2631975,2631976,2631977,2631978,2631979,2631980,2631981,2631982,2631983,2631984,2631985,2631986,2631987,2631988,2631989,2631990,2631991,2631992,2631993,2631994,2631995,2631996,2631997,2631998,2631999,2632032,2632033,2632034,2632035,2632036,2632037,2632038,2632039,2632040,2632041,2632042,2632043,2632044,2632045,2632046,2632047,2632057,2632064,2632065,2632066,2632067,2632068,2632069,2632070,2632071,2632072,2632073,2632074,2632075,2632076,2632077,2632078,2632079,2632080,2632081,2632082,2632083,2632084,2632085,2632086,2632087,2632088,2632089,2632090,2632091,2632092,2632093,2632094,2632095,2632096,2632097,2632098,2632099,2632100,2632101,2632102,2632103,2632104,2632105,2632106,2632107,2632108,2632109,2632110,2632111,2632112,2632113,2632114,2632115,2632116,2632117,2632118,2632119,2632120,2632121,2632122,2632123,2632124,2632125,2632126,2632127,2632160,2632161,2632162,2632163,2632164,2632165,2632166,2632167,2632168,2632169,2632170,2632171,2632172,2632173,2632174,2632175,2632185],"Grass":[2632234],"Grass Path":[2632491],"Gravel":[2632748],"Green Torch":[2633514,2633515,2633516,2633517,2633518],"Hafnium":[2604734],"Hanging Roots":[2730409],"Hardened Clay":[2633776],"Hardened Glass":[2634033],"Hardened Glass Pane":[2634290],"Hassium":[2604991],"Hay Bale":[2634545,2634546,2634547],"Heat Block":[2578263],"Helium":[2605248],"Holmium":[2605505],"Honeycomb Block":[2722699],"Hopper":[2634800,2634801,2634804,2634806,2634807,2634808,2634809,2634812,2634814,2634815],"Hydrogen":[2605762],"Ice":[2635061],"Indium":[2606019],"Infested Chiseled Stone Brick":[2635318],"Infested Cobblestone":[2635575],"Infested Cracked Stone Brick":[2635832],"Infested Mossy Stone Brick":[2636089],"Infested Stone":[2636346],"Infested Stone Brick":[2636603],"Invisible Bedrock":[2637374],"Iodine":[2606276],"Iridium":[2606533],"Iron":[2606790],"Iron Bars":[2637888],"Iron Block":[2637631],"Iron Door":[2638144,2638145,2638146,2638147,2638148,2638149,2638150,2638151,2638152,2638153,2638154,2638155,2638156,2638157,2638158,2638159,2638160,2638161,2638162,2638163,2638164,2638165,2638166,2638167,2638168,2638169,2638170,2638171,2638172,2638173,2638174,2638175],"Iron Ore":[2638402],"Iron Trapdoor":[2638656,2638657,2638658,2638659,2638660,2638661,2638662,2638663,2638664,2638665,2638666,2638667,2638668,2638669,2638670,2638671],"Item Frame":[2638912,2638913,2638916,2638917,2638918,2638919,2638920,2638921,2638924,2638925,2638926,2638927],"Jack o'Lantern":[2647396,2647397,2647398,2647399],"Jukebox":[2639173],"Jungle Button":[2639426,2639427,2639428,2639429,2639430,2639431,2639434,2639435,2639436,2639437,2639438,2639439],"Jungle Door":[2639680,2639681,2639682,2639683,2639684,2639685,2639686,2639687,2639688,2639689,2639690,2639691,2639692,2639693,2639694,2639695,2639696,2639697,2639698,2639699,2639700,2639701,2639702,2639703,2639704,2639705,2639706,2639707,2639708,2639709,2639710,2639711],"Jungle Fence":[2639944],"Jungle Fence Gate":[2640192,2640193,2640194,2640195,2640196,2640197,2640198,2640199,2640200,2640201,2640202,2640203,2640204,2640205,2640206,2640207],"Jungle Leaves":[2640456,2640457,2640458,2640459],"Jungle Log":[2640712,2640713,2640714,2640715,2640718,2640719],"Jungle Planks":[2640972],"Jungle Pressure Plate":[2641228,2641229],"Jungle Sapling":[2641486,2641487],"Jungle Sign":[2641728,2641729,2641730,2641731,2641732,2641733,2641734,2641735,2641736,2641737,2641738,2641739,2641740,2641741,2641742,2641743],"Jungle Slab":[2642000,2642001,2642002],"Jungle Stairs":[2642256,2642257,2642258,2642259,2642260,2642261,2642262,2642263],"Jungle Trapdoor":[2642512,2642513,2642514,2642515,2642516,2642517,2642518,2642519,2642520,2642521,2642522,2642523,2642524,2642525,2642526,2642527],"Jungle Wall Sign":[2642768,2642769,2642770,2642771],"Jungle Wood":[2643024,2643025,2643028,2643029,2643030,2643031],"Krypton":[2607047],"Lab Table":[2643284,2643285,2643286,2643287],"Ladder":[2643540,2643541,2643542,2643543],"Lantern":[2643798,2643799],"Lanthanum":[2607304],"Lapis Lazuli Block":[2644056],"Lapis Lazuli Ore":[2644313],"Large Fern":[2644570,2644571],"Lava":[2644800,2644801,2644802,2644803,2644804,2644805,2644806,2644807,2644808,2644809,2644810,2644811,2644812,2644813,2644814,2644815,2644816,2644817,2644818,2644819,2644820,2644821,2644822,2644823,2644824,2644825,2644826,2644827,2644828,2644829,2644830,2644831],"Lava Cauldron":[2732208,2732209,2732210,2732211,2732212,2732213],"Lawrencium":[2607561],"Lead":[2607818],"Lectern":[2645080,2645081,2645082,2645083,2645084,2645085,2645086,2645087],"Legacy Stonecutter":[2645341],"Lever":[2645584,2645585,2645586,2645587,2645588,2645589,2645590,2645591,2645592,2645593,2645594,2645595,2645596,2645597,2645598,2645599],"Light Block":[2703680,2703681,2703682,2703683,2703684,2703685,2703686,2703687,2703688,2703689,2703690,2703691,2703692,2703693,2703694,2703695],"Lightning Rod":[2727834,2727835,2727836,2727837,2727838,2727839],"Lilac":[2646368,2646369],"Lily Pad":[2646883],"Lily of the Valley":[2646626],"Lithium":[2608075],"Livermorium":[2608332],"Loom":[2647652,2647653,2647654,2647655],"Lutetium":[2608589],"Magma Block":[2648168],"Magnesium":[2608846],"Manganese":[2609103],"Mangrove Button":[2717040,2717041,2717044,2717045,2717046,2717047,2717048,2717049,2717052,2717053,2717054,2717055],"Mangrove Door":[2718560,2718561,2718562,2718563,2718564,2718565,2718566,2718567,2718568,2718569,2718570,2718571,2718572,2718573,2718574,2718575,2718576,2718577,2718578,2718579,2718580,2718581,2718582,2718583,2718584,2718585,2718586,2718587,2718588,2718589,2718590,2718591],"Mangrove Fence":[2713190],"Mangrove Fence Gate":[2719344,2719345,2719346,2719347,2719348,2719349,2719350,2719351,2719352,2719353,2719354,2719355,2719356,2719357,2719358,2719359],"Mangrove Leaves":[2735548,2735549,2735550,2735551],"Mangrove Log":[2714728,2714729,2714732,2714733,2714734,2714735],"Mangrove Planks":[2712419],"Mangrove Pressure Plate":[2717816,2717817],"Mangrove Roots":[2733493],"Mangrove Sign":[2720896,2720897,2720898,2720899,2720900,2720901,2720902,2720903,2720904,2720905,2720906,2720907,2720908,2720909,2720910,2720911],"Mangrove Slab":[2713960,2713961,2713963],"Mangrove Stairs":[2720128,2720129,2720130,2720131,2720132,2720133,2720134,2720135],"Mangrove Trapdoor":[2716272,2716273,2716274,2716275,2716276,2716277,2716278,2716279,2716280,2716281,2716282,2716283,2716284,2716285,2716286,2716287],"Mangrove Wall Sign":[2721668,2721669,2721670,2721671],"Mangrove Wood":[2715498,2715499,2715500,2715501,2715502,2715503],"Material Reducer":[2648424,2648425,2648426,2648427],"Meitnerium":[2609360],"Melon Block":[2648682],"Melon Stem":[2648896,2648897,2648898,2648899,2648900,2648901,2648902,2648903,2648904,2648905,2648906,2648907,2648908,2648909,2648910,2648911,2648928,2648929,2648930,2648931,2648932,2648933,2648934,2648935,2648944,2648945,2648946,2648947,2648948,2648949,2648950,2648951,2648952,2648953,2648954,2648955,2648956,2648957,2648958,2648959],"Mendelevium":[2609617],"Mercury":[2609874],"Mob Head":[2649152,2649153,2649154,2649156,2649157,2649158,2649159,2649160,2649161,2649162,2649164,2649165,2649166,2649167,2649184,2649185,2649186,2649188,2649189,2649190,2649191,2649200,2649201,2649202,2649204,2649205,2649206,2649207,2649208,2649209,2649210,2649212,2649213,2649214,2649215],"Molybdenum":[2610131],"Monster Spawner":[2649453],"Moscovium":[2610388],"Mossy Cobblestone":[2649710],"Mossy Cobblestone Slab":[2649965,2649966,2649967],"Mossy Cobblestone Stairs":[2650224,2650225,2650226,2650227,2650228,2650229,2650230,2650231],"Mossy Cobblestone Wall":[2650401,2650416,2650417,2650418,2650419,2650420,2650421,2650422,2650423,2650424,2650425,2650426,2650427,2650428,2650429,2650430,2650431,2650432,2650433,2650434,2650435,2650436,2650437,2650438,2650439,2650440,2650441,2650442,2650443,2650444,2650445,2650446,2650447,2650448,2650449,2650450,2650451,2650452,2650453,2650454,2650455,2650456,2650457,2650458,2650459,2650460,2650461,2650462,2650463,2650464,2650465,2650466,2650467,2650468,2650469,2650470,2650471,2650472,2650473,2650474,2650475,2650476,2650477,2650478,2650479,2650480,2650481,2650482,2650483,2650484,2650485,2650486,2650487,2650488,2650489,2650490,2650491,2650492,2650493,2650494,2650495,2650529,2650544,2650545,2650546,2650547,2650548,2650549,2650550,2650551,2650552,2650553,2650554,2650555,2650556,2650557,2650558,2650559,2650560,2650561,2650562,2650563,2650564,2650565,2650566,2650567,2650568,2650569,2650570,2650571,2650572,2650573,2650574,2650575,2650576,2650577,2650578,2650579,2650580,2650581,2650582,2650583,2650584,2650585,2650586,2650587,2650588,2650589,2650590,2650591,2650592,2650593,2650594,2650595,2650596,2650597,2650598,2650599,2650600,2650601,2650602,2650603,2650604,2650605,2650606,2650607,2650608,2650609,2650610,2650611,2650612,2650613,2650614,2650615,2650616,2650617,2650618,2650619,2650620,2650621,2650622,2650623],"Mossy Stone Brick Slab":[2650736,2650738,2650739],"Mossy Stone Brick Stairs":[2650992,2650993,2650994,2650995,2650996,2650997,2650998,2650999],"Mossy Stone Brick Wall":[2651172,2651184,2651185,2651186,2651187,2651188,2651189,2651190,2651191,2651192,2651193,2651194,2651195,2651196,2651197,2651198,2651199,2651200,2651201,2651202,2651203,2651204,2651205,2651206,2651207,2651208,2651209,2651210,2651211,2651212,2651213,2651214,2651215,2651216,2651217,2651218,2651219,2651220,2651221,2651222,2651223,2651224,2651225,2651226,2651227,2651228,2651229,2651230,2651231,2651232,2651233,2651234,2651235,2651236,2651237,2651238,2651239,2651240,2651241,2651242,2651243,2651244,2651245,2651246,2651247,2651248,2651249,2651250,2651251,2651252,2651253,2651254,2651255,2651256,2651257,2651258,2651259,2651260,2651261,2651262,2651263,2651300,2651312,2651313,2651314,2651315,2651316,2651317,2651318,2651319,2651320,2651321,2651322,2651323,2651324,2651325,2651326,2651327,2651328,2651329,2651330,2651331,2651332,2651333,2651334,2651335,2651336,2651337,2651338,2651339,2651340,2651341,2651342,2651343,2651344,2651345,2651346,2651347,2651348,2651349,2651350,2651351,2651352,2651353,2651354,2651355,2651356,2651357,2651358,2651359,2651360,2651361,2651362,2651363,2651364,2651365,2651366,2651367,2651368,2651369,2651370,2651371,2651372,2651373,2651374,2651375,2651376,2651377,2651378,2651379,2651380,2651381,2651382,2651383,2651384,2651385,2651386,2651387,2651388,2651389,2651390,2651391],"Mossy Stone Bricks":[2651509],"Mud":[2725526],"Mud Brick Slab":[2726040,2726041,2726042],"Mud Brick Stairs":[2726296,2726297,2726298,2726299,2726300,2726301,2726302,2726303],"Mud Brick Wall":[2726400,2726401,2726402,2726403,2726404,2726405,2726406,2726407,2726408,2726409,2726410,2726411,2726412,2726413,2726414,2726415,2726416,2726417,2726418,2726419,2726420,2726421,2726422,2726423,2726424,2726425,2726426,2726427,2726428,2726429,2726430,2726431,2726432,2726433,2726434,2726435,2726436,2726437,2726438,2726439,2726440,2726441,2726442,2726443,2726444,2726445,2726446,2726447,2726448,2726449,2726450,2726451,2726452,2726453,2726454,2726455,2726456,2726457,2726458,2726459,2726460,2726461,2726462,2726463,2726474,2726480,2726481,2726482,2726483,2726484,2726485,2726486,2726487,2726488,2726489,2726490,2726491,2726492,2726493,2726494,2726495,2726528,2726529,2726530,2726531,2726532,2726533,2726534,2726535,2726536,2726537,2726538,2726539,2726540,2726541,2726542,2726543,2726544,2726545,2726546,2726547,2726548,2726549,2726550,2726551,2726552,2726553,2726554,2726555,2726556,2726557,2726558,2726559,2726560,2726561,2726562,2726563,2726564,2726565,2726566,2726567,2726568,2726569,2726570,2726571,2726572,2726573,2726574,2726575,2726576,2726577,2726578,2726579,2726580,2726581,2726582,2726583,2726584,2726585,2726586,2726587,2726588,2726589,2726590,2726591,2726602,2726608,2726609,2726610,2726611,2726612,2726613,2726614,2726615,2726616,2726617,2726618,2726619,2726620,2726621,2726622,2726623],"Mud Bricks":[2725783],"Muddy Mangrove Roots":[2733748,2733750,2733751],"Mushroom Stem":[2651766],"Mycelium":[2652023],"Neodymium":[2610645],"Neon":[2610902],"Neptunium":[2611159],"Nether Brick Fence":[2652280],"Nether Brick Slab":[2652536,2652537,2652539],"Nether Brick Stairs":[2652792,2652793,2652794,2652795,2652796,2652797,2652798,2652799],"Nether Brick Wall":[2652971,2652976,2652977,2652978,2652979,2652980,2652981,2652982,2652983,2652984,2652985,2652986,2652987,2652988,2652989,2652990,2652991,2652992,2652993,2652994,2652995,2652996,2652997,2652998,2652999,2653000,2653001,2653002,2653003,2653004,2653005,2653006,2653007,2653008,2653009,2653010,2653011,2653012,2653013,2653014,2653015,2653016,2653017,2653018,2653019,2653020,2653021,2653022,2653023,2653024,2653025,2653026,2653027,2653028,2653029,2653030,2653031,2653032,2653033,2653034,2653035,2653036,2653037,2653038,2653039,2653040,2653041,2653042,2653043,2653044,2653045,2653046,2653047,2653048,2653049,2653050,2653051,2653052,2653053,2653054,2653055,2653099,2653104,2653105,2653106,2653107,2653108,2653109,2653110,2653111,2653112,2653113,2653114,2653115,2653116,2653117,2653118,2653119,2653120,2653121,2653122,2653123,2653124,2653125,2653126,2653127,2653128,2653129,2653130,2653131,2653132,2653133,2653134,2653135,2653136,2653137,2653138,2653139,2653140,2653141,2653142,2653143,2653144,2653145,2653146,2653147,2653148,2653149,2653150,2653151,2653152,2653153,2653154,2653155,2653156,2653157,2653158,2653159,2653160,2653161,2653162,2653163,2653164,2653165,2653166,2653167,2653168,2653169,2653170,2653171,2653172,2653173,2653174,2653175,2653176,2653177,2653178,2653179,2653180,2653181,2653182,2653183],"Nether Bricks":[2653308],"Nether Gold Ore":[2725269],"Nether Portal":[2653564,2653565],"Nether Quartz Ore":[2653822],"Nether Reactor Core":[2654079],"Nether Wart":[2654336,2654337,2654338,2654339],"Nether Wart Block":[2654593],"Netherite Block":[2731180],"Netherrack":[2654850],"Nickel":[2611416],"Nihonium":[2611673],"Niobium":[2611930],"Nitrogen":[2612187],"Nobelium":[2612444],"Note Block":[2655107],"Oak Button":[2655360,2655361,2655364,2655365,2655366,2655367,2655368,2655369,2655372,2655373,2655374,2655375],"Oak Door":[2655616,2655617,2655618,2655619,2655620,2655621,2655622,2655623,2655624,2655625,2655626,2655627,2655628,2655629,2655630,2655631,2655632,2655633,2655634,2655635,2655636,2655637,2655638,2655639,2655640,2655641,2655642,2655643,2655644,2655645,2655646,2655647],"Oak Fence":[2655878],"Oak Fence Gate":[2656128,2656129,2656130,2656131,2656132,2656133,2656134,2656135,2656136,2656137,2656138,2656139,2656140,2656141,2656142,2656143],"Oak Leaves":[2656392,2656393,2656394,2656395],"Oak Log":[2656648,2656649,2656650,2656651,2656652,2656653],"Oak Planks":[2656906],"Oak Pressure Plate":[2657162,2657163],"Oak Sapling":[2657420,2657421],"Oak Sign":[2657664,2657665,2657666,2657667,2657668,2657669,2657670,2657671,2657672,2657673,2657674,2657675,2657676,2657677,2657678,2657679],"Oak Slab":[2657932,2657934,2657935],"Oak Stairs":[2658184,2658185,2658186,2658187,2658188,2658189,2658190,2658191],"Oak Trapdoor":[2658448,2658449,2658450,2658451,2658452,2658453,2658454,2658455,2658456,2658457,2658458,2658459,2658460,2658461,2658462,2658463],"Oak Wall Sign":[2658704,2658705,2658706,2658707],"Oak Wood":[2658960,2658961,2658962,2658963,2658966,2658967],"Obsidian":[2659219],"Oganesson":[2612701],"Orange Tulip":[2659733],"Osmium":[2612958],"Oxeye Daisy":[2659990],"Oxygen":[2613215],"Packed Ice":[2660247],"Packed Mud":[2726811],"Palladium":[2613472],"Peony":[2660504,2660505],"Phosphorus":[2613729],"Pink Petals":[2741712,2741713,2741714,2741715,2741716,2741717,2741718,2741719,2741720,2741721,2741722,2741723,2741724,2741725,2741726,2741727],"Pink Tulip":[2661018],"Platinum":[2613986],"Plutonium":[2614243],"Podzol":[2661275],"Polished Andesite":[2661532],"Polished Andesite Slab":[2661788,2661789,2661791],"Polished Andesite Stairs":[2662040,2662041,2662042,2662043,2662044,2662045,2662046,2662047],"Polished Basalt":[2699053,2699054,2699055],"Polished Blackstone":[2700597],"Polished Blackstone Brick Slab":[2702652,2702653,2702655],"Polished Blackstone Brick Stairs":[2702904,2702905,2702906,2702907,2702908,2702909,2702910,2702911],"Polished Blackstone Brick Wall":[2703104,2703105,2703106,2703107,2703108,2703109,2703110,2703111,2703112,2703113,2703114,2703115,2703116,2703117,2703118,2703119,2703120,2703121,2703122,2703123,2703124,2703125,2703126,2703127,2703128,2703129,2703130,2703131,2703132,2703133,2703134,2703135,2703136,2703137,2703138,2703139,2703140,2703141,2703142,2703143,2703144,2703145,2703146,2703147,2703148,2703149,2703150,2703151,2703152,2703153,2703154,2703155,2703156,2703157,2703158,2703159,2703160,2703161,2703162,2703163,2703164,2703165,2703166,2703167,2703215,2703216,2703217,2703218,2703219,2703220,2703221,2703222,2703223,2703224,2703225,2703226,2703227,2703228,2703229,2703230,2703231,2703232,2703233,2703234,2703235,2703236,2703237,2703238,2703239,2703240,2703241,2703242,2703243,2703244,2703245,2703246,2703247,2703248,2703249,2703250,2703251,2703252,2703253,2703254,2703255,2703256,2703257,2703258,2703259,2703260,2703261,2703262,2703263,2703264,2703265,2703266,2703267,2703268,2703269,2703270,2703271,2703272,2703273,2703274,2703275,2703276,2703277,2703278,2703279,2703280,2703281,2703282,2703283,2703284,2703285,2703286,2703287,2703288,2703289,2703290,2703291,2703292,2703293,2703294,2703295,2703343,2703344,2703345,2703346,2703347,2703348,2703349,2703350,2703351,2703352,2703353,2703354,2703355,2703356,2703357,2703358,2703359],"Polished Blackstone Bricks":[2702396],"Polished Blackstone Button":[2700850,2700851,2700852,2700853,2700854,2700855,2700858,2700859,2700860,2700861,2700862,2700863],"Polished Blackstone Pressure Plate":[2701110,2701111],"Polished Blackstone Slab":[2701368,2701369,2701370],"Polished Blackstone Stairs":[2701624,2701625,2701626,2701627,2701628,2701629,2701630,2701631],"Polished Blackstone Wall":[2701824,2701825,2701826,2701827,2701828,2701829,2701830,2701831,2701832,2701833,2701834,2701835,2701836,2701837,2701838,2701839,2701840,2701841,2701842,2701843,2701844,2701845,2701846,2701847,2701848,2701849,2701850,2701851,2701852,2701853,2701854,2701855,2701856,2701857,2701858,2701859,2701860,2701861,2701862,2701863,2701864,2701865,2701866,2701867,2701868,2701869,2701870,2701871,2701872,2701873,2701874,2701875,2701876,2701877,2701878,2701879,2701880,2701881,2701882,2701883,2701884,2701885,2701886,2701887,2701930,2701936,2701937,2701938,2701939,2701940,2701941,2701942,2701943,2701944,2701945,2701946,2701947,2701948,2701949,2701950,2701951,2701952,2701953,2701954,2701955,2701956,2701957,2701958,2701959,2701960,2701961,2701962,2701963,2701964,2701965,2701966,2701967,2701968,2701969,2701970,2701971,2701972,2701973,2701974,2701975,2701976,2701977,2701978,2701979,2701980,2701981,2701982,2701983,2701984,2701985,2701986,2701987,2701988,2701989,2701990,2701991,2701992,2701993,2701994,2701995,2701996,2701997,2701998,2701999,2702000,2702001,2702002,2702003,2702004,2702005,2702006,2702007,2702008,2702009,2702010,2702011,2702012,2702013,2702014,2702015,2702058,2702064,2702065,2702066,2702067,2702068,2702069,2702070,2702071,2702072,2702073,2702074,2702075,2702076,2702077,2702078,2702079],"Polished Deepslate":[2708821],"Polished Deepslate Slab":[2709076,2709078,2709079],"Polished Deepslate Stairs":[2709328,2709329,2709330,2709331,2709332,2709333,2709334,2709335],"Polished Deepslate Wall":[2709512,2709520,2709521,2709522,2709523,2709524,2709525,2709526,2709527,2709528,2709529,2709530,2709531,2709532,2709533,2709534,2709535,2709568,2709569,2709570,2709571,2709572,2709573,2709574,2709575,2709576,2709577,2709578,2709579,2709580,2709581,2709582,2709583,2709584,2709585,2709586,2709587,2709588,2709589,2709590,2709591,2709592,2709593,2709594,2709595,2709596,2709597,2709598,2709599,2709600,2709601,2709602,2709603,2709604,2709605,2709606,2709607,2709608,2709609,2709610,2709611,2709612,2709613,2709614,2709615,2709616,2709617,2709618,2709619,2709620,2709621,2709622,2709623,2709624,2709625,2709626,2709627,2709628,2709629,2709630,2709631,2709640,2709648,2709649,2709650,2709651,2709652,2709653,2709654,2709655,2709656,2709657,2709658,2709659,2709660,2709661,2709662,2709663,2709696,2709697,2709698,2709699,2709700,2709701,2709702,2709703,2709704,2709705,2709706,2709707,2709708,2709709,2709710,2709711,2709712,2709713,2709714,2709715,2709716,2709717,2709718,2709719,2709720,2709721,2709722,2709723,2709724,2709725,2709726,2709727,2709728,2709729,2709730,2709731,2709732,2709733,2709734,2709735,2709736,2709737,2709738,2709739,2709740,2709741,2709742,2709743,2709744,2709745,2709746,2709747,2709748,2709749,2709750,2709751,2709752,2709753,2709754,2709755,2709756,2709757,2709758,2709759],"Polished Diorite":[2662303],"Polished Diorite Slab":[2662560,2662561,2662562],"Polished Diorite Stairs":[2662816,2662817,2662818,2662819,2662820,2662821,2662822,2662823],"Polished Granite":[2663074],"Polished Granite Slab":[2663329,2663330,2663331],"Polished Granite Stairs":[2663584,2663585,2663586,2663587,2663588,2663589,2663590,2663591],"Polonium":[2614500],"Poppy":[2663845],"Potassium":[2614757],"Potato Block":[2664096,2664097,2664098,2664099,2664100,2664101,2664102,2664103],"Potion Cauldron":[2732464,2732465,2732466,2732467,2732468,2732469],"Powered Rail":[2664354,2664355,2664356,2664357,2664358,2664359,2664362,2664363,2664364,2664365,2664366,2664367],"Praseodymium":[2615014],"Prismarine":[2664616],"Prismarine Bricks":[2664873],"Prismarine Bricks Slab":[2665128,2665130,2665131],"Prismarine Bricks Stairs":[2665384,2665385,2665386,2665387,2665388,2665389,2665390,2665391],"Prismarine Slab":[2665644,2665645,2665646],"Prismarine Stairs":[2665896,2665897,2665898,2665899,2665900,2665901,2665902,2665903],"Prismarine Wall":[2665984,2665985,2665986,2665987,2665988,2665989,2665990,2665991,2665992,2665993,2665994,2665995,2665996,2665997,2665998,2665999,2666000,2666001,2666002,2666003,2666004,2666005,2666006,2666007,2666008,2666009,2666010,2666011,2666012,2666013,2666014,2666015,2666016,2666017,2666018,2666019,2666020,2666021,2666022,2666023,2666024,2666025,2666026,2666027,2666028,2666029,2666030,2666031,2666032,2666033,2666034,2666035,2666036,2666037,2666038,2666039,2666040,2666041,2666042,2666043,2666044,2666045,2666046,2666047,2666080,2666081,2666082,2666083,2666084,2666085,2666086,2666087,2666088,2666089,2666090,2666091,2666092,2666093,2666094,2666095,2666110,2666112,2666113,2666114,2666115,2666116,2666117,2666118,2666119,2666120,2666121,2666122,2666123,2666124,2666125,2666126,2666127,2666128,2666129,2666130,2666131,2666132,2666133,2666134,2666135,2666136,2666137,2666138,2666139,2666140,2666141,2666142,2666143,2666144,2666145,2666146,2666147,2666148,2666149,2666150,2666151,2666152,2666153,2666154,2666155,2666156,2666157,2666158,2666159,2666160,2666161,2666162,2666163,2666164,2666165,2666166,2666167,2666168,2666169,2666170,2666171,2666172,2666173,2666174,2666175,2666208,2666209,2666210,2666211,2666212,2666213,2666214,2666215,2666216,2666217,2666218,2666219,2666220,2666221,2666222,2666223,2666238],"Promethium":[2615271],"Protactinium":[2615528],"Pumpkin":[2666415],"Pumpkin Stem":[2666640,2666641,2666642,2666643,2666644,2666645,2666646,2666647,2666648,2666649,2666650,2666651,2666652,2666653,2666654,2666655,2666656,2666657,2666658,2666659,2666660,2666661,2666662,2666663,2666664,2666665,2666666,2666667,2666668,2666669,2666670,2666671,2666680,2666681,2666682,2666683,2666684,2666685,2666686,2666687],"Purple Torch":[2667184,2667185,2667187,2667190,2667191],"Purpur Block":[2667443],"Purpur Pillar":[2667700,2667701,2667702],"Purpur Slab":[2667956,2667957,2667959],"Purpur Stairs":[2668208,2668209,2668210,2668211,2668212,2668213,2668214,2668215],"Quartz Block":[2668471],"Quartz Bricks":[2709849],"Quartz Pillar":[2668728,2668729,2668730],"Quartz Slab":[2668984,2668985,2668987],"Quartz Stairs":[2669240,2669241,2669242,2669243,2669244,2669245,2669246,2669247],"Radium":[2615785],"Radon":[2616042],"Rail":[2669490,2669491,2669496,2669497,2669498,2669499,2669500,2669501,2669502,2669503],"Raw Copper Block":[2703938],"Raw Gold Block":[2704195],"Raw Iron Block":[2704452],"Red Mushroom":[2670013],"Red Mushroom Block":[2670260,2670262,2670263,2670264,2670265,2670266,2670267,2670268,2670269,2670270,2670271],"Red Nether Brick Slab":[2670525,2670526,2670527],"Red Nether Brick Stairs":[2670784,2670785,2670786,2670787,2670788,2670789,2670790,2670791],"Red Nether Brick Wall":[2670848,2670849,2670850,2670851,2670852,2670853,2670854,2670855,2670856,2670857,2670858,2670859,2670860,2670861,2670862,2670863,2670865,2670912,2670913,2670914,2670915,2670916,2670917,2670918,2670919,2670920,2670921,2670922,2670923,2670924,2670925,2670926,2670927,2670928,2670929,2670930,2670931,2670932,2670933,2670934,2670935,2670936,2670937,2670938,2670939,2670940,2670941,2670942,2670943,2670944,2670945,2670946,2670947,2670948,2670949,2670950,2670951,2670952,2670953,2670954,2670955,2670956,2670957,2670958,2670959,2670960,2670961,2670962,2670963,2670964,2670965,2670966,2670967,2670968,2670969,2670970,2670971,2670972,2670973,2670974,2670975,2670976,2670977,2670978,2670979,2670980,2670981,2670982,2670983,2670984,2670985,2670986,2670987,2670988,2670989,2670990,2670991,2670993,2671040,2671041,2671042,2671043,2671044,2671045,2671046,2671047,2671048,2671049,2671050,2671051,2671052,2671053,2671054,2671055,2671056,2671057,2671058,2671059,2671060,2671061,2671062,2671063,2671064,2671065,2671066,2671067,2671068,2671069,2671070,2671071,2671072,2671073,2671074,2671075,2671076,2671077,2671078,2671079,2671080,2671081,2671082,2671083,2671084,2671085,2671086,2671087,2671088,2671089,2671090,2671091,2671092,2671093,2671094,2671095,2671096,2671097,2671098,2671099,2671100,2671101,2671102,2671103],"Red Nether Bricks":[2671298],"Red Sand":[2671555],"Red Sandstone":[2671812],"Red Sandstone Slab":[2672068,2672069,2672071],"Red Sandstone Stairs":[2672320,2672321,2672322,2672323,2672324,2672325,2672326,2672327],"Red Sandstone Wall":[2672384,2672385,2672386,2672387,2672388,2672389,2672390,2672391,2672392,2672393,2672394,2672395,2672396,2672397,2672398,2672399,2672407,2672448,2672449,2672450,2672451,2672452,2672453,2672454,2672455,2672456,2672457,2672458,2672459,2672460,2672461,2672462,2672463,2672464,2672465,2672466,2672467,2672468,2672469,2672470,2672471,2672472,2672473,2672474,2672475,2672476,2672477,2672478,2672479,2672480,2672481,2672482,2672483,2672484,2672485,2672486,2672487,2672488,2672489,2672490,2672491,2672492,2672493,2672494,2672495,2672496,2672497,2672498,2672499,2672500,2672501,2672502,2672503,2672504,2672505,2672506,2672507,2672508,2672509,2672510,2672511,2672512,2672513,2672514,2672515,2672516,2672517,2672518,2672519,2672520,2672521,2672522,2672523,2672524,2672525,2672526,2672527,2672535,2672576,2672577,2672578,2672579,2672580,2672581,2672582,2672583,2672584,2672585,2672586,2672587,2672588,2672589,2672590,2672591,2672592,2672593,2672594,2672595,2672596,2672597,2672598,2672599,2672600,2672601,2672602,2672603,2672604,2672605,2672606,2672607,2672608,2672609,2672610,2672611,2672612,2672613,2672614,2672615,2672616,2672617,2672618,2672619,2672620,2672621,2672622,2672623,2672624,2672625,2672626,2672627,2672628,2672629,2672630,2672631,2672632,2672633,2672634,2672635,2672636,2672637,2672638,2672639],"Red Torch":[2672841,2672842,2672843,2672844,2672845],"Red Tulip":[2673097],"Redstone":[2674896,2674897,2674898,2674899,2674900,2674901,2674902,2674903,2674904,2674905,2674906,2674907,2674908,2674909,2674910,2674911],"Redstone Block":[2673354],"Redstone Comparator":[2673600,2673601,2673602,2673603,2673604,2673605,2673606,2673607,2673608,2673609,2673610,2673611,2673612,2673613,2673614,2673615],"Redstone Lamp":[2673868,2673869],"Redstone Ore":[2674124,2674125],"Redstone Repeater":[2674368,2674369,2674370,2674371,2674372,2674373,2674374,2674375,2674376,2674377,2674378,2674379,2674380,2674381,2674382,2674383,2674384,2674385,2674386,2674387,2674388,2674389,2674390,2674391,2674392,2674393,2674394,2674395,2674396,2674397,2674398,2674399],"Redstone Torch":[2674626,2674627,2674628,2674629,2674630,2674634,2674635,2674636,2674637,2674638],"Reinforced Deepslate":[2736320],"Rhenium":[2616299],"Rhodium":[2616556],"Roentgenium":[2616813],"Rose Bush":[2675410,2675411],"Rubidium":[2617070],"Ruthenium":[2617327],"Rutherfordium":[2617584],"Samarium":[2617841],"Sand":[2675667],"Sandstone":[2675924],"Sandstone Slab":[2676180,2676181,2676183],"Sandstone Stairs":[2676432,2676433,2676434,2676435,2676436,2676437,2676438,2676439],"Sandstone Wall":[2676487,2676496,2676497,2676498,2676499,2676500,2676501,2676502,2676503,2676504,2676505,2676506,2676507,2676508,2676509,2676510,2676511,2676544,2676545,2676546,2676547,2676548,2676549,2676550,2676551,2676552,2676553,2676554,2676555,2676556,2676557,2676558,2676559,2676560,2676561,2676562,2676563,2676564,2676565,2676566,2676567,2676568,2676569,2676570,2676571,2676572,2676573,2676574,2676575,2676576,2676577,2676578,2676579,2676580,2676581,2676582,2676583,2676584,2676585,2676586,2676587,2676588,2676589,2676590,2676591,2676592,2676593,2676594,2676595,2676596,2676597,2676598,2676599,2676600,2676601,2676602,2676603,2676604,2676605,2676606,2676607,2676615,2676624,2676625,2676626,2676627,2676628,2676629,2676630,2676631,2676632,2676633,2676634,2676635,2676636,2676637,2676638,2676639,2676672,2676673,2676674,2676675,2676676,2676677,2676678,2676679,2676680,2676681,2676682,2676683,2676684,2676685,2676686,2676687,2676688,2676689,2676690,2676691,2676692,2676693,2676694,2676695,2676696,2676697,2676698,2676699,2676700,2676701,2676702,2676703,2676704,2676705,2676706,2676707,2676708,2676709,2676710,2676711,2676712,2676713,2676714,2676715,2676716,2676717,2676718,2676719,2676720,2676721,2676722,2676723,2676724,2676725,2676726,2676727,2676728,2676729,2676730,2676731,2676732,2676733,2676734,2676735],"Scandium":[2618098],"Sculk":[2735035],"Sea Lantern":[2676952],"Sea Pickle":[2677208,2677209,2677210,2677211,2677212,2677213,2677214,2677215],"Seaborgium":[2618355],"Selenium":[2618612],"Shroomlight":[2712162],"Shulker Box":[2677466],"Silicon":[2618869],"Silver":[2619126],"Slime Block":[2677723],"Small Dripleaf":[2740944,2740945,2740946,2740947,2740948,2740949,2740950,2740951],"Smithing Table":[2730923],"Smoker":[2677976,2677977,2677978,2677979,2677980,2677981,2677982,2677983],"Smooth Basalt":[2699312],"Smooth Quartz Block":[2678237],"Smooth Quartz Slab":[2678492,2678494,2678495],"Smooth Quartz Stairs":[2678744,2678745,2678746,2678747,2678748,2678749,2678750,2678751],"Smooth Red Sandstone":[2679008],"Smooth Red Sandstone Slab":[2679264,2679265,2679267],"Smooth Red Sandstone Stairs":[2679520,2679521,2679522,2679523,2679524,2679525,2679526,2679527],"Smooth Sandstone":[2679779],"Smooth Sandstone Slab":[2680036,2680037,2680038],"Smooth Sandstone Stairs":[2680288,2680289,2680290,2680291,2680292,2680293,2680294,2680295],"Smooth Stone":[2680550],"Smooth Stone Slab":[2680805,2680806,2680807],"Snow Block":[2681064],"Snow Layer":[2681320,2681321,2681322,2681323,2681324,2681325,2681326,2681327],"Sodium":[2619383],"Soul Fire":[2711905],"Soul Lantern":[2711390,2711391],"Soul Sand":[2681578],"Soul Soil":[2711648],"Soul Torch":[2711130,2711131,2711132,2711133,2711135],"Sponge":[2681834,2681835],"Spore Blossom":[2731437],"Spruce Button":[2682080,2682081,2682084,2682085,2682086,2682087,2682088,2682089,2682092,2682093,2682094,2682095],"Spruce Door":[2682336,2682337,2682338,2682339,2682340,2682341,2682342,2682343,2682344,2682345,2682346,2682347,2682348,2682349,2682350,2682351,2682352,2682353,2682354,2682355,2682356,2682357,2682358,2682359,2682360,2682361,2682362,2682363,2682364,2682365,2682366,2682367],"Spruce Fence":[2682606],"Spruce Fence Gate":[2682848,2682849,2682850,2682851,2682852,2682853,2682854,2682855,2682856,2682857,2682858,2682859,2682860,2682861,2682862,2682863],"Spruce Leaves":[2683120,2683121,2683122,2683123],"Spruce Log":[2683376,2683377,2683378,2683379,2683380,2683381],"Spruce Planks":[2683634],"Spruce Pressure Plate":[2683890,2683891],"Spruce Sapling":[2684148,2684149],"Spruce Sign":[2684400,2684401,2684402,2684403,2684404,2684405,2684406,2684407,2684408,2684409,2684410,2684411,2684412,2684413,2684414,2684415],"Spruce Slab":[2684660,2684662,2684663],"Spruce Stairs":[2684912,2684913,2684914,2684915,2684916,2684917,2684918,2684919],"Spruce Trapdoor":[2685168,2685169,2685170,2685171,2685172,2685173,2685174,2685175,2685176,2685177,2685178,2685179,2685180,2685181,2685182,2685183],"Spruce Wall Sign":[2685432,2685433,2685434,2685435],"Spruce Wood":[2685688,2685689,2685690,2685691,2685694,2685695],"Stained Clay":[2685936,2685937,2685938,2685939,2685940,2685941,2685942,2685943,2685944,2685945,2685946,2685947,2685948,2685949,2685950,2685951],"Stained Glass":[2686192,2686193,2686194,2686195,2686196,2686197,2686198,2686199,2686200,2686201,2686202,2686203,2686204,2686205,2686206,2686207],"Stained Glass Pane":[2686448,2686449,2686450,2686451,2686452,2686453,2686454,2686455,2686456,2686457,2686458,2686459,2686460,2686461,2686462,2686463],"Stained Hardened Glass":[2686704,2686705,2686706,2686707,2686708,2686709,2686710,2686711,2686712,2686713,2686714,2686715,2686716,2686717,2686718,2686719],"Stained Hardened Glass Pane":[2686960,2686961,2686962,2686963,2686964,2686965,2686966,2686967,2686968,2686969,2686970,2686971,2686972,2686973,2686974,2686975],"Stone":[2686976],"Stone Brick Slab":[2687232,2687233,2687235],"Stone Brick Stairs":[2687488,2687489,2687490,2687491,2687492,2687493,2687494,2687495],"Stone Brick Wall":[2687744,2687745,2687746,2687747,2687748,2687749,2687750,2687751,2687752,2687753,2687754,2687755,2687756,2687757,2687758,2687759,2687760,2687761,2687762,2687763,2687764,2687765,2687766,2687767,2687768,2687769,2687770,2687771,2687772,2687773,2687774,2687775,2687776,2687777,2687778,2687779,2687780,2687781,2687782,2687783,2687784,2687785,2687786,2687787,2687788,2687789,2687790,2687791,2687792,2687793,2687794,2687795,2687796,2687797,2687798,2687799,2687800,2687801,2687802,2687803,2687804,2687805,2687806,2687807,2687808,2687809,2687810,2687811,2687812,2687813,2687814,2687815,2687816,2687817,2687818,2687819,2687820,2687821,2687822,2687823,2687827,2687872,2687873,2687874,2687875,2687876,2687877,2687878,2687879,2687880,2687881,2687882,2687883,2687884,2687885,2687886,2687887,2687888,2687889,2687890,2687891,2687892,2687893,2687894,2687895,2687896,2687897,2687898,2687899,2687900,2687901,2687902,2687903,2687904,2687905,2687906,2687907,2687908,2687909,2687910,2687911,2687912,2687913,2687914,2687915,2687916,2687917,2687918,2687919,2687920,2687921,2687922,2687923,2687924,2687925,2687926,2687927,2687928,2687929,2687930,2687931,2687932,2687933,2687934,2687935,2687936,2687937,2687938,2687939,2687940,2687941,2687942,2687943,2687944,2687945,2687946,2687947,2687948,2687949,2687950,2687951,2687955],"Stone Bricks":[2688004],"Stone Button":[2688256,2688257,2688260,2688261,2688262,2688263,2688264,2688265,2688268,2688269,2688270,2688271],"Stone Pressure Plate":[2688518,2688519],"Stone Slab":[2688773,2688774,2688775],"Stone Stairs":[2689032,2689033,2689034,2689035,2689036,2689037,2689038,2689039],"Stonecutter":[2689288,2689289,2689290,2689291],"Strontium":[2619640],"Sugarcane":[2692624,2692625,2692626,2692627,2692628,2692629,2692630,2692631,2692632,2692633,2692634,2692635,2692636,2692637,2692638,2692639],"Sulfur":[2619897],"Sunflower":[2692886,2692887],"Sweet Berry Bush":[2693144,2693145,2693146,2693147],"TNT":[2693656,2693657,2693658,2693659],"Tall Grass":[2693401],"Tantalum":[2620154],"Technetium":[2620411],"Tellurium":[2620668],"Tennessine":[2620925],"Terbium":[2621182],"Thallium":[2621439],"Thorium":[2621440],"Thulium":[2621697],"Tin":[2621954],"Tinted Glass":[2722442],"Titanium":[2622211],"Torch":[2693912,2693913,2693914,2693918,2693919],"Trapped Chest":[2694172,2694173,2694174,2694175],"Tripwire":[2694416,2694417,2694418,2694419,2694420,2694421,2694422,2694423,2694424,2694425,2694426,2694427,2694428,2694429,2694430,2694431],"Tripwire Hook":[2694672,2694673,2694674,2694675,2694676,2694677,2694678,2694679,2694680,2694681,2694682,2694683,2694684,2694685,2694686,2694687],"Tuff":[2710877],"Tungsten":[2622468],"Twisting Vines":[2734240,2734241,2734248,2734249,2734250,2734251,2734252,2734253,2734254,2734255,2734256,2734257,2734258,2734259,2734260,2734261,2734262,2734263,2734264,2734265,2734266,2734267,2734268,2734269,2734270,2734271],"Underwater Torch":[2694938,2694939,2694940,2694941,2694942],"Uranium":[2622725],"Vanadium":[2622982],"Vines":[2695200,2695201,2695202,2695203,2695204,2695205,2695206,2695207,2695208,2695209,2695210,2695211,2695212,2695213,2695214,2695215],"Wall Banner":[2695424,2695425,2695426,2695427,2695428,2695429,2695430,2695431,2695432,2695433,2695434,2695435,2695436,2695437,2695438,2695439,2695440,2695441,2695442,2695443,2695444,2695445,2695446,2695447,2695448,2695449,2695450,2695451,2695452,2695453,2695454,2695455,2695456,2695457,2695458,2695459,2695460,2695461,2695462,2695463,2695464,2695465,2695466,2695467,2695468,2695469,2695470,2695471,2695472,2695473,2695474,2695475,2695476,2695477,2695478,2695479,2695480,2695481,2695482,2695483,2695484,2695485,2695486,2695487],"Wall Coral Fan":[2695680,2695681,2695682,2695683,2695686,2695688,2695689,2695690,2695691,2695694,2695696,2695697,2695698,2695699,2695702,2695704,2695705,2695706,2695707,2695710,2695712,2695713,2695714,2695715,2695718,2695720,2695721,2695722,2695723,2695726,2695728,2695729,2695730,2695731,2695734,2695736,2695737,2695738,2695739,2695742],"Warped Button":[2717554,2717555,2717556,2717557,2717558,2717559,2717562,2717563,2717564,2717565,2717566,2717567],"Warped Door":[2719072,2719073,2719074,2719075,2719076,2719077,2719078,2719079,2719080,2719081,2719082,2719083,2719084,2719085,2719086,2719087,2719088,2719089,2719090,2719091,2719092,2719093,2719094,2719095,2719096,2719097,2719098,2719099,2719100,2719101,2719102,2719103],"Warped Fence":[2713704],"Warped Fence Gate":[2719872,2719873,2719874,2719875,2719876,2719877,2719878,2719879,2719880,2719881,2719882,2719883,2719884,2719885,2719886,2719887],"Warped Hyphae":[2716016,2716017,2716018,2716019,2716020,2716021],"Warped Planks":[2712933],"Warped Pressure Plate":[2718330,2718331],"Warped Sign":[2721408,2721409,2721410,2721411,2721412,2721413,2721414,2721415,2721416,2721417,2721418,2721419,2721420,2721421,2721422,2721423],"Warped Slab":[2714473,2714474,2714475],"Warped Stairs":[2720640,2720641,2720642,2720643,2720644,2720645,2720646,2720647],"Warped Stem":[2715242,2715243,2715244,2715245,2715246,2715247],"Warped Trapdoor":[2716784,2716785,2716786,2716787,2716788,2716789,2716790,2716791,2716792,2716793,2716794,2716795,2716796,2716797,2716798,2716799],"Warped Wall Sign":[2722184,2722185,2722186,2722187],"Warped Wart Block":[2727068],"Water":[2695968,2695969,2695970,2695971,2695972,2695973,2695974,2695975,2695976,2695977,2695978,2695979,2695980,2695981,2695982,2695983,2695984,2695985,2695986,2695987,2695988,2695989,2695990,2695991,2695992,2695993,2695994,2695995,2695996,2695997,2695998,2695999],"Water Cauldron":[2731946,2731947,2731948,2731949,2731950,2731951],"Weeping Vines":[2734496,2734497,2734504,2734505,2734506,2734507,2734508,2734509,2734510,2734511,2734512,2734513,2734514,2734515,2734516,2734517,2734518,2734519,2734520,2734521,2734522,2734523,2734524,2734525,2734526,2734527],"Weighted Pressure Plate Heavy":[2696224,2696225,2696226,2696227,2696228,2696229,2696230,2696231,2696232,2696233,2696234,2696235,2696236,2696237,2696238,2696239],"Weighted Pressure Plate Light":[2696480,2696481,2696482,2696483,2696484,2696485,2696486,2696487,2696488,2696489,2696490,2696491,2696492,2696493,2696494,2696495],"Wheat Block":[2696736,2696737,2696738,2696739,2696740,2696741,2696742,2696743],"White Tulip":[2697256],"Wither Rose":[2730152],"Wool":[2697504,2697505,2697506,2697507,2697508,2697509,2697510,2697511,2697512,2697513,2697514,2697515,2697516,2697517,2697518,2697519],"Xenon":[2623239],"Ytterbium":[2623496],"Yttrium":[2623753],"Zinc":[2624267],"Zirconium":[2624524],"ate!upd":[2637117],"reserved6":[2675153],"update!":[2636860]},"stateDataBits":8} \ No newline at end of file