mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-12 12:25:09 +00:00
Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
325ffec1be | |||
fa715a074a | |||
4caa2c7690 | |||
d04da9b1d8 | |||
633e77a34c | |||
092d130c96 | |||
c09390d20f | |||
22f8623e17 | |||
f04151dbe6 | |||
5dcd8bf289 | |||
b70ff32548 | |||
023460db2c | |||
2910ffebf4 | |||
fea820a99e | |||
7c19f14cf5 | |||
5a54d09869 | |||
1d10107024 | |||
54ae4d0ea2 | |||
0d21e591d1 | |||
408616723c | |||
b162d688a3 | |||
3b09c3a48a | |||
87781cff4d | |||
db0cf4bb5a | |||
a4f2b99ed5 | |||
3ecc980bc4 | |||
107b56154b | |||
f86fde064d | |||
84a16ce69a | |||
d06d3bc871 | |||
11e34b3e5c | |||
f9318bf286 | |||
674b65f789 | |||
a77fc8109f | |||
6102740ee3 | |||
40168a457e | |||
d07acd0013 | |||
9561ae5af7 | |||
56fbd45dd5 | |||
b5dc72b0ee | |||
4ba57f2b03 | |||
df0d72bf61 |
22
.github/workflows/main.yml
vendored
22
.github/workflows/main.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.16, 8.2.3]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
|
||||
steps:
|
||||
- name: Build and prepare PHP cache
|
||||
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -32,13 +32,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.16, 8.2.3]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -71,13 +71,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.16, 8.2.3]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -110,7 +110,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.16, 8.2.3]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -118,7 +118,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -151,13 +151,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.16, 8.2.3]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -206,7 +206,7 @@ jobs:
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer:3.11
|
||||
tools: php-cs-fixer:3.16
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
* [Building and running from source](BUILDING.md)
|
||||
* [Developer documentation](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers
|
||||
* [Latest release API documentation](https://apidoc.pmmp.io) - Doxygen API documentation generated for each release
|
||||
* [Latest bleeding-edge API documentation](https://apidoc-dev.pmmp.io) - Doxygen API documentation generated weekly from `next-major` branch
|
||||
* [Latest bleeding-edge API documentation](https://apidoc-dev.pmmp.io) - Doxygen API documentation generated weekly from `major-next` branch
|
||||
* [DevTools](https://github.com/pmmp/DevTools/) - Development tools plugin for creating plugins
|
||||
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
* [Contributing Guidelines](CONTRIBUTING.md)
|
||||
|
@ -94,3 +94,18 @@ Released 14th April 2023.
|
||||
- Fixed player timings duplication leading to extremely large timings reports when timings runs for a long time with many players.
|
||||
- Packet timings are now indexed by class FQN instead of packet ID. This prevents erroneous timer reuse on packet ID reuse (e.g. multi version servers).
|
||||
- Fixed entity timings being shared by different classes with the same short name. This led to incorrect timings being reported for some entities when custom entities were used.
|
||||
|
||||
# 4.19.3
|
||||
Released 21st April 2023.
|
||||
|
||||
## General
|
||||
- Error IDs for `Packet processing error` disconnects are now split into 4-character chunks to make them easier to type (since they can't be copied from the disconnection screen of a client).
|
||||
|
||||
## Fixes
|
||||
- Fixed entity-block intersections being checked twice per tick. Besides wasting CPU time, this may have caused unexpected behaviour during entity-block interactions with blocks like water or cacti.
|
||||
- Fixed performance issue in network inventory synchronization due item NBT being prepared twice.
|
||||
- Fixed `tools/simulate-chunk-selector.php` argument parsing being completely broken (weird behaviour of PHP `getopt()`).
|
||||
|
||||
## Internals
|
||||
- `TimingsHandler->stopTiming()` now logs an error message if a subtimer wasn't stopped, rather than throwing an exception.
|
||||
- Due to interactions between `try...finally` and unexpected errors, throwing exceptions made it difficult for plugin developers to debug errors in their plugins, since it obscured the original error.
|
||||
|
66
changelogs/4.20.md
Normal file
66
changelogs/4.20.md
Normal file
@ -0,0 +1,66 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.80**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the `pocketmine\network\mcpe` namespace are compatible with any previous 4.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 `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
### Interim releases
|
||||
If you're upgrading from 4.17.x directly to 4.20.x, please also read the following changelogs, as the interim releases contain important changes:
|
||||
|
||||
- [4.18.0](https://github.com/pmmp/PocketMine-MP/blob/4.20.0/changelogs/4.18.md#4180) - major performance improvements, internal network changes, minor API additions
|
||||
- [4.19.0](https://github.com/pmmp/PocketMine-MP/blob/4.20.0/changelogs/4.19.md#4190) - minor performance improvements, improved timings system, minor API additions
|
||||
|
||||
# 4.20.0
|
||||
Released 26th April 2023.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.80.
|
||||
- Removed support for older versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed packet processing error when attempting to use a stonecutter.
|
||||
- Fixed armor slots containing ghost items when cancelling right-click to equip armor.
|
||||
- Fixed crash in `HandlerList->getListenersByPriority()` when no listeners are registered at the given priority.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- The following API methods have been added:
|
||||
- `public BaseSign->getEditorEntityRuntimeId() : int` - returns the entity runtime ID of the player currently editing this sign, or `null` if none
|
||||
- `public BaseSign->setEditorEntityRuntimeId(?int $editorEntityRuntimeId) : $this` - sets the entity runtime ID of the player currently editing this sign
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following API methods have been added:
|
||||
- `public Player->openSignEditor(Vector3 $position) : void` - opens the client-side sign editor GUI for the given position
|
||||
|
||||
# 4.20.1
|
||||
Released 27th April 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when firing a bow while holding arrows in the offhand slot.
|
||||
|
||||
## Internals
|
||||
- `ItemStackContainerIdTranslator::translate()` now requires an additional `int $slotId` parameter and returns `array{int, int}` (translated window ID, translated slot ID) to be used with `InventoryManager->locateWindowAndSlot()`.
|
||||
- `InventoryManager->locateWindowAndSlot()` now checks if the translated slot actually exists in the requested inventory, and returns `null` if not. Previously, it would return potentially invalid slot IDs without checking them, potentially leading to crashes.
|
||||
|
||||
# 4.20.2
|
||||
Released 4th May 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed all types of wooden logs appearing as oak in the inventory.
|
||||
- Fixed a performance issue in `BaseInventory->canAddItem()` (missing `continue` causing useless logic to run).
|
||||
|
||||
# 4.20.3
|
||||
Released 6th May 2023.
|
||||
|
||||
## Improvements
|
||||
- Reduced memory usage of `RuntimeBlockMapping` from 25 MB to 9 MB. Since every thread has its own copy of the block map, this saves a substantial amount of memory.
|
||||
|
||||
## Fixes
|
||||
- Fixed players falling through blocks in spectator mode.
|
||||
- Fixed timings reports getting bloated by prolific usage of `PluginManager->registerEvent()`.
|
||||
- This was caused by creating a new timings handler for each call, regardless of whether a timer already existed for the given event and callback.
|
||||
- Fixed `Full Server Tick` and other records being missing from timings reports.
|
||||
- This was caused by timings handler depth not getting reset when timings was disabled and later re-enabled.
|
@ -34,10 +34,10 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~1.1.1+bedrock-1.19.70",
|
||||
"pocketmine/bedrock-data": "~2.1.1+bedrock-1.19.70",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.1.0+bedrock-1.19.70",
|
||||
"pocketmine/bedrock-protocol": "~20.1.1+bedrock-1.19.70",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~2.1.0+bedrock-1.19.80",
|
||||
"pocketmine/bedrock-data": "~2.2.0+bedrock-1.19.80",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.2.0+bedrock-1.19.80",
|
||||
"pocketmine/bedrock-protocol": "~21.0.0+bedrock-1.19.80",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
@ -56,7 +56,7 @@
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.13",
|
||||
"phpstan/phpstan": "1.10.14",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
103
composer.lock
generated
103
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": "d819da2b086b38c4dd2a5028084e251c",
|
||||
"content-hash": "7ca2d4ed0987ac17cf615d67945f5687",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -67,26 +67,25 @@
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.10.2",
|
||||
"version": "0.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brick/math.git",
|
||||
"reference": "459f2781e1a08d52ee56b0b1444086e038561e3f"
|
||||
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f",
|
||||
"reference": "459f2781e1a08d52ee56b0b1444086e038561e3f",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
|
||||
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": "^7.4 || ^8.0"
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"vimeo/psalm": "4.25.0"
|
||||
"vimeo/psalm": "5.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -111,7 +110,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brick/math/issues",
|
||||
"source": "https://github.com/brick/math/tree/0.10.2"
|
||||
"source": "https://github.com/brick/math/tree/0.11.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -119,7 +118,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-08-10T22:54:19+00:00"
|
||||
"time": "2023-01-15T23:15:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fgrosse/phpasn1",
|
||||
@ -250,16 +249,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "1.1.1",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "e0540343e649a92126a1d4071ec401a811416c76"
|
||||
"reference": "1c07ced86be7d185551082441b5a2b9b7fbd6b21"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/e0540343e649a92126a1d4071ec401a811416c76",
|
||||
"reference": "e0540343e649a92126a1d4071ec401a811416c76",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1c07ced86be7d185551082441b5a2b9b7fbd6b21",
|
||||
"reference": "1c07ced86be7d185551082441b5a2b9b7fbd6b21",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -270,22 +269,22 @@
|
||||
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/1.1.1"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/2.1.0"
|
||||
},
|
||||
"time": "2023-03-08T23:45:59+00:00"
|
||||
"time": "2023-04-19T17:58:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "2.1.1+bedrock-1.19.70",
|
||||
"version": "2.2.0+bedrock-1.19.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "cba0567bcb25f987f2712092f8d662056719e82d"
|
||||
"reference": "33dd83601442b377af42ac91473278243cafd576"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/cba0567bcb25f987f2712092f8d662056719e82d",
|
||||
"reference": "cba0567bcb25f987f2712092f8d662056719e82d",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/33dd83601442b377af42ac91473278243cafd576",
|
||||
"reference": "33dd83601442b377af42ac91473278243cafd576",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -296,22 +295,22 @@
|
||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/2.1.1+bedrock-1.19.70"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.80"
|
||||
},
|
||||
"time": "2023-03-14T18:03:19+00:00"
|
||||
"time": "2023-04-26T20:00:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||
"reference": "aab89a1f121a0c127557a4a0cf981330301c9c45"
|
||||
"reference": "dd804c3f2b1e8990434812627e62eb5bde9670a5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/aab89a1f121a0c127557a4a0cf981330301c9c45",
|
||||
"reference": "aab89a1f121a0c127557a4a0cf981330301c9c45",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/dd804c3f2b1e8990434812627e62eb5bde9670a5",
|
||||
"reference": "dd804c3f2b1e8990434812627e62eb5bde9670a5",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -322,22 +321,22 @@
|
||||
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.1.0"
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.2.0"
|
||||
},
|
||||
"time": "2023-03-08T22:27:13+00:00"
|
||||
"time": "2023-04-19T18:16:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "20.1.2+bedrock-1.19.70",
|
||||
"version": "21.0.1+bedrock-1.19.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "2787c531039b3d92fa3ec92f28b95158dc24b915"
|
||||
"reference": "981ea2e76e207a25c1361df858c639feba5cf348"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2787c531039b3d92fa3ec92f28b95158dc24b915",
|
||||
"reference": "2787c531039b3d92fa3ec92f28b95158dc24b915",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/981ea2e76e207a25c1361df858c639feba5cf348",
|
||||
"reference": "981ea2e76e207a25c1361df858c639feba5cf348",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -369,9 +368,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/20.1.2+bedrock-1.19.70"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/21.0.1+bedrock-1.19.80"
|
||||
},
|
||||
"time": "2023-04-10T11:40:32+00:00"
|
||||
"time": "2023-04-26T21:00:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -994,20 +993,20 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.7.3",
|
||||
"version": "4.7.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "433b2014e3979047db08a17a205f410ba3869cf2"
|
||||
"reference": "60a4c63ab724854332900504274f6150ff26d286"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
|
||||
"reference": "433b2014e3979047db08a17a205f410ba3869cf2",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286",
|
||||
"reference": "60a4c63ab724854332900504274f6150ff26d286",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10",
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
|
||||
"ext-json": "*",
|
||||
"php": "^8.0",
|
||||
"ramsey/collection": "^1.2 || ^2.0"
|
||||
@ -1070,7 +1069,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.3"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1082,20 +1081,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-12T18:13:24+00:00"
|
||||
"time": "2023-04-15T23:01:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v5.4.21",
|
||||
"version": "v5.4.23",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f"
|
||||
"reference": "b2f79d86cd9e7de0fff6d03baa80eaed7a5f38b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
|
||||
"reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b2f79d86cd9e7de0fff6d03baa80eaed7a5f38b5",
|
||||
"reference": "b2f79d86cd9e7de0fff6d03baa80eaed7a5f38b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1130,7 +1129,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v5.4.21"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v5.4.23"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1146,7 +1145,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-14T08:03:56+00:00"
|
||||
"time": "2023-03-02T11:38:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@ -1884,16 +1883,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.13",
|
||||
"version": "1.10.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "f07bf8c6980b81bf9e49d44bd0caf2e737614a70"
|
||||
"reference": "d232901b09e67538e5c86a724be841bea5768a7c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f07bf8c6980b81bf9e49d44bd0caf2e737614a70",
|
||||
"reference": "f07bf8c6980b81bf9e49d44bd0caf2e737614a70",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c",
|
||||
"reference": "d232901b09e67538e5c86a724be841bea5768a7c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1942,7 +1941,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-12T19:29:52+00:00"
|
||||
"time": "2023-04-19T13:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.19.2";
|
||||
public const BASE_VERSION = "4.20.3";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
|
@ -97,6 +97,15 @@ abstract class BaseSign extends Transparent{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onPostPlace() : void{
|
||||
$player = $this->editorEntityRuntimeId !== null ?
|
||||
$this->position->getWorld()->getEntity($this->editorEntityRuntimeId) :
|
||||
null;
|
||||
if($player instanceof Player){
|
||||
$player->openSignEditor($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing information about the sign text.
|
||||
*/
|
||||
@ -110,6 +119,19 @@ abstract class BaseSign extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the runtime entity ID of the player editing this sign. Only this player will be able to edit the sign.
|
||||
* This is used to prevent multiple players from editing the same sign at the same time, and to prevent players
|
||||
* from editing signs they didn't place.
|
||||
*/
|
||||
public function getEditorEntityRuntimeId() : ?int{ return $this->editorEntityRuntimeId; }
|
||||
|
||||
/** @return $this */
|
||||
public function setEditorEntityRuntimeId(?int $editorEntityRuntimeId) : self{
|
||||
$this->editorEntityRuntimeId = $editorEntityRuntimeId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the player controller (network session) to update the sign text, firing events as appropriate.
|
||||
*
|
||||
@ -133,6 +155,7 @@ abstract class BaseSign extends Transparent{
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->setText($ev->getNewText());
|
||||
$this->setEditorEntityRuntimeId(null);
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
return true;
|
||||
}
|
||||
|
@ -45,12 +45,18 @@ class Sign extends Spawnable{
|
||||
public const TAG_TEXT_LINE = "Text%d"; //sprintf()able
|
||||
public const TAG_TEXT_COLOR = "SignTextColor";
|
||||
public const TAG_GLOWING_TEXT = "IgnoreLighting";
|
||||
public const TAG_PERSIST_FORMATTING = "PersistFormatting"; //TAG_Byte
|
||||
/**
|
||||
* This tag is set to indicate that MCPE-117835 has been addressed in whatever version this sign was created.
|
||||
* @see https://bugs.mojang.com/browse/MCPE-117835
|
||||
*/
|
||||
public const TAG_LEGACY_BUG_RESOLVE = "TextIgnoreLegacyBugResolved";
|
||||
|
||||
public const TAG_FRONT_TEXT = "FrontText"; //TAG_Compound
|
||||
public const TAG_BACK_TEXT = "BackText"; //TAG_Compound
|
||||
public const TAG_WAXED = "IsWaxed"; //TAG_Byte
|
||||
public const TAG_LOCKED_FOR_EDITING_BY = "LockedForEditingBy"; //TAG_Long
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
@ -118,12 +124,22 @@ class Sign extends Spawnable{
|
||||
}
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()));
|
||||
|
||||
//the following are not yet used by the server, but needed to roll back any changes to glowing state or colour
|
||||
//if the client uses dye on the sign
|
||||
$nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt(0xff_00_00_00));
|
||||
$nbt->setByte(self::TAG_GLOWING_TEXT, 0);
|
||||
$nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1);
|
||||
$textPolyfill = function(CompoundTag $textTag) : CompoundTag{
|
||||
//the following are not yet used by the server, but needed to roll back any changes to glowing state or colour
|
||||
//if the client uses dye on the sign
|
||||
return $textTag
|
||||
->setInt(self::TAG_TEXT_COLOR, Binary::signInt(0xff_00_00_00))
|
||||
->setByte(self::TAG_GLOWING_TEXT, 0)
|
||||
->setByte(self::TAG_PERSIST_FORMATTING, 1); //TODO: not sure what this is used for
|
||||
};
|
||||
$nbt->setTag(self::TAG_FRONT_TEXT, $textPolyfill(CompoundTag::create()
|
||||
->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()))
|
||||
));
|
||||
//TODO: this is not yet used by the server, but needed to rollback any client-side changes to the back text
|
||||
$nbt->setTag(self::TAG_BACK_TEXT, $textPolyfill(CompoundTag::create()
|
||||
->setString(self::TAG_TEXT_BLOB, "")
|
||||
));
|
||||
$nbt->setByte(self::TAG_WAXED, 0);
|
||||
$nbt->setLong(self::TAG_LOCKED_FOR_EDITING_BY, $this->editorEntityRuntimeId ?? -1);
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class CraftingManager{
|
||||
* @var FurnaceRecipeManager[]
|
||||
* @phpstan-var array<int, FurnaceRecipeManager>
|
||||
*/
|
||||
protected $furnaceRecipeManagers;
|
||||
protected $furnaceRecipeManagers = [];
|
||||
|
||||
/**
|
||||
* @var PotionTypeRecipe[][]
|
||||
|
@ -115,6 +115,8 @@ abstract class Entity{
|
||||
/** @var Block[]|null */
|
||||
protected $blocksAround;
|
||||
|
||||
private bool $checkBlockIntersectionsNextTick = true;
|
||||
|
||||
/** @var Location */
|
||||
protected $location;
|
||||
/** @var Location */
|
||||
@ -649,7 +651,10 @@ abstract class Entity{
|
||||
|
||||
$hasUpdate = false;
|
||||
|
||||
$this->checkBlockIntersections();
|
||||
if($this->checkBlockIntersectionsNextTick){
|
||||
$this->checkBlockIntersections();
|
||||
}
|
||||
$this->checkBlockIntersectionsNextTick = true;
|
||||
|
||||
if($this->location->y <= World::Y_MIN - 16 && $this->isAlive()){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10);
|
||||
@ -1308,6 +1313,7 @@ abstract class Entity{
|
||||
}
|
||||
|
||||
protected function checkBlockIntersections() : void{
|
||||
$this->checkBlockIntersectionsNextTick = false;
|
||||
$vectors = [];
|
||||
|
||||
foreach($this->getBlocksAroundWithEntityInsideActions() as $block){
|
||||
@ -1319,10 +1325,12 @@ abstract class Entity{
|
||||
}
|
||||
}
|
||||
|
||||
$vector = Vector3::sum(...$vectors);
|
||||
if($vector->lengthSquared() > 0){
|
||||
$d = 0.014;
|
||||
$this->motion = $this->motion->addVector($vector->normalize()->multiply($d));
|
||||
if(count($vectors) > 0){
|
||||
$vector = Vector3::sum(...$vectors);
|
||||
if($vector->lengthSquared() > 0){
|
||||
$d = 0.014;
|
||||
$this->motion = $this->motion->addVector($vector->normalize()->multiply($d));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ class HandlerList{
|
||||
* @return RegisteredListener[]
|
||||
*/
|
||||
public function getListenersByPriority(int $priority) : array{
|
||||
return $this->handlerSlots[$priority];
|
||||
return $this->handlerSlots[$priority] ?? [];
|
||||
}
|
||||
|
||||
public function getParent() : ?HandlerList{
|
||||
|
@ -57,8 +57,11 @@ class RegisteredListener{
|
||||
return;
|
||||
}
|
||||
$this->timings->startTiming();
|
||||
($this->handler)($event);
|
||||
$this->timings->stopTiming();
|
||||
try{
|
||||
($this->handler)($event);
|
||||
}finally{
|
||||
$this->timings->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
public function isHandlingCancelled() : bool{
|
||||
|
@ -219,6 +219,7 @@ abstract class BaseInventory implements Inventory{
|
||||
$item = $this->getItem($i);
|
||||
if($item->isNull()){
|
||||
$emptySlots[] = $i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if($slot->canStackWith($item) && $item->getCount() < $item->getMaxStackSize()){
|
||||
|
@ -353,30 +353,35 @@ class Item implements \JsonSerializable{
|
||||
}
|
||||
|
||||
protected function serializeCompoundTag(CompoundTag $tag) : void{
|
||||
$display = $tag->getCompoundTag(self::TAG_DISPLAY) ?? new CompoundTag();
|
||||
$display = $tag->getCompoundTag(self::TAG_DISPLAY);
|
||||
|
||||
$this->hasCustomName() ?
|
||||
$display->setString(self::TAG_DISPLAY_NAME, $this->getCustomName()) :
|
||||
$display->removeTag(self::TAG_DISPLAY_NAME);
|
||||
if($this->customName !== ""){
|
||||
$display ??= new CompoundTag();
|
||||
$display->setString(self::TAG_DISPLAY_NAME, $this->customName);
|
||||
}else{
|
||||
$display?->removeTag(self::TAG_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
if(count($this->lore) > 0){
|
||||
$loreTag = new ListTag();
|
||||
foreach($this->lore as $line){
|
||||
$loreTag->push(new StringTag($line));
|
||||
}
|
||||
$display ??= new CompoundTag();
|
||||
$display->setTag(self::TAG_DISPLAY_LORE, $loreTag);
|
||||
}else{
|
||||
$display->removeTag(self::TAG_DISPLAY_LORE);
|
||||
$display?->removeTag(self::TAG_DISPLAY_LORE);
|
||||
}
|
||||
$display->count() > 0 ?
|
||||
$display !== null && $display->count() > 0 ?
|
||||
$tag->setTag(self::TAG_DISPLAY, $display) :
|
||||
$tag->removeTag(self::TAG_DISPLAY);
|
||||
|
||||
if($this->hasEnchantments()){
|
||||
if(count($this->enchantments) > 0){
|
||||
$ench = new ListTag();
|
||||
foreach($this->getEnchantments() as $enchantmentInstance){
|
||||
$enchantmentIdMap = EnchantmentIdMap::getInstance();
|
||||
foreach($this->enchantments as $enchantmentInstance){
|
||||
$ench->push(CompoundTag::create()
|
||||
->setShort(self::TAG_ENCH_ID, EnchantmentIdMap::getInstance()->toId($enchantmentInstance->getType()))
|
||||
->setShort(self::TAG_ENCH_ID, $enchantmentIdMap->toId($enchantmentInstance->getType()))
|
||||
->setShort(self::TAG_ENCH_LVL, $enchantmentInstance->getLevel())
|
||||
);
|
||||
}
|
||||
@ -385,8 +390,8 @@ class Item implements \JsonSerializable{
|
||||
$tag->removeTag(self::TAG_ENCH);
|
||||
}
|
||||
|
||||
($blockData = $this->getCustomBlockData()) !== null ?
|
||||
$tag->setTag(self::TAG_BLOCK_ENTITY_TAG, clone $blockData) :
|
||||
$this->blockEntityTag !== null ?
|
||||
$tag->setTag(self::TAG_BLOCK_ENTITY_TAG, clone $this->blockEntityTag) :
|
||||
$tag->removeTag(self::TAG_BLOCK_ENTITY_TAG);
|
||||
|
||||
if(count($this->canPlaceOn) > 0){
|
||||
|
@ -205,11 +205,13 @@ class InventoryManager{
|
||||
if($entry === null){
|
||||
return null;
|
||||
}
|
||||
$inventory = $entry->getInventory();
|
||||
$coreSlotId = $entry->mapNetToCore($netSlotId);
|
||||
return $coreSlotId !== null ? [$entry->getInventory(), $coreSlotId] : null;
|
||||
return $coreSlotId !== null && $inventory->slotExists($coreSlotId) ? [$inventory, $coreSlotId] : null;
|
||||
}
|
||||
if(isset($this->networkIdToInventoryMap[$windowId])){
|
||||
return [$this->networkIdToInventoryMap[$windowId], $netSlotId];
|
||||
$inventory = $this->networkIdToInventoryMap[$windowId] ?? null;
|
||||
if($inventory !== null && $inventory->slotExists($netSlotId)){
|
||||
return [$inventory, $netSlotId];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ final class InventoryManagerEntry{
|
||||
public array $itemStackInfos = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @var ItemStack[]
|
||||
* @phpstan-var array<int, ItemStack>
|
||||
*/
|
||||
public array $pendingSyncs = [];
|
||||
|
@ -60,6 +60,7 @@ use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\OpenSignPacket;
|
||||
use pocketmine\network\mcpe\protocol\Packet;
|
||||
use pocketmine\network\mcpe\protocol\PacketDecodeException;
|
||||
use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
@ -893,6 +894,12 @@ class NetworkSession{
|
||||
[
|
||||
//TODO: dynamic flying speed! FINALLY!!!!!!!!!!!!!!!!!
|
||||
new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, 0.05, 0.1),
|
||||
|
||||
//TODO: HACK! In 1.19.80, the client starts falling in our faux spectator mode when it clips into a
|
||||
//block. I have no idea why this works, since we don't actually use the real spectator mode.
|
||||
new AbilitiesLayer(AbilitiesLayer::LAYER_SPECTATOR, [
|
||||
AbilitiesLayer::ABILITY_FLYING => true,
|
||||
], null, null)
|
||||
]
|
||||
)));
|
||||
}
|
||||
@ -1093,6 +1100,10 @@ class NetworkSession{
|
||||
$this->sendDataPacket(ToastRequestPacket::create($title, $body));
|
||||
}
|
||||
|
||||
public function onOpenSignEditor(Vector3 $signPosition, bool $frontSide) : void{
|
||||
$this->sendDataPacket(OpenSignPacket::create(BlockPosition::fromVector3($signPosition), $frontSide));
|
||||
}
|
||||
|
||||
public function tick() : void{
|
||||
if(!$this->isConnected()){
|
||||
$this->dispose();
|
||||
|
@ -110,6 +110,12 @@ final class ItemTranslator{
|
||||
//new item without a fixed legacy ID - we can't handle this right now
|
||||
continue;
|
||||
}
|
||||
if(isset($complexMappings[$newId]) && $complexMappings[$newId][0] === $intId && $complexMappings[$newId][1] <= $meta){
|
||||
//TODO: HACK! Multiple legacy ID/meta pairs can be mapped to the same new ID (see minecraft:log)
|
||||
//Assume that the first one is the most relevant for now
|
||||
//However, this could catch fire in the future if this assumption is broken
|
||||
continue;
|
||||
}
|
||||
$complexMappings[$newId] = [$intId, (int) $meta];
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,12 @@ use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockLegacyIds;
|
||||
use pocketmine\data\bedrock\BedrockDataFiles;
|
||||
use pocketmine\data\bedrock\LegacyBlockIdToStringIdMap;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\Filesystem;
|
||||
@ -53,15 +58,45 @@ final class RuntimeBlockMapping{
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $keyIndex
|
||||
* @param (ByteTag|StringTag|IntTag)[][] $valueIndex
|
||||
* @phpstan-param array<string, string> $keyIndex
|
||||
* @phpstan-param array<int, array<int|string, ByteTag|IntTag|StringTag>> $valueIndex
|
||||
*/
|
||||
private static function deduplicateCompound(CompoundTag $tag, array &$keyIndex, array &$valueIndex) : CompoundTag{
|
||||
if($tag->count() === 0){
|
||||
return $tag;
|
||||
}
|
||||
|
||||
$newTag = CompoundTag::create();
|
||||
foreach($tag as $key => $value){
|
||||
$key = $keyIndex[$key] ??= $key;
|
||||
|
||||
if($value instanceof CompoundTag){
|
||||
$value = $valueIndex[$value->getType()][(new LittleEndianNbtSerializer())->write(new TreeRoot($value))] ??= self::deduplicateCompound($value, $keyIndex, $valueIndex);
|
||||
}elseif($value instanceof ByteTag || $value instanceof IntTag || $value instanceof StringTag){
|
||||
$value = $valueIndex[$value->getType()][$value->getValue()] ??= $value;
|
||||
}
|
||||
|
||||
$newTag->setTag($key, $value);
|
||||
}
|
||||
|
||||
return $newTag;
|
||||
}
|
||||
|
||||
public function __construct(string $canonicalBlockStatesFile, string $r12ToCurrentBlockMapFile){
|
||||
$stream = new BinaryStream(Filesystem::fileGetContents($canonicalBlockStatesFile));
|
||||
$list = [];
|
||||
$nbtReader = new NetworkNbtSerializer();
|
||||
|
||||
$keyIndex = [];
|
||||
$valueIndex = [];
|
||||
while(!$stream->feof()){
|
||||
$offset = $stream->getOffset();
|
||||
$blockState = $nbtReader->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$stream->setOffset($offset);
|
||||
$list[] = $blockState;
|
||||
$list[] = self::deduplicateCompound($blockState, $keyIndex, $valueIndex);
|
||||
}
|
||||
$this->bedrockKnownStates = $list;
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\convert;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockLegacyIds;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\Item;
|
||||
@ -135,14 +136,16 @@ class TypeConverter{
|
||||
if($itemStack->isNull()){
|
||||
return ItemStack::null();
|
||||
}
|
||||
$nbt = null;
|
||||
if($itemStack->hasNamedTag()){
|
||||
$nbt = clone $itemStack->getNamedTag();
|
||||
$nbt = $itemStack->getNamedTag();
|
||||
if($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
}else{
|
||||
$nbt = clone $nbt;
|
||||
}
|
||||
|
||||
$isBlockItem = $itemStack->getId() < 256;
|
||||
|
||||
$idMeta = ItemTranslator::getInstance()->toNetworkIdQuiet($itemStack->getId(), $itemStack->getMeta());
|
||||
$internalId = $itemStack->getId();
|
||||
$internalMeta = $itemStack->getMeta();
|
||||
$idMeta = ItemTranslator::getInstance()->toNetworkIdQuiet($internalId, $internalMeta);
|
||||
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.
|
||||
@ -150,8 +153,8 @@ class TypeConverter{
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_ID_TAG, $itemStack->getId());
|
||||
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
|
||||
$nbt->setInt(self::PM_ID_TAG, $internalId);
|
||||
$nbt->setInt(self::PM_META_TAG, $internalMeta);
|
||||
}else{
|
||||
[$id, $meta] = $idMeta;
|
||||
|
||||
@ -166,23 +169,15 @@ class TypeConverter{
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
|
||||
$meta = 0;
|
||||
}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());
|
||||
$meta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$blockRuntimeId = 0;
|
||||
if($isBlockItem){
|
||||
if($internalId < 256){
|
||||
$block = $itemStack->getBlock();
|
||||
if($block->getId() !== BlockLegacyIds::AIR){
|
||||
$blockRuntimeId = RuntimeBlockMapping::getInstance()->toRuntimeId($block->getFullId());
|
||||
$meta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,6 +203,11 @@ class TypeConverter{
|
||||
$compound = $itemStack->getNbt();
|
||||
|
||||
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkId($itemStack->getId(), $itemStack->getMeta());
|
||||
if($itemStack->getBlockRuntimeId() !== 0){
|
||||
//blockitem meta is zeroed out by the client, so we have to infer it from the block runtime ID
|
||||
$blockFullId = RuntimeBlockMapping::getInstance()->fromRuntimeId($itemStack->getBlockRuntimeId());
|
||||
$meta = $blockFullId & Block::INTERNAL_METADATA_MASK;
|
||||
}
|
||||
|
||||
if($compound !== null){
|
||||
$compound = clone $compound;
|
||||
@ -222,12 +222,6 @@ class TypeConverter{
|
||||
$compound->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$compound->setTag(self::DAMAGE_TAG, $conflicted);
|
||||
}
|
||||
}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
|
||||
//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.
|
||||
$meta = $metaTag->getValue();
|
||||
$compound->removeTag(self::PM_META_TAG);
|
||||
}
|
||||
if($compound->count() === 0){
|
||||
$compound = null;
|
||||
|
@ -328,6 +328,9 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if(count($packet->trData->getActions()) > 50){
|
||||
throw new PacketHandlingException("Too many actions in inventory transaction");
|
||||
}
|
||||
if(count($packet->requestChangedSlots) > 10){
|
||||
throw new PacketHandlingException("Too many slot sync requests in inventory transaction");
|
||||
}
|
||||
|
||||
$this->inventoryManager->setCurrentItemStackRequestId($packet->requestId);
|
||||
$this->inventoryManager->addRawPredictedSlotChanges($packet->trData->getActions());
|
||||
@ -347,6 +350,21 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
|
||||
|
||||
//requestChangedSlots asks the server to always send out the contents of the specified slots, even if they
|
||||
//haven't changed. Handling these is necessary to ensure the client inventory stays in sync if the server
|
||||
//rejects the transaction. The most common example of this is equipping armor by right-click, which doesn't send
|
||||
//a legacy prediction action for the destination armor slot.
|
||||
foreach($packet->requestChangedSlots as $containerInfo){
|
||||
foreach($containerInfo->getChangedSlotIndexes() as $netSlot){
|
||||
[$windowId, $slot] = ItemStackContainerIdTranslator::translate($containerInfo->getContainerId(), $this->inventoryManager->getCurrentWindowId(), $netSlot);
|
||||
$inventoryAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slot);
|
||||
if($inventoryAndSlot !== null){ //trigger the normal slot sync logic
|
||||
$this->inventoryManager->onSlotChange($inventoryAndSlot[0], $inventoryAndSlot[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->inventoryManager->setCurrentItemStackRequestId(null);
|
||||
return $result;
|
||||
}
|
||||
@ -730,7 +748,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if(!($nbt instanceof CompoundTag)) throw new AssumptionFailedError("PHPStan should ensure this is a CompoundTag"); //for phpstorm's benefit
|
||||
|
||||
if($block instanceof BaseSign){
|
||||
if(($textBlobTag = $nbt->getTag(Sign::TAG_TEXT_BLOB)) instanceof StringTag){
|
||||
if(($textBlobTag = $nbt->getCompoundTag(Sign::TAG_FRONT_TEXT)?->getTag(Sign::TAG_TEXT_BLOB)) instanceof StringTag){
|
||||
try{
|
||||
$text = SignText::fromBlob($textBlobTag->getValue());
|
||||
}catch(\InvalidArgumentException $e){
|
||||
|
@ -33,15 +33,21 @@ final class ItemStackContainerIdTranslator{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public static function translate(int $containerInterfaceId, int $currentWindowId) : int{
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
* @throws PacketHandlingException
|
||||
*/
|
||||
public static function translate(int $containerInterfaceId, int $currentWindowId, int $slotId) : array{
|
||||
return match($containerInterfaceId){
|
||||
ContainerUIIds::ARMOR => ContainerIds::ARMOR,
|
||||
ContainerUIIds::ARMOR => [ContainerIds::ARMOR, $slotId],
|
||||
|
||||
ContainerUIIds::HOTBAR,
|
||||
ContainerUIIds::INVENTORY,
|
||||
ContainerUIIds::COMBINED_HOTBAR_AND_INVENTORY => ContainerIds::INVENTORY,
|
||||
ContainerUIIds::COMBINED_HOTBAR_AND_INVENTORY => [ContainerIds::INVENTORY, $slotId],
|
||||
|
||||
ContainerUIIds::OFFHAND => ContainerIds::OFFHAND,
|
||||
//TODO: HACK! The client sends an incorrect slot ID for the offhand as of 1.19.70 (though this doesn't really matter since the offhand has only 1 slot anyway)
|
||||
ContainerUIIds::OFFHAND => [ContainerIds::OFFHAND, 0],
|
||||
|
||||
ContainerUIIds::ANVIL_INPUT,
|
||||
ContainerUIIds::ANVIL_MATERIAL,
|
||||
@ -68,7 +74,7 @@ final class ItemStackContainerIdTranslator{
|
||||
ContainerUIIds::TRADE2_INGREDIENT1,
|
||||
ContainerUIIds::TRADE2_INGREDIENT2,
|
||||
ContainerUIIds::TRADE_INGREDIENT1,
|
||||
ContainerUIIds::TRADE_INGREDIENT2 => ContainerIds::UI,
|
||||
ContainerUIIds::TRADE_INGREDIENT2 => [ContainerIds::UI, $slotId],
|
||||
|
||||
ContainerUIIds::BARREL,
|
||||
ContainerUIIds::BLAST_FURNACE_INGREDIENT,
|
||||
@ -80,7 +86,7 @@ final class ItemStackContainerIdTranslator{
|
||||
ContainerUIIds::FURNACE_RESULT,
|
||||
ContainerUIIds::LEVEL_ENTITY, //chest
|
||||
ContainerUIIds::SHULKER_BOX,
|
||||
ContainerUIIds::SMOKER_INGREDIENT => $currentWindowId,
|
||||
ContainerUIIds::SMOKER_INGREDIENT => [$currentWindowId, $slotId],
|
||||
|
||||
//all preview slots are ignored, since the client shouldn't be modifying those directly
|
||||
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\handler;
|
||||
|
||||
use pocketmine\crafting\CraftingGrid;
|
||||
use pocketmine\inventory\CreativeInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\action\CreateItemAction;
|
||||
@ -112,12 +111,7 @@ class ItemStackRequestExecutor{
|
||||
* @throws ItemStackRequestProcessException
|
||||
*/
|
||||
protected function getBuilderInventoryAndSlot(ItemStackRequestSlotInfo $info) : array{
|
||||
$windowId = ItemStackContainerIdTranslator::translate($info->getContainerId(), $this->inventoryManager->getCurrentWindowId());
|
||||
$slotId = $info->getSlotId();
|
||||
if($info->getContainerId() === ContainerUIIds::OFFHAND && $slotId === 1){
|
||||
//TODO: HACK! The client sends an incorrect slot ID for the offhand as of 1.19.70
|
||||
$slotId = 0;
|
||||
}
|
||||
[$windowId, $slotId] = ItemStackContainerIdTranslator::translate($info->getContainerId(), $this->inventoryManager->getCurrentWindowId(), $info->getSlotId());
|
||||
$windowAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slotId);
|
||||
if($windowAndSlot === null){
|
||||
throw new ItemStackRequestProcessException("No open inventory matches container UI ID: " . $info->getContainerId() . ", slot ID: " . $info->getSlotId());
|
||||
@ -246,13 +240,11 @@ class ItemStackRequestExecutor{
|
||||
|
||||
$this->specialTransaction = new CraftingTransaction($this->player, $craftingManager, [], $recipe, $repetitions);
|
||||
|
||||
$currentWindow = $this->player->getCurrentWindow();
|
||||
if($currentWindow !== null && !($currentWindow instanceof CraftingGrid)){
|
||||
throw new ItemStackRequestProcessException("Player's current window is not a crafting grid");
|
||||
}
|
||||
$craftingGrid = $currentWindow ?? $this->player->getCraftingGrid();
|
||||
|
||||
$craftingResults = $recipe->getResultsFor($craftingGrid);
|
||||
//TODO: Since the system assumes that crafting can only be done in the crafting grid, we have to give it a
|
||||
//crafting grid to make the API happy. No implementation of getResultsFor() actually uses the crafting grid
|
||||
//right now, so this will work, but this will become a problem in the future for things like shulker boxes and
|
||||
//custom crafting recipes.
|
||||
$craftingResults = $recipe->getResultsFor($this->player->getCraftingGrid());
|
||||
foreach($craftingResults as $k => $craftingResult){
|
||||
$craftingResult->setCount($craftingResult->getCount() * $repetitions);
|
||||
$this->craftingResults[$k] = $craftingResult;
|
||||
|
@ -53,11 +53,7 @@ final class ItemStackResponseBuilder{
|
||||
* @phpstan-return array{Inventory, int}
|
||||
*/
|
||||
private function getInventoryAndSlot(int $containerInterfaceId, int $slotId) : ?array{
|
||||
if($containerInterfaceId === ContainerUIIds::OFFHAND && $slotId === 1){
|
||||
//TODO: HACK! The client sends an incorrect slot ID for the offhand as of 1.19.70
|
||||
$slotId = 0;
|
||||
}
|
||||
$windowId = ItemStackContainerIdTranslator::translate($containerInterfaceId, $this->inventoryManager->getCurrentWindowId());
|
||||
[$windowId, $slotId] = ItemStackContainerIdTranslator::translate($containerInterfaceId, $this->inventoryManager->getCurrentWindowId(), $slotId);
|
||||
$windowAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slotId);
|
||||
if($windowAndSlot === null){
|
||||
return null;
|
||||
|
@ -105,6 +105,7 @@ class PreSpawnPacketHandler extends PacketHandler{
|
||||
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
|
||||
Uuid::fromString(Uuid::NIL),
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
0,
|
||||
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries(),
|
||||
|
@ -53,6 +53,7 @@ use function implode;
|
||||
use function mt_rand;
|
||||
use function random_bytes;
|
||||
use function rtrim;
|
||||
use function str_split;
|
||||
use function substr;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
@ -196,7 +197,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
try{
|
||||
$session->handleEncoded($buf);
|
||||
}catch(PacketHandlingException $e){
|
||||
$errorId = bin2hex(random_bytes(6));
|
||||
$errorId = implode("-", str_split(bin2hex(random_bytes(6)), 4));
|
||||
|
||||
$logger = $session->getLogger();
|
||||
$logger->error("Bad packet (error ID $errorId): " . $e->getMessage());
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\player;
|
||||
|
||||
use pocketmine\block\BaseSign;
|
||||
use pocketmine\block\Bed;
|
||||
use pocketmine\block\BlockLegacyIds;
|
||||
use pocketmine\block\UnknownBlock;
|
||||
@ -2626,6 +2627,20 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->permanentWindows = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the player's sign editor GUI for the sign at the given position.
|
||||
* TODO: add support for editing the rear side of the sign (not currently supported due to technical limitations)
|
||||
*/
|
||||
public function openSignEditor(Vector3 $position) : void{
|
||||
$block = $this->getWorld()->getBlock($position);
|
||||
if($block instanceof BaseSign){
|
||||
$this->getWorld()->setBlock($position, $block->setEditorEntityRuntimeId($this->getId()));
|
||||
$this->getNetworkSession()->onOpenSignEditor($position, true);
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Block at this position is not a sign");
|
||||
}
|
||||
}
|
||||
|
||||
use ChunkListenerNoOpTrait {
|
||||
onChunkChanged as private;
|
||||
onChunkUnloaded as private;
|
||||
|
@ -37,7 +37,7 @@ use pocketmine\permission\DefaultPermissions;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\permission\PermissionParser;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
@ -651,7 +651,7 @@ class PluginManager{
|
||||
throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled");
|
||||
}
|
||||
|
||||
$timings = new TimingsHandler($handlerName . "(" . (new \ReflectionClass($event))->getShortName() . ")", group: $plugin->getDescription()->getFullName());
|
||||
$timings = Timings::getEventHandlerTimings($event, $handlerName, $plugin->getDescription()->getFullName());
|
||||
|
||||
$registeredListener = new RegisteredListener($handler, $priority, $plugin, $handleCancelled, $timings);
|
||||
HandlerListManager::global()->getListFor($event)->register($registeredListener);
|
||||
|
@ -166,6 +166,8 @@ abstract class Timings{
|
||||
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $events = [];
|
||||
/** @var TimingsHandler[][] */
|
||||
private static array $eventHandlers = [];
|
||||
|
||||
public static function init() : void{
|
||||
if(self::$initialized){
|
||||
@ -336,4 +338,16 @@ abstract class Timings{
|
||||
|
||||
return self::$events[$eventClass];
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TEvent of Event
|
||||
* @phpstan-param class-string<TEvent> $event
|
||||
*/
|
||||
public static function getEventHandlerTimings(string $event, string $handlerName, string $group) : TimingsHandler{
|
||||
if(!isset(self::$eventHandlers[$event][$handlerName])){
|
||||
self::$eventHandlers[$event][$handlerName] = new TimingsHandler($handlerName . "(" . self::shortenCoreClassName($event, "pocketmine\\event\\") . ")", group: $group);
|
||||
}
|
||||
|
||||
return self::$eventHandlers[$event][$handlerName];
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ class TimingsHandler{
|
||||
}
|
||||
|
||||
public static function reload() : void{
|
||||
TimingsRecord::clearRecords();
|
||||
TimingsRecord::reset();
|
||||
if(self::$enabled){
|
||||
self::$timingStart = hrtime(true);
|
||||
}
|
||||
@ -189,12 +189,12 @@ class TimingsHandler{
|
||||
}
|
||||
|
||||
$record = TimingsRecord::getCurrentRecord();
|
||||
if($record !== null){
|
||||
if($record->getTimerId() !== spl_object_id($this)){
|
||||
throw new \LogicException("Timer \"" . $record->getName() . "\" should have been stopped before stopping timer \"" . $this->name . "\"");
|
||||
}
|
||||
$timerId = spl_object_id($this);
|
||||
for(; $record !== null && $record->getTimerId() !== $timerId; $record = TimingsRecord::getCurrentRecord()){
|
||||
\GlobalLogger::get()->error("Timer \"" . $record->getName() . "\" should have been stopped before stopping timer \"" . $this->name . "\"");
|
||||
$record->stopTiming($now);
|
||||
}
|
||||
$record?->stopTiming($now);
|
||||
if($this->parent !== null){
|
||||
$this->parent->internalStopTiming($now);
|
||||
}
|
||||
@ -219,8 +219,9 @@ class TimingsHandler{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function destroyCycles() : void{
|
||||
public function reset() : void{
|
||||
$this->rootRecord = null;
|
||||
$this->recordsByParent = [];
|
||||
$this->timingDepth = 0;
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,12 @@ final class TimingsRecord{
|
||||
|
||||
private static ?self $currentRecord = null;
|
||||
|
||||
public static function clearRecords() : void{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function reset() : void{
|
||||
foreach(self::$records as $record){
|
||||
$record->handler->destroyCycles();
|
||||
$record->handler->reset();
|
||||
}
|
||||
self::$records = [];
|
||||
self::$currentRecord = null;
|
||||
|
@ -24,12 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\tools\simulate_chunk_selector;
|
||||
|
||||
use pocketmine\player\ChunkSelector;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\World;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function fwrite;
|
||||
@ -128,7 +126,12 @@ if(count(getopt("", ["help"])) !== 0){
|
||||
exit(0);
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys(getopt("", ["radius:", "baseX:", "baseZ:", "scale:", "chunksPerStep:"])) as $name => $value){
|
||||
$opts = getopt("", ["radius:", "baseX:", "baseZ:", "scale:", "chunksPerStep:", "output:"]);
|
||||
foreach(["radius", "baseX", "baseZ", "scale", "chunksPerStep"] as $name){
|
||||
$value = $opts[$name] ?? null;
|
||||
if($value === null){
|
||||
continue;
|
||||
}
|
||||
if(!is_string($value) || (string) ((int) $value) !== $value){
|
||||
fwrite(STDERR, "Value for --$name must be an integer\n");
|
||||
exit(1);
|
||||
@ -139,8 +142,7 @@ foreach(Utils::stringifyKeys(getopt("", ["radius:", "baseX:", "baseZ:", "scale:"
|
||||
"baseX" => ($baseX = $value),
|
||||
"baseZ" => ($baseZ = $value),
|
||||
"scale" => ($scale = $value),
|
||||
"chunksPerStep" => ($nChunksPerStep = $value),
|
||||
default => throw new AssumptionFailedError("getopt() returned unknown option")
|
||||
"chunksPerStep" => ($nChunksPerStep = $value)
|
||||
};
|
||||
}
|
||||
if($radius === null){
|
||||
@ -149,10 +151,10 @@ if($radius === null){
|
||||
}
|
||||
|
||||
$outputDirectory = null;
|
||||
foreach(Utils::stringifyKeys(getopt("", ["output:"])) as $name => $value){
|
||||
assert($name === "output");
|
||||
if(isset($opts["output"])){
|
||||
$value = $opts["output"];
|
||||
if(!is_string($value)){
|
||||
fwrite(STDERR, "Value for --$name must be a string\n");
|
||||
fwrite(STDERR, "Value for --output be a string\n");
|
||||
exit(1);
|
||||
}
|
||||
if(!@mkdir($value) && !is_dir($value)){
|
||||
|
Reference in New Issue
Block a user