mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-12 20:35:11 +00:00
Compare commits
149 Commits
4.0.0-BETA
...
4.0.0-BETA
Author | SHA1 | Date | |
---|---|---|---|
46920818b5 | |||
69cb575789 | |||
fee6478cbe | |||
9c5cec77b1 | |||
f48b703533 | |||
70636f6eb4 | |||
c70b80c273 | |||
a794d24c81 | |||
8db5732b44 | |||
48f809d3fa | |||
0348236860 | |||
8c07748100 | |||
06e7338ff9 | |||
bdbfa70558 | |||
7a4af7a0bc | |||
34b1392598 | |||
321345fcc8 | |||
0ac9f4fe61 | |||
2db53775e0 | |||
8523f0fb0b | |||
b570324288 | |||
6284cd14c7 | |||
ce8af4e3bc | |||
b65e89b605 | |||
d3f74d6ce1 | |||
bbd925abc4 | |||
4bf6205a6c | |||
b5699679ef | |||
824a89edfe | |||
ead9aae23c | |||
aefa0afd7c | |||
ec2699ffee | |||
49c1e4c06e | |||
9b94a4661b | |||
62f11360ee | |||
a5833327f0 | |||
d73ea8efe4 | |||
835e18ce6e | |||
01c0602043 | |||
8fd475f87b | |||
34f54750c8 | |||
092aabeb97 | |||
89d7b7198f | |||
70deea0ef9 | |||
859cdfa5d2 | |||
fa93a8d78f | |||
7b6632941d | |||
e62794e4cf | |||
500c298aaf | |||
8ac16345a3 | |||
19a66a8d03 | |||
6d728e8d98 | |||
e1ee320c8d | |||
965a16d19d | |||
5bae458a91 | |||
2696698926 | |||
912e612743 | |||
fd2df637b6 | |||
aa53dc6709 | |||
c1f843a42c | |||
09715906c8 | |||
13068ba3a7 | |||
b54854529f | |||
974d08efd6 | |||
289553fa46 | |||
e38866c4ba | |||
58a95f8836 | |||
ccc881ee58 | |||
308d7c126a | |||
4910250a81 | |||
e0d2e24698 | |||
d5f02a0bf8 | |||
2a3a57c519 | |||
5115387feb | |||
dd0aaf59b5 | |||
a555f21b18 | |||
1be9b2f037 | |||
32fd9879e5 | |||
dc2e8e7e8f | |||
847e24fc41 | |||
9e6d740570 | |||
8e3772ceef | |||
e032b8fe20 | |||
90800a4124 | |||
4b00465e24 | |||
10b3596eef | |||
258c38f9cd | |||
d07517fe8b | |||
31a176286d | |||
1fafce6d6f | |||
5061bbbc25 | |||
a101d1cdf9 | |||
fec48003d9 | |||
e25c03eec1 | |||
7245d15abe | |||
13178a47a5 | |||
817ab88c70 | |||
fef8297907 | |||
dbeaf27cb7 | |||
2db79cf58d | |||
7d06b76aaf | |||
0ad663ff50 | |||
a27c14c00c | |||
5b26abcb0e | |||
f2d6059613 | |||
bb6ea8cbdc | |||
356a49d225 | |||
6332af3e59 | |||
fb570970a8 | |||
30e10c38b6 | |||
05dc675d5b | |||
d63b9d1648 | |||
f9e6fd44bc | |||
0108888450 | |||
dd0c2fed82 | |||
2566123e49 | |||
54174eefa0 | |||
f5266ec816 | |||
c6b2c63a9b | |||
f26f063164 | |||
81d5598e96 | |||
c7e9138994 | |||
88f799da2c | |||
8de30e8162 | |||
e6f6a036ef | |||
32f8b8163e | |||
5b818827db | |||
42bf9578ce | |||
aee4a00a50 | |||
2fdd8d039e | |||
349f37b15f | |||
afa3349c04 | |||
6a8280b1ba | |||
003c002208 | |||
d417b1e2f5 | |||
65e468e3c2 | |||
a11cf8c296 | |||
c931c52617 | |||
b3e8314b9f | |||
f138004913 | |||
8e2d06a880 | |||
eb80515e99 | |||
1cb540387c | |||
d455188d03 | |||
14fba36636 | |||
43ac3fbf3e | |||
352162a6e6 | |||
b3601c9390 | |||
817fec9e3d |
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.9]
|
||||
php: [8.0.11]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2 #needed for build.sh
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.9]
|
||||
php: [8.0.11]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -87,7 +87,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.9]
|
||||
php: [8.0.11]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -139,7 +139,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.9]
|
||||
php: [8.0.11]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -191,7 +191,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.9]
|
||||
php: [8.0.11]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
2
.github/workflows/update-php-versions.php
vendored
2
.github/workflows/update-php-versions.php
vendored
@ -22,8 +22,6 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const VERSIONS = [
|
||||
"7.3",
|
||||
"7.4",
|
||||
"8.0"
|
||||
];
|
||||
|
||||
|
@ -2,13 +2,13 @@
|
||||
## Pre-requisites
|
||||
- A bash shell (git bash is sufficient for Windows)
|
||||
- [`git`](https://git-scm.com) available in your shell
|
||||
- PHP 7.4 or newer available in your shell
|
||||
- PHP 8.0 or newer available in your shell
|
||||
- [`composer`](https://getcomposer.org) available in your shell
|
||||
|
||||
## Custom PHP binaries
|
||||
Because PocketMine-MP requires several non-standard PHP extensions and configuration, PMMP provides scripts to build custom binaries for running PocketMine-MP, as well as prebuilt binaries.
|
||||
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.4-Aggregate)
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-8.0-Aggregate)
|
||||
- [Compile scripts](https://github.com/pmmp/php-build-scripts) are provided as a submodule in the path `build/php`
|
||||
|
||||
If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`.
|
||||
@ -34,7 +34,7 @@ There is a bug in PHP that might cause an error which looks like this:
|
||||
```
|
||||
Fatal error: Uncaught BadMethodCallException: unable to create temporary file in PocketMine-MP/build/server-phar.php:119
|
||||
```
|
||||
You can work around it by setting `ulimit -n` to some bigger number, e.g. `8192`, or by updating your PHP version to at least 7.4.16 or 8.0.3.
|
||||
You can work around it by setting `ulimit -n` to some bigger number, e.g. `8192`, or by updating your PHP version to at least 8.0.3.
|
||||
|
||||
## Running PocketMine-MP from source code
|
||||
Run `src/PocketMine.php` using your preferred PHP binary.
|
||||
|
Submodule build/php updated: ad9cd1fdb4...9a042c5593
14
changelogs/3.23.md
Normal file
14
changelogs/3.23.md
Normal file
@ -0,0 +1,14 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.30**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.23.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.30.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
# 3.23.1
|
||||
- Fixed broken build of 3.23.0.
|
12
changelogs/3.24.md
Normal file
12
changelogs/3.24.md
Normal file
@ -0,0 +1,12 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.30**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.24.0
|
||||
- PHP 8.0 is now required as a minimum.
|
||||
- Fixed stats reporting checking the wrong `pocketmine.yml` property.
|
||||
- Fixed `Projectile->move()` not respecting the given `dx`/`dy`/`dz` and using its own motion instead.
|
11
changelogs/3.25.md
Normal file
11
changelogs/3.25.md
Normal file
@ -0,0 +1,11 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.40**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.25.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.40.
|
||||
- Removed compatibility with earlier versions.
|
@ -329,6 +329,8 @@ However, if we add `src-namespace-prefix: pmmp\TesterPlugin` to the `plugin.yml`
|
||||
- The following methods have signature changes:
|
||||
- `Entity->entityBaseTick()` is now `protected`.
|
||||
- `Entity->move()` is now `protected`.
|
||||
- `Entity->setPosition()` is now `protected` (use `Entity->teleport()` instead).
|
||||
- `Entity->setPositionAndRotation()` is now `protected` (use `Entity->teleport()` instead).
|
||||
- `Living->knockBack()` now accepts `float, float, float` (the first two parameters have been removed).
|
||||
- `Living->getEffects()` now returns `EffectManager` instead of `Effect[]`.
|
||||
- The following classes have been added:
|
||||
@ -1145,7 +1147,7 @@ However, if we add `src-namespace-prefix: pmmp\TesterPlugin` to the `plugin.yml`
|
||||
|
||||
#### Particles
|
||||
- `DestroyBlockParticle` has been renamed to `BlockBreakParticle` for consistency.
|
||||
- `DustParticle->__construct()` now accepts a `pocketmine\utils\Color` object instead of `r, g, b, a`.
|
||||
- `DustParticle->__construct()` now accepts a `pocketmine\color\Color` object instead of `r, g, b, a`.
|
||||
- `pocketmine\world\particle\Particle` no longer extends `pocketmine\math\Vector3`, and has been converted to an interface.
|
||||
- Added the following `Particle` classes:
|
||||
- `DragonEggTeleportParticle`
|
||||
@ -1332,3 +1334,129 @@ Released 10th September 2021.
|
||||
- `Liquid->getMinAdjacentSourcesToFormSource()`: returns how many adjacent source blocks of the same liquid must be present in order for the current block to become a source itself
|
||||
- `Player->getPlayerInfo()`
|
||||
- `Liquid` minimum-cost flow calculation code has been extracted to `MinimumCostFlowCalculator`.
|
||||
|
||||
# 4.0.0-BETA4
|
||||
Released 6th October 2021.
|
||||
|
||||
## General
|
||||
- Improved performance of lighting calculation by avoiding copies of useless data from the main thread.
|
||||
- Resource pack loading now accepts `dependencies` and `capabilities` fields in the `manifest.json` (although it doesn't currently check them).
|
||||
- `/help` is now localized according to language set in `server.properties`.
|
||||
- Various messages related to world loading, generation and conversion are now localized according to the language set in `server.properties`.
|
||||
- Compasses now point to the correct (current) world's spawn point after teleporting players to a different world. Previously, they would continue to point to the spawn of the world that the player initially spawned in.
|
||||
- The `--bootstrap` CLI option has been removed.
|
||||
- RakLib 0.14.2 is now required. This fixes the following issues:
|
||||
- Fixed incorrect handling of sessions on client disconnect (leading to timeout debug messages).
|
||||
- Fixed transferring players often not working correctly.
|
||||
- Fixed disconnect screens sometimes not displaying.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when UPnP encounters an error.
|
||||
- Fixed server crash when clients sent items with bad NBT in inventory transactions.
|
||||
- Fixed server crash when loading a plugin with legacy nested permission structure (now the plugin will fail to load, but the server won't crash).
|
||||
- Fixed server crash when using `/give` with bad NBT on any item.
|
||||
- Fixed server crash when loading a plugin with improperly formatted API version (now the plugin will fail to load, but the server won't crash).
|
||||
- Fixed server crash when changing player gamemode during `PlayerLoginEvent`.
|
||||
- Incorrect structure of `commands` in plugin manifest is now detected earlier and handled more gracefully.
|
||||
- Fixed console reader subprocess lingering on server crash on Windows and screwing up the terminal.
|
||||
- Fixed `Player` object memory leak when kicking players during `PlayerLoginEvent` (this caused the `World` to report warnings about leaked entities on shutdown).
|
||||
- Fixed `Projectile->move()` ignoring the `dx`/`dy`/`dz` parameters given and using its own `motion` instead.
|
||||
- Fixed `BlockFactory->get()` erroneously accepting meta values of `16`.
|
||||
- Fixed `Block->isSameState()` false negatives for some types of slabs.
|
||||
- Fixed being unable to place slabs of the same type on top of each other to create double slabs.
|
||||
|
||||
## API changes
|
||||
- Plugin commands in `plugin.yml` must now declare `permission` for each command. Previously, the `permission` key was optional, causing anyone to be able to use the command.
|
||||
- This behaviour was removed because of the potential for security issues - a typo in `plugin.yml` could lead to dangerous functionality being exposed to everyone.
|
||||
- If you want to make a command that everyone can use, declare a permission with a `default` of `true` and assign it to the command.
|
||||
- Plugin permissions in `plugin.yml` must now declare `default` for each permission. Previously, the `default` key was optional, causing the permission to silently be denied to everyone (PM4) or granted to ops implicitly (PM3).
|
||||
|
||||
### Block
|
||||
- Added the following classes:
|
||||
- `pocketmine\block\utils\LeverFacing`
|
||||
- Added the following API methods:
|
||||
- `pocketmine\block\Lever->isActivated()`
|
||||
- `pocketmine\block\Lever->setActivated()`
|
||||
- `pocketmine\block\Lever->getFacing()`
|
||||
- `pocketmine\block\Lever->setFacing()`
|
||||
|
||||
### World
|
||||
- The following API methods have signature changes:
|
||||
- `Chunk->getSubChunks()` now returns `array<int, SubChunk>` instead of `SplFixedArray<SubChunk>`.
|
||||
- The following API methods have been removed:
|
||||
- `FastChunkSerializer::serialize()`
|
||||
- `FastChunkSerializer::deserialize()`
|
||||
- The following API methods have been added:
|
||||
- `FastChunkSerializer::serializeTerrain()`: serializes blocks and biomes only
|
||||
- `FastChunkSerializer::deserializeTerrain()`: deserializes the output of `serializeTerrain()`
|
||||
|
||||
### Utils
|
||||
- The following API methods have signature changes:
|
||||
- `Process::kill()` now requires an additional `bool $subprocesses` parameter.
|
||||
|
||||
# 4.0.0-BETA5
|
||||
Released 12th October 2021.
|
||||
|
||||
## General
|
||||
- Exception log format has been changed. Now, exception info is logged in one big block message. This saves space on the console and improves readability, as well as reducing the ability for bad `ThreadedLoggerAttachment`s to break exception output.
|
||||
- Log messages are now pushed to `server.log` before calling logger attachment, instead of after. This fixes messages not being written to disk when an error occurs in a logger attachment.
|
||||
- Improved startup performance when loading many plugins.
|
||||
- The `worlds` config in `pocketmine.yml` no longer supports attaching the generator settings to the `generator` key (use the `preset` key instead).
|
||||
- Using an unknown generator in `server.properties` or `pocketmine.yml` will now cause a failure to generate the world.
|
||||
- Using invalid/incorrect world generator options (presets) in `server.properties` or `pocketmine.yml` will now cause a failure to generate the world.
|
||||
- Generator options of existing worlds are now validated before loading them. If they are invalid, the server will fail to load them.
|
||||
- Several more log messages have been localized, including plugin loading errors.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when using `/give` to give an item by ID which doesn't exist in Minecraft.
|
||||
- Fixed server crash when boolean `server.properties` options were given an integer value (e.g. `0` or `1` instead of `false` or `true`).
|
||||
- Fixed stats reporting checking for a nonexistent `pocketmine.yml` setting.
|
||||
- Fixed use of commands without the proper permission sending a message `commands.generic.permission` instead of the proper message.
|
||||
- Fixed entities set on fire appearing to stay on fire, although not taking any damage.
|
||||
- Fixed a duplicate `MB` suffix on the `Memory freed` output of `/gc`.
|
||||
- Fixed the server attempting to generate a world if it failed to load.
|
||||
|
||||
## API
|
||||
### Block
|
||||
- The following API methods have been renamed:
|
||||
- `Block->getPositionOffset()` -> `Block->getModelPositionOffset()`.
|
||||
|
||||
### Event
|
||||
- `@handleCancelled` PhpDoc annotation can no longer be used on event handlers for non-cancellable events.
|
||||
- The following API methods have been added:
|
||||
- `StructureGrowEvent->getPlayer()`
|
||||
|
||||
### Inventory
|
||||
- The following API methods have been added:
|
||||
- `Inventory->getAddableItemQuantity()`
|
||||
|
||||
### Scheduler
|
||||
- `ClosureTask` now permits closures without an explicit return type (useful for arrow functions).
|
||||
|
||||
### Utils
|
||||
- The following API methods have been added:
|
||||
- `Config::parseProperties()`
|
||||
- `Config::writeProperties()`
|
||||
- `Config::parseList()`
|
||||
- `Config::writeList()`
|
||||
|
||||
### World
|
||||
- The following API methods have signature changes:
|
||||
- `GeneratorManager->registerGenerator()` now requires a `\Closure $presetValidator` parameter. This is used to check generator options of worlds and configs before attempting to use them.
|
||||
|
||||
# 4.0.0-BETA6
|
||||
Released 19th October 2021.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.40.
|
||||
- Removed support for earlier versions.
|
||||
- CTRL+C signal handling has been restored, and is now supported on Windows. Pressing CTRL+C while the server is running will behave as if the `/stop` command was invoked.
|
||||
- Added a script `tools/generate-permission-doc.php` to generate a Markdown file with a list of all permissions and their relationships. In the future, this will be used to maintain the official documentation, but plugin developers might find it useful for their own purposes too.
|
||||
- [`respect/validation`](https://packagist.org/packages/respect/validation) is no longer a core dependency.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when using `/give` to give an item with a too-large item ID, or `/clear` to clear an item that does not exist.
|
||||
- Now, `LegacyStringToItemParser` is used exclusively, and numeric IDs are no longer parsed.
|
||||
|
||||
## Gameplay
|
||||
- Picking up some items from a dropped stack of items is now supported. This fixes various bugs with being unable to pick up items with an almost-full inventory.
|
||||
|
@ -34,7 +34,7 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-protocol": "2.0.0+bedrock1.17.30",
|
||||
"pocketmine/bedrock-protocol": "3.0.0+bedrock1.17.40",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "dev-master",
|
||||
@ -44,16 +44,15 @@
|
||||
"pocketmine/log-pthreads": "^0.2.0",
|
||||
"pocketmine/math": "^0.3.0",
|
||||
"pocketmine/nbt": "^0.3.0",
|
||||
"pocketmine/raklib": "^0.14.0",
|
||||
"pocketmine/raklib": "^0.14.2",
|
||||
"pocketmine/raklib-ipc": "^0.1.0",
|
||||
"pocketmine/snooze": "^0.3.0",
|
||||
"pocketmine/spl": "dev-master",
|
||||
"ramsey/uuid": "^4.1",
|
||||
"respect/validation": "^2.0",
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.98",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
401
composer.lock
generated
401
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bd5d7fc81a75739bfd2ef04b38220b84",
|
||||
"content-hash": "ddffa959e1045fc084bb9142884f27a3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -63,16 +63,16 @@
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.9.2",
|
||||
"version": "0.9.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brick/math.git",
|
||||
"reference": "dff976c2f3487d42c1db75a3b180e2b9f0e72ce0"
|
||||
"reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/dff976c2f3487d42c1db75a3b180e2b9f0e72ce0",
|
||||
"reference": "dff976c2f3487d42c1db75a3b180e2b9f0e72ce0",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae",
|
||||
"reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -82,7 +82,7 @@
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0",
|
||||
"vimeo/psalm": "4.3.2"
|
||||
"vimeo/psalm": "4.9.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -107,15 +107,19 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brick/math/issues",
|
||||
"source": "https://github.com/brick/math/tree/0.9.2"
|
||||
"source": "https://github.com/brick/math/tree/0.9.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/BenMorel",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/brick/math",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-20T22:51:39+00:00"
|
||||
"time": "2021-08-15T20:50:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fgrosse/phpasn1",
|
||||
@ -245,22 +249,22 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "2.0.0+bedrock1.17.30",
|
||||
"version": "3.0.0+bedrock1.17.40",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "faff7da904e68f69b1a9128956dac3122e87308a"
|
||||
"reference": "ea9e22563b3d56f3b94b4a132c725912d029c101"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/faff7da904e68f69b1a9128956dac3122e87308a",
|
||||
"reference": "faff7da904e68f69b1a9128956dac3122e87308a",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/ea9e22563b3d56f3b94b4a132c725912d029c101",
|
||||
"reference": "ea9e22563b3d56f3b94b4a132c725912d029c101",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"php": "^8.0",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/math": "^0.3.0",
|
||||
@ -286,9 +290,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/bedrock-1.17.30"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/bedrock-1.17.40"
|
||||
},
|
||||
"time": "2021-09-21T23:25:51+00:00"
|
||||
"time": "2021-10-19T17:28:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -671,16 +675,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "ed27bfd83f4de5ff32f71ec7611a66c4857a82ce"
|
||||
"reference": "e3a861187470e1facc6625040128f447ebbcbaec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/ed27bfd83f4de5ff32f71ec7611a66c4857a82ce",
|
||||
"reference": "ed27bfd83f4de5ff32f71ec7611a66c4857a82ce",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/e3a861187470e1facc6625040128f447ebbcbaec",
|
||||
"reference": "e3a861187470e1facc6625040128f447ebbcbaec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -708,9 +712,9 @@
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.14.0"
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.14.2"
|
||||
},
|
||||
"time": "2021-09-20T21:53:31+00:00"
|
||||
"time": "2021-10-04T20:39:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib-ipc",
|
||||
@ -832,20 +836,21 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/collection",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/collection.git",
|
||||
"reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1"
|
||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1",
|
||||
"reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1",
|
||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8"
|
||||
"php": "^7.3 || ^8",
|
||||
"symfony/polyfill-php81": "^1.23"
|
||||
},
|
||||
"require-dev": {
|
||||
"captainhook/captainhook": "^5.3",
|
||||
@ -855,6 +860,7 @@
|
||||
"hamcrest/hamcrest-php": "^2",
|
||||
"jangregor/phpstan-prophecy": "^0.8",
|
||||
"mockery/mockery": "^1.3",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpstan/extension-installer": "^1",
|
||||
"phpstan/phpstan": "^0.12.32",
|
||||
"phpstan/phpstan-mockery": "^0.12.5",
|
||||
@ -882,7 +888,7 @@
|
||||
"homepage": "https://benramsey.com"
|
||||
}
|
||||
],
|
||||
"description": "A PHP 7.2+ library for representing and manipulating collections.",
|
||||
"description": "A PHP library for representing and manipulating collections.",
|
||||
"keywords": [
|
||||
"array",
|
||||
"collection",
|
||||
@ -893,7 +899,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/collection/issues",
|
||||
"source": "https://github.com/ramsey/collection/tree/1.1.3"
|
||||
"source": "https://github.com/ramsey/collection/tree/1.2.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -905,28 +911,29 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-21T17:40:04+00:00"
|
||||
"time": "2021-10-10T03:01:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.2.1",
|
||||
"version": "4.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "fe665a03df4f056aa65af552a96e1976df8c8dae"
|
||||
"reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fe665a03df4f056aa65af552a96e1976df8c8dae",
|
||||
"reference": "fe665a03df4f056aa65af552a96e1976df8c8dae",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df",
|
||||
"reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8 || ^0.9",
|
||||
"ext-json": "*",
|
||||
"php": "^7.2 || ^8",
|
||||
"php": "^7.2 || ^8.0",
|
||||
"ramsey/collection": "^1.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-php80": "^1.14"
|
||||
},
|
||||
"replace": {
|
||||
"rhumsaa/uuid": "self.version"
|
||||
@ -990,7 +997,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.2.1"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.2.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1002,131 +1009,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-11T01:06:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "respect/stringifier",
|
||||
"version": "0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Respect/Stringifier.git",
|
||||
"reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
|
||||
"reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.8",
|
||||
"malukenho/docheader": "^0.1.7",
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Respect\\Stringifier\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/stringify.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Respect/Stringifier Contributors",
|
||||
"homepage": "https://github.com/Respect/Stringifier/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Converts any value to a string",
|
||||
"homepage": "http://respect.github.io/Stringifier/",
|
||||
"keywords": [
|
||||
"respect",
|
||||
"stringifier",
|
||||
"stringify"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Respect/Stringifier/issues",
|
||||
"source": "https://github.com/Respect/Stringifier/tree/0.2.0"
|
||||
},
|
||||
"time": "2017-12-29T19:39:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "respect/validation",
|
||||
"version": "2.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Respect/Validation.git",
|
||||
"reference": "4c21a7ffc9a4915673cb2c2843963919e664e627"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Respect/Validation/zipball/4c21a7ffc9a4915673cb2c2843963919e664e627",
|
||||
"reference": "4c21a7ffc9a4915673cb2c2843963919e664e627",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"respect/stringifier": "^0.2.0",
|
||||
"symfony/polyfill-mbstring": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"egulias/email-validator": "^3.0",
|
||||
"malukenho/docheader": "^0.1",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12",
|
||||
"phpstan/phpstan-phpunit": "^0.12",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"psr/http-message": "^1.0",
|
||||
"respect/coding-standard": "^3.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/validator": "^3.0||^4.0",
|
||||
"zendframework/zend-validator": "^2.1"
|
||||
},
|
||||
"suggest": {
|
||||
"egulias/email-validator": "Strict (RFC compliant) email validation",
|
||||
"ext-bcmath": "Arbitrary Precision Mathematics",
|
||||
"ext-fileinfo": "File Information",
|
||||
"ext-mbstring": "Multibyte String Functions",
|
||||
"symfony/validator": "Use Symfony validator through Respect\\Validation",
|
||||
"zendframework/zend-validator": "Use Zend Framework validator through Respect\\Validation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Respect\\Validation\\": "library/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Respect/Validation Contributors",
|
||||
"homepage": "https://github.com/Respect/Validation/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "The most awesome validation engine ever created for PHP",
|
||||
"homepage": "http://respect.github.io/Validation/",
|
||||
"keywords": [
|
||||
"respect",
|
||||
"validation",
|
||||
"validator"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Respect/Validation/issues",
|
||||
"source": "https://github.com/Respect/Validation/tree/2.2.3"
|
||||
},
|
||||
"time": "2021-03-19T14:12:45+00:00"
|
||||
"time": "2021-09-25T23:10:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@ -1208,25 +1091,22 @@
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.23.0",
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.23.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1"
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
|
||||
"reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
|
||||
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@ -1239,10 +1119,96 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ion Bazan",
|
||||
"email": "ion.bazan@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-28T13:41:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "e66119f3de95efc359483f810c4c3e6436279436"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436",
|
||||
"reference": "e66119f3de95efc359483f810c4c3e6436279436",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php81\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@ -1259,17 +1225,16 @@
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.0"
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1285,7 +1250,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-27T09:27:20+00:00"
|
||||
"time": "2021-05-21T13:25:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
@ -1526,16 +1491,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.12.0",
|
||||
"version": "v4.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "6608f01670c3cc5079e18c1dab1104e002579143"
|
||||
"reference": "50953a2691a922aa1769461637869a0a2faa3f53"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143",
|
||||
"reference": "6608f01670c3cc5079e18c1dab1104e002579143",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53",
|
||||
"reference": "50953a2691a922aa1769461637869a0a2faa3f53",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1576,9 +1541,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0"
|
||||
},
|
||||
"time": "2021-07-21T10:44:31+00:00"
|
||||
"time": "2021-09-20T12:20:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1802,16 +1767,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
|
||||
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
|
||||
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae",
|
||||
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1819,7 +1784,8 @@
|
||||
"phpdocumentor/reflection-common": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*"
|
||||
"ext-tokenizer": "*",
|
||||
"psalm/phar": "^4.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -1845,39 +1811,39 @@
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1"
|
||||
},
|
||||
"time": "2020-09-17T18:55:26+00:00"
|
||||
"time": "2021-10-02T14:08:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.13.0",
|
||||
"version": "1.14.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
|
||||
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
|
||||
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
|
||||
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.2",
|
||||
"php": "^7.2 || ~8.0, <8.1",
|
||||
"php": "^7.2 || ~8.0, <8.2",
|
||||
"phpdocumentor/reflection-docblock": "^5.2",
|
||||
"sebastian/comparator": "^3.0 || ^4.0",
|
||||
"sebastian/recursion-context": "^3.0 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^6.0",
|
||||
"phpspec/phpspec": "^6.0 || ^7.0",
|
||||
"phpunit/phpunit": "^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11.x-dev"
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1912,22 +1878,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpspec/prophecy/issues",
|
||||
"source": "https://github.com/phpspec/prophecy/tree/1.13.0"
|
||||
"source": "https://github.com/phpspec/prophecy/tree/1.14.0"
|
||||
},
|
||||
"time": "2021-03-17T13:42:18+00:00"
|
||||
"time": "2021-09-10T09:02:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "0.12.98",
|
||||
"version": "0.12.99",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "3bb7cc246c057405dd5e290c3ecc62ab51d57e00"
|
||||
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/3bb7cc246c057405dd5e290c3ecc62ab51d57e00",
|
||||
"reference": "3bb7cc246c057405dd5e290c3ecc62ab51d57e00",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7",
|
||||
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1958,7 +1924,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.98"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.99"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1978,7 +1944,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-02T12:33:01+00:00"
|
||||
"time": "2021-09-12T20:09:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
@ -2086,23 +2052,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.6",
|
||||
"version": "9.2.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "f6293e1b30a2354e8428e004689671b83871edde"
|
||||
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
|
||||
"reference": "f6293e1b30a2354e8428e004689671b83871edde",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
|
||||
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.10.2",
|
||||
"nikic/php-parser": "^4.12.0",
|
||||
"php": ">=7.3",
|
||||
"phpunit/php-file-iterator": "^3.0.3",
|
||||
"phpunit/php-text-template": "^2.0.2",
|
||||
@ -2151,7 +2117,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2159,7 +2125,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-28T07:26:59+00:00"
|
||||
"time": "2021-09-17T05:39:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -2404,16 +2370,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.9",
|
||||
"version": "9.5.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "ea8c2dfb1065eb35a79b3681eee6e6fb0a6f273b"
|
||||
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea8c2dfb1065eb35a79b3681eee6e6fb0a6f273b",
|
||||
"reference": "ea8c2dfb1065eb35a79b3681eee6e6fb0a6f273b",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
|
||||
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2429,7 +2395,7 @@
|
||||
"phar-io/version": "^3.0.2",
|
||||
"php": ">=7.3",
|
||||
"phpspec/prophecy": "^1.12.1",
|
||||
"phpunit/php-code-coverage": "^9.2.3",
|
||||
"phpunit/php-code-coverage": "^9.2.7",
|
||||
"phpunit/php-file-iterator": "^3.0.5",
|
||||
"phpunit/php-invoker": "^3.1.1",
|
||||
"phpunit/php-text-template": "^2.0.3",
|
||||
@ -2491,7 +2457,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.9"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2503,7 +2469,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-31T06:47:40+00:00"
|
||||
"time": "2021-09-25T07:38:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -3358,7 +3324,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2020-09-28T06:45:17+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -38,6 +38,9 @@ parameters:
|
||||
- tests/phpunit
|
||||
- tests/plugins/TesterPlugin
|
||||
- tools
|
||||
excludePaths:
|
||||
analyseAndScan:
|
||||
- build/php
|
||||
dynamicConstantNames:
|
||||
- pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD
|
||||
- pocketmine\DEBUG
|
||||
@ -54,5 +57,5 @@ parameters:
|
||||
#variadics don't work for this - mixed probably shouldn't work either, but for now it does
|
||||
#what we actually need is something that accepts an infinite number of parameters, but in the absence of that,
|
||||
#we'll just fill it with 10 - it's very unlikely to encounter a callable with 10 parameters anyway.
|
||||
anyCallable: 'callable(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed) : mixed'
|
||||
anyClosure: '\Closure(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed) : mixed'
|
||||
anyCallable: 'callable(never, never, never, never, never, never, never, never, never, never) : mixed'
|
||||
anyClosure: '\Closure(never, never, never, never, never, never, never, never, never, never) : mixed'
|
||||
|
@ -1,4 +1,691 @@
|
||||
{
|
||||
"-2": -2,
|
||||
"-3": -3,
|
||||
"-4": -4,
|
||||
"-5": -5,
|
||||
"-6": -6,
|
||||
"-7": -7,
|
||||
"-8": -8,
|
||||
"-9": -9,
|
||||
"-10": -10,
|
||||
"-11": -11,
|
||||
"-12": -12,
|
||||
"-13": -13,
|
||||
"-14": -14,
|
||||
"-15": -15,
|
||||
"-16": -16,
|
||||
"-17": -17,
|
||||
"-18": -18,
|
||||
"-19": -19,
|
||||
"-20": -20,
|
||||
"-21": -21,
|
||||
"-22": -22,
|
||||
"-23": -23,
|
||||
"-24": -24,
|
||||
"-25": -25,
|
||||
"-26": -26,
|
||||
"-27": -27,
|
||||
"-28": -28,
|
||||
"-29": -29,
|
||||
"-30": -30,
|
||||
"-31": -31,
|
||||
"-32": -32,
|
||||
"-33": -33,
|
||||
"-34": -34,
|
||||
"-35": -35,
|
||||
"-36": -36,
|
||||
"-37": -37,
|
||||
"-38": -38,
|
||||
"-39": -39,
|
||||
"-40": -40,
|
||||
"-41": -41,
|
||||
"-42": -42,
|
||||
"-43": -43,
|
||||
"-44": -44,
|
||||
"-45": -45,
|
||||
"-46": -46,
|
||||
"-47": -47,
|
||||
"-48": -48,
|
||||
"-49": -49,
|
||||
"-50": -50,
|
||||
"-51": -51,
|
||||
"-52": -52,
|
||||
"-53": -53,
|
||||
"-54": -54,
|
||||
"-55": -55,
|
||||
"-56": -56,
|
||||
"-57": -57,
|
||||
"-58": -58,
|
||||
"-59": -59,
|
||||
"-60": -60,
|
||||
"-61": -61,
|
||||
"-62": -62,
|
||||
"-63": -63,
|
||||
"-64": -64,
|
||||
"-65": -65,
|
||||
"-66": -66,
|
||||
"-67": -67,
|
||||
"-68": -68,
|
||||
"-69": -69,
|
||||
"-70": -70,
|
||||
"-71": -71,
|
||||
"-72": -72,
|
||||
"-73": -73,
|
||||
"-74": -74,
|
||||
"-75": -75,
|
||||
"-76": -76,
|
||||
"-77": -77,
|
||||
"-78": -78,
|
||||
"-79": -79,
|
||||
"-80": -80,
|
||||
"-81": -81,
|
||||
"-82": -82,
|
||||
"-83": -83,
|
||||
"-84": -84,
|
||||
"-85": -85,
|
||||
"-86": -86,
|
||||
"-87": -87,
|
||||
"-88": -88,
|
||||
"-89": -89,
|
||||
"-90": -90,
|
||||
"-91": -91,
|
||||
"-92": -92,
|
||||
"-93": -93,
|
||||
"-94": -94,
|
||||
"-95": -95,
|
||||
"-96": -96,
|
||||
"-97": -97,
|
||||
"-98": -98,
|
||||
"-99": -99,
|
||||
"-100": -100,
|
||||
"-101": -101,
|
||||
"-102": -102,
|
||||
"-103": -103,
|
||||
"-104": -104,
|
||||
"-105": -105,
|
||||
"-106": -106,
|
||||
"-107": -107,
|
||||
"-108": -108,
|
||||
"-109": -109,
|
||||
"-110": -110,
|
||||
"-111": -111,
|
||||
"-112": -112,
|
||||
"-113": -113,
|
||||
"-114": -114,
|
||||
"-115": -115,
|
||||
"-116": -116,
|
||||
"-117": -117,
|
||||
"-118": -118,
|
||||
"-119": -119,
|
||||
"-120": -120,
|
||||
"-121": -121,
|
||||
"-122": -122,
|
||||
"-123": -123,
|
||||
"-124": -124,
|
||||
"-125": -125,
|
||||
"-126": -126,
|
||||
"-127": -127,
|
||||
"-128": -128,
|
||||
"-129": -129,
|
||||
"-130": -130,
|
||||
"-131": -131,
|
||||
"-132": -132,
|
||||
"-133": -133,
|
||||
"-134": -134,
|
||||
"-135": -135,
|
||||
"-136": -136,
|
||||
"-137": -137,
|
||||
"-138": -138,
|
||||
"-139": -139,
|
||||
"-140": -140,
|
||||
"-141": -141,
|
||||
"-142": -142,
|
||||
"-143": -143,
|
||||
"-144": -144,
|
||||
"-145": -145,
|
||||
"-146": -146,
|
||||
"-147": -147,
|
||||
"-148": -148,
|
||||
"-149": -149,
|
||||
"-150": -150,
|
||||
"-151": -151,
|
||||
"-152": -152,
|
||||
"-153": -153,
|
||||
"-154": -154,
|
||||
"-155": -155,
|
||||
"-156": -156,
|
||||
"-157": -157,
|
||||
"-159": -159,
|
||||
"-160": -160,
|
||||
"-161": -161,
|
||||
"-162": -162,
|
||||
"-163": -163,
|
||||
"-164": -164,
|
||||
"-165": -165,
|
||||
"-166": -166,
|
||||
"-167": -167,
|
||||
"-168": -168,
|
||||
"-169": -169,
|
||||
"-170": -170,
|
||||
"-171": -171,
|
||||
"-172": -172,
|
||||
"-173": -173,
|
||||
"-174": -174,
|
||||
"-175": -175,
|
||||
"-176": -176,
|
||||
"-177": -177,
|
||||
"-178": -178,
|
||||
"-179": -179,
|
||||
"-180": -180,
|
||||
"-181": -181,
|
||||
"-182": -182,
|
||||
"-183": -183,
|
||||
"-184": -184,
|
||||
"-185": -185,
|
||||
"-186": -186,
|
||||
"-187": -187,
|
||||
"-188": -188,
|
||||
"-189": -189,
|
||||
"-190": -190,
|
||||
"-191": -191,
|
||||
"-192": -192,
|
||||
"-193": -193,
|
||||
"-194": -194,
|
||||
"-195": -195,
|
||||
"-196": -196,
|
||||
"-197": -197,
|
||||
"-198": -198,
|
||||
"-199": -199,
|
||||
"-200": -200,
|
||||
"-201": -201,
|
||||
"-202": -202,
|
||||
"-203": -203,
|
||||
"-204": -204,
|
||||
"-206": -206,
|
||||
"-207": -207,
|
||||
"-208": -208,
|
||||
"-209": -209,
|
||||
"-210": -210,
|
||||
"-211": -211,
|
||||
"-213": -213,
|
||||
"-214": -214,
|
||||
"0": 0,
|
||||
"1": 1,
|
||||
"2": 2,
|
||||
"3": 3,
|
||||
"4": 4,
|
||||
"5": 5,
|
||||
"6": 6,
|
||||
"7": 7,
|
||||
"8": 8,
|
||||
"9": 9,
|
||||
"10": 10,
|
||||
"11": 11,
|
||||
"12": 12,
|
||||
"13": 13,
|
||||
"14": 14,
|
||||
"15": 15,
|
||||
"16": 16,
|
||||
"17": 17,
|
||||
"18": 18,
|
||||
"19": 19,
|
||||
"20": 20,
|
||||
"21": 21,
|
||||
"22": 22,
|
||||
"23": 23,
|
||||
"24": 24,
|
||||
"25": 25,
|
||||
"26": 26,
|
||||
"27": 27,
|
||||
"28": 28,
|
||||
"29": 29,
|
||||
"30": 30,
|
||||
"31": 31,
|
||||
"32": 32,
|
||||
"33": 33,
|
||||
"34": 34,
|
||||
"35": 35,
|
||||
"36": 36,
|
||||
"37": 37,
|
||||
"38": 38,
|
||||
"39": 39,
|
||||
"40": 40,
|
||||
"41": 41,
|
||||
"42": 42,
|
||||
"43": 43,
|
||||
"44": 44,
|
||||
"45": 45,
|
||||
"46": 46,
|
||||
"47": 47,
|
||||
"48": 48,
|
||||
"49": 49,
|
||||
"50": 50,
|
||||
"51": 51,
|
||||
"52": 52,
|
||||
"53": 53,
|
||||
"54": 54,
|
||||
"55": 55,
|
||||
"56": 56,
|
||||
"57": 57,
|
||||
"58": 58,
|
||||
"59": 59,
|
||||
"60": 60,
|
||||
"61": 61,
|
||||
"62": 62,
|
||||
"63": 63,
|
||||
"64": 64,
|
||||
"65": 65,
|
||||
"66": 66,
|
||||
"67": 67,
|
||||
"68": 68,
|
||||
"69": 69,
|
||||
"70": 70,
|
||||
"71": 71,
|
||||
"72": 72,
|
||||
"73": 73,
|
||||
"74": 74,
|
||||
"75": 75,
|
||||
"76": 76,
|
||||
"77": 77,
|
||||
"78": 78,
|
||||
"79": 79,
|
||||
"80": 80,
|
||||
"81": 81,
|
||||
"82": 82,
|
||||
"83": 83,
|
||||
"84": 84,
|
||||
"85": 85,
|
||||
"86": 86,
|
||||
"87": 87,
|
||||
"88": 88,
|
||||
"89": 89,
|
||||
"90": 90,
|
||||
"91": 91,
|
||||
"92": 92,
|
||||
"93": 93,
|
||||
"94": 94,
|
||||
"95": 95,
|
||||
"96": 96,
|
||||
"97": 97,
|
||||
"98": 98,
|
||||
"99": 99,
|
||||
"100": 100,
|
||||
"101": 101,
|
||||
"102": 102,
|
||||
"103": 103,
|
||||
"104": 104,
|
||||
"105": 105,
|
||||
"106": 106,
|
||||
"107": 107,
|
||||
"108": 108,
|
||||
"109": 109,
|
||||
"110": 110,
|
||||
"111": 111,
|
||||
"112": 112,
|
||||
"113": 113,
|
||||
"114": 114,
|
||||
"115": 115,
|
||||
"116": 116,
|
||||
"117": 117,
|
||||
"118": 118,
|
||||
"119": 119,
|
||||
"120": 120,
|
||||
"121": 121,
|
||||
"122": 122,
|
||||
"123": 123,
|
||||
"124": 124,
|
||||
"125": 125,
|
||||
"126": 126,
|
||||
"127": 127,
|
||||
"128": 128,
|
||||
"129": 129,
|
||||
"130": 130,
|
||||
"131": 131,
|
||||
"132": 132,
|
||||
"133": 133,
|
||||
"134": 134,
|
||||
"135": 135,
|
||||
"136": 136,
|
||||
"137": 137,
|
||||
"138": 138,
|
||||
"139": 139,
|
||||
"140": 140,
|
||||
"141": 141,
|
||||
"142": 142,
|
||||
"143": 143,
|
||||
"144": 144,
|
||||
"145": 145,
|
||||
"146": 146,
|
||||
"147": 147,
|
||||
"148": 148,
|
||||
"149": 149,
|
||||
"150": 150,
|
||||
"151": 151,
|
||||
"152": 152,
|
||||
"153": 153,
|
||||
"154": 154,
|
||||
"155": 155,
|
||||
"156": 156,
|
||||
"157": 157,
|
||||
"158": 158,
|
||||
"159": 159,
|
||||
"160": 160,
|
||||
"161": 161,
|
||||
"162": 162,
|
||||
"163": 163,
|
||||
"164": 164,
|
||||
"165": 165,
|
||||
"166": 166,
|
||||
"167": 167,
|
||||
"168": 168,
|
||||
"169": 169,
|
||||
"170": 170,
|
||||
"171": 171,
|
||||
"172": 172,
|
||||
"173": 173,
|
||||
"174": 174,
|
||||
"175": 175,
|
||||
"176": 176,
|
||||
"177": 177,
|
||||
"178": 178,
|
||||
"179": 179,
|
||||
"180": 180,
|
||||
"181": 181,
|
||||
"182": 182,
|
||||
"183": 183,
|
||||
"184": 184,
|
||||
"185": 185,
|
||||
"186": 186,
|
||||
"187": 187,
|
||||
"188": 188,
|
||||
"189": 189,
|
||||
"190": 190,
|
||||
"191": 191,
|
||||
"192": 192,
|
||||
"193": 193,
|
||||
"194": 194,
|
||||
"195": 195,
|
||||
"196": 196,
|
||||
"197": 197,
|
||||
"198": 198,
|
||||
"199": 199,
|
||||
"200": 200,
|
||||
"201": 201,
|
||||
"202": 202,
|
||||
"203": 203,
|
||||
"204": 204,
|
||||
"205": 205,
|
||||
"206": 206,
|
||||
"207": 207,
|
||||
"208": 208,
|
||||
"209": 209,
|
||||
"213": 213,
|
||||
"214": 214,
|
||||
"215": 215,
|
||||
"216": 216,
|
||||
"218": 218,
|
||||
"219": 219,
|
||||
"220": 220,
|
||||
"221": 221,
|
||||
"222": 222,
|
||||
"223": 223,
|
||||
"224": 224,
|
||||
"225": 225,
|
||||
"226": 226,
|
||||
"227": 227,
|
||||
"228": 228,
|
||||
"229": 229,
|
||||
"231": 231,
|
||||
"232": 232,
|
||||
"233": 233,
|
||||
"234": 234,
|
||||
"235": 235,
|
||||
"236": 236,
|
||||
"237": 237,
|
||||
"238": 238,
|
||||
"239": 239,
|
||||
"240": 240,
|
||||
"241": 241,
|
||||
"243": 243,
|
||||
"244": 244,
|
||||
"245": 245,
|
||||
"246": 246,
|
||||
"247": 247,
|
||||
"248": 248,
|
||||
"249": 249,
|
||||
"250": 250,
|
||||
"251": 251,
|
||||
"252": 252,
|
||||
"253": 253,
|
||||
"254": 254,
|
||||
"255": 255,
|
||||
"256": 256,
|
||||
"257": 257,
|
||||
"258": 258,
|
||||
"259": 259,
|
||||
"260": 260,
|
||||
"261": 261,
|
||||
"262": 262,
|
||||
"263": 263,
|
||||
"264": 264,
|
||||
"265": 265,
|
||||
"266": 266,
|
||||
"267": 267,
|
||||
"268": 268,
|
||||
"269": 269,
|
||||
"270": 270,
|
||||
"271": 271,
|
||||
"272": 272,
|
||||
"273": 273,
|
||||
"274": 274,
|
||||
"275": 275,
|
||||
"276": 276,
|
||||
"277": 277,
|
||||
"278": 278,
|
||||
"279": 279,
|
||||
"280": 280,
|
||||
"281": 281,
|
||||
"282": 282,
|
||||
"283": 283,
|
||||
"284": 284,
|
||||
"285": 285,
|
||||
"286": 286,
|
||||
"287": 287,
|
||||
"288": 288,
|
||||
"289": 289,
|
||||
"290": 290,
|
||||
"291": 291,
|
||||
"292": 292,
|
||||
"293": 293,
|
||||
"294": 294,
|
||||
"295": 295,
|
||||
"296": 296,
|
||||
"297": 297,
|
||||
"298": 298,
|
||||
"299": 299,
|
||||
"300": 300,
|
||||
"301": 301,
|
||||
"302": 302,
|
||||
"303": 303,
|
||||
"304": 304,
|
||||
"305": 305,
|
||||
"306": 306,
|
||||
"307": 307,
|
||||
"308": 308,
|
||||
"309": 309,
|
||||
"310": 310,
|
||||
"311": 311,
|
||||
"312": 312,
|
||||
"313": 313,
|
||||
"314": 314,
|
||||
"315": 315,
|
||||
"316": 316,
|
||||
"317": 317,
|
||||
"318": 318,
|
||||
"319": 319,
|
||||
"320": 320,
|
||||
"321": 321,
|
||||
"322": 322,
|
||||
"323": 323,
|
||||
"324": 324,
|
||||
"325": 325,
|
||||
"328": 328,
|
||||
"329": 329,
|
||||
"330": 330,
|
||||
"331": 331,
|
||||
"332": 332,
|
||||
"333": 333,
|
||||
"334": 334,
|
||||
"335": 335,
|
||||
"336": 336,
|
||||
"337": 337,
|
||||
"338": 338,
|
||||
"339": 339,
|
||||
"340": 340,
|
||||
"341": 341,
|
||||
"342": 342,
|
||||
"344": 344,
|
||||
"345": 345,
|
||||
"346": 346,
|
||||
"347": 347,
|
||||
"348": 348,
|
||||
"349": 349,
|
||||
"350": 350,
|
||||
"351": 351,
|
||||
"352": 352,
|
||||
"353": 353,
|
||||
"354": 354,
|
||||
"355": 355,
|
||||
"356": 356,
|
||||
"357": 357,
|
||||
"358": 358,
|
||||
"359": 359,
|
||||
"360": 360,
|
||||
"361": 361,
|
||||
"362": 362,
|
||||
"363": 363,
|
||||
"364": 364,
|
||||
"365": 365,
|
||||
"366": 366,
|
||||
"367": 367,
|
||||
"368": 368,
|
||||
"369": 369,
|
||||
"370": 370,
|
||||
"371": 371,
|
||||
"372": 372,
|
||||
"373": 373,
|
||||
"374": 374,
|
||||
"375": 375,
|
||||
"376": 376,
|
||||
"377": 377,
|
||||
"378": 378,
|
||||
"379": 379,
|
||||
"380": 380,
|
||||
"381": 381,
|
||||
"382": 382,
|
||||
"383": 383,
|
||||
"384": 384,
|
||||
"385": 385,
|
||||
"386": 386,
|
||||
"387": 387,
|
||||
"388": 388,
|
||||
"389": 389,
|
||||
"390": 390,
|
||||
"391": 391,
|
||||
"392": 392,
|
||||
"393": 393,
|
||||
"394": 394,
|
||||
"395": 395,
|
||||
"396": 396,
|
||||
"397": 397,
|
||||
"398": 398,
|
||||
"399": 399,
|
||||
"400": 400,
|
||||
"401": 401,
|
||||
"402": 402,
|
||||
"403": 403,
|
||||
"404": 404,
|
||||
"405": 405,
|
||||
"406": 406,
|
||||
"407": 407,
|
||||
"408": 408,
|
||||
"409": 409,
|
||||
"410": 410,
|
||||
"411": 411,
|
||||
"412": 412,
|
||||
"413": 413,
|
||||
"414": 414,
|
||||
"415": 415,
|
||||
"416": 416,
|
||||
"417": 417,
|
||||
"418": 418,
|
||||
"419": 419,
|
||||
"420": 420,
|
||||
"421": 421,
|
||||
"422": 422,
|
||||
"423": 423,
|
||||
"424": 424,
|
||||
"425": 425,
|
||||
"426": 426,
|
||||
"427": 427,
|
||||
"428": 428,
|
||||
"429": 429,
|
||||
"430": 430,
|
||||
"431": 431,
|
||||
"432": 432,
|
||||
"433": 433,
|
||||
"434": 434,
|
||||
"437": 437,
|
||||
"438": 438,
|
||||
"441": 441,
|
||||
"442": 442,
|
||||
"443": 443,
|
||||
"444": 444,
|
||||
"445": 445,
|
||||
"446": 446,
|
||||
"447": 447,
|
||||
"448": 448,
|
||||
"449": 449,
|
||||
"450": 450,
|
||||
"451": 451,
|
||||
"452": 452,
|
||||
"453": 453,
|
||||
"455": 455,
|
||||
"457": 457,
|
||||
"458": 458,
|
||||
"459": 459,
|
||||
"460": 460,
|
||||
"461": 461,
|
||||
"462": 462,
|
||||
"463": 463,
|
||||
"464": 464,
|
||||
"465": 465,
|
||||
"466": 466,
|
||||
"467": 467,
|
||||
"468": 468,
|
||||
"469": 469,
|
||||
"470": 470,
|
||||
"471": 471,
|
||||
"472": 472,
|
||||
"473": 473,
|
||||
"474": 474,
|
||||
"475": 475,
|
||||
"476": 476,
|
||||
"477": 477,
|
||||
"499": 499,
|
||||
"500": 500,
|
||||
"501": 501,
|
||||
"502": 502,
|
||||
"503": 503,
|
||||
"504": 504,
|
||||
"505": 505,
|
||||
"506": 506,
|
||||
"507": 507,
|
||||
"508": 508,
|
||||
"509": 509,
|
||||
"510": 510,
|
||||
"511": 511,
|
||||
"513": 513,
|
||||
"acacia_button": -140,
|
||||
"acacia_door": 430,
|
||||
"acacia_door_block": 196,
|
||||
@ -226,27 +913,16 @@
|
||||
"egg": 344,
|
||||
"element_0": 36,
|
||||
"element_1": -12,
|
||||
"element_2": -13,
|
||||
"element_3": -14,
|
||||
"element_4": -15,
|
||||
"element_5": -16,
|
||||
"element_6": -17,
|
||||
"element_7": -18,
|
||||
"element_8": -19,
|
||||
"element_9": -20,
|
||||
"element_10": -21,
|
||||
"element_100": -111,
|
||||
"element_101": -112,
|
||||
"element_102": -113,
|
||||
"element_103": -114,
|
||||
"element_104": -115,
|
||||
"element_105": -116,
|
||||
"element_106": -117,
|
||||
"element_107": -118,
|
||||
"element_108": -119,
|
||||
"element_109": -120,
|
||||
"element_11": -22,
|
||||
"element_110": -121,
|
||||
"element_111": -122,
|
||||
"element_112": -123,
|
||||
"element_113": -124,
|
||||
"element_114": -125,
|
||||
"element_115": -126,
|
||||
"element_116": -127,
|
||||
"element_117": -128,
|
||||
"element_118": -129,
|
||||
"element_12": -23,
|
||||
"element_13": -24,
|
||||
"element_14": -25,
|
||||
@ -255,7 +931,6 @@
|
||||
"element_17": -28,
|
||||
"element_18": -29,
|
||||
"element_19": -30,
|
||||
"element_2": -13,
|
||||
"element_20": -31,
|
||||
"element_21": -32,
|
||||
"element_22": -33,
|
||||
@ -266,7 +941,6 @@
|
||||
"element_27": -38,
|
||||
"element_28": -39,
|
||||
"element_29": -40,
|
||||
"element_3": -14,
|
||||
"element_30": -41,
|
||||
"element_31": -42,
|
||||
"element_32": -43,
|
||||
@ -277,7 +951,6 @@
|
||||
"element_37": -48,
|
||||
"element_38": -49,
|
||||
"element_39": -50,
|
||||
"element_4": -15,
|
||||
"element_40": -51,
|
||||
"element_41": -52,
|
||||
"element_42": -53,
|
||||
@ -288,7 +961,6 @@
|
||||
"element_47": -58,
|
||||
"element_48": -59,
|
||||
"element_49": -60,
|
||||
"element_5": -16,
|
||||
"element_50": -61,
|
||||
"element_51": -62,
|
||||
"element_52": -63,
|
||||
@ -299,7 +971,6 @@
|
||||
"element_57": -68,
|
||||
"element_58": -69,
|
||||
"element_59": -70,
|
||||
"element_6": -17,
|
||||
"element_60": -71,
|
||||
"element_61": -72,
|
||||
"element_62": -73,
|
||||
@ -310,7 +981,6 @@
|
||||
"element_67": -78,
|
||||
"element_68": -79,
|
||||
"element_69": -80,
|
||||
"element_7": -18,
|
||||
"element_70": -81,
|
||||
"element_71": -82,
|
||||
"element_72": -83,
|
||||
@ -321,7 +991,6 @@
|
||||
"element_77": -88,
|
||||
"element_78": -89,
|
||||
"element_79": -90,
|
||||
"element_8": -19,
|
||||
"element_80": -91,
|
||||
"element_81": -92,
|
||||
"element_82": -93,
|
||||
@ -332,7 +1001,6 @@
|
||||
"element_87": -98,
|
||||
"element_88": -99,
|
||||
"element_89": -100,
|
||||
"element_9": -20,
|
||||
"element_90": -101,
|
||||
"element_91": -102,
|
||||
"element_92": -103,
|
||||
@ -343,6 +1011,25 @@
|
||||
"element_97": -108,
|
||||
"element_98": -109,
|
||||
"element_99": -110,
|
||||
"element_100": -111,
|
||||
"element_101": -112,
|
||||
"element_102": -113,
|
||||
"element_103": -114,
|
||||
"element_104": -115,
|
||||
"element_105": -116,
|
||||
"element_106": -117,
|
||||
"element_107": -118,
|
||||
"element_108": -119,
|
||||
"element_109": -120,
|
||||
"element_110": -121,
|
||||
"element_111": -122,
|
||||
"element_112": -123,
|
||||
"element_113": -124,
|
||||
"element_114": -125,
|
||||
"element_115": -126,
|
||||
"element_116": -127,
|
||||
"element_117": -128,
|
||||
"element_118": -129,
|
||||
"elytra": 444,
|
||||
"emerald": 388,
|
||||
"emerald_block": 133,
|
||||
@ -532,8 +1219,8 @@
|
||||
"leather_pants": 300,
|
||||
"leather_tunic": 299,
|
||||
"leave": 18,
|
||||
"leaves": 18,
|
||||
"leave2": 161,
|
||||
"leaves": 18,
|
||||
"leaves2": 161,
|
||||
"lectern": -194,
|
||||
"lever": 69,
|
||||
|
Submodule resources/locale updated: 4a322da43e...17fc6a1050
Submodule resources/vanilla updated: 19569dd729...f29b7be8fa
@ -35,3 +35,4 @@ define('pocketmine\_CORE_CONSTANTS_INCLUDED', true);
|
||||
|
||||
define('pocketmine\PATH', dirname(__DIR__) . '/');
|
||||
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');
|
||||
|
@ -214,20 +214,13 @@ JIT_WARNING
|
||||
error_reporting(-1);
|
||||
set_ini_entries();
|
||||
|
||||
$opts = getopt("", ["bootstrap:"]);
|
||||
if(isset($opts["bootstrap"])){
|
||||
$bootstrap = ($real = realpath($opts["bootstrap"])) !== false ? $real : $opts["bootstrap"];
|
||||
}else{
|
||||
$bootstrap = dirname(__FILE__, 2) . '/vendor/autoload.php';
|
||||
}
|
||||
|
||||
if($bootstrap === false or !is_file($bootstrap)){
|
||||
$bootstrap = dirname(__FILE__, 2) . '/vendor/autoload.php';
|
||||
if(!is_file($bootstrap)){
|
||||
critical_error("Composer autoloader not found at " . $bootstrap);
|
||||
critical_error("Please install/update Composer dependencies or use provided builds.");
|
||||
exit(1);
|
||||
}
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
require_once($bootstrap);
|
||||
|
||||
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
|
||||
if($composerGitHash !== null){
|
||||
@ -307,7 +300,7 @@ JIT_WARNING
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
$logger->debug("Some threads could not be stopped, performing a force-kill");
|
||||
Process::kill(Process::pid());
|
||||
Process::kill(Process::pid(), true);
|
||||
}
|
||||
}while(false);
|
||||
|
||||
|
257
src/Server.php
257
src/Server.php
@ -40,6 +40,7 @@ use pocketmine\entity\Location;
|
||||
use pocketmine\event\HandlerListManager;
|
||||
use pocketmine\event\player\PlayerCreationEvent;
|
||||
use pocketmine\event\player\PlayerDataSaveEvent;
|
||||
use pocketmine\event\player\PlayerLoginEvent;
|
||||
use pocketmine\event\server\CommandEvent;
|
||||
use pocketmine\event\server\DataPacketSendEvent;
|
||||
use pocketmine\event\server\QueryRegenerateEvent;
|
||||
@ -97,6 +98,7 @@ use pocketmine\utils\NotCloneable;
|
||||
use pocketmine\utils\NotSerializable;
|
||||
use pocketmine\utils\Process;
|
||||
use pocketmine\utils\Promise;
|
||||
use pocketmine\utils\SignalHandler;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
@ -105,24 +107,22 @@ use pocketmine\world\format\io\WorldProviderManager;
|
||||
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
||||
use pocketmine\world\generator\Generator;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
||||
use pocketmine\world\World;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use pocketmine\world\WorldManager;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function array_shift;
|
||||
use function array_sum;
|
||||
use function base64_encode;
|
||||
use function cli_set_process_title;
|
||||
use function copy;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
@ -251,6 +251,8 @@ class Server{
|
||||
/** @var Player[] */
|
||||
private array $playerList = [];
|
||||
|
||||
private SignalHandler $signalHandler;
|
||||
|
||||
/**
|
||||
* @var CommandSender[][]
|
||||
* @phpstan-var array<string, array<int, CommandSender>>
|
||||
@ -744,6 +746,11 @@ class Server{
|
||||
$this->autoloader = $autoloader;
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->signalHandler = new SignalHandler(function() : void{
|
||||
$this->logger->info("Received signal interrupt, stopping the server");
|
||||
$this->shutdown();
|
||||
});
|
||||
|
||||
try{
|
||||
foreach([
|
||||
$dataPath,
|
||||
@ -966,91 +973,16 @@ class Server{
|
||||
$this->pluginManager->loadPlugins($this->pluginPath);
|
||||
$this->enablePlugins(PluginEnableOrder::STARTUP());
|
||||
|
||||
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
continue;
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($name, true)){
|
||||
$creationOptions = WorldCreationOptions::create();
|
||||
//TODO: error checking
|
||||
|
||||
if(isset($options["generator"])){
|
||||
$generatorOptions = explode(":", $options["generator"]);
|
||||
$creationOptions->setGeneratorClass(GeneratorManager::getInstance()->getGenerator(array_shift($generatorOptions)));
|
||||
if(count($generatorOptions) > 0){
|
||||
$creationOptions->setGeneratorOptions(implode(":", $generatorOptions));
|
||||
}
|
||||
}
|
||||
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
|
||||
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
|
||||
}
|
||||
if(isset($options["preset"]) && is_string($options["preset"])){
|
||||
$creationOptions->setGeneratorOptions($options["preset"]);
|
||||
}
|
||||
if(isset($options["seed"])){
|
||||
$convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? ""));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
}
|
||||
|
||||
$this->worldManager->generateWorld($name, $creationOptions);
|
||||
}
|
||||
if(!$this->startupPrepareWorlds()){
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->worldManager->getDefaultWorld() === null){
|
||||
$default = $this->configGroup->getConfigString("level-name", "world");
|
||||
if(trim($default) == ""){
|
||||
$this->getLogger()->warning("level-name cannot be null, using default");
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString("level-name", "world");
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($default, true)){
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
->setGeneratorClass(GeneratorManager::getInstance()->getGenerator($this->configGroup->getConfigString("level-type")))
|
||||
->setGeneratorOptions($this->configGroup->getConfigString("generator-settings"));
|
||||
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
$this->worldManager->generateWorld($default, $creationOptions);
|
||||
}
|
||||
|
||||
$world = $this->worldManager->getWorldByName($default);
|
||||
if($world === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->forceShutdown();
|
||||
|
||||
return;
|
||||
}
|
||||
$this->worldManager->setDefaultWorld($world);
|
||||
}
|
||||
|
||||
$this->enablePlugins(PluginEnableOrder::POSTWORLD());
|
||||
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
if(!$this->network->registerInterface(new RakLibInterface($this)) && $useQuery){
|
||||
//RakLib would normally handle the transport for Query packets
|
||||
//if it's not registered we need to make sure Query still works
|
||||
$this->network->registerInterface(new DedicatedQueryNetworkInterface($this->getIp(), $this->getPort(), new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($this->getIp(), (string) $this->getPort())));
|
||||
|
||||
if($useQuery){
|
||||
$this->network->registerRawPacketHandler(new QueryHandler($this));
|
||||
if(!$this->startupPrepareNetworkInterfaces()){
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($this->getIPBans()->getEntries() as $entry){
|
||||
$this->network->blockAddress($entry->getName(), -1);
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
|
||||
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("settings.send-usage", true)){
|
||||
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
|
||||
$this->sendUsageTicker = 6000;
|
||||
$this->sendUsage(SendUsageTask::TYPE_OPEN);
|
||||
}
|
||||
@ -1085,6 +1017,123 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
private function startupPrepareWorlds() : bool{
|
||||
$getGenerator = function(string $generatorName, string $generatorOptions, string $worldName) : ?string{
|
||||
$generatorEntry = GeneratorManager::getInstance()->getGenerator($generatorName);
|
||||
if($generatorEntry === null){
|
||||
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
|
||||
$worldName,
|
||||
KnownTranslationFactory::pocketmine_level_unknownGenerator($generatorName)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
$generatorEntry->validateGeneratorOptions($generatorOptions);
|
||||
}catch(InvalidGeneratorOptionsException $e){
|
||||
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
|
||||
$worldName,
|
||||
KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions($generatorOptions, $generatorName, $e->getMessage())
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
return $generatorEntry->getGeneratorClass();
|
||||
};
|
||||
|
||||
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
continue;
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($name, true) && !$this->worldManager->isWorldGenerated($name)){
|
||||
$creationOptions = WorldCreationOptions::create();
|
||||
//TODO: error checking
|
||||
|
||||
$generatorName = $options["generator"] ?? "default";
|
||||
$generatorOptions = isset($options["preset"]) && is_string($options["preset"]) ? $options["preset"] : "";
|
||||
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $name);
|
||||
if($generatorClass === null){
|
||||
continue;
|
||||
}
|
||||
$creationOptions->setGeneratorClass($generatorClass);
|
||||
$creationOptions->setGeneratorOptions($generatorOptions);
|
||||
|
||||
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
|
||||
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
|
||||
}
|
||||
|
||||
if(isset($options["seed"])){
|
||||
$convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? ""));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
}
|
||||
|
||||
$this->worldManager->generateWorld($name, $creationOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->worldManager->getDefaultWorld() === null){
|
||||
$default = $this->configGroup->getConfigString("level-name", "world");
|
||||
if(trim($default) == ""){
|
||||
$this->getLogger()->warning("level-name cannot be null, using default");
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString("level-name", "world");
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($default, true) && !$this->worldManager->isWorldGenerated($default)){
|
||||
$generatorName = $this->configGroup->getConfigString("level-type");
|
||||
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
|
||||
if($generatorClass !== null){
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
->setGeneratorClass($generatorClass)
|
||||
->setGeneratorOptions($generatorOptions);
|
||||
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
$this->worldManager->generateWorld($default, $creationOptions);
|
||||
}
|
||||
}
|
||||
|
||||
$world = $this->worldManager->getWorldByName($default);
|
||||
if($world === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->forceShutdown();
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->worldManager->setDefaultWorld($world);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function startupPrepareNetworkInterfaces() : bool{
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
if(!$this->network->registerInterface(new RakLibInterface($this)) && $useQuery){
|
||||
//RakLib would normally handle the transport for Query packets
|
||||
//if it's not registered we need to make sure Query still works
|
||||
$this->network->registerInterface(new DedicatedQueryNetworkInterface($this->getIp(), $this->getPort(), new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($this->getIp(), (string) $this->getPort())));
|
||||
|
||||
if($useQuery){
|
||||
$this->network->registerRawPacketHandler(new QueryHandler($this));
|
||||
}
|
||||
|
||||
foreach($this->getIPBans()->getEntries() as $entry){
|
||||
$this->network->blockAddress($entry->getName(), -1);
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
|
||||
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a particular message broadcast channel.
|
||||
* The channel ID can be any arbitrary string.
|
||||
@ -1293,20 +1342,17 @@ class Server{
|
||||
$commandLine = $ev->getCommand();
|
||||
}
|
||||
|
||||
if($this->commandMap->dispatch($sender, $commandLine)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_notFound()->prefix(TextFormat::RED));
|
||||
|
||||
return false;
|
||||
return $this->commandMap->dispatch($sender, $commandLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts the server down correctly
|
||||
*/
|
||||
public function shutdown() : void{
|
||||
$this->isRunning = false;
|
||||
if($this->isRunning){
|
||||
$this->isRunning = false;
|
||||
$this->signalHandler->unregister();
|
||||
}
|
||||
}
|
||||
|
||||
public function forceShutdown() : void{
|
||||
@ -1371,7 +1417,7 @@ class Server{
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
$this->logger->emergency("Crashed while crashing, killing process");
|
||||
@Process::kill(Process::pid());
|
||||
@Process::kill(Process::pid(), true);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1501,7 +1547,7 @@ class Server{
|
||||
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
sleep($spacing);
|
||||
}
|
||||
@Process::kill(Process::pid());
|
||||
@Process::kill(Process::pid(), true);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -1527,7 +1573,28 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
public function addOnlinePlayer(Player $player) : void{
|
||||
public function addOnlinePlayer(Player $player) : bool{
|
||||
$ev = new PlayerLoginEvent($player, "Plugin reason");
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$player->isConnected()){
|
||||
$player->disconnect($ev->getKickMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$session = $player->getNetworkSession();
|
||||
$position = $player->getPosition();
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_player_logIn(
|
||||
TextFormat::AQUA . $player->getName() . TextFormat::WHITE,
|
||||
$session->getIp(),
|
||||
(string) $session->getPort(),
|
||||
(string) $player->getId(),
|
||||
$position->getWorld()->getDisplayName(),
|
||||
(string) round($position->x, 4),
|
||||
(string) round($position->y, 4),
|
||||
(string) round($position->z, 4)
|
||||
)));
|
||||
|
||||
foreach($this->playerList as $p){
|
||||
$p->getNetworkSession()->onPlayerAdded($player);
|
||||
}
|
||||
@ -1537,6 +1604,8 @@ class Server{
|
||||
if($this->sendUsageTicker > 0){
|
||||
$this->uniquePlayers[$rawUUID] = $rawUUID;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function removeOnlinePlayer(Player $player) : void{
|
||||
|
@ -27,6 +27,8 @@ use pocketmine\utils\Config;
|
||||
use function array_key_exists;
|
||||
use function getopt;
|
||||
use function is_bool;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function strtolower;
|
||||
|
||||
final class ServerConfigGroup{
|
||||
@ -110,16 +112,20 @@ final class ServerConfigGroup{
|
||||
}else{
|
||||
$value = $this->serverProperties->exists($variable) ? $this->serverProperties->get($variable) : $defaultValue;
|
||||
}
|
||||
|
||||
if(is_bool($value)){
|
||||
return $value;
|
||||
}
|
||||
switch(strtolower($value)){
|
||||
case "on":
|
||||
case "true":
|
||||
case "1":
|
||||
case "yes":
|
||||
return true;
|
||||
if(is_int($value)){
|
||||
return $value !== 0;
|
||||
}
|
||||
if(is_string($value)){
|
||||
switch(strtolower($value)){
|
||||
case "on":
|
||||
case "true":
|
||||
case "1":
|
||||
case "yes":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -29,7 +29,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.0.0-BETA3";
|
||||
public const BASE_VERSION = "4.0.0-BETA6";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_NUMBER = 0;
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
|
@ -115,7 +115,7 @@ class Bamboo extends Transparent{
|
||||
return 12 + (self::getOffsetSeed($x, 0, $z) % 5);
|
||||
}
|
||||
|
||||
public function getPositionOffset() : ?Vector3{
|
||||
public function getModelPositionOffset() : ?Vector3{
|
||||
$seed = self::getOffsetSeed($this->position->getFloorX(), 0, $this->position->getFloorZ());
|
||||
$retX = (($seed % 12) + 1) / 16;
|
||||
$retZ = ((($seed >> 8) % 12) + 1) / 16;
|
||||
@ -145,12 +145,12 @@ class Bamboo extends Transparent{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$top = $this->seekToTop();
|
||||
if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2))){
|
||||
if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
}elseif($item instanceof ItemBamboo){
|
||||
if($this->seekToTop()->grow(PHP_INT_MAX, 1)){
|
||||
if($this->seekToTop()->grow(PHP_INT_MAX, 1, $player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
@ -165,7 +165,7 @@ class Bamboo extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow(int $maxHeight, int $growAmount) : bool{
|
||||
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||
return false;
|
||||
@ -212,7 +212,7 @@ class Bamboo extends Transparent{
|
||||
$tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock);
|
||||
}
|
||||
|
||||
$ev = new StructureGrowEvent($this, $tx);
|
||||
$ev = new StructureGrowEvent($this, $tx, $player);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
@ -229,7 +229,7 @@ class Bamboo extends Transparent{
|
||||
$world = $this->position->getWorld();
|
||||
if($this->ready){
|
||||
$this->ready = false;
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1)){
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1, null)){
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}elseif($world->getBlock($this->position->up())->canBeReplaced()){
|
||||
|
@ -73,7 +73,7 @@ final class BambooSapling extends Flowable{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer || $item instanceof ItemBamboo){
|
||||
if($this->grow()){
|
||||
if($this->grow($player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
@ -87,7 +87,7 @@ final class BambooSapling extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow() : bool{
|
||||
private function grow(?Player $player) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||
return false;
|
||||
@ -98,7 +98,7 @@ final class BambooSapling extends Flowable{
|
||||
$tx->addBlock($this->position, $bamboo)
|
||||
->addBlock($this->position->up(), (clone $bamboo)->setLeafSize(Bamboo::SMALL_LEAVES));
|
||||
|
||||
$ev = new StructureGrowEvent($this, $tx);
|
||||
$ev = new StructureGrowEvent($this, $tx, $player);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
@ -115,7 +115,7 @@ final class BambooSapling extends Flowable{
|
||||
$world = $this->position->getWorld();
|
||||
if($this->ready){
|
||||
$this->ready = false;
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow()){
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow(null)){
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}elseif($world->getBlock($this->position->up())->canBeReplaced()){
|
||||
|
@ -37,8 +37,6 @@ use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\BellRingSound;
|
||||
|
||||
final class Bell extends Transparent{
|
||||
private const BELL_RINGING_REPEAT_TICKS = 20;
|
||||
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
private BellAttachmentType $attachmentType;
|
||||
|
@ -179,7 +179,7 @@ class Block{
|
||||
* Returns whether the given block has the same type and properties as this block.
|
||||
*/
|
||||
public function isSameState(Block $other) : bool{
|
||||
return $this->isSameType($other) and $this->writeStateToMeta() === $other->writeStateToMeta();
|
||||
return $this->getFullId() === $other->getFullId();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -577,7 +577,7 @@ class Block{
|
||||
final public function getCollisionBoxes() : array{
|
||||
if($this->collisionBoxes === null){
|
||||
$this->collisionBoxes = $this->recalculateCollisionBoxes();
|
||||
$extraOffset = $this->getPositionOffset();
|
||||
$extraOffset = $this->getModelPositionOffset();
|
||||
$offset = $extraOffset !== null ? $this->position->addVector($extraOffset) : $this->position;
|
||||
foreach($this->collisionBoxes as $bb){
|
||||
$bb->offset($offset->x, $offset->y, $offset->z);
|
||||
@ -588,10 +588,10 @@ class Block{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an additional fractional vector to shift the block's effective position by based on the current position.
|
||||
* Returns an additional fractional vector to shift the block model's position by based on the current position.
|
||||
* Used to randomize position of things like bamboo canes and tall grass.
|
||||
*/
|
||||
public function getPositionOffset() : ?Vector3{
|
||||
public function getModelPositionOffset() : ?Vector3{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -183,6 +183,7 @@ class BlockFactory{
|
||||
$this->register(new EnderChest(new BID(Ids::ENDER_CHEST, 0, null, TileEnderChest::class), "Ender Chest", new BlockBreakInfo(22.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 3000.0)));
|
||||
$this->register(new Farmland(new BID(Ids::FARMLAND, 0), "Farmland", new BlockBreakInfo(0.6, BlockToolType::SHOVEL)));
|
||||
$this->register(new Fire(new BID(Ids::FIRE, 0), "Fire Block", BlockBreakInfo::instant()));
|
||||
$this->register(new FletchingTable(new BID(Ids::FLETCHING_TABLE, 0), "Fletching Table", new BlockBreakInfo(2.5, BlockToolType::AXE, 0, 2.5)));
|
||||
$this->register(new Flower(new BID(Ids::DANDELION, 0), "Dandelion", BlockBreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_ALLIUM), "Allium", BlockBreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_AZURE_BLUET), "Azure Bluet", BlockBreakInfo::instant()));
|
||||
@ -402,6 +403,7 @@ class BlockFactory{
|
||||
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_STONE), "Stone", $stoneSlabBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::STONECUTTER, 0), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Sugarcane(new BID(Ids::REEDS_BLOCK, 0, ItemIds::REEDS), "Sugarcane", BlockBreakInfo::instant()));
|
||||
$this->register(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
|
||||
$this->register(new TNT(new BID(Ids::TNT, 0), "TNT", BlockBreakInfo::instant()));
|
||||
|
||||
$fern = new TallGrass(new BID(Ids::TALLGRASS, Meta::TALLGRASS_FERN), "Fern", BlockBreakInfo::instant(BlockToolType::SHEARS, 1));
|
||||
@ -567,7 +569,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:dropper
|
||||
//TODO: minecraft:end_gateway
|
||||
//TODO: minecraft:end_portal
|
||||
//TODO: minecraft:fletching_table
|
||||
//TODO: minecraft:grindstone
|
||||
//TODO: minecraft:jigsaw
|
||||
//TODO: minecraft:kelp
|
||||
@ -584,7 +585,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:sticky_piston
|
||||
//TODO: minecraft:stonecutter_block
|
||||
//TODO: minecraft:structure_block
|
||||
$this->register(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
|
||||
//TODO: minecraft:turtle_egg
|
||||
//endregion
|
||||
|
||||
@ -952,7 +952,7 @@ class BlockFactory{
|
||||
* Deserializes a block from the provided legacy ID and legacy meta.
|
||||
*/
|
||||
public function get(int $id, int $meta) : Block{
|
||||
if($meta < 0 or $meta > (1 << Block::INTERNAL_METADATA_BITS)){
|
||||
if($meta < 0 or $meta >= (1 << Block::INTERNAL_METADATA_BITS)){
|
||||
throw new \InvalidArgumentException("Block meta value $meta is out of bounds");
|
||||
}
|
||||
|
||||
|
30
src/block/FletchingTable.php
Normal file
30
src/block/FletchingTable.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
class FletchingTable extends Opaque{
|
||||
public function getFuelTime() : int{
|
||||
return 300;
|
||||
}
|
||||
}
|
@ -23,50 +23,72 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\RedstonePowerOffSound;
|
||||
use pocketmine\world\sound\RedstonePowerOnSound;
|
||||
|
||||
class Lever extends Flowable{
|
||||
protected const BOTTOM = 0;
|
||||
protected const SIDE = 1;
|
||||
protected const TOP = 2;
|
||||
protected LeverFacing $facing;
|
||||
protected bool $activated = false;
|
||||
|
||||
protected int $leverPos = self::BOTTOM;
|
||||
protected int $facing = Facing::NORTH;
|
||||
protected bool $powered = false;
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->facing = LeverFacing::UP_AXIS_X();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
|
||||
protected function writeStateToMeta() : int{
|
||||
if($this->leverPos === self::BOTTOM){
|
||||
$rotationMeta = Facing::axis($this->facing) === Axis::Z ? 7 : 0;
|
||||
}elseif($this->leverPos === self::TOP){
|
||||
$rotationMeta = Facing::axis($this->facing) === Axis::Z ? 5 : 6;
|
||||
}else{
|
||||
$rotationMeta = 6 - BlockDataSerializer::writeHorizontalFacing($this->facing);
|
||||
}
|
||||
return $rotationMeta | ($this->powered ? BlockLegacyMetadata::LEVER_FLAG_POWERED : 0);
|
||||
$rotationMeta = match($this->facing->id()){
|
||||
LeverFacing::DOWN_AXIS_X()->id() => 0,
|
||||
LeverFacing::EAST()->id() => 1,
|
||||
LeverFacing::WEST()->id() => 2,
|
||||
LeverFacing::SOUTH()->id() => 3,
|
||||
LeverFacing::NORTH()->id() => 4,
|
||||
LeverFacing::UP_AXIS_Z()->id() => 5,
|
||||
LeverFacing::UP_AXIS_X()->id() => 6,
|
||||
LeverFacing::DOWN_AXIS_Z()->id() => 7,
|
||||
default => throw new AssumptionFailedError(),
|
||||
};
|
||||
return $rotationMeta | ($this->activated ? BlockLegacyMetadata::LEVER_FLAG_POWERED : 0);
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$rotationMeta = $stateMeta & 0x07;
|
||||
if($rotationMeta === 5 or $rotationMeta === 6){
|
||||
$this->leverPos = self::TOP;
|
||||
$this->facing = $rotationMeta === 5 ? Facing::SOUTH : Facing::EAST;
|
||||
}elseif($rotationMeta === 7 or $rotationMeta === 0){
|
||||
$this->leverPos = self::BOTTOM;
|
||||
$this->facing = $rotationMeta === 7 ? Facing::SOUTH : Facing::EAST;
|
||||
}else{
|
||||
$this->leverPos = self::SIDE;
|
||||
$this->facing = BlockDataSerializer::readHorizontalFacing(6 - $rotationMeta);
|
||||
}
|
||||
$this->facing = match($rotationMeta){
|
||||
0 => LeverFacing::DOWN_AXIS_X(),
|
||||
1 => LeverFacing::EAST(),
|
||||
2 => LeverFacing::WEST(),
|
||||
3 => LeverFacing::SOUTH(),
|
||||
4 => LeverFacing::NORTH(),
|
||||
5 => LeverFacing::UP_AXIS_Z(),
|
||||
6 => LeverFacing::UP_AXIS_X(),
|
||||
7 => LeverFacing::DOWN_AXIS_Z(),
|
||||
default => throw new AssumptionFailedError("0x07 mask should make this impossible"), //phpstan doesn't understand :(
|
||||
};
|
||||
|
||||
$this->powered = ($stateMeta & BlockLegacyMetadata::LEVER_FLAG_POWERED) !== 0;
|
||||
$this->activated = ($stateMeta & BlockLegacyMetadata::LEVER_FLAG_POWERED) !== 0;
|
||||
}
|
||||
|
||||
public function getFacing() : LeverFacing{ return $this->facing; }
|
||||
|
||||
/** @return $this */
|
||||
public function setFacing(LeverFacing $facing) : self{
|
||||
$this->facing = $facing;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isActivated() : bool{ return $this->activated; }
|
||||
|
||||
/** @return $this */
|
||||
public function setActivated(bool $activated) : self{
|
||||
$this->activated = $activated;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -78,39 +100,37 @@ class Lever extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Facing::axis($face) === Axis::Y){
|
||||
$selectUpDownPos = function(LeverFacing $x, LeverFacing $z) use ($player) : LeverFacing{
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
return Facing::axis($player->getHorizontalFacing()) === Axis::X ? $x : $z;
|
||||
}
|
||||
$this->leverPos = $face === Facing::DOWN ? self::BOTTOM : self::TOP;
|
||||
}else{
|
||||
$this->facing = $face;
|
||||
$this->leverPos = self::SIDE;
|
||||
}
|
||||
return $x;
|
||||
};
|
||||
$this->facing = match($face){
|
||||
Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X(), LeverFacing::DOWN_AXIS_Z()),
|
||||
Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X(), LeverFacing::UP_AXIS_Z()),
|
||||
Facing::NORTH => LeverFacing::NORTH(),
|
||||
Facing::SOUTH => LeverFacing::SOUTH(),
|
||||
Facing::WEST => LeverFacing::WEST(),
|
||||
Facing::EAST => LeverFacing::EAST(),
|
||||
default => throw new AssumptionFailedError("Bad facing value"),
|
||||
};
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->leverPos === self::BOTTOM){
|
||||
$face = Facing::UP;
|
||||
}elseif($this->leverPos === self::TOP){
|
||||
$face = Facing::DOWN;
|
||||
}else{
|
||||
$face = Facing::opposite($this->facing);
|
||||
}
|
||||
|
||||
if(!$this->getSide($face)->isSolid()){
|
||||
if(!$this->getSide(Facing::opposite($this->facing->getFacing()))->isSolid()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->powered = !$this->powered;
|
||||
$this->activated = !$this->activated;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$this->position->getWorld()->addSound(
|
||||
$this->position->add(0.5, 0.5, 0.5),
|
||||
$this->powered ? new RedstonePowerOnSound() : new RedstonePowerOffSound()
|
||||
$this->activated ? new RedstonePowerOnSound() : new RedstonePowerOffSound()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class Sapling extends Flowable{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$this->grow();
|
||||
$this->grow($player);
|
||||
|
||||
$item->pop();
|
||||
|
||||
@ -100,7 +100,7 @@ class Sapling extends Flowable{
|
||||
public function onRandomTick() : void{
|
||||
if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 and mt_rand(1, 7) === 1){
|
||||
if($this->ready){
|
||||
$this->grow();
|
||||
$this->grow(null);
|
||||
}else{
|
||||
$this->ready = true;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
@ -108,7 +108,7 @@ class Sapling extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow() : void{
|
||||
private function grow(?Player $player) : void{
|
||||
$random = new Random(mt_rand());
|
||||
$tree = TreeFactory::get($random, $this->treeType);
|
||||
$transaction = $tree?->getBlockTransaction($this->position->getWorld(), $this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), $random);
|
||||
@ -116,13 +116,11 @@ class Sapling extends Flowable{
|
||||
return;
|
||||
}
|
||||
|
||||
$ev = new StructureGrowEvent($this, $transaction);
|
||||
$ev = new StructureGrowEvent($this, $transaction, $player);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
if(!$ev->isCancelled()){
|
||||
$transaction->apply();
|
||||
}
|
||||
|
||||
$transaction->apply();
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
|
@ -300,6 +300,7 @@ use function assert;
|
||||
* @method static Farmland FARMLAND()
|
||||
* @method static TallGrass FERN()
|
||||
* @method static Fire FIRE()
|
||||
* @method static FletchingTable FLETCHING_TABLE()
|
||||
* @method static FlowerPot FLOWER_POT()
|
||||
* @method static FrostedIce FROSTED_ICE()
|
||||
* @method static Furnace FURNACE()
|
||||
@ -866,6 +867,7 @@ final class VanillaBlocks{
|
||||
self::register("farmland", $factory->get(60, 0));
|
||||
self::register("fern", $factory->get(31, 2));
|
||||
self::register("fire", $factory->get(51, 0));
|
||||
self::register("fletching_table", $factory->get(456, 0));
|
||||
self::register("flower_pot", $factory->get(140, 0));
|
||||
self::register("frosted_ice", $factory->get(207, 0));
|
||||
self::register("furnace", $factory->get(61, 2));
|
||||
|
67
src/block/utils/LeverFacing.php
Normal file
67
src/block/utils/LeverFacing.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\utils\EnumTrait;
|
||||
|
||||
/**
|
||||
* This doc-block is generated automatically, do not modify it manually.
|
||||
* This must be regenerated whenever registry members are added, removed or changed.
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static LeverFacing DOWN_AXIS_X()
|
||||
* @method static LeverFacing DOWN_AXIS_Z()
|
||||
* @method static LeverFacing EAST()
|
||||
* @method static LeverFacing NORTH()
|
||||
* @method static LeverFacing SOUTH()
|
||||
* @method static LeverFacing UP_AXIS_X()
|
||||
* @method static LeverFacing UP_AXIS_Z()
|
||||
* @method static LeverFacing WEST()
|
||||
*/
|
||||
final class LeverFacing{
|
||||
use EnumTrait {
|
||||
__construct as Enum___construct;
|
||||
}
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new self("up_axis_x", Facing::UP),
|
||||
new self("up_axis_z", Facing::UP),
|
||||
new self("down_axis_x", Facing::DOWN),
|
||||
new self("down_axis_z", Facing::DOWN),
|
||||
new self("north", Facing::NORTH),
|
||||
new self("east", Facing::EAST),
|
||||
new self("south", Facing::SOUTH),
|
||||
new self("west", Facing::WEST),
|
||||
);
|
||||
}
|
||||
|
||||
private function __construct(string $enumName, private int $facing){
|
||||
$this->Enum___construct($enumName);
|
||||
}
|
||||
|
||||
public function getFacing() : int{ return $this->facing; }
|
||||
}
|
@ -117,7 +117,7 @@ abstract class Command{
|
||||
}
|
||||
|
||||
if($this->permissionMessage === null){
|
||||
$target->sendMessage(KnownTranslationFactory::commands_generic_permission()->prefix(TextFormat::RED));
|
||||
$target->sendMessage(KnownTranslationFactory::pocketmine_command_error_permission($this->name)->prefix(TextFormat::RED));
|
||||
}elseif($this->permissionMessage !== ""){
|
||||
$target->sendMessage(str_replace("<permission>", $permission ?? $this->permission, $this->permissionMessage));
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ use pocketmine\command\defaults\WhitelistCommand;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function explode;
|
||||
@ -210,26 +211,23 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sentCommandLabel = array_shift($args);
|
||||
if($sentCommandLabel === null){
|
||||
return false;
|
||||
}
|
||||
$target = $this->getCommand($sentCommandLabel);
|
||||
if($target === null){
|
||||
return false;
|
||||
if($sentCommandLabel !== null && ($target = $this->getCommand($sentCommandLabel)) !== null){
|
||||
$target->timings->startTiming();
|
||||
|
||||
try{
|
||||
$target->execute($sender, $sentCommandLabel, $args);
|
||||
}catch(InvalidCommandSyntaxException $e){
|
||||
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
|
||||
}finally{
|
||||
$target->timings->stopTiming();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$target->timings->startTiming();
|
||||
|
||||
try{
|
||||
$target->execute($sender, $sentCommandLabel, $args);
|
||||
}catch(InvalidCommandSyntaxException $e){
|
||||
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
|
||||
}finally{
|
||||
$target->timings->stopTiming();
|
||||
}
|
||||
|
||||
return true;
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound($sentCommandLabel ?? "", "/help")->prefix(TextFormat::RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
public function clearCommands() : void{
|
||||
|
@ -53,7 +53,7 @@ class DefaultGamemodeCommand extends VanillaCommand{
|
||||
|
||||
$gameMode = GameMode::fromString($args[0]);
|
||||
if($gameMode === null){
|
||||
$sender->sendMessage("Unknown game mode");
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_unknown($args[0]));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ class GamemodeCommand extends VanillaCommand{
|
||||
|
||||
$gameMode = GameMode::fromString($args[0]);
|
||||
if($gameMode === null){
|
||||
$sender->sendMessage("Unknown game mode");
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_unknown($args[0]));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ class GamemodeCommand extends VanillaCommand{
|
||||
|
||||
$target->setGamemode($gameMode);
|
||||
if(!$gameMode->equals($target->getGamemode())){
|
||||
$sender->sendMessage("Game mode change for " . $target->getName() . " failed!");
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
|
||||
}else{
|
||||
if($target === $sender){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_gamemode_success_self($gameMode->getTranslatableName()));
|
||||
|
@ -68,7 +68,7 @@ class GarbageCollectorCommand extends VanillaCommand{
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_entities(TextFormat::RED . number_format($entitiesCollected))->prefix(TextFormat::GOLD));
|
||||
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_cycles(TextFormat::RED . number_format($cyclesCollected))->prefix(TextFormat::GOLD));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_memoryFreed(TextFormat::RED . number_format(round((($memory - memory_get_usage()) / 1024) / 1024, 2), 2) . " MB")->prefix(TextFormat::GOLD));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_memoryFreed(TextFormat::RED . number_format(round((($memory - memory_get_usage()) / 1024) / 1024, 2), 2))->prefix(TextFormat::GOLD));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\item\StringToItemParser;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\nbt\JsonNbtParser;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function array_slice;
|
||||
@ -86,7 +87,12 @@ class GiveCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
$item->setNamedTag($tags);
|
||||
try{
|
||||
$item->setNamedTag($tags);
|
||||
}catch(NbtException $e){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_give_tagError($e->getMessage()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: overflow
|
||||
|
@ -105,17 +105,20 @@ class HelpCommand extends VanillaCommand{
|
||||
$lang = $sender->getLanguage();
|
||||
$description = $cmd->getDescription();
|
||||
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
|
||||
$message = TextFormat::YELLOW . "--------- " . TextFormat::WHITE . " Help: /" . $cmd->getName() . TextFormat::YELLOW . " ---------\n";
|
||||
$message .= TextFormat::GOLD . "Description: " . TextFormat::WHITE . $descriptionString . "\n";
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($commandName)
|
||||
->format(TextFormat::YELLOW . "--------- " . TextFormat::WHITE, TextFormat::YELLOW . " ---------"));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::WHITE . $descriptionString)
|
||||
->prefix(TextFormat::GOLD));
|
||||
|
||||
$usage = $cmd->getUsage();
|
||||
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
|
||||
$message .= TextFormat::GOLD . "Usage: " . TextFormat::WHITE . implode("\n" . TextFormat::WHITE, explode("\n", $usageString)) . "\n";
|
||||
$sender->sendMessage($message);
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::WHITE . implode("\n" . TextFormat::WHITE, explode("\n", $usageString)))
|
||||
->prefix(TextFormat::GOLD));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$sender->sendMessage(TextFormat::RED . "No help for " . strtolower($commandName));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound($commandName, "/help")->prefix(TextFormat::RED));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -681,10 +681,12 @@ abstract class Entity{
|
||||
throw new \InvalidArgumentException("Fire ticks must be in range 0 ... " . 0x7fff . ", got $fireTicks");
|
||||
}
|
||||
$this->fireTicks = $fireTicks;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function extinguish() : void{
|
||||
$this->fireTicks = 0;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function isFireProof() : bool{
|
||||
|
@ -227,8 +227,8 @@ class ItemEntity extends Entity{
|
||||
|
||||
$item = $this->getItem();
|
||||
$playerInventory = match(true){
|
||||
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->canAddItem($item) => $player->getOffHandInventory(),
|
||||
$player->getInventory()->canAddItem($item) => $player->getInventory(),
|
||||
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->getAddableItemQuantity($item) > 0 => $player->getOffHandInventory(),
|
||||
$player->getInventory()->getAddableItemQuantity($item) > 0 => $player->getInventory(),
|
||||
default => null
|
||||
};
|
||||
|
||||
@ -246,7 +246,12 @@ class ItemEntity extends Entity{
|
||||
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
|
||||
}
|
||||
|
||||
$ev->getInventory()?->addItem($ev->getItem());
|
||||
$inventory = $ev->getInventory();
|
||||
if($inventory !== null){
|
||||
foreach($inventory->addItem($ev->getItem()) as $remains){
|
||||
$this->getWorld()->dropItem($this->location, $remains, new Vector3(0, 0, 0));
|
||||
}
|
||||
}
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ abstract class Projectile extends Entity{
|
||||
Timings::$entityMove->startTiming();
|
||||
|
||||
$start = $this->location->asVector3();
|
||||
$end = $start->addVector($this->motion);
|
||||
$end = $start->add($dx, $dy, $dz);
|
||||
|
||||
$blockHit = null;
|
||||
$entityHit = null;
|
||||
|
@ -7,6 +7,7 @@ namespace pocketmine\event\block;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
/**
|
||||
@ -17,13 +18,23 @@ class StructureGrowEvent extends BlockEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
private BlockTransaction $transaction;
|
||||
private ?Player $player;
|
||||
|
||||
public function __construct(Block $block, BlockTransaction $transaction){
|
||||
public function __construct(Block $block, BlockTransaction $transaction, ?Player $player){
|
||||
parent::__construct($block);
|
||||
$this->transaction = $transaction;
|
||||
$this->player = $player;
|
||||
}
|
||||
|
||||
public function getTransaction() : BlockTransaction{
|
||||
return $this->transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the player which grows the structure.
|
||||
* It returns null when the structure grows by itself.
|
||||
*/
|
||||
public function getPlayer() : ?Player{
|
||||
return $this->player;
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ use pocketmine\player\Player;
|
||||
|
||||
class PlayerDeathEvent extends EntityDeathEvent{
|
||||
/** @var Player */
|
||||
protected $entity;
|
||||
protected $player;
|
||||
|
||||
/** @var Translatable|string */
|
||||
private $deathMessage;
|
||||
@ -49,6 +49,7 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
*/
|
||||
public function __construct(Player $entity, array $drops, int $xp, Translatable|string|null $deathMessage){
|
||||
parent::__construct($entity, $drops, $xp);
|
||||
$this->player = $entity;
|
||||
$this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause());
|
||||
}
|
||||
|
||||
@ -56,11 +57,11 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
* @return Player
|
||||
*/
|
||||
public function getEntity(){
|
||||
return $this->entity;
|
||||
return $this->player;
|
||||
}
|
||||
|
||||
public function getPlayer() : Player{
|
||||
return $this->entity;
|
||||
return $this->player;
|
||||
}
|
||||
|
||||
public function getDeathMessage() : Translatable|string{
|
||||
|
@ -169,6 +169,10 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
public function canAddItem(Item $item) : bool{
|
||||
return $this->getAddableItemQuantity($item) === $item->getCount();
|
||||
}
|
||||
|
||||
public function getAddableItemQuantity(Item $item) : int{
|
||||
$count = $item->getCount();
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$slot = $this->getItem($i);
|
||||
@ -181,11 +185,11 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
if($count <= 0){
|
||||
return true;
|
||||
return $item->getCount();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $item->getCount() - $count;
|
||||
}
|
||||
|
||||
public function addItem(Item ...$slots) : array{
|
||||
|
@ -63,6 +63,11 @@ interface Inventory{
|
||||
*/
|
||||
public function canAddItem(Item $item) : bool;
|
||||
|
||||
/**
|
||||
* Returns how many items from the given itemstack can be added to this inventory.
|
||||
*/
|
||||
public function getAddableItemQuantity(Item $item) : int;
|
||||
|
||||
/**
|
||||
* Removes the given Item from the inventory.
|
||||
* It will return the Items that couldn't be removed.
|
||||
|
@ -37,6 +37,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
@ -240,6 +241,7 @@ class Item implements \JsonSerializable{
|
||||
* Sets the Item's NBT from the supplied CompoundTag object.
|
||||
*
|
||||
* @return $this
|
||||
* @throws NbtException
|
||||
*/
|
||||
public function setNamedTag(CompoundTag $tag) : Item{
|
||||
if($tag->getCount() === 0){
|
||||
@ -255,6 +257,7 @@ class Item implements \JsonSerializable{
|
||||
/**
|
||||
* Removes the Item's NBT.
|
||||
* @return $this
|
||||
* @throws NbtException
|
||||
*/
|
||||
public function clearNamedTag() : Item{
|
||||
$this->nbt = new CompoundTag();
|
||||
@ -262,6 +265,9 @@ class Item implements \JsonSerializable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NbtException
|
||||
*/
|
||||
protected function deserializeCompoundTag(CompoundTag $tag) : void{
|
||||
$this->customName = "";
|
||||
$this->lore = [];
|
||||
|
@ -41,6 +41,7 @@ use pocketmine\entity\Villager;
|
||||
use pocketmine\entity\Zombie;
|
||||
use pocketmine\inventory\ArmorInventory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\world\World;
|
||||
@ -444,6 +445,7 @@ class ItemFactory{
|
||||
* Deserializes an item from the provided legacy ID, legacy meta, count and NBT.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws NbtException
|
||||
*/
|
||||
public function get(int $id, int $meta = 0, int $count = 1, ?CompoundTag $tags = null) : Item{
|
||||
/** @var Item|null $item */
|
||||
|
@ -31,7 +31,6 @@ use function file_get_contents;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_numeric;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function str_replace;
|
||||
use function strtolower;
|
||||
@ -63,8 +62,8 @@ final class LegacyStringToItemParser{
|
||||
if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array");
|
||||
|
||||
foreach($mappings as $name => $id){
|
||||
if(!is_string($name) or !is_int($id)) throw new AssumptionFailedError("Invalid mappings format, expected string keys and int values");
|
||||
$result->addMapping($name, $id);
|
||||
if(!is_int($id)) throw new AssumptionFailedError("Invalid mappings format, expected int values");
|
||||
$result->addMapping((string) $name, $id);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@ -84,6 +83,14 @@ final class LegacyStringToItemParser{
|
||||
$this->map[$alias] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<string, int>
|
||||
*/
|
||||
public function getMappings() : array{
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse the specified string into Item types.
|
||||
*
|
||||
@ -106,9 +113,7 @@ final class LegacyStringToItemParser{
|
||||
throw new LegacyStringToItemParserException("Unable to parse \"" . $b[1] . "\" from \"" . $input . "\" as a valid meta value");
|
||||
}
|
||||
|
||||
if(is_numeric($b[0])){
|
||||
$item = $this->itemFactory->get((int) $b[0], $meta);
|
||||
}elseif(isset($this->map[strtolower($b[0])])){
|
||||
if(isset($this->map[strtolower($b[0])])){
|
||||
$item = $this->itemFactory->get($this->map[strtolower($b[0])], $meta);
|
||||
}else{
|
||||
throw new LegacyStringToItemParserException("Unable to resolve \"" . $input . "\" to a valid item");
|
||||
|
@ -530,6 +530,7 @@ final class StringToItemParser{
|
||||
$result->registerBlock("fence_gate_spruce", fn() => VanillaBlocks::SPRUCE_FENCE_GATE());
|
||||
$result->registerBlock("fern", fn() => VanillaBlocks::FERN());
|
||||
$result->registerBlock("fire", fn() => VanillaBlocks::FIRE());
|
||||
$result->registerBlock("fletching_table", fn() => VanillaBlocks::FLETCHING_TABLE());
|
||||
$result->registerBlock("flower_pot", fn() => VanillaBlocks::FLOWER_POT());
|
||||
$result->registerBlock("flower_pot_block", fn() => VanillaBlocks::FLOWER_POT());
|
||||
$result->registerBlock("flowing_lava", fn() => VanillaBlocks::LAVA());
|
||||
|
@ -573,6 +573,12 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_WHITELIST_USAGE, []);
|
||||
}
|
||||
|
||||
public static function death_attack_anvil(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ANVIL, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function death_attack_arrow(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ARROW, [
|
||||
0 => $param0,
|
||||
@ -1103,6 +1109,18 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ENCHANT_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_error_permission(Translatable|string $commandName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ERROR_PERMISSION, [
|
||||
"commandName" => $commandName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_error_playerNotFound(Translatable|string $playerName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ERROR_PLAYERNOTFOUND, [
|
||||
"playerName" => $playerName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_exception(Translatable|string $param0, Translatable|string $param1, Translatable|string $param2) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_EXCEPTION, [
|
||||
0 => $param0,
|
||||
@ -1115,6 +1133,18 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_GAMEMODE_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_gamemode_failure(Translatable|string $playerName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_GAMEMODE_FAILURE, [
|
||||
"playerName" => $playerName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_gamemode_unknown(Translatable|string $gameModeName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_GAMEMODE_UNKNOWN, [
|
||||
"gameModeName" => $gameModeName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_gc_chunks(Translatable|string $chunksCollected) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_GC_CHUNKS, [
|
||||
"chunksCollected" => $chunksCollected,
|
||||
@ -1159,6 +1189,24 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_HELP_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_help_specificCommand_description(Translatable|string $description) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_HELP_SPECIFICCOMMAND_DESCRIPTION, [
|
||||
"description" => $description,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_help_specificCommand_header(Translatable|string $commandName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_HELP_SPECIFICCOMMAND_HEADER, [
|
||||
"commandName" => $commandName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_help_specificCommand_usage(Translatable|string $usage) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_HELP_SPECIFICCOMMAND_USAGE, [
|
||||
"usage" => $usage,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_kick_description() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_KICK_DESCRIPTION, []);
|
||||
}
|
||||
@ -1179,6 +1227,13 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ME_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_notFound(Translatable|string $commandName, Translatable|string $helpCommand) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_NOTFOUND, [
|
||||
"commandName" => $commandName,
|
||||
"helpCommand" => $helpCommand,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_op_description() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_OP_DESCRIPTION, []);
|
||||
}
|
||||
@ -1490,6 +1545,25 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_conversion_finish(Translatable|string $worldName, Translatable|string $backupPath) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_CONVERSION_FINISH, [
|
||||
"worldName" => $worldName,
|
||||
"backupPath" => $backupPath,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_conversion_start(Translatable|string $worldName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_CONVERSION_START, [
|
||||
"worldName" => $worldName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_corrupted(Translatable|string $details) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_CORRUPTED, [
|
||||
"details" => $details,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_defaultError() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_DEFAULTERROR, []);
|
||||
}
|
||||
@ -1501,6 +1575,14 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_invalidGeneratorOptions(Translatable|string $preset, Translatable|string $generatorName, Translatable|string $details) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_INVALIDGENERATOROPTIONS, [
|
||||
"preset" => $preset,
|
||||
"generatorName" => $generatorName,
|
||||
"details" => $details,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_loadError(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_LOADERROR, [
|
||||
0 => $param0,
|
||||
@ -1520,16 +1602,36 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_spawnTerrainGenerationProgress(Translatable|string $done, Translatable|string $total, Translatable|string $percentageDone) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_SPAWNTERRAINGENERATIONPROGRESS, [
|
||||
"done" => $done,
|
||||
"total" => $total,
|
||||
"percentageDone" => $percentageDone,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_unknownFormat() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_UNKNOWNFORMAT, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_unknownGenerator(Translatable|string $generatorName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_UNKNOWNGENERATOR, [
|
||||
"generatorName" => $generatorName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_unloading(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_UNLOADING, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_unsupportedFormat(Translatable|string $details) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_UNSUPPORTEDFORMAT, [
|
||||
"details" => $details,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_player_invalidEntity(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLAYER_INVALIDENTITY, [
|
||||
0 => $param0,
|
||||
@ -1577,6 +1679,12 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_badDataFolder(Translatable|string $dataFolder) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_BADDATAFOLDER, [
|
||||
"dataFolder" => $dataFolder,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_circularDependency() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_CIRCULARDEPENDENCY, []);
|
||||
}
|
||||
@ -1602,23 +1710,36 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_disallowedByBlacklist() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_DISALLOWEDBYBLACKLIST, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_disallowedByWhitelist() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_DISALLOWEDBYWHITELIST, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_duplicateError(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_DUPLICATEERROR, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_emptyExtensionVersionConstraint(Translatable|string $constraintIndex, Translatable|string $extensionName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_EMPTYEXTENSIONVERSIONCONSTRAINT, [
|
||||
"constraintIndex" => $constraintIndex,
|
||||
"extensionName" => $extensionName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_enable(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_ENABLE, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_fileError(Translatable|string $param0, Translatable|string $param1, Translatable|string $param2) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_FILEERROR, [
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
2 => $param2,
|
||||
public static function pocketmine_plugin_extensionNotLoaded(Translatable|string $extensionName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_EXTENSIONNOTLOADED, [
|
||||
"extensionName" => $extensionName,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -1634,6 +1755,14 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_incompatibleExtensionVersion(Translatable|string $extensionVersion, Translatable|string $extensionName, Translatable|string $pluginRequirement) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INCOMPATIBLEEXTENSIONVERSION, [
|
||||
"extensionVersion" => $extensionVersion,
|
||||
"extensionName" => $extensionName,
|
||||
"pluginRequirement" => $pluginRequirement,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_incompatibleOS(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INCOMPATIBLEOS, [
|
||||
0 => $param0,
|
||||
@ -1652,6 +1781,25 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_invalidAPI(Translatable|string $apiVersion) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INVALIDAPI, [
|
||||
"apiVersion" => $apiVersion,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_invalidExtensionVersionConstraint(Translatable|string $versionConstraint, Translatable|string $extensionName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INVALIDEXTENSIONVERSIONCONSTRAINT, [
|
||||
"versionConstraint" => $versionConstraint,
|
||||
"extensionName" => $extensionName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_invalidManifest(Translatable|string $details) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INVALIDMANIFEST, [
|
||||
"details" => $details,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_load(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_LOAD, [
|
||||
0 => $param0,
|
||||
@ -1665,6 +1813,16 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_mainClassNotFound() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_MAINCLASSNOTFOUND, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_mainClassWrongType(Translatable|string $pluginInterface) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_MAINCLASSWRONGTYPE, [
|
||||
"pluginInterface" => $pluginInterface,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_restrictedName() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_RESTRICTEDNAME, []);
|
||||
}
|
||||
|
@ -128,6 +128,7 @@ final class KnownTranslationKeys{
|
||||
public const COMMANDS_WHITELIST_REMOVE_SUCCESS = "commands.whitelist.remove.success";
|
||||
public const COMMANDS_WHITELIST_REMOVE_USAGE = "commands.whitelist.remove.usage";
|
||||
public const COMMANDS_WHITELIST_USAGE = "commands.whitelist.usage";
|
||||
public const DEATH_ATTACK_ANVIL = "death.attack.anvil";
|
||||
public const DEATH_ATTACK_ARROW = "death.attack.arrow";
|
||||
public const DEATH_ATTACK_ARROW_ITEM = "death.attack.arrow.item";
|
||||
public const DEATH_ATTACK_CACTUS = "death.attack.cactus";
|
||||
@ -243,8 +244,12 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION = "pocketmine.command.difficulty.description";
|
||||
public const POCKETMINE_COMMAND_EFFECT_DESCRIPTION = "pocketmine.command.effect.description";
|
||||
public const POCKETMINE_COMMAND_ENCHANT_DESCRIPTION = "pocketmine.command.enchant.description";
|
||||
public const POCKETMINE_COMMAND_ERROR_PERMISSION = "pocketmine.command.error.permission";
|
||||
public const POCKETMINE_COMMAND_ERROR_PLAYERNOTFOUND = "pocketmine.command.error.playerNotFound";
|
||||
public const POCKETMINE_COMMAND_EXCEPTION = "pocketmine.command.exception";
|
||||
public const POCKETMINE_COMMAND_GAMEMODE_DESCRIPTION = "pocketmine.command.gamemode.description";
|
||||
public const POCKETMINE_COMMAND_GAMEMODE_FAILURE = "pocketmine.command.gamemode.failure";
|
||||
public const POCKETMINE_COMMAND_GAMEMODE_UNKNOWN = "pocketmine.command.gamemode.unknown";
|
||||
public const POCKETMINE_COMMAND_GC_CHUNKS = "pocketmine.command.gc.chunks";
|
||||
public const POCKETMINE_COMMAND_GC_CYCLES = "pocketmine.command.gc.cycles";
|
||||
public const POCKETMINE_COMMAND_GC_DESCRIPTION = "pocketmine.command.gc.description";
|
||||
@ -254,11 +259,15 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_COMMAND_GIVE_DESCRIPTION = "pocketmine.command.give.description";
|
||||
public const POCKETMINE_COMMAND_GIVE_USAGE = "pocketmine.command.give.usage";
|
||||
public const POCKETMINE_COMMAND_HELP_DESCRIPTION = "pocketmine.command.help.description";
|
||||
public const POCKETMINE_COMMAND_HELP_SPECIFICCOMMAND_DESCRIPTION = "pocketmine.command.help.specificCommand.description";
|
||||
public const POCKETMINE_COMMAND_HELP_SPECIFICCOMMAND_HEADER = "pocketmine.command.help.specificCommand.header";
|
||||
public const POCKETMINE_COMMAND_HELP_SPECIFICCOMMAND_USAGE = "pocketmine.command.help.specificCommand.usage";
|
||||
public const POCKETMINE_COMMAND_KICK_DESCRIPTION = "pocketmine.command.kick.description";
|
||||
public const POCKETMINE_COMMAND_KILL_DESCRIPTION = "pocketmine.command.kill.description";
|
||||
public const POCKETMINE_COMMAND_KILL_USAGE = "pocketmine.command.kill.usage";
|
||||
public const POCKETMINE_COMMAND_LIST_DESCRIPTION = "pocketmine.command.list.description";
|
||||
public const POCKETMINE_COMMAND_ME_DESCRIPTION = "pocketmine.command.me.description";
|
||||
public const POCKETMINE_COMMAND_NOTFOUND = "pocketmine.command.notFound";
|
||||
public const POCKETMINE_COMMAND_OP_DESCRIPTION = "pocketmine.command.op.description";
|
||||
public const POCKETMINE_COMMAND_PARTICLE_DESCRIPTION = "pocketmine.command.particle.description";
|
||||
public const POCKETMINE_COMMAND_PARTICLE_USAGE = "pocketmine.command.particle.usage";
|
||||
@ -324,33 +333,50 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_LEVEL_AMBIGUOUSFORMAT = "pocketmine.level.ambiguousFormat";
|
||||
public const POCKETMINE_LEVEL_BACKGROUNDGENERATION = "pocketmine.level.backgroundGeneration";
|
||||
public const POCKETMINE_LEVEL_BADDEFAULTFORMAT = "pocketmine.level.badDefaultFormat";
|
||||
public const POCKETMINE_LEVEL_CONVERSION_FINISH = "pocketmine.level.conversion.finish";
|
||||
public const POCKETMINE_LEVEL_CONVERSION_START = "pocketmine.level.conversion.start";
|
||||
public const POCKETMINE_LEVEL_CORRUPTED = "pocketmine.level.corrupted";
|
||||
public const POCKETMINE_LEVEL_DEFAULTERROR = "pocketmine.level.defaultError";
|
||||
public const POCKETMINE_LEVEL_GENERATIONERROR = "pocketmine.level.generationError";
|
||||
public const POCKETMINE_LEVEL_INVALIDGENERATOROPTIONS = "pocketmine.level.invalidGeneratorOptions";
|
||||
public const POCKETMINE_LEVEL_LOADERROR = "pocketmine.level.loadError";
|
||||
public const POCKETMINE_LEVEL_NOTFOUND = "pocketmine.level.notFound";
|
||||
public const POCKETMINE_LEVEL_PREPARING = "pocketmine.level.preparing";
|
||||
public const POCKETMINE_LEVEL_SPAWNTERRAINGENERATIONPROGRESS = "pocketmine.level.spawnTerrainGenerationProgress";
|
||||
public const POCKETMINE_LEVEL_UNKNOWNFORMAT = "pocketmine.level.unknownFormat";
|
||||
public const POCKETMINE_LEVEL_UNKNOWNGENERATOR = "pocketmine.level.unknownGenerator";
|
||||
public const POCKETMINE_LEVEL_UNLOADING = "pocketmine.level.unloading";
|
||||
public const POCKETMINE_LEVEL_UNSUPPORTEDFORMAT = "pocketmine.level.unsupportedFormat";
|
||||
public const POCKETMINE_PLAYER_INVALIDENTITY = "pocketmine.player.invalidEntity";
|
||||
public const POCKETMINE_PLAYER_INVALIDMOVE = "pocketmine.player.invalidMove";
|
||||
public const POCKETMINE_PLAYER_LOGIN = "pocketmine.player.logIn";
|
||||
public const POCKETMINE_PLAYER_LOGOUT = "pocketmine.player.logOut";
|
||||
public const POCKETMINE_PLUGIN_ALIASERROR = "pocketmine.plugin.aliasError";
|
||||
public const POCKETMINE_PLUGIN_AMBIGUOUSMINAPI = "pocketmine.plugin.ambiguousMinAPI";
|
||||
public const POCKETMINE_PLUGIN_BADDATAFOLDER = "pocketmine.plugin.badDataFolder";
|
||||
public const POCKETMINE_PLUGIN_CIRCULARDEPENDENCY = "pocketmine.plugin.circularDependency";
|
||||
public const POCKETMINE_PLUGIN_COMMANDERROR = "pocketmine.plugin.commandError";
|
||||
public const POCKETMINE_PLUGIN_DEPRECATEDEVENT = "pocketmine.plugin.deprecatedEvent";
|
||||
public const POCKETMINE_PLUGIN_DISABLE = "pocketmine.plugin.disable";
|
||||
public const POCKETMINE_PLUGIN_DISALLOWEDBYBLACKLIST = "pocketmine.plugin.disallowedByBlacklist";
|
||||
public const POCKETMINE_PLUGIN_DISALLOWEDBYWHITELIST = "pocketmine.plugin.disallowedByWhitelist";
|
||||
public const POCKETMINE_PLUGIN_DUPLICATEERROR = "pocketmine.plugin.duplicateError";
|
||||
public const POCKETMINE_PLUGIN_EMPTYEXTENSIONVERSIONCONSTRAINT = "pocketmine.plugin.emptyExtensionVersionConstraint";
|
||||
public const POCKETMINE_PLUGIN_ENABLE = "pocketmine.plugin.enable";
|
||||
public const POCKETMINE_PLUGIN_FILEERROR = "pocketmine.plugin.fileError";
|
||||
public const POCKETMINE_PLUGIN_EXTENSIONNOTLOADED = "pocketmine.plugin.extensionNotLoaded";
|
||||
public const POCKETMINE_PLUGIN_GENERICLOADERROR = "pocketmine.plugin.genericLoadError";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEAPI = "pocketmine.plugin.incompatibleAPI";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEEXTENSIONVERSION = "pocketmine.plugin.incompatibleExtensionVersion";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEOS = "pocketmine.plugin.incompatibleOS";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEPHPVERSION = "pocketmine.plugin.incompatiblePhpVersion";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEPROTOCOL = "pocketmine.plugin.incompatibleProtocol";
|
||||
public const POCKETMINE_PLUGIN_INVALIDAPI = "pocketmine.plugin.invalidAPI";
|
||||
public const POCKETMINE_PLUGIN_INVALIDEXTENSIONVERSIONCONSTRAINT = "pocketmine.plugin.invalidExtensionVersionConstraint";
|
||||
public const POCKETMINE_PLUGIN_INVALIDMANIFEST = "pocketmine.plugin.invalidManifest";
|
||||
public const POCKETMINE_PLUGIN_LOAD = "pocketmine.plugin.load";
|
||||
public const POCKETMINE_PLUGIN_LOADERROR = "pocketmine.plugin.loadError";
|
||||
public const POCKETMINE_PLUGIN_MAINCLASSNOTFOUND = "pocketmine.plugin.mainClassNotFound";
|
||||
public const POCKETMINE_PLUGIN_MAINCLASSWRONGTYPE = "pocketmine.plugin.mainClassWrongType";
|
||||
public const POCKETMINE_PLUGIN_RESTRICTEDNAME = "pocketmine.plugin.restrictedName";
|
||||
public const POCKETMINE_PLUGIN_SPACESDISCOURAGED = "pocketmine.plugin.spacesDiscouraged";
|
||||
public const POCKETMINE_PLUGIN_UNKNOWNDEPENDENCY = "pocketmine.plugin.unknownDependency";
|
||||
|
@ -58,7 +58,7 @@ class ChunkRequestTask extends AsyncTask{
|
||||
public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor, ?\Closure $onError = null){
|
||||
$this->compressor = $compressor;
|
||||
|
||||
$this->chunk = FastChunkSerializer::serializeWithoutLight($chunk);
|
||||
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
|
||||
$this->chunkX = $chunkX;
|
||||
$this->chunkZ = $chunkZ;
|
||||
$this->tiles = ChunkSerializer::serializeTiles($chunk);
|
||||
@ -68,7 +68,7 @@ class ChunkRequestTask extends AsyncTask{
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
$chunk = FastChunkSerializer::deserialize($this->chunk);
|
||||
$chunk = FastChunkSerializer::deserializeTerrain($this->chunk);
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk);
|
||||
$encoderContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, RuntimeBlockMapping::getInstance(), $encoderContext, $this->tiles);
|
||||
|
@ -234,6 +234,10 @@ class NetworkSession{
|
||||
return;
|
||||
}
|
||||
$this->player = $player;
|
||||
if(!$this->server->addOnlinePlayer($player)){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invManager = new InventoryManager($this->player, $this);
|
||||
|
||||
$effectManager = $this->player->getEffects();
|
||||
@ -752,10 +756,16 @@ class NetworkSession{
|
||||
$this->sendDataPacket(SetSpawnPositionPacket::playerSpawn($x, $y, $z, DimensionIds::OVERWORLD, $x, $y, $z));
|
||||
}
|
||||
|
||||
public function syncWorldSpawnPoint(Position $newSpawn) : void{
|
||||
$this->sendDataPacket(SetSpawnPositionPacket::worldSpawn($newSpawn->getFloorX(), $newSpawn->getFloorY(), $newSpawn->getFloorZ(), DimensionIds::OVERWORLD));
|
||||
}
|
||||
|
||||
public function syncGameMode(GameMode $mode, bool $isRollback = false) : void{
|
||||
$this->sendDataPacket(SetPlayerGameTypePacket::create(TypeConverter::getInstance()->coreGameModeToProtocol($mode)));
|
||||
$this->syncAdventureSettings($this->player);
|
||||
if(!$isRollback){
|
||||
if($this->player !== null){
|
||||
$this->syncAdventureSettings($this->player);
|
||||
}
|
||||
if(!$isRollback && $this->invManager !== null){
|
||||
$this->invManager->syncCreative();
|
||||
}
|
||||
}
|
||||
@ -933,8 +943,8 @@ class NetworkSession{
|
||||
$world = $this->player->getWorld();
|
||||
$this->syncWorldTime($world->getTime());
|
||||
$this->syncWorldDifficulty($world->getDifficulty());
|
||||
$this->syncWorldSpawnPoint($world->getSpawnLocation());
|
||||
//TODO: weather needs to be synced here (when implemented)
|
||||
//TODO: world spawn needs to be synced here
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\convert;
|
||||
|
||||
use pocketmine\data\bedrock\LegacyItemIdToStringIdMap;
|
||||
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
@ -76,10 +77,7 @@ final class ItemTranslator{
|
||||
if($legacyStringToIntMapRaw === false){
|
||||
throw new AssumptionFailedError("Missing required resource file");
|
||||
}
|
||||
$legacyStringToIntMap = json_decode($legacyStringToIntMapRaw, true);
|
||||
if(!is_array($legacyStringToIntMap)){
|
||||
throw new AssumptionFailedError("Invalid mapping table format");
|
||||
}
|
||||
$legacyStringToIntMap = LegacyItemIdToStringIdMap::getInstance();
|
||||
|
||||
/** @phpstan-var array<string, int> $simpleMappings */
|
||||
$simpleMappings = [];
|
||||
@ -87,13 +85,14 @@ final class ItemTranslator{
|
||||
if(!is_string($oldId) || !is_string($newId)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
if(!isset($legacyStringToIntMap[$oldId])){
|
||||
$intId = $legacyStringToIntMap->stringToLegacy($oldId);
|
||||
if($intId === null){
|
||||
//new item without a fixed legacy ID - we can't handle this right now
|
||||
continue;
|
||||
}
|
||||
$simpleMappings[$newId] = $legacyStringToIntMap[$oldId];
|
||||
$simpleMappings[$newId] = $intId;
|
||||
}
|
||||
foreach($legacyStringToIntMap as $stringId => $intId){
|
||||
foreach($legacyStringToIntMap->getStringToLegacyMap() as $stringId => $intId){
|
||||
if(isset($simpleMappings[$stringId])){
|
||||
throw new \UnexpectedValueException("Old ID $stringId collides with new ID");
|
||||
}
|
||||
@ -110,7 +109,12 @@ final class ItemTranslator{
|
||||
if(!is_numeric($meta) || !is_string($newId)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
$complexMappings[$newId] = [$legacyStringToIntMap[$oldId], (int) $meta];
|
||||
$intId = $legacyStringToIntMap->stringToLegacy($oldId);
|
||||
if($intId === null){
|
||||
//new item without a fixed legacy ID - we can't handle this right now
|
||||
continue;
|
||||
}
|
||||
$complexMappings[$newId] = [$intId, (int) $meta];
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,10 +146,10 @@ final class ItemTranslator{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
* @return int[]|null
|
||||
* @phpstan-return array{int, int}|null
|
||||
*/
|
||||
public function toNetworkId(int $internalId, int $internalMeta) : array{
|
||||
public function toNetworkIdQuiet(int $internalId, int $internalMeta) : ?array{
|
||||
if($internalMeta === -1){
|
||||
$internalMeta = 0x7fff;
|
||||
}
|
||||
@ -156,7 +160,16 @@ final class ItemTranslator{
|
||||
return [$this->simpleCoreToNetMapping[$internalId], $internalMeta];
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
public function toNetworkId(int $internalId, int $internalMeta) : array{
|
||||
return $this->toNetworkIdQuiet($internalId, $internalMeta) ??
|
||||
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
|
35
src/network/mcpe/convert/TypeConversionException.php
Normal file
35
src/network/mcpe/convert/TypeConversionException.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?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\network\mcpe\convert;
|
||||
|
||||
/**
|
||||
* Thrown by TypeConverter if a problem occurred during converting of network types to PM core types (e.g. invalid item
|
||||
* ID, invalid NBT, etc).
|
||||
*/
|
||||
final class TypeConversionException extends \RuntimeException{
|
||||
|
||||
public static function wrap(\Throwable $previous, ?string $prefix = null) : self{
|
||||
return new self(($prefix !== null ? $prefix . ": " : "") . $previous->getMessage(), 0, $previous);
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ use pocketmine\item\Durable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\ItemIds;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\InventoryManager;
|
||||
@ -57,6 +58,7 @@ class TypeConverter{
|
||||
|
||||
private const DAMAGE_TAG = "Damage"; //TAG_Int
|
||||
private const DAMAGE_TAG_CONFLICT_RESOLUTION = "___Damage_ProtocolCollisionResolution___";
|
||||
private const PM_ID_TAG = "___Id___";
|
||||
private const PM_META_TAG = "___Meta___";
|
||||
|
||||
/** @var int */
|
||||
@ -142,26 +144,40 @@ class TypeConverter{
|
||||
}
|
||||
|
||||
$isBlockItem = $itemStack->getId() < 256;
|
||||
if($itemStack instanceof Durable and $itemStack->getDamage() > 0){
|
||||
if($nbt !== null){
|
||||
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
$nbt->setTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION, $existing);
|
||||
}
|
||||
}else{
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
|
||||
}elseif($isBlockItem && $itemStack->getMeta() !== 0){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
|
||||
$idMeta = ItemTranslator::getInstance()->toNetworkIdQuiet($itemStack->getId(), $itemStack->getMeta());
|
||||
if($idMeta === null){
|
||||
//Display unmapped items as INFO_UPDATE, but stick something in their NBT to make sure they don't stack with
|
||||
//other unmapped items.
|
||||
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId(ItemIds::INFO_UPDATE, 0);
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_ID_TAG, $itemStack->getId());
|
||||
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
|
||||
}else{
|
||||
[$id, $meta] = $idMeta;
|
||||
|
||||
if($itemStack instanceof Durable and $itemStack->getDamage() > 0){
|
||||
if($nbt !== null){
|
||||
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
$nbt->setTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION, $existing);
|
||||
}
|
||||
}else{
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
|
||||
}elseif($isBlockItem && $itemStack->getMeta() !== 0){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
|
||||
}
|
||||
}
|
||||
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId($itemStack->getId(), $itemStack->getMeta());
|
||||
|
||||
$blockRuntimeId = 0;
|
||||
if($isBlockItem){
|
||||
@ -183,6 +199,9 @@ class TypeConverter{
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TypeConversionException
|
||||
*/
|
||||
public function netItemStackToCore(ItemStack $itemStack) : Item{
|
||||
if($itemStack->getId() === 0){
|
||||
return ItemFactory::getInstance()->get(ItemIds::AIR, 0, 0);
|
||||
@ -193,14 +212,16 @@ class TypeConverter{
|
||||
|
||||
if($compound !== null){
|
||||
$compound = clone $compound;
|
||||
if(($idTag = $compound->getTag(self::PM_ID_TAG)) instanceof IntTag){
|
||||
$id = $idTag->getValue();
|
||||
$compound->removeTag(self::PM_ID_TAG);
|
||||
}
|
||||
if(($damageTag = $compound->getTag(self::DAMAGE_TAG)) instanceof IntTag){
|
||||
$meta = $damageTag->getValue();
|
||||
$compound->removeTag(self::DAMAGE_TAG);
|
||||
if(($conflicted = $compound->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$compound->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$compound->setTag(self::DAMAGE_TAG, $conflicted);
|
||||
}elseif($compound->count() === 0){
|
||||
$compound = null;
|
||||
}
|
||||
}elseif(($metaTag = $compound->getTag(self::PM_META_TAG)) instanceof IntTag){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
@ -208,18 +229,22 @@ class TypeConverter{
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
$meta = $metaTag->getValue();
|
||||
$compound->removeTag(self::PM_META_TAG);
|
||||
if($compound->count() === 0){
|
||||
$compound = null;
|
||||
}
|
||||
}
|
||||
if($compound->count() === 0){
|
||||
$compound = null;
|
||||
}
|
||||
}
|
||||
|
||||
return ItemFactory::getInstance()->get(
|
||||
$id,
|
||||
$meta,
|
||||
$itemStack->getCount(),
|
||||
$compound
|
||||
);
|
||||
try{
|
||||
return ItemFactory::getInstance()->get(
|
||||
$id,
|
||||
$meta,
|
||||
$itemStack->getCount(),
|
||||
$compound
|
||||
);
|
||||
}catch(NbtException $e){
|
||||
throw TypeConversionException::wrap($e, "Bad itemstack NBT data");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,15 +264,23 @@ class TypeConverter{
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws TypeConversionException
|
||||
*/
|
||||
public function createInventoryAction(NetworkInventoryAction $action, Player $player, InventoryManager $inventoryManager) : ?InventoryAction{
|
||||
if($action->oldItem->getItemStack()->equals($action->newItem->getItemStack())){
|
||||
//filter out useless noise in 1.13
|
||||
return null;
|
||||
}
|
||||
$old = $this->netItemStackToCore($action->oldItem->getItemStack());
|
||||
$new = $this->netItemStackToCore($action->newItem->getItemStack());
|
||||
try{
|
||||
$old = $this->netItemStackToCore($action->oldItem->getItemStack());
|
||||
}catch(TypeConversionException $e){
|
||||
throw TypeConversionException::wrap($e, "Inventory action: oldItem");
|
||||
}
|
||||
try{
|
||||
$new = $this->netItemStackToCore($action->newItem->getItemStack());
|
||||
}catch(TypeConversionException $e){
|
||||
throw TypeConversionException::wrap($e, "Inventory action: newItem");
|
||||
}
|
||||
switch($action->sourceType){
|
||||
case NetworkInventoryAction::SOURCE_CONTAINER:
|
||||
if($action->windowId === ContainerIds::UI and $action->inventorySlot > 0){
|
||||
@ -273,7 +306,7 @@ class TypeConverter{
|
||||
fn(Inventory $i) => $i instanceof LoomInventory);
|
||||
}
|
||||
if($mapped === null){
|
||||
throw new \UnexpectedValueException("Unmatched UI inventory slot offset $pSlot");
|
||||
throw new TypeConversionException("Unmatched UI inventory slot offset $pSlot");
|
||||
}
|
||||
[$slot, $window] = $mapped;
|
||||
}else{
|
||||
@ -284,10 +317,10 @@ class TypeConverter{
|
||||
return new SlotChangeAction($window, $slot, $old, $new);
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException("No open container with window ID $action->windowId");
|
||||
throw new TypeConversionException("No open container with window ID $action->windowId");
|
||||
case NetworkInventoryAction::SOURCE_WORLD:
|
||||
if($action->inventorySlot !== NetworkInventoryAction::ACTION_MAGIC_SLOT_DROP_ITEM){
|
||||
throw new \UnexpectedValueException("Only expecting drop-item world actions from the client!");
|
||||
throw new TypeConversionException("Only expecting drop-item world actions from the client!");
|
||||
}
|
||||
|
||||
return new DropItemAction($new);
|
||||
@ -298,7 +331,7 @@ class TypeConverter{
|
||||
case NetworkInventoryAction::ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM:
|
||||
return new CreateItemAction($old);
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unexpected creative action type $action->inventorySlot");
|
||||
throw new TypeConversionException("Unexpected creative action type $action->inventorySlot");
|
||||
|
||||
}
|
||||
case NetworkInventoryAction::SOURCE_TODO:
|
||||
@ -310,9 +343,9 @@ class TypeConverter{
|
||||
}
|
||||
|
||||
//TODO: more stuff
|
||||
throw new \UnexpectedValueException("No open container with window ID $action->windowId");
|
||||
throw new TypeConversionException("No open container with window ID $action->windowId");
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown inventory source type $action->sourceType");
|
||||
throw new TypeConversionException("Unknown inventory source type $action->sourceType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\convert\TypeConversionException;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\InventoryManager;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
@ -271,8 +272,8 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if($action !== null){
|
||||
$actions[] = $action;
|
||||
}
|
||||
}catch(\UnexpectedValueException $e){
|
||||
$this->session->getLogger()->debug("Unhandled inventory action: " . $e->getMessage());
|
||||
}catch(TypeConversionException $e){
|
||||
$this->session->getLogger()->debug("Error unpacking inventory action: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ use pocketmine\network\Network;
|
||||
use pocketmine\network\PacketHandlingException;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use raklib\protocol\EncapsulatedPacket;
|
||||
use raklib\protocol\PacketReliability;
|
||||
@ -51,7 +50,6 @@ use function mt_rand;
|
||||
use function random_bytes;
|
||||
use function rtrim;
|
||||
use function substr;
|
||||
use const PTHREADS_INHERIT_CONSTANTS;
|
||||
|
||||
class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
/**
|
||||
@ -122,7 +120,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
while($this->eventReceiver->handle($this));
|
||||
});
|
||||
$this->server->getLogger()->debug("Waiting for RakLib to start...");
|
||||
$this->rakLib->startAndWait(PTHREADS_INHERIT_CONSTANTS); //HACK: MainLogger needs constants for exception logging
|
||||
$this->rakLib->startAndWait();
|
||||
$this->server->getLogger()->debug("RakLib booted successfully");
|
||||
}
|
||||
|
||||
@ -193,10 +191,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$logger->error("Bad packet (error ID $errorId): " . $e->getMessage());
|
||||
|
||||
//intentionally doesn't use logException, we don't want spammy packet error traces to appear in release mode
|
||||
$logger->debug("Origin: " . Filesystem::cleanPath($e->getFile()) . "(" . $e->getLine() . ")");
|
||||
foreach(Utils::printableTrace($e->getTrace()) as $frame){
|
||||
$logger->debug($frame);
|
||||
}
|
||||
$logger->debug(implode("\n", Utils::printableExceptionInfo($e)));
|
||||
$session->disconnect("Packet processing error (Error ID: $errorId)");
|
||||
$this->interface->blockAddress($address, 5);
|
||||
}
|
||||
|
@ -32,10 +32,8 @@ use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\PalettedBlockArray;
|
||||
use pocketmine\world\format\SubChunk;
|
||||
use function count;
|
||||
use function str_repeat;
|
||||
|
||||
final class ChunkSerializer{
|
||||
|
||||
@ -48,7 +46,7 @@ final class ChunkSerializer{
|
||||
* Chunks are sent in a stack, so every chunk below the top non-empty one must be sent.
|
||||
*/
|
||||
public static function getSubChunkCount(Chunk $chunk) : int{
|
||||
for($count = $chunk->getSubChunks()->count(); $count > 0; --$count){
|
||||
for($count = count($chunk->getSubChunks()); $count > 0; --$count){
|
||||
if($chunk->getSubChunk($count - 1)->isEmptyFast()){
|
||||
continue;
|
||||
}
|
||||
@ -83,23 +81,18 @@ final class ChunkSerializer{
|
||||
$stream->putByte(count($layers));
|
||||
|
||||
foreach($layers as $blocks){
|
||||
if($blocks->getBitsPerBlock() === 0){
|
||||
//TODO: we use these in memory, but the game doesn't support them yet
|
||||
//polyfill them with 1-bpb instead
|
||||
$bitsPerBlock = 1;
|
||||
$words = str_repeat("\x00", PalettedBlockArray::getExpectedWordArraySize(1));
|
||||
}else{
|
||||
$bitsPerBlock = $blocks->getBitsPerBlock();
|
||||
$words = $blocks->getWordArray();
|
||||
}
|
||||
$bitsPerBlock = $blocks->getBitsPerBlock();
|
||||
$words = $blocks->getWordArray();
|
||||
$stream->putByte(($bitsPerBlock << 1) | ($persistentBlockStates ? 0 : 1));
|
||||
$stream->put($words);
|
||||
$palette = $blocks->getPalette();
|
||||
|
||||
//these LSHIFT by 1 uvarints are optimizations: the client expects zigzag varints here
|
||||
//but since we know they are always unsigned, we can avoid the extra fcall overhead of
|
||||
//zigzag and just shift directly.
|
||||
$stream->putUnsignedVarInt(count($palette) << 1); //yes, this is intentionally zigzag
|
||||
if($bitsPerBlock !== 0){
|
||||
//these LSHIFT by 1 uvarints are optimizations: the client expects zigzag varints here
|
||||
//but since we know they are always unsigned, we can avoid the extra fcall overhead of
|
||||
//zigzag and just shift directly.
|
||||
$stream->putUnsignedVarInt(count($palette) << 1); //yes, this is intentionally zigzag
|
||||
}
|
||||
if($persistentBlockStates){
|
||||
$nbtSerializer = new NetworkNbtSerializer();
|
||||
foreach($palette as $p){
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\network\upnp;
|
||||
|
||||
use pocketmine\network\NetworkInterface;
|
||||
use pocketmine\utils\Internet;
|
||||
use pocketmine\utils\InternetException;
|
||||
|
||||
final class UPnPNetworkInterface implements NetworkInterface{
|
||||
|
||||
@ -50,12 +51,12 @@ final class UPnPNetworkInterface implements NetworkInterface{
|
||||
|
||||
public function start() : void{
|
||||
$this->logger->info("Attempting to portforward...");
|
||||
$this->serviceURL = UPnP::getServiceUrl();
|
||||
|
||||
try{
|
||||
$this->serviceURL = UPnP::getServiceUrl();
|
||||
UPnP::portForward($this->serviceURL, Internet::getInternalIP(), $this->port, $this->port);
|
||||
$this->logger->info("Forwarded $this->ip:$this->port to external port $this->port");
|
||||
}catch(UPnPException $e){
|
||||
}catch(UPnPException | InternetException $e){
|
||||
$this->logger->error("UPnP portforward failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -51,65 +51,56 @@ abstract class DefaultPermissions{
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::BROADCAST_ADMIN, "Allows the user to receive administrative broadcasts"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::BROADCAST_USER, "Allows the user to receive user broadcasts"), [$everyoneRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_ADD, "Allows the user to add a player to the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_REMOVE, "Allows the user to remove a player from the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_RELOAD, "Allows the user to reload the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_ENABLE, "Allows the user to enable the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_DISABLE, "Allows the user to disable the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_LIST, "Allows the user to list all players on the server whitelist"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_BAN_PLAYER, "Allows the user to ban players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_BAN_IP, "Allows the user to ban IP addresses"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_BAN_LIST, "Allows the user to list banned players"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_UNBAN_PLAYER, "Allows the user to unban players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_UNBAN_IP, "Allows the user to unban IP addresses"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_BAN_PLAYER, "Allows the user to ban players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_CLEAR_OTHER, "Allows the user to clear inventory of other players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_CLEAR_SELF, "Allows the user to clear their own inventory"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_DEFAULTGAMEMODE, "Allows the user to change the default gamemode"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_DIFFICULTY, "Allows the user to change the game difficulty"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_DUMPMEMORY, "Allows the user to dump memory contents"), [$consoleRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_EFFECT, "Allows the user to give/take potion effects"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_ENCHANT, "Allows the user to enchant items"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_GAMEMODE, "Allows the user to change the gamemode of players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_GC, "Allows the user to fire garbage collection tasks"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_GIVE, "Allows the user to give items to players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_HELP, "Allows the user to view the help menu"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_KICK, "Allows the user to kick players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_KILL_OTHER, "Allows the user to kill other players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_KILL_SELF, "Allows the user to commit suicide"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_LIST, "Allows the user to list all online players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_ME, "Allows the user to perform a chat action"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_OP_GIVE, "Allows the user to give a player operator status"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_OP_TAKE, "Allows the user to take a player's operator status"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SAVE_ENABLE, "Allows the user to enable automatic saving"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_PARTICLE, "Allows the user to create particle effects"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_PLUGINS, "Allows the user to view the list of plugins"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SAVE_DISABLE, "Allows the user to disable automatic saving"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SAVE_ENABLE, "Allows the user to enable automatic saving"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SAVE_PERFORM, "Allows the user to perform a manual save"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SAY, "Allows the user to talk as the console"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SEED, "Allows the user to view the seed of the world"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SETWORLDSPAWN, "Allows the user to change the world spawn"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SPAWNPOINT, "Allows the user to change player's spawnpoint"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_STATUS, "Allows the user to view the server performance"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_STOP, "Allows the user to stop the server"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TELEPORT, "Allows the user to teleport players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TELL, "Allows the user to privately message another player"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIME_ADD, "Allows the user to fast-forward time"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIME_QUERY, "Allows the user query the time"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIME_SET, "Allows the user to change the time"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIME_START, "Allows the user to restart the time"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIME_STOP, "Allows the user to stop the time"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIME_QUERY, "Allows the user query the time"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_KILL_SELF, "Allows the user to commit suicide"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_KILL_OTHER, "Allows the user to kill other players"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_CLEAR_SELF, "Allows the user to clear their own inventory"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_CLEAR_OTHER, "Allows the user to clear inventory of other players"), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_ME, "Allows the user to perform a chat action"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TELL, "Allows the user to privately message another player"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SAY, "Allows the user to talk as the console"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_GIVE, "Allows the user to give items to players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_EFFECT, "Allows the user to give/take potion effects"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_ENCHANT, "Allows the user to enchant items"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_PARTICLE, "Allows the user to create particle effects"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TELEPORT, "Allows the user to teleport players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_KICK, "Allows the user to kick players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_STOP, "Allows the user to stop the server"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_LIST, "Allows the user to list all online players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_HELP, "Allows the user to view the help menu"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_PLUGINS, "Allows the user to view the list of plugins"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_VERSION, "Allows the user to view the version of the server"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_GAMEMODE, "Allows the user to change the gamemode of players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_DEFAULTGAMEMODE, "Allows the user to change the default gamemode"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SEED, "Allows the user to view the seed of the world"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_STATUS, "Allows the user to view the server performance"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_GC, "Allows the user to fire garbage collection tasks"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_DUMPMEMORY, "Allows the user to dump memory contents"), [$consoleRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIMINGS, "Allows the user to records timings for all plugin events"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SPAWNPOINT, "Allows the user to change player's spawnpoint"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_SETWORLDSPAWN, "Allows the user to change the world spawn"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TRANSFERSERVER, "Allows the user to transfer self to another server"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TIMINGS, "Allows the user to record timings to analyse server performance"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TITLE, "Allows the user to send a title to the specified player"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_DIFFICULTY, "Allows the user to change the game difficulty"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_TRANSFERSERVER, "Allows the user to transfer self to another server"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_UNBAN_IP, "Allows the user to unban IP addresses"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_UNBAN_PLAYER, "Allows the user to unban players"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_VERSION, "Allows the user to view the version of the server"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_ADD, "Allows the user to add a player to the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_DISABLE, "Allows the user to disable the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_ENABLE, "Allows the user to enable the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_LIST, "Allows the user to list all players on the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_RELOAD, "Allows the user to reload the server whitelist"), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(DefaultPermissionNames::COMMAND_WHITELIST_REMOVE, "Allows the user to remove a player from the server whitelist"), [$operatorRoot]);
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class PermissionParser{
|
||||
/**
|
||||
* @param bool|string $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws PermissionParserException
|
||||
*/
|
||||
public static function defaultFromString($value) : string{
|
||||
if(is_bool($value)){
|
||||
@ -70,7 +70,7 @@ class PermissionParser{
|
||||
return self::DEFAULT_STRING_MAP[$lower];
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Unknown permission default name \"$value\"");
|
||||
throw new PermissionParserException("Unknown permission default name \"$value\"");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,6 +79,7 @@ class PermissionParser{
|
||||
*
|
||||
* @return Permission[][]
|
||||
* @phpstan-return array<string, list<Permission>>
|
||||
* @throws PermissionParserException
|
||||
*/
|
||||
public static function loadPermissions(array $data, string $default = self::DEFAULT_FALSE) : array{
|
||||
$result = [];
|
||||
@ -89,7 +90,7 @@ class PermissionParser{
|
||||
}
|
||||
|
||||
if(isset($entry["children"])){
|
||||
throw new \InvalidArgumentException("Nested permission declarations are no longer supported. Declare each permission separately.");
|
||||
throw new PermissionParserException("Nested permission declarations are no longer supported. Declare each permission separately.");
|
||||
}
|
||||
|
||||
if(isset($entry["description"])){
|
||||
|
31
src/permission/PermissionParserException.php
Normal file
31
src/permission/PermissionParserException.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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\permission;
|
||||
|
||||
/**
|
||||
* Thrown by PermissionParser when it encounters data that it doesn't like.
|
||||
*/
|
||||
final class PermissionParserException extends \RuntimeException{
|
||||
|
||||
}
|
@ -63,7 +63,6 @@ use pocketmine\event\player\PlayerItemUseEvent;
|
||||
use pocketmine\event\player\PlayerJoinEvent;
|
||||
use pocketmine\event\player\PlayerJumpEvent;
|
||||
use pocketmine\event\player\PlayerKickEvent;
|
||||
use pocketmine\event\player\PlayerLoginEvent;
|
||||
use pocketmine\event\player\PlayerMoveEvent;
|
||||
use pocketmine\event\player\PlayerQuitEvent;
|
||||
use pocketmine\event\player\PlayerRespawnEvent;
|
||||
@ -133,7 +132,6 @@ use function max;
|
||||
use function microtime;
|
||||
use function min;
|
||||
use function preg_match;
|
||||
use function round;
|
||||
use function spl_object_id;
|
||||
use function sqrt;
|
||||
use function strlen;
|
||||
@ -277,27 +275,6 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->usedChunks[World::chunkHash($spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE)] = UsedChunkStatus::NEEDED();
|
||||
|
||||
parent::__construct($spawnLocation, $this->playerInfo->getSkin(), $namedtag);
|
||||
|
||||
$ev = new PlayerLoginEvent($this, "Plugin reason");
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->isConnected()){
|
||||
$this->disconnect($ev->getKickMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translate(KnownTranslationFactory::pocketmine_player_logIn(
|
||||
TextFormat::AQUA . $this->username . TextFormat::WHITE,
|
||||
$session->getIp(),
|
||||
(string) $session->getPort(),
|
||||
(string) $this->id,
|
||||
$this->getWorld()->getDisplayName(),
|
||||
(string) round($this->location->x, 4),
|
||||
(string) round($this->location->y, 4),
|
||||
(string) round($this->location->z, 4)
|
||||
)));
|
||||
|
||||
$this->server->addOnlinePlayer($this);
|
||||
}
|
||||
|
||||
protected function initHumanData(CompoundTag $nbt) : void{
|
||||
|
@ -38,10 +38,6 @@ use function dirname;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_string;
|
||||
use function mkdir;
|
||||
use function rtrim;
|
||||
use function stream_copy_to_stream;
|
||||
@ -171,45 +167,34 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_commandError($key, $this->getDescription()->getFullName())));
|
||||
continue;
|
||||
}
|
||||
if(is_array($data)){ //TODO: error out if it isn't
|
||||
$newCmd = new PluginCommand($key, $this, $this);
|
||||
if(isset($data["description"])){
|
||||
$newCmd->setDescription($data["description"]);
|
||||
}
|
||||
|
||||
if(isset($data["usage"])){
|
||||
$newCmd->setUsage($data["usage"]);
|
||||
}
|
||||
|
||||
if(isset($data["aliases"]) and is_array($data["aliases"])){
|
||||
$aliasList = [];
|
||||
foreach($data["aliases"] as $alias){
|
||||
if(strpos($alias, ":") !== false){
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_aliasError($alias, $this->getDescription()->getFullName())));
|
||||
continue;
|
||||
}
|
||||
$aliasList[] = $alias;
|
||||
}
|
||||
|
||||
$newCmd->setAliases($aliasList);
|
||||
}
|
||||
|
||||
if(isset($data["permission"])){
|
||||
if(is_bool($data["permission"])){
|
||||
$newCmd->setPermission($data["permission"] ? "true" : "false");
|
||||
}elseif(is_string($data["permission"])){
|
||||
$newCmd->setPermission($data["permission"]);
|
||||
}else{
|
||||
$this->logger->error("Permission must be a string, " . gettype($data["permission"]) . " given for command $key");
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($data["permission-message"])){
|
||||
$newCmd->setPermissionMessage($data["permission-message"]);
|
||||
}
|
||||
|
||||
$pluginCmds[] = $newCmd;
|
||||
$newCmd = new PluginCommand($key, $this, $this);
|
||||
if(($description = $data->getDescription()) !== null){
|
||||
$newCmd->setDescription($description);
|
||||
}
|
||||
|
||||
if(($usageMessage = $data->getUsageMessage()) !== null){
|
||||
$newCmd->setUsage($usageMessage);
|
||||
}
|
||||
|
||||
$aliasList = [];
|
||||
foreach($data->getAliases() as $alias){
|
||||
if(strpos($alias, ":") !== false){
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_aliasError($alias, $this->getDescription()->getFullName())));
|
||||
continue;
|
||||
}
|
||||
$aliasList[] = $alias;
|
||||
}
|
||||
|
||||
$newCmd->setAliases($aliasList);
|
||||
|
||||
$newCmd->setPermission($data->getPermission());
|
||||
|
||||
if(($permissionDeniedMessage = $data->getPermissionDeniedMessage()) !== null){
|
||||
$newCmd->setPermissionMessage($permissionDeniedMessage);
|
||||
}
|
||||
|
||||
$pluginCmds[] = $newCmd;
|
||||
}
|
||||
|
||||
if(count($pluginCmds) > 0){
|
||||
|
@ -25,9 +25,11 @@ namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\permission\Permission;
|
||||
use pocketmine\permission\PermissionParser;
|
||||
use pocketmine\permission\PermissionParserException;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function phpversion;
|
||||
use function preg_match;
|
||||
use function str_replace;
|
||||
@ -42,53 +44,46 @@ class PluginDescription{
|
||||
* @var mixed[]
|
||||
* @phpstan-var array<string, mixed>
|
||||
*/
|
||||
private $map;
|
||||
private array $map;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
/** @var string */
|
||||
private $main;
|
||||
private string $name;
|
||||
private string $main;
|
||||
private string $srcNamespacePrefix = "";
|
||||
/** @var string[] */
|
||||
private $api;
|
||||
private array $api;
|
||||
/** @var int[] */
|
||||
private $compatibleMcpeProtocols = [];
|
||||
private array $compatibleMcpeProtocols = [];
|
||||
/** @var string[] */
|
||||
private $compatibleOperatingSystems = [];
|
||||
private array $compatibleOperatingSystems = [];
|
||||
/**
|
||||
* @var string[][]
|
||||
* @phpstan-var array<string, list<string>>
|
||||
*/
|
||||
private $extensions = [];
|
||||
private array $extensions = [];
|
||||
/** @var string[] */
|
||||
private $depend = [];
|
||||
private array $depend = [];
|
||||
/** @var string[] */
|
||||
private $softDepend = [];
|
||||
private array $softDepend = [];
|
||||
/** @var string[] */
|
||||
private $loadBefore = [];
|
||||
/** @var string */
|
||||
private $version;
|
||||
private array $loadBefore = [];
|
||||
private string $version;
|
||||
/**
|
||||
* @var mixed[][]
|
||||
* @phpstan-var array<string, array<string, mixed>>
|
||||
* @var PluginDescriptionCommandEntry[]
|
||||
* @phpstan-var array<string, PluginDescriptionCommandEntry>
|
||||
*/
|
||||
private $commands = [];
|
||||
/** @var string */
|
||||
private $description = "";
|
||||
private array $commands = [];
|
||||
private string $description = "";
|
||||
/** @var string[] */
|
||||
private $authors = [];
|
||||
/** @var string */
|
||||
private $website = "";
|
||||
/** @var string */
|
||||
private $prefix = "";
|
||||
/** @var PluginEnableOrder */
|
||||
private $order;
|
||||
private array $authors = [];
|
||||
private string $website = "";
|
||||
private string $prefix = "";
|
||||
private PluginEnableOrder $order;
|
||||
|
||||
/**
|
||||
* @var Permission[][]
|
||||
* @phpstan-var array<string, list<Permission>>
|
||||
*/
|
||||
private $permissions = [];
|
||||
private array $permissions = [];
|
||||
|
||||
/**
|
||||
* @param string|mixed[] $yamlString
|
||||
@ -99,20 +94,20 @@ class PluginDescription{
|
||||
|
||||
/**
|
||||
* @param mixed[] $plugin
|
||||
* @throws PluginException
|
||||
* @throws PluginDescriptionParseException
|
||||
*/
|
||||
private function loadMap(array $plugin) : void{
|
||||
$this->map = $plugin;
|
||||
|
||||
$this->name = $plugin["name"];
|
||||
if(preg_match('/^[A-Za-z0-9 _.-]+$/', $this->name) === 0){
|
||||
throw new PluginException("Invalid Plugin name");
|
||||
throw new PluginDescriptionParseException("Invalid Plugin name");
|
||||
}
|
||||
$this->name = str_replace(" ", "_", $this->name);
|
||||
$this->version = (string) $plugin["version"];
|
||||
$this->main = $plugin["main"];
|
||||
if(stripos($this->main, "pocketmine\\") === 0){
|
||||
throw new PluginException("Invalid Plugin main, cannot start within the PocketMine namespace");
|
||||
throw new PluginDescriptionParseException("Invalid Plugin main, cannot start within the PocketMine namespace");
|
||||
}
|
||||
|
||||
$this->srcNamespacePrefix = $plugin["src-namespace-prefix"] ?? "";
|
||||
@ -122,7 +117,24 @@ class PluginDescription{
|
||||
$this->compatibleOperatingSystems = array_map("\strval", (array) ($plugin["os"] ?? []));
|
||||
|
||||
if(isset($plugin["commands"]) and is_array($plugin["commands"])){
|
||||
$this->commands = $plugin["commands"];
|
||||
foreach($plugin["commands"] as $commandName => $commandData){
|
||||
if(!is_string($commandName)){
|
||||
throw new PluginDescriptionParseException("Invalid Plugin commands, key must be the name of the command");
|
||||
}
|
||||
if(!is_array($commandData)){
|
||||
throw new PluginDescriptionParseException("Command $commandName has invalid properties");
|
||||
}
|
||||
if(!isset($commandData["permission"]) || !is_string($commandData["permission"])){
|
||||
throw new PluginDescriptionParseException("Command $commandName does not have a valid permission set");
|
||||
}
|
||||
$this->commands[$commandName] = new PluginDescriptionCommandEntry(
|
||||
$commandData["description"] ?? null,
|
||||
$commandData["usage"] ?? null,
|
||||
$commandData["aliases"] ?? [],
|
||||
$commandData["permission"],
|
||||
$commandData["permission-message"] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($plugin["depend"])){
|
||||
@ -153,7 +165,7 @@ class PluginDescription{
|
||||
if(isset($plugin["load"])){
|
||||
$order = PluginEnableOrder::fromString($plugin["load"]);
|
||||
if($order === null){
|
||||
throw new PluginException("Invalid Plugin \"load\"");
|
||||
throw new PluginDescriptionParseException("Invalid Plugin \"load\"");
|
||||
}
|
||||
$this->order = $order;
|
||||
}else{
|
||||
@ -175,7 +187,11 @@ class PluginDescription{
|
||||
}
|
||||
|
||||
if(isset($plugin["permissions"])){
|
||||
$this->permissions = PermissionParser::loadPermissions($plugin["permissions"]);
|
||||
try{
|
||||
$this->permissions = PermissionParser::loadPermissions($plugin["permissions"]);
|
||||
}catch(PermissionParserException $e){
|
||||
throw new PluginDescriptionParseException("Invalid Plugin \"permissions\": " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,8 +232,8 @@ class PluginDescription{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[][]
|
||||
* @phpstan-return array<string, array<string, mixed>>
|
||||
* @return PluginDescriptionCommandEntry[]
|
||||
* @phpstan-return array<string, PluginDescriptionCommandEntry>
|
||||
*/
|
||||
public function getCommands() : array{
|
||||
return $this->commands;
|
||||
|
53
src/plugin/PluginDescriptionCommandEntry.php
Normal file
53
src/plugin/PluginDescriptionCommandEntry.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\plugin;
|
||||
|
||||
final class PluginDescriptionCommandEntry{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function __construct(
|
||||
private ?string $description,
|
||||
private ?string $usageMessage,
|
||||
private array $aliases,
|
||||
private string $permission,
|
||||
private ?string $permissionDeniedMessage,
|
||||
){}
|
||||
|
||||
public function getDescription() : ?string{ return $this->description; }
|
||||
|
||||
public function getUsageMessage() : ?string{ return $this->usageMessage; }
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getAliases() : array{ return $this->aliases; }
|
||||
|
||||
public function getPermission() : string{ return $this->permission; }
|
||||
|
||||
public function getPermissionDeniedMessage() : ?string{ return $this->permissionDeniedMessage; }
|
||||
}
|
31
src/plugin/PluginDescriptionParseException.php
Normal file
31
src/plugin/PluginDescriptionParseException.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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\plugin;
|
||||
|
||||
/**
|
||||
* Thrown when invalid things are found in a PluginDescription during loading
|
||||
*/
|
||||
final class PluginDescriptionParseException extends PluginException{
|
||||
|
||||
}
|
@ -23,15 +23,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use Respect\Validation\Exceptions\NestedValidationException;
|
||||
use Respect\Validation\Rules\AllOf;
|
||||
use Respect\Validation\Rules\ArrayType;
|
||||
use Respect\Validation\Rules\Each;
|
||||
use Respect\Validation\Rules\In;
|
||||
use Respect\Validation\Rules\Key;
|
||||
use Respect\Validation\Rules\StringType;
|
||||
use Respect\Validation\Validator;
|
||||
use function array_flip;
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
|
||||
class PluginGraylist{
|
||||
|
||||
@ -70,17 +66,27 @@ class PluginGraylist{
|
||||
* @param mixed[] $array
|
||||
*/
|
||||
public static function fromArray(array $array) : PluginGraylist{
|
||||
$validator = new Validator(
|
||||
new Key("mode", new In(['whitelist', 'blacklist'], true), true),
|
||||
new Key("plugins", new AllOf(new ArrayType(), new Each(new StringType())), true)
|
||||
);
|
||||
$validator->setName('plugin_list.yml');
|
||||
try{
|
||||
$validator->assert($array);
|
||||
}catch(NestedValidationException $e){
|
||||
throw new \InvalidArgumentException($e->getFullMessage(), 0, $e);
|
||||
if(!isset($array["mode"]) || ($array["mode"] !== "whitelist" && $array["mode"] !== "blacklist")){
|
||||
throw new \InvalidArgumentException("\"mode\" must be set");
|
||||
}
|
||||
return new PluginGraylist($array["plugins"], $array["mode"] === 'whitelist');
|
||||
$isWhitelist = match($array["mode"]){
|
||||
"whitelist" => true,
|
||||
"blacklist" => false,
|
||||
default => throw new \InvalidArgumentException("\"mode\" must be either \"whitelist\" or \"blacklist\"")
|
||||
};
|
||||
$plugins = [];
|
||||
if(isset($array["plugins"])){
|
||||
if(!is_array($array["plugins"])){
|
||||
throw new \InvalidArgumentException("\"plugins\" must be an array");
|
||||
}
|
||||
foreach($array["plugins"] as $k => $v){
|
||||
if(!is_string($v) && !is_int($v) && !is_float($v)){
|
||||
throw new \InvalidArgumentException("\"plugins\" contains invalid element at position $k");
|
||||
}
|
||||
$plugins[] = (string) $v;
|
||||
}
|
||||
}
|
||||
return new PluginGraylist($plugins, $isWhitelist);
|
||||
}
|
||||
|
||||
/**
|
||||
|
42
src/plugin/PluginLoadTriageEntry.php
Normal file
42
src/plugin/PluginLoadTriageEntry.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class PluginLoadTriageEntry{
|
||||
|
||||
public function __construct(
|
||||
private string $file,
|
||||
private PluginLoader $loader,
|
||||
private PluginDescription $description
|
||||
){}
|
||||
|
||||
public function getFile() : string{ return $this->file; }
|
||||
|
||||
public function getLoader() : PluginLoader{ return $this->loader; }
|
||||
|
||||
public function getDescription() : PluginDescription{ return $this->description; }
|
||||
}
|
@ -40,6 +40,7 @@ interface PluginLoader{
|
||||
|
||||
/**
|
||||
* Gets the PluginDescription from the file
|
||||
* @throws PluginDescriptionParseException
|
||||
*/
|
||||
public function getPluginDescription(string $file) : ?PluginDescription;
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\event\EventPriority;
|
||||
use pocketmine\event\HandlerListManager;
|
||||
@ -32,6 +33,7 @@ use pocketmine\event\plugin\PluginDisableEvent;
|
||||
use pocketmine\event\plugin\PluginEnableEvent;
|
||||
use pocketmine\event\RegisteredListener;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\permission\DefaultPermissions;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
@ -40,6 +42,7 @@ use pocketmine\Server;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function array_intersect;
|
||||
use function array_merge;
|
||||
@ -58,6 +61,7 @@ use function is_subclass_of;
|
||||
use function iterator_to_array;
|
||||
use function mkdir;
|
||||
use function shuffle;
|
||||
use function sprintf;
|
||||
use function stripos;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
@ -127,6 +131,47 @@ class PluginManager{
|
||||
return Path::join(dirname($pluginPath), $pluginName);
|
||||
}
|
||||
|
||||
private function checkPluginLoadability(PluginDescription $description) : Translatable|string|null{
|
||||
$name = $description->getName();
|
||||
if(stripos($name, "pocketmine") !== false or stripos($name, "minecraft") !== false or stripos($name, "mojang") !== false){
|
||||
return KnownTranslationFactory::pocketmine_plugin_restrictedName();
|
||||
}
|
||||
|
||||
foreach($description->getCompatibleApis() as $api){
|
||||
if(!VersionString::isValidBaseVersion($api)){
|
||||
return KnownTranslationFactory::pocketmine_plugin_invalidAPI($api);
|
||||
}
|
||||
}
|
||||
|
||||
if(!ApiVersion::isCompatible($this->server->getApiVersion(), $description->getCompatibleApis())){
|
||||
return KnownTranslationFactory::pocketmine_plugin_incompatibleAPI(implode(", ", $description->getCompatibleApis()));
|
||||
}
|
||||
|
||||
$ambiguousVersions = ApiVersion::checkAmbiguousVersions($description->getCompatibleApis());
|
||||
if(count($ambiguousVersions) > 0){
|
||||
return KnownTranslationFactory::pocketmine_plugin_ambiguousMinAPI(implode(", ", $ambiguousVersions));
|
||||
}
|
||||
|
||||
if(count($description->getCompatibleOperatingSystems()) > 0 and !in_array(Utils::getOS(), $description->getCompatibleOperatingSystems(), true)) {
|
||||
return KnownTranslationFactory::pocketmine_plugin_incompatibleOS(implode(", ", $description->getCompatibleOperatingSystems()));
|
||||
}
|
||||
|
||||
if(count($pluginMcpeProtocols = $description->getCompatibleMcpeProtocols()) > 0){
|
||||
$serverMcpeProtocols = [ProtocolInfo::CURRENT_PROTOCOL];
|
||||
if(count(array_intersect($pluginMcpeProtocols, $serverMcpeProtocols)) === 0){
|
||||
return KnownTranslationFactory::pocketmine_plugin_incompatibleProtocol(implode(", ", $pluginMcpeProtocols));
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
$description->checkRequiredExtensions();
|
||||
}catch(PluginException $ex){
|
||||
return $ex->getMessage();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PluginLoader[] $loaders
|
||||
*/
|
||||
@ -135,73 +180,7 @@ class PluginManager{
|
||||
if($loader->canLoadPlugin($path)){
|
||||
$description = $loader->getPluginDescription($path);
|
||||
if($description instanceof PluginDescription){
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_load($description->getFullName())));
|
||||
try{
|
||||
$description->checkRequiredExtensions();
|
||||
}catch(PluginException $ex){
|
||||
$this->server->getLogger()->error($ex->getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
$dataFolder = $this->getDataDirectory($path, $description->getName());
|
||||
if(file_exists($dataFolder) and !is_dir($dataFolder)){
|
||||
$this->server->getLogger()->error("Projected dataFolder '" . $dataFolder . "' for " . $description->getName() . " exists and is not a directory");
|
||||
return null;
|
||||
}
|
||||
if(!file_exists($dataFolder)){
|
||||
mkdir($dataFolder, 0777, true);
|
||||
}
|
||||
|
||||
$prefixed = $loader->getAccessProtocol() . $path;
|
||||
$loader->loadPlugin($prefixed);
|
||||
|
||||
$mainClass = $description->getMain();
|
||||
if(!class_exists($mainClass, true)){
|
||||
$this->server->getLogger()->error("Main class for plugin " . $description->getName() . " not found");
|
||||
return null;
|
||||
}
|
||||
if(!is_a($mainClass, Plugin::class, true)){
|
||||
$this->server->getLogger()->error("Main class for plugin " . $description->getName() . " is not an instance of " . Plugin::class);
|
||||
return null;
|
||||
}
|
||||
|
||||
$permManager = PermissionManager::getInstance();
|
||||
$opRoot = $permManager->getPermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
$everyoneRoot = $permManager->getPermission(DefaultPermissions::ROOT_USER);
|
||||
foreach($description->getPermissions() as $default => $perms){
|
||||
foreach($perms as $perm){
|
||||
$permManager->addPermission($perm);
|
||||
switch($default){
|
||||
case PermissionParser::DEFAULT_TRUE:
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_OP:
|
||||
$opRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_NOT_OP:
|
||||
//TODO: I don't think anyone uses this, and it currently relies on some magic inside PermissibleBase
|
||||
//to ensure that the operator override actually applies.
|
||||
//Explore getting rid of this.
|
||||
//The following grants this permission to anyone who has the "everyone" root permission.
|
||||
//However, if the operator root node (which has higher priority) is present, the
|
||||
//permission will be denied instead.
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
$opRoot->addChild($perm->getName(), false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Plugin $plugin
|
||||
* @see Plugin::__construct()
|
||||
*/
|
||||
$plugin = new $mainClass($loader, $this->server, $description, $dataFolder, $prefixed, new DiskResourceProvider($prefixed . "/resources/"));
|
||||
$this->plugins[$plugin->getDescription()->getName()] = $plugin;
|
||||
|
||||
return $plugin;
|
||||
$this->internalLoadPlugin($path, $loader, $description);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,6 +188,80 @@ class PluginManager{
|
||||
return null;
|
||||
}
|
||||
|
||||
private function internalLoadPlugin(string $path, PluginLoader $loader, PluginDescription $description) : ?Plugin{
|
||||
$language = $this->server->getLanguage();
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_load($description->getFullName())));
|
||||
|
||||
$dataFolder = $this->getDataDirectory($path, $description->getName());
|
||||
if(file_exists($dataFolder) and !is_dir($dataFolder)){
|
||||
$this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_badDataFolder($dataFolder)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
if(!file_exists($dataFolder)){
|
||||
mkdir($dataFolder, 0777, true);
|
||||
}
|
||||
|
||||
$prefixed = $loader->getAccessProtocol() . $path;
|
||||
$loader->loadPlugin($prefixed);
|
||||
|
||||
$mainClass = $description->getMain();
|
||||
if(!class_exists($mainClass, true)){
|
||||
$this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_mainClassNotFound()
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
if(!is_a($mainClass, Plugin::class, true)){
|
||||
$this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_mainClassWrongType(Plugin::class)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
|
||||
$permManager = PermissionManager::getInstance();
|
||||
$opRoot = $permManager->getPermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
$everyoneRoot = $permManager->getPermission(DefaultPermissions::ROOT_USER);
|
||||
foreach($description->getPermissions() as $default => $perms){
|
||||
foreach($perms as $perm){
|
||||
$permManager->addPermission($perm);
|
||||
switch($default){
|
||||
case PermissionParser::DEFAULT_TRUE:
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_OP:
|
||||
$opRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_NOT_OP:
|
||||
//TODO: I don't think anyone uses this, and it currently relies on some magic inside PermissibleBase
|
||||
//to ensure that the operator override actually applies.
|
||||
//Explore getting rid of this.
|
||||
//The following grants this permission to anyone who has the "everyone" root permission.
|
||||
//However, if the operator root node (which has higher priority) is present, the
|
||||
//permission will be denied instead.
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
$opRoot->addChild($perm->getName(), false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Plugin $plugin
|
||||
* @see Plugin::__construct()
|
||||
*/
|
||||
$plugin = new $mainClass($loader, $this->server, $description, $dataFolder, $prefixed, new DiskResourceProvider($prefixed . "/resources/"));
|
||||
$this->plugins[$plugin->getDescription()->getName()] = $plugin;
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[]|null $newLoaders
|
||||
* @phpstan-param list<class-string<PluginLoader>> $newLoaders
|
||||
@ -245,8 +298,14 @@ class PluginManager{
|
||||
}
|
||||
try{
|
||||
$description = $loader->getPluginDescription($file);
|
||||
}catch(PluginDescriptionParseException $e){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$file,
|
||||
KnownTranslationFactory::pocketmine_plugin_invalidManifest($e->getMessage())
|
||||
)));
|
||||
continue;
|
||||
}catch(\RuntimeException $e){ //TODO: more specific exception handling
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_fileError($file, $directory, $e->getMessage())));
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($file, $e->getMessage())));
|
||||
$this->server->getLogger()->logException($e);
|
||||
continue;
|
||||
}
|
||||
@ -255,62 +314,29 @@ class PluginManager{
|
||||
}
|
||||
|
||||
$name = $description->getName();
|
||||
if(stripos($name, "pocketmine") !== false or stripos($name, "minecraft") !== false or stripos($name, "mojang") !== false){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, KnownTranslationFactory::pocketmine_plugin_restrictedName())));
|
||||
|
||||
if(($loadabilityError = $this->checkPluginLoadability($description)) !== null){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, $loadabilityError)));
|
||||
continue;
|
||||
}
|
||||
if(strpos($name, " ") !== false){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_spacesDiscouraged($name)));
|
||||
}
|
||||
|
||||
if(isset($plugins[$name]) or $this->getPlugin($name) instanceof Plugin){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_duplicateError($name)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!ApiVersion::isCompatible($this->server->getApiVersion(), $description->getCompatibleApis())){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_plugin_incompatibleAPI(implode(", ", $description->getCompatibleApis()))
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
$ambiguousVersions = ApiVersion::checkAmbiguousVersions($description->getCompatibleApis());
|
||||
if(count($ambiguousVersions) > 0){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_plugin_ambiguousMinAPI(implode(", ", $ambiguousVersions))
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(count($description->getCompatibleOperatingSystems()) > 0 and !in_array(Utils::getOS(), $description->getCompatibleOperatingSystems(), true)) {
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_plugin_incompatibleOS(implode(", ", $description->getCompatibleOperatingSystems()))
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(count($pluginMcpeProtocols = $description->getCompatibleMcpeProtocols()) > 0){
|
||||
$serverMcpeProtocols = [ProtocolInfo::CURRENT_PROTOCOL];
|
||||
if(count(array_intersect($pluginMcpeProtocols, $serverMcpeProtocols)) === 0){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_plugin_incompatibleProtocol(implode(", ", $pluginMcpeProtocols))
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
if(strpos($name, " ") !== false){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_spacesDiscouraged($name)));
|
||||
}
|
||||
|
||||
if($this->graylist !== null and !$this->graylist->isAllowed($name)){
|
||||
$this->server->getLogger()->notice($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$name,
|
||||
"Disallowed by graylist"
|
||||
$this->graylist->isWhitelist() ? KnownTranslationFactory::pocketmine_plugin_disallowedByWhitelist() : KnownTranslationFactory::pocketmine_plugin_disallowedByBlacklist()
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
$plugins[$name] = $file;
|
||||
$plugins[$name] = new PluginLoadTriageEntry($file, $loader, $description);
|
||||
|
||||
$softDependencies[$name] = array_merge($softDependencies[$name] ?? [], $description->getSoftDepend());
|
||||
$dependencies[$name] = $description->getDepend();
|
||||
@ -327,7 +353,7 @@ class PluginManager{
|
||||
|
||||
while(count($plugins) > 0){
|
||||
$loadedThisLoop = 0;
|
||||
foreach($plugins as $name => $file){
|
||||
foreach($plugins as $name => $entry){
|
||||
if(isset($dependencies[$name])){
|
||||
foreach($dependencies[$name] as $key => $dependency){
|
||||
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
|
||||
@ -369,10 +395,8 @@ class PluginManager{
|
||||
if(!isset($dependencies[$name]) and !isset($softDependencies[$name])){
|
||||
unset($plugins[$name]);
|
||||
$loadedThisLoop++;
|
||||
if(($plugin = $this->loadPlugin($file, $loaders)) instanceof Plugin){
|
||||
if(($plugin = $this->internalLoadPlugin($entry->getFile(), $entry->getLoader(), $entry->getDescription())) instanceof Plugin){
|
||||
$loadedPlugins[$name] = $plugin;
|
||||
}else{
|
||||
$this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_genericLoadError($name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -506,6 +530,14 @@ class PluginManager{
|
||||
|
||||
$handleCancelled = false;
|
||||
if(isset($tags[ListenerMethodTags::HANDLE_CANCELLED])){
|
||||
if(!is_a($eventClass, Cancellable::class, true)){
|
||||
throw new PluginException(sprintf(
|
||||
"Event handler %s() declares @%s for non-cancellable event of type %s",
|
||||
Utils::getNiceClosureName($handlerClosure),
|
||||
ListenerMethodTags::HANDLE_CANCELLED,
|
||||
$eventClass
|
||||
));
|
||||
}
|
||||
switch(strtolower($tags[ListenerMethodTags::HANDLE_CANCELLED])){
|
||||
case "true":
|
||||
case "":
|
||||
|
@ -40,4 +40,10 @@ final class Manifest{
|
||||
public array $modules;
|
||||
|
||||
public ?ManifestMetadata $metadata = null;
|
||||
|
||||
/** @var string[] */
|
||||
public ?array $capabilities = null;
|
||||
|
||||
/** @var ManifestDependencyEntry[] */
|
||||
public ?array $dependencies = null;
|
||||
}
|
||||
|
37
src/resourcepacks/json/ManifestDependencyEntry.php
Normal file
37
src/resourcepacks/json/ManifestDependencyEntry.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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\resourcepacks\json;
|
||||
|
||||
final class ManifestDependencyEntry{
|
||||
|
||||
/** @required */
|
||||
public string $uuid;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array{int, int, int}
|
||||
* @required
|
||||
*/
|
||||
public array $version;
|
||||
}
|
@ -33,7 +33,6 @@ use function count;
|
||||
use function spl_object_id;
|
||||
use function time;
|
||||
use const PHP_INT_MAX;
|
||||
use const PTHREADS_INHERIT_CONSTANTS;
|
||||
use const PTHREADS_INHERIT_INI;
|
||||
|
||||
/**
|
||||
@ -41,7 +40,7 @@ use const PTHREADS_INHERIT_INI;
|
||||
* workers.
|
||||
*/
|
||||
class AsyncPool{
|
||||
private const WORKER_START_OPTIONS = PTHREADS_INHERIT_INI | PTHREADS_INHERIT_CONSTANTS;
|
||||
private const WORKER_START_OPTIONS = PTHREADS_INHERIT_INI;
|
||||
|
||||
/** @var \ClassLoader */
|
||||
private $classLoader;
|
||||
|
@ -129,6 +129,7 @@ abstract class AsyncTask extends \Threaded{
|
||||
public function cancelRun() : void{
|
||||
$this->cancelRun = true;
|
||||
}
|
||||
|
||||
public function hasCancelledRun() : bool{
|
||||
return $this->cancelRun;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\scheduler;
|
||||
|
||||
use DaveRandom\CallbackValidator\CallbackType;
|
||||
use DaveRandom\CallbackValidator\ReturnType;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
/**
|
||||
@ -49,7 +51,7 @@ class ClosureTask extends Task{
|
||||
* @phpstan-param \Closure() : void $closure
|
||||
*/
|
||||
public function __construct(\Closure $closure){
|
||||
Utils::validateCallableSignature(function() : void{}, $closure);
|
||||
Utils::validateCallableSignature(new CallbackType(new ReturnType()), $closure);
|
||||
$this->closure = $closure;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\utils;
|
||||
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function array_change_key_case;
|
||||
use function array_fill_keys;
|
||||
use function array_keys;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
@ -170,7 +171,7 @@ class Config{
|
||||
$config = null;
|
||||
switch($this->type){
|
||||
case Config::PROPERTIES:
|
||||
$config = $this->parseProperties($content);
|
||||
$config = self::parseProperties($content);
|
||||
break;
|
||||
case Config::JSON:
|
||||
$config = json_decode($content, true);
|
||||
@ -183,7 +184,7 @@ class Config{
|
||||
$config = unserialize($content);
|
||||
break;
|
||||
case Config::ENUM:
|
||||
$config = self::parseList($content);
|
||||
$config = array_fill_keys(self::parseList($content), true);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidStateException("Config type is unknown");
|
||||
@ -211,7 +212,7 @@ class Config{
|
||||
$content = null;
|
||||
switch($this->type){
|
||||
case Config::PROPERTIES:
|
||||
$content = $this->writeProperties();
|
||||
$content = self::writeProperties($this->config);
|
||||
break;
|
||||
case Config::JSON:
|
||||
$content = json_encode($this->config, $this->jsonOptions);
|
||||
@ -223,7 +224,7 @@ class Config{
|
||||
$content = serialize($this->config);
|
||||
break;
|
||||
case Config::ENUM:
|
||||
$content = implode("\r\n", array_keys($this->config));
|
||||
$content = self::writeList(array_keys($this->config));
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidStateException("Config type is unknown, has not been set or not detected");
|
||||
@ -509,28 +510,38 @@ class Config{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true[]
|
||||
* @phpstan-return array<string, true>
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
private static function parseList(string $content) : array{
|
||||
public static function parseList(string $content) : array{
|
||||
$result = [];
|
||||
foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){
|
||||
$v = trim($v);
|
||||
if($v == ""){
|
||||
if($v === ""){
|
||||
continue;
|
||||
}
|
||||
$result[$v] = true;
|
||||
$result[] = $v;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function writeProperties() : string{
|
||||
/**
|
||||
* @param string[] $entries
|
||||
* @phpstan-param list<string> $entries
|
||||
*/
|
||||
public static function writeList(array $entries) : string{
|
||||
return implode("\n", $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[]|int[]|float[]|bool[] $config
|
||||
* @phpstan-param array<string, string|int|float|bool> $config
|
||||
*/
|
||||
public static function writeProperties(array $config) : string{
|
||||
$content = "#Properties Config file\r\n#" . date("D M j H:i:s T Y") . "\r\n";
|
||||
foreach($this->config as $k => $v){
|
||||
foreach($config as $k => $v){
|
||||
if(is_bool($v)){
|
||||
$v = $v ? "on" : "off";
|
||||
}elseif(is_array($v)){
|
||||
$v = implode(";", $v);
|
||||
}
|
||||
$content .= $k . "=" . $v . "\r\n";
|
||||
}
|
||||
@ -539,9 +550,10 @@ class Config{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return string[]|int[]|float[]|bool[]
|
||||
* @phpstan-return array<string, string|int|float|bool>
|
||||
*/
|
||||
private function parseProperties(string $content) : array{
|
||||
public static function parseProperties(string $content) : array{
|
||||
$result = [];
|
||||
if(preg_match_all('/^\s*([a-zA-Z0-9\-_\.]+)[ \t]*=([^\r\n]*)/um', $content, $matches) > 0){ //false or 0 matches
|
||||
foreach($matches[1] as $i => $k){
|
||||
@ -557,11 +569,15 @@ class Config{
|
||||
case "no":
|
||||
$v = false;
|
||||
break;
|
||||
default:
|
||||
$v = match($v){
|
||||
(string) ((int) $v) => (int) $v,
|
||||
(string) ((float) $v) => (float) $v,
|
||||
default => $v,
|
||||
};
|
||||
break;
|
||||
}
|
||||
if(isset($result[$k])){
|
||||
\GlobalLogger::get()->debug("[Config] Repeated property " . $k . " on file " . $this->file);
|
||||
}
|
||||
$result[$k] = $v;
|
||||
$result[(string) $k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ use function curl_getinfo;
|
||||
use function curl_init;
|
||||
use function curl_setopt_array;
|
||||
use function explode;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function preg_match;
|
||||
use function socket_close;
|
||||
@ -218,8 +219,8 @@ class Internet{
|
||||
throw new InternetException(curl_error($ch));
|
||||
}
|
||||
if(!is_string($raw)) throw new AssumptionFailedError("curl_exec() should return string|false when CURLOPT_RETURNTRANSFER is set");
|
||||
/** @var int $httpCode */
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if(!is_int($httpCode)) throw new AssumptionFailedError("curl_getinfo(CURLINFO_HTTP_CODE) always returns int");
|
||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$rawHeaders = substr($raw, 0, $headerSize);
|
||||
$body = substr($raw, $headerSize);
|
||||
|
@ -24,14 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use LogLevel;
|
||||
use pocketmine\errorhandler\ErrorTypeToStringMap;
|
||||
use pocketmine\thread\Thread;
|
||||
use pocketmine\thread\Worker;
|
||||
use function get_class;
|
||||
use function is_int;
|
||||
use function preg_replace;
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
use function trim;
|
||||
use const PHP_EOL;
|
||||
use const PTHREADS_INHERIT_NONE;
|
||||
|
||||
@ -136,44 +132,11 @@ class MainLogger extends \AttachableThreadedLogger implements \BufferedLogger{
|
||||
* @return void
|
||||
*/
|
||||
public function logException(\Throwable $e, $trace = null){
|
||||
if($trace === null){
|
||||
$trace = $e->getTrace();
|
||||
}
|
||||
|
||||
$this->buffer(function() use ($e, $trace) : void{
|
||||
$this->critical(self::printExceptionMessage($e));
|
||||
foreach(Utils::printableTrace($trace) as $line){
|
||||
$this->critical($line);
|
||||
}
|
||||
for($prev = $e->getPrevious(); $prev !== null; $prev = $prev->getPrevious()){
|
||||
$this->critical("Previous: " . self::printExceptionMessage($prev));
|
||||
foreach(Utils::printableTrace($prev->getTrace()) as $line){
|
||||
$this->critical(" " . $line);
|
||||
}
|
||||
}
|
||||
});
|
||||
$this->critical(implode("\n", Utils::printableExceptionInfo($e, $trace)));
|
||||
|
||||
$this->syncFlushBuffer();
|
||||
}
|
||||
|
||||
private static function printExceptionMessage(\Throwable $e) : string{
|
||||
$errstr = preg_replace('/\s+/', ' ', trim($e->getMessage()));
|
||||
|
||||
$errno = $e->getCode();
|
||||
if(is_int($errno)){
|
||||
try{
|
||||
$errno = ErrorTypeToStringMap::get($errno);
|
||||
}catch(\InvalidArgumentException $ex){
|
||||
//pass
|
||||
}
|
||||
}
|
||||
|
||||
$errfile = Filesystem::cleanPath($e->getFile());
|
||||
$errline = $e->getLine();
|
||||
|
||||
return get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline";
|
||||
}
|
||||
|
||||
public function log($level, $message){
|
||||
switch($level){
|
||||
case LogLevel::EMERGENCY:
|
||||
@ -244,12 +207,11 @@ class MainLogger extends \AttachableThreadedLogger implements \BufferedLogger{
|
||||
|
||||
$this->synchronized(function() use ($message, $level, $time) : void{
|
||||
Terminal::writeLine($message);
|
||||
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
|
||||
|
||||
foreach($this->attachments as $attachment){
|
||||
$attachment->call($level, $message);
|
||||
}
|
||||
|
||||
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -125,18 +125,21 @@ final class Process{
|
||||
return count(ThreadManager::getInstance()->getAll()) + 2; //MainLogger + Main Thread
|
||||
}
|
||||
|
||||
public static function kill(int $pid) : void{
|
||||
public static function kill(int $pid, bool $subprocesses) : void{
|
||||
$logger = \GlobalLogger::get();
|
||||
if($logger instanceof MainLogger){
|
||||
$logger->syncFlushBuffer();
|
||||
}
|
||||
switch(Utils::getOS()){
|
||||
case Utils::OS_WINDOWS:
|
||||
exec("taskkill.exe /F /PID $pid > NUL");
|
||||
exec("taskkill.exe /F " . ($subprocesses ? "/T " : "") . "/PID $pid > NUL 2> NUL");
|
||||
break;
|
||||
case Utils::OS_MACOS:
|
||||
case Utils::OS_LINUX:
|
||||
default:
|
||||
if($subprocesses){
|
||||
$pid = -$pid;
|
||||
}
|
||||
if(function_exists("posix_kill")){
|
||||
posix_kill($pid, 9); //SIGKILL
|
||||
}else{
|
||||
|
@ -50,7 +50,7 @@ class ServerKiller extends Thread{
|
||||
});
|
||||
if(time() - $start >= $this->time){
|
||||
echo "\nTook too long to stop, server was killed forcefully!\n";
|
||||
@Process::kill(Process::pid());
|
||||
@Process::kill(Process::pid(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
80
src/utils/SignalHandler.php
Normal file
80
src/utils/SignalHandler.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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\utils;
|
||||
|
||||
use function function_exists;
|
||||
use function pcntl_async_signals;
|
||||
use function pcntl_signal;
|
||||
use function sapi_windows_set_ctrl_handler;
|
||||
use const PHP_WINDOWS_EVENT_CTRL_BREAK;
|
||||
use const PHP_WINDOWS_EVENT_CTRL_C;
|
||||
use const SIG_DFL;
|
||||
use const SIGHUP;
|
||||
use const SIGINT;
|
||||
use const SIGTERM;
|
||||
|
||||
final class SignalHandler{
|
||||
/** @phpstan-var (\Closure(int) : void)|null */
|
||||
private ?\Closure $interruptCallback;
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure() : void $interruptCallback
|
||||
*/
|
||||
public function __construct(\Closure $interruptCallback){
|
||||
$this->interruptCallback = $interruptCallback;
|
||||
|
||||
if(function_exists('sapi_windows_set_ctrl_handler')){
|
||||
sapi_windows_set_ctrl_handler($this->interruptCallback = function(int $signo) use ($interruptCallback) : void{
|
||||
if($signo === PHP_WINDOWS_EVENT_CTRL_C || $signo === PHP_WINDOWS_EVENT_CTRL_BREAK){
|
||||
$interruptCallback();
|
||||
}
|
||||
});
|
||||
}elseif(function_exists('pcntl_signal')){
|
||||
foreach([
|
||||
SIGTERM,
|
||||
SIGINT,
|
||||
SIGHUP
|
||||
] as $signal){
|
||||
pcntl_signal($signal, $this->interruptCallback = fn(int $signo) => $interruptCallback());
|
||||
}
|
||||
pcntl_async_signals(true);
|
||||
}else{
|
||||
//no supported signal handlers :(
|
||||
}
|
||||
}
|
||||
|
||||
public function unregister() : void{
|
||||
if(function_exists('sapi_windows_set_ctrl_handler')){
|
||||
sapi_windows_set_ctrl_handler($this->interruptCallback, false);
|
||||
}elseif(function_exists('pcntl_signal')){
|
||||
foreach([
|
||||
SIGTERM,
|
||||
SIGINT,
|
||||
SIGHUP
|
||||
] as $signal){
|
||||
pcntl_signal($signal, SIG_DFL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use DaveRandom\CallbackValidator\CallbackType;
|
||||
use pocketmine\errorhandler\ErrorTypeToStringMap;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use function array_combine;
|
||||
@ -46,6 +47,7 @@ use function file;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function get_current_user;
|
||||
use function get_loaded_extensions;
|
||||
use function getenv;
|
||||
@ -53,6 +55,7 @@ use function gettype;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function mb_check_encoding;
|
||||
@ -384,6 +387,49 @@ final class Utils{
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static function printableExceptionMessage(\Throwable $e) : string{
|
||||
$errstr = preg_replace('/\s+/', ' ', trim($e->getMessage()));
|
||||
|
||||
$errno = $e->getCode();
|
||||
if(is_int($errno)){
|
||||
try{
|
||||
$errno = ErrorTypeToStringMap::get($errno);
|
||||
}catch(\InvalidArgumentException $ex){
|
||||
//pass
|
||||
}
|
||||
}
|
||||
|
||||
$errfile = Filesystem::cleanPath($e->getFile());
|
||||
$errline = $e->getLine();
|
||||
|
||||
return get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $trace
|
||||
* @return string[]
|
||||
*/
|
||||
public static function printableExceptionInfo(\Throwable $e, $trace = null) : array{
|
||||
if($trace === null){
|
||||
$trace = $e->getTrace();
|
||||
}
|
||||
|
||||
$lines = [self::printableExceptionMessage($e)];
|
||||
$lines[] = "--- Stack trace ---";
|
||||
foreach(Utils::printableTrace($trace) as $line){
|
||||
$lines[] = " " . $line;
|
||||
}
|
||||
for($prev = $e->getPrevious(); $prev !== null; $prev = $prev->getPrevious()){
|
||||
$lines[] = "--- Previous ---";
|
||||
$lines[] = self::printableExceptionMessage($prev);
|
||||
foreach(Utils::printableTrace($prev->getTrace()) as $line){
|
||||
$lines[] = " " . $line;
|
||||
}
|
||||
}
|
||||
$lines[] = "--- End of exception information ---";
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[][] $trace
|
||||
* @phpstan-param list<array<string, mixed>> $trace
|
||||
@ -488,17 +534,20 @@ final class Utils{
|
||||
* Verifies that the given callable is compatible with the desired signature. Throws a TypeError if they are
|
||||
* incompatible.
|
||||
*
|
||||
* @param callable $signature Dummy callable with the required parameters and return type
|
||||
* @param callable $subject Callable to check the signature of
|
||||
* @phpstan-param anyCallable $signature
|
||||
* @phpstan-param anyCallable $subject
|
||||
* @param callable|CallbackType $signature Dummy callable with the required parameters and return type
|
||||
* @param callable $subject Callable to check the signature of
|
||||
* @phpstan-param anyCallable|CallbackType $signature
|
||||
* @phpstan-param anyCallable $subject
|
||||
*
|
||||
* @throws \DaveRandom\CallbackValidator\InvalidCallbackException
|
||||
* @throws \TypeError
|
||||
*/
|
||||
public static function validateCallableSignature(callable $signature, callable $subject) : void{
|
||||
if(!($sigType = CallbackType::createFromCallable($signature))->isSatisfiedBy($subject)){
|
||||
throw new \TypeError("Declaration of callable `" . CallbackType::createFromCallable($subject) . "` must be compatible with `" . $sigType . "`");
|
||||
public static function validateCallableSignature(callable|CallbackType $signature, callable $subject) : void{
|
||||
if(!($signature instanceof CallbackType)){
|
||||
$signature = CallbackType::createFromCallable($signature);
|
||||
}
|
||||
if(!$signature->isSatisfiedBy($subject)){
|
||||
throw new \TypeError("Declaration of callable `" . CallbackType::createFromCallable($subject) . "` must be compatible with `" . $signature . "`");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,10 @@ class VersionString{
|
||||
$this->suffix = $matches[4] ?? "";
|
||||
}
|
||||
|
||||
public static function isValidBaseVersion(string $baseVersion) : bool{
|
||||
return preg_match('/^\d+\.\d+\.\d+(?:-(.*))?$/', $baseVersion, $matches) === 1;
|
||||
}
|
||||
|
||||
public function getNumber() : int{
|
||||
return (($this->major << 9) | ($this->minor << 5) | $this->patch);
|
||||
}
|
||||
|
@ -411,8 +411,10 @@ class World implements ChunkManager{
|
||||
$this->maxY = $this->provider->getWorldMaxY();
|
||||
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_preparing($this->displayName)));
|
||||
$this->generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator(), true);
|
||||
//TODO: validate generator options
|
||||
$generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator()) ??
|
||||
throw new AssumptionFailedError("WorldManager should already have checked that the generator exists");
|
||||
$generator->validateGeneratorOptions($this->provider->getWorldData()->getGeneratorOptions());
|
||||
$this->generator = $generator->getGeneratorClass();
|
||||
$this->chunkPopulationRequestQueue = new \SplQueue();
|
||||
$this->addOnUnloadCallback(function() : void{
|
||||
$this->logger->debug("Cancelling unfulfilled generation requests");
|
||||
@ -2289,10 +2291,6 @@ class World implements ChunkManager{
|
||||
}
|
||||
}
|
||||
$pos = $entity->getPosition()->asVector3();
|
||||
$chunk = $this->getOrLoadChunkAtPosition($pos);
|
||||
if($chunk === null){
|
||||
throw new \InvalidArgumentException("Cannot add an Entity in an ungenerated chunk");
|
||||
}
|
||||
$this->entitiesByChunk[World::chunkHash($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE)][$entity->getId()] = $entity;
|
||||
$this->entityLastKnownPositions[$entity->getId()] = $pos;
|
||||
|
||||
|
@ -38,6 +38,7 @@ use pocketmine\world\format\io\FormatConverter;
|
||||
use pocketmine\world\format\io\WorldProviderManager;
|
||||
use pocketmine\world\format\io\WritableWorldProvider;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function array_keys;
|
||||
use function array_shift;
|
||||
@ -50,6 +51,7 @@ use function iterator_to_array;
|
||||
use function microtime;
|
||||
use function round;
|
||||
use function sprintf;
|
||||
use function strval;
|
||||
use function trim;
|
||||
|
||||
class WorldManager{
|
||||
@ -207,28 +209,50 @@ class WorldManager{
|
||||
try{
|
||||
$provider = $providerClass->fromPath($path);
|
||||
}catch(CorruptedWorldException $e){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError($name, "Corruption detected: " . $e->getMessage())));
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_level_corrupted($e->getMessage())
|
||||
)));
|
||||
return false;
|
||||
}catch(UnsupportedWorldFormatException $e){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError($name, "Unsupported format: " . $e->getMessage())));
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_level_unsupportedFormat($e->getMessage())
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
|
||||
$generatorEntry = GeneratorManager::getInstance()->getGenerator($provider->getWorldData()->getGenerator());
|
||||
if($generatorEntry === null){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_level_unknownGenerator($provider->getWorldData()->getGenerator())
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
try{
|
||||
GeneratorManager::getInstance()->getGenerator($provider->getWorldData()->getGenerator(), true);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError($name, "Unknown generator \"" . $provider->getWorldData()->getGenerator() . "\"")));
|
||||
$generatorEntry->validateGeneratorOptions($provider->getWorldData()->getGeneratorOptions());
|
||||
}catch(InvalidGeneratorOptionsException $e){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
|
||||
$name,
|
||||
KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions(
|
||||
$provider->getWorldData()->getGeneratorOptions(),
|
||||
$provider->getWorldData()->getGenerator(),
|
||||
$e->getMessage()
|
||||
)
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
if(!($provider instanceof WritableWorldProvider)){
|
||||
if(!$autoUpgrade){
|
||||
throw new UnsupportedWorldFormatException("World \"$name\" is in an unsupported format and needs to be upgraded");
|
||||
}
|
||||
$this->server->getLogger()->notice("Upgrading world \"$name\" to new format. This may take a while.");
|
||||
$this->server->getLogger()->notice($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_conversion_start($name)));
|
||||
|
||||
$converter = new FormatConverter($provider, $this->providerManager->getDefault(), Path::join($this->server->getDataPath(), "backups", "worlds"), $this->server->getLogger());
|
||||
$provider = $converter->execute();
|
||||
|
||||
$this->server->getLogger()->notice("Upgraded world \"$name\" to new format successfully. Backed up pre-conversion world at " . $converter->getBackupPath());
|
||||
$this->server->getLogger()->notice($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_conversion_finish($name, $converter->getBackupPath())));
|
||||
}
|
||||
|
||||
$world = new World($this->server, $name, $provider, $this->server->getAsyncPool());
|
||||
@ -282,7 +306,7 @@ class WorldManager{
|
||||
$oldProgress = (int) floor(($done / $total) * 100);
|
||||
$newProgress = (int) floor((++$done / $total) * 100);
|
||||
if(intdiv($oldProgress, 10) !== intdiv($newProgress, 10) || $done === $total || $done === 1){
|
||||
$world->getLogger()->info("Generating spawn terrain chunks: $done / $total ($newProgress%)");
|
||||
$world->getLogger()->info($world->getServer()->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_spawnTerrainGenerationProgress(strval($done), strval($total), strval($newProgress))));
|
||||
}
|
||||
},
|
||||
static function() : void{
|
||||
|
@ -299,11 +299,11 @@ class Chunk{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SplFixedArray|SubChunk[]
|
||||
* @phpstan-return \SplFixedArray<SubChunk>
|
||||
* @return SubChunk[]
|
||||
* @phpstan-return array<int, SubChunk>
|
||||
*/
|
||||
public function getSubChunks() : \SplFixedArray{
|
||||
return $this->subChunks;
|
||||
public function getSubChunks() : array{
|
||||
return $this->subChunks->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,8 +26,6 @@ namespace pocketmine\world\format\io;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\world\format\BiomeArray;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\HeightArray;
|
||||
use pocketmine\world\format\LightArray;
|
||||
use pocketmine\world\format\PalettedBlockArray;
|
||||
use pocketmine\world\format\SubChunk;
|
||||
use function array_values;
|
||||
@ -41,34 +39,25 @@ use function unpack;
|
||||
* The serialization format **is not intended for permanent storage** and may change without warning.
|
||||
*/
|
||||
final class FastChunkSerializer{
|
||||
private const FLAG_GENERATED = 1 << 0;
|
||||
private const FLAG_POPULATED = 1 << 1;
|
||||
private const FLAG_HAS_LIGHT = 1 << 2;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public static function serializeWithoutLight(Chunk $chunk) : string{
|
||||
return self::serialize($chunk, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast-serializes the chunk for passing between threads
|
||||
* TODO: tiles and entities
|
||||
*/
|
||||
public static function serialize(Chunk $chunk, bool $includeLight = true) : string{
|
||||
$includeLight = $includeLight && $chunk->isLightPopulated() === true;
|
||||
|
||||
public static function serializeTerrain(Chunk $chunk) : string{
|
||||
$stream = new BinaryStream();
|
||||
$stream->putByte(
|
||||
($includeLight ? self::FLAG_HAS_LIGHT : 0) |
|
||||
($chunk->isPopulated() ? self::FLAG_POPULATED : 0)
|
||||
);
|
||||
|
||||
//subchunks
|
||||
$subChunks = $chunk->getSubChunks();
|
||||
$count = $subChunks->count();
|
||||
$count = count($subChunks);
|
||||
$stream->putByte($count);
|
||||
|
||||
foreach($subChunks as $y => $subChunk){
|
||||
@ -86,18 +75,10 @@ final class FastChunkSerializer{
|
||||
$stream->putInt(strlen($serialPalette));
|
||||
$stream->put($serialPalette);
|
||||
}
|
||||
|
||||
if($includeLight){
|
||||
$stream->put($subChunk->getBlockSkyLightArray()->getData());
|
||||
$stream->put($subChunk->getBlockLightArray()->getData());
|
||||
}
|
||||
}
|
||||
|
||||
//biomes
|
||||
$stream->put($chunk->getBiomeIdArray());
|
||||
if($includeLight){
|
||||
$stream->put(pack("S*", ...$chunk->getHeightMapArray()));
|
||||
}
|
||||
|
||||
return $stream->getBuffer();
|
||||
}
|
||||
@ -105,15 +86,13 @@ final class FastChunkSerializer{
|
||||
/**
|
||||
* Deserializes a fast-serialized chunk
|
||||
*/
|
||||
public static function deserialize(string $data) : Chunk{
|
||||
public static function deserializeTerrain(string $data) : Chunk{
|
||||
$stream = new BinaryStream($data);
|
||||
|
||||
$flags = $stream->getByte();
|
||||
$lightPopulated = (bool) ($flags & self::FLAG_HAS_LIGHT);
|
||||
$terrainPopulated = (bool) ($flags & self::FLAG_POPULATED);
|
||||
|
||||
$subChunks = [];
|
||||
$heightMap = null;
|
||||
|
||||
$count = $stream->getByte();
|
||||
for($subCount = 0; $subCount < $count; ++$subCount){
|
||||
@ -131,21 +110,13 @@ final class FastChunkSerializer{
|
||||
|
||||
$layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
|
||||
}
|
||||
$subChunks[$y] = new SubChunk(
|
||||
$airBlockId, $layers, $lightPopulated ? new LightArray($stream->get(2048)) : null, $lightPopulated ? new LightArray($stream->get(2048)) : null
|
||||
);
|
||||
$subChunks[$y] = new SubChunk($airBlockId, $layers);
|
||||
}
|
||||
|
||||
$biomeIds = new BiomeArray($stream->get(256));
|
||||
if($lightPopulated){
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("S*", $stream->get(512)); //unpack() will never fail here
|
||||
$heightMap = new HeightArray(array_values($unpackedHeightMap));
|
||||
}
|
||||
|
||||
$chunk = new Chunk($subChunks, $biomeIds, $heightMap);
|
||||
$chunk = new Chunk($subChunks, $biomeIds);
|
||||
$chunk->setPopulated($terrainPopulated);
|
||||
$chunk->setLightPopulated($lightPopulated);
|
||||
$chunk->clearTerrainDirtyFlags();
|
||||
|
||||
return $chunk;
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\world\format\io;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\generator\normal\Normal;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function basename;
|
||||
@ -112,7 +113,9 @@ class FormatConverter{
|
||||
Filesystem::recursiveUnlink($convertedOutput);
|
||||
}
|
||||
$this->newProvider->generate($convertedOutput, $data->getName(), WorldCreationOptions::create()
|
||||
->setGeneratorClass(GeneratorManager::getInstance()->getGenerator($data->getGenerator()))
|
||||
//TODO: defaulting to NORMAL here really isn't very good behaviour, but it's consistent with what we already
|
||||
//did previously; besides, WorldManager checks for unknown generators before this is reached anyway.
|
||||
->setGeneratorClass(GeneratorManager::getInstance()->getGenerator($data->getGenerator())?->getGeneratorClass() ?? Normal::class)
|
||||
->setGeneratorOptions($data->getGeneratorOptions())
|
||||
->setSeed($data->getSeed())
|
||||
->setSpawnPosition($data->getSpawn())
|
||||
|
@ -39,6 +39,7 @@ use pocketmine\world\generator\Generator;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\World;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function strlen;
|
||||
@ -101,7 +102,7 @@ class BedrockWorldData extends BaseNbtWorldData{
|
||||
|
||||
$nbt = new LittleEndianNbtSerializer();
|
||||
$buffer = $nbt->write(new TreeRoot($worldData));
|
||||
file_put_contents($path . "level.dat", Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
|
||||
file_put_contents(Path::join($path, "level.dat"), Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
|
||||
}
|
||||
|
||||
protected function load() : CompoundTag{
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\world\format\io\exception\CorruptedWorldException;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\World;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function ceil;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
@ -68,7 +69,7 @@ class JavaWorldData extends BaseNbtWorldData{
|
||||
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$buffer = zlib_encode($nbt->write(new TreeRoot(CompoundTag::create()->setTag("Data", $worldData))), ZLIB_ENCODING_GZIP);
|
||||
file_put_contents($path . "level.dat", $buffer);
|
||||
file_put_contents(Path::join($path, "level.dat"), $buffer);
|
||||
}
|
||||
|
||||
protected function load() : CompoundTag{
|
||||
|
@ -24,47 +24,31 @@ declare(strict_types=1);
|
||||
namespace pocketmine\world\generator;
|
||||
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\item\LegacyStringToItemParser;
|
||||
use pocketmine\item\LegacyStringToItemParserException;
|
||||
use pocketmine\world\ChunkManager;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\SubChunk;
|
||||
use pocketmine\world\generator\object\OreType;
|
||||
use pocketmine\world\generator\populator\Ore;
|
||||
use pocketmine\world\generator\populator\Populator;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function preg_match;
|
||||
use function preg_match_all;
|
||||
|
||||
class Flat extends Generator{
|
||||
|
||||
/** @var Chunk */
|
||||
private $chunk;
|
||||
/** @var Populator[] */
|
||||
private $populators = [];
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private $structure;
|
||||
/** @var int */
|
||||
private $biome;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
* @phpstan-var array<string, mixed>
|
||||
*/
|
||||
private array $options = [];
|
||||
private FlatGeneratorOptions $options;
|
||||
|
||||
/**
|
||||
* @throws InvalidGeneratorOptionsException
|
||||
*/
|
||||
public function __construct(int $seed, string $preset){
|
||||
parent::__construct($seed, $preset !== "" ? $preset : "2;bedrock,2xdirt,grass;1;");
|
||||
$this->parsePreset();
|
||||
$this->options = FlatGeneratorOptions::parsePreset($this->preset);
|
||||
|
||||
if(isset($this->options["decoration"])){
|
||||
if(isset($this->options->getExtraOptions()["decoration"])){
|
||||
$ores = new Ore();
|
||||
$stone = VanillaBlocks::STONE();
|
||||
$ores->setOreTypes([
|
||||
@ -83,76 +67,22 @@ class Flat extends Generator{
|
||||
$this->generateBaseChunk();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*
|
||||
* @throws InvalidGeneratorOptionsException
|
||||
*/
|
||||
public static function parseLayers(string $layers) : array{
|
||||
$result = [];
|
||||
$split = array_map('\trim', explode(',', $layers));
|
||||
$y = 0;
|
||||
$itemParser = LegacyStringToItemParser::getInstance();
|
||||
foreach($split as $line){
|
||||
preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches);
|
||||
if(count($matches) !== 3){
|
||||
throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\"");
|
||||
}
|
||||
|
||||
$cnt = $matches[1] !== "" ? (int) $matches[1] : 1;
|
||||
try{
|
||||
$b = $itemParser->parse($matches[2])->getBlock();
|
||||
}catch(LegacyStringToItemParserException $e){
|
||||
throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\": " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
for($cY = $y, $y += $cnt; $cY < $y; ++$cY){
|
||||
$result[$cY] = $b->getFullId();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function parsePreset() : void{
|
||||
$preset = explode(";", $this->preset);
|
||||
$blocks = $preset[1] ?? "";
|
||||
$this->biome = (int) ($preset[2] ?? 1);
|
||||
$options = $preset[3] ?? "";
|
||||
$this->structure = self::parseLayers($blocks);
|
||||
|
||||
//TODO: more error checking
|
||||
preg_match_all('#(([0-9a-z_]{1,})\(?([0-9a-z_ =:]{0,})\)?),?#', $options, $matches);
|
||||
foreach($matches[2] as $i => $option){
|
||||
$params = true;
|
||||
if($matches[3][$i] !== ""){
|
||||
$params = [];
|
||||
$p = explode(" ", $matches[3][$i]);
|
||||
foreach($p as $k){
|
||||
$k = explode("=", $k);
|
||||
if(isset($k[1])){
|
||||
$params[$k[0]] = $k[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->options[$option] = $params;
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateBaseChunk() : void{
|
||||
$this->chunk = new Chunk();
|
||||
|
||||
$biomeId = $this->options->getBiomeId();
|
||||
for($Z = 0; $Z < Chunk::EDGE_LENGTH; ++$Z){
|
||||
for($X = 0; $X < Chunk::EDGE_LENGTH; ++$X){
|
||||
$this->chunk->setBiomeId($X, $Z, $this->biome);
|
||||
$this->chunk->setBiomeId($X, $Z, $biomeId);
|
||||
}
|
||||
}
|
||||
|
||||
$count = count($this->structure);
|
||||
$structure = $this->options->getStructure();
|
||||
$count = count($structure);
|
||||
for($sy = 0; $sy < $count; $sy += SubChunk::EDGE_LENGTH){
|
||||
$subchunk = $this->chunk->getSubChunk($sy >> SubChunk::COORD_BIT_SIZE);
|
||||
for($y = 0; $y < SubChunk::EDGE_LENGTH and isset($this->structure[$y | $sy]); ++$y){
|
||||
$id = $this->structure[$y | $sy];
|
||||
for($y = 0; $y < SubChunk::EDGE_LENGTH and isset($structure[$y | $sy]); ++$y){
|
||||
$id = $structure[$y | $sy];
|
||||
|
||||
for($Z = 0; $Z < SubChunk::EDGE_LENGTH; ++$Z){
|
||||
for($X = 0; $X < SubChunk::EDGE_LENGTH; ++$X){
|
||||
|
127
src/world/generator/FlatGeneratorOptions.php
Normal file
127
src/world/generator/FlatGeneratorOptions.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?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\world\generator;
|
||||
|
||||
use pocketmine\data\bedrock\BiomeIds;
|
||||
use pocketmine\item\LegacyStringToItemParser;
|
||||
use pocketmine\item\LegacyStringToItemParserException;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function preg_match;
|
||||
use function preg_match_all;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class FlatGeneratorOptions{
|
||||
|
||||
/**
|
||||
* @param int[] $structure
|
||||
* @param mixed[] $extraOptions
|
||||
* @phpstan-param array<int, int> $structure
|
||||
* @phpstan-param array<string, array<string, string>|true> $extraOptions
|
||||
*/
|
||||
public function __construct(
|
||||
private array $structure,
|
||||
private int $biomeId,
|
||||
private array $extraOptions = []
|
||||
){}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*/
|
||||
public function getStructure() : array{ return $this->structure; }
|
||||
|
||||
public function getBiomeId() : int{ return $this->biomeId; }
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @phpstan-return array<string, array<string, string>|true>
|
||||
*/
|
||||
public function getExtraOptions() : array{ return $this->extraOptions; }
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*
|
||||
* @throws InvalidGeneratorOptionsException
|
||||
*/
|
||||
public static function parseLayers(string $layers) : array{
|
||||
$result = [];
|
||||
$split = array_map('\trim', explode(',', $layers));
|
||||
$y = 0;
|
||||
$itemParser = LegacyStringToItemParser::getInstance();
|
||||
foreach($split as $line){
|
||||
preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches);
|
||||
if(count($matches) !== 3){
|
||||
throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\"");
|
||||
}
|
||||
|
||||
$cnt = $matches[1] !== "" ? (int) $matches[1] : 1;
|
||||
try{
|
||||
$b = $itemParser->parse($matches[2])->getBlock();
|
||||
}catch(LegacyStringToItemParserException $e){
|
||||
throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\": " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
for($cY = $y, $y += $cnt; $cY < $y; ++$cY){
|
||||
$result[$cY] = $b->getFullId();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidGeneratorOptionsException
|
||||
*/
|
||||
public static function parsePreset(string $presetString) : self{
|
||||
$preset = explode(";", $presetString);
|
||||
$blocks = $preset[1] ?? "";
|
||||
$biomeId = (int) ($preset[2] ?? BiomeIds::PLAINS);
|
||||
$optionsString = $preset[3] ?? "";
|
||||
$structure = self::parseLayers($blocks);
|
||||
|
||||
$options = [];
|
||||
//TODO: more error checking
|
||||
preg_match_all('#(([0-9a-z_]{1,})\(?([0-9a-z_ =:]{0,})\)?),?#', $optionsString, $matches);
|
||||
foreach($matches[2] as $i => $option){
|
||||
$params = true;
|
||||
if($matches[3][$i] !== ""){
|
||||
$params = [];
|
||||
$p = explode(" ", $matches[3][$i]);
|
||||
foreach($p as $k){
|
||||
$k = explode("=", $k);
|
||||
if(isset($k[1])){
|
||||
$params[$k[0]] = $k[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
$options[(string) $option] = $params;
|
||||
}
|
||||
return new self($structure, $biomeId, $options);
|
||||
}
|
||||
|
||||
}
|
@ -34,35 +34,49 @@ final class GeneratorManager{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var string[] name => classname mapping
|
||||
* @phpstan-var array<string, class-string<Generator>>
|
||||
* @var GeneratorManagerEntry[] name => classname mapping
|
||||
* @phpstan-var array<string, GeneratorManagerEntry>
|
||||
*/
|
||||
private $list = [];
|
||||
|
||||
public function __construct(){
|
||||
$this->addGenerator(Flat::class, "flat");
|
||||
$this->addGenerator(Normal::class, "normal");
|
||||
$this->addGenerator(Normal::class, "default");
|
||||
$this->addGenerator(Nether::class, "hell");
|
||||
$this->addGenerator(Nether::class, "nether");
|
||||
$this->addGenerator(Flat::class, "flat", \Closure::fromCallable(function(string $preset) : ?InvalidGeneratorOptionsException{
|
||||
if($preset === ""){
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
FlatGeneratorOptions::parsePreset($preset);
|
||||
return null;
|
||||
}catch(InvalidGeneratorOptionsException $e){
|
||||
return $e;
|
||||
}
|
||||
}));
|
||||
$this->addGenerator(Normal::class, "normal", fn() => null);
|
||||
$this->addGenerator(Normal::class, "default", fn() => null);
|
||||
$this->addGenerator(Nether::class, "hell", fn() => null);
|
||||
$this->addGenerator(Nether::class, "nether", fn() => null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class Fully qualified name of class that extends \pocketmine\world\generator\Generator
|
||||
* @param string $name Alias for this generator type that can be written in configs
|
||||
* @param bool $overwrite Whether to force overwriting any existing registered generator with the same name
|
||||
* @param string $class Fully qualified name of class that extends \pocketmine\world\generator\Generator
|
||||
* @param string $name Alias for this generator type that can be written in configs
|
||||
* @param \Closure $presetValidator Callback to validate generator options for new worlds
|
||||
* @param bool $overwrite Whether to force overwriting any existing registered generator with the same name
|
||||
*
|
||||
* @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator
|
||||
*
|
||||
* @phpstan-param class-string<Generator> $class
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addGenerator(string $class, string $name, bool $overwrite = false) : void{
|
||||
public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false) : void{
|
||||
Utils::testValidInstance($class, Generator::class);
|
||||
|
||||
if(!$overwrite and isset($this->list[$name = strtolower($name)])){
|
||||
throw new \InvalidArgumentException("Alias \"$name\" is already assigned");
|
||||
}
|
||||
|
||||
$this->list[$name] = $class;
|
||||
$this->list[$name] = new GeneratorManagerEntry($class, $presetValidator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,24 +89,10 @@ final class GeneratorManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a class name of a registered Generator matching the given name.
|
||||
*
|
||||
* @param bool $throwOnMissing @deprecated this is for backwards compatibility only
|
||||
*
|
||||
* @return string Name of class that extends Generator
|
||||
* @phpstan-return class-string<Generator>
|
||||
*
|
||||
* @throws \InvalidArgumentException if the generator type isn't registered
|
||||
* Returns the generator entry of a registered Generator matching the given name, or null if not found.
|
||||
*/
|
||||
public function getGenerator(string $name, bool $throwOnMissing = false){
|
||||
if(isset($this->list[$name = strtolower($name)])){
|
||||
return $this->list[$name];
|
||||
}
|
||||
|
||||
if($throwOnMissing){
|
||||
throw new \InvalidArgumentException("Alias \"$name\" does not map to any known generator");
|
||||
}
|
||||
return Normal::class;
|
||||
public function getGenerator(string $name) : ?GeneratorManagerEntry{
|
||||
return $this->list[strtolower($name)] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +106,7 @@ final class GeneratorManager{
|
||||
public function getGeneratorName(string $class) : string{
|
||||
Utils::testValidInstance($class, Generator::class);
|
||||
foreach($this->list as $name => $c){
|
||||
if($c === $class){
|
||||
if($c->getGeneratorClass() === $class){
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
48
src/world/generator/GeneratorManagerEntry.php
Normal file
48
src/world/generator/GeneratorManagerEntry.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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\world\generator;
|
||||
|
||||
final class GeneratorManagerEntry{
|
||||
|
||||
/**
|
||||
* @phpstan-param class-string<Generator> $generatorClass
|
||||
* @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator
|
||||
*/
|
||||
public function __construct(
|
||||
private string $generatorClass,
|
||||
private \Closure $presetValidator
|
||||
){}
|
||||
|
||||
/** @phpstan-return class-string<Generator> */
|
||||
public function getGeneratorClass() : string{ return $this->generatorClass; }
|
||||
|
||||
/**
|
||||
* @throws InvalidGeneratorOptionsException
|
||||
*/
|
||||
public function validateGeneratorOptions(string $generatorOptions) : void{
|
||||
if(($exception = ($this->presetValidator)($generatorOptions)) !== null){
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
@ -68,10 +68,10 @@ class PopulationTask extends AsyncTask{
|
||||
$this->worldId = $world->getId();
|
||||
$this->chunkX = $chunkX;
|
||||
$this->chunkZ = $chunkZ;
|
||||
$this->chunk = $chunk !== null ? FastChunkSerializer::serializeWithoutLight($chunk) : null;
|
||||
$this->chunk = $chunk !== null ? FastChunkSerializer::serializeTerrain($chunk) : null;
|
||||
|
||||
foreach($world->getAdjacentChunks($chunkX, $chunkZ) as $i => $c){
|
||||
$this->{"chunk$i"} = $c !== null ? FastChunkSerializer::serializeWithoutLight($c) : null;
|
||||
$this->{"chunk$i"} = $c !== null ? FastChunkSerializer::serializeTerrain($c) : null;
|
||||
}
|
||||
|
||||
$this->storeLocal(self::TLS_KEY_WORLD, $world);
|
||||
@ -88,7 +88,7 @@ class PopulationTask extends AsyncTask{
|
||||
/** @var Chunk[] $chunks */
|
||||
$chunks = [];
|
||||
|
||||
$chunk = $this->chunk !== null ? FastChunkSerializer::deserialize($this->chunk) : null;
|
||||
$chunk = $this->chunk !== null ? FastChunkSerializer::deserializeTerrain($this->chunk) : null;
|
||||
|
||||
for($i = 0; $i < 9; ++$i){
|
||||
if($i === 4){
|
||||
@ -98,55 +98,53 @@ class PopulationTask extends AsyncTask{
|
||||
if($ck === null){
|
||||
$chunks[$i] = null;
|
||||
}else{
|
||||
$chunks[$i] = FastChunkSerializer::deserialize($ck);
|
||||
$chunks[$i] = FastChunkSerializer::deserializeTerrain($ck);
|
||||
}
|
||||
}
|
||||
|
||||
$manager->setChunk($this->chunkX, $this->chunkZ, $chunk ?? new Chunk());
|
||||
self::setOrGenerateChunk($manager, $generator, $this->chunkX, $this->chunkZ, $chunk);
|
||||
|
||||
$resultChunks = []; //this is just to keep phpstan's type inference happy
|
||||
foreach($chunks as $i => $c){
|
||||
$cX = (-1 + $i % 3) + $this->chunkX;
|
||||
$cZ = (-1 + intdiv($i, 3)) + $this->chunkZ;
|
||||
$resultChunks[$i] = self::setOrGenerateChunk($manager, $generator, $cX, $cZ, $c);
|
||||
}
|
||||
$chunks = $resultChunks;
|
||||
|
||||
$generator->populateChunk($manager, $this->chunkX, $this->chunkZ);
|
||||
$chunk = $manager->getChunk($this->chunkX, $this->chunkZ);
|
||||
if($chunk === null){
|
||||
$generator->generateChunk($manager, $this->chunkX, $this->chunkZ);
|
||||
$chunk = $manager->getChunk($this->chunkX, $this->chunkZ);
|
||||
throw new AssumptionFailedError("We just generated this chunk, so it must exist");
|
||||
}
|
||||
$chunk->setPopulated();
|
||||
|
||||
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
|
||||
|
||||
foreach($chunks as $i => $c){
|
||||
$this->{"chunk$i"} = $c->isTerrainDirty() ? FastChunkSerializer::serializeTerrain($c) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function setOrGenerateChunk(SimpleChunkManager $manager, Generator $generator, int $chunkX, int $chunkZ, ?Chunk $chunk) : Chunk{
|
||||
$manager->setChunk($chunkX, $chunkZ, $chunk ?? new Chunk());
|
||||
if($chunk === null){
|
||||
$generator->generateChunk($manager, $chunkX, $chunkZ);
|
||||
$chunk = $manager->getChunk($chunkX, $chunkZ);
|
||||
if($chunk === null){
|
||||
throw new AssumptionFailedError("We just set this chunk, so it must exist");
|
||||
}
|
||||
$chunk->setTerrainDirtyFlag(Chunk::DIRTY_FLAG_TERRAIN, true);
|
||||
$chunk->setTerrainDirtyFlag(Chunk::DIRTY_FLAG_BIOMES, true);
|
||||
}
|
||||
|
||||
$resultChunks = []; //this is just to keep phpstan's type inference happy
|
||||
foreach($chunks as $i => $c){
|
||||
$cX = (-1 + $i % 3) + $this->chunkX;
|
||||
$cZ = (-1 + intdiv($i, 3)) + $this->chunkZ;
|
||||
$manager->setChunk($cX, $cZ, $c ?? new Chunk());
|
||||
if($c === null){
|
||||
$generator->generateChunk($manager, $cX, $cZ);
|
||||
$c = $manager->getChunk($cX, $cZ);
|
||||
if($c === null){
|
||||
throw new AssumptionFailedError("We just set this chunk, so it must exist");
|
||||
}
|
||||
$c->setTerrainDirtyFlag(Chunk::DIRTY_FLAG_TERRAIN, true);
|
||||
$c->setTerrainDirtyFlag(Chunk::DIRTY_FLAG_BIOMES, true);
|
||||
}
|
||||
$resultChunks[$i] = $c;
|
||||
}
|
||||
$chunks = $resultChunks;
|
||||
|
||||
$generator->populateChunk($manager, $this->chunkX, $this->chunkZ);
|
||||
$chunk = $manager->getChunk($this->chunkX, $this->chunkZ);
|
||||
$chunk->setPopulated();
|
||||
|
||||
$this->chunk = FastChunkSerializer::serializeWithoutLight($chunk);
|
||||
|
||||
foreach($chunks as $i => $c){
|
||||
$this->{"chunk$i"} = $c->isTerrainDirty() ? FastChunkSerializer::serializeWithoutLight($c) : null;
|
||||
}
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
public function onCompletion() : void{
|
||||
/** @var World $world */
|
||||
$world = $this->fetchLocal(self::TLS_KEY_WORLD);
|
||||
if($world->isLoaded()){
|
||||
$chunk = $this->chunk !== null ? FastChunkSerializer::deserialize($this->chunk) : null;
|
||||
$chunk = $this->chunk !== null ? FastChunkSerializer::deserializeTerrain($this->chunk) : null;
|
||||
|
||||
for($i = 0; $i < 9; ++$i){
|
||||
if($i === 4){
|
||||
@ -157,7 +155,7 @@ class PopulationTask extends AsyncTask{
|
||||
$xx = -1 + $i % 3;
|
||||
$zz = -1 + intdiv($i, 3);
|
||||
|
||||
$c = FastChunkSerializer::deserialize($c);
|
||||
$c = FastChunkSerializer::deserializeTerrain($c);
|
||||
$world->generateChunkCallback($this->chunkX + $xx, $this->chunkZ + $zz, $c);
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ class LightPopulationTask extends AsyncTask{
|
||||
* @phpstan-param \Closure(array<int, LightArray> $blockLight, array<int, LightArray> $skyLight, array<int, int> $heightMap) : void $onCompletion
|
||||
*/
|
||||
public function __construct(Chunk $chunk, \Closure $onCompletion){
|
||||
$this->chunk = FastChunkSerializer::serialize($chunk);
|
||||
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
|
||||
$this->storeLocal(self::TLS_KEY_COMPLETION_CALLBACK, $onCompletion);
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
$chunk = FastChunkSerializer::deserialize($this->chunk);
|
||||
$chunk = FastChunkSerializer::deserializeTerrain($this->chunk);
|
||||
|
||||
$manager = new SimpleChunkManager(World::Y_MIN, World::Y_MAX);
|
||||
$manager->setChunk(0, 0, $chunk);
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\world\format\SubChunk;
|
||||
use pocketmine\world\utils\SubChunkExplorer;
|
||||
use pocketmine\world\utils\SubChunkExplorerStatus;
|
||||
use pocketmine\world\World;
|
||||
use function count;
|
||||
use function max;
|
||||
|
||||
class SkyLightUpdate extends LightUpdate{
|
||||
@ -114,7 +115,7 @@ class SkyLightUpdate extends LightUpdate{
|
||||
//have to avoid filling full light for any subchunk that contains a heightmap Y coordinate
|
||||
$highestHeightMapPlusOne = max($chunk->getHeightMapArray()) + 1;
|
||||
$lowestClearSubChunk = ($highestHeightMapPlusOne >> SubChunk::COORD_BIT_SIZE) + (($highestHeightMapPlusOne & SubChunk::COORD_MASK) !== 0 ? 1 : 0);
|
||||
$chunkHeight = $chunk->getSubChunks()->count();
|
||||
$chunkHeight = count($chunk->getSubChunks());
|
||||
for($y = 0; $y < $lowestClearSubChunk && $y < $chunkHeight; $y++){
|
||||
$chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(0));
|
||||
}
|
||||
@ -173,7 +174,7 @@ class SkyLightUpdate extends LightUpdate{
|
||||
* @phpstan-param \SplFixedArray<bool> $directSkyLightBlockers
|
||||
*/
|
||||
private static function recalculateHeightMap(Chunk $chunk, \SplFixedArray $directSkyLightBlockers) : HeightArray{
|
||||
$maxSubChunkY = $chunk->getSubChunks()->count() - 1;
|
||||
$maxSubChunkY = count($chunk->getSubChunks()) - 1;
|
||||
for(; $maxSubChunkY >= 0; $maxSubChunkY--){
|
||||
if(!$chunk->getSubChunk($maxSubChunkY)->isEmptyFast()){
|
||||
break;
|
||||
|
@ -42,7 +42,7 @@ function build_leveldb {
|
||||
build_leveldb 84348b9b826cc280cde659185695d2170b54824c
|
||||
|
||||
rm -rf php-build
|
||||
git clone https://github.com/php-build/php-build.git
|
||||
git clone https://github.com/pmmp/php-build.git
|
||||
cd php-build
|
||||
./install-dependencies.sh
|
||||
echo '"pthreads",,"https://github.com/pmmp/pthreads.git",,,"extension",' >> share/php-build/extension/definition
|
||||
|
@ -28,6 +28,3 @@ if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
|
||||
if(!extension_loaded('libdeflate')){
|
||||
function libdeflate_deflate_compress(string $data, int $level = 6) : string{}
|
||||
}
|
||||
|
||||
//TODO: these need to be defined properly or removed
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 2) . '/vendor/autoload.php');
|
||||
|
@ -40,11 +40,6 @@ parameters:
|
||||
count: 2
|
||||
path: ../../../src/ServerConfigGroup.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function strtolower expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/ServerConfigGroup.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'git' on mixed\\.$#"
|
||||
count: 2
|
||||
@ -80,21 +75,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/permission/PermissionParser.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$description of method pocketmine\\\\command\\\\Command\\:\\:setDescription\\(\\) expects pocketmine\\\\lang\\\\Translatable\\|string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginBase.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$permissionMessage of method pocketmine\\\\command\\\\Command\\:\\:setPermissionMessage\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginBase.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$usage of method pocketmine\\\\command\\\\Command\\:\\:setUsage\\(\\) expects pocketmine\\\\lang\\\\Translatable\\|string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginBase.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
@ -155,11 +135,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$plugins of class pocketmine\\\\plugin\\\\PluginGraylist constructor expects array\\<string\\>, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginGraylist.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$code of class pocketmine\\\\resourcepacks\\\\ResourcePackException constructor expects int, mixed given\\.$#"
|
||||
count: 1
|
||||
@ -181,9 +156,9 @@ parameters:
|
||||
path: ../../../src/thread/ThreadManager.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:time\\(\\) should return TClosureReturn but returns TClosureReturn\\.$#"
|
||||
message: "#^Parameter \\#1 \\$config of static method pocketmine\\\\utils\\\\Config\\:\\:writeProperties\\(\\) expects array\\<string, bool\\|float\\|int\\|string\\>, array\\<string, mixed\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/timings/TimingsHandler.php
|
||||
path: ../../../src/utils/Config.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$offset of function substr expects int, mixed given\\.$#"
|
||||
@ -195,21 +170,26 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/utils/Internet.php
|
||||
|
||||
-
|
||||
message: "#^Part \\$errno \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/MainLogger.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$trace of static method pocketmine\\\\utils\\\\Utils\\:\\:printableTrace\\(\\) expects array\\<int, array\\<string, mixed\\>\\>, array given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Part \\$errno \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$keys of function array_fill_keys expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user