Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor
2023-09-06 13:03:51 +01:00
67 changed files with 1184 additions and 569 deletions

View File

@ -28,7 +28,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Clone pmmp/PocketMine-Docker repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: pmmp/PocketMine-Docker
fetch-depth: 1

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.25.5

View File

@ -15,7 +15,7 @@ jobs:
php-version: [8.1]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
@ -86,7 +86,7 @@ jobs:
${{ github.workspace }}/build_info.json
- name: Create draft release
uses: ncipollo/release-action@v1.12.0
uses: ncipollo/release-action@v1.13.0
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}

View File

@ -17,7 +17,7 @@ jobs:
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
@ -52,7 +52,7 @@ jobs:
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
@ -87,7 +87,7 @@ jobs:
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
@ -124,7 +124,7 @@ jobs:
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
@ -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
@ -170,7 +173,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.25.5

View File

@ -14,7 +14,7 @@ jobs:
- name: Install jq
run: sudo apt update && sudo apt install jq -y
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/update.pmmp.io
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}

View File

@ -0,0 +1,120 @@
<?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");
fwrite($file, <<<'CONSTRUCTOR'
private function __construct(){
//NOOP
}
CONSTRUCTOR
);
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";

View File

@ -60,3 +60,9 @@ Released 9th August 2023.
- Fixed `PluginBase->saveResource()` leaking file resources when the data file already exists in the plugin's data folder. This bug existed since 2014 and was only discovered recently.
- Fixed coral blocks becoming dead after calling `getDropsForCompatibleTool()` on them.
- Fixed `BlockDeathEvent->getOldState()` returning a block which is already dead.
# 4.23.6
Released 21st August 2023.
## Fixes
- Added a workaround for armor and other inventories not working correctly after inventory sync. This is caused by a client bug.

View File

