mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-03 18:42:37 +00:00
Merge branch 'minor-next' into stable
This commit is contained in:
commit
5a010e8213
3
.github/workflows/main.yml
vendored
3
.github/workflows/main.yml
vendored
@ -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
|
||||
|
111
build/generate-pocketmine-yml-property-consts.php
Normal file
111
build/generate-pocketmine-yml-property-consts.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$defaultConfig = yaml_parse(Filesystem::fileGetContents(dirname(__DIR__) . '/resources/pocketmine.yml'));
|
||||
|
||||
if(!is_array($defaultConfig)){
|
||||
fwrite(STDERR, "Invalid default pocketmine.yml\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$constants = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $properties
|
||||
* @param string[] $constants
|
||||
* @phpstan-param array<string, string> $constants
|
||||
* @phpstan-param-out array<string, string> $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, "<?php\n");
|
||||
fwrite($file, <<<'HEADER'
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
HEADER
|
||||
);
|
||||
fwrite($file, "declare(strict_types=1);\n\n");
|
||||
fwrite($file, "namespace pocketmine;\n\n");
|
||||
|
||||
fwrite($file, <<<'DOC'
|
||||
/**
|
||||
* @internal
|
||||
* Constants for all properties available in pocketmine.yml.
|
||||
* This is generated by build/generate-pocketmine-yml-property-consts.php.
|
||||
* Do not edit this file manually.
|
||||
*/
|
||||
|
||||
DOC
|
||||
);
|
||||
fwrite($file, "final class YmlServerProperties{\n");
|
||||
foreach(Utils::stringifyKeys($constants) as $constName => $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";
|
156
changelogs/5.5-beta.md
Normal file
156
changelogs/5.5-beta.md
Normal file
@ -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<string>` returns a list of strings indicating which types of enchantment can be applied to the block when in item form
|
||||
- `public BlockTypeInfo->getEnchantmentTags() : list<string>`
|
||||
- `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<Entity> $entities) : list<Entity>` - 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<string> $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<? extends Event> $event) : list<RegisteredListener>` - 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<string>` - 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<string> $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
|
@ -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",
|
||||
|
30
composer.lock
generated
30
composer.lock
generated
@ -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",
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
147
src/Server.php
147
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 = [];
|
||||
|
54
src/ServerProperties.php
Normal file
54
src/ServerProperties.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Constants for all properties available in server.properties.
|
||||
*/
|
||||
final class ServerProperties{
|
||||
|
||||
public const AUTO_SAVE = "auto-save";
|
||||
public const DEFAULT_WORLD_GENERATOR = "level-type";
|
||||
public const DEFAULT_WORLD_GENERATOR_SETTINGS = "generator-settings";
|
||||
public const DEFAULT_WORLD_NAME = "level-name";
|
||||
public const DEFAULT_WORLD_SEED = "level-seed";
|
||||
public const DIFFICULTY = "difficulty";
|
||||
public const ENABLE_IPV6 = "enable-ipv6";
|
||||
public const ENABLE_QUERY = "enable-query";
|
||||
public const FORCE_GAME_MODE = "force-gamemode";
|
||||
public const GAME_MODE = "gamemode";
|
||||
public const HARDCORE = "hardcore";
|
||||
public const LANGUAGE = "language";
|
||||
public const MAX_PLAYERS = "max-players";
|
||||
public const MOTD = "motd";
|
||||
public const PVP = "pvp";
|
||||
public const SERVER_IPV4 = "server-ip";
|
||||
public const SERVER_IPV6 = "server-ipv6";
|
||||
public const SERVER_PORT_IPV4 = "server-port";
|
||||
public const SERVER_PORT_IPV6 = "server-portv6";
|
||||
public const VIEW_DISTANCE = "view-distance";
|
||||
public const WHITELIST = "white-list";
|
||||
public const XBOX_AUTH = "xbox-auth";
|
||||
}
|
@ -31,9 +31,9 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.4.5";
|
||||
public const BASE_VERSION = "5.5.0-BETA2";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
|
||||
/**
|
||||
* PocketMine-MP-specific version ID for world data. Used to determine what fixes need to be applied to old world
|
||||
|
113
src/YmlServerProperties.php
Normal file
113
src/YmlServerProperties.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Constants for all properties available in pocketmine.yml.
|
||||
* This is generated by build/generate-pocketmine-yml-property-consts.php.
|
||||
* Do not edit this file manually.
|
||||
*/
|
||||
final class YmlServerProperties{
|
||||
public const ALIASES = 'aliases';
|
||||
public const ANONYMOUS_STATISTICS = 'anonymous-statistics';
|
||||
public const ANONYMOUS_STATISTICS_ENABLED = 'anonymous-statistics.enabled';
|
||||
public const ANONYMOUS_STATISTICS_HOST = 'anonymous-statistics.host';
|
||||
public const AUTO_REPORT = 'auto-report';
|
||||
public const AUTO_REPORT_ENABLED = 'auto-report.enabled';
|
||||
public const AUTO_REPORT_HOST = 'auto-report.host';
|
||||
public const AUTO_REPORT_SEND_CODE = 'auto-report.send-code';
|
||||
public const AUTO_REPORT_SEND_PHPINFO = 'auto-report.send-phpinfo';
|
||||
public const AUTO_REPORT_SEND_SETTINGS = 'auto-report.send-settings';
|
||||
public const AUTO_REPORT_USE_HTTPS = 'auto-report.use-https';
|
||||
public const AUTO_UPDATER = 'auto-updater';
|
||||
public const AUTO_UPDATER_ENABLED = 'auto-updater.enabled';
|
||||
public const AUTO_UPDATER_HOST = 'auto-updater.host';
|
||||
public const AUTO_UPDATER_ON_UPDATE = 'auto-updater.on-update';
|
||||
public const AUTO_UPDATER_ON_UPDATE_WARN_CONSOLE = 'auto-updater.on-update.warn-console';
|
||||
public const AUTO_UPDATER_PREFERRED_CHANNEL = 'auto-updater.preferred-channel';
|
||||
public const AUTO_UPDATER_SUGGEST_CHANNELS = 'auto-updater.suggest-channels';
|
||||
public const CHUNK_GENERATION = 'chunk-generation';
|
||||
public const CHUNK_GENERATION_POPULATION_QUEUE_SIZE = 'chunk-generation.population-queue-size';
|
||||
public const CHUNK_SENDING = 'chunk-sending';
|
||||
public const CHUNK_SENDING_PER_TICK = 'chunk-sending.per-tick';
|
||||
public const CHUNK_SENDING_SPAWN_RADIUS = 'chunk-sending.spawn-radius';
|
||||
public const CHUNK_TICKING = 'chunk-ticking';
|
||||
public const CHUNK_TICKING_BLOCKS_PER_SUBCHUNK_PER_TICK = 'chunk-ticking.blocks-per-subchunk-per-tick';
|
||||
public const CHUNK_TICKING_DISABLE_BLOCK_TICKING = 'chunk-ticking.disable-block-ticking';
|
||||
public const CHUNK_TICKING_TICK_RADIUS = 'chunk-ticking.tick-radius';
|
||||
public const CONSOLE = 'console';
|
||||
public const CONSOLE_ENABLE_INPUT = 'console.enable-input';
|
||||
public const CONSOLE_TITLE_TICK = 'console.title-tick';
|
||||
public const DEBUG = 'debug';
|
||||
public const DEBUG_LEVEL = 'debug.level';
|
||||
public const LEVEL_SETTINGS = 'level-settings';
|
||||
public const LEVEL_SETTINGS_DEFAULT_FORMAT = 'level-settings.default-format';
|
||||
public const MEMORY = 'memory';
|
||||
public const MEMORY_ASYNC_WORKER_HARD_LIMIT = 'memory.async-worker-hard-limit';
|
||||
public const MEMORY_CHECK_RATE = 'memory.check-rate';
|
||||
public const MEMORY_CONTINUOUS_TRIGGER = 'memory.continuous-trigger';
|
||||
public const MEMORY_CONTINUOUS_TRIGGER_RATE = 'memory.continuous-trigger-rate';
|
||||
public const MEMORY_GARBAGE_COLLECTION = 'memory.garbage-collection';
|
||||
public const MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER = 'memory.garbage-collection.collect-async-worker';
|
||||
public const MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER = 'memory.garbage-collection.low-memory-trigger';
|
||||
public const MEMORY_GARBAGE_COLLECTION_PERIOD = 'memory.garbage-collection.period';
|
||||
public const MEMORY_GLOBAL_LIMIT = 'memory.global-limit';
|
||||
public const MEMORY_MAIN_HARD_LIMIT = 'memory.main-hard-limit';
|
||||
public const MEMORY_MAIN_LIMIT = 'memory.main-limit';
|
||||
public const MEMORY_MAX_CHUNKS = 'memory.max-chunks';
|
||||
public const MEMORY_MAX_CHUNKS_CHUNK_RADIUS = 'memory.max-chunks.chunk-radius';
|
||||
public const MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT = 'memory.max-chunks.trigger-chunk-collect';
|
||||
public const MEMORY_MEMORY_DUMP = 'memory.memory-dump';
|
||||
public const MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER = 'memory.memory-dump.dump-async-worker';
|
||||
public const MEMORY_WORLD_CACHES = 'memory.world-caches';
|
||||
public const MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE = 'memory.world-caches.disable-chunk-cache';
|
||||
public const MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER = 'memory.world-caches.low-memory-trigger';
|
||||
public const NETWORK = 'network';
|
||||
public const NETWORK_ASYNC_COMPRESSION = 'network.async-compression';
|
||||
public const NETWORK_ASYNC_COMPRESSION_THRESHOLD = 'network.async-compression-threshold';
|
||||
public const NETWORK_BATCH_THRESHOLD = 'network.batch-threshold';
|
||||
public const NETWORK_COMPRESSION_LEVEL = 'network.compression-level';
|
||||
public const NETWORK_ENABLE_ENCRYPTION = 'network.enable-encryption';
|
||||
public const NETWORK_MAX_MTU_SIZE = 'network.max-mtu-size';
|
||||
public const NETWORK_UPNP_FORWARDING = 'network.upnp-forwarding';
|
||||
public const PLAYER = 'player';
|
||||
public const PLAYER_SAVE_PLAYER_DATA = 'player.save-player-data';
|
||||
public const PLAYER_VERIFY_XUID = 'player.verify-xuid';
|
||||
public const PLUGINS = 'plugins';
|
||||
public const PLUGINS_LEGACY_DATA_DIR = 'plugins.legacy-data-dir';
|
||||
public const SETTINGS = 'settings';
|
||||
public const SETTINGS_ASYNC_WORKERS = 'settings.async-workers';
|
||||
public const SETTINGS_ENABLE_DEV_BUILDS = 'settings.enable-dev-builds';
|
||||
public const SETTINGS_ENABLE_PROFILING = 'settings.enable-profiling';
|
||||
public const SETTINGS_FORCE_LANGUAGE = 'settings.force-language';
|
||||
public const SETTINGS_PROFILE_REPORT_TRIGGER = 'settings.profile-report-trigger';
|
||||
public const SETTINGS_QUERY_PLUGINS = 'settings.query-plugins';
|
||||
public const SETTINGS_SHUTDOWN_MESSAGE = 'settings.shutdown-message';
|
||||
public const TICKS_PER = 'ticks-per';
|
||||
public const TICKS_PER_AUTOSAVE = 'ticks-per.autosave';
|
||||
public const TIMINGS = 'timings';
|
||||
public const TIMINGS_HOST = 'timings.host';
|
||||
public const WORLDS = 'worlds';
|
||||
}
|
@ -23,10 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\BlockDeathEvent;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
@ -46,11 +46,7 @@ abstract class BaseCoral extends Transparent{
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
if(!$this->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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
119
src/block/PinkPetals.php
Normal file
119
src/block/PinkPetals.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class PinkPetals extends Flowable{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public const MIN_COUNT = 1;
|
||||
public const MAX_COUNT = 4;
|
||||
|
||||
protected int $count = self::MIN_COUNT;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->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)];
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class WeightedPressurePlateHeavy extends WeightedPressurePlate{
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class WeightedPressurePlateLight extends WeightedPressurePlate{
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
115
src/block/utils/BlockEventHelper.php
Normal file
115
src/block/utils/BlockEventHelper.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\event\block\BlockDeathEvent;
|
||||
use pocketmine\event\block\BlockFormEvent;
|
||||
use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\event\block\BlockMeltEvent;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Helper class to call block changing events and apply the results to the world.
|
||||
* TODO: try to further reduce the amount of code duplication here - while this is much better than before, it's still
|
||||
* very repetitive.
|
||||
*/
|
||||
final class BlockEventHelper{
|
||||
|
||||
public static function grow(Block $oldState, Block $newState, ?Player $causingPlayer) : bool{
|
||||
if(BlockGrowEvent::hasHandlers()){
|
||||
$ev = new BlockGrowEvent($oldState, $newState, $causingPlayer);
|
||||
$ev->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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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){
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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){
|
||||
|
@ -29,18 +29,10 @@ use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class DyeColorIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var DyeColor[]
|
||||
* @phpstan-var array<int, DyeColor>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<DyeColor> */
|
||||
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);
|
||||
}
|
||||
|
@ -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<int, Effect>
|
||||
*/
|
||||
private array $idToEffect = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $effectToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<Effect> */
|
||||
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)];
|
||||
}
|
||||
}
|
||||
|
@ -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<int, Enchantment>
|
||||
*/
|
||||
private array $idToEnch = [];
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enchToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<Enchantment> */
|
||||
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)];
|
||||
}
|
||||
}
|
||||
|
81
src/data/bedrock/IntSaveIdMapTrait.php
Normal file
81
src/data/bedrock/IntSaveIdMapTrait.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
use function array_key_exists;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* @phpstan-template TObject of object
|
||||
*/
|
||||
trait IntSaveIdMapTrait{
|
||||
|
||||
/**
|
||||
* @var object[]
|
||||
* @phpstan-var array<int, TObject>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
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];
|
||||
}
|
||||
}
|
@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class MedicineTypeIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var MedicineType[]
|
||||
* @phpstan-var array<int, MedicineType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<MedicineType> */
|
||||
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()];
|
||||
}
|
||||
}
|
||||
|
@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class MobHeadTypeIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var MobHeadType[]
|
||||
* @phpstan-var array<int, MobHeadType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<MobHeadType> */
|
||||
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()];
|
||||
}
|
||||
}
|
||||
|
@ -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<int, MushroomBlockType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<MushroomBlockType> */
|
||||
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()];
|
||||
}
|
||||
}
|
||||
|
@ -28,18 +28,8 @@ use pocketmine\world\sound\NoteInstrument;
|
||||
|
||||
final class NoteInstrumentIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var NoteInstrument[]
|
||||
* @phpstan-var array<int, NoteInstrument>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<NoteInstrument> */
|
||||
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()];
|
||||
}
|
||||
}
|
||||
|
@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class PotionTypeIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var PotionType[]
|
||||
* @phpstan-var array<int, PotionType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<PotionType> */
|
||||
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()];
|
||||
}
|
||||
}
|
||||
|
@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class SuspiciousStewTypeIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var SuspiciousStewType[]
|
||||
* @phpstan-var array<int, SuspiciousStewType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
/** @phpstan-use IntSaveIdMapTrait<SuspiciousStewType> */
|
||||
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()];
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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()
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ class HandlerListManager{
|
||||
|
||||
/** @var HandlerList[] classname => HandlerList */
|
||||
private array $allLists = [];
|
||||
/**
|
||||
* @var RegisteredListenerCache[] event class name => cache
|
||||
* @phpstan-var array<class-string<Event>, 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<TEvent> $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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
59
src/event/block/FarmlandHydrationChangeEvent.php
Normal file
59
src/event/block/FarmlandHydrationChangeEvent.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\block;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Farmland;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
|
||||
/**
|
||||
* Called when farmland hydration is updated.
|
||||
*/
|
||||
class FarmlandHydrationChangeEvent extends BlockEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
Block $block,
|
||||
private int $oldHydration,
|
||||
private int $newHydration,
|
||||
){
|
||||
parent::__construct($block);
|
||||
}
|
||||
|
||||
public function getOldHydration() : int{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
52
src/event/block/PressurePlateUpdateEvent.php
Normal file
52
src/event/block/PressurePlateUpdateEvent.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\block;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
/**
|
||||
* Called whenever the list of entities on a pressure plate changes.
|
||||
* Depending on the type of pressure plate, this might turn on/off its signal, or change the signal strength.
|
||||
*/
|
||||
final class PressurePlateUpdateEvent extends BaseBlockChangeEvent{
|
||||
/**
|
||||
* @param Entity[] $activatingEntities
|
||||
*/
|
||||
public function __construct(
|
||||
Block $block,
|
||||
Block $newState,
|
||||
private array $activatingEntities
|
||||
){
|
||||
parent::__construct($block, $newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of entities intersecting the pressure plate's activation box.
|
||||
* If the pressure plate is about to deactivate, this list will be empty.
|
||||
*
|
||||
* @return Entity[]
|
||||
*/
|
||||
public function getActivatingEntities() : array{ return $this->activatingEntities; }
|
||||
}
|
75
src/event/player/PlayerEnchantingOptionsRequestEvent.php
Normal file
75
src/event/player/PlayerEnchantingOptionsRequestEvent.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\block\inventory\EnchantInventory;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\item\enchantment\EnchantingOption;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Called when a player inserts an item into an enchanting table's input slot.
|
||||
* The options provided by the event will be shown on the enchanting table menu.
|
||||
*/
|
||||
class PlayerEnchantingOptionsRequestEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
/**
|
||||
* @param EnchantingOption[] $options
|
||||
*/
|
||||
public function __construct(
|
||||
Player $player,
|
||||
private readonly EnchantInventory $inventory,
|
||||
private array $options
|
||||
){
|
||||
$this->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;
|
||||
}
|
||||
}
|
85
src/event/player/PlayerItemEnchantEvent.php
Normal file
85
src/event/player/PlayerItemEnchantEvent.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\inventory\transaction\EnchantingTransaction;
|
||||
use pocketmine\item\enchantment\EnchantingOption;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Called when a player enchants an item using an enchanting table.
|
||||
*/
|
||||
class PlayerItemEnchantEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
Player $player,
|
||||
private readonly EnchantingTransaction $transaction,
|
||||
private readonly EnchantingOption $option,
|
||||
private readonly Item $inputItem,
|
||||
private readonly Item $outputItem,
|
||||
private readonly int $cost
|
||||
){
|
||||
$this->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;
|
||||
}
|
||||
}
|
44
src/event/world/WorldDifficultyChangeEvent.php
Normal file
44
src/event/world/WorldDifficultyChangeEvent.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\world;
|
||||
|
||||
use pocketmine\world\World;
|
||||
|
||||
/**
|
||||
* Called when a world's difficulty is changed.
|
||||
*/
|
||||
final class WorldDifficultyChangeEvent extends WorldEvent{
|
||||
|
||||
public function __construct(
|
||||
World $world,
|
||||
private int $oldDifficulty,
|
||||
private int $newDifficulty
|
||||
){
|
||||
parent::__construct($world);
|
||||
}
|
||||
|
||||
public function getOldDifficulty() : int{ return $this->oldDifficulty; }
|
||||
|
||||
public function getNewDifficulty() : int{ return $this->newDifficulty; }
|
||||
}
|
134
src/inventory/transaction/EnchantingTransaction.php
Normal file
134
src/inventory/transaction/EnchantingTransaction.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\inventory\transaction;
|
||||
|
||||
use pocketmine\event\player\PlayerItemEnchantEvent;
|
||||
use pocketmine\item\enchantment\EnchantingHelper;
|
||||
use pocketmine\item\enchantment\EnchantingOption;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function count;
|
||||
use function min;
|
||||
|
||||
class EnchantingTransaction extends InventoryTransaction{
|
||||
|
||||
private ?Item $inputItem = null;
|
||||
private ?Item $outputItem = null;
|
||||
|
||||
public function __construct(
|
||||
Player $source,
|
||||
private readonly EnchantingOption $option,
|
||||
private readonly int $cost
|
||||
){
|
||||
parent::__construct($source);
|
||||
}
|
||||
|
||||
private function validateOutput() : void{
|
||||
if($this->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();
|
||||
}
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
42
src/item/ArmorMaterial.php
Normal file
42
src/item/ArmorMaterial.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
class ArmorMaterial{
|
||||
|
||||
public function __construct(
|
||||
private readonly int $enchantability
|
||||
){
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
30
src/item/EnchantedBook.php
Normal file
30
src/item/EnchantedBook.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
class EnchantedBook extends Item{
|
||||
public function getMaxStackSize() : int{
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -107,10 +107,13 @@ class Item implements \JsonSerializable{
|
||||
* NOTE: This should NOT BE USED for creating items to set into an inventory. Use VanillaItems for that
|
||||
* purpose.
|
||||
* @see VanillaItems
|
||||
*
|
||||
* @param string[] $enchantmentTags
|
||||
*/
|
||||
public function __construct(
|
||||
private ItemIdentifier $identifier,
|
||||
protected string $name = "Unknown"
|
||||
protected string $name = "Unknown",
|
||||
private array $enchantmentTags = []
|
||||
){
|
||||
$this->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();
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
73
src/item/VanillaArmorMaterials.php
Normal file
73
src/item/VanillaArmorMaterials.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\utils\RegistryTrait;
|
||||
|
||||
/**
|
||||
* This doc-block is generated automatically, do not modify it manually.
|
||||
* This must be regenerated whenever registry members are added, removed or changed.
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static ArmorMaterial CHAINMAIL()
|
||||
* @method static ArmorMaterial DIAMOND()
|
||||
* @method static ArmorMaterial GOLD()
|
||||
* @method static ArmorMaterial IRON()
|
||||
* @method static ArmorMaterial LEATHER()
|
||||
* @method static ArmorMaterial NETHERITE()
|
||||
* @method static ArmorMaterial TURTLE()
|
||||
*/
|
||||
final class VanillaArmorMaterials{
|
||||
use RegistryTrait;
|
||||
|
||||
private function __construct(){
|
||||
// NOOP
|
||||
}
|
||||
|
||||
protected static function register(string $name, ArmorMaterial $armorMaterial) : void{
|
||||
self::_registryRegister($name, $armorMaterial);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArmorMaterial[]
|
||||
* @phpstan-return array<string, ArmorMaterial>
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
@ -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]));
|
||||
}
|
||||
|
||||
}
|
||||
|
211
src/item/enchantment/AvailableEnchantmentRegistry.php
Normal file
211
src/item/enchantment/AvailableEnchantmentRegistry.php
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTagRegistry as TagRegistry;
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTags as Tags;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments as Enchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use function array_filter;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Registry of enchantments that can be applied to items during in-game enchanting (enchanting table, anvil, fishing, etc.).
|
||||
*/
|
||||
final class AvailableEnchantmentRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
/** @var Enchantment[] */
|
||||
private array $enchantments = [];
|
||||
|
||||
/** @var string[][] */
|
||||
private array $primaryItemTags = [];
|
||||
|
||||
/** @var string[][] */
|
||||
private array $secondaryItemTags = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->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;
|
||||
}
|
||||
}
|
233
src/item/enchantment/EnchantingHelper.php
Normal file
233
src/item/enchantment/EnchantingHelper.php
Normal file
@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\item\enchantment\AvailableEnchantmentRegistry as EnchantmentRegistry;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\item\VanillaItems as Items;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\world\Position;
|
||||
use function abs;
|
||||
use function array_filter;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function floor;
|
||||
use function max;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
use function ord;
|
||||
use function round;
|
||||
|
||||
/**
|
||||
* Helper methods used for enchanting using the enchanting table.
|
||||
*/
|
||||
final class EnchantingHelper{
|
||||
private const MAX_BOOKSHELF_COUNT = 15;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new random seed for enchant option randomization.
|
||||
*/
|
||||
public static function generateSeed() : int{
|
||||
return mt_rand(Limits::INT32_MIN, Limits::INT32_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EnchantmentInstance[] $enchantments
|
||||
*/
|
||||
public static function enchantItem(Item $item, array $enchantments) : Item{
|
||||
$resultItem = $item->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;
|
||||
}
|
||||
}
|
66
src/item/enchantment/EnchantingOption.php
Normal file
66
src/item/enchantment/EnchantingOption.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
/**
|
||||
* Represents an option on the enchanting table menu.
|
||||
* If selected, all the enchantments in the option will be applied to the item.
|
||||
*/
|
||||
class EnchantingOption{
|
||||
|
||||
/**
|
||||
* @param EnchantmentInstance[] $enchantments
|
||||
*/
|
||||
public function __construct(
|
||||
private int $requiredXpLevel,
|
||||
private string $displayName,
|
||||
private array $enchantments
|
||||
){}
|
||||
|
||||
/**
|
||||
* Returns the minimum amount of XP levels required to select this enchantment option.
|
||||
* It's NOT the number of XP levels that will be subtracted after enchanting.
|
||||
*/
|
||||
public function getRequiredXpLevel() : int{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
34
src/item/enchantment/IncompatibleEnchantmentGroups.php
Normal file
34
src/item/enchantment/IncompatibleEnchantmentGroups.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
/**
|
||||
* Constants for groupings of incompatible enchantments.
|
||||
* Enchantments belonging to the same incompatibility group cannot be applied side-by-side on the same item.
|
||||
*/
|
||||
final class IncompatibleEnchantmentGroups{
|
||||
public const PROTECTION = "protection";
|
||||
public const BOW_INFINITE = "bow_infinite";
|
||||
public const BLOCK_DROPS = "block_drops";
|
||||
}
|
94
src/item/enchantment/IncompatibleEnchantmentRegistry.php
Normal file
94
src/item/enchantment/IncompatibleEnchantmentRegistry.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
use pocketmine\item\enchantment\IncompatibleEnchantmentGroups as Groups;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments as Enchantments;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use function array_intersect_key;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Manages which enchantments are incompatible with each other.
|
||||
* Enchantments can be added to groups to make them incompatible with all other enchantments already in that group.
|
||||
*/
|
||||
final class IncompatibleEnchantmentRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @phpstan-var array<int, array<string, true>>
|
||||
* @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;
|
||||
}
|
||||
}
|
190
src/item/enchantment/ItemEnchantmentTagRegistry.php
Normal file
190
src/item/enchantment/ItemEnchantmentTagRegistry.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTags as Tags;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_diff;
|
||||
use function array_intersect;
|
||||
use function array_merge;
|
||||
use function array_search;
|
||||
use function array_shift;
|
||||
use function array_unique;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Manages known item enchantment tags and the relations between them.
|
||||
* Used to determine which tags belong to which other tags, and to check if lists of tags intersect.
|
||||
*/
|
||||
final class ItemEnchantmentTagRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @phpstan-var array<string, list<string>>
|
||||
* @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'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
57
src/item/enchantment/ItemEnchantmentTags.php
Normal file
57
src/item/enchantment/ItemEnchantmentTags.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
/**
|
||||
* Tags used by items and enchantments to determine which enchantments can be applied to which items.
|
||||
* Some tags may contain other tags.
|
||||
* @see ItemEnchantmentTagRegistry
|
||||
*/
|
||||
final class ItemEnchantmentTags{
|
||||
public const ALL = "all";
|
||||
public const ARMOR = "armor";
|
||||
public const HELMET = "helmet";
|
||||
public const CHESTPLATE = "chestplate";
|
||||
public const LEGGINGS = "leggings";
|
||||
public const BOOTS = "boots";
|
||||
public const SHIELD = "shield";
|
||||
public const SWORD = "sword";
|
||||
public const TRIDENT = "trident";
|
||||
public const BOW = "bow";
|
||||
public const CROSSBOW = "crossbow";
|
||||
public const SHEARS = "shears";
|
||||
public const FLINT_AND_STEEL = "flint_and_steel";
|
||||
public const BLOCK_TOOLS = "block_tools";
|
||||
public const AXE = "axe";
|
||||
public const PICKAXE = "pickaxe";
|
||||
public const SHOVEL = "shovel";
|
||||
public const HOE = "hoe";
|
||||
public const FISHING_ROD = "fishing_rod";
|
||||
public const CARROT_ON_STICK = "carrot_on_stick";
|
||||
public const COMPASS = "compass";
|
||||
public const MASK = "mask";
|
||||
public const ELYTRA = "elytra";
|
||||
public const BRUSH = "brush";
|
||||
public const WEAPONS = "weapons";
|
||||
}
|
@ -23,14 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
/** @deprecated */
|
||||
final class ItemFlags{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
//TODO: this should probably move to protocol
|
||||
|
||||
public const NONE = 0x0;
|
||||
public const ALL = 0xffff;
|
||||
public const ARMOR = self::HEAD | self::TORSO | self::LEGS | self::FEET;
|
||||
|
@ -36,10 +36,15 @@ class ProtectionEnchantment extends Enchantment{
|
||||
/**
|
||||
* ProtectionEnchantment constructor.
|
||||
*
|
||||
* @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower
|
||||
*
|
||||
* @param int $primaryItemFlags @deprecated
|
||||
* @param int $secondaryItemFlags @deprecated
|
||||
* @param int[]|null $applicableDamageTypes EntityDamageEvent::CAUSE_* constants which this enchantment type applies to, or null if it applies to all types of damage.
|
||||
* @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange)
|
||||
*/
|
||||
public function __construct(Translatable|string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel, float $typeModifier, ?array $applicableDamageTypes){
|
||||
parent::__construct($name, $rarity, $primaryItemFlags, $secondaryItemFlags, $maxLevel);
|
||||
public function __construct(Translatable|string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel, float $typeModifier, ?array $applicableDamageTypes, ?\Closure $minEnchantingPower = null, int $enchantingPowerRange = 50){
|
||||
parent::__construct($name, $rarity, $primaryItemFlags, $secondaryItemFlags, $maxLevel, $minEnchantingPower, $enchantingPowerRange);
|
||||
|
||||
$this->typeModifier = $typeModifier;
|
||||
if($applicableDamageTypes !== null){
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user