Compare commits

...

516 Commits

Author SHA1 Message Date
b5fc31a781 Release 4.22.0 2023-06-07 21:23:54 +01:00
441f1f534f Random change PHP-CS-Fixer wanted to make 2023-06-07 20:59:32 +01:00
e747478afd and one more 2023-06-07 20:58:15 +01:00
92c45dd7e1 Fixed PHPUnit deprecation warnings 2023-06-07 20:57:43 +01:00
2538880408 1.20.0 2023-06-07 20:56:59 +01:00
c715efb18e Jukebox: fix music not stopping when destroyed by explosion
closes #5794
2023-06-03 21:22:26 +01:00
6678360c00 Make changelogs less infuriating in PhpStorm 2023-06-02 13:34:16 +01:00
3db45b6a68 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-06-01 14:51:14 +01:00
3e87ad281f Use tagged fork of netresearch/jsonmapper
we need this for it to not be a huge pain in the ass to install PM as a composer dependency, which plugin CIs may do.
2023-06-01 14:51:04 +01:00
b72da777eb Bump tests/plugins/DevTools from a67f9af to a2f36e8 (#5785)
Bumps [tests/plugins/DevTools](https://github.com/pmmp/DevTools) from `a67f9af` to `a2f36e8`.
- [Release notes](https://github.com/pmmp/DevTools/releases)
- [Commits](a67f9af8d6...a2f36e8dbf)

---
updated-dependencies:
- dependency-name: tests/plugins/DevTools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 13:45:20 +01:00
1101f35c17 Update setup-php-action to 2.0.0
this version vastly improves build time by using optimized prebuilts
instead of building the binaries on the runner.
2023-05-31 22:09:33 +01:00
3948dc4f75 Remove calls to ReflectionProperty::setAccessible() (#5783)
This is a no-op in PHP 8.1 and up.
2023-05-31 14:03:14 +01:00
20942b37eb Update composer dependencies 2023-05-30 16:07:01 +01:00
d343db8750 4.21.2 is next 2023-05-30 14:42:59 +01:00
f2df702c67 Release 4.21.1 2023-05-30 14:42:59 +01:00
481270e6aa Merge tag '4.20.5' into stable 2023-05-30 14:42:11 +01:00
e7bdaa8579 Release 4.20.5 2023-05-30 14:35:17 +01:00
76749cbaa7 Use fork of JsonMapper to solve cweiske/jsonmapper#210 2023-05-30 14:30:28 +01:00
a897bdfaa0 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-05-30 14:17:21 +01:00
09668a37d6 Use fork of JsonMapper to solve cweiske/JsonMapper#210 2023-05-30 14:17:09 +01:00
ea92a23d0d Bump build/php from b1d5c0d to f2ece7b (#5765)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `b1d5c0d` to `f2ece7b`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](b1d5c0d737...f2ece7b30d)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-29 21:37:18 +01:00
691e67018d Bump phpstan/phpstan-phpunit from 1.3.11 to 1.3.13 (#5772)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.3.11 to 1.3.13.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.3.11...1.3.13)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-29 21:36:58 +01:00
fe2140a716 Bump shivammathur/setup-php from 2.25.1 to 2.25.2 (#5766)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.25.1 to 2.25.2.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.25.1...2.25.2)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-29 21:36:46 +01:00
57330a7186 Bump build/php from f860ade to b1d5c0d (#5760)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `f860ade` to `b1d5c0d`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](f860ade30a...b1d5c0d737)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 12:27:12 +01:00
9ddac21de0 Bump shivammathur/setup-php from 2.24.0 to 2.25.1 (#5711)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.24.0 to 2.25.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.24.0...2.25.1)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-21 00:13:22 +01:00
c91aa24daa Bump phpunit/phpunit from 9.6.8 to 10.1.3 (#5753)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.6.8 to 10.1.3.
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.1.3/ChangeLog-10.1.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.6.8...10.1.3)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-19 15:09:24 +01:00
6186fc0bfe 4.21.1 is next 2023-05-17 16:45:35 +01:00
ef40934d24 Release 4.21.0 2023-05-17 16:45:32 +01:00
69b668355f Merge branch 'minor-next' into stable 2023-05-17 16:12:24 +01:00
0547383296 Update build/php submodule to pmmp/PHP-Binaries@f860ade30a 2023-05-17 15:08:05 +01:00
c7dff9ea40 bootstrap: remove ext-parallel bootstrapping code
I have no intention of using parallel, so this code is not necessary.
2023-05-17 14:11:43 +01:00
043350753b Drop PHP 8.0, 8.1 is now minimum version 2023-05-17 13:53:57 +01:00
5ad8016b99 Merge branch 'stable' into minor-next 2023-05-17 13:44:45 +01:00
2e5b2eed6e Update composer dependencies 2023-05-17 13:43:28 +01:00
5a0cde49cc AsyncPool: do not double-check progress updates on finished tasks
checkProgressUpdates is called directly before onCompletion, so we only need to call it again if the task isn't finished yet.
2023-05-16 23:37:58 +01:00
008a022ec1 Players now have finite resources in spectator mode
this seems like the logical solution for the block picking issues.
2023-05-16 23:02:33 +01:00
5c85a7c306 Merge remote-tracking branch 'origin/stable' into minor-next 2023-05-16 22:54:53 +01:00
599c4284f5 Introduce 10 KB threshold for async compression
due to the extremely large performance cost of instantiating AsyncTasks, it's usually not worth bothering with async compression except for very large packets.
While this large overhead can be significantly reduced by using specialized threads, it's early days in the testing stages for such improvements, and for now, we still have this to deal with.

Since async compression is always used prior to player spawn, this change may slightly improve the performance of the pre-join stage of the game.
2023-05-16 22:54:06 +01:00
9499e2e595 always the CS... 2023-05-16 14:22:03 +01:00
a4fea1444a Remove validateCallableSignature() calls from network hot paths
we rely on phpstan for validation of this internally, and plugins shouldn't be calling these methods anyway.
this significantly reduces the overhead of CompressBatchPromise.
2023-05-16 14:21:32 +01:00
1ba47802a8 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-05-15 14:59:02 +01:00
9d111e13f1 CONTRIBUTING: added table of in-house dependencies and which classes, functions or namespaces they contain 2023-05-15 14:58:31 +01:00
44bc4d8c7c Bump phpstan/phpstan from 1.10.14 to 1.10.15 (#5741)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.14 to 1.10.15.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.14...1.10.15)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-10 15:02:59 +01:00
d317347a9b WorldTimings: remove TODO
I tried this, and it didn't really provide any information that the tree table didn't already show.
2023-05-08 16:35:30 +01:00
077fac84bf Added aggregate timers for all world timings
this allows timings list view to display totals for these sections. It does make the tree view a bit more annoying in some cases though.
2023-05-08 16:27:46 +01:00
fdb3a5b121 Fixed incorrect implementation of peak timings 2023-05-07 18:29:37 +01:00
e3bc36ab5b Merge branch 'stable' into minor-next 2023-05-06 18:26:47 +01:00
283ff28aa9 4.20.5 is next 2023-05-06 18:20:19 +01:00
c3ceeeace7 Release 4.20.4 2023-05-06 18:20:18 +01:00
aac4f6c0e1 Fixed all game modes allowing flight
moral of the story: do not trust that mojang things do what they say they do - the spectator ability layer always applies, regardless of whether the player is actually in spectator mode or not ...
2023-05-06 18:18:05 +01:00
bb60a9057f Merge branch 'stable' into minor-next 2023-05-06 17:08:29 +01:00
3b893961e4 4.20.4 is next 2023-05-06 17:01:49 +01:00
325ffec1be Release 4.20.3 2023-05-06 17:01:49 +01:00
fa715a074a Fixed TimingsHandler depth not getting reset when timings is disabled
When timings was disabled, internalStopTiming is not called, and timer depth is not decremented.
If timings is later reenabled, the next call to internalStartTiming will think the timer is already running, and won't generate any new records for the timer.
This has led to broken timings reports with missing Full Server Tick entries, amongst other things.
2023-05-06 16:56:39 +01:00
4caa2c7690 NetworkSession: send FLYING flag on spectator ability layer
fixes #5722

I'm not very clear why this works. PM doesn't use real spectator mode yet (we're still using the faux spectator mode PM has had for years, because I haven't yet assessed how real spectator mode will affect stuff like block interactions), so this ability layer shouldn't have any effect.

thank you @Alemiz112
2023-05-06 15:54:23 +01:00
d04da9b1d8 Reuse timings handlers for event handlers of the same events
due to direct repeated usage of registerEvent() with closures, we've seen some libraries like muqsit/SimplePacketHandler generate very large timings reports, because a new timings handler gets created every time a plugin registers or unregisters a new packet handler callback.

This change fixes the problem by ensuring that any handlers derived from the same function, handling the same event class, will share the same timer.
2023-05-06 15:42:52 +01:00
02cf5ed388 RuntimeBlockMapping: lazy-load NBT blockstates
this saves a considerable amount of memory.

we don't actually need this state array in PM4 anyway, since we don't support the client-side chunk cache yet.
when the time comes to support it, it'll be much more practical to cache binary states and copy bytes anyway, instead of doing it the current way, which is both slow and memory-intensive.

Measured footprint change: 9 MB -> 400 KB.
2023-05-05 16:18:03 +01:00
6cad559dbe Merge branch 'stable' into minor-next 2023-05-05 16:08:30 +01:00
84a943bcec BaseInventory: slap a TODO on isSlotEmpty() 2023-05-05 16:06:37 +01:00
633e77a34c RuntimeBlockMapping: share states CompoundTags if they are the same
this allows saving about 4 MB of memory, because there are many blocks which have identical states, although they have different IDs.

this relies on a potentially risky assumption that the tags in knownStates won't be modified. If they are modified, the changes will influence all blockstates which share the tag.
However, I don't expect this to happen, and the 4 MB memory saving is substantial enough to be worth the risk.
2023-05-04 23:21:54 +01:00
092d130c96 RuntimeBlockMapping: borrow a hack from PM5 to reduce memory footprint
we can't change the internals of this on a patch release, but this hack provides a 12 MB memory usage reduction, which is very significant.
2023-05-04 23:01:10 +01:00
c09390d20f 4.20.3 is next 2023-05-04 21:06:30 +01:00
22f8623e17 Release 4.20.2 2023-05-04 21:06:27 +01:00
f04151dbe6 README: next-major branch was renamed (#5731)
[ci skip]
2023-05-01 14:08:20 +01:00
5dcd8bf289 Bump symfony/filesystem from 5.4.21 to 5.4.23 (#5730)
Bumps [symfony/filesystem](https://github.com/symfony/filesystem) from 5.4.21 to 5.4.23.
- [Release notes](https://github.com/symfony/filesystem/releases)
- [Changelog](https://github.com/symfony/filesystem/blob/6.2/CHANGELOG.md)
- [Commits](https://github.com/symfony/filesystem/compare/v5.4.21...v5.4.23)

---
updated-dependencies:
- dependency-name: symfony/filesystem
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-01 13:30:02 +01:00
b70ff32548 ItemTranslator: Fixed log items not displaying correctly on the client
closes #5724

this uses a (potentially bogus) assumption that the lowest mapped meta value associated with an ID is valid. I don't want to break this during a patch release, and this works for now.

In the future it would probably make more sense to bypass ItemTranslator entirely, and rely solely on the blockstate returned by RuntimeBlockMapping to fetch the correct ID. This is similar to how we serialize items for saving on disk in PM5.
2023-04-28 13:54:40 +01:00
73bf5d4b29 DoubleChestInventory: specialize isSlotEmpty() and getMatchingItemCount() 2023-04-27 21:17:55 +01:00
eb136e60c8 BaseInventory: added getMatchingItemCount() helper
this eliminates the performance issues described by #5719.
closes #5719

we may want to consider exposing a public API for this in the future, since it might be useful for plugins.
2023-04-27 21:08:35 +01:00
4228880509 BaseInventory: change dumb variable names in internalAddItem() 2023-04-27 20:29:02 +01:00
709d874204 BaseInventory: clean up max stack size handling
we can safely assume that:
- the inventory's max stack size won't change during the operation
- two items which stack together have the same max stack size
- the item's max stack size won't change during the operation
2023-04-27 20:27:05 +01:00
07dc10d6e6 World: improve performance of tickChunks() selection process (#5721)
Since light population is required to make a chunk tickable, a chunk may not be tickable for some time if the async workers get backlogged.
The previous version of this system only cached the eligibility result if the result was a "yes", but we can also track it when it's a "no", rather than rechecking it every tick.

This change should improve performance in factions and similar gamemodes, which involve large maps with sparsely distributed players, where each player likely has an independent, non-overlapping ticking chunk circle.

We also ditch TickingChunkEntry in favour of multiple arrays to track the eligibility states. This allows us to avoid rechecking the (even cached) readiness of potentially thousands of chunks. If there are no ticking chunks to recheck, this reduces the cost of the selection process to zero.
2023-04-27 16:59:29 +01:00
7f6269c432 Introduce and use optimised versions of Inventory->isSlotEmpty()
this avoids useless cloning, improving the performance of several functions.
2023-04-27 16:52:52 +01:00
194714b448 Merge branch 'stable' into minor-next 2023-04-27 15:37:54 +01:00
023460db2c BaseInventory: fixed internalAddItem() doing useless canStackWith() checks on null items
if the item is null, it's never going to stack with anything given to this function, because addItem() already discards null items.
2023-04-27 14:53:54 +01:00
2910ffebf4 4.20.2 is next 2023-04-27 13:31:02 +01:00
fea820a99e Release 4.20.1 2023-04-27 13:31:02 +01:00
7c19f14cf5 Fixed up offhand handling for ItemStackRequest, fixes #5723 2023-04-27 13:25:08 +01:00
5a54d09869 InventoryManager: verify slot existence in locateWindowAndSlot()
previously, this would happily return invalid slot IDs, potentially leading to a crash.
2023-04-27 13:18:28 +01:00
4def4d52d9 Merge branch 'stable' into minor-next 2023-04-26 23:22:00 +01:00
1d10107024 4.20.1 is next 2023-04-26 23:15:38 +01:00
54ae4d0ea2 Release 4.20.0 2023-04-26 23:15:34 +01:00
0d21e591d1 Support sign editing UI in 1.19.80, with APIs to allow plugins to use it
this doesn't support editing the rear side of a sign, since the 1.12 format doesn't allow us to represent the rear text, and it would necessitate API breaks to support anyway.

However, we can quite trivially support APIs for the sign GUI, which plugins can use to enable editing signs. PocketMine-MP doesn't currently permit this, since it's currently an experimental feature in 1.20, but plugins can simply use Player->openSignEditor() to mimic it.

This is, however, a byproduct of the fact that APIs needed to be added in order to facilitate the use of OpenSignPacket in 1.19.80.
2023-04-26 22:55:05 +01:00
408616723c Changes for 1.19.80 2023-04-26 22:52:02 +01:00
9bfcd39f2a World: improve type info for getTickingChunks() 2023-04-26 17:06:52 +01:00
8102616ff4 Added ticking chunk count to /status
closes #5716
2023-04-26 17:05:31 +01:00
b162d688a3 CI: use php-cs-fixer 3.16 2023-04-26 16:05:06 +01:00
eb130f2906 Move primary version to PHP 8.1
8.0 is still supported for now, but won't be updated any longer.
2023-04-26 16:03:33 +01:00
3b09c3a48a actions: updated setup-php-action to pmmp/setup-php-action@c7fb29d835 2023-04-26 14:40:39 +01:00
87781cff4d Update GitHub Actions PHP versions 2023-04-26 14:38:40 +01:00
db0cf4bb5a Update composer dependencies 2023-04-26 14:35:05 +01:00
eb4679fefd Merge remote-tracking branch 'origin/stable' into minor-next 2023-04-26 14:28:29 +01:00
a4f2b99ed5 InGamePacketHandler: queue slots for syncing if they appear in requestChangedSlots
this is essentially a prediction without the actual predicted item. We have to sync these regardless of what happens.

fixes #5708
2023-04-24 14:06:29 +01:00
3ecc980bc4 ÂInventoryManagerEntry: fixed incorrect PHPDoc type 2023-04-24 13:42:11 +01:00
107b56154b ItemStackRequestExecutor: fixed stonecutter recipes
this uses the same dodgy hack used by CraftingTransaction, which assumes that getResultsFor() does not care about the crafting inputs.

While this is currently OK, since none of the currently-implemented recipes care about inputs anyway, it will become a problem when we implement shulker box recipes, so this needs to be addressed.

However, it can't be addressed without BC breaks, so this will have to be dealt with in PM5.

closes #5715
2023-04-24 13:35:00 +01:00
f86fde064d CraftingManager: fixed uninitialized field
I'm having deja vu about this ...
2023-04-24 12:34:34 +01:00
84a16ce69a HandlerList: fixed crash on getting unused priority
these sub-arrays are no longer allocated if no handlers are registered.

fixes #5713
2023-04-21 16:19:15 +01:00
d06d3bc871 4.19.4 is next 2023-04-21 15:53:05 +01:00
11e34b3e5c Release 4.19.3 2023-04-21 15:53:02 +01:00
f9318bf286 TimingsHandler: stop throwing exceptions when timers aren't stopped in the right order
this is usually because of an uncaught exception interacting with a try...finally block.
This will normally result in a crash anyway, and we don't want to obscure the real error.
2023-04-21 15:38:11 +01:00
71b78b02d3 Merge remote-tracking branch 'origin/stable' into minor-next 2023-04-19 23:57:26 +01:00
674b65f789 Item: optimise serializeCompoundTag() a little 2023-04-18 16:18:34 +01:00
a77fc8109f TypeConverter: avoid repeated calls to getId() and getMeta() 2023-04-18 15:02:52 +01:00
6102740ee3 TypeConverter: use a less slow hack to restore meta values on items sent by the client
this isn't even really needed anymore, since we don't decode items from the client since 4.18.

However, this may still be useful for tools.
2023-04-18 15:00:34 +01:00
40168a457e TypeConverter: fixed coreItemStackToNet() causing item NBT to be prepared twice
hasNamedTag() calls getNamedTag(), which calls serializeCompoundTag(), which writes the item's properties into the given NBT tag.
2023-04-18 14:43:25 +01:00
d07acd0013 RakLibInterface: split error ID into 4-character chunks
this makes it easier to read, since the error ID can't be copy-pasted from the disconnection screen on the client.
2023-04-17 14:05:46 +01:00
9561ae5af7 Entity: micro optimisation for checkBlockIntersections() 2023-04-16 17:57:51 +01:00
56fbd45dd5 Entity: avoid double-checking block intersections for moving entities
fixes #1824
2023-04-16 17:38:26 +01:00
b5dc72b0ee tools/simulate-chunk-selector: fixed the script being completely broken
getopt() behaviour is really, really dumb
2023-04-16 16:47:17 +01:00
4ba57f2b03 RegisteredListener: use try...finally to stop timings
While event handlers should not throw exceptions, we need to make sure the timings get stopped in the correct order, because the parent Event timer will be stopped due to using a finally block.
If this happens while the handler timing is still running, a second exception will occur, obscuring the real error.
2023-04-16 00:40:43 +01:00
84cb070d56 Merge branch 'stable' into minor-next 2023-04-14 20:12:33 +01:00
df0d72bf61 4.19.3 is next 2023-04-14 18:43:44 +01:00
a534ac759a Release 4.19.2 2023-04-14 18:43:41 +01:00
5ab954b7a0 Update composer dependencies 2023-04-14 18:31:57 +01:00
6c6f686f8e Timings: be more intelligent about shortening timer names
non-pocketmine classes may reuse the names of pocketmine core classes. We don't want timers to get erroneously reused in this case.
2023-04-14 18:24:53 +01:00
bf7975da57 Timings: use class to index packet timings, not IDs
on multi version servers, the same packet may have different IDs, or different packets might use the same ID. In these cases, we don't want the timings to get split up or erroneously reused.
2023-04-14 18:22:35 +01:00
7aeedd8220 Timings: fixed every player getting its own timings
we need a more consistent way to deal with this
2023-04-14 17:32:56 +01:00
3dd1ce2d02 4.19.2 is next 2023-04-14 16:47:46 +01:00
5a29d07021 Release 4.19.1 2023-04-14 16:47:43 +01:00
692e1253c6 Fixed AABB height for farmland and grass paths (fixed in 1.19.50) 2023-04-14 16:02:28 +01:00
ab0c444823 Player: fixed ticking chunks not being re-registered if they never left the view distance
closes #5699
2023-04-14 16:02:28 +01:00
e48a4aaa55 World: fixed chunk ticking not being disabled by setting chunk ticking radius to 0
I can't believe I missed this ...
2023-04-14 16:02:28 +01:00
6fc4ce0f86 ÂTimingsCommand: include HTTP response code in debug message
we should probably show this in the regular response message, but we need new translations for that.
2023-04-14 16:02:27 +01:00
10243c7b2c Bump phpstan/phpstan from 1.10.11 to 1.10.13 (#5697)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.11 to 1.10.13.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.11...1.10.13)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-13 13:19:24 +01:00
8102586ee0 Merge branch 'stable' into minor-next 2023-04-12 21:04:03 +01:00
18b528f72d ItemStackRequestExecutor: fixed borked taking of created items
closes #5695
2023-04-12 21:01:46 +01:00
0336394098 Human: remove useless NameTag tag
this is not written anywhere, so this code never does anything.
2023-04-12 16:44:10 +01:00
7e92da126d DelegateInventory: fixed slots being synced twice and breaking ItemStackRequests
the second time the slot is synced, there is no prediction, so the slot update isn't associated with a request anymore. This causes subsequent requests in the same packet to fail, since the dependency request ID isn't associated with the slot anymore.

This change fixes the problem by only allowing the backing inventory to trigger a call to DelegateInventory->on*Change(). While we could have removed and re-added the listener instead, this way is safer since it doesn't assume that the backing inventory won't modify the given item in setItem().

closes #5692
2023-04-12 15:43:51 +01:00
1569bed37a Merge branch 'stable' into minor-next 2023-04-11 23:56:22 +01:00
ba62e0f9cb WorldManager: fixed borked pre-generation for new worlds' spawn terrain
perhaps directly altering the behaviour of selectChunks() wasn't a good
idea? ...
2023-04-11 23:55:53 +01:00
a6e79c5004 TimingsHandler: remove useless paste metadata
these fields are not used by any version of timings, so this code is redundant.
2023-04-11 23:45:01 +01:00
858d4a2ed2 changelog: fixed indentation
I have no idea what happened here...
2023-04-11 23:35:14 +01:00
87d8c1ea11 4.19.1 is next 2023-04-11 22:48:05 +01:00
89deb0fe18 Release 4.19.0 2023-04-11 22:48:02 +01:00
ad88490e84 Mark TickingChunkEntry as internal 2023-04-11 22:34:11 +01:00
3490e2b06a Mark RegisteredListenerCache as internal 2023-04-11 22:33:44 +01:00
946c2fbacc Ticking chunks rewrite (#5689)
This API is much more flexible than the old, allowing any arbitrary set of chunks to be ticked.

These changes also improve the performance of random chunk ticking by almost entirely eliminating the cost of chunk selection. Ticking chunks are now reevaluated when a player moves, instead of every tick.

The system also does not attempt to check the same chunks twice, leading to further improvements.

Overall, the overhead of random chunk selection is reduced anywhere from 80-96%. In practice, this can offer a 5-10% performance gain for servers with sparsely distributed players.
2023-04-11 20:01:19 +01:00
1c0eed56f1 Added runtime test for event handler inheritance, to ensure I don't accidentally break it with optimisations 2023-04-11 13:52:37 +01:00
9e9b4db00f Merge branch 'stable' into minor-next 2023-04-10 14:32:33 +01:00
e667b5c7db 4.18.5 is next 2023-04-10 14:17:56 +01:00
f61f72180f Release 4.18.4 2023-04-10 14:17:53 +01:00
3f82150837 Update Composer dependencies 2023-04-10 14:06:50 +01:00
017fcde6aa always the CS... 2023-04-10 13:56:53 +01:00
24374297e7 NetworkSession: extract rate limiting functionality into its own unit, and apply a separate rate limit to game packets 2023-04-10 13:53:22 +01:00
76ebedff6a HandlerList: remove unnecessary variable 2023-04-07 22:58:30 +01:00
3ea8d27a3b HandlerList: improve listener list development to make way for #5678 2023-04-07 22:55:27 +01:00
d6c923b525 ExperienceOrb: add get/setDespawnDelay
closes #5645

the code for this is borrowed from ItemEntity. I didn't feel like a base class was appropriate, and we can't (yet) declare constants in traits.
2023-04-07 22:33:30 +01:00
7b55c984bf InGamePacketHandler: reduce debug noise on outdated movements 2023-04-07 21:40:46 +01:00
f5b4d64668 Player: increase max distance between movements to allow high levels of speed to work correctly
speed 255 may allow the player to move as much as 14.8 blocks per tick when sprinting.
2023-04-07 21:35:58 +01:00
1683aa681d TimingsHandler: added format version 2023-04-06 15:08:49 +01:00
bf84caa02c Timings: record peak tick time and active ticks
this information is useful for determining the sizes of lag spikes, and giving more accurate average times.
2023-04-06 15:05:40 +01:00
734adec90d TimingsCommand: log the response body on failed paste 2023-04-06 14:46:02 +01:00
4724195791 Improved performance of event calls
This change significantly reduces the amount of work done by event handlers. Instead of traversing all of the priorities and event parent chain multiple times, we reduce event handlers down to a simple list, which doesn't require any logic to iterate over.
Previously, calling an event with lots of parents costed more than an event which directly descended from Event.
In addition, we had to do a lot of usually useless work to check all priorities, when in practice, only NORMAL will be used in almost all cases.

This change makes it more cost effective to implement the feature suggested by #5678; however, it will still require additional changes.
2023-04-05 23:02:44 +01:00
f32a853bd4 HandlerList: remove useless isset 2023-04-05 21:37:08 +01:00
61b0ad3e7f PreSpawnPacketHandler: added dedicated timer for the humongous amount of crap that has to be sent pre-spawn 2023-04-05 20:58:49 +01:00
b2f755720d Use a proper Breakdown timing group instead of the unwieldy INCLUDED_BY_OTHER_TIMINGS_PREFIX 2023-04-05 20:47:47 +01:00
8ef2780dcd Use group format for tasks 2023-04-05 20:35:54 +01:00
b19c7212ab Merge branch 'stable' into minor-next 2023-04-05 20:12:31 +01:00
14c1a9550d Update composer dependencies 2023-04-05 20:12:21 +01:00
9037d5f16b 4.18.4 is next 2023-04-05 20:07:38 +01:00
8b64ea9e65 Release 4.18.3 2023-04-05 20:07:23 +01:00
2936726bf8 Fixed packets sent by EntityEventBroadcaster not firing DataPacketSendEvent
closes #5670

I'm not super happy with this fix, since it can still be broken if StandardPacketBroadcaster is replaced by something else. However, fixing that problem is probably going to require internal BC breaks, which are not suitable for a patch release.
2023-04-03 22:46:14 +01:00
9cd07f6721 NetworkBroadcastUtils: remove dead code
we don't allow changing the target list anymore, since it increases internal complexity, so this code is redundant.
2023-04-03 22:37:22 +01:00
4bb8daa1a5 ItemStackRequestExecutor: allow any action to take from the created output slot
fixes #5679
2023-04-03 22:24:40 +01:00
6e8eda4ac1 Fixed creative inventory items getting modified by ItemStackRequests 2023-04-03 22:22:21 +01:00
73522d06ef ... 2023-03-31 21:51:07 +01:00
a6a360d179 Revert "Be more concise in event handler timing names"
This reverts commit 9db7e5f0ca.
2023-03-31 21:51:00 +01:00
199ef7401f Revert "Timings: do not shorten event handler timing names"
This reverts commit a2ff9649d5.
2023-03-31 21:49:25 +01:00
f63d349be4 Merge remote-tracking branch 'origin/stable' into minor-next 2023-03-31 21:28:14 +01:00
02e11b5a60 Timings tree (#5587)
Split timings into tree reports
this will allow the timings site to display timings as a tree, instead of as a list as is done now, which will enable more precise identification of performance issues.

An example of this can be seen here: https://timings.pmmp.io/?id=302629

The format changes are fully backwards compatible, as the timings site
aggregates timings from timers with the same names, and doesn't limit
how much extra data can appear at the end of a line.
2023-03-31 21:26:58 +01:00
4a770e5801 CS 2023-03-31 20:32:42 +01:00
a2ff9649d5 Timings: do not shorten event handler timing names
this doesn't work very well in tree view timings
2023-03-31 19:44:06 +01:00
a862cf5144 Workaround ItemStackRequest offhand incorrect slot bug
closes #5667

this appears to be a client bug specific to ItemStackRequest.
2023-03-31 17:27:11 +01:00
5ac0d7ae11 TimingsRecord: fixed incorrect violations calculation
closes #5665
2023-03-31 17:08:59 +01:00
0c47455b24 Timings: ensure that Average Players count is shown properly when custom player classes are used 2023-03-30 18:12:06 +01:00
821dd8885b Merge branch 'stable' into minor-next 2023-03-29 23:56:08 +01:00
a78ae73119 4.18.3 is next 2023-03-29 23:52:31 +01:00
17a1266056 Release 4.18.2 2023-03-29 23:52:31 +01:00
217d7ab4cf Merge tag '4.17.2' into stable 2023-03-29 23:50:58 +01:00
9e8c0a6bea Release 4.17.2 2023-03-29 23:47:49 +01:00
dc1b5a9285 it might help if we actually included the fix 2023-03-29 23:46:53 +01:00
c3a16d9b1f ItemStackResponseBuilder: fixed durability appearing to reset when moving durables around the inventory
closes #5656
2023-03-29 23:31:46 +01:00
bed218d1dd Fixed the first letter of event timing names getting trimmed off when src-namespace-prefix is not used 2023-03-29 23:11:30 +01:00
5e1f837a73 ... 2023-03-28 22:46:18 +01:00
b49a9ae81d Added timings for calling events
this gives a somewhat better overview of events, particularly if many plugins are subscribed to the same costly event (e.g. PlayerMoveEvent).

In addition, it allows us to see the frequency that events are occurring.
2023-03-28 17:26:20 +01:00
4c60e82110 Merge remote-tracking branch 'origin/stable' into minor-next 2023-03-27 19:08:18 +01:00
beb0713a40 4.18.2 is next 2023-03-27 18:03:17 +01:00
cd603e8266 Release 4.18.1 2023-03-27 18:03:16 +01:00
af385668c2 InventoryManager: give more detailed information on failure to get info for held item 2023-03-27 17:55:39 +01:00
3ee62d8440 InGamePacketHandler: increase max ItemStackRequest actions to 60
due to implementation quirks + some unforeseen ways these actions can behave, there can be as many as 53 actions in a single crafting request. This is an edge case, but it has to be catered for.
2023-03-27 15:44:42 +01:00
811639f2cd InGamePacketHandler: relax errors on normal transactions to fix book editing
for some reason book edits generate a transaction in addition to BookEditPacket. PM has never used the transaction, and it doesn't pass anyway because CreateItemAction can't be used in survival mode.
However, since the strict validation introduced since ItemStackRequest, this dud transaction now causes the player to get kicked without these changes.
2023-03-27 13:26:26 +01:00
58974765a6 InGamePacketHandler: fixed crash when attempting to drop more of an item than is available 2023-03-27 13:26:26 +01:00
eca9fe50b6 Bump build/php from a464454 to 9d8807b (#5654)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `a464454` to `9d8807b`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](a464454d1e...9d8807be82)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-27 13:03:30 +01:00
1959d6dc9b Fix CS 2023-03-27 12:59:26 +01:00
9db7e5f0ca Be more concise in event handler timing names 2023-03-27 01:19:17 +01:00
c1cef19f84 stringifyKeys 2023-03-27 01:17:15 +01:00
cebdb95265 Optimise plugin timings report entries
this format is already supported by the timings host, so no changes are required to support this.
2023-03-27 01:15:42 +01:00
006cdaf6ea RakLibInterface: log the name of the session which triggered an unhandled exception
this makes it easier to identify what sequence of events led up to the crash.
2023-03-26 18:56:24 +01:00
acc8ae87fb 4.18.1 is next 2023-03-25 20:51:38 +00:00
0e8b28716a Release 4.18.0 2023-03-25 20:51:34 +00:00
7c77233d12 Merge branch 'stable' into minor-next 2023-03-25 20:26:54 +00:00
6f02b83a26 Update composer dependencies 2023-03-25 20:26:50 +00:00
fbfdf749f2 Merge branch 'stable' into minor-next 2023-03-25 20:25:48 +00:00
289c0b08f4 Explicitly state that pocketmine\network\mcpe is an internal package 2023-03-24 14:06:25 +00:00
dd37b531ad CONTRIBUTING.md: document network API policy 2023-03-24 14:02:23 +00:00
58d5126ada InventoryManager: fixed crashes when setting contents or slots of inventories during InventoryCloseEvent (and other similar logic) 2023-03-24 13:31:30 +00:00
f978c1e9a0 Merge remote-tracking branch 'origin/stable' into minor-next 2023-03-22 22:45:41 +00:00
0b8193aeb3 4.17.2 is next 2023-03-22 22:35:25 +00:00
00286e761c Release 4.17.1 2023-03-22 22:35:24 +00:00
db59f71130 attempt to fix ghcr.io docker image push 2023-03-22 22:29:00 +00:00
b11457d605 Fixed uncaught exception when retrieving a packet from the pool 2023-03-22 22:24:25 +00:00
ea386c42d3 InGamePacketHandler: fixed dropping items from unselected hotbar slots 2023-03-21 14:45:18 +00:00
043e81e737 4.18.0-ALPHA3 is next 2023-03-21 00:26:19 +00:00
66a4c4c88b Release 4.18.0-ALPHA2 2023-03-21 00:26:19 +00:00
1a9322c00a ItemStackRequestExecutor: added some missing @throws 2023-03-21 00:23:31 +00:00
c8d9477da1 ItemStackRequestExecutor: make non-final, and make some stuff protected
this allows for plugin extension, for example to implement anvils.
2023-03-21 00:22:21 +00:00
08e8ef275f remove comment 2023-03-21 00:17:24 +00:00
e57fbff28c ItemStackRequestExecutor: added a sanity check for recipe repetitions 2023-03-21 00:16:03 +00:00
f90315c4a2 ItemStackRequestExecutor: harden against invalid item counts
these cases should all be impossible, but that's assuming that the core code doesn't start using them for a different purpose in the future.
2023-03-21 00:13:21 +00:00
955f7944bb ItemStackRequestExecutor: fixed another possible crash condition 2023-03-21 00:06:33 +00:00
ccd288d7fa Avoid repeated calls to getItemInHand() in drop item handler 2023-03-21 00:04:29 +00:00
097632902a InGamePacketHandler: fixed crash condition in drop item handler 2023-03-21 00:02:32 +00:00
e7771d76f2 Cover buffered inventory sync in timings 2023-03-20 23:29:02 +00:00
ecc830a689 InventoryManager: avoid calling TypeConverter::getInstance() in a loop 2023-03-20 23:24:52 +00:00
ee72e80fbb ItemStackResponseBuilder: removed incorrect code
the client expects that all itemstacks must be acked by ItemStackResponse, regardless of whether the server changed them to some other item.
We'll overwrite the item to the correct thing at the end of the tick anyway.
2023-03-20 23:21:24 +00:00
63310cf764 Do not cache ItemStacks for every item
this is very memory inefficient, and only provides a performance advantage in cold code anyway.
2023-03-20 23:18:43 +00:00
1992d3b6db InventoryManager: avoid useless work in trackItemStack()
this attempts to accommodate slots being set to themselves, which is a rare enough occurrence (only plugins will cause it) that it doesn't make sense to penalize every inventory update this way.
attempting to avoid changing the itemstackID in this way is detrimental to performance, and it doesn't actually matter if we set a new itemstackID anyway.
2023-03-20 23:08:17 +00:00
035a0a4e9d InventoryManager: specialize trackItemStack() to avoid useless lookups 2023-03-20 22:57:58 +00:00
23ea721164 Reduce packets-per-batch limit to 100
this should be well in excess of requirements with the ItemStackRequest system in use.
2023-03-20 22:15:02 +00:00
8408da8534 Merge branch 'item-stack-request' into minor-next 2023-03-20 22:05:50 +00:00
c9601ae67d Fixed crash when opening crafting table and other 'UI' inventories 2023-03-20 22:00:38 +00:00
758b5ee500 InventoryManager: fixed armor slots hack
the correct condition for this should be an unsynced armor slot changed during a transaction, but conveying this information to syncSlot() is a bit of a hassle, so this will do for now.
2023-03-20 21:27:56 +00:00
ca6d51498f Buffer slot and content syncing until the end of the tick
we may receive multiple requests in one tick (e.g. crafting in a batch)
2023-03-20 19:16:00 +00:00
e8085e22a0 Fixed crash when opening main inventory
the InventoryManagerEntry was getting overwritten, since we don't expect to open the same inventory with two different window IDs.
2023-03-20 18:40:18 +00:00
a83fc85f1e InventoryManagerEntry: fixed missing default 2023-03-20 17:32:44 +00:00
3d70a169e1 Reduce chaos in InventoryManager
the information in these arrays is usually needed all at the same time, so it doesn't make sense to force multiple array lookups for it.

in addition, this (obviously) cleans up the code quite a lot.
2023-03-20 17:31:54 +00:00
6ccb8f7373 git 2023-03-20 16:57:38 +00:00
59bae9b077 Give InventoryManager internals clearer names
and stop mixing 'window' and 'inventory' terminology...
2023-03-20 16:53:57 +00:00
2751e1ec02 replacing new Vector3(0, 0, 0) with Vector3::zero() (#5640) 2023-03-20 12:54:28 +00:00
c91168db66 ... 2023-03-20 01:35:15 +00:00
4e55433ed8 Fixed request rejecting 2023-03-20 01:35:03 +00:00
eece6c4433 InGamePacketHandler: remove dead code 2023-03-20 01:28:18 +00:00
67b7b60d18 .............. 2023-03-20 01:19:07 +00:00
804feedb67 Added some dumb limits 2023-03-20 00:54:33 +00:00
d57aca1367 CS 2023-03-20 00:53:00 +00:00
7b0816e42f Properly handle transaction building errors instead of kicking the player 2023-03-20 00:52:26 +00:00
4864444440 Added CraftingManager::getCraftingRecipeFromIndex() 2023-03-19 22:14:23 +00:00
52ea4feac0 Updated pocketmine/locale-data 2023-03-19 16:53:02 +00:00
01d557062a Remove dead baseline 2023-03-19 16:41:01 +00:00
a619fd2be6 Scrub PHPStan baselines 2023-03-19 16:37:38 +00:00
05d9298958 PHPStan 1.10.7 2023-03-19 16:33:59 +00:00
f696a5881b Merge remote-tracking branch 'origin/stable' into minor-next 2023-03-19 16:23:09 +00:00
419962d3a2 Added timer for player-specific movement code
players use an entirely different pathway for movement processing, which could be costly.
2023-03-19 16:12:47 +00:00
054c06fab9 Add specialized entityBaseTick timer for item entities
since item merging is a potential hotspot, we want to know if this code section is a performance problem.
Current timers only tell us whether overall ticking of a particular entity is slow, but that includes movement and therefore isn't particularly helpful.
2023-03-19 15:59:06 +00:00
7bc5d8c824 Rename more timers 2023-03-19 15:57:36 +00:00
607bdfa42f Timings: added new timers for entity move collision checks and projectile move ray tracing
projectiles get their own distinct sub-timer, since the logic is completely different from regular entities.
2023-03-19 15:49:35 +00:00
eec53f9ae0 Timings: clean up timer names 2023-03-19 15:39:44 +00:00
3d56bd267c Timings: fixup network timer inheritance 2023-03-18 23:13:25 +00:00
9a969e21c7 ÂNetworkSession: ensure onResolve handler for CompressBatchPromise is covered by network send timings 2023-03-18 22:49:52 +00:00
195bc3b623 NetworkSession: prevent dev client asserts from missing ability flags 2023-03-18 21:53:17 +00:00
2177d8d352 Push Docker image tags to ghcr.io 2023-03-17 16:32:28 +00:00
471625e697 readme: remove docker hub shield
sadly there isn't any ghcr replacement right now.

[ci skip]
2023-03-17 16:24:13 +00:00
2135776c19 readme: goodbye docker hub, won't miss you
[ci skip]
2023-03-17 16:21:57 +00:00
765aef0810 4.18.0-ALPHA2 is next 2023-03-16 21:45:21 +00:00
bd21feffc4 Release 4.18.0-ALPHA1 2023-03-16 21:45:18 +00:00
5b324f695c Merge branch 'stable' into minor-next 2023-03-16 15:04:19 +00:00
9caed10488 update-updater-api: use github.repository_owner to make fork testing of this workflow less obnoxious 2023-03-16 15:03:14 +00:00
83945ff0a0 Do not update release channels if the new build has a lower version ID
this prevents stuff like 5.0.0 beta versions getting overwritten by 4.x beta versions.
2023-03-16 15:02:42 +00:00
ef45180b80 Rename DataPacketPreReceiveEvent -> DataPacketDecodeEvent
thank you @IvanCraft623 for the suggestion
2023-03-16 13:40:37 +00:00
941fd03998 Remove useless code 2023-03-15 22:58:10 +00:00
1af8da3c1f Merge branch 'minor-next' of github.com:pmmp/PocketMine-MP into minor-next 2023-03-15 22:54:05 +00:00
a5985dcf7d Merge branch 'stable' into minor-next 2023-03-15 22:53:44 +00:00
183d1f4038 Implement DataPacketPreReceiveEvent (#5559)
closes #5554

This is called just before the packet is decoded, allowing the event to be used to drop packets from clients without wasting CPU time decoding them. This can be particularly useful for mitigating denial-of-service attacks.
2023-03-15 22:47:19 +00:00
08ee825d91 StandardPacketBroadcaster: Include varint length prefix in length calculation
varints encode 7 bits per byte, so a log with base 128 will tell us how many bytes are required to encode the length of the packet.
2023-03-15 22:41:19 +00:00
337a254768 Use NetworkBroadcastUtils for broadcasting packets
this eradicates all but 4 usages of Server in Entity, which is extremely cool.
2023-03-15 22:28:51 +00:00
a31e3331fd Move Server::broadcastPackets() to NetworkBroadcastUtils::broadcastPackets()
this has no business being in Server, and it also doesn't need to be an instance method, since it never uses $this.
2023-03-15 22:25:23 +00:00
acebbeed16 Added version channels for update.pmmp.io 2023-03-15 20:59:36 +00:00
e0fdbe6eb1 make-release: don't automatically push
this is rather obnoxious when attempting to push test releases to a
fork.
2023-03-15 20:47:01 +00:00
cc8660629b First look at shared EntityEventBroadcaster,
this improves performance in PvP servers and other areas where lots of players or entities exist in one space.

fixes #5622
2023-03-15 18:22:56 +00:00
e7e19abe85 IPv4 and IPv6 RakLibInterface instances now both use the same broadcaster and context
fixes #5625
2023-03-15 17:17:56 +00:00
5f9e0081fd Fixed mushroom block silk-touch drops and block picking behaviour
fixes #5284
2023-03-15 16:36:35 +00:00
b266f45152 Bump build/php from 71b9f9d to a464454 (#5637)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `71b9f9d` to `a464454`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](71b9f9d2d7...a464454d1e)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 13:44:20 +00:00
34ced382db Eliminate final remaining usage of TypeConverter::netItemStackToCore()
instead, we can verify that the held items match by comparing the received ItemStack with the one cached in InventoryManager, which is more cost effective and closes off internal item deserializers to external attacks.
2023-03-14 22:56:11 +00:00
a573a279fa Merge branch 'minor-next' into item-stack-request 2023-03-14 22:25:49 +00:00
14f141fab2 NetworkSession: Stop counting DataPacketReceiveEvent in handler timings
we want it to be included in receive timings, but not handler timings. Handler timings should reflect the time spent in the actual session PacketHandler, not in the event.
2023-03-14 19:00:15 +00:00
daff955bc4 Merge remote-tracking branch 'origin/stable' into minor-next 2023-03-14 18:42:14 +00:00
0022d82779 Merge commit 'd376399b7f332384532a82eaf69b9b02dad5bd0c' into minor-next 2023-03-14 18:39:03 +00:00
7cad9be0d2 Bump build/php from b2207cf to 71b9f9d (#5634)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `b2207cf` to `71b9f9d`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](b2207cf70d...71b9f9d2d7)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-14 18:32:10 +00:00
2f862a552a actions: Replace deprecated ::set-output commands 2023-03-14 18:31:25 +00:00
590f6dad08 4.17.1 is next 2023-03-14 18:14:40 +00:00
9564c81582 Release 4.17.0 2023-03-14 18:14:40 +00:00
3de7a8c27f Updated for 1.19.70 2023-03-14 18:08:10 +00:00
d376399b7f Update composer dependencies
bedrock-item-upgrade-schema and bedrock-block-upgrade-schema are now minor-version-locked, to prevent introducing new upgrade schemas not intended for the currently in-use version
previously I'd intended to do this using max schema IDs, but this has proven to be error-prone, so it makes more sense to lock them in using package version constraints instead.
2023-03-14 17:29:25 +00:00
e2071e59c8 actions: update PHP versions 2023-03-13 17:44:10 +00:00
8e280ebb8b RuntimeBlockMapping: avoid unnecessary PacketSerializer usage 2023-03-11 22:16:24 +00:00
fa7c38276c Fixing gigantic clusterfuck with protocol contexts and broadcasting
fixes #5623
2023-03-11 21:54:14 +00:00
b13e97de3d Timings: fixed receivePacket timer showing 2x the actual number of received packets 2023-03-11 19:13:10 +00:00
328b87fc18 Bump phpstan/phpstan from 1.10.4 to 1.10.6 (#5617)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.4 to 1.10.6.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.4...1.10.6)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-10 11:49:34 +00:00
acaa1a9ce1 contributing: update table to reflect branch name changes
these branches were renamed to make them more auto-complete-friendly.
2023-03-07 17:28:31 +00:00
3aec0fa3df 4.16.1 is next 2023-03-07 16:19:51 +00:00
fa131dab12 Release 4.16.0 2023-03-07 16:19:50 +00:00
bb4a82b1e7 Merge branch 'next-minor' into stable 2023-03-07 16:15:57 +00:00
93d844a281 build/make-release: improve support for non-stable release channels 2023-03-07 16:12:27 +00:00
616844696e 4.15.4 is next 2023-03-07 15:33:03 +00:00
71e3e36522 Release 4.15.3 2023-03-07 15:33:03 +00:00
a1b42d419f Merge branch 'stable' into next-minor 2023-03-07 15:24:24 +00:00
ef942a627f actions: drop concurrency group
this is causing builds to get randomly cancelled when multiple branches are pushed at once.
2023-03-07 15:23:12 +00:00
fd8c276bd2 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-03-07 15:15:21 +00:00
9783380d1a Merge branch 'stable' into next-minor 2023-03-07 15:13:39 +00:00
a784d93bfd Update composer dependencies 2023-03-07 15:13:16 +00:00
a05e8b366f Bump phpstan/phpstan from 1.10.3 to 1.10.4 (#5610)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.3 to 1.10.4.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.3...1.10.4)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-07 14:40:08 +00:00
87a2e0460c Ping Discord news subscribers on new releases 2023-03-04 17:09:32 +00:00
4073c3fb39 Update composer dependencies 2023-03-04 16:56:08 +00:00
e227e6d8bf Merge branch 'stable' into next-minor 2023-03-04 16:55:56 +00:00
3aa40829ae Update composer dependencies 2023-03-04 16:55:31 +00:00
035d4b7263 MemoryManager: stringify floats, fixes #5598 2023-03-04 16:47:58 +00:00
3db1492c18 Fix CS again 2023-03-04 16:43:29 +00:00
a523189149 Added separate timings for broadcast and session buffer compression 2023-03-04 16:41:41 +00:00
f8893efb94 Don't bother with global batch compression if there is only 1 recipient
this allows the session to achieve better ratios, and also reduces worker pool spam.
2023-03-04 16:34:00 +00:00
70f1ee3e97 draft-release: set prerelease flag properly 2023-03-04 16:29:26 +00:00
eb2f0ed3d0 4.16.0-BETA3 is next 2023-03-04 16:19:34 +00:00
14e7d3e143 Release 4.16.0-BETA2 2023-03-04 16:19:34 +00:00
6d636fc2c7 4.16.0-BETA2 is next 2023-03-04 16:18:04 +00:00
a39f61a33d Release 4.16.0-BETA1 2023-03-04 16:18:01 +00:00
aaec21f544 Compressor: Use minCompressionThreshold exclusively
closes #5589
2023-03-04 15:07:50 +00:00
0edc5f8113 Bump phpunit/phpunit from 9.6.3 to 9.6.4 (#5597)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.6.3 to 9.6.4.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.6.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.6.3...9.6.4)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 14:06:18 +00:00
a382f0fd92 Bump phpstan/phpstan-phpunit from 1.3.8 to 1.3.10 (#5602)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.3.8 to 1.3.10.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.3.8...1.3.10)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 14:05:59 +00:00
0fcd2e7894 Merge branch 'stable' into next-minor 2023-02-28 19:23:25 +00:00
369e0855a7 Update composer dependencies 2023-02-28 19:17:46 +00:00
a6cf39b94e Update composer dependencies 2023-02-25 20:39:01 +00:00
17afd38274 Bump phpstan/phpstan from 1.10.1 to 1.10.3 (#5593)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.1 to 1.10.3.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.1...1.10.3)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-25 20:36:33 +00:00
8f024cb382 Bump docker/build-push-action from 3.3.0 to 4.0.0 (#5545)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.3.0 to 4.0.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.3.0...v4.0.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-25 20:04:34 +00:00
e7209679fb ... 2023-02-24 22:23:00 +00:00
da054736b1 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-02-24 22:19:38 +00:00
d92173cded 4.15.3 is next 2023-02-24 22:18:34 +00:00
308cdb6863 Release 4.15.2 2023-02-24 22:18:34 +00:00
ae50b952f1 Accept 1.19.63 (same protocol, different protocol version) 2023-02-24 22:15:58 +00:00
f44946cb49 ... 2023-02-23 22:00:24 +00:00
f704bfb63a Use BedrockData 2.0.0 2023-02-23 21:52:17 +00:00
9acb4d64db Added generated constants for available BedrockData files
this makes it easier to detect unused files, detect removed files, and also avoid typos in usages.
2023-02-23 21:45:12 +00:00
8234360c8d Avoid creating batch buffers just to determine whether a batch should be globally compressed
Instead, sum together the lengths of encoded packet buffers and use that to decide whether to build the buffer or not.
2023-02-22 22:43:10 +00:00
6a64486f55 StandardPacketBroadcaster: Improve performance when broadcasting small packets
In refactors during PM4, I stripped out packet buffer caching, as it was problematic when events alter packets in undetectable ways.
However, I never cleaned this part of the code up properly after enabling DataPacketSendEvent to include multiple packets and multiple targets, so we were still individually encoding the packet(s) for every single session if the sum total of the sizes was below 256 bytes.

This change encodes packets once in the StandardPacketBroadcaster and retains their buffers to post to the session's send buffer directly if the resulting batch is below compression threshold.
This code is still not optimal (see ##5589), but fixing this brings broadcasting performance back to PM3 levels, without any of PM3's problems.
2023-02-22 21:52:12 +00:00
6ec778d0af Bump phpstan/phpstan from 1.9.18 to 1.10.1 (#5588)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.18 to 1.10.1.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.18...1.10.1)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-22 13:47:54 +00:00
75bb4f8da6 Merge branch 'stable' into next-minor 2023-02-21 18:32:58 +00:00
efdd7a186d World: fixed population timer sometimes not being stopped 2023-02-21 18:31:33 +00:00
c4ecb3d128 Merge branch 'stable' into next-minor 2023-02-21 15:37:06 +00:00
b574d49d36 4.15.2 is next 2023-02-21 15:23:25 +00:00
47e9ecd257 Release 4.15.1 2023-02-21 15:23:25 +00:00
799739fe86 Updated build/php submodule to pmmp/PHP-Binaries@b2207cf70d 2023-02-21 15:22:38 +00:00
59a04c971f Getter and setter for gravity (#5584)
closes #5525
2023-02-21 15:01:22 +00:00
168af31fd7 Bump phpstan/phpstan from 1.9.17 to 1.9.18 (#5585)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.17 to 1.9.18.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.17...1.9.18)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-20 18:39:29 +00:00
871bd169a8 Timings: remove unnecessary code 2023-02-19 17:58:12 +00:00
4dbcd714bd NetworkSession: fixed some segments of recv/send logic not being covered by their respective network timings 2023-02-19 17:21:10 +00:00
d5e92b4ae6 ... 2023-02-19 16:53:04 +00:00
2a3288c4f9 Avoid useless throwaway PacketBatch objects 2023-02-19 16:50:03 +00:00
9cdb641936 Added encode packet timings
these changes required some new APIs in BedrockProtocol.
2023-02-19 16:47:20 +00:00
b56b35b10d ItemEntity: fixed a bunch of suspicious logic in entityBaseTick()
closes #5580
2023-02-17 20:14:38 +00:00
324bc27b5a Merge branch 'stable' into next-minor 2023-02-17 19:59:28 +00:00
71aad310c6 stfu 2023-02-17 16:39:46 +00:00
38828e2b42 4.15.1 is next 2023-02-17 16:37:34 +00:00
9a6d7b505c Release 4.15.0 2023-02-17 16:37:34 +00:00
1e3b025916 1.19.62 2023-02-17 16:36:32 +00:00
396d64c60b 4.14.2 is next 2023-02-15 15:19:39 +00:00
d7a0f5362e Release 4.14.1 2023-02-15 15:19:38 +00:00
c5dcd268ad CS 2023-02-15 15:04:41 +00:00
910c4c4b24 Updated BedrockProtocol 2023-02-15 15:02:00 +00:00
2fd6e769e6 NetworkSession: Improved packet budgeting
this fixes players getting kicked during server lag spikes.

closes #5532
2023-02-15 14:59:05 +00:00
69155015c9 Double quote array expansions to avoid re-splitting elements. (#5570)
See: https://github.com/koalaman/shellcheck/wiki/SC2068
2023-02-13 12:24:47 +00:00
6854830b6e start.sh: Use -n instead of ! -z (#5567)
See https://github.com/koalaman/shellcheck/wiki/SC2236
2023-02-13 12:21:35 +00:00
2c413768a5 Merge branch 'stable' into next-minor 2023-02-11 17:15:12 +00:00
fbaf8e3fc8 Update composer dependencies 2023-02-11 17:13:12 +00:00
c62845e92a 4.14.1 is next 2023-02-08 20:21:43 +00:00
c7930ce9ec Release 4.14.0 2023-02-08 20:21:42 +00:00
475888b031 InGamePacketHandler: do not process repeated skin change requests for the same full skin ID
this fixes a feedback loop with persona skins in 1.19.60.
2023-02-08 20:16:41 +00:00
40b90bb722 InGamePacketHandler: log a debug when processing skin change requests 2023-02-08 19:47:12 +00:00
5a4550a4fc CS 2023-02-08 18:55:49 +00:00
7bbc04e6de Silence PlayerSkinPacket debug messages during spawn response stage
the client sends its skin here in 1.19.60 for some reason, which makes no sense - I can only assume it's a bug
2023-02-08 18:55:38 +00:00
3ba662f64f 1.19.60 2023-02-08 18:46:37 +00:00
5d7b99daf4 Bump phpstan/phpstan from 1.9.15 to 1.9.16 (#5560)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.15 to 1.9.16.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.15...1.9.16)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 10:55:09 +00:00
e47627f565 Bump build/php from f51e954 to fb297eb (#5558)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `f51e954` to `fb297eb`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](f51e954743...fb297eb511)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 10:09:15 +00:00
39207c7992 Bump phpstan/phpstan from 1.9.14 to 1.9.15 (#5557)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.14 to 1.9.15.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.14...1.9.15)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-07 12:39:49 +00:00
41ab698f93 wrong filename 2023-02-06 12:29:54 +00:00
e45a6d8311 Merge branch 'stable' into next-minor 2023-02-06 12:27:09 +00:00
8912a97be7 Update Composer dependencies 2023-02-06 12:11:42 +00:00
8d2a9ce67c Clean PHPStan baselines 2023-02-06 12:09:19 +00:00
811352e2ef Bump build/php from c658506 to f51e954 (#5555)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `c658506` to `f51e954`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](c6585061ca...f51e954743)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-06 11:50:10 +00:00
981385cf4a GeneratorManager: Removed redundant Closure wrapping (#5551) 2023-02-04 14:04:31 +00:00
cfa1e7486a Move legacy recipes and creative items in-house
having them here allows BedrockData to represent latest versions freely, without being limited by technical limitations of PM4.
2023-02-02 15:25:03 +00:00
3c46bf01c6 Begin removing dependence on obsolete files from BedrockData
these files were only kept for backwards compatibility, and aren't actively maintained. They are only needed for legacy conversions in the modern day era.
2023-02-02 14:47:38 +00:00
4562cfb85b 4.13.1 is next 2023-01-30 21:55:26 +00:00
cb1aac3cd4 Release 4.13.0 2023-01-30 21:55:26 +00:00
3dd1a14fb7 Merge branch 'next-minor' into stable 2023-01-30 21:52:41 +00:00
63c3127248 Scrub PHPStan baselines 2023-01-30 21:52:31 +00:00
96c32d24ba Update composer dependencies 2023-01-30 13:23:40 +00:00
d64a9d8b52 Merge branch 'stable' into next-minor 2023-01-30 12:58:50 +00:00
92c29b8172 Update transitive composer dependencies 2023-01-30 12:53:06 +00:00
0ac9584bbb Bump shivammathur/setup-php from 2.23.0 to 2.24.0 (#5543)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.23.0 to 2.24.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.23.0...2.24.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-30 12:49:42 +00:00
fe12e8d944 Bump build/php from 1c44c61 to c658506 (#5542)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `1c44c61` to `c658506`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](1c44c615c3...c6585061ca)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-30 12:39:14 +00:00
7529953f0a Bump phpstan/phpstan from 1.9.13 to 1.9.14 (#5523)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.13 to 1.9.14.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.13...1.9.14)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-27 23:08:36 +00:00
00dbb6855a Bump build/php from 83bec42 to 1c44c61 (#5539)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `83bec42` to `1c44c61`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](83bec42c3c...1c44c615c3)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-27 23:08:08 +00:00
7eca3e8081 Fix typo
closes #5533
2023-01-26 14:52:50 +00:00
b58d7fc82a Bump build/php from af250d7 to 83bec42 (#5534)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `af250d7` to `83bec42`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](af250d7e06...83bec42c3c)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-26 13:00:07 +00:00
ceea8220a9 Bump symfony/filesystem from 5.4.13 to 5.4.19 (#5530)
Bumps [symfony/filesystem](https://github.com/symfony/filesystem) from 5.4.13 to 5.4.19.
- [Release notes](https://github.com/symfony/filesystem/releases)
- [Changelog](https://github.com/symfony/filesystem/blob/6.2/CHANGELOG.md)
- [Commits](https://github.com/symfony/filesystem/compare/v5.4.13...v5.4.19)

---
updated-dependencies:
- dependency-name: symfony/filesystem
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 13:23:48 +00:00
18013e9551 Bump build/php from b479ec4 to af250d7 (#5527)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `b479ec4` to `af250d7`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](b479ec438f...af250d7e06)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 13:51:50 +00:00
bd3e9e1cad Do not allow more than 1 run to compile PHP at a time
this causes cache collisions and build failures
2023-01-23 20:13:29 +00:00
6173471cca Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-01-23 19:40:14 +00:00
644881372d Merge branch 'stable' into next-minor 2023-01-23 19:37:02 +00:00
a12aac71fd Updated setup-php-action 2023-01-23 19:36:52 +00:00
608fcd6cd7 4.12.12 is next 2023-01-22 20:44:31 +00:00
ce9b25e97a Release 4.12.11 2023-01-22 20:44:26 +00:00
f948cb0086 PocketMine.php: refuse pthreads 5.0 2023-01-21 15:30:36 +00:00
6c52723d97 Merge branch 'stable' into next-minor 2023-01-20 15:40:32 +00:00
d5b7bf77b0 Bump build/php from 6b605ed to b479ec4 (#5521)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `6b605ed` to `b479ec4`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](6b605ed7c4...b479ec438f)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-20 09:44:19 +00:00
b1a5b02d3a Updated DevTools to 1.16.1 2023-01-20 01:55:52 +00:00
74e052de51 Terminal: fix deprecation error on PHP 8.2 2023-01-20 01:30:39 +00:00
a5397d55fe Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-01-20 01:13:27 +00:00
65ef929d22 Update Actions PHP versions 2023-01-20 01:13:15 +00:00
441919c5e3 Begin testing on PHP 8.2 2023-01-20 01:12:35 +00:00
448aeec780 Bump phpstan/phpstan from 1.9.12 to 1.9.13 (#5520)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.12 to 1.9.13.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.12...1.9.13)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-19 13:04:28 +00:00
78aea5c34c Merge branch 'stable' into next-minor 2023-01-18 20:46:10 +00:00
d7f40f75d2 PlayerPreLoginEvent: fixed documentation errors 2023-01-18 20:45:49 +00:00
b47035fbab Merge branch 'stable' into next-minor 2023-01-18 19:48:29 +00:00
f0925ff9dc draft-release: link to the correct channel-specific changelog
not having this made releasing alphas and betas error-prone, because I'd have to manually amend the changelog URL in the release.
2023-01-18 16:32:22 +00:00
d9324b9951 4.13.0-BETA2 is next 2023-01-18 16:15:47 +00:00
1d9336ed67 Release 4.13.0-BETA1 2023-01-18 16:15:43 +00:00
d37142af4b Merge branch 'stable' into next-minor 2023-01-18 15:30:42 +00:00
7c068101b7 CSÂ 2023-01-18 15:17:37 +00:00
217f9aea02 4.12.11 is next 2023-01-18 15:16:33 +00:00
edb8f19a0c Merge branch 'stable' into next-minor 2023-01-16 19:56:38 +00:00
ad6a423d12 Merge branch 'stable' into next-minor 2023-01-16 18:30:13 +00:00
289e86e899 Make use of World::requestSafeSpawn() 2023-01-14 17:55:00 +00:00
7d59bafd83 World: added requestSafeSpawn() (async)
this simplifies usages of safe spawns, since the caller doesn't need to know which chunks will be needed for the spawn to be selected.

We'll need this in the future, because safe spawns may also get diverted horizontally as well as vertically, which might require loading adjacent chunks as well as the chunk the position is actually in.
2023-01-14 17:42:17 +00:00
1bbe053848 Language: fixed another couple of hardcoded translation keys 2023-01-13 17:52:20 +00:00
2ed48c8469 ... 2023-01-13 17:46:16 +00:00
d786ed5ebf WorldManager: fixed debug spam 2023-01-13 17:43:02 +00:00
a9f06fc5f4 Replaced hardcoded record.nowPlaying with KnownTranslationKeys 2023-01-13 17:27:57 +00:00
dff3f45d22 Constify more tick-related things 2023-01-13 16:29:09 +00:00
1e17d86421 Constify server TPS and server tick time
this makes it significantly easier to perform experiments involving the server TPS.
2023-01-13 16:03:15 +00:00
329c2a6c0f Merge branch 'stable' into next-minor 2023-01-12 22:17:00 +00:00
fc487b17be DumpMemoryCommand: use localized description 2023-01-12 22:10:13 +00:00
91ac47ecba Merge branch 'stable' into next-minor 2023-01-12 21:47:37 +00:00
4df1f7f502 Updated composer dependencies (next-minor) 2023-01-12 19:03:55 +00:00
d74719704e Merge branch 'stable' into next-minor 2023-01-12 19:02:07 +00:00
5021096bdd Fixed walls and thin blocks not connecting to each other
closes #5498
2023-01-09 20:46:57 +00:00
d2eddf9d33 relocate comments 2023-01-08 20:56:51 +00:00
81ca0c8fbf Language: do not parse translations if the text was a plain key
this unintentionally allowed translations to reference other translations 1 level deep, which is not desired behaviour.
This also improves performance for the cases where formatting isn't used.
2023-01-08 20:56:12 +00:00
d4b8c47a65 Language: document poorly-named function parseTranslation() 2023-01-08 16:46:25 +00:00
4a3d9f8f83 Make client-aware translation handling more coherent
I have no clear idea why this was still using translateString(), since it's entirely unnecessary when we aren't selectively translating.
2023-01-08 16:45:57 +00:00
1c96e7936c Remove dead translation code
we don't translate raw string parameters anywhere else these days, so there's no reason to do so here either. The parameters array is already reduced to string[] by this point anyway.
2023-01-08 16:20:05 +00:00
faaec12aaf Update BedrockProtocol 2023-01-06 22:16:29 +00:00
1123a5aa23 InventoryManager: Track predictions using ItemStack directly, instead of internal Item
this removes the need for deserializing network itemstacks to core items, thereby eliminating a whole bunch of potential security issues.
2023-01-06 20:45:08 +00:00
8633804f15 InventoryManager: disentangle slot tracking from slot syncing 2023-01-06 20:26:19 +00:00
d3cea2ca7c CS 2023-01-06 02:07:31 +00:00
ece49f011c Merge branch 'stable' into next-minor 2023-01-06 01:50:17 +00:00
5d6dba96af Merge branch 'stable' into item-stack-request 2023-01-06 01:47:27 +00:00
b24eb153f9 Constrain inventory transaction predictions
these are now only used for actions done with a closed inventory window. This means that they can only predict the slots of inventory, offhand and armor (total 41 slots) and perhaps include some DropItem actions.
2023-01-05 21:18:30 +00:00
36525d9055 Fixed multi-output recipe handling 2023-01-05 20:41:44 +00:00
3d6baa8a55 Working creative inventory, with a few more hacks than I'd like 2023-01-05 18:09:57 +00:00
30d3869eea Remove dead code 2023-01-05 17:26:58 +00:00
81697111b9 Merge branch 'item-stack-request' of github.com:pmmp/PocketMine-MP into item-stack-request 2023-01-05 17:24:15 +00:00
eedc943766 Confine legacy transaction handling to dropping items only 2023-01-05 17:23:50 +00:00
e647e8c933 World: Use existing function to notify nearby blocks of an update (#5494) 2023-01-05 16:55:35 +00:00
c6e11a8453 Remove unnecessary ternary operator (#5493) 2023-01-04 23:22:31 +00:00
2e9a3f9160 Working crafting :woohoo: 2023-01-04 22:29:29 +00:00
3d4ed5308e Merge branch 'stable' into item-stack-request 2023-01-04 01:28:42 +00:00
58dd4a44e3 Merge branch 'stable' into item-stack-request 2023-01-04 00:41:58 +00:00
5fdbb19852 Fixed a whole bunch of issues with legacy transactions 2023-01-04 00:13:51 +00:00
6b2156151f Merge branch 'stable' into item-stack-request 2023-01-03 23:51:37 +00:00
d8d236842f Fixed merge error 2023-01-03 19:54:41 +00:00
f51717323b Merge branch 'stable' into item-stack-request 2023-01-03 19:53:25 +00:00
172ce659b8 Use str_starts_with, str_ends_with and str_contains instead of strpos (#5485) 2022-12-31 13:02:23 +00:00
0d31b25fba Use str_starts_with and str_contains instead of strpos (#5482) 2022-12-30 21:41:30 +00:00
0d169b4e80 Filesystem: added fileGetContents to reduce ErrorToExceptionHandler boilerplate code 2022-12-25 17:13:51 +00:00
b1c0eae1f6 NetworkSession: tidy up common disconnection logic 2022-12-24 20:36:18 +00:00
80832ff763 NetworkSession: do not send packets to disconnected sessions
this is mostly harmless, since the packets land in a buffer that gets discarded, but we also need to avoid calling DataPacketSendEvent.
2022-12-24 20:12:50 +00:00
f7d0d16eb3 NetworkSession: defer destructive cleanup until the next session tick() call
this fixes crashes when kicking players during PlayerJoinEvent and various other events.
2022-12-24 20:06:00 +00:00
6375139d0b DefaultPermissions: improve readability slightly 2022-12-24 17:47:17 +00:00
567bd8abb5 Add .self and .other permissions for gameplay-altering commands (#5470)
I'm not quite sure this is the best way to enable such functionality, but it's already used for some other stuff, so I'm not too worried for now.

This allows the following commands to have their usage limited to self or others:
- /effect
- /enchant
- /gamemode
- /give
- /spawnpoint
- /teleport
- /title

I envision this being useful for creative mode servers, and test servers such as test.pmmp.io.
2022-12-24 17:22:18 +00:00
3d038b28ff SplashPotion: const-ify missed NBT key 2022-12-24 15:19:56 +00:00
0c9b6a6797 DatFilePlayerDataProvider: added documentation 2022-12-23 19:00:45 +00:00
639f089c55 .. 2022-12-23 18:59:36 +00:00
9010b2743c Move player data storage handling behind an interface 2022-12-23 18:58:49 +00:00
4c91c4aaf1 ÂMemoryManager: replace switch with match 2022-12-23 17:36:08 +00:00
17125ce0e3 Merge branch 'stable' into next-minor 2022-12-23 16:56:54 +00:00
3987ee6cb2 PluginDescription: const-ify plugin.yml parsing keys 2022-12-23 16:11:03 +00:00
51a684c0ea PermissionParser: remove hardcoded default strings in defaultFromString() 2022-12-23 16:03:01 +00:00
43e69041fc PermissionParser: use constants for keys 2022-12-23 16:00:38 +00:00
2a33c9ed3b Fix PHPStan 2022-12-22 16:53:14 +00:00
b03733442b Move translation flattening logic from Player to NetworkSession
this is network-specific stuff, so it doesn't belong in Player.
2022-12-22 16:51:09 +00:00
9c9929ff39 Player: break cycle between sendMessage() and sendTranslation() 2022-12-22 16:37:36 +00:00
566a8a261f Player: deprecate sendTranslation() in favour of sendMessage() 2022-12-22 16:32:54 +00:00
a9e5f92958 Show death message on death screen (#5386) 2022-12-22 15:36:31 +00:00
ee7d4728d8 World: added cache for isChunkTickable()
this considerably reduces the amount of work done by the function, since it's usually checking the same chunks over and over again.
2022-12-19 21:20:21 +00:00
923dcec4e7 Revert "World: do not refresh ticked chunks list every tick"
This reverts commit aebcfc516f.

this has edge cases in the handling of adjacent chunk locks which I
didn't consider at the time. Once accounting for those edge cases, it
became significantly more complex to the point that I realized this
needed more planning.
2022-12-19 20:57:51 +00:00
aebcfc516f World: do not refresh ticked chunks list every tick
this is just wasting CPU time, since the effects aren't noticeable on such a small timescale anyway.
This reduces the CPU impact of chunk selection by 95%. However, this is the lesser part of chunk ticking, and the lion's share of the performance impact still comes from actually ticking the chunks.
2022-12-19 20:17:29 +00:00
99faeb8d05 Merge remote-tracking branch 'origin/stable' into next-minor 2022-12-19 15:55:59 +00:00
b2017c8462 and more 2022-12-18 22:14:31 +00:00
bf44edd179 Constify a bunch of NBT keys, pass 1 2022-12-18 22:12:15 +00:00
aa374083d8 Accept new BedrockProtocol version 2022-12-18 21:00:52 +00:00
8c0d3943d8 Added length validation for resource pack encryption keys 2022-12-15 22:36:06 +00:00
d02c6668b2 ResourcePackManager: added setPackEncryptionKey()
this allows plugins to install their own encrypted resource packs
2022-12-15 22:29:34 +00:00
880d01daea ResourcePackManager: added setResourceStack()
this enables plugins to modify the resource pack stack however they see fit.

Modifying the existing stack can be done by doing array modifications on the result of getResourceStack() and then setting it back again using this method.
2022-12-15 22:10:53 +00:00
50b70708fb ResourcePackManager: extracted loadPackFromPath() private method from constructor body 2022-12-15 21:59:54 +00:00
c5d716dc9d Added keep on death methods for items (#5395) 2022-12-15 20:10:20 +00:00
95d0a3bf41 Merge branch 'stable' into next-minor 2022-12-15 19:38:22 +00:00
cf707e15c2 fix 2022-12-15 19:36:16 +00:00
1308cda5c2 Implemented hook method Item::onInteractEntity() (#5432)
this is called when the player right-clicks on an entity to do some action, such as shearing, naming etc.
2022-12-15 19:30:52 +00:00
4357c110c8 Add the event: WorldParticleEvent (#5428) 2022-12-06 14:19:14 +00:00
fed2a6d917 Add the event: WorldSoundEvent (#5322) 2022-12-06 13:06:40 +00:00
1d4b6dc66e Only reduce durability when armor reduced damage. (#5444)
https://minecraft.fandom.com/wiki/Durability#Armor_durability
2022-12-05 21:10:36 +00:00
174c9a48f5 Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2022-12-05 14:12:45 +00:00
3984d220bb Implemented the swift sneak enchantment (#5404)
Co-authored-by: Dylan T <dktapps@pmmp.io>

closes #5301
2022-12-01 20:38:41 +00:00
0b497654f2 Merge remote-tracking branch 'origin/stable' into next-minor 2022-11-30 19:51:24 +00:00
d476a4c1aa Implement a Living::getDisplayName() (#5384) 2022-11-27 19:48:55 +00:00
0039af984d Merge branch 'next-minor' into item-stack-request 2022-10-16 16:56:26 +01:00
3235d128e5 Fixed handling of fake requests during block placement and other actions 2022-08-18 18:25:49 +01:00
2b7510945a First look at ItemStackRequest usage (very unstable) 2022-08-18 17:38:57 +01:00
205 changed files with 62101 additions and 2833 deletions

View File

@ -20,6 +20,13 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Clone pmmp/PocketMine-Docker repository
uses: actions/checkout@v3
with:
@ -30,68 +37,68 @@ jobs:
id: tag-name
run: |
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
echo ::set-output name=TAG_NAME::$VERSION
echo ::set-output name=MAJOR::$(echo $VERSION | cut -d. -f1)
echo ::set-output name=MINOR::$(echo $VERSION | cut -d. -f1-2)
echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
- name: Download new release information
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
- name: Detect channel
id: channel
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
run: echo CHANNEL=$(jq -r '.channel' new_build_info.json) >> $GITHUB_OUTPUT
- name: Get name of Docker repository name
id: docker-repo-name
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v3.3.0
uses: docker/build-push-action@v4.0.0
with:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.TAG_NAME }}
# ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.TAG_NAME }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.TAG_NAME }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.3.0
uses: docker/build-push-action@v4.0.0
with:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
# ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.3.0
uses: docker/build-push-action@v4.0.0
with:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
# ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.3.0
uses: docker/build-push-action@v4.0.0
with:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:latest
# ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:latest
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:latest
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}

View File

@ -18,8 +18,9 @@ require dirname(__DIR__, 2) . '/vendor/autoload.php';
/**
* @phpstan-return array<string, mixed>
*/
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl) : array{
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl, int $newsPingRoleId) : array{
return [
"content" => "<@&$newsPingRoleId> New PocketMine-MP release: $version ($channel)",
"embeds" => [
[
"title" => "New PocketMine-MP release: $version ($channel)",
@ -35,11 +36,11 @@ DESCRIPTION,
];
}
if(count($argv) !== 5){
fwrite(STDERR, "Required arguments: github repo, version, API token\n");
if(count($argv) !== 6){
fwrite(STDERR, "Required arguments: github repo, version, API token, webhook URL, ping role ID\n");
exit(1);
}
[, $repo, $tagName, $token, $hookURL] = $argv;
[, $repo, $tagName, $token, $hookURL, $newsPingRoleId] = $argv;
$result = Internet::getURL('https://api.github.com/repos/' . $repo . '/releases/tags/' . $tagName, extraHeaders: [
'Authorization: token ' . $token
@ -86,7 +87,7 @@ $buildLogUrl = $buildInfoJson["build_log_url"];
$description = $releaseInfoJson["body"];
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl);
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl, (int) $newsPingRoleId);
$response = Internet::postURL(
$hookURL,

View File

@ -13,9 +13,9 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.23.0
uses: shivammathur/setup-php@2.25.2
with:
php-version: 8.0
php-version: 8.1
- name: Restore Composer package cache
uses: actions/cache@v3
@ -32,7 +32,7 @@ jobs:
- name: Get actual tag name
id: tag-name
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
- name: Run webhook post script
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }}

View File

@ -18,9 +18,9 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.23.0
uses: shivammathur/setup-php@2.25.2
with:
php-version: 8.0
php-version: 8.1
- name: Restore Composer package cache
uses: actions/cache@v3
@ -40,7 +40,7 @@ jobs:
run: |
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
echo "Build number: $BUILD_NUMBER"
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
echo BUILD_NUMBER=$BUILD_NUMBER >> $GITHUB_OUTPUT
- name: Minify BedrockData JSON files
run: php vendor/pocketmine/bedrock-data/.minify_json.php
@ -51,10 +51,12 @@ jobs:
- name: Get PocketMine-MP release version
id: get-pm-version
run: |
echo ::set-output name=PM_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;')
echo ::set-output name=MCPE_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;')
echo ::set-output name=PM_VERSION_SHORT::$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);')
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
echo PM_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;') >> $GITHUB_OUTPUT
echo MCPE_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;') >> $GITHUB_OUTPUT
echo PM_VERSION_SHORT=$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);') >> $GITHUB_OUTPUT
echo PM_VERSION_MD=$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);') >> $GITHUB_OUTPUT
echo CHANGELOG_SUFFIX=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;') >> $GITHUB_OUTPUT
echo PRERELEASE=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "false" : "true";') >> $GITHUB_OUTPUT
- name: Generate build info
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} ${{ github.run_id }} > build_info.json
@ -74,10 +76,11 @@ jobs:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}
draft: true
prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }}
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
token: ${{ secrets.GITHUB_TOKEN }}
body: |
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}${{ steps.get-pm-version.outputs.CHANGELOG_SUFFIX }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.

View File

@ -6,44 +6,25 @@ on:
workflow_dispatch:
jobs:
build-php:
name: Prepare PHP
runs-on: ${{ matrix.image }}
strategy:
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
steps:
- name: Build and prepare PHP cache
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
phpstan:
name: PHPStan analysis
needs: build-php
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
pm-version-major: "4"
- name: Restore Composer package cache
uses: actions/cache@v3
@ -56,32 +37,29 @@ jobs:
composer-v2-cache-
- name: Install Composer dependencies
run: php composer.phar install --prefer-dist --no-interaction
run: composer install --prefer-dist --no-interaction
- name: Run PHPStan
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
phpunit:
name: PHPUnit tests
needs: build-php
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
pm-version-major: "4"
- name: Restore Composer package cache
uses: actions/cache@v3
@ -94,20 +72,19 @@ jobs:
composer-v2-cache-
- name: Install Composer dependencies
run: php composer.phar install --prefer-dist --no-interaction
run: composer install --prefer-dist --no-interaction
- name: Run PHPUnit tests
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
integration:
name: Integration tests
needs: build-php
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
@ -115,13 +92,11 @@ jobs:
submodules: true
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
pm-version-major: "4"
- name: Restore Composer package cache
uses: actions/cache@v3
@ -134,32 +109,29 @@ jobs:
composer-v2-cache-
- name: Install Composer dependencies
run: php composer.phar install --no-dev --prefer-dist --no-interaction
run: composer install --no-dev --prefer-dist --no-interaction
- name: Run integration tests
run: ./tests/travis.sh -t4
codegen:
name: Generated Code consistency checks
needs: build-php
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: ["8.1", "8.2"]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
pm-version-major: "4"
- name: Restore Composer package cache
uses: actions/cache@v3
@ -172,7 +144,7 @@ jobs:
composer-v2-cache-
- name: Install Composer dependencies
run: php composer.phar install --no-dev --prefer-dist --no-interaction
run: composer install --no-dev --prefer-dist --no-interaction
- name: Regenerate registry annotations
run: php build/generate-registry-annotations.php src
@ -180,6 +152,9 @@ jobs:
- name: Regenerate KnownTranslation APIs
run: php build/generate-known-translation-apis.php
- name: Regenerate BedrockData available files constants
run: php build/generate-bedrockdata-path-consts.php
- name: Verify code is unchanged
run: |
git diff
@ -195,10 +170,10 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.23.0
uses: shivammathur/setup-php@2.25.2
with:
php-version: 8.0
tools: php-cs-fixer:3.11
php-version: 8.1
tools: php-cs-fixer:3.16
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,50 +0,0 @@
<?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);
const VERSIONS = [
"8.0",
"8.1"
];
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
$newWorkflowFile = $workflowFile;
foreach(VERSIONS as $v){
$releaseInfo = file_get_contents("https://secure.php.net/releases?json&version=$v");
if($releaseInfo === false){
throw new \RuntimeException("Failed to contact php.net API");
}
$data = json_decode($releaseInfo, true);
if(!is_array($data) || !isset($data["version"]) || !is_string($data["version"]) || preg_match('/^\d+\.\d+\.\d+(-[A-Za-z\d]+)?$/', $data["version"]) === 0){
throw new \RuntimeException("Invalid data returned by API");
}
$updated = preg_replace("/$v\.\d+/", $data["version"], $newWorkflowFile);
if($updated !== $newWorkflowFile){
echo "Updated $v revision to " . $data["version"] . "\n";
}
$newWorkflowFile = $updated;
}
if($workflowFile !== $newWorkflowFile){
echo "Writing modified workflow file\n";
file_put_contents(__DIR__ . '/main.yml', $newWorkflowFile);
}

View File

@ -15,23 +15,68 @@ jobs:
- uses: actions/checkout@v3
with:
repository: pmmp/update.pmmp.io
repository: ${{ github.repository_owner }}/update.pmmp.io
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
- name: Get actual tag name
id: tag-name
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
- name: Download new release information
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
- name: Detect channel
- name: Detect channels
id: channel
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
- name: Copy release information
run: |
cp new_build_info.json channels/${{ steps.channel.outputs.CHANNEL }}.json
CHANNEL=$(jq -r '.channel' new_build_info.json)
VERSION=${{ steps.tag-name.outputs.TAG_NAME }}
echo CHANNEL=$CHANNEL >> $GITHUB_OUTPUT
if [ "$CHANNEL" == "stable" ]; then
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
else
echo MAJOR=$(echo $VERSION | cut -d. -f1)-$CHANNEL >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2)-$CHANNEL >> $GITHUB_OUTPUT
fi
- name: Update channel info
run: |
function version_id() {
major=$(echo $1 | cut -d. -f1)
minor=$(echo $1 | cut -d. -f2)
patch=$(echo $1 | cut -d. -f3)
echo $(((major * 1000000) + (minor * 1000) + patch))
}
function update_channel() {
local target_file_name="$1"
local new_file_name="$2"
local old_version_id
local new_version_id
if [ ! -f "$target_file_name" ]; then
echo "Creating channel file: $target_file_name"
cp "$new_file_name" "$target_file_name"
else
old_version_id=$(version_id "$(jq -r '.base_version' "$target_file_name")")
new_version_id=$(version_id "$(jq -r '.base_version' "$new_file_name")")
echo "Old version ID: $old_version_id"
echo "New version ID: $new_version_id"
if [ $new_version_id -ge $old_version_id ]; then #suffixed versions will have the same version ID - assume they'll always be newer
echo "Updating channel file: $target_file_name ($old_version_id -> $new_version_id)"
cp "$new_file_name" "$target_file_name"
else
echo "Version $new_version_id is less than $old_version_id, not updating channel file: $target_file_name"
fi
fi
}
update_channel "channels/${{ steps.channel.outputs.CHANNEL }}.json" "new_build_info.json"
update_channel "channels/${{ steps.channel.outputs.MAJOR }}.json" "new_build_info.json"
update_channel "channels/${{ steps.channel.outputs.MINOR }}.json" "new_build_info.json"
rm new_build_info.json
- name: Commit changes

View File

@ -30,6 +30,13 @@
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Markdown">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="CLASS_BRACE_STYLE" value="1" />
<option name="METHOD_BRACE_STYLE" value="1" />

View File

@ -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 8.0 or newer available in your shell
- PHP 8.1 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-8.0-Aggregate)
- [Prebuilt binaries](https://github.com/pmmp/PHP-Binaries/releases)
- [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`.
@ -29,11 +29,5 @@ Run `composer make-server` using your preferred PHP binary. It'll drop a `Pocket
You can also use the `--out` option to change the output filename.
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 8.0.3.
## Running PocketMine-MP from source code
Run `src/PocketMine.php` using your preferred PHP binary.

View File

@ -18,24 +18,49 @@ Larger contributions like feature additions should be preceded by a [Change Prop
## Other things you'll need
- [git](https://git-scm.com/)
## List of `pocketmine` namespaces which are in other repos
PocketMine-MP has several dependencies which are independent from the main server code. Most of them use the `pocketmine` namespace.
Some of these add extra classes to packages which already exist in PocketMine-MP.
Take a look at the table below if you can't find the class or function you're looking for.
| Source URL | Namespace, class or function |
|:----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
| [pmmp/BedrockProtocol](https://github.com/pmmp/BedrockProtocol) | `pocketmine\network\mcpe\protocol` |
| [pmmp/BinaryUtils](https://github.com/pmmp/BinaryUtils) | `pocketmine\utils\BinaryDataException`</br>`pocketmine\utils\BinaryStream`</br>`pocketmine\utils\Binary` |
| [pmmp/ClassLoader](https://github.com/pmmp/`ClassLoader`) | `BaseClassLoader`</br>`ClassLoader`</br>`DynamicClassLoader` |
| [pmmp/Color](https://github.com/pmmp/Color) | `pocketmine\color` |
| [pmmp/ErrorHandler](https://github.com/pmmp/ErrorHandler) | `pocketmine\errorhandler` |
| [pmmp/LogPthreads](https://github.com/pmmp/LogPthreads) | `ThreadedLoggerAttachment`</br>`ThreadedLogger`</br>`AttachableThreadedLogger` |
| [pmmp/Log](https://github.com/pmmp/Log) | `AttachableLogger`</br>`BufferedLogger`</br>`GlobalLogger`</br>`LogLevel`</br>`Logger`</br>`PrefixedLogger`</br>`SimpleLogger` |
| [pmmp/Math](https://github.com/pmmp/Math) | `pocketmine\math` |
| [pmmp/NBT](https://github.com/pmmp/NBT) | `pocketmine\nbt` |
| [pmmp/RakLibIpc](https://github.com/pmmp/RakLibIpc) | `raklib\server\ipc` |
| [pmmp/RakLib](https://github.com/pmmp/RakLib) | `raklib` |
| [pmmp/Snooze](https://github.com/pmmp/Snooze) | `pocketmine\snooze` |
| [pmmp/ext-chunkutils2](https://github.com/pmmp/ext-chunkutils2) | `pocketmine\world\format\LightArray`</br>`pocketmine\world\format\PalettedBlockArray`</br>`pocketmine\world\format\io\SubChunkConverter` |
| [pmmp/ext-morton](https://github.com/pmmp/ext-morton) | `morton2d_decode`</br>`morton2d_encode`</br>`morton3d_decode`</br>`morton3d_encode` |
| [pmmp/ext-libdeflate](https://github.com/pmmp/ext-libdeflate) | `libdeflate_deflate_compress`</br>`libdeflate_gzip_compress`</br>`libdeflate_zlib_compress` |
## Choosing a target branch
PocketMine-MP has three primary branches of development.
| Type of change | `stable` | `next-minor` | `next-major` |
|:---------------|:--------:|:------------:|:------------:|
| Bug fixes | ✔️ | ✔️ | ✔️ |
| Improvements to API docs | ✔️ | ✔️ | ✔️ |
| Cleaning up code | ❌ | ✔️ | ✔️ |
| Changing code formatting or style | ❌ | ✔️ | ✔️ |
| Addition of new core features | ❌ | 🟡 Only if non-disruptive | ✔️ |
| Changing core behaviour (e.g. making something use threads) | ❌ | ✔️ | ✔️ |
| Addition of new configuration options | ❌ | 🟡 Only if optional | ✔️ |
| Addition of new API classes, methods or constants | ❌ | ✔️ | ✔️ |
| Deprecating API classes, methods or constants | ❌ | ✔️ | ✔️ |
| Adding optional parameters to an API method | ❌ | ✔️ | ✔️ |
| Changing API behaviour | ❌ | 🟡 Only if backwards-compatible | ✔️ |
| Removal of API | ❌ | ❌ | ✔️ |
| Backwards-incompatible API change (e.g. renaming a method) | ❌ | ❌ | ✔️ |
| Type of change | `stable` | `minor-next` | `major-next` |
|:--------------------------------------------------------------------------------------------|:--------:|:-------------------------------:|:------------:|
| Bug fixes | ✔️ | ✔️ | ✔️ |
| Improvements to API docs | ✔️ | ✔️ | ✔️ |
| Cleaning up code | ❌ | ✔️ | ✔️ |
| Changing code formatting or style | ❌ | ✔️ | ✔️ |
| Addition of new core features | ❌ | 🟡 Only if non-disruptive | ✔️ |
| Changing core behaviour (e.g. making something use threads) | ❌ | ✔️ | ✔️ |
| Addition of new configuration options | ❌ | 🟡 Only if optional | ✔️ |
| Addition of new API classes, methods or constants | ❌ | ✔️ | ✔️ |
| Deprecating API classes, methods or constants | ❌ | ✔️ | ✔️ |
| Adding optional parameters to an API method | ❌ | ✔️ | ✔️ |
| Changing API behaviour | ❌ | 🟡 Only if backwards-compatible | ✔️ |
| Removal of API | ❌ | ❌ | ✔️ |
| Backwards-incompatible API change (e.g. renaming a method) | ❌ | ❌ | ✔️ |
| Backwards-incompatible internals change (e.g. changing things in `pocketmine\network\mcpe`) | ❌ | ✔️ | ✔️ |
### Notes
- **Non-disruptive** means that usage should not be significantly altered by the change.
@ -43,6 +68,10 @@ PocketMine-MP has three primary branches of development.
- Examples of **disruptive** changes include changing the way the server is run, world format changes (since those require downtime for the user to convert their world).
- **API** includes all public and protected classes, functions and constants (unless marked as `@internal`).
- Private members are not part of the API, **unless in a trait**.
- The `pocketmine\network\mcpe` package is considered implicitly `@internal` in its entirety (see its [README](src/network/mcpe/README.md) for more details).
- Minecraft's protocol changes are considered necessary internal changes, and are **not** subject to the same rules.
- Protocol changes must always be released in a new minor version, since they disrupt user experience by requiring a client update.
- BC-breaking changes to the internal network API are allowed, but only in new minor versions. This ensures that plugins which use the internal network API will not break (though they shouldn't use such API anyway).
## Making a pull request
The basic procedure to create a pull request is:

View File

@ -14,7 +14,6 @@
<p align="center">
<a href="https://github.com/pmmp/PocketMine-MP/actions/workflows/main.yml"><img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" /></a>
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver"></a>
<a href="https://hub.docker.com/r/pmmp/pocketmine-mp"><img src="https://img.shields.io/docker/v/pmmp/pocketmine-mp?logo=docker&label=image" alt="Docker image version (latest semver)" /></a>
<a href="https://discord.gg/bmSAZBG"><img src="https://img.shields.io/discord/373199722573201408?label=discord&color=7289DA&logo=discord" alt="Discord" /></a>
<br>
<a href="https://github.com/pmmp/PocketMine-MP/releases"><img alt="GitHub all releases" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/total?label=downloads%40total"></a>
@ -24,7 +23,7 @@
## Getting started
- [Documentation](http://pmmp.readthedocs.org/)
- [Installation instructions](https://pmmp.readthedocs.io/en/rtfd/installation.html)
- [Docker image](https://hub.docker.com/r/pmmp/pocketmine-mp)
- [Docker image](https://github.com/pmmp/PocketMine-MP/pkgs/container/pocketmine-mp)
- [Plugin repository](https://poggit.pmmp.io/plugins)
## Discussion/Help
@ -36,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)

View File

@ -0,0 +1,128 @@
<?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\build\generate_bedrockdata_path_consts;
use Symfony\Component\Filesystem\Path;
use function dirname;
use function fclose;
use function fopen;
use function fwrite;
use function is_file;
use function scandir;
use function str_replace;
use function strtoupper;
use const PHP_EOL;
use const pocketmine\BEDROCK_DATA_PATH;
use const SCANDIR_SORT_ASCENDING;
use const STDERR;
require dirname(__DIR__) . '/vendor/autoload.php';
function constantify(string $permissionName) : string{
return strtoupper(str_replace([".", "-"], "_", $permissionName));
}
$files = scandir(BEDROCK_DATA_PATH, SCANDIR_SORT_ASCENDING);
if($files === false){
fwrite(STDERR, "Couldn't find any files in " . BEDROCK_DATA_PATH . PHP_EOL);
exit(1);
}
$consts = [];
foreach($files as $file){
if($file === '.' || $file === '..'){
continue;
}
if($file[0] === '.'){
continue;
}
$path = Path::join(BEDROCK_DATA_PATH, $file);
if(!is_file($path)){
continue;
}
foreach([
'README.md',
'LICENSE',
'composer.json',
] as $ignored){
if($file === $ignored){
continue 2;
}
}
$consts[] = $file;
}
$output = fopen(dirname(__DIR__) . '/src/data/bedrock/BedrockDataFiles.php', 'wb');
if($output === false){
fwrite(STDERR, "Couldn't open output file" . PHP_EOL);
exit(1);
}
fwrite($output, <<<'HEADER'
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\data\bedrock;
use const pocketmine\BEDROCK_DATA_PATH;
final class BedrockDataFiles{
private function __construct(){
//NOOP
}
HEADER
);
foreach($consts as $constName => $fileName){
fwrite($output, "\tpublic const " . constantify($fileName) . " = BEDROCK_DATA_PATH . '/$fileName';\n");
}
fwrite($output, "}\n");
fclose($output);
echo "Done. Don't forget to run CS fixup after generating code.\n";

View File

@ -36,8 +36,8 @@ use function ksort;
use function mb_strtoupper;
use function preg_match;
use function sprintf;
use function str_ends_with;
use function str_replace;
use function substr;
use const SORT_STRING;
use const STDERR;
@ -121,7 +121,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
if(is_dir($argv[1])){
/** @var string $file */
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
if(substr($file, -4) !== ".php"){
if(!str_ends_with($file, ".php")){
continue;
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\build\make_release;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
use pocketmine\VersionInfo;
@ -30,17 +31,17 @@ use function array_keys;
use function array_map;
use function dirname;
use function fgets;
use function file_get_contents;
use function file_put_contents;
use function fwrite;
use function getopt;
use function is_string;
use function max;
use function preg_match;
use function preg_replace;
use function sleep;
use function sprintf;
use function str_pad;
use function strlen;
use function strtolower;
use function system;
use const STDERR;
use const STDIN;
@ -50,7 +51,7 @@ use const STR_PAD_LEFT;
require_once dirname(__DIR__) . '/vendor/autoload.php';
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
$versionInfo = Utils::assumeNotFalse(file_get_contents($versionInfoPath), $versionInfoPath . " should always exist");
$versionInfo = Filesystem::fileGetContents($versionInfoPath);
$versionInfo = preg_replace(
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
'$1const BASE_VERSION = "' . $newVersion . '";',
@ -102,22 +103,43 @@ function main() : void{
$filteredOpts[$optName] = $optValue;
}
$channel = $filteredOpts["channel"] ?? null;
if(isset($filteredOpts["current"])){
$currentVer = new VersionString($filteredOpts["current"]);
}else{
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
}
if(isset($filteredOpts["next"])){
$nextVer = new VersionString($filteredOpts["next"]);
$nextVer = isset($filteredOpts["next"]) ? new VersionString($filteredOpts["next"]) : null;
$suffix = $currentVer->getSuffix();
if($suffix !== ""){
if($channel === "stable"){
fwrite(STDERR, "error: cannot release a suffixed build into the stable channel\n");
exit(1);
}
if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
echo "error: invalid current version suffix \"$suffix\"; aborting\n";
exit(1);
}
$nextVer ??= new VersionString(sprintf(
"%u.%u.%u-%s%u",
$currentVer->getMajor(),
$currentVer->getMinor(),
$currentVer->getPatch(),
$matches[1],
((int) $matches[2]) + 1
));
$channel ??= strtolower($matches[1]);
}else{
$nextVer = new VersionString(sprintf(
$nextVer ??= new VersionString(sprintf(
"%u.%u.%u",
$currentVer->getMajor(),
$currentVer->getMinor(),
$currentVer->getPatch() + 1
));
$channel ??= "stable";
}
$channel = $filteredOpts["channel"] ?? VersionInfo::BUILD_CHANNEL;
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
echo "$currentVer will be published on release channel \"$channel\".\n";
@ -137,9 +159,6 @@ function main() : void{
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
echo "pushing changes in 5 seconds\n";
sleep(5);
systemWrapper('git push origin HEAD ' . $currentVer->getBaseVersion(), "failed to push changes to remote");
}
main();

View File

@ -104,3 +104,16 @@ Released 18th January 2023.
## Note about server load & performance
This version will report higher apparent server load than previous versions. The actual performance of the server is unchanged; the previous reported load was inaccurate.
These bugs have been present for nearly 5 years (ever since the first introduction of Snooze in 3.0.0).
# 4.12.11
Released 22nd January 2023.
## General
- Code is now tested and analysed using PHP 8.2 in addition to 8.1 and 8.0.
## Fixes
- Fixed pthreads 5.0.0 incorrectly being treated as compatible.
- Fixed deprecation errors on PHP 8.2.
## Documentation
- Updated documentation in `PlayerPreLoginEvent`.

94
changelogs/4.13-beta.md Normal file
View File

@ -0,0 +1,94 @@
**For Minecraft: Bedrock Edition 1.19.50**
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.13.0-BETA1
Released 18th January 2023.
## Gameplay
- Death message is now shown on the death screen when a player dies.
- Armour damage is now only increased if the armour reduced the damage taken.
- Implemented Swift Sneak enchantment.
- Fixed incorrect collision box calculation of walls and glass/bars when connected. Note: Client-side, wall connections are still broken; this only fixes projectile flight server-side.
## Performance
- Improved performance of chunk selection for chunk random ticking using a cache. This improves performance of chunk random ticking by 10-20%.
## Localization
- Added localized description for the `/dumpmemory` command.
## Permissions
- Added the following new core permissions:
- `pocketmine.command.effect.other` - allows the player to use the `/effect` command on other players (default operator only)
- `pocketmine.command.effect.self` - allows the player to use the `/effect` command on themselves (default operator only)
- `pocketmine.command.enchant.other` - allows the player to use the `/enchant` command on other players (default operator only)
- `pocketmine.command.enchant.self` - allows the player to use the `/enchant` command on themselves (default operator only)
- `pocketmine.command.gamemode.other` - allows the player to use the `/gamemode` command on other players (default operator only)
- `pocketmine.command.gamemode.self` - allows the player to use the `/gamemode` command on themselves (default operator only)
- `pocketmine.command.give.other` - allows the player to use the `/give` command on other players (default operator only)
- `pocketmine.command.give.self` - allows the player to use the `/give` command on themselves (default operator only)
- `pocketmine.command.spawnpoint.other` - allows the player to use the `/spawnpoint` command on other players (default operator only)
- `pocketmine.command.spawnpoint.self` - allows the player to use the `/spawnpoint` command on themselves (default operator only)
- `pocketmine.command.teleport.other` - allows the player to use the `/teleport` command on other players (default operator only)
- `pocketmine.command.teleport.self` - allows the player to use the `/teleport` command on themselves (default operator only)
- `pocketmine.command.title.other` - allows the player to use the `/title` command on other players (default operator only)
- `pocketmine.command.title.self` - allows the player to use the `/title` command on themselves (default operator only)
## Internals
- Decoupled `Player->sendMessage()` and `Player->sendTranslation()`.
- Refactored resource pack loading in `ResourcePackManager` to make it easier to understand.
- Client-aware translation processing has been moved to `NetworkSession` due to being client-specific.
- Replaced hardcoded strings with constants in various places.
- `NetworkSession` destructive cleanup is now deferred to the next session tick. This fixes various `InventoryManager` crashes when kicking players during events.
- Updated code using `strpos()` to use `str_starts_with()`, `str_ends_with()` and `str_contains()` where appropriate.
- Added documentation for some internal methods.
## API
### `pocketmine\command`
- The following new API methods have been added:
- `protected VanillaCommand->fetchPermittedPlayerTarget(...) : ?Player` - fetches a player target according to the given sender permissions, or null if not found or not permitted
### `pocketmine\entity`
- The following new API methods have been added:
- `public Living->getDisplayName() : string` - the name of the entity to be shown in death messages, commands etc.
### `pocketmine\event\world`
- The following new classes have been added:
- `WorldSoundEvent` - called when a sound is played in a world
- `WorldParticleEvent` - called when a particle is spawned in a world
### `pocketmine\item`
- The following new API methods have been added:
- `public Item->onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool` - called when a player interacts with an entity with this item in hand
### `pocketmine\lang`
- `Language->translate()` and `Language->translateString()` no longer parse nested translation in the "base text". This was never intended behaviour, and didn't work beyond the first level anyway.
### `pocketmine\player`
- The following new interfaces have been added:
- `PlayerDataProvider` - implemented by classes which want to offer storage for player data
- The following new classes have been added:
- `DatFilePlayerDataProvider` - the default player data provider, which stores `.dat` files in the `players` folder
- `PlayerDataLoadException` - thrown when an error occurs while loading player data
- `PlayerDataSaveException` - thrown when an error occurs while saving player data
- The following API methods have been deprecated:
- `Player->sendTranslation()` - use `Player->sendMessage()` instead with a `Translatable` message
### `pocketmine\resourcepacks`
- The following new API methods have been added:
- `public ResourcePackManager->setResourceStack(list<ResourcePack> $resourceStack) : void` - sets the list of resource packs to be applied by players
- `public ResourcePackManager->setPackEncryptionKey(string $id, ?string $key) : void` - sets the encryption key to be used for a resource pack
### `pocketmine\utils`
- The following new API methods have been added:
- `public static Filesystem::fileGetContents(...) : string` - a wrapper around `file_get_contents()` which throws an exception on failure
### `pocketmine\world`
- The following new API methods have been added:
- `public World->requestSafeSpawn(?Vector3 $spawn = null) : Promise<Position>` - an async version of `getSafeSpawn()` which generates all the needed chunks before returning

94
changelogs/4.13.md Normal file
View File

@ -0,0 +1,94 @@
**For Minecraft: Bedrock Edition 1.19.50**
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.13.0
Released 30th January 2023.
## Gameplay
- Death message is now shown on the death screen when a player dies.
- Armour damage is now only increased if the armour reduced the damage taken.
- Implemented Swift Sneak enchantment.
- Fixed incorrect collision box calculation of walls and glass/bars when connected. Note: Client-side, wall connections are still broken; this only fixes projectile flight server-side.
## Performance
- Improved performance of chunk selection for chunk random ticking using a cache. This improves performance of chunk random ticking by 10-20%.
## Localization
- Added localized description for the `/dumpmemory` command.
## Permissions
- Added the following new core permissions:
- `pocketmine.command.effect.other` - allows the player to use the `/effect` command on other players (default operator only)
- `pocketmine.command.effect.self` - allows the player to use the `/effect` command on themselves (default operator only)
- `pocketmine.command.enchant.other` - allows the player to use the `/enchant` command on other players (default operator only)
- `pocketmine.command.enchant.self` - allows the player to use the `/enchant` command on themselves (default operator only)
- `pocketmine.command.gamemode.other` - allows the player to use the `/gamemode` command on other players (default operator only)
- `pocketmine.command.gamemode.self` - allows the player to use the `/gamemode` command on themselves (default operator only)
- `pocketmine.command.give.other` - allows the player to use the `/give` command on other players (default operator only)
- `pocketmine.command.give.self` - allows the player to use the `/give` command on themselves (default operator only)
- `pocketmine.command.spawnpoint.other` - allows the player to use the `/spawnpoint` command on other players (default operator only)
- `pocketmine.command.spawnpoint.self` - allows the player to use the `/spawnpoint` command on themselves (default operator only)
- `pocketmine.command.teleport.other` - allows the player to use the `/teleport` command on other players (default operator only)
- `pocketmine.command.teleport.self` - allows the player to use the `/teleport` command on themselves (default operator only)
- `pocketmine.command.title.other` - allows the player to use the `/title` command on other players (default operator only)
- `pocketmine.command.title.self` - allows the player to use the `/title` command on themselves (default operator only)
## Internals
- Decoupled `Player->sendMessage()` and `Player->sendTranslation()`.
- Refactored resource pack loading in `ResourcePackManager` to make it easier to understand.
- Client-aware translation processing has been moved to `NetworkSession` due to being client-specific.
- Replaced hardcoded strings with constants in various places.
- `NetworkSession` destructive cleanup is now deferred to the next session tick. This fixes various `InventoryManager` crashes when kicking players during events.
- Updated code using `strpos()` to use `str_starts_with()`, `str_ends_with()` and `str_contains()` where appropriate.
- Added documentation for some internal methods.
## API
### `pocketmine\command`
- The following new API methods have been added:
- `protected VanillaCommand->fetchPermittedPlayerTarget(...) : ?Player` - fetches a player target according to the given sender permissions, or null if not found or not permitted
### `pocketmine\entity`
- The following new API methods have been added:
- `public Living->getDisplayName() : string` - the name of the entity to be shown in death messages, commands etc.
### `pocketmine\event\world`
- The following new classes have been added:
- `WorldSoundEvent` - called when a sound is played in a world
- `WorldParticleEvent` - called when a particle is spawned in a world
### `pocketmine\item`
- The following new API methods have been added:
- `public Item->onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool` - called when a player interacts with an entity with this item in hand
### `pocketmine\lang`
- `Language->translate()` and `Language->translateString()` no longer parse nested translation in the "base text". This was never intended behaviour, and didn't work beyond the first level anyway.
### `pocketmine\player`
- The following new interfaces have been added:
- `PlayerDataProvider` - implemented by classes which want to offer storage for player data
- The following new classes have been added:
- `DatFilePlayerDataProvider` - the default player data provider, which stores `.dat` files in the `players` folder
- `PlayerDataLoadException` - thrown when an error occurs while loading player data
- `PlayerDataSaveException` - thrown when an error occurs while saving player data
- The following API methods have been deprecated:
- `Player->sendTranslation()` - use `Player->sendMessage()` instead with a `Translatable` message
### `pocketmine\resourcepacks`
- The following new API methods have been added:
- `public ResourcePackManager->setResourceStack(list<ResourcePack> $resourceStack) : void` - sets the list of resource packs to be applied by players
- `public ResourcePackManager->setPackEncryptionKey(string $id, ?string $key) : void` - sets the encryption key to be used for a resource pack
### `pocketmine\utils`
- The following new API methods have been added:
- `public static Filesystem::fileGetContents(...) : string` - a wrapper around `file_get_contents()` which throws an exception on failure
### `pocketmine\world`
- The following new API methods have been added:
- `public World->requestSafeSpawn(?Vector3 $spawn = null) : Promise<Position>` - an async version of `getSafeSpawn()` which generates all the needed chunks before returning

21
changelogs/4.14.md Normal file
View File

@ -0,0 +1,21 @@
**For Minecraft: Bedrock Edition 1.19.60**
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.14.0
Released 8th February 2023.
## General
- Added support for Minecraft: Bedrock Edition 1.19.60.
- Removed support for older versions.
# 4.14.1
Released 15th February 2023.
## Fixes
- Fixed all players getting kicked with `Receiving packets too fast` if a server tick takes longer than 5 seconds (e.g. because of autosave or GC).
- Fixed players getting kicked when linking with entities.

38
changelogs/4.15.md Normal file
View File

@ -0,0 +1,38 @@
**For Minecraft: Bedrock Edition 1.19.62**
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.15.0
Released 17th February 2023.
## General
- Added support for Minecraft: Bedrock Edition 1.19.62.
- Removed support for older versions.
# 4.15.1
Released 21st February 2023.
## Fixes
- Fixed dropped items not despawning when in non-ticking chunks.
- Fixed dropped items not despawning if an infinite pickup delay is set.
- Fixed infinite despawn delay (never despawn) being ignored for dropped items.
# 4.15.2
Released 24th February 2023.
## General
- Accept Minecraft: Bedrock Edition 1.19.63 (identical protocol to 1.19.62, but different version due to Mojang mixup).
## Fixes
- Fixed `World Population` timer sometimes not being stopped, causing strange results in timings reports.
# 4.15.3
Released 7th March 2023.
## Fixes
- Fixed `/dumpmemory` crash when any object contained an `INF` or `NaN` float value.
- Updated RakLib for security fixes.

45
changelogs/4.16-beta.md Normal file
View File

@ -0,0 +1,45 @@
**For Minecraft: Bedrock Edition 1.19.62**
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.16.0-BETA1
Released 4th March 2023.
## General
- Added granular timings for packet encode, similar to the existing timings for packet decode.
- Timings now covers several areas of the network system which were previously not counted by network timings, but were counted by total timings. This provides a better insight into the performance of the network system.
## Performance
- Improved performance of packet batch handling by avoiding unnecessary object allocations.
- Improved performance of packet broadcasting when the broadcast size is below the batch threshold. Previously, the packets would be encoded once by every recipient, but now they are encoded once and then added to the send buffer of each session in their raw form.
- This change mostly affects servers with larger maps, where players are more widely distributed.
## Build system
- Added a new script `build/generate-bedrockdata-path-consts.php`, which must be run whenever BedrockData is updated. This script generates a class of constants with the file paths of all BedrockData files.
## API
### `pocketmine\entity`
- The following new API methods have been added:
- `public Entity->getGravity() : float` - returns the entity's gravity acceleration in blocks/tick^2
- `public Entity->setGravity(float $gravity) : void` - sets the entity's gravity acceleration in blocks/tick^2
## Internals
- Now uses [`pocketmine/bedrock-data` 2.0.0](https://github.com/pmmp/BedrockData/releases/tag/2.0.0+bedrock-1.19.60).
- This version is now used by both PM4 and PM5, reducing maintenance burden.
- Now uses [`pocketmine/bedrock-protocol` 19.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/19.3.0+bedrock-1.19.62).
- This version provides new APIs for handling packet batches which enabled improving performance and adding new features, such as detailed packet encode timings.
- Crafting recipes and creative inventory data are now loaded from `recipes/legacy_recipes.json` and `recipes/legacy_creativeitems.json` respectively. Previously, these were loaded from BedrockData directly, but BedrockData 2.0 now uses a format which can't be supported in 4.x without BC breaks.
- Added dependencies on [`pocketmine/bedrock-block-upgrade-schema`](https://github.com/pmmp/BedrockBlockUpgradeSchema) and [`pocketmine/bedrock-item-upgrade-schema`](https://github.com/pmmp/BedrockItemUpgradeSchema). These provide mapping files no longer present in BedrockData 2.0.
- Reduced and/or eliminated most usages of `PacketBatch`, since it only appeared as a throwaway object and was therefore wasting performance.
- `Compressor` now exposes `getCompressionThreshold()` instead of `willCompress()`, which allows determining whether a batch will be compressed without allocating it.
- Added `pocketmine\data\bedrock\BedrockDataFiles`, an auto-generated class of constants with the file paths of all BedrockData files. This makes it easier to locate usages, detect unused files and avoid typos.
# 4.16.0-BETA2
Released 4th March 2023.
## General
- Fixed incorrect release channel for 4.16.0-BETA1.

41
changelogs/4.16.md Normal file
View File

@ -0,0 +1,41 @@
**For Minecraft: Bedrock Edition 1.19.62**
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.16.0
Released 7th March 2023.
## General
- Added granular timings for packet encode, similar to the existing timings for packet decode.
- Split `Player Network Send - Compression` timings into two timers, one for `Session Buffer` compression and one for `Broadcast` compression.
- Timings now covers several areas of the network system which were previously not counted by network timings, but were counted by total timings. This provides a better insight into the performance of the network system.
## Performance
- Improved performance of packet batch handling by avoiding unnecessary object allocations.
- Improved performance of packet broadcasting when the broadcast size is below the batch threshold. Previously, the packets would be encoded once by every recipient, but now they are encoded once and then added to the send buffer of each session in their raw form.
- This change mostly affects servers with larger maps, where players are more widely distributed.
- Improved performance of packet broadcasting when the broadcast has only one recipient (allow the session to compress the packet with the rest of its buffer).
## Build system
- Added a new script `build/generate-bedrockdata-path-consts.php`, which must be run whenever BedrockData is updated. This script generates a class of constants with the file paths of all BedrockData files.
## API
### `pocketmine\entity`
- The following new API methods have been added:
- `public Entity->getGravity() : float` - returns the entity's gravity acceleration in blocks/tick^2
- `public Entity->setGravity(float $gravity) : void` - sets the entity's gravity acceleration in blocks/tick^2
## Internals
- Now uses [`pocketmine/bedrock-data` 2.0.0](https://github.com/pmmp/BedrockData/releases/tag/2.0.0+bedrock-1.19.60).
- This version is now used by both PM4 and PM5, reducing maintenance burden.
- Now uses [`pocketmine/bedrock-protocol` 19.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/19.3.0+bedrock-1.19.62).
- This version provides new APIs for handling packet batches which enabled improving performance and adding new features, such as detailed packet encode timings.
- Crafting recipes and creative inventory data are now loaded from `recipes/legacy_recipes.json` and `recipes/legacy_creativeitems.json` respectively. Previously, these were loaded from BedrockData directly, but BedrockData 2.0 now uses a format which can't be supported in 4.x without BC breaks.
- Added dependencies on [`pocketmine/bedrock-block-upgrade-schema`](https://github.com/pmmp/BedrockBlockUpgradeSchema) and [`pocketmine/bedrock-item-upgrade-schema`](https://github.com/pmmp/BedrockItemUpgradeSchema). These provide mapping files no longer present in BedrockData 2.0.
- Reduced and/or eliminated most usages of `PacketBatch`, since it only appeared as a throwaway object and was therefore wasting performance.
- `Compressor` now exposes `getCompressionThreshold()` instead of `willCompress()`, which allows determining whether a batch will be compressed without allocating it.
- Added `pocketmine\data\bedrock\BedrockDataFiles`, an auto-generated class of constants with the file paths of all BedrockData files. This makes it easier to locate usages, detect unused files and avoid typos.

39
changelogs/4.17.md Normal file
View File

@ -0,0 +1,39 @@
**For Minecraft: Bedrock Edition 1.19.70**
### Note about API versions
Plugins which don't touch the protocol and 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 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.
# 4.17.0
Released 14th March 2023.
## General
- Added support for Minecraft: Bedrock Edition 1.19.70.
- Removed support for older versions.
# 4.17.1
Released 22nd March 2023.
## General
- Docker images for PocketMine-MP are now published on [GitHub Container Registry](https://github.com/pmmp/PocketMine-MP/pkgs/container/pocketmine-mp). The Docker Hub images will stop being maintained in the future.
- Updated translations.
## Fixes
- Fixed server crash on empty packets in certain cases.
- Fixed mushroom blocks dropping the wrong items when broken with a silk-touch tool.
- Fixed mushroom blocks giving the wrong items when block-picked.
- Fixed missing ability flag `PRIVILEGED_BUILDER`.
## Internals
- `update-updater-api.yml` workflow now uses `github.repository_owner` to make it easier to test the workflow on forks.
- Added version-specific channels to `update.pmmp.io`, such as `4`, `4.18-beta`, `4.17`, etc.
- Replaced deprecated `::set-output` commands in GitHub Actions workflows.
- `build/make-release.php` no longer automatically pushes changes, to avoid accidents when testing release workflows on forks.
# 4.17.2
Released 29th March 2023.
## Fixes
- Fixed players being unable to join due to the appearance of a new `x5t` field in the JWT header of Xbox Live authentication tokens.

91
changelogs/4.18-alpha.md Normal file
View File

@ -0,0 +1,91 @@
**For Minecraft: Bedrock Edition 1.19.70**
### 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.
### Alpha release warning
Alpha releases are **experimental**. Features introduced in these releases are subject to change or removal.
APIs which existed **prior** to this version will continue to work as normal, so plugins which use them will continue to work.
### Highlights
This version makes changes to the internal network system to improve server performance and reduce memory usage.
While these changes don't affect non-internal API, they are still significant enough to warrant a new minor version, as they may break plugins which use the internal network API (not recommended).
# 4.18.0-ALPHA1
Released 16th March 2023.
## General
- Improved server performance in congested areas of the world (lots of players and/or entities in the same area).
## API
### `pocketmine\event\server`
- The following new classes have been added:
- `DataPacketDecodeEvent` - called before a packet is decoded by a `NetworkSession`; useful to mitigate DoS attacks if PocketMine-MP hasn't been patched against new bugs yet
## Internals
- Introduced new system for broadcasting entity events to network sessions.
- This change improves performance when lots of players and/or entities are in the same area.
- New interface `EntityEventBroadcaster` and class `StandardEntityEventBroadcaster` have been added to implement this.
- All entity-specific `on*()` and `sync*()` methods have been removed from `NetworkSession` (BC break).
- `NetworkSession` now accepts an `EntityEventBroadcaster` instance in its constructor.
- `NetworkBroadcastUtils::broadcastEntityEvent()` can be used to efficiently broadcast events to unique broadcasters shared by several network sessions.
- All network sessions now share the same `PacketSerializerContext` and `PacketBroadcaster` by default.
- Previously, every session had its own context, meaning that broadcast optimisations were not used, causing significant performance losses compared to 3.x.
- This change improves performance in congested areas by allowing reuse of previously encoded packet buffers for all sessions sharing the same context.
- Packet broadcasts are automatically encoded separately per unique `PacketSerializerContext` instance. This allows, for example, a multi-version fork to have a separate context for each protocol version, to ensure maximum broadcast efficiency while encoding different packets for different versions.
- `PacketSerializerContext` is now passed in `NetworkSession::__construct()`, instead of being created by the session.
- `StandardPacketBroadcaster` is now locked to a single `PacketSerializer` context, reducing complexity.
- Introduced `NetworkBroadcastUtils::broadcastPackets()`, replacing `Server->broadcastPackets()`.
- `Server->broadcastPackets()` has been deprecated. It will be removed in a future version.
# 4.18.0-ALPHA2
Released 21st March 2023.
## General
- Included more sections of the network system in Player Network Send timings.
- Changed the names of some timings to make them more user-friendly.
- Removed packet IDs from `receivePacket` and `sendPacket` timings, as they were not very useful.
- Added new specialized timers for the following:
- Item entity base ticking (merging)
- Player movement processing
- Entity movement processing (collision checking section)
- Projectile movement (all)
- Projectile movement processing (ray tracing section)
## API
### `pocketmine\crafting`
- The following new API methods have been added:
- `CraftingManager->getCraftingRecipeIndex() : array<int, CraftingRecipe>` - returns a list of all crafting recipes
- `CraftingManager->getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe` - returns the crafting recipe at the given index, or null if it doesn't exist
### `pocketmine\inventory\transaction`
- The following API methods have changed signatures:
- `CraftingTransaction->__construct()` now accepts additional arguments `?CraftingRecipe $recipe = null, ?int $repetitions = null`
- The following new API methods have been added:
- `TransactionBuilderInventory->getActualInventory() : Inventory` - returns the actual inventory that this inventory is a proxy for
## Internals
### Network
- Introduced support for the `ItemStackRequest` Minecraft: Bedrock network protocol.
- This fixes a large number of inventory- and crafting-related bugs.
- This also improves server security by closing off many code pathways that might have been used for exploits. `TypeConverter->netItemStackToCore()` is no longer used in server code, and remains for tool usage only.
- This system is also significantly more bandwidth-efficient and has lower overhead than the legacy system.
- This now opens the gateway to easily implement lots of gameplay features which have been missing for a long time, such as enchanting, anvils, looms, and more.
- Significant changes have been made to `pocketmine\network\mcpe\InventoryManager` internals. These shouldn't affect plugins, but may affect plugins which use internal network API.
- **No changes have been made to the plugin `InventoryTransaction` API**.
- This system has been implemented as a shim for the existing PocketMine-MP transaction system to preserve plugin compatibility. Plugins using `InventoryTransactionEvent` should continue to work seamlessly.
- The `InventoryTransaction` API will be redesigned in a future major version to make use of the new information provided by the `ItemStackRequest` system.
- `InventoryTransactionPacket` is no longer sent by the client for "regular" inventory actions. However, it is still sent when dropping items, interacting with blocks, and using items.
- Inventory slot and content syncing is now buffered until the end of the tick. This reduces outbound network usage when the client performs multiple transactions in a single tick (e.g. crafting a stack of items).
- Renamed some `InventoryManager` internal properties to make them easier to understand.
- `TypeConverter->createInventoryAction()` has been removed.
- Packet batch limit has been lowered to `100` packets. With the introduction of `ItemStackRequest`, this is more than sufficient for normal gameplay.
### Other
- Use `Vector3::zero()` instead of `new Vector3()` in some places.

123
changelogs/4.18.md Normal file
View File

@ -0,0 +1,123 @@
**For Minecraft: Bedrock Edition 1.19.70**
### 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.
### Highlights
This version significantly improves server performance with many players and/or entities by making changes to the internal network system.
It also introduces support for the newer `ItemStackRequest` protocol, which fixes many bugs and improves server security.
While these changes don't affect non-internal API, they are still significant enough to warrant a new minor version, as they may break plugins which use the internal network API (not recommended).
# 4.18.0
Released 25th March 2023.
## General
- Significantly improved server performance in congested areas of the world (lots of players and/or entities in the same area).
- Included more sections of the network system in `Player Network Send` performance timings.
- Changed the names of some performance timings to make them more user-friendly.
- Removed packet IDs from `receivePacket` and `sendPacket` performance timings, as they were not very useful.
- Added new specialized performance timings for the following:
- Item entity base ticking (merging)
- Player movement processing
- Entity movement processing (collision checking section)
- Projectile movement (all)
- Projectile movement processing (ray tracing section)
## API
### `pocketmine\crafting`
- The following new API methods have been added:
- `CraftingManager->getCraftingRecipeIndex() : array<int, CraftingRecipe>` - returns a list of all crafting recipes
- `CraftingManager->getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe` - returns the crafting recipe at the given index, or null if it doesn't exist
### `pocketmine\event\server`
- The following new classes have been added:
- `DataPacketDecodeEvent` - called before a packet is decoded by a `NetworkSession`; useful to mitigate DoS attacks if PocketMine-MP hasn't been patched against new bugs yet
### `pocketmine\inventory\transaction`
- The following API methods have changed signatures:
- `CraftingTransaction->__construct()` now accepts additional arguments `?CraftingRecipe $recipe = null, ?int $repetitions = null`
- The following new API methods have been added:
- `TransactionBuilderInventory->getActualInventory() : Inventory` - returns the actual inventory that this inventory is a proxy for
## Internals
### Network
- Introduced new system for broadcasting entity events to network sessions.
- This change improves performance when lots of players and/or entities are in the same area.
- New interface `EntityEventBroadcaster` and class `StandardEntityEventBroadcaster` have been added to implement this.
- All entity-specific `on*()` and `sync*()` methods have been removed from `NetworkSession` (internals backwards compatibility break, not covered by API version guarantee).
- `NetworkSession` now accepts an `EntityEventBroadcaster` instance in its constructor.
- `NetworkBroadcastUtils::broadcastEntityEvent()` can be used to efficiently broadcast events to unique broadcasters shared by several network sessions.
- All network sessions now share the same `PacketSerializerContext` and `PacketBroadcaster` by default.
- Previously, every session had its own context, meaning that broadcast optimisations were not used, causing significant performance losses compared to 3.x.
- This change improves performance in congested areas by allowing reuse of previously encoded packet buffers for all sessions sharing the same context.
- Packet broadcasts are automatically encoded separately per unique `PacketSerializerContext` instance. This allows, for example, a multi-version fork to have a separate context for each protocol version, to ensure maximum broadcast efficiency while encoding different packets for different versions.
- `PacketSerializerContext` is now passed in `NetworkSession::__construct()`, instead of being created by the session.
- `StandardPacketBroadcaster` is now locked to a single `PacketSerializer` context, reducing complexity.
- Introduced `NetworkBroadcastUtils::broadcastPackets()`, replacing `Server->broadcastPackets()`.
- `Server->broadcastPackets()` has been deprecated. It will be removed in a future version.
- Introduced support for the `ItemStackRequest` Minecraft: Bedrock network protocol.
- This fixes a large number of inventory- and crafting-related bugs.
- This also improves server security by closing off many code pathways that might have been used for exploits. `TypeConverter->netItemStackToCore()` is no longer used in server code, and remains for tool usage only.
- This system is also significantly more bandwidth-efficient and has lower overhead than the legacy system.
- This now opens the gateway to easily implement lots of gameplay features which have been missing for a long time, such as enchanting, anvils, looms, and more.
- Significant changes have been made to `pocketmine\network\mcpe\InventoryManager` internals. These shouldn't affect plugins, but may affect plugins which use internal network API.
- **No changes have been made to the plugin `InventoryTransaction` API**.
- This system has been implemented as a shim for the existing PocketMine-MP transaction system to preserve plugin compatibility. Plugins using `InventoryTransactionEvent` should continue to work seamlessly.
- The `InventoryTransaction` API will be redesigned in a future major version to make use of the new information provided by the `ItemStackRequest` system.
- `InventoryTransactionPacket` is no longer sent by the client for "regular" inventory actions. However, it is still sent when dropping items, interacting with blocks, and using items.
- Inventory slot and content syncing is now buffered until the end of the tick. This reduces outbound network usage when the client performs multiple transactions in a single tick (e.g. crafting a stack of items).
- Renamed some `InventoryManager` internal properties to make them easier to understand.
- `TypeConverter->createInventoryAction()` has been removed.
- Packet batch limit has been lowered to `100` packets. With the introduction of `ItemStackRequest`, this is more than sufficient for normal gameplay.
### Other
- Use `Vector3::zero()` instead of `new Vector3()` in some places.
# 4.18.1
Released 27th March 2023.
## General
- `RakLibInterface` now logs the name of the currently active session if a crash occurs when processing a packet. This makes it easier to reproduce bugs, which is important to be able to fix them.
- Added more detailed debugging information to `InventoryManager->syncSelectedHotbarSlot()`.
## Fixes
- Fixed server crash when attempting to drop more of an item from a stack than available in the inventory.
- Fixed packet processing errors when editing writable books.
- Fixed packet processing errors when shift-clicking on the recipe book to craft recipes which draw from a large number of inventory slots.
# 4.18.2
Released 29th March 2023.
## Fixes
- Fixed players being unable to join due to the appearance of a new `x5t` field in the JWT header of Xbox Live authentication tokens.
- Fixed items' durability appearing to reset when moving them around in the inventory.
# 4.18.3
Released 5th April 2023.
## Fixes
- Fixed Average Players not being shown on timings reports when custom player classes are used.
- Fixed incorrect tick violation calculation in timings reports.
- Fixed not being able to add or remove items from the offhand slot.
- Fixed creative inventory item count corruption when taking items (some players would see 64x items in the creative inventory after rejoining or changing gamemode).
- Fixed not being able to drop items directly from the creative inventory on mobile.
- Fixed `DataPacketReceiveEvent` not being called for packets sent by `EntityEventBroadcaster`.
- `CreativeInventory::getItem()` and `CreativeInventory::getAll()` now return cloned itemstacks, to prevent accidental modification of the creative inventory.
# 4.18.4
Released 10th April 2023.
## Fixes
- Fixed movement becoming broken when the player moves at high speed (e.g. due to high levels of the Speed effect).
- Updated dependencies to get fixes in `pocketmine/nbt` and `pocketmine/bedrock-protocol`.
## Internals
### Network
- Game packets are now rate-limited in a similar manner to packet batches. This helps to more effectively mitigate certain types of DoS attacks.
- Added a new class `PacketRateLimiter`, implementing functionality previously baked directly into `NetworkSession` in a more generic way to allow reuse.

111
changelogs/4.19.md Normal file
View File

@ -0,0 +1,111 @@
**For Minecraft: Bedrock Edition 1.19.70**
### 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.
### Highlights
This version introduces support for a new, more advanced version of Timings.
This improved system provides more detail than the old system, and supports being displayed in a tree view, making it much easier to see which timers contribute to which other timers.
In addition, some minor performance improvements have been made, along with a couple of minor API additions.
# 4.19.0
Released 11th April 2023.
## General
- Updated the Timings system.
- Timings records now include parent information, allowing them to be displayed in a tree view (e.g. https://timings.pmmp.io/?id=303556).
- Timings records now include additional information, such as Peak (max time spent on any single tick), and Ticks (number of ticks the timer was active on).
- New timings have been added for every event.
- A new timer `Player Network Send - Pre-Spawn Game Data` has been added, and covers most of the time spent handling `ResourcePackClientResponsePacket`, giving a clearer picture of what's happening.
- Improved performance of the plugin event system.
- By introducing some caching, the event system now has 90% less overhead than in previous versions.
- Improved performance of the random chunk ticking system.
- The selection of ticked random chunks, and their validation for ticking, is now cached. This significantly reduces the overhead of chunk selection.
- Factions servers and other game modes with big maps and sparsely populated areas will see the most benefit from this change.
- Real-world performance benefit of this change is anywhere from 0-20%, depending on server type and configuration.
- The `timings paste` command now logs a debug message with the server response on failure to paste a timings report.
## API
### `pocketmine\entity\object`
- The following API constants have been added:
- `ExperienceOrb::DEFAULT_DESPAWN_DELAY` - the default delay in ticks before an experience orb despawns
- `ExperienceOrb::NEVER_DESPAWN` - magic value for `setDespawnDelay()` to make an experience orb never despawn
- `ExperienceOrb::MAX_DESPAWN_DELAY` - the maximum delay in ticks before an experience orb despawns
- The following API methods have been added:
- `public ExperienceOrb->getDespawnDelay() : int` - returns the delay in ticks before this experience orb despawns
- `public ExperienceOrb->setDespawnDelay(int $despawnDelay) : void` - sets the delay in ticks before this experience orb despawns
- The following properties have been deprecated
- `ExperienceOrb->age` - superseded by despawn delay methods
### `pocketmine\event`
- The following API methods have been added:
- `public HandlerList->getListenerList() : list<RegisteredListener>` - returns an ordered list of handlers to be called for the event
### `pocketmine\player`
- The following API methods have behavioural changes:
- `ChunkSelector->selectChunks()` now yields the distance in chunks from the center as the key, instead of an incrementing integer.
- The following classes have been deprecated:
- `PlayerChunkLoader` (this was technically internal, but never marked as such)
### `pocketmine\timings`
- The following API constants have been deprecated:
- `Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX` - this is superseded by timings group support (see `Timings::GROUP_BREAKDOWN`)
- The following API constants have been added:
- `Timings::GROUP_BREAKDOWN` - this group makes a timer appear in the `Minecraft - Breakdown` section of a timings report
- The following API methods have been added:
- `public TimingsHandler->getGroup() : string` - returns the name of the table in which this timer will appear in a timings report
- The following API methods have changed signatures:
- `TimingsHandler->__construct()` now accepts an additional, optional `string $group` parameter, which defaults to `Minecraft`.
### `pocketmine\world`
#### Highlights
Ticking chunks is now done using the `ChunkTicker` system, which has a much more fine-grained API than the old `TickingChunkLoader` system, as well as better performance.
It works similarly to the `ChunkLoader` system, in that chunks will be ticked as long as at least one `ChunkTicker` is registered for them.
#### API changes
- The following classes have been deprecated:
- `TickingChunkLoader` - this has been superseded by the more powerful and performant `ChunkTicker` APIs
- The following classes have been added:
- `ChunkTicker` - an opaque object used for `registerTickingChunk()` to instruct the `World` that we want a chunk to be ticked
- The following API methods have been added:
- `public World->registerTickingChunk(ChunkTicker $ticker, int $chunkX, int $chunkZ) : void` - registers a chunk to be ticked by the given `ChunkTicker`
- `public World->unregisterTickingChunk(ChunkTicker $ticker, int $chunkX, int $chunkZ) : void` - unregisters a chunk from being ticked by the given `ChunkTicker`
# 4.19.1
Released 14th April 2023.
## Fixes
- Fixed inventory rollbacks when spreading items in ender chests.
- Fixed inventory rollbacks when shift-clicking to craft and the outputs would have been split across multiple inventory slots.
- Fixed incorrect spawn terrain generation for newly created worlds.
- Fixed `chunk-ticking.tick-radius` not disabling chunk ticking when set to `0`.
- Fixed chunks not being ticked if they previously left a player's simulation distance without leaving their view distance.
- Fixed height of collision boxes for Grass Path and Farmland blocks.
# 4.19.2
Released 14th April 2023.
## Fixes
- 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.

79
changelogs/4.20.md Normal file
View File

@ -0,0 +1,79 @@
**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.
# 4.20.4
Released 6th May 2023.
## Fixes
- Fixed players being forced into flight mode in every game mode.
- Moral of the story: do not assume anything in Mojang internals does what its name suggests...
# 4.20.5
Released 30th May 2023.
## Fixes
- Fixed server crash due to a bug in upstream dependency [`netresearch/jsonmapper`](https://github.com/cweiske/JsonMapper).

77
changelogs/4.21.md Normal file
View File

@ -0,0 +1,77 @@
**For Minecraft: Bedrock Edition 1.19.80**
### Note about API versions
Plugins which don't touch the `pocketmine\network\mcpe` namespace, and don't use reflection or any internal methods,
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.
# 4.21.0
Released 17th May 2023.
## General
- PHP 8.1 is now required. Most plugins should run without changes, but some might need to be updated due to language-level deprecations.
- Ticking chunk count is now shown separately from loaded chunk count in the `/status` command, providing useful performance information.
- Further improved performance of ticking chunk selection.
- Improved performance of some inventory functions.
- Reduced server memory footprint in most cases by ~9 MB per thread.
- Due to large overhead, async network compression is now only used for packets larger than 10 KB by default.
## Configuration
- Added the following new `pocketmine.yml` configuration options:
- `network.async-compression-threshold` - minimum size of packet which will be compressed using `AsyncTask`
- Default is 10 KB, which means that very few packets will use async compression in practice. This is because the overhead of compressing async is currently so high that it's not worth it for smaller packets.
## Timings
- Timings reports no longer include the unused metadata fields `Entities` and `LivingEntities`.
- Timings reports now correctly calculate the peak time of a timer.
- Previously it was incorrectly recorded as the longest time spent in a single tick, rather than the longest time spent in a single activation.
- Timings report version has been bumped to `2` to reflect this change.
- All world-specific timers now have generic aggregate timings, making it much easier to locate performance patterns across all worlds.
## Gameplay
- Players in spectator mode are no longer able to pick blocks, and now have finite resources similar to survival mode.
## API
### `pocketmine\world`
- The following API methods have been added:
- `public World->getTickingChunks() : list<int>` - returns a list of chunk position hashes (a la `World::chunkHash()`) which are currently valid for random ticking
### `pocketmine\inventory`
- The following API methods have been added:
- `protected BaseInventory->getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int` - returns the number of items in the given stack if the content of the slot matches the test item, or zero otherwise
- This should be overridden if directly extending `BaseInventory` to provide a performance-optimised version. A slow default implementation is provided, but it will be removed in the future.
## Internals
### Entity
- Unused `NameTag` tag is no longer saved for `Human` entities.
### Inventory
- `BaseInventory` now uses a new internal method `getMatchingItemCount()` to locate items in the inventory without useless cloning. This improves performance of various API methods, such as `addItem()`, `contains()`, and more.
- Specialization of `Inventory->isSlotEmpty()` in `BaseInventory` subclasses has been added to improve performance of some API methods.
### Network
- `RuntimeBlockMapping` no longer keeps all block palette NBT data in memory.
- This significantly reduces server idle memory footprint.
- For multi-version implementations, this will have a significant impact on memory usage, since a different block palette is often required to support each version.
- NBT will be lazy-loaded into memory and cached if `getBedrockKnownStates()` is called. However, this is not used by PocketMine-MP under normal circumstances.
- Removed unnecessary usage of `Utils::validateCallableSignature()` from some internal network pathways, improving performance.
### Scheduler
- `AsyncPool` no longer double-checks progress updates on completed tasks.
### World
- Ticking chunks are now tracked in `World->validTickingChunks` and `World->recheckTickingChunks`.
- This allows avoiding rechecking every ticking chunk for validity during ticking chunk selection, improving performance.
- In some cases, this allows bypassing chunk selection entirely, reducing selection cost to zero.
- Registered but ineligible ticking chunks are no longer rechecked every tick.
- This was causing wasted cycles during async worker backlog.
- The internal system must call `markTickingChunkForRecheck()` whenever a ticking chunk's eligibility for ticking has potentially changed, rather than just when it has changed from a yes to a no.
# 4.21.1
Released 30th May 2023.
## Fixes
- Fixed server crash due to a bug in upstream dependency [`netresearch/jsonmapper`](https://github.com/cweiske/JsonMapper).

24
changelogs/4.22.md Normal file
View File

@ -0,0 +1,24 @@
# 4.22.0
Released 7th June 2023.
**For Minecraft: Bedrock Edition 1.20.0**
This is a support release for Minecraft: Bedrock Edition 1.20.0.
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## Interim releases
If you're upgrading from 4.20.x directly to 4.22.x, please also read the following changelogs, as the interim releases contain important changes:
- [4.21.0](https://github.com/pmmp/PocketMine-MP/blob/4.22.0/changelogs/4.21.md#4210) - PHP 8.1 minimum version, minor performance improvements
## General
- Added support for Minecraft: Bedrock Edition 1.20.0.
- Removed support for older versions.
## Fixes
- Removed deprecated `ReflectionProperty::setAccessible()` calls.
- Fixed jukebox music not stopping when destroyed by an explosion.

View File

@ -5,7 +5,7 @@
"homepage": "https://pmmp.io",
"license": "LGPL-3.0",
"require": {
"php": "^8.0",
"php": "^8.1",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-crypto": "^0.3.1",
@ -33,15 +33,17 @@
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.13.0+bedrock-1.19.50",
"pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50",
"pocketmine/netresearch-jsonmapper": "~v4.2.999",
"pocketmine/bedrock-block-upgrade-schema": "~2.2.0+bedrock-1.20.0",
"pocketmine/bedrock-data": "~2.3.0+bedrock-1.20.0",
"pocketmine/bedrock-item-upgrade-schema": "~1.3.0+bedrock-1.20.0",
"pocketmine/bedrock-protocol": "~22.0.0+bedrock-1.20.0",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0",
"pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.11.0",
"pocketmine/locale-data": "~2.19.0",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0",
@ -54,10 +56,10 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "1.9.12",
"phpstan/phpstan": "1.10.15",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"
"phpunit/phpunit": "^10.1"
},
"autoload": {
"psr-4": {
@ -75,7 +77,7 @@
},
"config": {
"platform": {
"php": "8.0.0"
"php": "8.1.0"
},
"sort-packages": true
},

1023
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,6 @@ includes:
- tests/phpstan/configs/impossible-generics.neon
- tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon
- tests/phpstan/configs/runtime-type-checks.neon
- tests/phpstan/configs/spl-fixed-array-sucks.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon

File diff suppressed because it is too large Load Diff

49988
resources/legacy_recipes.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -85,8 +85,11 @@ network:
batch-threshold: 256
#Compression level used when sending batched packets. Higher = more CPU, less bandwidth usage
compression-level: 6
#Use AsyncTasks for compression. Adds half/one tick delay, less CPU load on main thread
#Use AsyncTasks for compression during the main game session. Increases latency, but may reduce main thread load
async-compression: false
#Threshold for async compression, in bytes. Only packets larger than this will be compressed asynchronously
#Due to large overhead of AsyncTask, async compression isn't worth it except for large packets
async-compression-threshold: 10000
#Experimental. Use UPnP to automatically port forward
upnp-forwarding: false
#Maximum size in bytes of packets sent over the network (default 1492 bytes). Packets larger than this will be

View File

@ -37,4 +37,6 @@ define('pocketmine\PATH', dirname(__DIR__) . '/');
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
define('pocketmine\BEDROCK_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-data/');
define('pocketmine\LOCALE_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/locale-data/');
define('pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-block-upgrade-schema/');
define('pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-item-upgrade-schema/');
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');

View File

@ -49,6 +49,7 @@ use function ini_get;
use function ini_set;
use function intdiv;
use function is_array;
use function is_float;
use function is_object;
use function is_resource;
use function is_string;
@ -69,6 +70,10 @@ use const JSON_UNESCAPED_SLASHES;
use const SORT_NUMERIC;
class MemoryManager{
private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND;
private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2;
private const DEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND;
private int $memoryLimit;
private int $globalMemoryLimit;
private int $checkRate;
@ -113,20 +118,12 @@ class MemoryManager{
if($m <= 0){
$defaultMemory = 0;
}else{
switch(mb_strtoupper($matches[2])){
case "K":
$defaultMemory = intdiv($m, 1024);
break;
case "M":
$defaultMemory = $m;
break;
case "G":
$defaultMemory = $m * 1024;
break;
default:
$defaultMemory = $m;
break;
}
$defaultMemory = match(mb_strtoupper($matches[2])){
"K" => intdiv($m, 1024),
"M" => $m,
"G" => $m * 1024,
default => $m,
};
}
}
@ -139,11 +136,11 @@ class MemoryManager{
}
$this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024;
$this->checkRate = $config->getPropertyInt("memory.check-rate", 20);
$this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE);
$this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true);
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", 30);
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", 36000);
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEFAULT_TICKS_PER_GC);
$this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true);
$this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true);
@ -319,9 +316,6 @@ class MemoryManager{
continue;
}
if(!$property->isPublic()){
$property->setAccessible(true);
}
if(!$property->isInitialized()){
continue;
}
@ -445,9 +439,6 @@ class MemoryManager{
continue;
}
}
if(!$property->isPublic()){
$property->setAccessible(true);
}
if(!$property->isInitialized($object)){
continue;
}
@ -523,6 +514,8 @@ class MemoryManager{
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
}elseif(is_resource($from)){
$data = "(resource) " . print_r($from, true);
}elseif(is_float($from)){
$data = "(float) $from";
}else{
$data = $from;
}

View File

@ -50,7 +50,7 @@ namespace pocketmine {
require_once __DIR__ . '/VersionInfo.php';
const MIN_PHP_VERSION = "8.0.0";
const MIN_PHP_VERSION = "8.1.0";
/**
* @param string $message
@ -122,7 +122,7 @@ namespace pocketmine {
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") > 0){
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") >= 0){
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
}
}
@ -265,9 +265,6 @@ JIT_WARNING
exit(1);
}
}
if(extension_loaded('parallel')){
\parallel\bootstrap(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}
ErrorToExceptionHandler::set();

View File

@ -43,27 +43,29 @@ 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;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Language;
use pocketmine\lang\LanguageNotFoundException;
use pocketmine\lang\Translatable;
use pocketmine\nbt\BigEndianNbtSerializer;
use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\TreeRoot;
use pocketmine\network\mcpe\compression\CompressBatchPromise;
use pocketmine\network\mcpe\compression\CompressBatchTask;
use pocketmine\network\mcpe\compression\Compressor;
use pocketmine\network\mcpe\compression\ZlibCompressor;
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
use pocketmine\network\mcpe\encryption\EncryptionContext;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\PacketBroadcaster;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\raklib\RakLibInterface;
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
use pocketmine\network\mcpe\StandardPacketBroadcaster;
use pocketmine\network\Network;
use pocketmine\network\NetworkInterfaceStartException;
use pocketmine\network\query\DedicatedQueryNetworkInterface;
@ -72,9 +74,13 @@ use pocketmine\network\query\QueryInfo;
use pocketmine\network\upnp\UPnPNetworkInterface;
use pocketmine\permission\BanList;
use pocketmine\permission\DefaultPermissions;
use pocketmine\player\DatFilePlayerDataProvider;
use pocketmine\player\GameMode;
use pocketmine\player\OfflinePlayer;
use pocketmine\player\Player;
use pocketmine\player\PlayerDataLoadException;
use pocketmine\player\PlayerDataProvider;
use pocketmine\player\PlayerDataSaveException;
use pocketmine\player\PlayerInfo;
use pocketmine\plugin\PharPluginLoader;
use pocketmine\plugin\Plugin;
@ -105,17 +111,18 @@ use pocketmine\utils\SignalHandler;
use pocketmine\utils\Terminal;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\world\format\Chunk;
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\Position;
use pocketmine\world\World;
use pocketmine\world\WorldCreationOptions;
use pocketmine\world\WorldManager;
use Ramsey\Uuid\UuidInterface;
use Symfony\Component\Filesystem\Path;
use function array_fill;
use function array_sum;
use function base64_encode;
use function cli_set_process_title;
@ -124,7 +131,6 @@ use function count;
use function date;
use function fclose;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function filemtime;
use function fopen;
@ -161,12 +167,9 @@ use function time;
use function touch;
use function trim;
use function yaml_parse;
use function zlib_decode;
use function zlib_encode;
use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
use const PHP_INT_MAX;
use const ZLIB_ENCODING_GZIP;
/**
* The class that manages everything
@ -184,6 +187,29 @@ class Server{
public const DEFAULT_PORT_IPV6 = 19133;
public const DEFAULT_MAX_VIEW_DISTANCE = 16;
/**
* Worlds, network, commands and most other things are polled this many times per second on average.
* Between ticks, the server will sleep to ensure that the average tick rate is maintained.
* It may wake up between ticks if a Snooze notification source is triggered (e.g. to process network packets).
*/
public const TARGET_TICKS_PER_SECOND = 20;
/**
* The average time between ticks, in seconds.
*/
public const TARGET_SECONDS_PER_TICK = 1 / self::TARGET_TICKS_PER_SECOND;
public const TARGET_NANOSECONDS_PER_TICK = 1_000_000_000 / self::TARGET_TICKS_PER_SECOND;
/**
* The TPS threshold below which the server will generate log warnings.
*/
private const TPS_OVERLOAD_WARNING_THRESHOLD = self::TARGET_TICKS_PER_SECOND * 0.6;
private const TICKS_PER_WORLD_CACHE_CLEAR = 5 * self::TARGET_TICKS_PER_SECOND;
private const TICKS_PER_TPS_OVERLOAD_WARNING = 5 * self::TARGET_TICKS_PER_SECOND;
private const TICKS_PER_STATS_REPORT = 300 * self::TARGET_TICKS_PER_SECOND;
private const DEFAULT_ASYNC_COMPRESSION_THRESHOLD = 10_000;
private static ?Server $instance = null;
private TimeTrackingSleeperHandler $tickSleeper;
@ -202,7 +228,7 @@ class Server{
private PluginManager $pluginManager;
private float $profilingTickRate = 20;
private float $profilingTickRate = self::TARGET_TICKS_PER_SECOND;
private UpdateChecker $updater;
@ -212,10 +238,10 @@ class Server{
private int $tickCounter = 0;
private float $nextTick = 0;
/** @var float[] */
private array $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
private array $tickAverage;
/** @var float[] */
private array $useAverage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
private float $currentTPS = 20;
private array $useAverage;
private float $currentTPS = self::TARGET_TICKS_PER_SECOND;
private float $currentUse = 0;
private float $startTime;
@ -242,6 +268,7 @@ class Server{
private Network $network;
private bool $networkCompressionAsync = true;
private int $networkCompressionAsyncThreshold = self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD;
private Language $language;
private bool $forceLanguage = false;
@ -251,6 +278,8 @@ class Server{
private string $dataPath;
private string $pluginPath;
private PlayerDataProvider $playerDataProvider;
/**
* @var string[]
* @phpstan-var array<string, string>
@ -484,49 +513,22 @@ class Server{
return $result;
}
private function getPlayerDataPath(string $username) : string{
return Path::join($this->getDataPath(), 'players', strtolower($username) . '.dat');
}
/**
* Returns whether the server has stored any saved data for this player.
*/
public function hasOfflinePlayerData(string $name) : bool{
return file_exists($this->getPlayerDataPath($name));
}
private function handleCorruptedPlayerData(string $name) : void{
$path = $this->getPlayerDataPath($name);
rename($path, $path . '.bak');
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
return $this->playerDataProvider->hasData($name);
}
public function getOfflinePlayerData(string $name) : ?CompoundTag{
return Timings::$syncPlayerDataLoad->time(function() use ($name) : ?CompoundTag{
$name = strtolower($name);
$path = $this->getPlayerDataPath($name);
if(file_exists($path)){
$contents = @file_get_contents($path);
if($contents === false){
throw new \RuntimeException("Failed to read player data file \"$path\" (permission denied?)");
}
$decompressed = @zlib_decode($contents);
if($decompressed === false){
$this->logger->debug("Failed to decompress raw player data for \"$name\"");
$this->handleCorruptedPlayerData($name);
return null;
}
try{
return (new BigEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag();
}catch(NbtDataException $e){ //corrupt data
$this->logger->debug("Failed to decode NBT data for \"$name\": " . $e->getMessage());
$this->handleCorruptedPlayerData($name);
return null;
}
try{
return $this->playerDataProvider->loadData($name);
}catch(PlayerDataLoadException $e){
$this->logger->debug("Failed to load player data for $name: " . $e->getMessage());
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
return null;
}
return null;
});
}
@ -540,11 +542,9 @@ class Server{
if(!$ev->isCancelled()){
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
$nbt = new BigEndianNbtSerializer();
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
try{
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
}catch(\RuntimeException $e){
$this->playerDataProvider->saveData($name, $ev->getSaveData());
}catch(PlayerDataSaveException $e){
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
$this->logger->logException($e);
}
@ -560,51 +560,47 @@ class Server{
$ev->call();
$class = $ev->getPlayerClass();
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
$spawn = $playerPos->asVector3();
}else{
$world = $this->worldManager->getDefaultWorld();
if($world === null){
throw new AssumptionFailedError("Default world should always be loaded");
}
$playerPos = null;
$spawn = $world->getSpawnLocation();
}
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
$playerPromiseResolver = new PromiseResolver();
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
function() use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
if(!$session->isConnected()){
$playerPromiseResolver->reject();
return;
}
/* Stick with the original spawn at the time of generation request, even if it changed since then.
* This is because we know for sure that that chunk will be generated, but the one at the new location
* might not be, and it would be much more complex to go back and redo the whole thing.
*
* TODO: this relies on the assumption that getSafeSpawn() will only alter the Y coordinate of the
* provided position. If this assumption is broken, we'll start seeing crashes in here.
*/
/**
* @see Player::__construct()
* @var Player $player
*/
$player = new $class($this, $session, $playerInfo, $authenticated, $playerPos ?? Location::fromObject($world->getSafeSpawn($spawn), $world), $offlinePlayerData);
if(!$player->hasPlayedBefore()){
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
}
$playerPromiseResolver->resolve($player);
},
static function() use ($playerPromiseResolver, $session) : void{
if($session->isConnected()){
$session->disconnect("Spawn terrain generation failed");
}
$playerPromiseResolver->reject();
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
if(!$player->hasPlayedBefore()){
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
}
);
$playerPromiseResolver->resolve($player);
};
if($playerPos === null){ //new player or no valid position due to world not being loaded
$world->requestSafeSpawn()->onCompletion(
function(Position $spawn) use ($createPlayer, $playerPromiseResolver, $session, $world) : void{
if(!$session->isConnected()){
$playerPromiseResolver->reject();
return;
}
$createPlayer(Location::fromObject($spawn, $world));
},
function() use ($playerPromiseResolver, $session) : void{
if($session->isConnected()){
//TODO: this needs to be localized - this might be reached if the spawn world was unloaded while the player was logging in
$session->disconnect("Failed to find a safe spawn location");
}
$playerPromiseResolver->reject();
}
);
}else{ //returning player with a valid position - safe spawn not required
$createPlayer($playerPos);
}
return $playerPromiseResolver->getPromise();
}
@ -780,6 +776,8 @@ class Server{
}
self::$instance = $this;
$this->startTime = microtime(true);
$this->tickAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, self::TARGET_TICKS_PER_SECOND);
$this->useAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, 0);
Timings::init();
$this->tickSleeper = new TimeTrackingSleeperHandler(Timings::$serverInterrupts);
@ -807,7 +805,7 @@ class Server{
$this->logger->info("Loading server configuration");
$pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml");
if(!file_exists($pocketmineYmlPath)){
$content = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")), "Missing required resource file");
$content = Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml"));
if(VersionInfo::IS_DEVELOPMENT_BUILD){
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
}
@ -901,6 +899,9 @@ class Server{
if($this->configGroup->getPropertyInt("network.batch-threshold", 256) >= 0){
$netCompressionThreshold = $this->configGroup->getPropertyInt("network.batch-threshold", 256);
}
if($netCompressionThreshold < 0){
$netCompressionThreshold = null;
}
$netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6);
if($netCompressionLevel < 1 || $netCompressionLevel > 9){
@ -910,6 +911,10 @@ class Server{
ZlibCompressor::setInstance(new ZlibCompressor($netCompressionLevel, $netCompressionThreshold, ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE));
$this->networkCompressionAsync = $this->configGroup->getPropertyBool("network.async-compression", true);
$this->networkCompressionAsyncThreshold = max(
$this->configGroup->getPropertyInt("network.async-compression-threshold", self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD),
$netCompressionThreshold ?? self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD
);
EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool("network.enable-encryption", true);
@ -963,13 +968,13 @@ class Server{
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false));
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", 20);
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND);
DefaultPermissions::registerCorePermissions();
$this->commandMap = new SimpleCommandMap($this);
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes.json"));
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\RESOURCE_PATH, "legacy_recipes.json"));
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
@ -979,7 +984,7 @@ class Server{
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
}
try{
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(file_get_contents($graylistFile)));
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile)));
}catch(\InvalidArgumentException $e){
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
$this->forceShutdownExit();
@ -1001,12 +1006,14 @@ class Server{
$this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager);
$this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave()));
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", 6000));
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval()));
$this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io"));
$this->queryInfo = new QueryInfo($this);
$this->playerDataProvider = new DatFilePlayerDataProvider(Path::join($this->dataPath, "players"));
register_shutdown_function([$this, "crashDump"]);
$loadErrorCount = 0;
@ -1039,7 +1046,7 @@ class Server{
}
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
$this->sendUsageTicker = 6000;
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
$this->sendUsage(SendUsageTask::TYPE_OPEN);
}
@ -1174,10 +1181,18 @@ class Server{
return !$anyWorldFailedToLoad;
}
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
private function startupPrepareConnectableNetworkInterfaces(
string $ip,
int $port,
bool $ipV6,
bool $useQuery,
PacketBroadcaster $packetBroadcaster,
EntityEventBroadcaster $entityEventBroadcaster,
PacketSerializerContext $packetSerializerContext
) : bool{
$prettyIp = $ipV6 ? "[$ip]" : $ip;
try{
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6));
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext));
}catch(NetworkInterfaceStartException $e){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
$ip,
@ -1203,11 +1218,15 @@ class Server{
private function startupPrepareNetworkInterfaces() : bool{
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
$packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
$packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext);
$entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster);
if(
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery) ||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext) ||
(
$this->configGroup->getConfigBool("enable-ipv6", true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery)
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext)
)
){
return false;
@ -1339,46 +1358,10 @@ class Server{
/**
* @param Player[] $players
* @param ClientboundPacket[] $packets
* @deprecated
*/
public function broadcastPackets(array $players, array $packets) : bool{
if(count($packets) === 0){
throw new \InvalidArgumentException("Cannot broadcast empty list of packets");
}
return Timings::$broadcastPackets->time(function() use ($players, $packets) : bool{
/** @var NetworkSession[] $recipients */
$recipients = [];
foreach($players as $player){
if($player->isConnected()){
$recipients[] = $player->getNetworkSession();
}
}
if(count($recipients) === 0){
return false;
}
$ev = new DataPacketSendEvent($recipients, $packets);
$ev->call();
if($ev->isCancelled()){
return false;
}
$recipients = $ev->getTargets();
/** @var PacketBroadcaster[] $broadcasters */
$broadcasters = [];
/** @var NetworkSession[][] $broadcasterTargets */
$broadcasterTargets = [];
foreach($recipients as $recipient){
$broadcaster = $recipient->getBroadcaster();
$broadcasters[spl_object_id($broadcaster)] = $broadcaster;
$broadcasterTargets[spl_object_id($broadcaster)][] = $recipient;
}
foreach($broadcasters as $broadcaster){
$broadcaster->broadcastPackets($broadcasterTargets[spl_object_id($broadcaster)], $packets);
}
return true;
});
return NetworkBroadcastUtils::broadcastPackets($players, $packets);
}
/**
@ -1386,18 +1369,20 @@ class Server{
*
* @param bool|null $sync Compression on the main thread (true) or workers (false). Default is automatic (null).
*/
public function prepareBatch(PacketBatch $stream, Compressor $compressor, ?bool $sync = null) : CompressBatchPromise{
public function prepareBatch(PacketBatch $stream, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise{
$timings ??= Timings::$playerNetworkSendCompress;
try{
Timings::$playerNetworkSendCompress->startTiming();
$timings->startTiming();
$buffer = $stream->getBuffer();
if($sync === null){
$sync = !($this->networkCompressionAsync && $compressor->willCompress($buffer));
$threshold = $compressor->getCompressionThreshold();
$sync = !$this->networkCompressionAsync || $threshold === null || strlen($stream->getBuffer()) < $threshold;
}
$promise = new CompressBatchPromise();
if(!$sync){
if(!$sync && strlen($buffer) >= $this->networkCompressionAsyncThreshold){
$task = new CompressBatchTask($buffer, $promise, $compressor);
$this->asyncPool->submitTask($task);
}else{
@ -1406,7 +1391,7 @@ class Server{
return $promise;
}finally{
Timings::$playerNetworkSendCompress->stopTiming();
$timings->stopTiming();
}
}
@ -1821,11 +1806,11 @@ class Server{
$this->network->tick();
Timings::$connection->stopTiming();
if(($this->tickCounter % 20) === 0){
if(($this->tickCounter % self::TARGET_TICKS_PER_SECOND) === 0){
if($this->doTitleTick){
$this->titleTick();
}
$this->currentTPS = 20;
$this->currentTPS = self::TARGET_TICKS_PER_SECOND;
$this->currentUse = 0;
$queryRegenerateEvent = new QueryRegenerateEvent(new QueryInfo($this));
@ -1837,18 +1822,18 @@ class Server{
}
if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){
$this->sendUsageTicker = 6000;
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
$this->sendUsage(SendUsageTask::TYPE_STATUS);
}
if(($this->tickCounter % 100) === 0){
if(($this->tickCounter % self::TICKS_PER_WORLD_CACHE_CLEAR) === 0){
foreach($this->worldManager->getWorlds() as $world){
$world->clearCache();
}
}
if($this->getTicksPerSecondAverage() < 12){
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
}
if(($this->tickCounter % self::TICKS_PER_TPS_OVERLOAD_WARNING) === 0 && $this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
}
$this->getMemoryManager()->check();
@ -1866,12 +1851,12 @@ class Server{
$now = microtime(true);
$totalTickTimeSeconds = $now - $tickTime + ($this->tickSleeper->getNotificationProcessingTime() / 1_000_000_000);
$this->currentTPS = min(20, 1 / max(0.001, $totalTickTimeSeconds));
$this->currentUse = min(1, $totalTickTimeSeconds / 0.05);
$this->currentTPS = min(self::TARGET_TICKS_PER_SECOND, 1 / max(0.001, $totalTickTimeSeconds));
$this->currentUse = min(1, $totalTickTimeSeconds / self::TARGET_SECONDS_PER_TICK);
TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate);
$idx = $this->tickCounter % 20;
$idx = $this->tickCounter % self::TARGET_TICKS_PER_SECOND;
$this->tickAverage[$idx] = $this->currentTPS;
$this->useAverage[$idx] = $this->currentUse;
$this->tickSleeper->resetNotificationProcessingTime();
@ -1879,7 +1864,7 @@ class Server{
if(($this->nextTick - $tickTime) < -1){
$this->nextTick = $tickTime;
}else{
$this->nextTick += 0.05;
$this->nextTick += self::TARGET_SECONDS_PER_TICK;
}
}
}

View File

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

View File

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

View File

@ -64,7 +64,7 @@ class Farmland extends Transparent{
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()]; //TODO: this should be trimmed at the top by 1/16, but MCPE currently treats them as a full block (https://bugs.mojang.com/browse/MCPE-12109)
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
}
public function onNearbyBlockChange() : void{

View File

@ -33,7 +33,7 @@ class GrassPath extends Transparent{
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()]; //TODO: this should be trimmed at the top by 1/16, but MCPE currently treats them as a full block (https://bugs.mojang.com/browse/MCPE-12109)
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
}
public function onNearbyBlockChange() : void{

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Jukebox as JukeboxTile;
use pocketmine\item\Item;
use pocketmine\item\Record;
use pocketmine\lang\KnownTranslationKeys;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\RecordSound;
@ -44,7 +45,7 @@ class Jukebox extends Opaque{
if($this->record !== null){
$this->ejectRecord();
}elseif($item instanceof Record){
$player->sendJukeboxPopup("record.nowPlaying", [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]);
$player->sendJukeboxPopup(KnownTranslationKeys::RECORD_NOWPLAYING, [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]);
$this->insertRecord($item->pop());
}
}

View File

@ -34,7 +34,7 @@ class RedMushroomBlock extends Opaque{
protected MushroomBlockType $mushroomBlockType;
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
$this->mushroomBlockType = MushroomBlockType::PORES();
$this->mushroomBlockType = MushroomBlockType::ALL_CAP();
parent::__construct($idInfo, $name, $breakInfo);
}
@ -42,6 +42,11 @@ class RedMushroomBlock extends Opaque{
return MushroomBlockTypeIdMap::getInstance()->toId($this->mushroomBlockType);
}
protected function writeStateToItemMeta() : int{
//these blocks always drop as all-cap, but may exist in other forms in the inventory (particularly creative)
return BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_CAP;
}
public function readStateFromData(int $id, int $stateMeta) : void{
$type = MushroomBlockTypeIdMap::getInstance()->fromId($stateMeta);
if($type === null){

View File

@ -85,6 +85,20 @@ class DoubleChestInventory extends BaseInventory implements BlockInventory, Inve
$this->right->setContents($rightContents);
}
protected function getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int{
$leftSize = $this->left->getSize();
return $slot < $leftSize ?
$this->left->getMatchingItemCount($slot, $test, $checkDamage, $checkTags) :
$this->right->getMatchingItemCount($slot - $leftSize, $test, $checkDamage, $checkTags);
}
public function isSlotEmpty(int $index) : bool{
$leftSize = $this->left->getSize();
return $index < $leftSize ?
$this->left->isSlotEmpty($index) :
$this->right->isSlotEmpty($index - $leftSize);
}
protected function getOpenSound() : Sound{ return new ChestOpenSound(); }
protected function getCloseSound() : Sound{ return new ChestCloseSound(); }

View File

@ -52,7 +52,7 @@ trait ContainerTrait{
$newContents = [];
/** @var CompoundTag $itemNBT */
foreach($inventoryTag as $itemNBT){
$newContents[$itemNBT->getByte("Slot")] = Item::nbtDeserialize($itemNBT);
$newContents[$itemNBT->getByte(Item::TAG_SLOT)] = Item::nbtDeserialize($itemNBT);
}
$inventory->setContents($newContents);

View File

@ -26,6 +26,7 @@ namespace pocketmine\block\tile;
use pocketmine\item\Item;
use pocketmine\item\Record;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\sound\RecordStopSound;
class Jukebox extends Spawnable{
private const TAG_RECORD = "RecordItem"; //Item CompoundTag
@ -61,4 +62,8 @@ class Jukebox extends Spawnable{
$nbt->setTag(self::TAG_RECORD, $this->record->nbtSerialize());
}
}
protected function onBlockDestroyedHook() : void{
$this->position->getWorld()->addSound($this->position, new RecordStopSound());
}
}

View File

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

View File

@ -30,7 +30,7 @@ use function array_slice;
use function count;
use function explode;
use function is_int;
use function strpos;
use function str_contains;
class SignText{
public const LINE_COUNT = 4;
@ -54,7 +54,7 @@ class SignText{
foreach($lines as $k => $line){
$this->checkLineIndex($k);
Utils::checkUTF8($line);
if(strpos($line, "\n") !== false){
if(str_contains($line, "\n")){
throw new \InvalidArgumentException("Line must not contain newlines");
}
//TODO: add length checks

View File

@ -139,7 +139,7 @@ abstract class Command{
public function setLabel(string $name) : bool{
$this->nextLabel = $name;
if(!$this->isRegistered()){
$this->timings = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Command: " . $name);
$this->timings = new TimingsHandler("Command: " . $name, group: Timings::GROUP_BREAKDOWN);
$this->label = $name;
return true;

View File

@ -72,8 +72,8 @@ use pocketmine\utils\TextFormat;
use function array_shift;
use function count;
use function implode;
use function str_contains;
use function strcasecmp;
use function strpos;
use function strtolower;
use function trim;
@ -238,7 +238,7 @@ class SimpleCommandMap implements CommandMap{
$values = $this->server->getCommandAliases();
foreach($values as $alias => $commandStrings){
if(strpos($alias, ":") !== false){
if(str_contains($alias, ":")){
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
continue;
}

View File

@ -33,7 +33,6 @@ use pocketmine\item\LegacyStringToItemParserException;
use pocketmine\item\StringToItemParser;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
@ -59,23 +58,9 @@ class ClearCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
if(isset($args[0])){
$target = $sender->getServer()->getPlayerByPrefix($args[0]);
if($target === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}
if($target !== $sender && !$this->testPermission($sender, DefaultPermissionNames::COMMAND_CLEAR_OTHER)){
return true;
}
}elseif($sender instanceof Player){
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_CLEAR_SELF)){
return true;
}
$target = $sender;
}else{
throw new InvalidCommandSyntaxException();
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
if($target === null){
return true;
}
$targetItem = null;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use Symfony\Component\Filesystem\Path;
use function date;
@ -33,7 +34,7 @@ class DumpMemoryCommand extends VanillaCommand{
public function __construct(string $name){
parent::__construct(
$name,
"Dumps the memory",
KnownTranslationFactory::pocketmine_command_dumpmemory_description(),
"/$name [path]"
);
$this->setPermission(DefaultPermissionNames::COMMAND_DUMPMEMORY);

View File

@ -32,6 +32,7 @@ use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\Limits;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
use function strtolower;
class EffectCommand extends VanillaCommand{
@ -42,7 +43,10 @@ class EffectCommand extends VanillaCommand{
KnownTranslationFactory::pocketmine_command_effect_description(),
KnownTranslationFactory::commands_effect_usage()
);
$this->setPermission(DefaultPermissionNames::COMMAND_EFFECT);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_EFFECT_SELF,
DefaultPermissionNames::COMMAND_EFFECT_OTHER
]));
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -54,10 +58,8 @@ class EffectCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}
$effectManager = $player->getEffects();

View File

@ -29,8 +29,8 @@ use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\StringToEnchantmentParser;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
class EnchantCommand extends VanillaCommand{
@ -40,7 +40,10 @@ class EnchantCommand extends VanillaCommand{
KnownTranslationFactory::pocketmine_command_enchant_description(),
KnownTranslationFactory::commands_enchant_usage()
);
$this->setPermission(DefaultPermissionNames::COMMAND_ENCHANT);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_ENCHANT_SELF,
DefaultPermissionNames::COMMAND_ENCHANT_OTHER
]));
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -52,10 +55,8 @@ class EnchantCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}

View File

@ -29,9 +29,8 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\GameMode;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
class GamemodeCommand extends VanillaCommand{
@ -41,7 +40,10 @@ class GamemodeCommand extends VanillaCommand{
KnownTranslationFactory::pocketmine_command_gamemode_description(),
KnownTranslationFactory::commands_gamemode_usage()
);
$this->setPermission(DefaultPermissionNames::COMMAND_GAMEMODE);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_GAMEMODE_SELF,
DefaultPermissionNames::COMMAND_GAMEMODE_OTHER
]));
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -59,17 +61,9 @@ class GamemodeCommand extends VanillaCommand{
return true;
}
if(isset($args[1])){
$target = $sender->getServer()->getPlayerByPrefix($args[1]);
if($target === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}
}elseif($sender instanceof Player){
$target = $sender;
}else{
throw new InvalidCommandSyntaxException();
$target = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
if($target === null){
return true;
}
if($target->getGamemode()->equals($gameMode)){

View File

@ -47,7 +47,10 @@ class GiveCommand extends VanillaCommand{
KnownTranslationFactory::pocketmine_command_give_description(),
KnownTranslationFactory::pocketmine_command_give_usage()
);
$this->setPermission(DefaultPermissionNames::COMMAND_GIVE);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_GIVE_SELF,
DefaultPermissionNames::COMMAND_GIVE_OTHER
]));
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -59,9 +62,8 @@ class GiveCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}

View File

@ -29,8 +29,6 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
@ -55,32 +53,16 @@ class KillCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
if(count($args) === 1){
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_KILL_OTHER)){
return true;
}
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
if($player instanceof Player){
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
}else{
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
}
$player = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
if($player === null){
return true;
}
if($sender instanceof Player){
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_KILL_SELF)){
return true;
}
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
if($player === $sender){
$sender->sendMessage(KnownTranslationFactory::commands_kill_successful($sender->getName()));
}else{
throw new InvalidCommandSyntaxException();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
}
return true;

View File

@ -70,7 +70,7 @@ use function explode;
use function max;
use function microtime;
use function mt_rand;
use function strpos;
use function str_starts_with;
use function strtolower;
class ParticleCommand extends VanillaCommand{
@ -208,17 +208,17 @@ class ParticleCommand extends VanillaCommand{
return new EntityFlameParticle();
}
if(strpos($name, "iconcrack_") === 0){
if(str_starts_with($name, "iconcrack_")){
$d = explode("_", $name);
if(count($d) === 3){
return new ItemBreakParticle(ItemFactory::getInstance()->get((int) $d[1], (int) $d[2]));
}
}elseif(strpos($name, "blockcrack_") === 0){
}elseif(str_starts_with($name, "blockcrack_")){
$d = explode("_", $name);
if(count($d) === 2){
return new TerrainParticle(BlockFactory::getInstance()->get(((int) $d[1]) & 0xff, ((int) $d[1]) >> 12));
}
}elseif(strpos($name, "blockdust_") === 0){
}elseif(str_starts_with($name, "blockdust_")){
$d = explode("_", $name);
if(count($d) >= 4){
return new DustParticle(new Color(((int) $d[1]) & 0xff, ((int) $d[2]) & 0xff, ((int) $d[3]) & 0xff, isset($d[4]) ? ((int) $d[4]) & 0xff : 255));

View File

@ -29,10 +29,10 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use pocketmine\world\Position;
use pocketmine\world\World;
use function count;
use function implode;
use function round;
class SpawnpointCommand extends VanillaCommand{
@ -43,7 +43,10 @@ class SpawnpointCommand extends VanillaCommand{
KnownTranslationFactory::pocketmine_command_spawnpoint_description(),
KnownTranslationFactory::commands_spawnpoint_usage()
);
$this->setPermission(DefaultPermissionNames::COMMAND_SPAWNPOINT);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF,
DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER
]));
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -51,23 +54,9 @@ class SpawnpointCommand extends VanillaCommand{
return true;
}
$target = null;
if(count($args) === 0){
if($sender instanceof Player){
$target = $sender;
}else{
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
return true;
}
}else{
$target = $sender->getServer()->getPlayerByPrefix($args[0]);
if($target === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF, DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER);
if($target === null){
return true;
}
if(count($args) === 4){
@ -81,19 +70,13 @@ class SpawnpointCommand extends VanillaCommand{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($x, 2), (string) round($y, 2), (string) round($z, 2)));
return true;
}elseif(count($args) <= 1){
if($sender instanceof Player){
$cpos = $sender->getPosition();
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
$target->setSpawn($pos);
}elseif(count($args) <= 1 && $sender instanceof Player){
$cpos = $sender->getPosition();
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
$target->setSpawn($pos);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
return true;
}else{
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
return true;
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
return true;
}
throw new InvalidCommandSyntaxException();

View File

@ -110,7 +110,8 @@ class StatusCommand extends VanillaCommand{
$worldName = $world->getFolderName() !== $world->getDisplayName() ? " (" . $world->getDisplayName() . ")" : "";
$timeColor = $world->getTickRateTime() > 40 ? TextFormat::RED : TextFormat::YELLOW;
$sender->sendMessage(TextFormat::GOLD . "World \"{$world->getFolderName()}\"$worldName: " .
TextFormat::RED . number_format(count($world->getLoadedChunks())) . TextFormat::GREEN . " chunks, " .
TextFormat::RED . number_format(count($world->getLoadedChunks())) . TextFormat::GREEN . " loaded chunks, " .
TextFormat::RED . number_format(count($world->getTickingChunks())) . TextFormat::GREEN . " ticking chunks, " .
TextFormat::RED . number_format(count($world->getEntities())) . TextFormat::GREEN . " entities. " .
"Time $timeColor" . round($world->getTickRateTime(), 2) . "ms"
);

View File

@ -35,6 +35,7 @@ use pocketmine\utils\TextFormat;
use pocketmine\world\World;
use function array_shift;
use function count;
use function implode;
use function round;
class TeleportCommand extends VanillaCommand{
@ -46,7 +47,10 @@ class TeleportCommand extends VanillaCommand{
KnownTranslationFactory::commands_tp_usage(),
["teleport"]
);
$this->setPermission(DefaultPermissionNames::COMMAND_TELEPORT);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_TELEPORT_SELF,
DefaultPermissionNames::COMMAND_TELEPORT_OTHER
]));
}
private function findPlayer(CommandSender $sender, string $playerName) : ?Player{
@ -67,31 +71,25 @@ class TeleportCommand extends VanillaCommand{
case 1: // /tp targetPlayer
case 3: // /tp x y z
case 5: // /tp x y z yaw pitch - TODO: 5 args could be target x y z yaw :(
if(!($sender instanceof Player)){
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
return true;
}
$subject = $sender;
$targetArgs = $args;
$subjectName = null; //self
break;
case 2: // /tp player1 player2
case 4: // /tp player1 x y z - TODO: 4 args could be x y z yaw :(
case 6: // /tp player1 x y z yaw pitch
$subject = $this->findPlayer($sender, $args[0]);
if($subject === null){
return true;
}
$targetArgs = $args;
array_shift($targetArgs);
$subjectName = array_shift($args);
break;
default:
throw new InvalidCommandSyntaxException();
}
switch(count($targetArgs)){
$subject = $this->fetchPermittedPlayerTarget($sender, $subjectName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
if($subject === null){
return true;
}
switch(count($args)){
case 1:
$targetPlayer = $this->findPlayer($sender, $targetArgs[0]);
$targetPlayer = $this->findPlayer($sender, $args[0]);
if($targetPlayer === null){
return true;
}
@ -103,17 +101,17 @@ class TeleportCommand extends VanillaCommand{
case 3:
case 5:
$base = $subject->getLocation();
if(count($targetArgs) === 5){
$yaw = (float) $targetArgs[3];
$pitch = (float) $targetArgs[4];
if(count($args) === 5){
$yaw = (float) $args[3];
$pitch = (float) $args[4];
}else{
$yaw = $base->yaw;
$pitch = $base->pitch;
}
$x = $this->getRelativeDouble($base->x, $sender, $targetArgs[0]);
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], World::Y_MIN, World::Y_MAX);
$z = $this->getRelativeDouble($base->z, $sender, $targetArgs[2]);
$x = $this->getRelativeDouble($base->x, $sender, $args[0]);
$y = $this->getRelativeDouble($base->y, $sender, $args[1], World::Y_MIN, World::Y_MAX);
$z = $this->getRelativeDouble($base->z, $sender, $args[2]);
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
$subject->teleport($targetLocation);

View File

@ -167,6 +167,7 @@ class TimingsCommand extends VanillaCommand{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
"https://" . $host . "/?id=" . $response["id"]));
}else{
$sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
}
}

View File

@ -27,7 +27,6 @@ use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\TextFormat;
use function array_slice;
use function count;
use function implode;
@ -40,7 +39,10 @@ class TitleCommand extends VanillaCommand{
KnownTranslationFactory::pocketmine_command_title_description(),
KnownTranslationFactory::commands_title_usage()
);
$this->setPermission(DefaultPermissionNames::COMMAND_TITLE);
$this->setPermission(implode(";", [
DefaultPermissionNames::COMMAND_TITLE_SELF,
DefaultPermissionNames::COMMAND_TITLE_OTHER
]));
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -52,9 +54,8 @@ class TitleCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_TITLE_SELF, DefaultPermissionNames::COMMAND_TITLE_OTHER);
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return true;
}

View File

@ -27,6 +27,7 @@ use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use function is_numeric;
use function substr;
@ -35,6 +36,28 @@ abstract class VanillaCommand extends Command{
public const MAX_COORD = 30000000;
public const MIN_COORD = -30000000;
protected function fetchPermittedPlayerTarget(CommandSender $sender, ?string $target, string $selfPermission, string $otherPermission) : ?Player{
if($target !== null){
$player = $sender->getServer()->getPlayerByPrefix($target);
}elseif($sender instanceof Player){
$player = $sender;
}else{
throw new InvalidCommandSyntaxException();
}
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return null;
}
if(
($player === $sender && $this->testPermission($sender, $selfPermission)) ||
($player !== $sender && $this->testPermission($sender, $otherPermission))
){
return $player;
}
return null;
}
protected function getInteger(CommandSender $sender, string $value, int $min = self::MIN_COORD, int $max = self::MAX_COORD) : int{
$i = (int) $value;

View File

@ -45,11 +45,17 @@ class CraftingManager{
*/
protected $shapelessRecipes = [];
/**
* @var CraftingRecipe[]
* @phpstan-var array<int, CraftingRecipe>
*/
private array $craftingRecipeIndex = [];
/**
* @var FurnaceRecipeManager[]
* @phpstan-var array<int, FurnaceRecipeManager>
*/
protected $furnaceRecipeManagers;
protected $furnaceRecipeManagers = [];
/**
* @var PotionTypeRecipe[][]
@ -153,6 +159,18 @@ class CraftingManager{
return $this->shapedRecipes;
}
/**
* @return CraftingRecipe[]
* @phpstan-return array<int, CraftingRecipe>
*/
public function getCraftingRecipeIndex() : array{
return $this->craftingRecipeIndex;
}
public function getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe{
return $this->craftingRecipeIndex[$index] ?? null;
}
public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
return $this->furnaceRecipeManagers[$furnaceType->id()];
}
@ -175,6 +193,7 @@ class CraftingManager{
public function registerShapedRecipe(ShapedRecipe $recipe) : void{
$this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
$this->craftingRecipeIndex[] = $recipe;
foreach($this->recipeRegisteredCallbacks as $callback){
$callback();
@ -183,6 +202,7 @@ class CraftingManager{
public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
$this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
$this->craftingRecipeIndex[] = $recipe;
foreach($this->recipeRegisteredCallbacks as $callback){
$callback();

View File

@ -26,9 +26,8 @@ namespace pocketmine\crafting;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use pocketmine\utils\Filesystem;
use function array_map;
use function file_get_contents;
use function is_array;
use function json_decode;
@ -52,7 +51,7 @@ final class CraftingManagerFromDataHelper{
}
public static function make(string $filePath) : CraftingManager{
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true);
$recipes = json_decode(Filesystem::fileGetContents($filePath), true);
if(!is_array($recipes)){
throw new AssumptionFailedError("recipes.json root should contain a map of recipe types");
}

View File

@ -29,8 +29,8 @@ use pocketmine\utils\Utils;
use function array_values;
use function count;
use function implode;
use function str_contains;
use function strlen;
use function strpos;
class ShapedRecipe implements CraftingRecipe{
/** @var string[] */
@ -86,7 +86,7 @@ class ShapedRecipe implements CraftingRecipe{
$this->shape = $shape;
foreach($ingredients as $char => $i){
if(strpos(implode($this->shape), $char) === false){
if(!str_contains(implode($this->shape), $char)){
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
}

View File

@ -55,6 +55,7 @@ use function phpversion;
use function preg_replace;
use function sprintf;
use function str_split;
use function str_starts_with;
use function strpos;
use function substr;
use function zend_version;
@ -237,7 +238,7 @@ class CrashDump{
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
$frameCleanPath = Filesystem::cleanPath($filePath);
if(strpos($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX) !== 0){
if(!str_starts_with($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX)){
if($crashFrame){
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
}else{
@ -247,10 +248,9 @@ class CrashDump{
if(file_exists($filePath)){
$reflection = new \ReflectionClass(PluginBase::class);
$file = $reflection->getProperty("file");
$file->setAccessible(true);
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
$filePath = Filesystem::cleanPath($file->getValue($plugin));
if(strpos($frameCleanPath, $filePath) === 0){
if(str_starts_with($frameCleanPath, $filePath)){
$this->data->plugin = $plugin->getName();
break;
}

View File

@ -0,0 +1,50 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\data\bedrock;
use const pocketmine\BEDROCK_DATA_PATH;
final class BedrockDataFiles{
private function __construct(){
//NOOP
}
public const BANNER_PATTERNS_JSON = BEDROCK_DATA_PATH . '/banner_patterns.json';
public const BIOME_DEFINITIONS_NBT = BEDROCK_DATA_PATH . '/biome_definitions.nbt';
public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt';
public const BIOME_ID_MAP_JSON = BEDROCK_DATA_PATH . '/biome_id_map.json';
public const BLOCK_ID_TO_ITEM_ID_MAP_JSON = BEDROCK_DATA_PATH . '/block_id_to_item_id_map.json';
public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json';
public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt';
public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json';
public const CREATIVEITEMS_JSON = BEDROCK_DATA_PATH . '/creativeitems.json';
public const ENTITY_ID_MAP_JSON = BEDROCK_DATA_PATH . '/entity_id_map.json';
public const ENTITY_IDENTIFIERS_NBT = BEDROCK_DATA_PATH . '/entity_identifiers.nbt';
public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json';
public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json';
public const PARTICLE_ID_MAP_JSON = BEDROCK_DATA_PATH . '/particle_id_map.json';
public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin';
public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json';
public const REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json';
}

View File

@ -73,6 +73,8 @@ final class EnchantmentIdMap{
$this->register(EnchantmentIds::MENDING, VanillaEnchantments::MENDING());
$this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING());
$this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK());
}
public function register(int $mcpeId, Enchantment $enchantment) : void{

View File

@ -66,4 +66,5 @@ final class EnchantmentIds{
public const PIERCING = 34;
public const QUICK_CHARGE = 35;
public const SOUL_SPEED = 36;
public const SWIFT_SNEAK = 37;
}

View File

@ -24,12 +24,11 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock;
use pocketmine\utils\SingletonTrait;
use Symfony\Component\Filesystem\Path;
final class LegacyBiomeIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'biome_id_map.json'));
parent::__construct(BedrockDataFiles::BIOME_ID_MAP_JSON);
}
}

View File

@ -30,6 +30,6 @@ final class LegacyBlockIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'block_id_map.json'));
parent::__construct(Path::join(\pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH, 'block_legacy_id_map.json'));
}
}

View File

@ -24,12 +24,11 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock;
use pocketmine\utils\SingletonTrait;
use Symfony\Component\Filesystem\Path;
final class LegacyEntityIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'entity_id_map.json'));
parent::__construct(BedrockDataFiles::ENTITY_ID_MAP_JSON);
}
}

View File

@ -30,6 +30,6 @@ final class LegacyItemIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'item_id_map.json'));
parent::__construct(Path::join(\pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, 'item_legacy_id_map.json'));
}
}

View File

@ -24,8 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use function file_get_contents;
use pocketmine\utils\Filesystem;
use function is_array;
use function is_int;
use function is_string;
@ -45,7 +44,7 @@ abstract class LegacyToStringBidirectionalIdMap{
private array $stringToLegacy = [];
public function __construct(string $file){
$stringToLegacyId = json_decode(Utils::assumeNotFalse(file_get_contents($file), "Missing required resource file"), true);
$stringToLegacyId = json_decode(Filesystem::fileGetContents($file), true);
if(!is_array($stringToLegacyId)){
throw new AssumptionFailedError("Invalid format of ID map");
}

View File

@ -44,6 +44,8 @@ use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\AddActorPacket;
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
@ -81,6 +83,15 @@ abstract class Entity{
public const MOTION_THRESHOLD = 0.00001;
protected const STEP_CLIP_MULTIPLIER = 0.4;
private const TAG_FIRE = "Fire"; //TAG_Short
private const TAG_ON_GROUND = "OnGround"; //TAG_Byte
private const TAG_FALL_DISTANCE = "FallDistance"; //TAG_Float
private const TAG_CUSTOM_NAME = "CustomName"; //TAG_String
private const TAG_CUSTOM_NAME_VISIBLE = "CustomNameVisible"; //TAG_Byte
public const TAG_POS = "Pos"; //TAG_List<TAG_Double>|TAG_List<TAG_Float>
public const TAG_MOTION = "Motion"; //TAG_List<TAG_Double>|TAG_List<TAG_Float>
public const TAG_ROTATION = "Rotation"; //TAG_List<TAG_Float>
private static int $entityCount = 1;
/**
@ -104,6 +115,8 @@ abstract class Entity{
/** @var Block[]|null */
protected $blocksAround;
private bool $checkBlockIntersectionsNextTick = true;
/** @var Location */
protected $location;
/** @var Location */
@ -233,9 +246,9 @@ abstract class Entity{
$this->recalculateBoundingBox();
if($nbt !== null){
$this->motion = EntityDataHelper::parseVec3($nbt, "Motion", true);
$this->motion = EntityDataHelper::parseVec3($nbt, self::TAG_MOTION, true);
}else{
$this->motion = new Vector3(0, 0, 0);
$this->motion = Vector3::zero();
}
$this->resetLastMovements();
@ -466,17 +479,17 @@ abstract class Entity{
public function saveNBT() : CompoundTag{
$nbt = CompoundTag::create()
->setTag("Pos", new ListTag([
->setTag(self::TAG_POS, new ListTag([
new DoubleTag($this->location->x),
new DoubleTag($this->location->y),
new DoubleTag($this->location->z)
]))
->setTag("Motion", new ListTag([
->setTag(self::TAG_MOTION, new ListTag([
new DoubleTag($this->motion->x),
new DoubleTag($this->motion->y),
new DoubleTag($this->motion->z)
]))
->setTag("Rotation", new ListTag([
->setTag(self::TAG_ROTATION, new ListTag([
new FloatTag($this->location->yaw),
new FloatTag($this->location->pitch)
]));
@ -485,33 +498,33 @@ abstract class Entity{
EntityFactory::getInstance()->injectSaveId(get_class($this), $nbt);
if($this->getNameTag() !== ""){
$nbt->setString("CustomName", $this->getNameTag());
$nbt->setByte("CustomNameVisible", $this->isNameTagVisible() ? 1 : 0);
$nbt->setString(self::TAG_CUSTOM_NAME, $this->getNameTag());
$nbt->setByte(self::TAG_CUSTOM_NAME_VISIBLE, $this->isNameTagVisible() ? 1 : 0);
}
}
$nbt->setFloat("FallDistance", $this->fallDistance);
$nbt->setShort("Fire", $this->fireTicks);
$nbt->setByte("OnGround", $this->onGround ? 1 : 0);
$nbt->setFloat(self::TAG_FALL_DISTANCE, $this->fallDistance);
$nbt->setShort(self::TAG_FIRE, $this->fireTicks);
$nbt->setByte(self::TAG_ON_GROUND, $this->onGround ? 1 : 0);
return $nbt;
}
protected function initEntity(CompoundTag $nbt) : void{
$this->fireTicks = $nbt->getShort("Fire", 0);
$this->fireTicks = $nbt->getShort(self::TAG_FIRE, 0);
$this->onGround = $nbt->getByte("OnGround", 0) !== 0;
$this->onGround = $nbt->getByte(self::TAG_ON_GROUND, 0) !== 0;
$this->fallDistance = $nbt->getFloat("FallDistance", 0.0);
$this->fallDistance = $nbt->getFloat(self::TAG_FALL_DISTANCE, 0.0);
if(($customNameTag = $nbt->getTag("CustomName")) instanceof StringTag){
if(($customNameTag = $nbt->getTag(self::TAG_CUSTOM_NAME)) instanceof StringTag){
$this->setNameTag($customNameTag->getValue());
if(($customNameVisibleTag = $nbt->getTag("CustomNameVisible")) instanceof StringTag){
if(($customNameVisibleTag = $nbt->getTag(self::TAG_CUSTOM_NAME_VISIBLE)) instanceof StringTag){
//Older versions incorrectly saved this as a string (see 890f72dbf23a77f294169b79590770470041adc4)
$this->setNameTagVisible($customNameVisibleTag->getValue() !== "");
}else{
$this->setNameTagVisible($nbt->getByte("CustomNameVisible", 1) !== 0);
$this->setNameTagVisible($nbt->getByte(self::TAG_CUSTOM_NAME_VISIBLE, 1) !== 0);
}
}
}
@ -638,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);
@ -776,7 +792,7 @@ abstract class Entity{
$this->spawnTo($player);
}
}else{
$this->server->broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
$this->id,
$this->getOffsetPosition($this->location),
$this->location->pitch,
@ -791,7 +807,16 @@ abstract class Entity{
}
protected function broadcastMotion() : void{
$this->server->broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
}
public function getGravity() : float{
return $this->gravity;
}
public function setGravity(float $gravity) : void{
Utils::checkFloatNotInfOrNaN("gravity", $gravity);
$this->gravity = $gravity;
}
public function hasGravity() : bool{
@ -1119,6 +1144,7 @@ abstract class Entity{
$this->blocksAround = null;
Timings::$entityMove->startTiming();
Timings::$entityMoveCollision->startTiming();
$wantedX = $dx;
$wantedY = $dy;
@ -1203,6 +1229,7 @@ abstract class Entity{
$this->boundingBox = $moveBB;
}
Timings::$entityMoveCollision->stopTiming();
$this->location = new Location(
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
@ -1286,6 +1313,7 @@ abstract class Entity{
}
protected function checkBlockIntersections() : void{
$this->checkBlockIntersectionsNextTick = false;
$vectors = [];
foreach($this->getBlocksAroundWithEntityInsideActions() as $block){
@ -1297,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));
}
}
}
@ -1519,7 +1549,7 @@ abstract class Entity{
$id = spl_object_id($player);
if(isset($this->hasSpawned[$id])){
if($send){
$player->getNetworkSession()->onEntityRemoved($this);
$player->getNetworkSession()->getEntityEventBroadcaster()->onEntityRemoved([$player->getNetworkSession()], $this);
}
unset($this->hasSpawned[$id]);
}
@ -1530,9 +1560,11 @@ abstract class Entity{
* player moves, viewers will once again be able to see the entity.
*/
public function despawnFromAll() : void{
foreach($this->hasSpawned as $player){
$this->despawnFrom($player);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->hasSpawned,
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEntityRemoved($recipients, $this)
);
$this->hasSpawned = [];
}
/**
@ -1606,9 +1638,7 @@ abstract class Entity{
$targets = $targets ?? $this->hasSpawned;
$data = $data ?? $this->getAllNetworkData();
foreach($targets as $p){
$p->getNetworkSession()->syncActorData($this, $data);
}
NetworkBroadcastUtils::broadcastEntityEvent($targets, fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->syncActorData($recipients, $this, $data));
}
/**
@ -1662,7 +1692,7 @@ abstract class Entity{
* @param Player[]|null $targets
*/
public function broadcastAnimation(Animation $animation, ?array $targets = null) : void{
$this->server->broadcastPackets($targets ?? $this->getViewers(), $animation->encode());
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $animation->encode());
}
/**
@ -1671,7 +1701,7 @@ abstract class Entity{
*/
public function broadcastSound(Sound $sound, ?array $targets = null) : void{
if(!$this->silent){
$this->server->broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
}
}

View File

@ -57,19 +57,19 @@ final class EntityDataHelper{
* @throws SavedDataLoadingException
*/
public static function parseLocation(CompoundTag $nbt, World $world) : Location{
$pos = self::parseVec3($nbt, "Pos", false);
$pos = self::parseVec3($nbt, Entity::TAG_POS, false);
$yawPitch = $nbt->getTag("Rotation");
$yawPitch = $nbt->getTag(Entity::TAG_ROTATION);
if(!($yawPitch instanceof ListTag) || $yawPitch->getTagType() !== NBT::TAG_Float){
throw new SavedDataLoadingException("'Rotation' should be a List<Float>");
throw new SavedDataLoadingException("'" . Entity::TAG_ROTATION . "' should be a List<Float>");
}
/** @var FloatTag[] $values */
$values = $yawPitch->getValue();
if(count($values) !== 2){
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
}
self::validateFloat("Rotation", "yaw", $values[0]->getValue());
self::validateFloat("Rotation", "pitch", $values[1]->getValue());
self::validateFloat(Entity::TAG_ROTATION, "yaw", $values[0]->getValue());
self::validateFloat(Entity::TAG_ROTATION, "pitch", $values[1]->getValue());
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
}
@ -80,7 +80,7 @@ final class EntityDataHelper{
public static function parseVec3(CompoundTag $nbt, string $tagName, bool $optional) : Vector3{
$pos = $nbt->getTag($tagName);
if($pos === null && $optional){
return new Vector3(0, 0, 0);
return Vector3::zero();
}
if(!($pos instanceof ListTag) || ($pos->getTagType() !== NBT::TAG_Double && $pos->getTagType() !== NBT::TAG_Float)){
throw new SavedDataLoadingException("'$tagName' should be a List<Double> or List<Float>");

View File

@ -66,6 +66,9 @@ use function reset;
final class EntityFactory{
use SingletonTrait;
public const TAG_IDENTIFIER = "identifier"; //TAG_String
public const TAG_LEGACY_ID = "id"; //TAG_Int
/**
* @var \Closure[] save ID => creator function
* @phpstan-var array<int|string, \Closure(World, CompoundTag) : Entity>
@ -113,9 +116,9 @@ final class EntityFactory{
}, ['FallingSand', 'minecraft:falling_block'], LegacyIds::FALLING_BLOCK);
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
$itemTag = $nbt->getCompoundTag("Item");
$itemTag = $nbt->getCompoundTag(ItemEntity::TAG_ITEM);
if($itemTag === null){
throw new SavedDataLoadingException("Expected \"Item\" NBT tag not found");
throw new SavedDataLoadingException("Expected \"" . ItemEntity::TAG_ITEM . "\" NBT tag not found");
}
$item = Item::nbtDeserialize($itemTag);
@ -126,14 +129,14 @@ final class EntityFactory{
}, ['Item', 'minecraft:item'], LegacyIds::ITEM);
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
$motive = PaintingMotive::getMotiveByName($nbt->getString("Motive"));
$motive = PaintingMotive::getMotiveByName($nbt->getString(Painting::TAG_MOTIVE));
if($motive === null){
throw new SavedDataLoadingException("Unknown painting motive");
}
$blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ"));
if(($directionTag = $nbt->getTag("Direction")) instanceof ByteTag){
$blockIn = new Vector3($nbt->getInt(Painting::TAG_TILE_X), $nbt->getInt(Painting::TAG_TILE_Y), $nbt->getInt(Painting::TAG_TILE_Z));
if(($directionTag = $nbt->getTag(Painting::TAG_DIRECTION_BE)) instanceof ByteTag){
$facing = Painting::DATA_TO_FACING[$directionTag->getValue()] ?? Facing::NORTH;
}elseif(($facingTag = $nbt->getTag("Facing")) instanceof ByteTag){
}elseif(($facingTag = $nbt->getTag(Painting::TAG_FACING_JE)) instanceof ByteTag){
$facing = Painting::DATA_TO_FACING[$facingTag->getValue()] ?? Facing::NORTH;
}else{
throw new SavedDataLoadingException("Missing facing info");
@ -151,7 +154,7 @@ final class EntityFactory{
}, ['Snowball', 'minecraft:snowball'], LegacyIds::SNOWBALL);
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER));
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort(SplashPotion::TAG_POTION_ID, PotionTypeIds::WATER));
if($potionType === null){
throw new SavedDataLoadingException("No such potion type");
}
@ -217,7 +220,7 @@ final class EntityFactory{
*/
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
try{
$saveId = $nbt->getTag("identifier") ?? $nbt->getTag("id");
$saveId = $nbt->getTag(self::TAG_IDENTIFIER) ?? $nbt->getTag(self::TAG_LEGACY_ID);
$func = null;
if($saveId instanceof StringTag){
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
@ -238,7 +241,7 @@ final class EntityFactory{
public function injectSaveId(string $class, CompoundTag $saveData) : void{
if(isset($this->saveNames[$class])){
$saveData->setTag("identifier", new StringTag($this->saveNames[$class]));
$saveData->setTag(self::TAG_IDENTIFIER, new StringTag($this->saveNames[$class]));
}else{
throw new \InvalidArgumentException("Entity $class is not registered");
}

View File

@ -47,9 +47,13 @@ use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\AbilitiesData;
use pocketmine\network\mcpe\protocol\types\AbilitiesLayer;
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
@ -60,7 +64,6 @@ use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\UpdateAbilitiesPacketLayer;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\player\Player;
use pocketmine\utils\Limits;
@ -77,6 +80,25 @@ use function random_int;
class Human extends Living implements ProjectileSource, InventoryHolder{
private const TAG_INVENTORY = "Inventory"; //TAG_List<TAG_Compound>
private const TAG_OFF_HAND_ITEM = "OffHandItem"; //TAG_Compound
private const TAG_ENDER_CHEST_INVENTORY = "EnderChestInventory"; //TAG_List<TAG_Compound>
private const TAG_SELECTED_INVENTORY_SLOT = "SelectedInventorySlot"; //TAG_Int
private const TAG_FOOD_LEVEL = "foodLevel"; //TAG_Int
private const TAG_FOOD_EXHAUSTION_LEVEL = "foodExhaustionLevel"; //TAG_Float
private const TAG_FOOD_SATURATION_LEVEL = "foodSaturationLevel"; //TAG_Float
private const TAG_FOOD_TICK_TIMER = "foodTickTimer"; //TAG_Int
private const TAG_XP_LEVEL = "XpLevel"; //TAG_Int
private const TAG_XP_PROGRESS = "XpP"; //TAG_Float
private const TAG_LIFETIME_XP_TOTAL = "XpTotal"; //TAG_Int
private const TAG_XP_SEED = "XpSeed"; //TAG_Int
private const TAG_SKIN = "Skin"; //TAG_Compound
private const TAG_SKIN_NAME = "Name"; //TAG_String
private const TAG_SKIN_DATA = "Data"; //TAG_ByteArray
private const TAG_SKIN_CAPE_DATA = "CapeData"; //TAG_ByteArray
private const TAG_SKIN_GEOMETRY_NAME = "GeometryName"; //TAG_String
private const TAG_SKIN_GEOMETRY_DATA = "GeometryData"; //TAG_ByteArray
public static function getNetworkTypeId() : string{ return EntityIds::PLAYER; }
/** @var PlayerInventory */
@ -114,16 +136,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
* @throws SavedDataLoadingException
*/
public static function parseSkinNBT(CompoundTag $nbt) : Skin{
$skinTag = $nbt->getCompoundTag("Skin");
$skinTag = $nbt->getCompoundTag(self::TAG_SKIN);
if($skinTag === null){
throw new SavedDataLoadingException("Missing skin data");
}
return new Skin( //this throws if the skin is invalid
$skinTag->getString("Name"),
($skinDataTag = $skinTag->getTag("Data")) instanceof StringTag ? $skinDataTag->getValue() : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM)
$skinTag->getByteArray("CapeData", ""),
$skinTag->getString("GeometryName", ""),
$skinTag->getByteArray("GeometryData", "")
$skinTag->getString(self::TAG_SKIN_NAME),
($skinDataTag = $skinTag->getTag(self::TAG_SKIN_DATA)) instanceof StringTag ? $skinDataTag->getValue() : $skinTag->getByteArray(self::TAG_SKIN_DATA), //old data (this used to be saved as a StringTag in older versions of PM)
$skinTag->getByteArray(self::TAG_SKIN_CAPE_DATA, ""),
$skinTag->getString(self::TAG_SKIN_GEOMETRY_NAME, ""),
$skinTag->getByteArray(self::TAG_SKIN_GEOMETRY_DATA, "")
);
}
@ -153,7 +175,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
* @param Player[]|null $targets
*/
public function sendSkin(?array $targets = null) : void{
$this->server->broadcastPackets($targets ?? $this->hasSpawned, [
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->hasSpawned, [
PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin))
]);
}
@ -168,9 +190,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
public function emote(string $emoteId) : void{
foreach($this->getViewers() as $player){
$player->getNetworkSession()->onEmote($this, $emoteId);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEmote($recipients, $this, $emoteId)
);
}
public function getHungerManager() : HungerManager{
@ -221,10 +244,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
*/
protected function initHumanData(CompoundTag $nbt) : void{
if(($nameTagTag = $nbt->getTag("NameTag")) instanceof StringTag){
$this->setNameTag($nameTagTag->getValue());
}
//TODO: use of NIL UUID for namespace is a hack; we should provide a proper UUID for the namespace
$this->uuid = Uuid::uuid3(Uuid::NIL, ((string) $this->getId()) . $this->skin->getSkinData() . $this->getNameTag());
}
@ -249,11 +268,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->xpManager = new ExperienceManager($this);
$this->inventory = new PlayerInventory($this);
$syncHeldItem = function() : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
}
};
$syncHeldItem = fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this)
);
$this->inventory->getListeners()->add(new CallbackInventoryListener(
function(Inventory $unused, int $slot, Item $unused2) use ($syncHeldItem) : void{
if($slot === $this->inventory->getHeldItemIndex()){
@ -270,14 +288,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->enderInventory = new PlayerEnderInventory($this);
$this->initHumanData($nbt);
$inventoryTag = $nbt->getListTag("Inventory");
$inventoryTag = $nbt->getListTag(self::TAG_INVENTORY);
if($inventoryTag !== null){
$inventoryItems = [];
$armorInventoryItems = [];
/** @var CompoundTag $item */
foreach($inventoryTag as $i => $item){
$slot = $item->getByte("Slot");
$slot = $item->getByte(Item::TAG_SLOT);
if($slot >= 0 && $slot < 9){ //Hotbar
//Old hotbar saving stuff, ignore it
}elseif($slot >= 100 && $slot < 104){ //Armor
@ -290,45 +308,43 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
self::populateInventoryFromListTag($this->inventory, $inventoryItems);
self::populateInventoryFromListTag($this->armorInventory, $armorInventoryItems);
}
$offHand = $nbt->getCompoundTag("OffHandItem");
$offHand = $nbt->getCompoundTag(self::TAG_OFF_HAND_ITEM);
if($offHand !== null){
$this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand));
}
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(function() : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobOffHandItemChange($this);
}
}));
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobOffHandItemChange($recipients, $this)
)));
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
$enderChestInventoryTag = $nbt->getListTag(self::TAG_ENDER_CHEST_INVENTORY);
if($enderChestInventoryTag !== null){
$enderChestInventoryItems = [];
/** @var CompoundTag $item */
foreach($enderChestInventoryTag as $i => $item){
$enderChestInventoryItems[$item->getByte("Slot")] = Item::nbtDeserialize($item);
$enderChestInventoryItems[$item->getByte(Item::TAG_SLOT)] = Item::nbtDeserialize($item);
}
self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems);
}
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
}
});
$this->inventory->setHeldItemIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0));
$this->inventory->getHeldItemIndexChangeListeners()->add(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this)
));
$this->hungerManager->setFood((float) $nbt->getInt("foodLevel", (int) $this->hungerManager->getFood()));
$this->hungerManager->setExhaustion($nbt->getFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion()));
$this->hungerManager->setSaturation($nbt->getFloat("foodSaturationLevel", $this->hungerManager->getSaturation()));
$this->hungerManager->setFoodTickTimer($nbt->getInt("foodTickTimer", $this->hungerManager->getFoodTickTimer()));
$this->hungerManager->setFood((float) $nbt->getInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood()));
$this->hungerManager->setExhaustion($nbt->getFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion()));
$this->hungerManager->setSaturation($nbt->getFloat(self::TAG_FOOD_SATURATION_LEVEL, $this->hungerManager->getSaturation()));
$this->hungerManager->setFoodTickTimer($nbt->getInt(self::TAG_FOOD_TICK_TIMER, $this->hungerManager->getFoodTickTimer()));
$this->xpManager->setXpAndProgressNoEvent(
$nbt->getInt("XpLevel", 0),
$nbt->getFloat("XpP", 0.0));
$this->xpManager->setLifetimeTotalXp($nbt->getInt("XpTotal", 0));
$nbt->getInt(self::TAG_XP_LEVEL, 0),
$nbt->getFloat(self::TAG_XP_PROGRESS, 0.0));
$this->xpManager->setLifetimeTotalXp($nbt->getInt(self::TAG_LIFETIME_XP_TOTAL, 0));
if(($xpSeedTag = $nbt->getTag("XpSeed")) instanceof IntTag){
if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){
$this->xpSeed = $xpSeedTag->getValue();
}else{
$this->xpSeed = random_int(Limits::INT32_MIN, Limits::INT32_MAX);
@ -391,24 +407,24 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : [],
$this->offHandInventory !== null ? array_values($this->offHandInventory->getContents()) : [],
), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()); });
), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()) && !$item->keepOnDeath(); });
}
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setInt("foodLevel", (int) $this->hungerManager->getFood());
$nbt->setFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion());
$nbt->setFloat("foodSaturationLevel", $this->hungerManager->getSaturation());
$nbt->setInt("foodTickTimer", $this->hungerManager->getFoodTickTimer());
$nbt->setInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood());
$nbt->setFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion());
$nbt->setFloat(self::TAG_FOOD_SATURATION_LEVEL, $this->hungerManager->getSaturation());
$nbt->setInt(self::TAG_FOOD_TICK_TIMER, $this->hungerManager->getFoodTickTimer());
$nbt->setInt("XpLevel", $this->xpManager->getXpLevel());
$nbt->setFloat("XpP", $this->xpManager->getXpProgress());
$nbt->setInt("XpTotal", $this->xpManager->getLifetimeTotalXp());
$nbt->setInt("XpSeed", $this->xpSeed);
$nbt->setInt(self::TAG_XP_LEVEL, $this->xpManager->getXpLevel());
$nbt->setFloat(self::TAG_XP_PROGRESS, $this->xpManager->getXpProgress());
$nbt->setInt(self::TAG_LIFETIME_XP_TOTAL, $this->xpManager->getLifetimeTotalXp());
$nbt->setInt(self::TAG_XP_SEED, $this->xpSeed);
$inventoryTag = new ListTag([], NBT::TAG_Compound);
$nbt->setTag("Inventory", $inventoryTag);
$nbt->setTag(self::TAG_INVENTORY, $inventoryTag);
if($this->inventory !== null){
//Normal inventory
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
@ -427,11 +443,11 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
}
$nbt->setInt("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
$nbt->setInt(self::TAG_SELECTED_INVENTORY_SLOT, $this->inventory->getHeldItemIndex());
}
$offHandItem = $this->offHandInventory->getItem(0);
if(!$offHandItem->isNull()){
$nbt->setTag("OffHandItem", $offHandItem->nbtSerialize());
$nbt->setTag(self::TAG_OFF_HAND_ITEM, $offHandItem->nbtSerialize());
}
if($this->enderInventory !== null){
@ -446,16 +462,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
}
$nbt->setTag("EnderChestInventory", new ListTag($items, NBT::TAG_Compound));
$nbt->setTag(self::TAG_ENDER_CHEST_INVENTORY, new ListTag($items, NBT::TAG_Compound));
}
if($this->skin !== null){
$nbt->setTag("Skin", CompoundTag::create()
->setString("Name", $this->skin->getSkinId())
->setByteArray("Data", $this->skin->getSkinData())
->setByteArray("CapeData", $this->skin->getCapeData())
->setString("GeometryName", $this->skin->getGeometryName())
->setByteArray("GeometryData", $this->skin->getGeometryData())
$nbt->setTag(self::TAG_SKIN, CompoundTag::create()
->setString(self::TAG_SKIN_NAME, $this->skin->getSkinId())
->setByteArray(self::TAG_SKIN_DATA, $this->skin->getSkinData())
->setByteArray(self::TAG_SKIN_CAPE_DATA, $this->skin->getCapeData())
->setString(self::TAG_SKIN_GEOMETRY_NAME, $this->skin->getGeometryName())
->setByteArray(self::TAG_SKIN_GEOMETRY_DATA, $this->skin->getGeometryData())
);
}
@ -469,11 +485,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
protected function sendSpawnPacket(Player $player) : void{
$networkSession = $player->getNetworkSession();
if(!($this instanceof Player)){
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
$networkSession->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
}
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
$networkSession->sendDataPacket(AddPlayerPacket::create(
$this->getUniqueId(),
$this->getName(),
$this->getId(),
@ -487,14 +504,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
GameMode::SURVIVAL,
$this->getAllNetworkData(),
new PropertySyncData([], []),
UpdateAbilitiesPacket::create(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
new UpdateAbilitiesPacketLayer(
UpdateAbilitiesPacketLayer::LAYER_BASE,
array_fill(0, UpdateAbilitiesPacketLayer::NUMBER_OF_ABILITIES, false),
UpdateAbilitiesPacket::create(new AbilitiesData(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
new AbilitiesLayer(
AbilitiesLayer::LAYER_BASE,
array_fill(0, AbilitiesLayer::NUMBER_OF_ABILITIES, false),
0.0,
0.0
)
]),
])),
[], //TODO: entity links
"", //device ID (we intentionally don't send this - secvuln)
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)
@ -503,11 +520,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
$player->getNetworkSession()->onMobArmorChange($this);
$player->getNetworkSession()->onMobOffHandItemChange($this);
$entityEventBroadcaster = $networkSession->getEntityEventBroadcaster();
$entityEventBroadcaster->onMobArmorChange([$networkSession], $this);
$entityEventBroadcaster->onMobOffHandItemChange([$networkSession], $this);
if(!($this instanceof Player)){
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
$networkSession->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
}
}

View File

@ -50,6 +50,8 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
@ -77,6 +79,16 @@ use const M_PI;
abstract class Living extends Entity{
protected const DEFAULT_BREATH_TICKS = 300;
private const TAG_LEGACY_HEALTH = "HealF"; //TAG_Float
private const TAG_HEALTH = "Health"; //TAG_Float
private const TAG_BREATH_TICKS = "Air"; //TAG_Short
private const TAG_ACTIVE_EFFECTS = "ActiveEffects"; //TAG_List<TAG_Compound>
private const TAG_EFFECT_ID = "Id"; //TAG_Byte
private const TAG_EFFECT_DURATION = "Duration"; //TAG_Int
private const TAG_EFFECT_AMPLIFIER = "Amplifier"; //TAG_Byte
private const TAG_EFFECT_SHOW_PARTICLES = "ShowParticles"; //TAG_Byte
private const TAG_EFFECT_AMBIENT = "Ambient"; //TAG_Byte
protected $gravity = 0.08;
protected $drag = 0.02;
@ -133,19 +145,16 @@ abstract class Living extends Entity{
$this->armorInventory = new ArmorInventory($this);
//TODO: load/save armor inventory contents
$this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
function(Inventory $unused) : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobArmorChange($this);
}
}
));
$this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this)
)));
$health = $this->getMaxHealth();
if(($healFTag = $nbt->getTag("HealF")) instanceof FloatTag){
if(($healFTag = $nbt->getTag(self::TAG_LEGACY_HEALTH)) instanceof FloatTag){
$health = $healFTag->getValue();
}elseif(($healthTag = $nbt->getTag("Health")) instanceof ShortTag){
}elseif(($healthTag = $nbt->getTag(self::TAG_HEALTH)) instanceof ShortTag){
$health = $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
}elseif($healthTag instanceof FloatTag){
$health = $healthTag->getValue();
@ -153,23 +162,23 @@ abstract class Living extends Entity{
$this->setHealth($health);
$this->setAirSupplyTicks($nbt->getShort("Air", self::DEFAULT_BREATH_TICKS));
$this->setAirSupplyTicks($nbt->getShort(self::TAG_BREATH_TICKS, self::DEFAULT_BREATH_TICKS));
/** @var CompoundTag[]|ListTag|null $activeEffectsTag */
$activeEffectsTag = $nbt->getListTag("ActiveEffects");
$activeEffectsTag = $nbt->getListTag(self::TAG_ACTIVE_EFFECTS);
if($activeEffectsTag !== null){
foreach($activeEffectsTag as $e){
$effect = EffectIdMap::getInstance()->fromId($e->getByte("Id"));
$effect = EffectIdMap::getInstance()->fromId($e->getByte(self::TAG_EFFECT_ID));
if($effect === null){
continue;
}
$this->effectManager->add(new EffectInstance(
$effect,
$e->getInt("Duration"),
Binary::unsignByte($e->getByte("Amplifier")),
$e->getByte("ShowParticles", 1) !== 0,
$e->getByte("Ambient", 0) !== 0
$e->getInt(self::TAG_EFFECT_DURATION),
Binary::unsignByte($e->getByte(self::TAG_EFFECT_AMPLIFIER)),
$e->getByte(self::TAG_EFFECT_SHOW_PARTICLES, 1) !== 0,
$e->getByte(self::TAG_EFFECT_AMBIENT, 0) !== 0
));
}
}
@ -184,6 +193,13 @@ abstract class Living extends Entity{
$this->attributeMap->add($this->absorptionAttr = AttributeFactory::getInstance()->mustGet(Attribute::ABSORPTION));
}
/**
* Returns the name used to describe this entity in chat and command outputs.
*/
public function getDisplayName() : string{
return $this->nameTag !== "" ? $this->nameTag : $this->getName();
}
public function setHealth(float $amount) : void{
$wasAlive = $this->isAlive();
parent::setHealth($amount);
@ -272,22 +288,22 @@ abstract class Living extends Entity{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setFloat("Health", $this->getHealth());
$nbt->setFloat(self::TAG_HEALTH, $this->getHealth());
$nbt->setShort("Air", $this->getAirSupplyTicks());
$nbt->setShort(self::TAG_BREATH_TICKS, $this->getAirSupplyTicks());
if(count($this->effectManager->all()) > 0){
$effects = [];
foreach($this->effectManager->all() as $effect){
$effects[] = CompoundTag::create()
->setByte("Id", EffectIdMap::getInstance()->toId($effect->getType()))
->setByte("Amplifier", Binary::signByte($effect->getAmplifier()))
->setInt("Duration", $effect->getDuration())
->setByte("Ambient", $effect->isAmbient() ? 1 : 0)
->setByte("ShowParticles", $effect->isVisible() ? 1 : 0);
->setByte(self::TAG_EFFECT_ID, EffectIdMap::getInstance()->toId($effect->getType()))
->setByte(self::TAG_EFFECT_AMPLIFIER, Binary::signByte($effect->getAmplifier()))
->setInt(self::TAG_EFFECT_DURATION, $effect->getDuration())
->setByte(self::TAG_EFFECT_AMBIENT, $effect->isAmbient() ? 1 : 0)
->setByte(self::TAG_EFFECT_SHOW_PARTICLES, $effect->isVisible() ? 1 : 0);
}
$nbt->setTag("ActiveEffects", new ListTag($effects));
$nbt->setTag(self::TAG_ACTIVE_EFFECTS, new ListTag($effects));
}
return $nbt;
@ -448,7 +464,9 @@ abstract class Living extends Entity{
*/
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
$this->damageArmor($source->getBaseDamage());
if($source->canBeReducedByArmor()){
$this->damageArmor($source->getBaseDamage());
}
if($source instanceof EntityDamageByEntityEvent && ($attacker = $source->getDamager()) !== null){
$damage = 0;
@ -831,7 +849,8 @@ abstract class Living extends Entity{
protected function sendSpawnPacket(Player $player) : void{
parent::sendSpawnPacket($player);
$player->getNetworkSession()->onMobArmorChange($this);
$networkSession = $player->getNetworkSession();
$networkSession->getEntityEventBroadcaster()->onMobArmorChange([$networkSession], $this);
}
protected function syncNetworkData(EntityMetadataCollection $properties) : void{

View File

@ -36,6 +36,8 @@ class Villager extends Living implements Ageable{
public const PROFESSION_BLACKSMITH = 3;
public const PROFESSION_BUTCHER = 4;
private const TAG_PROFESSION = "Profession"; //TAG_Int
public static function getNetworkTypeId() : string{ return EntityIds::VILLAGER; }
private bool $baby = false;
@ -53,7 +55,7 @@ class Villager extends Living implements Ageable{
parent::initEntity($nbt);
/** @var int $profession */
$profession = $nbt->getInt("Profession", self::PROFESSION_FARMER);
$profession = $nbt->getInt(self::TAG_PROFESSION, self::PROFESSION_FARMER);
if($profession > 4 || $profession < 0){
$profession = self::PROFESSION_FARMER;
@ -64,7 +66,7 @@ class Villager extends Living implements Ageable{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setInt("Profession", $this->getProfession());
$nbt->setInt(self::TAG_PROFESSION, $this->getProfession());
return $nbt;
}

View File

@ -32,6 +32,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\player\Player;
use function max;
use function sqrt;
class ExperienceOrb extends Entity{
@ -40,6 +41,7 @@ class ExperienceOrb extends Entity{
public const TAG_VALUE_PC = "Value"; //short
public const TAG_VALUE_PE = "experience value"; //int (WTF?)
private const TAG_AGE = "Age"; //TAG_Short
/** Max distance an orb will follow a player across. */
public const MAX_TARGET_DISTANCE = 8.0;
@ -47,6 +49,10 @@ class ExperienceOrb extends Entity{
/** Split sizes used for dropping experience orbs. */
public const ORB_SPLIT_SIZES = [2477, 1237, 617, 307, 149, 73, 37, 17, 7, 3, 1]; //This is indexed biggest to smallest so that we can return as soon as we found the biggest value.
public const DEFAULT_DESPAWN_DELAY = 6000;
public const NEVER_DESPAWN = -1;
public const MAX_DESPAWN_DELAY = 32767 + self::DEFAULT_DESPAWN_DELAY; //max value storable by mojang NBT :(
/**
* Returns the largest size of normal XP orb that will be spawned for the specified amount of XP. Used to split XP
* up into multiple orbs when an amount of XP is dropped.
@ -81,7 +87,10 @@ class ExperienceOrb extends Entity{
public $gravity = 0.04;
public $drag = 0.02;
/** @var int */
/**
* @var int
* @deprecated
*/
protected $age = 0;
/**
@ -99,6 +108,8 @@ class ExperienceOrb extends Entity{
/** @var int */
protected $xpValue;
private int $despawnDelay = self::DEFAULT_DESPAWN_DELAY;
public function __construct(Location $location, int $xpValue, ?CompoundTag $nbt = null){
$this->xpValue = $xpValue;
parent::__construct($location, $nbt);
@ -109,13 +120,23 @@ class ExperienceOrb extends Entity{
protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt);
$this->age = $nbt->getShort("Age", 0);
$this->age = $nbt->getShort(self::TAG_AGE, 0);
if($this->age === -32768){
$this->despawnDelay = self::NEVER_DESPAWN;
}else{
$this->despawnDelay = max(0, self::DEFAULT_DESPAWN_DELAY - $this->age);
}
}
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setShort("Age", $this->age);
if($this->despawnDelay === self::NEVER_DESPAWN){
$age = -32768;
}else{
$age = self::DEFAULT_DESPAWN_DELAY - $this->despawnDelay;
}
$nbt->setShort(self::TAG_AGE, $age);
$nbt->setShort(self::TAG_VALUE_PC, $this->getXpValue());
$nbt->setInt(self::TAG_VALUE_PE, $this->getXpValue());
@ -123,6 +144,15 @@ class ExperienceOrb extends Entity{
return $nbt;
}
public function getDespawnDelay() : int{ return $this->despawnDelay; }
public function setDespawnDelay(int $despawnDelay) : void{
if(($despawnDelay < 0 || $despawnDelay > self::MAX_DESPAWN_DELAY) && $despawnDelay !== self::NEVER_DESPAWN){
throw new \InvalidArgumentException("Despawn ticker must be in range 0 ... " . self::MAX_DESPAWN_DELAY . " or " . self::NEVER_DESPAWN . ", got $despawnDelay");
}
$this->despawnDelay = $despawnDelay;
}
public function getXpValue() : int{
return $this->xpValue;
}
@ -160,7 +190,8 @@ class ExperienceOrb extends Entity{
$hasUpdate = parent::entityBaseTick($tickDiff);
$this->age += $tickDiff;
if($this->age > 6000){
$this->despawnDelay -= $tickDiff;
if($this->despawnDelay <= 0){
$this->flagForDespawn();
return true;
}

View File

@ -44,6 +44,10 @@ use function abs;
class FallingBlock extends Entity{
private const TAG_TILE_ID = "TileID"; //TAG_Int
private const TAG_TILE = "Tile"; //TAG_Byte
private const TAG_DATA = "Data"; //TAG_Byte
public static function getNetworkTypeId() : string{ return EntityIds::FALLING_BLOCK; }
protected $gravity = 0.04;
@ -65,9 +69,9 @@ class FallingBlock extends Entity{
$blockId = 0;
//TODO: 1.8+ save format
if(($tileIdTag = $nbt->getTag("TileID")) instanceof IntTag){
if(($tileIdTag = $nbt->getTag(self::TAG_TILE_ID)) instanceof IntTag){
$blockId = $tileIdTag->getValue();
}elseif(($tileTag = $nbt->getTag("Tile")) instanceof ByteTag){
}elseif(($tileTag = $nbt->getTag(self::TAG_TILE)) instanceof ByteTag){
$blockId = $tileTag->getValue();
}
@ -75,7 +79,7 @@ class FallingBlock extends Entity{
throw new SavedDataLoadingException("Missing block info from NBT");
}
$damage = $nbt->getByte("Data", 0);
$damage = $nbt->getByte(self::TAG_DATA, 0);
return $factory->get($blockId, $damage);
}
@ -138,8 +142,8 @@ class FallingBlock extends Entity{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setInt("TileID", $this->block->getId());
$nbt->setByte("Data", $this->block->getMeta());
$nbt->setInt(self::TAG_TILE_ID, $this->block->getId());
$nbt->setByte(self::TAG_DATA, $this->block->getMeta());
return $nbt;
}

View File

@ -35,14 +35,24 @@ use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\player\Player;
use pocketmine\timings\Timings;
use function max;
class ItemEntity extends Entity{
private const TAG_HEALTH = "Health"; //TAG_Short
private const TAG_AGE = "Age"; //TAG_Short
private const TAG_PICKUP_DELAY = "PickupDelay"; //TAG_Short
private const TAG_OWNER = "Owner"; //TAG_String
private const TAG_THROWER = "Thrower"; //TAG_String
public const TAG_ITEM = "Item"; //TAG_Compound
public static function getNetworkTypeId() : string{ return EntityIds::ITEM; }
public const MERGE_CHECK_PERIOD = 2; //0.1 seconds
@ -81,17 +91,17 @@ class ItemEntity extends Entity{
parent::initEntity($nbt);
$this->setMaxHealth(5);
$this->setHealth($nbt->getShort("Health", (int) $this->getHealth()));
$this->setHealth($nbt->getShort(self::TAG_HEALTH, (int) $this->getHealth()));
$age = $nbt->getShort("Age", 0);
$age = $nbt->getShort(self::TAG_AGE, 0);
if($age === -32768){
$this->despawnDelay = self::NEVER_DESPAWN;
}else{
$this->despawnDelay = max(0, self::DEFAULT_DESPAWN_DELAY - $age);
}
$this->pickupDelay = $nbt->getShort("PickupDelay", $this->pickupDelay);
$this->owner = $nbt->getString("Owner", $this->owner);
$this->thrower = $nbt->getString("Thrower", $this->thrower);
$this->pickupDelay = $nbt->getShort(self::TAG_PICKUP_DELAY, $this->pickupDelay);
$this->owner = $nbt->getString(self::TAG_OWNER, $this->owner);
$this->thrower = $nbt->getString(self::TAG_THROWER, $this->thrower);
}
protected function onFirstUpdate(int $currentTick) : void{
@ -104,13 +114,23 @@ class ItemEntity extends Entity{
return false;
}
$hasUpdate = parent::entityBaseTick($tickDiff);
Timings::$itemEntityBaseTick->startTiming();
try{
if(!$this->isFlaggedForDespawn() && $this->pickupDelay !== self::NEVER_DESPAWN){ //Infinite delay
$this->pickupDelay -= $tickDiff;
if($this->pickupDelay < 0){
$this->pickupDelay = 0;
$hasUpdate = parent::entityBaseTick($tickDiff);
if($this->isFlaggedForDespawn()){
return $hasUpdate;
}
if($this->pickupDelay !== self::NEVER_DESPAWN && $this->pickupDelay > 0){ //Infinite delay
$hasUpdate = true;
$this->pickupDelay -= $tickDiff;
if($this->pickupDelay < 0){
$this->pickupDelay = 0;
}
}
if($this->hasMovementUpdate() && $this->despawnDelay % self::MERGE_CHECK_PERIOD === 0){
$mergeable = [$this]; //in case the merge target ends up not being this
$mergeTarget = $this;
@ -133,20 +153,24 @@ class ItemEntity extends Entity{
}
}
$this->despawnDelay -= $tickDiff;
if($this->despawnDelay <= 0){
$ev = new ItemDespawnEvent($this);
$ev->call();
if($ev->isCancelled()){
$this->despawnDelay = self::DEFAULT_DESPAWN_DELAY;
}else{
$this->flagForDespawn();
$hasUpdate = true;
if(!$this->isFlaggedForDespawn() && $this->despawnDelay !== self::NEVER_DESPAWN){
$hasUpdate = true;
$this->despawnDelay -= $tickDiff;
if($this->despawnDelay <= 0){
$ev = new ItemDespawnEvent($this);
$ev->call();
if($ev->isCancelled()){
$this->despawnDelay = self::DEFAULT_DESPAWN_DELAY;
}else{
$this->flagForDespawn();
}
}
}
}
return $hasUpdate;
return $hasUpdate;
}finally{
Timings::$itemEntityBaseTick->stopTiming();
}
}
/**
@ -195,20 +219,20 @@ class ItemEntity extends Entity{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setTag("Item", $this->item->nbtSerialize());
$nbt->setShort("Health", (int) $this->getHealth());
$nbt->setTag(self::TAG_ITEM, $this->item->nbtSerialize());
$nbt->setShort(self::TAG_HEALTH, (int) $this->getHealth());
if($this->despawnDelay === self::NEVER_DESPAWN){
$age = -32768;
}else{
$age = self::DEFAULT_DESPAWN_DELAY - $this->despawnDelay;
}
$nbt->setShort("Age", $age);
$nbt->setShort("PickupDelay", $this->pickupDelay);
$nbt->setShort(self::TAG_AGE, $age);
$nbt->setShort(self::TAG_PICKUP_DELAY, $this->pickupDelay);
if($this->owner !== null){
$nbt->setString("Owner", $this->owner);
$nbt->setString(self::TAG_OWNER, $this->owner);
}
if($this->thrower !== null){
$nbt->setString("Thrower", $this->thrower);
$nbt->setString(self::TAG_THROWER, $this->thrower);
}
return $nbt;
@ -313,9 +337,10 @@ class ItemEntity extends Entity{
return;
}
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
);
$inventory = $ev->getInventory();
if($inventory !== null){

View File

@ -41,6 +41,13 @@ use pocketmine\world\World;
use function ceil;
class Painting extends Entity{
public const TAG_TILE_X = "TileX"; //TAG_Int
public const TAG_TILE_Y = "TileY"; //TAG_Int
public const TAG_TILE_Z = "TileZ"; //TAG_Int
public const TAG_FACING_JE = "Facing"; //TAG_Byte
public const TAG_DIRECTION_BE = "Direction"; //TAG_Byte
public const TAG_MOTIVE = "Motive"; //TAG_String
public static function getNetworkTypeId() : string{ return EntityIds::PAINTING; }
public const DATA_TO_FACING = [
@ -88,14 +95,14 @@ class Painting extends Entity{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setInt("TileX", (int) $this->blockIn->x);
$nbt->setInt("TileY", (int) $this->blockIn->y);
$nbt->setInt("TileZ", (int) $this->blockIn->z);
$nbt->setInt(self::TAG_TILE_X, (int) $this->blockIn->x);
$nbt->setInt(self::TAG_TILE_Y, (int) $this->blockIn->y);
$nbt->setInt(self::TAG_TILE_Z, (int) $this->blockIn->z);
$nbt->setByte("Facing", self::FACING_TO_DATA[$this->facing]);
$nbt->setByte("Direction", self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
$nbt->setByte(self::TAG_FACING_JE, self::FACING_TO_DATA[$this->facing]);
$nbt->setByte(self::TAG_DIRECTION_BE, self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
$nbt->setString("Motive", $this->motive->getName());
$nbt->setString(self::TAG_MOTIVE, $this->motive->getName());
return $nbt;
}

View File

@ -39,6 +39,8 @@ use pocketmine\world\Position;
class PrimedTNT extends Entity implements Explosive{
private const TAG_FUSE = "Fuse"; //TAG_Short
public static function getNetworkTypeId() : string{ return EntityIds::TNT; }
protected $gravity = 0.04;
@ -81,7 +83,7 @@ class PrimedTNT extends Entity implements Explosive{
protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt);
$this->fuse = $nbt->getShort("Fuse", 80);
$this->fuse = $nbt->getShort(self::TAG_FUSE, 80);
}
public function canCollideWith(Entity $entity) : bool{
@ -90,7 +92,7 @@ class PrimedTNT extends Entity implements Explosive{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setShort("Fuse", $this->fuse);
$nbt->setShort(self::TAG_FUSE, $this->fuse);
return $nbt;
}

View File

@ -33,6 +33,8 @@ use pocketmine\event\entity\ProjectileHitEvent;
use pocketmine\item\VanillaItems;
use pocketmine\math\RayTraceResult;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
@ -52,6 +54,7 @@ class Arrow extends Projectile{
private const TAG_PICKUP = "pickup"; //TAG_Byte
public const TAG_CRIT = "crit"; //TAG_Byte
private const TAG_LIFE = "life"; //TAG_Short
protected $gravity = 0.05;
protected $drag = 0.01;
@ -83,14 +86,14 @@ class Arrow extends Projectile{
$this->pickupMode = $nbt->getByte(self::TAG_PICKUP, self::PICKUP_ANY);
$this->critical = $nbt->getByte(self::TAG_CRIT, 0) === 1;
$this->collideTicks = $nbt->getShort("life", $this->collideTicks);
$this->collideTicks = $nbt->getShort(self::TAG_LIFE, $this->collideTicks);
}
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setByte(self::TAG_PICKUP, $this->pickupMode);
$nbt->setByte(self::TAG_CRIT, $this->critical ? 1 : 0);
$nbt->setShort("life", $this->collideTicks);
$nbt->setShort(self::TAG_LIFE, $this->collideTicks);
return $nbt;
}
@ -195,9 +198,10 @@ class Arrow extends Projectile{
return;
}
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
);
$ev->getInventory()?->addItem($ev->getItem());
$this->flagForDespawn();

View File

@ -50,6 +50,12 @@ use const M_PI;
use const PHP_INT_MAX;
abstract class Projectile extends Entity{
private const TAG_DAMAGE = "damage"; //TAG_Double
private const TAG_TILE_X = "tileX"; //TAG_Int
private const TAG_TILE_Y = "tileY"; //TAG_Int
private const TAG_TILE_Z = "tileZ"; //TAG_Int
private const TAG_BLOCK_ID = "blockId"; //TAG_Int
private const TAG_BLOCK_DATA = "blockData"; //TAG_Byte
/** @var float */
protected $damage = 0.0;
@ -75,22 +81,22 @@ abstract class Projectile extends Entity{
$this->setMaxHealth(1);
$this->setHealth(1);
$this->damage = $nbt->getDouble("damage", $this->damage);
$this->damage = $nbt->getDouble(self::TAG_DAMAGE, $this->damage);
(function() use ($nbt) : void{
if(($tileXTag = $nbt->getTag("tileX")) instanceof IntTag && ($tileYTag = $nbt->getTag("tileY")) instanceof IntTag && ($tileZTag = $nbt->getTag("tileZ")) instanceof IntTag){
if(($tileXTag = $nbt->getTag(self::TAG_TILE_X)) instanceof IntTag && ($tileYTag = $nbt->getTag(self::TAG_TILE_Y)) instanceof IntTag && ($tileZTag = $nbt->getTag(self::TAG_TILE_Z)) instanceof IntTag){
$blockPos = new Vector3($tileXTag->getValue(), $tileYTag->getValue(), $tileZTag->getValue());
}else{
return;
}
if(($blockIdTag = $nbt->getTag("blockId")) instanceof IntTag){
if(($blockIdTag = $nbt->getTag(self::TAG_BLOCK_ID)) instanceof IntTag){
$blockId = $blockIdTag->getValue();
}else{
return;
}
if(($blockDataTag = $nbt->getTag("blockData")) instanceof ByteTag){
if(($blockDataTag = $nbt->getTag(self::TAG_BLOCK_DATA)) instanceof ByteTag){
$blockData = $blockDataTag->getValue();
}else{
return;
@ -134,17 +140,17 @@ abstract class Projectile extends Entity{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setDouble("damage", $this->damage);
$nbt->setDouble(self::TAG_DAMAGE, $this->damage);
if($this->blockHit !== null){
$pos = $this->blockHit->getPosition();
$nbt->setInt("tileX", $pos->x);
$nbt->setInt("tileY", $pos->y);
$nbt->setInt("tileZ", $pos->z);
$nbt->setInt(self::TAG_TILE_X, $pos->x);
$nbt->setInt(self::TAG_TILE_Y, $pos->y);
$nbt->setInt(self::TAG_TILE_Z, $pos->z);
//we intentionally use different ones to PC because we don't have stringy IDs
$nbt->setInt("blockId", $this->blockHit->getId());
$nbt->setByte("blockData", $this->blockHit->getMeta());
$nbt->setInt(self::TAG_BLOCK_ID, $this->blockHit->getId());
$nbt->setByte(self::TAG_BLOCK_DATA, $this->blockHit->getMeta());
}
return $nbt;
@ -169,7 +175,8 @@ abstract class Projectile extends Entity{
protected function move(float $dx, float $dy, float $dz) : void{
$this->blocksAround = null;
Timings::$entityMove->startTiming();
Timings::$projectileMove->startTiming();
Timings::$projectileMoveRayTrace->startTiming();
$start = $this->location->asVector3();
$end = $start->add($dx, $dy, $dz);
@ -215,6 +222,8 @@ abstract class Projectile extends Entity{
}
}
Timings::$projectileMoveRayTrace->stopTiming();
$this->location = Location::fromObject(
$end,
$this->location->world,
@ -246,7 +255,7 @@ abstract class Projectile extends Entity{
}
$this->isCollided = $this->onGround = true;
$this->motion = new Vector3(0, 0, 0);
$this->motion = Vector3::zero();
}else{
$this->isCollided = $this->onGround = false;
$this->blockHit = null;
@ -262,7 +271,7 @@ abstract class Projectile extends Entity{
$this->getWorld()->onEntityMoved($this);
$this->checkBlockIntersections();
Timings::$entityMove->stopTiming();
Timings::$projectileMove->stopTiming();
}
/**

View File

@ -50,6 +50,8 @@ use function sqrt;
class SplashPotion extends Throwable{
public const TAG_POTION_ID = "PotionId"; //TAG_Short
public static function getNetworkTypeId() : string{ return EntityIds::SPLASH_POTION; }
protected $gravity = 0.05;
@ -66,7 +68,7 @@ class SplashPotion extends Throwable{
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setShort("PotionId", PotionTypeIdMap::getInstance()->toId($this->getPotionType()));
$nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->getPotionType()));
return $nbt;
}

View File

@ -26,6 +26,7 @@ declare(strict_types=1);
*/
namespace pocketmine\event;
use pocketmine\timings\Timings;
use function get_class;
abstract class Event{
@ -51,22 +52,19 @@ abstract class Event{
throw new \RuntimeException("Recursive event call detected (reached max depth of " . self::MAX_EVENT_CALL_DEPTH . " calls)");
}
$timings = Timings::getEventTimings($this);
$timings->startTiming();
$handlerList = HandlerListManager::global()->getListFor(get_class($this));
++self::$eventCallDepth;
try{
foreach(EventPriority::ALL as $priority){
$currentList = $handlerList;
while($currentList !== null){
foreach($currentList->getListenersByPriority($priority) as $registration){
$registration->callEvent($this);
}
$currentList = $currentList->getParent();
}
foreach($handlerList->getListenerList() as $registration){
$registration->callEvent($this);
}
}finally{
--self::$eventCallDepth;
$timings->stopTiming();
}
}
}

View File

@ -32,6 +32,8 @@ use function mb_strtoupper;
* LOWEST -> LOW -> NORMAL -> HIGH -> HIGHEST -> MONITOR
*
* MONITOR events should not change the event outcome or contents
*
* WARNING: If these values are changed, handler sorting in HandlerList::getListenerList() may need to be updated.
*/
final class EventPriority{

View File

@ -24,13 +24,20 @@ declare(strict_types=1);
namespace pocketmine\event;
use pocketmine\plugin\Plugin;
use function array_fill_keys;
use function array_merge;
use function krsort;
use function spl_object_id;
use const SORT_NUMERIC;
class HandlerList{
/** @var RegisteredListener[][] */
private array $handlerSlots = [];
private RegisteredListenerCache $handlerCache;
/** @var RegisteredListenerCache[] */
private array $affectedHandlerCaches = [];
/**
* @phpstan-template TEvent of Event
* @phpstan-param class-string<TEvent> $class
@ -39,7 +46,10 @@ class HandlerList{
private string $class,
private ?HandlerList $parentList
){
$this->handlerSlots = array_fill_keys(EventPriority::ALL, []);
$this->handlerCache = new RegisteredListenerCache();
for($list = $this; $list !== null; $list = $list->parentList){
$list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache;
}
}
/**
@ -50,6 +60,7 @@ class HandlerList{
throw new \InvalidArgumentException("This listener is already registered to priority {$listener->getPriority()} of event {$this->class}");
}
$this->handlerSlots[$listener->getPriority()][spl_object_id($listener)] = $listener;
$this->invalidateAffectedCaches();
}
/**
@ -59,6 +70,7 @@ class HandlerList{
foreach($listeners as $listener){
$this->register($listener);
}
$this->invalidateAffectedCaches();
}
/**
@ -76,24 +88,60 @@ class HandlerList{
}
}
}elseif($object instanceof RegisteredListener){
if(isset($this->handlerSlots[$object->getPriority()][spl_object_id($object)])){
unset($this->handlerSlots[$object->getPriority()][spl_object_id($object)]);
}
unset($this->handlerSlots[$object->getPriority()][spl_object_id($object)]);
}
$this->invalidateAffectedCaches();
}
public function clear() : void{
$this->handlerSlots = array_fill_keys(EventPriority::ALL, []);
$this->handlerSlots = [];
$this->invalidateAffectedCaches();
}
/**
* @return RegisteredListener[]
*/
public function getListenersByPriority(int $priority) : array{
return $this->handlerSlots[$priority];
return $this->handlerSlots[$priority] ?? [];
}
public function getParent() : ?HandlerList{
return $this->parentList;
}
/**
* Invalidates all known caches which might be affected by this list's contents.
*/
private function invalidateAffectedCaches() : void{
foreach($this->affectedHandlerCaches as $cache){
$cache->list = null;
}
}
/**
* @return RegisteredListener[]
* @phpstan-return list<RegisteredListener>
*/
public function getListenerList() : array{
if($this->handlerCache->list !== null){
return $this->handlerCache->list;
}
$handlerLists = [];
for($currentList = $this; $currentList !== null; $currentList = $currentList->parentList){
$handlerLists[] = $currentList;
}
$listenersByPriority = [];
foreach($handlerLists as $currentList){
foreach($currentList->handlerSlots as $priority => $listeners){
$listenersByPriority[$priority] = array_merge($listenersByPriority[$priority] ?? [], $listeners);
}
}
//TODO: why on earth do the priorities have higher values for lower priority?
krsort($listenersByPriority, SORT_NUMERIC);
return $this->handlerCache->list = array_merge(...$listenersByPriority);
}
}

View File

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

View File

@ -0,0 +1,38 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event;
/**
* @internal
*/
final class RegisteredListenerCache{
/**
* List of all handlers that will be called for a particular event, ordered by execution order.
*
* @var RegisteredListener[]
* @phpstan-var list<RegisteredListener>
*/
public ?array $list = null;
}

View File

@ -98,17 +98,15 @@ class PlayerDeathEvent extends EntityDeathEvent{
if($e instanceof Player){
return KnownTranslationFactory::death_attack_player($name, $e->getDisplayName());
}elseif($e instanceof Living){
return KnownTranslationFactory::death_attack_mob($name, $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName());
return KnownTranslationFactory::death_attack_mob($name, $e->getDisplayName());
}
}
break;
case EntityDamageEvent::CAUSE_PROJECTILE:
if($deathCause instanceof EntityDamageByEntityEvent){
$e = $deathCause->getDamager();
if($e instanceof Player){
if($e instanceof Living){
return KnownTranslationFactory::death_attack_arrow($name, $e->getDisplayName());
}elseif($e instanceof Living){
return KnownTranslationFactory::death_attack_arrow($name, $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName());
}
}
break;
@ -149,10 +147,8 @@ class PlayerDeathEvent extends EntityDeathEvent{
case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION:
if($deathCause instanceof EntityDamageByEntityEvent){
$e = $deathCause->getDamager();
if($e instanceof Player){
if($e instanceof Living){
return KnownTranslationFactory::death_attack_explosion_player($name, $e->getDisplayName());
}elseif($e instanceof Living){
return KnownTranslationFactory::death_attack_explosion_player($name, $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName());
}
}
return KnownTranslationFactory::death_attack_explosion($name);

View File

@ -59,7 +59,7 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
$this->player = $player;
$this->item = $item;
$this->blockTouched = $block;
$this->touchVector = $touchVector ?? new Vector3(0, 0, 0);
$this->touchVector = $touchVector ?? Vector3::zero();
$this->blockFace = $face;
$this->action = $action;
}

View File

@ -69,8 +69,8 @@ class PlayerPreLoginEvent extends Event implements Cancellable{
/**
* Returns an object containing self-proclaimed information about the connecting player.
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, it's unknown if the player is real or a
* hacker.
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, this could be a hacker posing as another
* player.
*/
public function getPlayerInfo() : PlayerInfo{
return $this->playerInfo;
@ -109,7 +109,7 @@ class PlayerPreLoginEvent extends Event implements Cancellable{
}
/**
* Sets a reason to disallow the player to continue continue authenticating, with a message.
* Sets a reason to disallow the player to continue authenticating, with a message.
* This can also be used to change kick messages for already-set flags.
*/
public function setKickReason(int $flag, string $message) : void{

Some files were not shown because too many files have changed in this diff Show More