@ -107,3 +107,27 @@ Released 9th August 2023.
## Fixes
- Fixed cake accepting candle placement when slices have already been eaten.
- Fixed fire charges not lighting candles.
# 5.4.3
Released 21st August 2023.
## Included releases
- [4.23.6](https://github.com/pmmp/PocketMine-MP/blob/4.23.6/changelogs/4.23.md#4236) - Armor inventory client bug workaround
## Fixes
- Fixed crashdumps not generating correctly on fatal errors.
- Fixed `PotionCauldron::setPotionItem()` not validating the item type.
- Fixed chorus fruit not considering teleport destinations below y=0.
- Fixed cake dropping itself when mined.
# 5.4.4
Released 6th September 2023.
## General
- Crashdumps caused by non-phar plugins are now submitted to the Crash Archive, the same as other plugins. Previously, non-phar plugin crashes would not be submitted, causing maintainers to potentially miss important issues.
## Fixes
- Fixed player Y coordinates sometimes being slightly below the top of the block they were standing on (floating point error due to subtracting eye height).
- Fixed template slot of smithing tables not accepting any items.
- `tools/generate-bedrock-data-from-packets.php` is now significantly less spammy when warning about duplicated recipes.
- Fixed empty stack traces in `lastError` data of crashdumps.

156
changelogs/5.5-beta.md Normal file
View 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

162
changelogs/5.5.md Normal file
View File

@ -0,0 +1,162 @@
# 5.5.0
Released 6th September 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
## Internals
- Various `TypeIdMap` classes in the `pocketmine\data\bedrock` package now use the new `IntSaveIdMapTrait` to reduce code duplication.
- Added a new `ServerProperties` class containing constants for all known `server.properties` keys.
- Added a new `YmlServerProperties` class containing generated constants for all known `pocketmine.yml` keys. These keys can be used with `Config->getNested()`.

View File

@ -44,7 +44,7 @@
"pocketmine/locale-data": "~2.19.0",
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
"pocketmine/nbt": "^0.3.2",
"pocketmine/nbt": "~1.0.0",
"pocketmine/raklib": "^0.15.0",
"pocketmine/raklib-ipc": "^0.2.0",
"pocketmine/snooze": "^0.5.0",

149
composer.lock generated
View File

@ -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": "2d51c1adf069760587b6d36f9c4a5db3",
"content-hash": "9237955fd97ba7c1697d80314fa9ad6f",
"packages": [
{
"name": "adhocore/json-comment",
@ -200,16 +200,16 @@
},
{
"name": "pocketmine/bedrock-protocol",
"version": "23.0.3+bedrock-1.20.10",
"version": "23.0.4+bedrock-1.20.10",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9"
"reference": "ae0d8f4d49506674b7ff622f7c81ce241dc49adb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e4157c7af3f91e1b08fe21be171eb73dad7029e9",
"reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/ae0d8f4d49506674b7ff622f7c81ce241dc49adb",
"reference": "ae0d8f4d49506674b7ff622f7c81ce241dc49adb",
"shasum": ""
},
"require": {
@ -223,7 +223,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.10.7",
"phpstan/phpstan": "1.10.33",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5"
@ -241,9 +241,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.3+bedrock-1.20.10"
"source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.4+bedrock-1.20.10"
},
"time": "2023-08-03T15:30:52+00:00"
"time": "2023-09-06T07:36:44+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -521,16 +521,16 @@
},
{
"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",
@ -985,16 +985,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": ""
},
"require": {
@ -1009,7 +1009,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1047,7 +1047,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
},
"funding": [
{
@ -1063,20 +1063,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
@ -1091,7 +1091,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1130,7 +1130,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@ -1146,7 +1146,7 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-07-28T09:04:16+00:00"
}
],
"packages-dev": [
@ -1440,16 +1440,16 @@
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.3.13",
"version": "1.3.14",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "d8bdab0218c5eb0964338d24a8511b65e9c94fa5"
"reference": "614acc10c522e319639bf38b0698a4a566665f04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d8bdab0218c5eb0964338d24a8511b65e9c94fa5",
"reference": "d8bdab0218c5eb0964338d24a8511b65e9c94fa5",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/614acc10c522e319639bf38b0698a4a566665f04",
"reference": "614acc10c522e319639bf38b0698a4a566665f04",
"shasum": ""
},
"require": {
@ -1462,7 +1462,7 @@
"require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-strict-rules": "^1.0",
"phpstan/phpstan-strict-rules": "^1.5.1",
"phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
@ -1486,9 +1486,9 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.13"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.14"
},
"time": "2023-05-26T11:05:59+00:00"
"time": "2023-08-25T09:46:39+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
@ -1541,16 +1541,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.3",
"version": "10.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "be1fe461fdc917de2a29a452ccf2657d325b443d"
"reference": "cd59bb34756a16ca8253ce9b2909039c227fff71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d",
"reference": "be1fe461fdc917de2a29a452ccf2657d325b443d",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cd59bb34756a16ca8253ce9b2909039c227fff71",
"reference": "cd59bb34756a16ca8253ce9b2909039c227fff71",
"shasum": ""
},
"require": {
@ -1607,7 +1607,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.4"
},
"funding": [
{
@ -1615,20 +1615,20 @@
"type": "github"
}
],
"time": "2023-07-26T13:45:28+00:00"
"time": "2023-08-31T14:04:38+00:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "4.0.2",
"version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "5647d65443818959172645e7ed999217360654b6"
"reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6",
"reference": "5647d65443818959172645e7ed999217360654b6",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
"reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
"shasum": ""
},
"require": {
@ -1668,7 +1668,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2"
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
},
"funding": [
{
@ -1676,7 +1676,7 @@
"type": "github"
}
],
"time": "2023-05-07T09:13:23+00:00"
"time": "2023-08-31T06:24:48+00:00"
},
{
"name": "phpunit/php-invoker",
@ -1743,16 +1743,16 @@
},
{
"name": "phpunit/php-text-template",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d"
"reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d",
"reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
"reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
"shasum": ""
},
"require": {
@ -1790,7 +1790,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0"
"security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
},
"funding": [
{
@ -1798,7 +1799,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:56:46+00:00"
"time": "2023-08-31T14:07:24+00:00"
},
{
"name": "phpunit/php-timer",
@ -1861,16 +1862,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.3.2",
"version": "10.3.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "0dafb1175c366dd274eaa9a625e914451506bcd1"
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0dafb1175c366dd274eaa9a625e914451506bcd1",
"reference": "0dafb1175c366dd274eaa9a625e914451506bcd1",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
"shasum": ""
},
"require": {
@ -1942,7 +1943,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.2"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.3"
},
"funding": [
{
@ -1958,7 +1959,7 @@
"type": "tidelift"
}
],
"time": "2023-08-15T05:34:23+00:00"
"time": "2023-09-05T04:34:51+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2206,16 +2207,16 @@
},
{
"name": "sebastian/complexity",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6"
"reference": "c70b73893e10757af9c6a48929fa6a333b56a97a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6",
"reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a",
"reference": "c70b73893e10757af9c6a48929fa6a333b56a97a",
"shasum": ""
},
"require": {
@ -2251,7 +2252,8 @@
"homepage": "https://github.com/sebastianbergmann/complexity",
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0"
"security": "https://github.com/sebastianbergmann/complexity/security/policy",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1"
},
"funding": [
{
@ -2259,7 +2261,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:59:47+00:00"
"time": "2023-08-31T09:55:53+00:00"
},
{
"name": "sebastian/diff",
@ -2533,16 +2535,16 @@
},
{
"name": "sebastian/lines-of-code",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130"
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130",
"reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d",
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d",
"shasum": ""
},
"require": {
@ -2578,7 +2580,8 @@
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
"support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0"
"security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1"
},
"funding": [
{
@ -2586,7 +2589,7 @@
"type": "github"
}
],
"time": "2023-02-03T07:08:02+00:00"
"time": "2023-08-31T09:25:50+00:00"
},
{
"name": "sebastian/object-enumerator",
@ -2960,5 +2963,5 @@
"platform-overrides": {
"php": "8.1.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View File

@ -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();
}

View File

@ -120,6 +120,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;
@ -357,15 +358,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));
}
/**
@ -376,12 +377,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 : "::";
}
@ -390,30 +391,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{
@ -496,7 +497,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{
@ -736,7 +737,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){
@ -811,36 +812,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){
@ -856,11 +857,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();
@ -878,7 +879,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;
@ -889,32 +890,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);
@ -932,9 +933,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{
@ -943,8 +944,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());
@ -963,8 +964,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();
@ -986,14 +987,14 @@ 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());
$this->pluginManager->registerInterface(new FolderPluginLoader($this->autoloader));
$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);
@ -1002,10 +1003,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);
@ -1042,7 +1043,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);
}
@ -1058,7 +1059,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);
}
@ -1093,7 +1094,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)){
@ -1137,11 +1138,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)){
@ -1149,8 +1150,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){
@ -1160,7 +1161,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);
}
@ -1214,7 +1215,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());
@ -1224,7 +1225,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)
)
){
@ -1239,7 +1240,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()));
}
@ -1459,7 +1460,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)){
@ -1596,7 +1597,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");
@ -1617,7 +1618,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",
@ -1728,7 +1729,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 = [];

58
src/ServerProperties.php Normal file
View File

@ -0,0 +1,58 @@
<?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{
private function __construct(){
//NOOP
}
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";
}

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.4.3";
public const BASE_VERSION = "5.5.1";
public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "stable";

118
src/YmlServerProperties.php Normal file
View File

@ -0,0 +1,118 @@
<?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{
private function __construct(){
//NOOP
}
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';
}

View File

@ -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();

View File

@ -42,7 +42,7 @@ class Fence extends Transparent{
foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing);
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
$this->connections[$facing] = true;
}else{
unset($this->connections[$facing]);

View File

@ -41,7 +41,7 @@ class Thin extends Transparent{
foreach(Facing::HORIZONTAL as $facing){
$side = $this->getSide($facing);
if($side instanceof Thin || $side instanceof Wall || $side->isFullCube()){
if($side instanceof Thin || $side instanceof Wall || $side->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
$this->connections[$facing] = true;
}else{
unset($this->connections[$facing]);

View File

@ -101,7 +101,7 @@ class Wall extends Transparent{
foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing);
if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || ($block->isSolid() && !$block->isTransparent())){
if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || $block->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
if(!isset($this->connections[$facing])){
$this->connections[$facing] = WallConnectionType::SHORT();
$changed++;

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\event\player\PlayerEnchantOptionsRequestEvent;
use pocketmine\event\player\PlayerEnchantingOptionsRequestEvent;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\item\enchantment\EnchantmentHelper as Helper;
use pocketmine\item\enchantment\EnchantOption;
use pocketmine\item\enchantment\EnchantingHelper as Helper;
use pocketmine\item\enchantment\EnchantingOption;
use pocketmine\item\Item;
use pocketmine\world\Position;
use function array_values;
@ -39,7 +39,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
public const SLOT_INPUT = 0;
public const SLOT_LAPIS = 1;
/** @var EnchantOption[] $options */
/** @var EnchantingOption[] $options */
private array $options = [];
public function __construct(Position $holder){
@ -52,9 +52,9 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
foreach($this->viewers as $viewer){
$this->options = [];
$item = $this->getInput();
$options = Helper::getEnchantOptions($this->holder, $item, $viewer->getEnchantmentSeed());
$options = Helper::generateOptions($this->holder, $item, $viewer->getEnchantmentSeed());
$event = new PlayerEnchantOptionsRequestEvent($viewer, $this, $options);
$event = new PlayerEnchantingOptionsRequestEvent($viewer, $this, $options);
$event->call();
if(!$event->isCancelled() && count($event->getOptions()) > 0){
$this->options = array_values($event->getOptions());
@ -79,7 +79,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
return $option === null ? null : Helper::enchantItem($this->getInput(), $option->getEnchantments());
}
public function getOption(int $optionId) : ?EnchantOption{
public function getOption(int $optionId) : ?EnchantingOption{
return $this->options[$optionId] ?? null;
}
}

View File

@ -32,6 +32,6 @@ final class SmithingTableInventory extends SimpleInventory implements BlockInven
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(2);
parent::__construct(3);
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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){

View File

@ -25,7 +25,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\item\enchantment\EnchantmentHelper;
use pocketmine\item\enchantment\EnchantingHelper;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\StringToEnchantmentParser;
use pocketmine\lang\KnownTranslationFactory;
@ -78,7 +78,7 @@ class EnchantCommand extends VanillaCommand{
}
//this is necessary to deal with enchanted books, which are a different item type than regular books
$enchantedItem = EnchantmentHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]);
$enchantedItem = EnchantingHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]);
$player->getInventory()->setItemInHand($enchantedItem);
self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName()));

View File

@ -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(

View File

@ -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());
}

View File

@ -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
@ -206,6 +207,7 @@ class CrashDump{
if(isset($lastError)){
$this->data->lastError = $lastError;
$this->data->lastError["message"] = mb_scrub($this->data->lastError["message"], 'UTF-8');
$this->data->lastError["trace"] = array_map(array: $lastError["trace"], callback: fn(ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
}
$this->data->error = $error;
@ -225,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){

View File

@ -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);
}

View File

@ -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)];
}
}

View File

@ -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)];
}
}

View 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];
}
}

View File

@ -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()];
}
}

View File

@ -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()];
}
}

View File

@ -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()];
}
}

View File

@ -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()];
}
}

View File

@ -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()];
}
}

View File

@ -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()];
}
}

View File

@ -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 mt_rand;
class Human extends Living implements ProjectileSource, InventoryHolder{
@ -219,8 +218,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->xpSeed = $seed;
}
public function generateEnchantmentSeed() : int{
return mt_rand(Limits::INT32_MIN, Limits::INT32_MAX);
public function regenerateEnchantmentSeed() : void{
$this->xpSeed = EnchantingHelper::generateSeed();
}
public function getXpDropAmount() : int{
@ -346,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 = $this->generateEnchantmentSeed();
$this->xpSeed = EnchantingHelper::generateSeed();
}
}

View File

@ -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{

View File

@ -25,13 +25,12 @@ namespace pocketmine\event\block;
use pocketmine\block\Block;
use pocketmine\entity\Entity;
use pocketmine\event\Cancellable;
/**
* 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 implements Cancellable{
final class PressurePlateUpdateEvent extends BaseBlockChangeEvent{
/**
* @param Entity[] $activatingEntities
*/

View File

@ -27,7 +27,7 @@ use pocketmine\block\inventory\EnchantInventory;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
use pocketmine\event\Event;
use pocketmine\item\enchantment\EnchantOption;
use pocketmine\item\enchantment\EnchantingOption;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
use function count;
@ -36,36 +36,36 @@ 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 PlayerEnchantOptionsRequestEvent extends PlayerEvent implements Cancellable{
class PlayerEnchantingOptionsRequestEvent extends PlayerEvent implements Cancellable{
use CancellableTrait;
/**
* @param EnchantOption[] $options
* @param EnchantingOption[] $options
*/
public function __construct(
Player $player,
private readonly EnchantInventory $enchantInventory,
private readonly EnchantInventory $inventory,
private array $options
){
$this->player = $player;
}
public function getEnchantInventory() : EnchantInventory{
return $this->enchantInventory;
public function getInventory() : EnchantInventory{
return $this->inventory;
}
/**
* @return EnchantOption[]
* @return EnchantingOption[]
*/
public function getOptions() : array{
return $this->options;
}
/**
* @param EnchantOption[] $options
* @param EnchantingOption[] $options
*/
public function setOptions(array $options) : void{
Utils::validateArrayValueType($options, function(EnchantOption $_) : 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");
}

View File

@ -25,8 +25,8 @@ namespace pocketmine\event\player;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
use pocketmine\inventory\transaction\EnchantTransaction;
use pocketmine\item\enchantment\EnchantOption;
use pocketmine\inventory\transaction\EnchantingTransaction;
use pocketmine\item\enchantment\EnchantingOption;
use pocketmine\item\Item;
use pocketmine\player\Player;
@ -38,8 +38,8 @@ class PlayerItemEnchantEvent extends PlayerEvent implements Cancellable{
public function __construct(
Player $player,
private readonly EnchantTransaction $transaction,
private readonly EnchantOption $option,
private readonly EnchantingTransaction $transaction,
private readonly EnchantingOption $option,
private readonly Item $inputItem,
private readonly Item $outputItem,
private readonly int $cost
@ -50,14 +50,14 @@ class PlayerItemEnchantEvent extends PlayerEvent implements Cancellable{
/**
* Returns the inventory transaction involved in this enchant event.
*/
public function getTransaction() : EnchantTransaction{
public function getTransaction() : EnchantingTransaction{
return $this->transaction;
}
/**
* Returns the enchantment option used.
*/
public function getOption() : EnchantOption{
public function getOption() : EnchantingOption{
return $this->option;
}

View File

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace pocketmine\inventory\transaction;
use pocketmine\event\player\PlayerItemEnchantEvent;
use pocketmine\item\enchantment\EnchantmentHelper;
use pocketmine\item\enchantment\EnchantOption;
use pocketmine\item\enchantment\EnchantingHelper;
use pocketmine\item\enchantment\EnchantingOption;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
use pocketmine\player\Player;
@ -33,14 +33,14 @@ use pocketmine\utils\AssumptionFailedError;
use function count;
use function min;
class EnchantTransaction extends InventoryTransaction{
class EnchantingTransaction extends InventoryTransaction{
private ?Item $inputItem = null;
private ?Item $outputItem = null;
public function __construct(
Player $source,
private readonly EnchantOption $option,
private readonly EnchantingOption $option,
private readonly int $cost
){
parent::__construct($source);
@ -51,7 +51,7 @@ class EnchantTransaction extends InventoryTransaction{
throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before validating output");
}
$enchantedInput = EnchantmentHelper::enchantItem($this->inputItem, $this->option->getEnchantments());
$enchantedInput = EnchantingHelper::enchantItem($this->inputItem, $this->option->getEnchantments());
if(!$this->outputItem->equalsExact($enchantedInput)){
throw new TransactionValidationException("Invalid output item");
}
@ -119,7 +119,7 @@ class EnchantTransaction extends InventoryTransaction{
//In this case, as much XP as possible will be taken.
$this->source->getXpManager()->subtractXpLevels(min($this->cost, $this->source->getXpManager()->getXpLevel()));
}
$this->source->setEnchantmentSeed($this->source->generateEnchantmentSeed());
$this->source->regenerateEnchantmentSeed();
}
protected function callExecuteEvent() : bool{

View File

@ -59,9 +59,9 @@ final class AvailableEnchantmentRegistry{
$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::DIG_TOOLS], [Tags::SHEARS]);
$this->register(Enchantments::FORTUNE(), [Tags::DIG_TOOLS], []);
$this->register(Enchantments::SILK_TOUCH(), [Tags::DIG_TOOLS], [Tags::SHEARS]);
$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],

View File

@ -28,6 +28,7 @@ use pocketmine\item\enchantment\AvailableEnchantmentRegistry as EnchantmentRegis
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;
@ -37,12 +38,27 @@ use function count;
use function floor;
use function max;
use function min;
use function mt_rand;
use function ord;
use function round;
final class EnchantmentHelper{
/**
* 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
*/
@ -57,9 +73,9 @@ final class EnchantmentHelper{
}
/**
* @return EnchantOption[]
* @return EnchantingOption[]
*/
public static function getEnchantOptions(Position $tablePos, Item $input, int $seed) : array{
public static function generateOptions(Position $tablePos, Item $input, int $seed) : array{
if($input->isNull() || $input->hasEnchantments()){
return [];
}
@ -73,9 +89,9 @@ final class EnchantmentHelper{
$bottomRequiredLevel = max($baseRequiredLevel, $bookshelfCount * 2);
return [
self::createEnchantOption($random, $input, $topRequiredLevel),
self::createEnchantOption($random, $input, $middleRequiredLevel),
self::createEnchantOption($random, $input, $bottomRequiredLevel),
self::createOption($random, $input, $topRequiredLevel),
self::createOption($random, $input, $middleRequiredLevel),
self::createOption($random, $input, $bottomRequiredLevel),
];
}
@ -117,7 +133,7 @@ final class EnchantmentHelper{
return $bookshelfCount;
}
private static function createEnchantOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantOption{
private static function createOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantingOption{
$enchantingPower = $requiredXpLevel;
$enchantability = $inputItem->getEnchantability();
@ -155,7 +171,7 @@ final class EnchantmentHelper{
}
}
return new EnchantOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments);
return new EnchantingOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments);
}
/**

View File

@ -27,7 +27,7 @@ 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 EnchantOption{
class EnchantingOption{
/**
* @param EnchantmentInstance[] $enchantments

View File

@ -83,6 +83,7 @@ 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;
@ -93,6 +94,7 @@ class Enchantment{
* an anvil.
*
* @deprecated
* @see AvailableEnchantmentRegistry::getSecondaryItemTags()
*/
public function getSecondaryItemFlags() : int{
return $this->secondaryItemFlags;
@ -102,6 +104,7 @@ 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;
@ -111,6 +114,7 @@ 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;

View File

@ -30,5 +30,5 @@ namespace pocketmine\item\enchantment;
final class IncompatibleEnchantmentGroups{
public const PROTECTION = "protection";
public const BOW_INFINITE = "bow_infinite";
public const DIG_DROP = "dig_drop";
public const BLOCK_DROPS = "block_drops";
}

View File

@ -46,7 +46,7 @@ final class IncompatibleEnchantmentRegistry{
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::DIG_DROP, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]);
$this->register(Groups::BLOCK_DROPS, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]);
}
/**

View File

@ -56,7 +56,7 @@ final class ItemEnchantmentTagRegistry{
$this->register(Tags::CROSSBOW);
$this->register(Tags::SHEARS);
$this->register(Tags::FLINT_AND_STEEL);
$this->register(Tags::DIG_TOOLS, [Tags::AXE, Tags::PICKAXE, Tags::SHOVEL, Tags::HOE]);
$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);
@ -68,7 +68,7 @@ final class ItemEnchantmentTagRegistry{
Tags::TRIDENT,
Tags::BOW,
Tags::CROSSBOW,
Tags::DIG_TOOLS,
Tags::BLOCK_TOOLS,
]);
}

View File

@ -42,7 +42,7 @@ final class ItemEnchantmentTags{
public const CROSSBOW = "crossbow";
public const SHEARS = "shears";
public const FLINT_AND_STEEL = "flint_and_steel";
public const DIG_TOOLS = "dig_tools";
public const BLOCK_TOOLS = "block_tools";
public const AXE = "axe";
public const PICKAXE = "pickaxe";
public const SHOVEL = "shovel";

View File

@ -39,8 +39,8 @@ use pocketmine\data\bedrock\EnchantmentIdMap;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\item\enchantment\EnchantingOption;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\EnchantOption;
use pocketmine\network\mcpe\cache\CreativeInventoryCache;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
@ -645,7 +645,7 @@ class InventoryManager{
}
/**
* @param EnchantOption[] $options
* @param EnchantingOption[] $options
*/
public function syncEnchantingTableOptions(array $options) : void{
$protocolOptions = [];

View File

@ -108,6 +108,7 @@ use pocketmine\utils\BinaryStream;
use pocketmine\utils\ObjectSet;
use pocketmine\utils\TextFormat;
use pocketmine\world\Position;
use pocketmine\YmlServerProperties;
use function array_map;
use function array_values;
use function base64_encode;
@ -740,7 +741,7 @@ class NetworkSession{
}
$this->logger->debug("Xbox Live authenticated: " . ($this->authenticated ? "YES" : "NO"));
$checkXUID = $this->server->getConfigGroup()->getPropertyBool("player.verify-xuid", true);
$checkXUID = $this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::PLAYER_VERIFY_XUID, true);
$myXUID = $this->info instanceof XboxLivePlayerInfo ? $this->info->getXuid() : "";
$kickForXUIDMismatch = function(string $xuid) use ($checkXUID, $myXUID) : bool{
if($checkXUID && $myXUID !== $xuid){

View File

@ -201,7 +201,7 @@ class InGamePacketHandler extends PacketHandler{
}
$hasMoved = $this->lastPlayerAuthInputPosition === null || !$this->lastPlayerAuthInputPosition->equals($rawPos);
$newPos = $rawPos->round(4)->subtract(0, 1.62, 0);
$newPos = $rawPos->subtract(0, 1.62, 0)->round(4);
if($this->forceMoveSync && $hasMoved){
$curPos = $this->player->getLocation();

View File

@ -29,7 +29,7 @@ use pocketmine\inventory\transaction\action\CreateItemAction;
use pocketmine\inventory\transaction\action\DestroyItemAction;
use pocketmine\inventory\transaction\action\DropItemAction;
use pocketmine\inventory\transaction\CraftingTransaction;
use pocketmine\inventory\transaction\EnchantTransaction;
use pocketmine\inventory\transaction\EnchantingTransaction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionBuilder;
use pocketmine\inventory\transaction\TransactionBuilderInventory;
@ -289,7 +289,7 @@ class ItemStackRequestExecutor{
* @throws ItemStackRequestProcessException
*/
private function assertDoingCrafting() : void{
if(!$this->specialTransaction instanceof CraftingTransaction && !$this->specialTransaction instanceof EnchantTransaction){
if(!$this->specialTransaction instanceof CraftingTransaction && !$this->specialTransaction instanceof EnchantingTransaction){
if($this->specialTransaction === null){
throw new ItemStackRequestProcessException("Expected CraftRecipe or CraftRecipeAuto action to precede this action");
}else{
@ -339,7 +339,7 @@ class ItemStackRequestExecutor{
if($window instanceof EnchantInventory){
$optionId = $this->inventoryManager->getEnchantingTableOptionIndex($action->getRecipeId());
if($optionId !== null && ($option = $window->getOption($optionId)) !== null){
$this->specialTransaction = new EnchantTransaction($this->player, $option, $optionId + 1);
$this->specialTransaction = new EnchantingTransaction($this->player, $option, $optionId + 1);
$this->setNextCreatedItem($window->getOutput($optionId));
}
}else{

View File

@ -42,6 +42,7 @@ use pocketmine\Server;
use pocketmine\thread\ThreadCrashException;
use pocketmine\timings\Timings;
use pocketmine\utils\Utils;
use pocketmine\YmlServerProperties;
use raklib\generic\DisconnectReason;
use raklib\generic\SocketException;
use raklib\protocol\EncapsulatedPacket;
@ -125,7 +126,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$threadToMainBuffer,
new InternetAddress($ip, $port, $ipV6 ? 6 : 4),
$this->rakServerId,
$this->server->getConfigGroup()->getPropertyInt("network.max-mtu-size", 1492),
$this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::NETWORK_MAX_MTU_SIZE, 1492),
self::MCPE_RAKNET_PROTOCOL_VERSION,
$sleeperEntry
);

View File

@ -29,6 +29,7 @@ use pocketmine\plugin\Plugin;
use pocketmine\Server;
use pocketmine\utils\Binary;
use pocketmine\utils\Utils;
use pocketmine\YmlServerProperties;
use function array_map;
use function chr;
use function count;
@ -66,7 +67,7 @@ final class QueryInfo{
public function __construct(Server $server){
$this->serverName = $server->getMotd();
$this->listPlugins = $server->getConfigGroup()->getPropertyBool("settings.query-plugins", true);
$this->listPlugins = $server->getConfigGroup()->getPropertyBool(YmlServerProperties::SETTINGS_QUERY_PLUGINS, true);
$this->plugins = $server->getPluginManager()->getPlugins();
$this->players = array_map(fn(Player $p) => $p->getName(), $server->getOnlinePlayers());

View File

@ -119,6 +119,7 @@ use pocketmine\permission\PermissibleBase;
use pocketmine\permission\PermissibleDelegateTrait;
use pocketmine\player\chat\StandardChatFormatter;
use pocketmine\Server;
use pocketmine\ServerProperties;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
@ -134,6 +135,7 @@ use pocketmine\world\sound\FireExtinguishSound;
use pocketmine\world\sound\ItemBreakSound;
use pocketmine\world\sound\Sound;
use pocketmine\world\World;
use pocketmine\YmlServerProperties;
use Ramsey\Uuid\UuidInterface;
use function abs;
use function array_filter;
@ -317,8 +319,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$rootPermissions[DefaultPermissions::ROOT_OPERATOR] = true;
}
$this->perm = new PermissibleBase($rootPermissions);
$this->chunksPerTick = $this->server->getConfigGroup()->getPropertyInt("chunk-sending.per-tick", 4);
$this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt("chunk-sending.spawn-radius", 4) ** 2) * M_PI);
$this->chunksPerTick = $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_PER_TICK, 4);
$this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4) ** 2) * M_PI);
$this->chunkSelector = new ChunkSelector();
$this->chunkLoader = new class implements ChunkLoader{};
@ -583,7 +585,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->viewDistance = $newViewDistance;
$this->spawnThreshold = (int) (min($this->viewDistance, $this->server->getConfigGroup()->getPropertyInt("chunk-sending.spawn-radius", 4)) ** 2 * M_PI);
$this->spawnThreshold = (int) (min($this->viewDistance, $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4)) ** 2 * M_PI);
$this->nextChunkOrderRun = 0;
@ -1330,18 +1332,20 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$deltaAngle = abs($this->lastLocation->yaw - $to->yaw) + abs($this->lastLocation->pitch - $to->pitch);
if($delta > 0.0001 || $deltaAngle > 1.0){
$ev = new PlayerMoveEvent($this, $from, $to);
if(PlayerMoveEvent::hasHandlers()){
$ev = new PlayerMoveEvent($this, $from, $to);
$ev->call();
$ev->call();
if($ev->isCancelled()){
$this->revertMovement($from);
return;
}
if($ev->isCancelled()){
$this->revertMovement($from);
return;
}
if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination
$this->teleport($ev->getTo());
return;
if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination
$this->teleport($ev->getTo());
return;
}
}
$this->lastLocation = $to;
@ -1842,7 +1846,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
if(!$this->canInteract($entity->getLocation(), self::MAX_REACH_DISTANCE_ENTITY_INTERACTION)){
$this->logger->debug("Cancelled attack of entity " . $entity->getId() . " due to not currently being interactable");
$ev->cancel();
}elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool("pvp"))){
}elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool(ServerProperties::PVP))){
$ev->cancel();
}

View File

@ -31,6 +31,7 @@ use pocketmine\utils\Internet;
use pocketmine\utils\Process;
use pocketmine\utils\Utils;
use pocketmine\VersionInfo;
use pocketmine\YmlServerProperties;
use Ramsey\Uuid\Uuid;
use function array_map;
use function array_values;
@ -57,7 +58,7 @@ class SendUsageTask extends AsyncTask{
* @phpstan-param array<string, string> $playerList
*/
public function __construct(Server $server, int $type, array $playerList = []){
$endpoint = "http://" . $server->getConfigGroup()->getPropertyString("anonymous-statistics.host", "stats.pocketmine.net") . "/";
$endpoint = "http://" . $server->getConfigGroup()->getPropertyString(YmlServerProperties::ANONYMOUS_STATISTICS_HOST, "stats.pocketmine.net") . "/";
$data = [];
$data["uniqueServerId"] = $server->getServerUniqueId()->toString();

View File

@ -27,6 +27,7 @@ use pocketmine\event\server\UpdateNotifyEvent;
use pocketmine\Server;
use pocketmine\utils\VersionString;
use pocketmine\VersionInfo;
use pocketmine\YmlServerProperties;
use function date;
use function strtolower;
use function ucfirst;
@ -43,7 +44,7 @@ class UpdateChecker{
$this->logger = new \PrefixedLogger($server->getLogger(), "Update Checker");
$this->endpoint = "http://$endpoint/api/";
if($server->getConfigGroup()->getPropertyBool("auto-updater.enabled", true)){
if($server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_UPDATER_ENABLED, true)){
$this->doCheck();
}
}
@ -59,7 +60,7 @@ class UpdateChecker{
$this->checkUpdate($updateInfo);
if($this->hasUpdate()){
(new UpdateNotifyEvent($this))->call();
if($this->server->getConfigGroup()->getPropertyBool("auto-updater.on-update.warn-console", true)){
if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_UPDATER_ON_UPDATE_WARN_CONSOLE, true)){
$this->showConsoleUpdate();
}
}else{
@ -157,7 +158,7 @@ class UpdateChecker{
* Returns the channel used for update checking (stable, beta, dev)
*/
public function getChannel() : string{
return strtolower($this->server->getConfigGroup()->getPropertyString("auto-updater.preferred-channel", "stable"));
return strtolower($this->server->getConfigGroup()->getPropertyString(YmlServerProperties::AUTO_UPDATER_PREFERRED_CHANNEL, "stable"));
}
/**

View File

@ -58,6 +58,21 @@ abstract class StringToTParser{
$this->callbackMap[$this->reprocess($alias)] = $callback;
}
/**
* Registers a new alias for an existing known alias.
*/
public function registerAlias(string $existing, string $alias) : void{
$existingKey = $this->reprocess($existing);
if(!isset($this->callbackMap[$existingKey])){
throw new \InvalidArgumentException("Cannot register new alias for unknown existing alias \"$existing\"");
}
$newKey = $this->reprocess($alias);
if(isset($this->callbackMap[$newKey])){
throw new \InvalidArgumentException("Alias \"$newKey\" is already registered");
}
$this->callbackMap[$newKey] = $this->callbackMap[$existingKey];
}
/**
* Tries to parse the specified string into a corresponding instance of T.
* @phpstan-return T|null

View File

@ -104,6 +104,7 @@ use pocketmine\world\particle\Particle;
use pocketmine\world\sound\BlockPlaceSound;
use pocketmine\world\sound\Sound;
use pocketmine\world\utils\SubChunkExplorer;
use pocketmine\YmlServerProperties;
use function abs;
use function array_filter;
use function array_key_exists;
@ -513,14 +514,14 @@ class World implements ChunkManager{
$this->time = $this->provider->getWorldData()->getTime();
$cfg = $this->server->getConfigGroup();
$this->chunkTickRadius = min($this->server->getViewDistance(), max(0, $cfg->getPropertyInt("chunk-ticking.tick-radius", 4)));
$this->chunkTickRadius = min($this->server->getViewDistance(), max(0, $cfg->getPropertyInt(YmlServerProperties::CHUNK_TICKING_TICK_RADIUS, 4)));
if($cfg->getPropertyInt("chunk-ticking.per-tick", 40) <= 0){
//TODO: this needs l10n
$this->logger->warning("\"chunk-ticking.per-tick\" setting is deprecated, but you've used it to disable chunk ticking. Set \"chunk-ticking.tick-radius\" to 0 in \"pocketmine.yml\" instead.");
$this->chunkTickRadius = 0;
}
$this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt("chunk-ticking.blocks-per-subchunk-per-tick", self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK);
$this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt("chunk-generation.population-queue-size", 2);
$this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt(YmlServerProperties::CHUNK_TICKING_BLOCKS_PER_SUBCHUNK_PER_TICK, self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK);
$this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2);
$this->initRandomTickBlocksFromConfig($cfg);
@ -541,7 +542,7 @@ class World implements ChunkManager{
private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{
$dontTickBlocks = [];
$parser = StringToItemParser::getInstance();
foreach($cfg->getProperty("chunk-ticking.disable-block-ticking", []) as $name){
foreach($cfg->getProperty(YmlServerProperties::CHUNK_TICKING_DISABLE_BLOCK_TICKING, []) as $name){
$name = (string) $name;
$item = $parser->parse($name);
if($item !== null){
@ -694,16 +695,19 @@ class World implements ChunkManager{
*/
public function addSound(Vector3 $pos, Sound $sound, ?array $players = null) : void{
$players ??= $this->getViewersForPosition($pos);
$ev = new WorldSoundEvent($this, $sound, $pos, $players);
$ev->call();
if(WorldSoundEvent::hasHandlers()){
$ev = new WorldSoundEvent($this, $sound, $pos, $players);
$ev->call();
if($ev->isCancelled()){
return;
}
if($ev->isCancelled()){
return;
$sound = $ev->getSound();
$players = $ev->getRecipients();
}
$pk = $ev->getSound()->encode($pos);
$players = $ev->getRecipients();
$pk = $sound->encode($pos);
if(count($pk) > 0){
if($players === $this->getViewersForPosition($pos)){
foreach($pk as $e){
@ -720,23 +724,26 @@ class World implements ChunkManager{
*/
public function addParticle(Vector3 $pos, Particle $particle, ?array $players = null) : void{
$players ??= $this->getViewersForPosition($pos);
$ev = new WorldParticleEvent($this, $particle, $pos, $players);
$ev->call();
if(WorldParticleEvent::hasHandlers()){
$ev = new WorldParticleEvent($this, $particle, $pos, $players);
$ev->call();
if($ev->isCancelled()){
return;
}
if($ev->isCancelled()){
return;
$particle = $ev->getParticle();
$players = $ev->getRecipients();
}
$pk = $ev->getParticle()->encode($pos);
$players = $ev->getRecipients();
$pk = $particle->encode($pos);
if(count($pk) > 0){
if($players === $this->getViewersForPosition($pos)){
foreach($pk as $e){
$this->broadcastPacketToViewers($pos, $e);
}
}else{
NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk);
NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $players), $pk);
}
}
}
@ -2517,7 +2524,9 @@ class World implements ChunkManager{
}
if($oldChunk === null){
(new ChunkLoadEvent($this, $chunkX, $chunkZ, $chunk, true))->call();
if(ChunkLoadEvent::hasHandlers()){
(new ChunkLoadEvent($this, $chunkX, $chunkZ, $chunk, true))->call();
}
foreach($this->getChunkListeners($chunkX, $chunkZ) as $listener){
$listener->onChunkLoaded($chunkX, $chunkZ, $chunk);
@ -2791,7 +2800,9 @@ class World implements ChunkManager{
$this->initChunk($x, $z, $chunkData);
(new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call();
if(ChunkLoadEvent::hasHandlers()){
(new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call();
}
if(!$this->isChunkInUse($x, $z)){
$this->logger->debug("Newly loaded chunk $x $z has no loaders registered, will be unloaded at next available opportunity");
@ -2904,12 +2915,14 @@ class World implements ChunkManager{
$chunk = $this->chunks[$chunkHash] ?? null;
if($chunk !== null){
$ev = new ChunkUnloadEvent($this, $x, $z, $chunk);
$ev->call();
if($ev->isCancelled()){
$this->timings->doChunkUnload->stopTiming();
if(ChunkUnloadEvent::hasHandlers()){
$ev = new ChunkUnloadEvent($this, $x, $z, $chunk);
$ev->call();
if($ev->isCancelled()){
$this->timings->doChunkUnload->stopTiming();
return false;
return false;
}
}
if($trySave && $this->getAutoSave()){
@ -3366,7 +3379,9 @@ class World implements ChunkManager{
}
if(($oldChunk === null || !$oldChunk->isPopulated()) && $chunk->isPopulated()){
(new ChunkPopulateEvent($this, $x, $z, $chunk))->call();
if(ChunkPopulateEvent::hasHandlers()){
(new ChunkPopulateEvent($this, $x, $z, $chunk))->call();
}
foreach($this->getChunkListeners($x, $z) as $listener){
$listener->onChunkPopulated($x, $z, $chunk);

View File

@ -440,17 +440,22 @@ class ParserPacketHandler extends PacketHandler{
//how the data is ordered doesn't matter as long as it's reproducible
foreach($recipes as $_type => $entries){
$_sortedRecipes = [];
$_seen = [];
foreach($entries as $entry){
$entry = self::sort($entry);
$_key = json_encode($entry);
while(isset($_sortedRecipes[$_key])){
echo "warning: duplicated $_type recipe: $_key\n";
$_key .= "a";
}
$_sortedRecipes[$_key] = $entry;
$duplicates = $_seen[$_key] ??= 0;
$_seen[$_key]++;
$suffix = chr(ord("a") + $duplicates);
$_sortedRecipes[$_key . $suffix] = $entry;
}
ksort($_sortedRecipes, SORT_STRING);
$recipes[$_type] = array_values($_sortedRecipes);
foreach($_seen as $_key => $_seenCount){
if($_seenCount > 1){
fwrite(STDERR, "warning: $_type recipe $_key was seen $_seenCount times\n");
}
}
}
ksort($recipes, SORT_STRING);