Compare commits

...

148 Commits

Author SHA1 Message Date
687112f4cd 5.27.0, Bedrock 1.21.70 support (#6665)
Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
2025-03-27 15:48:19 +00:00
c9e85603b0 Bump phpstan/phpstan-strict-rules in the development-patch-updates group (#6664) 2025-03-24 14:43:29 +00:00
c80a4d5b55 5.26.1 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/14011298411
2025-03-22 18:33:19 +00:00
f416cb8902 Merge pull request #6658 from pmmp/minor-next-release 2025-03-22 18:32:15 +00:00
f123df5e0d changelog: use more accurate terminology 2025-03-22 18:16:33 +00:00
de26ebd124 Prepare 5.26.0 release 2025-03-22 17:59:20 +00:00
1c6a4bde86 Bump pocketmine/locale-data in the production-patch-updates group (#6656) 2025-03-17 13:20:59 +00:00
c2f8e9365b BlockStateToObjectDeserializer: check that the returned state is actually registered
if not, this will cause random crashes in core code, which assumes that state IDs found on runtime chunk memory are valid and registered.

this problem exists in other places too, and probably requires a rethink of how we're dealing with this, but for now, this will do as a band-aid.
2025-03-15 20:53:49 +00:00
4ef21fabab Merge branch 'stable' into minor-next 2025-03-15 20:37:28 +00:00
4407e585e4 Update composer dependencies (minor-next) 2025-03-15 20:36:39 +00:00
463be36b72 Update composer dependencies 2025-03-15 20:33:47 +00:00
8b57e9007a 👺 2025-03-15 01:33:29 +00:00
e03c586c86 GarbageCollectorManager: promote debug message to info
this has such a big impact on performance that I think this is warranted. Should also make it more obvious what the GC is doing without needing to enable ALL debug info.
2025-03-15 01:29:49 +00:00
802e373bf3 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13868087288
2025-03-15 01:26:47 +00:00
09acbfab4c dependabot: ignore phpstan/phpstan updates
these are noisy and cause conflicts. Since they also usually cause new errors to be reported, we often can't directly update it anyway. Better to test & update this locally.
2025-03-15 00:03:09 +00:00
7cfaf04b87 stfu 2025-03-14 16:10:56 +00:00
d9e0e51e14 Reduce code duplication in copper block serialization handling 2025-03-14 16:08:06 +00:00
069ecf007f Merge branch 'stable' into minor-next 2025-03-14 15:41:46 +00:00
341c7a03a9 CopperMaterial: fixed missing @return $this docs 2025-03-14 15:40:27 +00:00
7ae90dda5d Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13825107599
2025-03-13 01:36:47 +00:00
73a4b076d6 actions: tidy support response message 2025-03-12 16:19:11 +00:00
00df508727 Update bug-report.yml 2025-03-12 13:06:57 +00:00
a6553097f4 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13754675430
2025-03-10 01:14:10 +00:00
afc4a3c7f1 Fixed entity position offset not being included in AddActorPacket (#6643)
this was causing TNT and falling blocks to briefly appear half a block lower than their true position, because their positions are measured from the center and not the base.
2025-03-09 02:09:53 +00:00
ac7b5b3b13 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13743358576
2025-03-09 01:16:28 +00:00
7af5eb3da2 crafting: validate array inputs
this makes sure wrong parameters don't show up as core errors, as seen in crash report 12373907
closes #6642
2025-03-09 01:10:12 +00:00
95284bc9de change error identifier 2025-03-09 00:54:39 +00:00
2291546610 phpstan: added rule to ban new $class
see #6635 for rationale on why we want to get rid of this

for now, this rule will prevent this anti-feature from being used in new code
2025-03-09 00:51:12 +00:00
aad2bce9e4 readme: call out Easy tasks issues 2025-03-09 00:23:59 +00:00
09f0ce458c Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13666455727
2025-03-05 01:27:20 +00:00
50a1e59aa4 5.25.3 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/13662905668
2025-03-04 21:05:10 +00:00
e8824a36b9 Merge pull request #6645 from pmmp/explode-limit
5.25.2
2025-03-04 21:03:52 +00:00
b1e63e544f Merge branch 'stable' into explode-limit 2025-03-04 21:00:40 +00:00
9e9f8a4870 Prepare 5.25.2 release 2025-03-04 20:57:47 +00:00
d0d84d4c51 New rule: explode() limit parameter must be set 2025-03-04 20:44:01 +00:00
9382e6e5b3 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13610829991
2025-03-02 01:38:07 +00:00
e3e0c14275 Bump the github-actions group with 2 updates (#6644) 2025-03-01 10:04:01 +00:00
afac178cf4 Merge branch 'stable' into minor-next 2025-02-26 17:31:55 +00:00
e2f5e3e73c Update composer dependencies 2025-02-26 17:31:26 +00:00
3a2d0d77d1 Update composer dependencies 2025-02-26 17:30:20 +00:00
32b98dcbde draft-release: add a warning about bug reporting
too many people just spam on discord and expect that to somehow do something ...
2025-02-26 17:23:27 +00:00
092ea07d51 5.25.2 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/13549549222
2025-02-26 17:14:49 +00:00
706f391068 Release 5.25.1 (#6641) 2025-02-26 17:13:44 +00:00
7c654271a8 Bump phpstan/phpstan in the development-patch-updates group (#6640) 2025-02-25 13:40:11 +00:00
19425e35ea Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13488997706
2025-02-24 01:26:36 +00:00
3df2bdb879 Fixed door facing
this was broken in 1.21.60 update.

should've known better to expect a blockstate upgrade to mean a
blockstate fix...
2025-02-24 01:04:52 +00:00
1fed9f6cb5 BlockBreakInfo: fixed confusing error message 2025-02-23 20:02:27 +00:00
3050af0bc0 ResourcePackManager: validate pack UUIDs
fixes CrashArchive ##12248760
2025-02-23 19:45:38 +00:00
a08b06d322 also sort table by ID 2025-02-18 15:34:20 +00:00
c876253f76 tools/blockstate-upgrade-schema-utils: added dump-table command
this command dumps a human-readable version of pmmp/mapping palette mapping files to a .txt file.
may be useful for debugging issues with the schema generator or the upgrade process.
2025-02-18 15:30:39 +00:00
67272f8f2b Merge branch 'stable' of github.com:pmmp/PocketMine-MP into minor-next 2025-02-18 01:25:46 +00:00
77be5f8e25 Update PHPStan 2025-02-17 17:51:39 +00:00
9744bd7073 CraftingManager: make a less dumb hash function
fixes #4415
2025-02-17 15:35:18 +00:00
51cf6817b1 World: fixed overflow checks for getCollisionBlocks(), closes #6630 2025-02-16 23:24:39 +00:00
fd04894a7b Merge branch 'stable' of github.com:pmmp/PocketMine-MP into minor-next 2025-02-16 23:18:34 +00:00
d2d6a59c72 ItemDeserializer: fix doc comment typo 2025-02-16 22:52:11 +00:00
788ee9a008 Allow overriding deserializers for block and item IDs
there's no technical reason not to support this, since it doesn't violate any assumptions and the type returned is a base anyway.

this enables implementing stuff like snow cauldrons in a plugin, which previously would require reflection due to the minecraft:cauldron deserializer being registered already.
it also enables overriding IDs to map to custom blocks, which might be useful for overriding some functionality (although this is inadvisable - and won't alter the usage of stuff like VanillaBlocks::WHATEVER()).

we do *not* allow overriding serializers, since type IDs are expected to be paired to block implementations, and allowing them to be reassigned could lead to crashes if the new class was incorrect. So the correct approach for overriding nether portals would be to create a custom type ID as if you were adding a fully custom item. This will also allow other plugins to distinguish between your implementation and the built-in one.
2025-02-16 22:49:40 +00:00
34f801ee3c 5.25.1 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/13359320328
2025-02-16 22:18:20 +00:00
d96ef21c4d Prepare 5.25.0 release (#6631) 2025-02-16 22:16:47 +00:00
246c1776df InventoryAction: avoid throwaway Item clones 2025-02-16 21:47:35 +00:00
03e4b53ac4 BedrockDataFiles: added constants for folders as well as files
we probably should have it recurse too, but this is an easy win.
2025-02-16 20:57:16 +00:00
91ac64783f Bedrock 1.21.60 (#6627)
Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
2025-02-16 20:51:53 +00:00
9402a20ee3 Update Utils::getOS() doc comment
closes #6628
2025-02-16 16:12:29 +00:00
2670e81668 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into minor-next 2025-02-12 02:14:51 +00:00
e29aa2f337 Added resin material color (#6622) 2025-02-11 16:15:42 +00:00
5ef89200c6 Bump phpstan/phpstan in the development-patch-updates group (#6620) 2025-02-10 22:14:30 +00:00
9b3b45258a Optimise collision box checks by caching some basic info (#6606)
This PR significantly improves performance of entity movement calculation.

Previous attempts to optimise this were ineffective, as they used a cache to mitigate the cost of recomputing AABBs. Entities tend to move around randomly, so the non-cached pathway really needed to be optimized.

This change improves performance on multiple fronts:
1) avoiding Block allocations for blocks with 1x1x1 AABBs and with no AABBs (the most common)
2) avoiding Block allocations and overlapping intersection checks unless a stateID is specifically known to potentially exceed its cell boundaries (like fences)
3) avoiding overlapping AABB checks when overlaps can't make any difference anyway (cubes)

Together, these changes improve the performance of World->getBlockCollisionBoxes() by a factor of 5. In real-world terms, this shows up as a major performance improvement in situations with lots of entities moving in random directions. Testing with item entities showed an increase from 400 to 1200 moving items with the same CPU usage.

This change is built on the assumption that `Block->recalculateCollisionBoxes()` and its overrides don't interact with any world. This is technically possible due to the crappy design of the `Block` architecture, but should be avoided. As a world is not available during `RuntimeBlockStateRegistry` initialization, attempting to interact with a world during `recalculateCollisionBoxes()` will now cause a crash.

This turned out to be a problem for `ChorusPlant`, which was fixed by 70fb9bbdfd. The correct solution in this case was to use dynamic states similar to how we currently deal with fence connections.
2025-02-06 15:42:10 +00:00
ca4debbf08 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13126338048
2025-02-04 01:22:15 +00:00
39e69276a1 Bump tests/plugins/DevTools from c6dca35 to a030d39 (#6617) 2025-02-03 11:43:03 +00:00
94797e3afa Bump build/php from ae94694 to 1549433 (#6616) 2025-02-03 11:42:37 +00:00
03f98ee0a9 New version of NBT for performance improvements 2025-02-02 19:47:47 +00:00
70368ea653 Merge branch 'stable' into minor-next 2025-02-02 19:47:03 +00:00
9d6a0cc738 Update composer dependencies 2025-02-02 19:46:54 +00:00
21ccd90147 ChunkCache: parameterize dimension ID
(cc @Muqsit)
2025-02-02 19:43:04 +00:00
0a9a45a126 Improve block break progress
closes #6500

This fixes break time animations for mining fatigue and haste, and
improves the underwater and on-ground behaviour.

on-ground is still not quite right for reasons not related to this PR
(see #6547).
I'm also not quite sure the underwater logic is correct (in water vs
underwater?) but it's definitely better than what we have currently.
2025-02-02 19:34:14 +00:00
88937f1785 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13094301661
2025-02-02 01:26:08 +00:00
07987890a0 Bump the github-actions group with 2 updates (#6613) 2025-02-01 13:38:25 +00:00
e5a783cb9e Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13001707314
2025-01-28 01:21:43 +00:00
70fb9bbdfd ChorusPlant: fixed recalculateCollisionBoxes() depending on the world 2025-01-27 21:28:26 +00:00
f60120a75e Bump the development-patch-updates group with 3 updates (#6603) 2025-01-27 10:43:28 +00:00
fc86d3a44e 5.24.1 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12916833718
2025-01-22 20:50:42 +00:00
393d7f72a9 5.24.0
5.24.0 minor release
2025-01-22 20:49:38 +00:00
b625fee94b Prepare 5.24.0 release 2025-01-22 18:00:41 +00:00
6b606dca95 UPnP: better fix for postURL error that doesn't require behavioural breaks 2025-01-22 17:46:43 +00:00
406ddf3e53 Revert "Internet: make postURL() error reporting behaviour more predictable"
This reverts commit 97c5902ae2.
2025-01-22 17:44:23 +00:00
ec6077776a Merge branch 'minor-next' of github.com:pmmp/PocketMine-MP into minor-next 2025-01-22 16:45:49 +00:00
f7b5cd7ff3 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/12899221905
2025-01-22 01:23:49 +00:00
3453ff03fd 5.23.4 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12898306655
2025-01-22 00:10:10 +00:00
04e63172c3 5.23.3 (#6597) 2025-01-22 00:08:49 +00:00
51cb1875bb Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/12839491049
2025-01-18 01:19:32 +00:00
0b60a47cde Noteblock instrument changes from 1.21.50 (#6596)
Good thing this isn't saved on disk :|
2025-01-17 19:56:19 +00:00
d1066d0199 Bump build/php from 56cec11 to ae94694 (#6595) 2025-01-17 19:54:48 +00:00
f349ce75e4 Player: add ability to get & set flight speed multiplier (#6076)
Since this doesn't directly correspond to flight speed (it's multiplied by different values depending on whether sprinting or not, and possibly other states), "multiplier" was preferred instead of directly calling it flight speed.

Default value is 0.05.
2025-01-09 20:13:46 +00:00
b3f15435cc Projectile: clean up dodgy code 2025-01-08 02:31:50 +00:00
847ae26cad PHPStan: don't remember possibly-impure function return values
I don't think we get much benefit from this, and the assumption that functions with a return value are pure is sketchy.
In any case, it's better to avoid these repeated calls anyway.
2025-01-08 02:04:06 +00:00
d42ec06647 ZippedResourcePack: don't pass exception code to new exception
this is a BUT (int|string) under PHPStan, and we don't need the errors. We don't care about this code anyway.
2025-01-08 01:48:55 +00:00
5e0f03dff0 Stub PalettedBlockArray functions that work with arrays
and workaround PHPStan stupidity
2025-01-08 01:48:15 +00:00
4a83920db9 PlayerPreLoginEvent: improve array type info 2025-01-08 01:47:04 +00:00
0a16daa619 Avoid dodgy array_flip hash building
the conventional way is using array_keys and array_fill_keys. Behaviour is more predictable & also avoids benevolent union fuckery from PHPStan.
2025-01-08 01:45:28 +00:00
e34f34f9f4 Update BedrockProtocol dependency 2025-01-07 23:09:28 +00:00
e8c4b743b5 LevelDB: stop overriding types from NBT
NBT has better quality type info already
2025-01-07 22:54:10 +00:00
689a7996b9 Update NBT dependency 2025-01-07 22:51:38 +00:00
794641c0f8 Utils: split some horrifying code across multiple lines 2025-01-07 22:35:19 +00:00
9633b7d8a7 Update to PHPStan 2.x 2025-01-07 22:34:43 +00:00
d25ec58a6f AsyncPoolTest: phpdoc 2025-01-07 22:25:37 +00:00
d69a887b0d Utils: fix parameter doc for printableExceptionInfo() 2025-01-07 22:24:26 +00:00
38441a6ba3 build: avoid weak comparison 2025-01-07 22:23:16 +00:00
47cb04f6a6 tools: fix PHPStan 2.0 issues 2025-01-07 22:15:50 +00:00
b1c7fc017a CS 2025-01-07 22:13:20 +00:00
cd59e272bc PHPStan 2.0 fixes 2025-01-07 22:10:42 +00:00
7b1b35ab1f generator: fixup issues reported by PHPStan 2.0 2025-01-07 22:07:38 +00:00
28d31c97f8 Server: fixup PHPStan 2.x reported issues 2025-01-07 22:05:01 +00:00
a17512de93 Command: don't trust plugins not to pass junk 2025-01-06 23:26:13 +00:00
601be3fb33 stfu 2025-01-06 23:09:26 +00:00
2e32c50670 NetworkSession: apparently aliases are already a list at this point??? 2025-01-06 23:08:48 +00:00
d1fa6edc50 InGamePacketHandler: fix weak comparisons 2025-01-06 23:08:18 +00:00
a1ba8bc3da NetworkSession: improve PHPDoc types 2025-01-06 23:07:54 +00:00
73edb8799d SignalHandler: fixed dodgy setup logic 2025-01-06 23:06:19 +00:00
9592f066f3 PHPDoc: Restrict ReversePriorityQueue to numeric priorities 2025-01-06 23:05:49 +00:00
db9ba83001 Make some assumptions about proc_open() 2025-01-06 23:05:06 +00:00
1b2d2a3fe1 plugin: improve PHPDocs and type compliance 2025-01-06 23:04:00 +00:00
84ec8b7abe Removed dead error patterns
I do think these are PHPStan bugs, since the trait should inherit the parent class's doc comment
But for the sake of catching more bugs, these doc comments have been manually added anyway.
2025-01-06 23:02:18 +00:00
357dfb5c7e Fixed build 2025-01-06 23:01:14 +00:00
0358b7dce4 utils: avoid weak comparisons 2025-01-06 22:53:35 +00:00
97c5902ae2 Internet: make postURL() error reporting behaviour more predictable
err is now always set to null when doing a new operation.

previously, if the same var was used multiple times and a previous one failed,
code might think that a previous error belonged to the current operation.
2025-01-06 22:52:05 +00:00
9a130bce32 Config: remove bad assumptions about string root keys
these could just as easily be integers and the code should still work.
2025-01-06 22:50:25 +00:00
20849d6268 Fixed potential crashes in type ID tests
if the constants had any non-stringable values, these would blow up.
this would still be fine in the sense that the tests would fail, but better that they fail gracefully if possible.
2025-01-06 22:48:22 +00:00
b6bd3ef30c Improve PHPDocs in world package 2025-01-06 22:46:16 +00:00
c5a1c15389 TimingsCommand: beware crash on invalid timings server response 2025-01-06 22:44:17 +00:00
e30ae487dc SimpleCommandMap: ensure we always pass a list to Command::setAliases()
some offsets may have been removed if the alias failed to be registered.
2025-01-06 22:44:17 +00:00
59f6c85105 Command: mark execute $args as being list<string> 2025-01-06 22:44:17 +00:00
90f0b85d2e Eliminate weak comparisons in entity package
Weak comparisons were used in cases when we were worried about comparing int and float.

In some cases (particularly involving Vector3) we do need to be wary of this, so floatval() is used to avoid incorrect type comparisons.
In other cases, we were already exclusively comparing float-float, so weak compare wasn't needed anyway.
2025-01-06 22:44:17 +00:00
8ee70b209e MemoryDump: fix PHPDoc types 2025-01-06 22:44:16 +00:00
5c905d9a95 BlockBreakInfo: use strict comparison
weak compare isn't needed here since this can be float/float
2025-01-06 22:44:16 +00:00
8b23231537 Fixup PHPDoc for blocks 2025-01-06 22:44:16 +00:00
8e039f2711 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/12615442245
2025-01-05 01:37:22 +00:00
4a4572131f Bump shivammathur/setup-php in the github-actions group (#6591) 2025-01-04 22:26:14 +00:00
3da0b82b86 Merge branch 'stable' into minor-next 2025-01-03 19:26:45 +00:00
da62eb9f33 ... 2025-01-03 19:26:24 +00:00
09c434983b Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/12510342531
2024-12-27 01:23:47 +00:00
fbaa125d0c Bump build/php from 9728fa5 to 56cec11 (#6588)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `9728fa5` to `56cec11`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](9728fa57f7...56cec11745)

---
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>
2024-12-26 12:36:17 +00:00
6ad9dde43d Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/12498201064
2024-12-26 01:23:25 +00:00
d634a5fa3d Bump build/php from b1eaaa4 to 9728fa5 (#6587)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `b1eaaa4` to `9728fa5`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](b1eaaa48ec...9728fa57f7)

---
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>
2024-12-25 13:06:46 +00:00
8cea4c13c4 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/12487960388
2024-12-25 01:23:09 +00:00
dc2e82df7f crash.yml: add field ID for crash archive "report github issue" button 2024-12-24 16:37:18 +00:00
debf8d18fc Upgrade issue templates 2024-12-24 16:27:40 +00:00
81e3730b99 Fixed crashes containing PHP internal stack frames being flagged as plugin-caused 2024-12-24 14:20:16 +00:00
8a5eb71432 ChunkCache: track strings in cache directly instead of CompressBatchPromise
this reduces memory footprint slightly, but more importantly reduces GC workload.
Since it also reduces the work done on cache hit, it might *slightly* improve performance,
but any improvement is likely to be minimal.
2024-12-23 21:04:33 +00:00
195 changed files with 3566 additions and 1366 deletions

View File

@ -1,19 +0,0 @@
---
name: API change request
about: Suggest a change, addition or removal to the plugin API
title: ''
labels: ''
assignees: ''
---
<!--- Describe the problem you want to solve -->
## Problem description
<!--- Describe what changes you want to make to solve this problem -->
## Proposed solution
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
## Alternative solutions that don't require API changes

87
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@ -0,0 +1,87 @@
name: Bug report
description: Report a feature of PocketMine-MP not working as expected
body:
- type: markdown
attributes:
value: |
## Plugin information
> [!IMPORTANT]
> It's strongly recommended to test for bugs without plugins before reporting an issue.
> This helps avoid wasting maintainers' time on bugs that are not actually caused by PocketMine-MP.
>
> If you're not sure whether a plugin might be causing your issue, please seek help on our [Discord](https://discord.gg/bmSAZBG) before writing an issue.
- type: dropdown
attributes:
label: Plugin information
options:
- "I haven't tested without plugins"
- Bug happens without plugins
- Bug only happens with certain plugins (describe below)
validations:
required: true
- type: markdown
attributes:
value: |
## Bug description
> [!TIP]
> Helpful information to include:
> - Steps to reproduce the issue
> - Error backtraces
> - Crashdumps
> - Plugin code that triggers the issue
> - List of installed plugins (use /plugins)
> [!IMPORTANT]
> **Steps to reproduce are critical to finding the cause of the problem!**
> Without reproducing steps, the issue will probably not be solvable and may be closed.
- type: textarea
attributes:
label: Problem description
description: Describe the problem, and how you encountered it
placeholder: e.g. Steps to reproduce the issue
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: What did you expect to happen?
validations:
required: true
- type: markdown
attributes:
value: |
## Version, OS and game info
> [!WARNING]
> "Latest" is not a valid version.
> Failure to fill these fields with valid information may result in your issue being closed.
- type: input
attributes:
label: PocketMine-MP version
placeholder: Use the /version command in PocketMine-MP
validations:
required: true
- type: input
attributes:
label: PHP version
placeholder: Use the /version command in PocketMine-MP
validations:
required: true
- type: input
attributes:
label: Server OS
placeholder: Use the /version command in PocketMine-MP
validations:
required: true
- type: input
attributes:
label: Game version (if applicable)
placeholder: e.g. Android, iOS, Windows, Xbox, PS4, Switch
validations:
required: false

View File

@ -1,37 +0,0 @@
---
name: Bug report
about: Unexpected non-crash behaviour (except missing gameplay features)
title: ''
labels: ''
assignees: ''
---
### Issue description
- Expected result: What were you expecting to happen?
- Actual result: What actually happened?
### Steps to reproduce the issue
1. ...
2. ...
### OS and versions
<!-- try the `version` command | LATEST IS NOT A VALID VERSION -->
* PocketMine-MP:
* PHP:
* Using JIT: yes/no (delete as appropriate) <!-- look for the giant yellow warning in the log that says you're using JIT -->
* Server OS:
* Game version: Android/iOS/Win10/Xbox/PS4/Switch (delete as appropriate)
### Plugins
<!--- use the `plugins` command and paste the output below -->
- If you remove all plugins, does the issue still occur?
- If the issue is **not** reproducible without plugins:
- Have you asked for help on our forums before creating an issue?
- Can you provide sample, *minimal* reproducing code for the issue? If so, paste it in the bottom section
### Crashdump, backtrace or other files
<!--- Submit crashdumps at https://crash.pmmp.io and paste a link -->
<!--- Use gist or anything else to add other files and add links here -->

View File

@ -1,16 +0,0 @@
---
name: Crash
about: Report a crash in PocketMine-MP (not plugins)
title: Server crashed
labels: ''
assignees: ''
---
<!--- submit crashdump files to https://crash.pmmp.io -->
<!--- or, copy the data between ===BEGIN CRASH DUMP=== and ===END CRASH DUMP and paste it on a site like https://pastebin.com -->
<!--- DON'T JUST PASTE the crashdump into an issue -->
Link to crashdump:
<!--- write additional information about the crash to help us find the problem -->
### Additional comments (optional)

25
.github/ISSUE_TEMPLATE/crash.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Crash
description: Report a crash in PocketMine-MP (not plugins)
title: Server crashed
body:
- type: markdown
attributes:
value: |
> [!TIP]
> Submit crashdump `.log` files to the [Crash Archive](https://crash.pmmp.io/submit).
> If you can't submit the crashdump to the Crash Archive, paste it on a site like [GitHub Gist](https://gist.github.com) or [Pastebin](https://pastebin.com).
> [!CAUTION]
> DON'T paste the crashdump data directly into an issue.
- type: input
id: crashdump-url
attributes:
label: Link to crashdump
validations:
required: true
- type: textarea
attributes:
label: Additional comments (optional)
description: Any other information that might help us solve the problem

View File

@ -0,0 +1,19 @@
name: Feature addition, change, or removal
description: Propose adding new features, or changing/removing existing ones
body:
- type: textarea
attributes:
label: Problem description
description: Explain why a change is needed
validations:
required: true
- type: textarea
attributes:
label: Proposed solution
description: Describe what changes you think should be made
validations:
required: true
- type: textarea
attributes:
label: "Alternative solutions or workarounds"
description: "Describe other ways you've explored to achieve your goal"

View File

@ -12,6 +12,10 @@ updates:
update-types:
- "version-update:semver-major"
- "version-update:semver-minor"
#since we lock this to exact versions, it causes conflicts with minor-next & major-next in composer.lock
#better to just test updates to this locally anyway since almost every version breaks something
- dependency-name: phpstan/phpstan
groups:
production-patch-updates:
dependency-type: production

View File

@ -53,7 +53,7 @@ jobs:
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.15.0
with:
push: true
context: ./pocketmine-mp
@ -66,7 +66,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.15.0
with:
push: true
context: ./pocketmine-mp
@ -79,7 +79,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.15.0
with:
push: true
context: ./pocketmine-mp
@ -92,7 +92,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.15.0
with:
push: true
context: ./pocketmine-mp

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.31.1
uses: shivammathur/setup-php@2.32.0
with:
php-version: 8.2

View File

@ -49,7 +49,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@2.31.1
uses: shivammathur/setup-php@2.32.0
with:
php-version: 8.2

View File

@ -87,7 +87,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.31.1
uses: shivammathur/setup-php@2.32.0
with:
php-version: ${{ env.PHP_VERSION }}
@ -165,7 +165,7 @@ jobs:
${{ github.workspace }}/core-permissions.rst
- name: Create draft release
uses: ncipollo/release-action@v1.14.0
uses: ncipollo/release-action@v1.16.0
id: create-draft
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst
@ -182,6 +182,8 @@ jobs:
:information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}).
:warning: Found a bug? Report it on our [issue tracker](${{ github.server_url }}/${{ github.repository }}/issues). **We can't fix bugs if you don't report them.**
- name: Post draft release URL on PR
if: github.event_name == 'pull_request_target'
uses: thollander/actions-comment-pull-request@v3

View File

@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.31.1
uses: shivammathur/setup-php@2.32.0
with:
php-version: 8.2
tools: php-cs-fixer:3.49

View File

@ -20,10 +20,7 @@ jobs:
- Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io)
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG)
close-issue: true
lock-issue: false

View File

@ -65,6 +65,8 @@ PocketMine-MP accepts community contributions! The following resources will be u
* [Building and running PocketMine-MP from source](BUILDING.md)
* [Contributing Guidelines](CONTRIBUTING.md)
New here? Check out [issues with the "Easy task" label](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Easy%20task%22) for things you could work to familiarise yourself with the codebase.
## Donate
PocketMine-MP is free, but it requires a lot of time and effort from unpaid volunteers to develop. Donations enable us to keep delivering support for new versions and adding features your players love.

View File

@ -36,7 +36,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
*/
$options = [
"base_version" => VersionInfo::BASE_VERSION,
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0],
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION, limit: 2)[0],
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
"changelog_file_name" => function() : string{

View File

@ -28,6 +28,7 @@ use function dirname;
use function fclose;
use function fopen;
use function fwrite;
use function is_dir;
use function is_file;
use function scandir;
use function str_replace;
@ -59,7 +60,7 @@ foreach($files as $file){
continue;
}
$path = Path::join(BEDROCK_DATA_PATH, $file);
if(!is_file($path)){
if(!is_file($path) && !is_dir($path)){
continue;
}
@ -67,6 +68,7 @@ foreach($files as $file){
'README.md',
'LICENSE',
'composer.json',
'.github'
] as $ignored){
if($file === $ignored){
continue 2;

View File

@ -129,7 +129,7 @@ function buildPhar(string $pharPath, string $basePath, array $includedPaths, arr
}
function main() : void{
if(ini_get("phar.readonly") == 1){
if(ini_get("phar.readonly") === "1"){
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
exit(1);
}

View File

@ -127,3 +127,13 @@ Released 9th December 2024.
## Internals
- Removed legacy `build/make-release.php` script. This script is no longer used, as all releases should now follow the PR workflow.
# 5.23.3
Released 22nd January 2025.
## Fixes
- Fixed crashes with PHP internal stack frames being flagged as plugin crashes.
- Fixed note block instrument sounds in 1.21.50.
## Internals
- Updated GitHub issue templates to use issue forms.

108
changelogs/5.24.md Normal file
View File

@ -0,0 +1,108 @@
# 5.24.0
Released 22nd January 2025.
This is a minor feature release, including new gameplay features, performance improvements, and minor API additions.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## Performance
- PHP garbage collection is now managed by the server, instead of being automatically triggered by PHP.
- The mechanism for GC triggering is designed to mimic PHP's to avoid behavioural changes. Only the place it's triggered from should be significantly different.
- This change also avoids unnecessary GCs during object-heavy operations, such as encoding `CraftingDataPacket`. As such, performance during server join should see an improvement.
- Timings is now able to directly measure the impact of GC. Previously, GC would show up as random spikes under random timers, skewing timing results.
- `ChunkCache` now uses `string` for completed caches directly instead of keeping them wrapped in `CompressBatchPromise`s. This reduces memory usage, improves performance, and reduces GC workload.
## Configuration
- The following settings have been removed from `pocketmine.yml` and will no longer have any effect:
- `memory.garbage-collection.collect-async-worker` (now always `true`)
- `memory.garbage-collection.low-memory-trigger` (now always `true`)
- `memory.max-chunks.trigger-chunk-collect` (now always `true`)
- `memory.world-caches.disable-chunk-cache` (now always `true`)
- `memory.world-caches.low-memory-trigger` (now always `true`)
## Gameplay
- Added the following new blocks:
- All types of pale oak wood, and leaves
- Resin
- Resin Bricks, Slabs, Stairs, and Walls
- Resin Clump
- Chiseled Resin Bricks
- Some blocks have had their tool tier requirements adjusted to match latest Bedrock updates.
- Added the following new items:
- Resin Brick
- Music Disc - Creator
- Music Disc - Creator (Music Box)
- Music Disc - Precipice
- Music Disc - Relic
## API
### General
- Many places had their PHPDoc improved to address issues highlighted by PHPStan 2.x. This may cause new, previously unreported issues to be reported in plugins using PHPStan.
### `pocketmine`
- The following methods have been deprecated:
- `MemoryManager->canUseChunkCache()`
- `MemoryManager::dumpMemory()` - relocated to `MemoryDump` class
### `pocketmine\item`
- The following new enum cases have been added:
- `RecordType::DISK_CREATOR`
- `RecordType::DISK_CREATOR_MUSIC_BOX`
- `RecordType::DISK_PRECIPICE`
- `RecordType::DISK_RELIC`
### `pocketmine\player`
- The following new methods have been added:
- `public Player->getFlightSpeedMultiplier() : float` - a base multiplier for player's flight speed
- `public Player->setFlightSpeedMultiplier(float $flightSpeedMultiplier) : void` - sets the player's flight speed multiplier
- The following new constants have been added:
- `Player::DEFAULT_FLIGHT_SPEED_MULTIPLIER`
### `pocketmine\utils`
- The following new methods have been added:
- `public static TextFormat::javaToBedrock(string $string) : string` - removes unsupported Java Edition format codes to prevent them being incorrectly displayed on Bedrock
- The following methods have behavioural changes:
- `TextFormat::toHTML()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline). This is because the codes previously used for `STRIKETHROUGH` and `UNDERLINE` conflict with the new material codes introduced by Minecraft Bedrock.
- `Terminal::toANSI()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline), as above. However, underline and strikethrough can still be used on the terminal using `Terminal::$FORMAT_UNDERLINE` and `Terminal::$FORMAT_STRIKETHROUGH` respectively.
- The following new constants have been added:
- `TextFormat::MATERIAL_QUARTZ`
- `TextFormat::MATERIAL_IRON`
- `TextFormat::MATERIAL_NETHERITE`
- `TextFormat::MATERIAL_REDSTONE`
- `TextFormat::MATERIAL_COPPER`
- `TextFormat::MATERIAL_GOLD`
- `TextFormat::MATERIAL_EMERALD`
- `TextFormat::MATERIAL_DIAMOND`
- `TextFormat::MATERIAL_LAPIS`
- `TextFormat::MATERIAL_AMETHYST`
- The following constants have been deprecated:
- `TextFormat::STRIKETHROUGH`
- `TextFormat::UNDERLINE`
- The following static properties have been added:
- `Terminal::$COLOR_MATERIAL_QUARTZ`
- `Terminal::$COLOR_MATERIAL_IRON`
- `Terminal::$COLOR_MATERIAL_NETHERITE`
- `Terminal::$COLOR_MATERIAL_REDSTONE`
- `Terminal::$COLOR_MATERIAL_COPPER`
- `Terminal::$COLOR_MATERIAL_GOLD`
- `Terminal::$COLOR_MATERIAL_EMERALD`
- `Terminal::$COLOR_MATERIAL_DIAMOND`
- `Terminal::$COLOR_MATERIAL_LAPIS`
- `Terminal::$COLOR_MATERIAL_AMETHYST`
## Tools
- Fixed some UI issues in `tools/convert-world.php`
## Internals
- Block cache in `World` is now size-limited. This prevents memory exhaustion when plugins call `getBlock()` many thousands of times with cache misses.
- `RakLibServer` now disables PHP GC. As RakLib doesn't generate any unmanaged cycles, GC is just a waste of CPU time in this context.
- `MemoryManager` now has the responsibility for triggering cycle GC. It's checked every tick, but GC won't take place unless the GC threshold is exceeded, similar to PHP.
- This mechanism could probably do with alterations to better suit PocketMine-MP, but it was chosen to mimic PHP's own GC to minimize behavioural changes for now.
- `AsyncTask` now triggers cycle GC after `onRun()` completes. As with `MemoryManager`, this is based on a threshold designed to mimic PHP's own behaviour.
- `FormatConverter` now performs world provider GC periodically. This is not needed with current active providers, but was found to be a problem while developing custom providers.
- Various internal adjustments were made to avoid returning incorrectly-keyed arrays in the code. These changes shouldn't affect anything as the arrays should have been properly ordered anyway.
- Many places that previously used `==` and `!=` have been updated to use strict variants. This kind of change needs to be done carefully to avoid breaking `int|float` comparisons.

52
changelogs/5.25.md Normal file
View File

@ -0,0 +1,52 @@
# 5.25.0
Released 16th February 2025.
This is a support release for Minecraft: Bedrock Edition 1.21.60. It also includes some minor API additions supporting new features in this version.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.21.60.
- Removed support for earlier versions.
## Documentation
- Fixed the documentation of `Utils::getOS()`. It now refers to the `Utils::OS_*` constants instead of a list of hardcoded strings.
## API
### `pocketmine\inventory`
This release allows plugins to decide which creative tab they want to add their new items to.
It also allows creating new collapsible groups of items, and modifying or removing existing ones.
- The following new methods have been added:
- `public CreativeInventory->getAllEntries() : list<CreativeInventoryEntry>` - returns an array of objects, each containing a creative item and information about its category and collapsible group (if applicable).
- `public CreativeInventory->getEntry(int $index) : ?CreativeInventoryEntry` - returns the creative item with the specified identifier, or `null` if not found
- The following methods have signature changes:
- `CreativeInventory->add()` now accepts two additional optional parameters: `CreativeCategory $category, ?CreativeGroup $group`. If not specified, the item will be added to the Items tab without a group.
- The following new classes have been added:
- `CreativeCategory` - enum of possible creative inventory categories (each has its own tab on the GUI)
- `CreativeGroup` - contains information about a collapsible group of creative items, including tooltip text and icon
- `CreativeInventoryEntry` - contains information about a creative inventory item, including its category and collapsible group (if applicable)
## Internals
- `CreativeContentPacket` is no longer fully cached due to the requirement for translation context during construction. The individual items are still cached (which is the most expensive part), but the packet itself is now constructed on demand, and group entries are constructed on the fly. This may affect performance, but this has not been investigated.
- `BedrockDataFiles` now includes constants for folders at the top level of `BedrockData` as well as files.
- The structure of creative data in `BedrockData` was changed to accommodate item category and grouping information. `creativeitems.json` has been replaced by `creative/*.json`, which contain information about item grouping and also segregates item lists per category.
- New information was added to `required_item_list.json` in `BedrockData`, as the server is now required to send item component NBT data in some cases.
# 5.25.1
Released 26th February 2025.
## Fixes
- Fixed confusing exception message when a block-breaking tool has an efficiency value of zero.
- Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.)
- Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join.
# 5.25.2
Released 4th March 2025.
## Fixes
- Added limits to various `explode()` calls.

71
changelogs/5.26.md Normal file
View File

@ -0,0 +1,71 @@
# 5.26.0
Released 22nd March 2025.
This is a minor feature release focused on performance improvements.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## Performance
- Significantly improved performance of entity movement. Load testing with item entities showed a 3x increase in the number of entities supported without lag.
- Significantly improved performance of on-ground checks for player movement. This still needs further work, but optimisations implemented in this version should improve performance substantially.
- Updated `pocketmine/nbt` dependency with performance improvements to `TAG_Compound` and `TAG_List` comparison. This should improve performance of inventory-related actions.
- `InventoryTransaction` now avoids useless item clones when processing transactions, which should improve performance of inventory-related actions.
## Dependencies
- `pocketmine/bedrock-protocol` has been updated to `36.2.0`, which adds new functions to access some packet fields.
- `pocketmine/nbt` has been updated to `1.1.0`, which improves performance when comparing NBT object trees.
## Gameplay
- Block breaking animation speed now takes into account the following: jumping, being in water, haste, mining fatigue
## Tools
- `blockstate-upgrade-schema-utils.php` now has a new `dump-table` command, which turns a `.bin` palette table file into human-readable text for debugging.
## API
### `pocketmine\block`
- The following methods have been added:
- `public RuntimeBlockStateRegistry->hasStateId(int $stateId) : bool` - checks whether the given state ID is registered
### `pocketmine\crafting`
- The following methods have been deprecated:
- `CraftingManager::sort()` - this was implicitly internal anyway
### `pocketmine\utils`
- The following constants have been added:
- `TextFormat::MATERIAL_RESIN`
- The following static properties have been added:
- `Terminal::$COLOR_MATERIAL_RESIN`
### `pocketmine\data\bedrock\block`
- `BlockStateToObjectDeserializer` now permits overriding **deserializers** for Bedrock IDs. This may be useful to implement custom state handling, or to implement missing block variants (such as snow cauldron).
- This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
- Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
- If you want to make a custom version of a vanilla block, create a custom type ID for it, exactly as you would for a regular custom block.
- The following methods have been added:
- `public BlockStateToObjectDeserializer->getDeserializerForId(string $id) : ?(\Closure(BlockStateReader) : Block)`
### `pocketmine\data\bedrock\item`
- `ItemDeserializer` now permits overriding **deserializers** for Bedrock IDs. As above, this may be useful to implement custom data handling, or to implement missing variants of existing items.
- This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
- Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
- As above, if you want to make a custom version of a vanilla item, create a custom type ID for it, exactly as you would for a regular custom item.
- The following methods have been added:
- `public ItemDeserializer->getDeserializerForId(string $id) : ?(\Closure(SavedItemData) : Item)`
## Internals
- `new $class` is now banned on new internals code by a PHPStan rule. Closures or factory objects should be used instead for greater flexibility and better static analysis.
- `CraftingManager` now uses a more stable hash function for recipe output filtering.
- `ChunkCache` now accepts `int $dimensionId` in the constructor. This may be useful for plugins which implement the nether.
- `RuntimeBlockStateRegistry` now precomputes basic collision info about known states for fast paths.
- This permits specialization for common shapes like cubes and collisionless blocks, which allows skipping complex logic in entity movement calculation. This vastly improves performance.
- Any block whose class overrides `readStateFromWorld()` or `getModelPositionOffset()` will *not* be optimised.
- `Block->recalculateCollisionBoxes()` now has a hard requirement not to depend on anything other than available properties. It must not use `World` or its position.
- This change was problematic for `ChorusPlant`, which used nearby blocks to calculate its collision boxes.
- Blocks which need nearby blocks should override `readStateFromWorld()` and set dynamic state properties, similar to fences.
- This design flaw will be corrected with a major change to `Block` internals currently in planning for a future major version.
- `Block->getCollisionBoxes()` may not be called at all during gameplay for blocks with shapes determined to be simple, like cubes and collisionless blocks.
- `BlockStateToObjectDeserializer` now checks if the returned blockstate is registered in `RuntimeBlockStateRegistry` to promote earlier error detection (instead of crashing in random code paths).

18
changelogs/5.27.md Normal file
View File

@ -0,0 +1,18 @@
# 5.27.0
Released 27th March 2025.
This is a support release for Minecraft: Bedrock Edition 1.21.70.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## Interim releases
If you're upgrading from 5.25.x directly to 5.27.0, please also read the following changelogs, as the interim releases contain important changes:
- [5.26.0](https://github.com/pmmp/PocketMine-MP/blob/5.26.0/changelogs/5.26.md#5260) - Performance improvements and other internal improvements
## General
- Aded support for Minecraft: Bedrock Edition 1.21.70.
- Removed support for earlier versions.

View File

@ -33,18 +33,18 @@
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"netresearch/jsonmapper": "~v5.0.0",
"pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
"pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50",
"pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
"pocketmine/bedrock-data": "~4.1.0+bedrock-1.21.70",
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~37.0.0+bedrock-1.21.70",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.7.0",
"pocketmine/locale-data": "~2.22.0",
"pocketmine/locale-data": "~2.24.0",
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
"pocketmine/nbt": "~1.0.0",
"pocketmine/nbt": "~1.1.0",
"pocketmine/raklib": "~1.1.0",
"pocketmine/raklib-ipc": "~1.0.0",
"pocketmine/snooze": "^0.5.0",
@ -52,9 +52,9 @@
"symfony/filesystem": "~6.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.11.11",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpstan/phpstan": "2.1.8",
"phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^10.5.24"
},
"autoload": {

243
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "732102eca72dc1d29e7b67dfbce07653",
"content-hash": "28b4de9a23a293646dbad2707cdfd9e0",
"packages": [
{
"name": "adhocore/json-comment",
@ -67,16 +67,16 @@
},
{
"name": "brick/math",
"version": "0.12.1",
"version": "0.12.3",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1"
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1",
"url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
"shasum": ""
},
"require": {
@ -85,7 +85,7 @@
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1",
"vimeo/psalm": "5.16.0"
"vimeo/psalm": "6.8.8"
},
"type": "library",
"autoload": {
@ -115,7 +115,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.12.1"
"source": "https://github.com/brick/math/tree/0.12.3"
},
"funding": [
{
@ -123,7 +123,7 @@
"type": "github"
}
],
"time": "2023-11-29T23:19:16+00:00"
"time": "2025-02-28T13:11:00+00:00"
},
{
"name": "netresearch/jsonmapper",
@ -178,16 +178,16 @@
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "5.0.0",
"version": "5.1.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52"
"reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/2218512e4b91f5bfd09ef55f7a4c4b04e169e41a",
"reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a",
"shasum": ""
},
"type": "library",
@ -198,22 +198,22 @@
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.1.0"
},
"time": "2024-11-03T14:13:50+00:00"
"time": "2025-02-11T17:41:44+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.15.0+bedrock-1.21.50",
"version": "4.1.0+bedrock-1.21.70",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad"
"reference": "d53fe98cb3b596ac016e275df5bd5e89b04a4817"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d53fe98cb3b596ac016e275df5bd5e89b04a4817",
"reference": "d53fe98cb3b596ac016e275df5bd5e89b04a4817",
"shasum": ""
},
"type": "library",
@ -224,9 +224,9 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.50"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.70"
},
"time": "2024-12-04T12:59:12+00:00"
"time": "2025-03-25T19:43:31+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
@ -256,16 +256,16 @@
},
{
"name": "pocketmine/bedrock-protocol",
"version": "35.0.0+bedrock-1.21.50",
"version": "37.0.0+bedrock-1.21.70",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435"
"reference": "7091dad2c12ed4a4106432df21fc698960c6be9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/7091dad2c12ed4a4106432df21fc698960c6be9e",
"reference": "7091dad2c12ed4a4106432df21fc698960c6be9e",
"shasum": ""
},
"require": {
@ -278,10 +278,10 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.11.9",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5 || ^10.0"
"phpstan/phpstan": "2.1.0",
"phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
},
"type": "library",
"autoload": {
@ -296,9 +296,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.50"
"source": "https://github.com/pmmp/BedrockProtocol/tree/37.0.0+bedrock-1.21.70"
},
"time": "2024-12-04T13:02:00+00:00"
"time": "2025-03-27T15:19:36+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -471,16 +471,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.22.1",
"version": "2.24.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49"
"reference": "8f48cbe1fb5835a8bb573bed00ef04c65c26c7e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/fa4e377c437391cfcfdedd08eea3a848eabd1b49",
"reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49",
"url": "https://api.github.com/repos/pmmp/Language/zipball/8f48cbe1fb5835a8bb573bed00ef04c65c26c7e5",
"reference": "8f48cbe1fb5835a8bb573bed00ef04c65c26c7e5",
"shasum": ""
},
"type": "library",
@ -488,9 +488,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.22.1"
"source": "https://github.com/pmmp/Language/tree/2.24.1"
},
"time": "2024-12-06T14:44:17+00:00"
"time": "2025-03-16T19:04:15+00:00"
},
{
"name": "pocketmine/log",
@ -576,16 +576,16 @@
},
{
"name": "pocketmine/nbt",
"version": "1.0.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "20540271cb59e04672cb163dca73366f207974f1"
"reference": "c3c7b0a7295daeaf7873d90fed5c5d10381d12e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1",
"reference": "20540271cb59e04672cb163dca73366f207974f1",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/c3c7b0a7295daeaf7873d90fed5c5d10381d12e1",
"reference": "c3c7b0a7295daeaf7873d90fed5c5d10381d12e1",
"shasum": ""
},
"require": {
@ -595,8 +595,8 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "1.10.25",
"phpstan/phpstan-strict-rules": "^1.0",
"phpstan/phpstan": "2.1.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.5"
},
"type": "library",
@ -612,9 +612,9 @@
"description": "PHP library for working with Named Binary Tags",
"support": {
"issues": "https://github.com/pmmp/NBT/issues",
"source": "https://github.com/pmmp/NBT/tree/1.0.0"
"source": "https://github.com/pmmp/NBT/tree/1.1.1"
},
"time": "2023-07-14T13:01:49+00:00"
"time": "2025-03-09T01:46:03+00:00"
},
{
"name": "pocketmine/raklib",
@ -742,16 +742,16 @@
},
{
"name": "ramsey/collection",
"version": "2.0.0",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
"reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
"reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
"shasum": ""
},
"require": {
@ -759,25 +759,22 @@
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"ergebnis/composer-normalize": "^2.45",
"fakerphp/faker": "^1.24",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"jangregor/phpstan-prophecy": "^2.1",
"mockery/mockery": "^1.6",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
"php-parallel-lint/php-parallel-lint": "^1.4",
"phpspec/prophecy-phpunit": "^2.3",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-mockery": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5",
"ramsey/coding-standard": "^2.3",
"ramsey/conventional-commits": "^1.6",
"roave/security-advisories": "dev-latest"
},
"type": "library",
"extra": {
@ -815,19 +812,9 @@
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/2.0.0"
"source": "https://github.com/ramsey/collection/tree/2.1.0"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
"type": "tidelift"
}
],
"time": "2022-12-31T21:50:55+00:00"
"time": "2025-03-02T04:48:29+00:00"
},
{
"name": "ramsey/uuid",
@ -1013,8 +1000,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -1092,8 +1079,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -1150,16 +1137,16 @@
"packages-dev": [
{
"name": "myclabs/deep-copy",
"version": "1.12.1",
"version": "1.13.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
"shasum": ""
},
"require": {
@ -1198,7 +1185,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
},
"funding": [
{
@ -1206,20 +1193,20 @@
"type": "tidelift"
}
],
"time": "2024-11-08T17:47:46+00:00"
"time": "2025-02-12T12:17:51+00:00"
},
{
"name": "nikic/php-parser",
"version": "v5.3.1",
"version": "v5.4.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
"reference": "447a020a1f875a434d62f2a401f53b82a396e494"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
"reference": "447a020a1f875a434d62f2a401f53b82a396e494",
"shasum": ""
},
"require": {
@ -1262,9 +1249,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
},
"time": "2024-10-08T18:51:32+00:00"
"time": "2024-12-30T11:07:19+00:00"
},
{
"name": "phar-io/manifest",
@ -1386,20 +1373,20 @@
},
{
"name": "phpstan/phpstan",
"version": "1.11.11",
"version": "2.1.8",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3"
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
@ -1440,34 +1427,33 @@
"type": "github"
}
],
"time": "2024-08-19T14:37:29+00:00"
"time": "2025-03-09T09:30:48+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.4.0",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11"
"reference": "d09e152f403c843998d7a52b5d87040c937525dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d09e152f403c843998d7a52b5d87040c937525dd",
"reference": "d09e152f403c843998d7a52b5d87040c937525dd",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.11"
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.0.4"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-strict-rules": "^1.5.1",
"phpunit/phpunit": "^9.5"
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.6"
},
"type": "phpstan-extension",
"extra": {
@ -1490,34 +1476,33 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.4"
},
"time": "2024-04-20T06:39:00+00:00"
"time": "2025-01-22T13:07:38+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "1.6.0",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1"
"reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
"reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.11"
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.0.4"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^9.5"
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^9.6"
},
"type": "phpstan-extension",
"extra": {
@ -1539,9 +1524,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.0"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4"
},
"time": "2024-04-20T06:37:51+00:00"
"time": "2025-03-18T11:42:40+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1866,16 +1851,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.5.38",
"version": "10.5.45",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132"
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132",
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8",
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8",
"shasum": ""
},
"require": {
@ -1885,7 +1870,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.12.0",
"myclabs/deep-copy": "^1.12.1",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.1",
@ -1947,7 +1932,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45"
},
"funding": [
{
@ -1963,7 +1948,7 @@
"type": "tidelift"
}
],
"time": "2024-10-28T13:06:21+00:00"
"time": "2025-02-06T16:08:12+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -11,14 +11,17 @@ includes:
rules:
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
- pocketmine\phpstan\rules\DisallowDynamicNewRule
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- pocketmine\phpstan\rules\ExplodeLimitRule
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
parameters:
level: 9
checkMissingCallableSignature: true
rememberPossiblyImpureFunctionValues: false #risky to remember these, better for performance to avoid repeated calls anyway
treatPhpDocTypesAsCertain: false
bootstrapFiles:
- tests/phpstan/bootstrap.php
@ -31,6 +34,7 @@ parameters:
paths:
- build
- src
- tests/phpstan/DummyPluginOwned.php
- tests/phpstan/rules
- tests/phpunit
- tests/plugins/TesterPlugin
@ -44,6 +48,7 @@ parameters:
- pocketmine\DEBUG
- pocketmine\IS_DEVELOPMENT_BUILD
stubFiles:
- tests/phpstan/stubs/chunkutils2.stub
- tests/phpstan/stubs/JsonMapper.stub
- tests/phpstan/stubs/leveldb.stub
- tests/phpstan/stubs/pmmpthread.stub

View File

@ -31,6 +31,7 @@ use function hrtime;
use function max;
use function min;
use function number_format;
use function sprintf;
/**
* Allows threads to manually trigger the cyclic garbage collector using a threshold like PHP's own garbage collector,
@ -48,6 +49,7 @@ final class GarbageCollectorManager{
private int $threshold = self::GC_THRESHOLD_DEFAULT;
private int $collectionTimeTotalNs = 0;
private int $runs = 0;
private \Logger $logger;
private TimingsHandler $timings;
@ -96,7 +98,16 @@ final class GarbageCollectorManager{
$time = $end - $start;
$this->collectionTimeTotalNs += $time;
$this->logger->debug("gc_collect_cycles: " . number_format($time) . " ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->collectionTimeTotalNs) . " ns");
$this->runs++;
$this->logger->info(sprintf(
"Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms",
$this->runs,
number_format($time / 1_000_000, 2),
$rootsBefore,
$rootsAfter,
$cycles,
number_format($this->collectionTimeTotalNs / 1_000_000, 2)
));
return $cycles;
}

View File

@ -253,12 +253,12 @@ final class MemoryDump{
}
/**
* @param object[] $objects reference parameter
* @param int[] $refCounts reference parameter
* @param object[]|true[] $objects reference parameter
* @param int[] $refCounts reference parameter
*
* @phpstan-param array<string, object> $objects
* @phpstan-param array<string, object|true> $objects
* @phpstan-param array<string, int> $refCounts
* @phpstan-param-out array<string, object> $objects
* @phpstan-param-out array<string, object|true> $objects
* @phpstan-param-out array<string, int> $refCounts
*/
private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{

View File

@ -264,7 +264,7 @@ JIT_WARNING
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
if($composerGitHash !== null){
//we can't verify dependency versions if we were installed without using git
$currentGitHash = explode("-", VersionInfo::GIT_HASH())[0];
$currentGitHash = explode("-", VersionInfo::GIT_HASH(), 2)[0];
if($currentGitHash !== $composerGitHash){
critical_error("Composer dependencies and/or autoloader are out of sync.");
critical_error("- Current revision is $currentGitHash");

View File

@ -36,6 +36,7 @@ use pocketmine\crafting\CraftingManager;
use pocketmine\crafting\CraftingManagerFromDataHelper;
use pocketmine\crash\CrashDump;
use pocketmine\crash\CrashDumpRenderer;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\entity\EntityDataHelper;
use pocketmine\entity\Location;
use pocketmine\event\HandlerListManager;
@ -138,6 +139,7 @@ use function file_put_contents;
use function filemtime;
use function fopen;
use function get_class;
use function gettype;
use function ini_set;
use function is_array;
use function is_dir;
@ -697,7 +699,7 @@ class Server{
public function removeOp(string $name) : void{
$lowercaseName = strtolower($name);
foreach($this->operators->getAll() as $operatorName => $_){
foreach(Utils::promoteKeys($this->operators->getAll()) as $operatorName => $_){
$operatorName = (string) $operatorName;
if($lowercaseName === strtolower($operatorName)){
$this->operators->remove($operatorName);
@ -918,6 +920,7 @@ class Server{
TimingsHandler::getCollectCallbacks()->add(function() : array{
$promises = [];
foreach($this->asyncPool->getRunningWorkers() as $workerId){
/** @phpstan-var PromiseResolver<list<string>> $resolver */
$resolver = new PromiseResolver();
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
@ -1003,7 +1006,7 @@ class Server{
$this->commandMap = new SimpleCommandMap($this);
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes"));
$this->craftingManager = CraftingManagerFromDataHelper::make(BedrockDataFiles::RECIPES);
$this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger);
@ -1013,7 +1016,11 @@ class Server{
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
}
try{
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile)));
$array = yaml_parse(Filesystem::fileGetContents($graylistFile));
if(!is_array($array)){
throw new \InvalidArgumentException("Expected array for root, but have " . gettype($array));
}
$pluginGraylist = PluginGraylist::fromArray($array);
}catch(\InvalidArgumentException $e){
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
$this->forceShutdownExit();
@ -1174,7 +1181,7 @@ class Server{
if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
if(trim($default) == ""){
if(trim($default) === ""){
$this->logger->warning("level-name cannot be null, using default");
$default = "world";
$this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");

View File

@ -31,8 +31,8 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.23.3";
public const IS_DEVELOPMENT_BUILD = true;
public const BASE_VERSION = "5.27.0";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";
/**

View File

@ -70,9 +70,6 @@ class Anvil extends Transparent implements Fallable{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)];
}

View File

@ -87,9 +87,6 @@ class Bamboo extends Transparent{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
//this places the BB at the northwest corner, not the center
$inset = 1 - (($this->thick ? 3 : 2) / 16);

View File

@ -30,7 +30,6 @@ use pocketmine\block\utils\SupportType;
use pocketmine\item\Banner as ItemBanner;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
@ -97,9 +96,6 @@ abstract class BaseBanner extends Transparent{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -34,7 +34,6 @@ use pocketmine\event\block\SignChangeEvent;
use pocketmine\item\Dye;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
@ -95,9 +94,6 @@ abstract class BaseSign extends Transparent{
return 16;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -76,9 +76,6 @@ class Bed extends Transparent{
}
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
}

View File

@ -75,7 +75,10 @@ class Block{
protected BlockTypeInfo $typeInfo;
protected Position $position;
/** @var AxisAlignedBB[]|null */
/**
* @var AxisAlignedBB[]|null
* @phpstan-var list<AxisAlignedBB>|null
*/
protected ?array $collisionBoxes = null;
private int $requiredBlockItemStateDataBits;
@ -907,6 +910,7 @@ class Block{
* - anti-cheat checks in plugins
*
* @return AxisAlignedBB[]
* @phpstan-return list<AxisAlignedBB>
*/
final public function getCollisionBoxes() : array{
if($this->collisionBoxes === null){
@ -931,6 +935,7 @@ class Block{
/**
* @return AxisAlignedBB[]
* @phpstan-return list<AxisAlignedBB>
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()];

View File

@ -95,7 +95,7 @@ class BlockBreakInfo{
* Returns whether this block can be instantly broken.
*/
public function breaksInstantly() : bool{
return $this->hardness == 0.0;
return $this->hardness === 0.0;
}
/**
@ -154,7 +154,7 @@ class BlockBreakInfo{
$efficiency = $item->getMiningEfficiency(($this->toolType & $item->getBlockToolType()) !== 0);
if($efficiency <= 0){
throw new \InvalidArgumentException(get_class($item) . " has invalid mining efficiency: expected >= 0, got $efficiency");
throw new \InvalidArgumentException(get_class($item) . " must have a positive mining efficiency, but got $efficiency");
}
$base /= $efficiency;

View File

@ -43,9 +43,6 @@ class Cactus extends Transparent{
return true;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
$shrinkSize = 1 / 16;
return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)];

View File

@ -40,9 +40,6 @@ class Cake extends BaseCake{
$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()

View File

@ -36,9 +36,6 @@ class CakeWithCandle extends BaseCake{
onInteract as onInteractCandle;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()

View File

@ -36,9 +36,6 @@ class Carpet extends Flowable{
return true;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
}

View File

@ -36,9 +36,6 @@ use pocketmine\player\Player;
class Chest extends Transparent{
use FacesOppositePlacingPlayerTrait;
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
//these are slightly bigger than in PC
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];

View File

@ -34,11 +34,16 @@ use function mt_rand;
final class ChorusPlant extends Flowable{
use StaticSupportTrait;
/**
* @var true[]
* @phpstan-var array<int, true>
*/
protected array $connections = [];
protected function recalculateCollisionBoxes() : array{
$bb = AxisAlignedBB::one();
foreach($this->getAllSides() as $facing => $block){
$id = $block->getTypeId();
if($id !== BlockTypeIds::END_STONE && $id !== BlockTypeIds::CHORUS_FLOWER && !$block->hasSameTypeId($this)){
foreach(Facing::ALL as $facing){
if(!isset($this->connections[$facing])){
$bb->trim($facing, 2 / 16);
}
}
@ -46,6 +51,26 @@ final class ChorusPlant extends Flowable{
return [$bb];
}
public function readStateFromWorld() : Block{
parent::readStateFromWorld();
$this->collisionBoxes = null;
foreach(Facing::ALL as $facing){
$block = $this->getSide($facing);
if(match($block->getTypeId()){
BlockTypeIds::END_STONE, BlockTypeIds::CHORUS_FLOWER, $this->getTypeId() => true,
default => false
}){
$this->connections[$facing] = true;
}else{
unset($this->connections[$facing]);
}
}
return $this;
}
private function canBeSupportedBy(Block $block) : bool{
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
}

View File

@ -50,9 +50,6 @@ class CocoaBlock extends Flowable{
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()

View File

@ -62,9 +62,6 @@ class DaylightSensor extends Transparent{
return 300;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)];
}

View File

@ -95,9 +95,6 @@ class Door extends Transparent{
return false;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
//TODO: doors are 0.1825 blocks thick, instead of 0.1875 like JE (https://bugs.mojang.com/browse/MCPE-19214)
return [AxisAlignedBB::one()->trim($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)];

View File

@ -33,9 +33,6 @@ use pocketmine\player\Player;
class EnchantingTable extends Transparent{
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 0.25)];
}

View File

@ -50,9 +50,6 @@ class EndPortalFrame extends Opaque{
return 1;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 3 / 16)];
}

View File

@ -52,9 +52,6 @@ class EndRod extends Flowable{
return 14;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
$myAxis = Facing::axis($this->facing);

View File

@ -40,9 +40,6 @@ class EnderChest extends Transparent{
return 7;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
//these are slightly bigger than in PC
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];

View File

@ -94,9 +94,6 @@ class Farmland extends Transparent{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
}

View File

@ -54,13 +54,9 @@ class Fence extends Transparent{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
$inset = 0.5 - $this->getThickness() / 2;
/** @var AxisAlignedBB[] $bbs */
$bbs = [];
$connectWest = isset($this->connections[Facing::WEST]);

View File

@ -64,9 +64,6 @@ class FenceGate extends Transparent{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return $this->open ? [] : [AxisAlignedBB::one()->extend(Facing::UP, 0.5)->squash(Facing::axis($this->facing), 6 / 16)];
}

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
/**
@ -46,9 +45,6 @@ abstract class Flowable extends Transparent{
parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -83,9 +83,6 @@ class FlowerPot extends Flowable{
return $block->hasTypeTag(BlockTypeTags::POTTABLE_PLANTS);
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
}

View File

@ -28,7 +28,6 @@ use pocketmine\block\utils\MultiAnySupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
@ -47,9 +46,6 @@ class GlowLichen extends Transparent{
return false;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -29,9 +29,6 @@ use pocketmine\math\Facing;
class GrassPath extends Transparent{
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
}

View File

@ -58,9 +58,6 @@ class Ladder extends Transparent{
return true;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)];
}

View File

@ -59,9 +59,6 @@ class Lantern extends Transparent{
return $this->lightLevel;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()

View File

@ -30,7 +30,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\utils\Utils;
@ -89,9 +88,6 @@ abstract class Liquid extends Transparent{
return false;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -104,9 +104,6 @@ class MobHead extends Flowable{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
$collisionBox = AxisAlignedBB::one()
->contract(0.25, 0, 0.25)

View File

@ -28,7 +28,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
class NetherPortal extends Transparent{
@ -62,9 +61,6 @@ class NetherPortal extends Transparent{
return false;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -79,9 +79,6 @@ class RedstoneComparator extends Flowable{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)];
}

View File

@ -62,9 +62,6 @@ class RedstoneRepeater extends Flowable{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)];
}

View File

@ -28,6 +28,7 @@ use pocketmine\block\BlockIdentifier as BID;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use pocketmine\world\light\LightUpdate;
use function count;
use function min;
/**
@ -40,6 +41,11 @@ use function min;
class RuntimeBlockStateRegistry{
use SingletonTrait;
public const COLLISION_CUSTOM = 0;
public const COLLISION_CUBE = 1;
public const COLLISION_NONE = 2;
public const COLLISION_MAY_OVERFLOW = 3;
/**
* @var Block[]
* @phpstan-var array<int, Block>
@ -74,6 +80,13 @@ class RuntimeBlockStateRegistry{
*/
public array $blastResistance = [];
/**
* Map of state ID -> useful AABB info to avoid unnecessary block allocations
* @var int[]
* @phpstan-var array<int, int>
*/
public array $collisionInfo = [];
public function __construct(){
foreach(VanillaBlocks::getAll() as $block){
$this->register($block);
@ -100,6 +113,70 @@ class RuntimeBlockStateRegistry{
}
}
/**
* Checks if the given class method overrides a method in Block.
* Used to determine if a block might need to disable fast path optimizations.
*
* @phpstan-param anyClosure $closure
*/
private static function overridesBlockMethod(\Closure $closure) : bool{
$declarer = (new \ReflectionFunction($closure))->getClosureScopeClass();
return $declarer !== null && $declarer->getName() !== Block::class;
}
/**
* A big ugly hack to set up fast paths for handling collisions on blocks with common shapes.
* The information returned here is stored in RuntimeBlockStateRegistry->collisionInfo, and is used during entity
* collision box calculations to avoid complex logic and unnecessary block object allocations.
* This hack allows significant performance improvements.
*
* TODO: We'll want to redesign block collision box handling and block shapes in the future, but that's a job for a
* major version. For now, this hack nets major performance wins.
*/
private static function calculateCollisionInfo(Block $block) : int{
if(
self::overridesBlockMethod($block->getModelPositionOffset(...)) ||
self::overridesBlockMethod($block->readStateFromWorld(...))
){
//getModelPositionOffset() might cause AABBs to shift outside the cell
//readStateFromWorld() might cause overflow in ways we can't predict just by looking at known states
//TODO: excluding overriders of readStateFromWorld() also excludes blocks with tiles that don't do anything
//weird with their AABBs, but for now this is the best we can do.
return self::COLLISION_MAY_OVERFLOW;
}
//TODO: this could blow up if any recalculateCollisionBoxes() uses the world
//it shouldn't, but that doesn't mean that custom blocks won't...
$boxes = $block->getCollisionBoxes();
if(count($boxes) === 0){
return self::COLLISION_NONE;
}
if(
count($boxes) === 1 &&
$boxes[0]->minX === 0.0 &&
$boxes[0]->minY === 0.0 &&
$boxes[0]->minZ === 0.0 &&
$boxes[0]->maxX === 1.0 &&
$boxes[0]->maxY === 1.0 &&
$boxes[0]->maxZ === 1.0
){
return self::COLLISION_CUBE;
}
foreach($boxes as $box){
if(
$box->minX < 0 || $box->maxX > 1 ||
$box->minY < 0 || $box->maxY > 1 ||
$box->minZ < 0 || $box->maxZ > 1
){
return self::COLLISION_MAY_OVERFLOW;
}
}
return self::COLLISION_CUSTOM;
}
private function fillStaticArrays(int $index, Block $block) : void{
$fullId = $block->getStateId();
if($index !== $fullId){
@ -112,6 +189,8 @@ class RuntimeBlockStateRegistry{
if($block->blocksDirectSkyLight()){
$this->blocksDirectSkyLight[$index] = true;
}
$this->collisionInfo[$index] = self::calculateCollisionInfo($block);
}
}
@ -130,6 +209,10 @@ class RuntimeBlockStateRegistry{
return $block;
}
public function hasStateId(int $stateId) : bool{
return isset($this->fullList[$stateId]);
}
/**
* @return Block[]
* @phpstan-return array<int, Block>

View File

@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
@ -70,9 +69,6 @@ class SeaPickle extends Transparent{
return $this->underwater ? ($this->count + 1) * 3 : 0;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}

View File

@ -93,9 +93,6 @@ class Slab extends Transparent{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
if($this->slabType === SlabType::DOUBLE){
return [AxisAlignedBB::one()];

View File

@ -65,9 +65,6 @@ class SnowLayer extends Flowable implements Fallable{
return $this->layers < self::MAX_LAYERS;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
//TODO: this zero-height BB is intended to stay in lockstep with a MCPE bug
return [AxisAlignedBB::one()->trim(Facing::UP, $this->layers >= 4 ? 0.5 : 1)];

View File

@ -28,9 +28,6 @@ use pocketmine\math\Facing;
class SoulSand extends Opaque{
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 8)];
}

View File

@ -56,7 +56,6 @@ class Thin extends Transparent{
protected function recalculateCollisionBoxes() : array{
$inset = 7 / 16;
/** @var AxisAlignedBB[] $bbs */
$bbs = [];
if(isset($this->connections[Facing::WEST]) || isset($this->connections[Facing::EAST])){

View File

@ -62,9 +62,6 @@ class Trapdoor extends Transparent{
return $this;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)];
}

View File

@ -33,9 +33,6 @@ class WaterLily extends Flowable{
canBePlacedAt as supportedWhenPlacedAt;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
}

View File

@ -62,9 +62,10 @@ class Sign extends Spawnable{
/**
* @return string[]
* @deprecated
*/
public static function fixTextBlob(string $blob) : array{
return array_slice(array_pad(explode("\n", $blob), 4, ""), 0, 4);
return array_slice(array_pad(explode("\n", $blob, limit: 5), 4, ""), 0, 4);
}
protected SignText $text;

View File

@ -43,7 +43,10 @@ trait CandleTrait{
return $this->lit ? 3 : 0;
}
/** @see Block::onInteract() */
/**
* @param Item[] &$returnedItems
* @see Block::onInteract()
*/
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($this->lit){

View File

@ -30,9 +30,15 @@ interface CopperMaterial{
public function getOxidation() : CopperOxidation;
/**
* @return $this
*/
public function setOxidation(CopperOxidation $oxidation) : CopperMaterial;
public function isWaxed() : bool;
/**
* @return $this
*/
public function setWaxed(bool $waxed) : CopperMaterial;
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\Block;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Axe;
use pocketmine\item\Item;
@ -58,6 +59,10 @@ trait CopperTrait{
return $this;
}
/**
* @param Item[] &$returnedItems
* @see Block::onInteract()
*/
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if(!$this->waxed && $item->getTypeId() === ItemTypeIds::HONEYCOMB){
$this->waxed = true;

View File

@ -79,7 +79,7 @@ class SignText{
* @throws \InvalidArgumentException if the text is not valid UTF-8
*/
public static function fromBlob(string $blob, ?Color $baseColor = null, bool $glowing = false) : SignText{
return new self(array_slice(array_pad(explode("\n", $blob), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing);
return new self(array_slice(array_pad(explode("\n", $blob, limit: self::LINE_COUNT + 1), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing);
}
/**

View File

@ -33,9 +33,11 @@ use pocketmine\permission\PermissionManager;
use pocketmine\Server;
use pocketmine\utils\BroadcastLoggerForwarder;
use pocketmine\utils\TextFormat;
use function array_values;
use function explode;
use function implode;
use function str_replace;
use const PHP_INT_MAX;
abstract class Command{
@ -80,6 +82,7 @@ abstract class Command{
/**
* @param string[] $args
* @phpstan-param list<string> $args
*
* @return mixed
* @throws CommandException
@ -111,7 +114,7 @@ abstract class Command{
}
public function setPermission(?string $permission) : void{
$this->setPermissions($permission === null ? [] : explode(";", $permission));
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
}
public function testPermission(CommandSender $target, ?string $permission = null) : bool{
@ -212,6 +215,7 @@ abstract class Command{
* @phpstan-param list<string> $aliases
*/
public function setAliases(array $aliases) : void{
$aliases = array_values($aliases); //because plugins can and will pass crap
$this->aliases = $aliases;
if(!$this->isRegistered()){
$this->activeAliases = $aliases;

View File

@ -121,6 +121,7 @@ class FormattedCommandAlias extends Command{
/**
* @param string[] $args
* @phpstan-param list<string> $args
*/
private function buildCommand(string $formatString, array $args) : ?string{
$index = 0;

View File

@ -73,6 +73,7 @@ use pocketmine\timings\Timings;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function array_shift;
use function array_values;
use function count;
use function implode;
use function str_contains;
@ -163,7 +164,7 @@ class SimpleCommandMap implements CommandMap{
unset($aliases[$index]);
}
}
$command->setAliases($aliases);
$command->setAliases(array_values($aliases));
if(!$registered){
$command->setLabel($fallbackPrefix . ":" . $label);

View File

@ -39,6 +39,7 @@ use function ksort;
use function min;
use function sort;
use function strtolower;
use const PHP_INT_MAX;
use const SORT_FLAG_CASE;
use const SORT_NATURAL;
@ -108,7 +109,7 @@ class HelpCommand extends VanillaCommand{
$usage = $cmd->getUsage();
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString)))
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
->prefix(TextFormat::GOLD));
$aliases = $cmd->getAliases();

View File

@ -219,7 +219,11 @@ class ParticleCommand extends VanillaCommand{
break;
case "blockdust":
if($data !== null){
$d = explode("_", $data);
//to preserve the old unlimited explode behaviour, allow this to split into at most 5 parts
//this allows the 4th argument to be processed normally if given without forcing it to also consume
//any unexpected parts
//we probably ought to error in this case, but this will do for now
$d = explode("_", $data, limit: 5);
if(count($d) >= 3){
return new DustParticle(new Color(
((int) $d[0]) & 0xff,

View File

@ -46,6 +46,8 @@ use function fwrite;
use function http_build_query;
use function implode;
use function is_array;
use function is_int;
use function is_string;
use function json_decode;
use function mkdir;
use function strtolower;
@ -178,7 +180,7 @@ class TimingsCommand extends VanillaCommand{
return;
}
$response = json_decode($result->getBody(), true);
if(is_array($response) && isset($response["id"])){
if(is_array($response) && isset($response["id"]) && (is_int($response["id"]) || is_string($response["id"]))){
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
"https://" . $host . "/?id=" . $response["id"]));
}else{

View File

@ -51,9 +51,8 @@ final class CommandStringHelper{
foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){
/** @var string $match */ //phpstan can't understand preg_match and friends by itself :(
$match = $matches[$i][$k];
$args[(int) $k] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
$args[] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
break;
}
}

View File

@ -62,7 +62,7 @@ class ConsoleCommandSender implements CommandSender{
$message = $this->getLanguage()->translate($message);
}
foreach(explode("\n", trim($message)) as $line){
foreach(explode("\n", trim($message), limit: PHP_INT_MAX) as $line){
Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::addBase(TextFormat::WHITE, $line));
}
}

View File

@ -29,8 +29,12 @@ use pocketmine\nbt\TreeRoot;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\DestructorCallbackTrait;
use pocketmine\utils\ObjectSet;
use function array_shift;
use function count;
use function implode;
use function ksort;
use function spl_object_id;
use function usort;
use const SORT_STRING;
class CraftingManager{
use DestructorCallbackTrait;
@ -100,6 +104,7 @@ class CraftingManager{
/**
* Function used to arrange Shapeless Recipe ingredient lists into a consistent order.
* @deprecated
*/
public static function sort(Item $i1, Item $i2) : int{
//Use spaceship operator to compare each property, then try the next one if they are equivalent.
@ -108,47 +113,30 @@ class CraftingManager{
return $retval;
}
/**
* @param Item[] $items
* @phpstan-param list<Item> $items
*
* @return Item[]
* @phpstan-return list<Item>
*/
private static function pack(array $items) : array{
$result = [];
private static function hashOutput(Item $output) : string{
$write = new BinaryStream();
$write->putVarInt($output->getStateId());
$write->put((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
foreach($items as $item){
foreach($result as $otherItem){
if($item->canStackWith($otherItem)){
$otherItem->setCount($otherItem->getCount() + $item->getCount());
continue 2;
}
}
//No matching item found
$result[] = clone $item;
}
return $result;
return $write->getBuffer();
}
/**
* @param Item[] $outputs
* @phpstan-param list<Item> $outputs
*/
private static function hashOutputs(array $outputs) : string{
$outputs = self::pack($outputs);
usort($outputs, [self::class, "sort"]);
$result = new BinaryStream();
if(count($outputs) === 1){
return self::hashOutput(array_shift($outputs));
}
$unique = [];
foreach($outputs as $o){
//count is not written because the outputs might be from multiple repetitions of a single recipe
//this reduces the accuracy of the hash, but it won't matter in most cases.
$result->putVarInt($o->getStateId());
$result->put((new LittleEndianNbtSerializer())->write(new TreeRoot($o->getNamedTag())));
$hash = self::hashOutput($o);
$unique[$hash] = $hash;
}
return $result->getBuffer();
ksort($unique, SORT_STRING);
return implode("", $unique);
}
/**

View File

@ -97,6 +97,7 @@ class ShapedRecipe implements CraftingRecipe{
$this->shape = $shape;
Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{});
foreach(Utils::stringifyKeys($ingredients) as $char => $i){
if(!str_contains(implode($this->shape), $char)){
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
@ -105,6 +106,7 @@ class ShapedRecipe implements CraftingRecipe{
$this->ingredientList[$char] = clone $i;
}
Utils::validateArrayValueType($results, function(Item $_) : void{});
$this->results = Utils::cloneObjectArray($results);
}

View File

@ -53,7 +53,9 @@ class ShapelessRecipe implements CraftingRecipe{
if(count($ingredients) > 9){
throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients");
}
Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{});
$this->ingredients = $ingredients;
Utils::validateArrayValueType($results, function(Item $_) : void{});
$this->results = Utils::cloneObjectArray($results);
}

View File

@ -39,14 +39,16 @@ final class BedrockDataFiles{
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 CREATIVE = BEDROCK_DATA_PATH . '/creative';
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 ENUMS = BEDROCK_DATA_PATH . '/enums';
public const ENUMS_PY = BEDROCK_DATA_PATH . '/enums.py';
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 PROTOCOL_INFO_JSON = BEDROCK_DATA_PATH . '/protocol_info.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 RECIPES = BEDROCK_DATA_PATH . '/recipes';
public const REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json';
}

View File

@ -39,10 +39,10 @@ final class NoteInstrumentIdMap{
NoteInstrument::SNARE => 2,
NoteInstrument::CLICKS_AND_STICKS => 3,
NoteInstrument::DOUBLE_BASS => 4,
NoteInstrument::BELL => 5,
NoteInstrument::FLUTE => 6,
NoteInstrument::CHIME => 7,
NoteInstrument::GUITAR => 8,
NoteInstrument::FLUTE => 5,
NoteInstrument::BELL => 6,
NoteInstrument::GUITAR => 7,
NoteInstrument::CHIME => 8,
NoteInstrument::XYLOPHONE => 9,
NoteInstrument::IRON_XYLOPHONE => 10,
NoteInstrument::COW_BELL => 11,

View File

@ -45,7 +45,7 @@ final class BlockStateData{
public const CURRENT_VERSION =
(1 << 24) | //major
(21 << 16) | //minor
(40 << 8) | //patch
(70 << 8) | //patch
(1); //revision
public const TAG_NAME = "name";

View File

@ -59,6 +59,7 @@ final class BlockStateNames{
public const COVERED_BIT = "covered_bit";
public const CRACKED_STATE = "cracked_state";
public const CRAFTING = "crafting";
public const CREAKING_HEART_STATE = "creaking_heart_state";
public const DEAD_BIT = "dead_bit";
public const DEPRECATED = "deprecated";
public const DIRECTION = "direction";

View File

@ -56,6 +56,10 @@ final class BlockStateStringValues{
public const CRACKED_STATE_MAX_CRACKED = "max_cracked";
public const CRACKED_STATE_NO_CRACKS = "no_cracks";
public const CREAKING_HEART_STATE_AWAKE = "awake";
public const CREAKING_HEART_STATE_DORMANT = "dormant";
public const CREAKING_HEART_STATE_UPROOTED = "uprooted";
public const DRIPSTONE_THICKNESS_BASE = "base";
public const DRIPSTONE_THICKNESS_FRUSTUM = "frustum";
public const DRIPSTONE_THICKNESS_MERGE = "merge";

View File

@ -175,7 +175,9 @@ final class BlockTypeNames{
public const BUBBLE_CORAL_FAN = "minecraft:bubble_coral_fan";
public const BUBBLE_CORAL_WALL_FAN = "minecraft:bubble_coral_wall_fan";
public const BUDDING_AMETHYST = "minecraft:budding_amethyst";
public const BUSH = "minecraft:bush";
public const CACTUS = "minecraft:cactus";
public const CACTUS_FLOWER = "minecraft:cactus_flower";
public const CAKE = "minecraft:cake";
public const CALCITE = "minecraft:calcite";
public const CALIBRATED_SCULK_SENSOR = "minecraft:calibrated_sculk_sensor";
@ -545,6 +547,7 @@ final class BlockTypeNames{
public const FIRE_CORAL_BLOCK = "minecraft:fire_coral_block";
public const FIRE_CORAL_FAN = "minecraft:fire_coral_fan";
public const FIRE_CORAL_WALL_FAN = "minecraft:fire_coral_wall_fan";
public const FIREFLY_BUSH = "minecraft:firefly_bush";
public const FLETCHING_TABLE = "minecraft:fletching_table";
public const FLOWER_POT = "minecraft:flower_pot";
public const FLOWERING_AZALEA = "minecraft:flowering_azalea";
@ -685,6 +688,7 @@ final class BlockTypeNames{
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
public const LARGE_FERN = "minecraft:large_fern";
public const LAVA = "minecraft:lava";
public const LEAF_LITTER = "minecraft:leaf_litter";
public const LECTERN = "minecraft:lectern";
public const LEVER = "minecraft:lever";
public const LIGHT_BLOCK_0 = "minecraft:light_block_0";
@ -1043,6 +1047,7 @@ final class BlockTypeNames{
public const SEA_LANTERN = "minecraft:sea_lantern";
public const SEA_PICKLE = "minecraft:sea_pickle";
public const SEAGRASS = "minecraft:seagrass";
public const SHORT_DRY_GRASS = "minecraft:short_dry_grass";
public const SHORT_GRASS = "minecraft:short_grass";
public const SHROOMLIGHT = "minecraft:shroomlight";
public const SILVER_GLAZED_TERRACOTTA = "minecraft:silver_glazed_terracotta";
@ -1140,6 +1145,7 @@ final class BlockTypeNames{
public const SUSPICIOUS_GRAVEL = "minecraft:suspicious_gravel";
public const SUSPICIOUS_SAND = "minecraft:suspicious_sand";
public const SWEET_BERRY_BUSH = "minecraft:sweet_berry_bush";
public const TALL_DRY_GRASS = "minecraft:tall_dry_grass";
public const TALL_GRASS = "minecraft:tall_grass";
public const TARGET = "minecraft:target";
public const TINTED_GLASS = "minecraft:tinted_glass";
@ -1267,6 +1273,7 @@ final class BlockTypeNames{
public const WHITE_TERRACOTTA = "minecraft:white_terracotta";
public const WHITE_TULIP = "minecraft:white_tulip";
public const WHITE_WOOL = "minecraft:white_wool";
public const WILDFLOWERS = "minecraft:wildflowers";
public const WITHER_ROSE = "minecraft:wither_rose";
public const WITHER_SKELETON_SKULL = "minecraft:wither_skeleton_skull";
public const WOODEN_BUTTON = "minecraft:wooden_button";

View File

@ -214,6 +214,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerLeavesSerializers();
$this->registerSaplingSerializers();
$this->registerMobHeadSerializers();
$this->registerCopperSerializers();
$this->registerSimpleSerializers();
$this->registerSerializers();
}
@ -791,6 +792,178 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
})->writeFacingWithoutDown($block->getFacing()));
}
private function registerCopperSerializers() : void{
$this->map(Blocks::COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) :
Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER)
);
});
$this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_CHISELED_COPPER,
Ids::WAXED_EXPOSED_CHISELED_COPPER,
Ids::WAXED_WEATHERED_CHISELED_COPPER,
Ids::WAXED_OXIDIZED_CHISELED_COPPER
) :
Helper::selectCopperId($oxidation,
Ids::CHISELED_COPPER,
Ids::EXPOSED_CHISELED_COPPER,
Ids::WEATHERED_CHISELED_COPPER,
Ids::OXIDIZED_CHISELED_COPPER
)
);
});
$this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_GRATE,
Ids::WAXED_EXPOSED_COPPER_GRATE,
Ids::WAXED_WEATHERED_COPPER_GRATE,
Ids::WAXED_OXIDIZED_COPPER_GRATE
) :
Helper::selectCopperId($oxidation,
Ids::COPPER_GRATE,
Ids::EXPOSED_COPPER_GRATE,
Ids::WEATHERED_COPPER_GRATE,
Ids::OXIDIZED_COPPER_GRATE
)
);
});
$this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) :
Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER)
);
});
$this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeSlab(
$block,
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_SLAB,
Ids::EXPOSED_CUT_COPPER_SLAB,
Ids::WEATHERED_CUT_COPPER_SLAB,
Ids::OXIDIZED_CUT_COPPER_SLAB
)
),
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::DOUBLE_CUT_COPPER_SLAB,
Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB
)
)
);
});
$this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeStairs(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_STAIRS,
Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS,
Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS,
Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_STAIRS,
Ids::EXPOSED_CUT_COPPER_STAIRS,
Ids::WEATHERED_CUT_COPPER_STAIRS,
Ids::OXIDIZED_CUT_COPPER_STAIRS
)
)
);
});
$this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{
$oxidation = $block->getOxidation();
return Writer::create($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_BULB,
Ids::WAXED_EXPOSED_COPPER_BULB,
Ids::WAXED_WEATHERED_COPPER_BULB,
Ids::WAXED_OXIDIZED_COPPER_BULB) :
Helper::selectCopperId($oxidation,
Ids::COPPER_BULB,
Ids::EXPOSED_COPPER_BULB,
Ids::WEATHERED_COPPER_BULB,
Ids::OXIDIZED_COPPER_BULB
))
->writeBool(StateNames::LIT, $block->isLit())
->writeBool(StateNames::POWERED_BIT, $block->isPowered());
});
$this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeDoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_DOOR,
Ids::WAXED_EXPOSED_COPPER_DOOR,
Ids::WAXED_WEATHERED_COPPER_DOOR,
Ids::WAXED_OXIDIZED_COPPER_DOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_DOOR,
Ids::EXPOSED_COPPER_DOOR,
Ids::WEATHERED_COPPER_DOOR,
Ids::OXIDIZED_COPPER_DOOR
)
)
);
});
$this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeTrapdoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_TRAPDOOR,
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_TRAPDOOR,
Ids::EXPOSED_COPPER_TRAPDOOR,
Ids::WEATHERED_COPPER_TRAPDOOR,
Ids::OXIDIZED_COPPER_TRAPDOOR
)
)
);
});
}
private function registerSimpleSerializers() : void{
$this->mapSimple(Blocks::AIR(), Ids::AIR);
$this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK);
@ -1265,175 +1438,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS);
$this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL)));
$this->map(Blocks::COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) :
Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER)
);
});
$this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_CHISELED_COPPER,
Ids::WAXED_EXPOSED_CHISELED_COPPER,
Ids::WAXED_WEATHERED_CHISELED_COPPER,
Ids::WAXED_OXIDIZED_CHISELED_COPPER
) :
Helper::selectCopperId($oxidation,
Ids::CHISELED_COPPER,
Ids::EXPOSED_CHISELED_COPPER,
Ids::WEATHERED_CHISELED_COPPER,
Ids::OXIDIZED_CHISELED_COPPER
)
);
});
$this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_GRATE,
Ids::WAXED_EXPOSED_COPPER_GRATE,
Ids::WAXED_WEATHERED_COPPER_GRATE,
Ids::WAXED_OXIDIZED_COPPER_GRATE
) :
Helper::selectCopperId($oxidation,
Ids::COPPER_GRATE,
Ids::EXPOSED_COPPER_GRATE,
Ids::WEATHERED_COPPER_GRATE,
Ids::OXIDIZED_COPPER_GRATE
)
);
});
$this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) :
Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER)
);
});
$this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeSlab(
$block,
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_SLAB,
Ids::EXPOSED_CUT_COPPER_SLAB,
Ids::WEATHERED_CUT_COPPER_SLAB,
Ids::OXIDIZED_CUT_COPPER_SLAB
)
),
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::DOUBLE_CUT_COPPER_SLAB,
Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB
)
)
);
});
$this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeStairs(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_STAIRS,
Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS,
Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS,
Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_STAIRS,
Ids::EXPOSED_CUT_COPPER_STAIRS,
Ids::WEATHERED_CUT_COPPER_STAIRS,
Ids::OXIDIZED_CUT_COPPER_STAIRS
)
)
);
});
$this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{
$oxidation = $block->getOxidation();
return Writer::create($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_BULB,
Ids::WAXED_EXPOSED_COPPER_BULB,
Ids::WAXED_WEATHERED_COPPER_BULB,
Ids::WAXED_OXIDIZED_COPPER_BULB) :
Helper::selectCopperId($oxidation,
Ids::COPPER_BULB,
Ids::EXPOSED_COPPER_BULB,
Ids::WEATHERED_COPPER_BULB,
Ids::OXIDIZED_COPPER_BULB
))
->writeBool(StateNames::LIT, $block->isLit())
->writeBool(StateNames::POWERED_BIT, $block->isPowered());
});
$this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeDoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_DOOR,
Ids::WAXED_EXPOSED_COPPER_DOOR,
Ids::WAXED_WEATHERED_COPPER_DOOR,
Ids::WAXED_OXIDIZED_COPPER_DOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_DOOR,
Ids::EXPOSED_COPPER_DOOR,
Ids::WEATHERED_COPPER_DOOR,
Ids::OXIDIZED_COPPER_DOOR
)
)
);
});
$this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeTrapdoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_TRAPDOOR,
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_TRAPDOOR,
Ids::EXPOSED_COPPER_TRAPDOOR,
Ids::WEATHERED_COPPER_TRAPDOOR,
Ids::OXIDIZED_COPPER_TRAPDOOR
)
)
);
});
$this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{
return Writer::create(Ids::COCOA)
->writeInt(StateNames::AGE, $block->getAge())

View File

@ -126,12 +126,19 @@ final class BlockStateDeserializerHelper{
->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15));
}
/** @throws BlockStateDeserializeException */
/**
* @phpstan-template TDoor of Door
* @phpstan-param TDoor $block
* @phpstan-return TDoor
*
* @throws BlockStateDeserializeException
*/
public static function decodeDoor(Door $block, BlockStateReader $in) : Door{
//TODO: check if these need any special treatment to get the appropriate data to both halves of the door
return $block
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT))
->setFacing(Facing::rotateY($in->readLegacyHorizontalFacing(), false))
//a door facing "east" is actually facing north - thanks mojang
->setFacing(Facing::rotateY($in->readCardinalHorizontalFacing(), clockwise: false))
->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT))
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}
@ -145,7 +152,7 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{
return $block
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setInWall($in->readBool(BlockStateNames::IN_WALL_BIT))
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}
@ -236,18 +243,36 @@ final class BlockStateDeserializerHelper{
return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0);
}
/** @throws BlockStateDeserializeException */
/**
* @phpstan-template TSlab of Slab
* @phpstan-param TSlab $block
* @phpstan-return TSlab
*
* @throws BlockStateDeserializeException
*/
public static function decodeSingleSlab(Slab $block, BlockStateReader $in) : Slab{
return $block->setSlabType($in->readSlabPosition());
}
/** @throws BlockStateDeserializeException */
/**
* @phpstan-template TSlab of Slab
* @phpstan-param TSlab $block
* @phpstan-return TSlab
*
* @throws BlockStateDeserializeException
*/
public static function decodeDoubleSlab(Slab $block, BlockStateReader $in) : Slab{
$in->ignored(StateNames::MC_VERTICAL_HALF);
return $block->setSlabType(SlabType::DOUBLE);
}
/** @throws BlockStateDeserializeException */
/**
* @phpstan-template TStair of Stair
* @phpstan-param TStair $block
* @phpstan-return TStair
*
* @throws BlockStateDeserializeException
*/
public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{
return $block
->setUpsideDown($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT))
@ -264,7 +289,13 @@ final class BlockStateDeserializerHelper{
->setFacing($facing === Facing::DOWN ? Facing::UP : $facing);
}
/** @throws BlockStateDeserializeException */
/**
* @phpstan-template TTrapdoor of Trapdoor
* @phpstan-param TTrapdoor $block
* @phpstan-return TTrapdoor
*
* @throws BlockStateDeserializeException
*/
public static function decodeTrapdoor(Trapdoor $block, BlockStateReader $in) : Trapdoor{
return $block
->setFacing($in->read5MinusHorizontalFacing())

View File

@ -100,7 +100,8 @@ final class BlockStateSerializerHelper{
public static function encodeDoor(Door $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
->writeLegacyHorizontalFacing(Facing::rotateY($block->getFacing(), true))
//a door facing north is encoded as "east"
->writeCardinalHorizontalFacing(Facing::rotateY($block->getFacing(), clockwise: true))
->writeBool(BlockStateNames::DOOR_HINGE_BIT, $block->isHingeRight())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
@ -112,7 +113,7 @@ final class BlockStateSerializerHelper{
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
return $out
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}

View File

@ -33,11 +33,13 @@ use pocketmine\block\DoublePitcherCrop;
use pocketmine\block\Opaque;
use pocketmine\block\PinkPetals;
use pocketmine\block\PitcherCrop;
use pocketmine\block\RuntimeBlockStateRegistry;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\SweetBerryBush;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DirtType;
@ -59,6 +61,7 @@ use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper
use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\utils\Utils;
use function array_key_exists;
use function count;
use function min;
@ -87,6 +90,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerSaplingDeserializers();
$this->registerLightDeserializers();
$this->registerMobHeadDeserializers();
$this->registerCopperDeserializers();
$this->registerSimpleDeserializers();
$this->registerDeserializers();
}
@ -94,19 +98,37 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
public function deserialize(BlockStateData $stateData) : int{
if(count($stateData->getStates()) === 0){
//if a block has zero properties, we can keep a map of string ID -> internal blockstate ID
return $this->simpleCache[$stateData->getName()] ??= $this->deserializeBlock($stateData)->getStateId();
return $this->simpleCache[$stateData->getName()] ??= $this->deserializeToStateId($stateData);
}
//we can't cache blocks that have properties - go ahead and deserialize the slow way
return $this->deserializeBlock($stateData)->getStateId();
return $this->deserializeToStateId($stateData);
}
private function deserializeToStateId(BlockStateData $stateData) : int{
$stateId = $this->deserializeBlock($stateData)->getStateId();
//plugin devs seem to keep missing this and causing core crashes, so we need to verify this at the earliest
//available opportunity
if(!RuntimeBlockStateRegistry::getInstance()->hasStateId($stateId)){
throw new \LogicException("State ID $stateId returned by deserializer for " . $stateData->getName() . " is not registered in RuntimeBlockStateRegistry");
}
return $stateId;
}
/** @phpstan-param \Closure(Reader) : Block $c */
public function map(string $id, \Closure $c) : void{
if(array_key_exists($id, $this->deserializeFuncs)){
throw new \InvalidArgumentException("Deserializer is already assigned for \"$id\"");
}
$this->deserializeFuncs[$id] = $c;
$this->simpleCache = [];
}
/**
* Returns the existing data deserializer for the given ID, or null if none exists.
* This may be useful if you need to override a deserializer, but still want to be able to fall back to the original.
*
* @phpstan-return ?\Closure(Reader) : Block
*/
public function getDeserializerForId(string $id) : ?\Closure{
return $this->deserializeFuncs[$id] ?? null;
}
/** @phpstan-param \Closure() : Block $getBlock */
@ -715,6 +737,150 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}
}
/**
* @phpstan-param \Closure(Reader) : (CopperMaterial&Block) $deserializer
*/
private function mapCopper(
string $normalId,
string $waxedNormalId,
string $exposedId,
string $waxedExposedId,
string $weatheredId,
string $waxedWeatheredId,
string $oxidizedId,
string $waxedOxidizedId,
\Closure $deserializer
) : void{
foreach(Utils::stringifyKeys([
$normalId => [CopperOxidation::NONE, false],
$waxedNormalId => [CopperOxidation::NONE, true],
$exposedId => [CopperOxidation::EXPOSED, false],
$waxedExposedId => [CopperOxidation::EXPOSED, true],
$weatheredId => [CopperOxidation::WEATHERED, false],
$waxedWeatheredId => [CopperOxidation::WEATHERED, true],
$oxidizedId => [CopperOxidation::OXIDIZED, false],
$waxedOxidizedId => [CopperOxidation::OXIDIZED, true],
]) as $id => [$oxidation, $waxed]){
$this->map($id, fn(Reader $in) => $deserializer($in)->setOxidation($oxidation)->setWaxed($waxed));
}
}
private function registerCopperDeserializers() : void{
$this->mapCopper(
Ids::CUT_COPPER_SLAB,
Ids::WAXED_CUT_COPPER_SLAB,
Ids::EXPOSED_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_CUT_COPPER_SLAB,
Ids::WEATHERED_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_CUT_COPPER_SLAB,
Ids::OXIDIZED_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB,
fn(Reader $in) => Helper::decodeSingleSlab(Blocks::CUT_COPPER_SLAB(), $in)
);
$this->mapCopper(
Ids::DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_DOUBLE_CUT_COPPER_SLAB,
Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB,
fn(Reader $in) => Helper::decodeDoubleSlab(Blocks::CUT_COPPER_SLAB(), $in)
);
$this->mapCopper(
Ids::COPPER_BULB,
Ids::WAXED_COPPER_BULB,
Ids::EXPOSED_COPPER_BULB,
Ids::WAXED_EXPOSED_COPPER_BULB,
Ids::WEATHERED_COPPER_BULB,
Ids::WAXED_WEATHERED_COPPER_BULB,
Ids::OXIDIZED_COPPER_BULB,
Ids::WAXED_OXIDIZED_COPPER_BULB,
fn(Reader $in) => Blocks::COPPER_BULB()
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT))
);
$this->mapCopper(
Ids::COPPER_DOOR,
Ids::WAXED_COPPER_DOOR,
Ids::EXPOSED_COPPER_DOOR,
Ids::WAXED_EXPOSED_COPPER_DOOR,
Ids::WEATHERED_COPPER_DOOR,
Ids::WAXED_WEATHERED_COPPER_DOOR,
Ids::OXIDIZED_COPPER_DOOR,
Ids::WAXED_OXIDIZED_COPPER_DOOR,
fn(Reader $in) => Helper::decodeDoor(Blocks::COPPER_DOOR(), $in)
);
$this->mapCopper(
Ids::COPPER_TRAPDOOR,
Ids::WAXED_COPPER_TRAPDOOR,
Ids::EXPOSED_COPPER_TRAPDOOR,
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
Ids::WEATHERED_COPPER_TRAPDOOR,
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
Ids::OXIDIZED_COPPER_TRAPDOOR,
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR,
fn(Reader $in) => Helper::decodeTrapdoor(Blocks::COPPER_TRAPDOOR(), $in)
);
$this->mapCopper(
Ids::COPPER_BLOCK,
Ids::WAXED_COPPER,
Ids::EXPOSED_COPPER,
Ids::WAXED_EXPOSED_COPPER,
Ids::WEATHERED_COPPER,
Ids::WAXED_WEATHERED_COPPER,
Ids::OXIDIZED_COPPER,
Ids::WAXED_OXIDIZED_COPPER,
fn(Reader $in) => Blocks::COPPER()
);
$this->mapCopper(
Ids::CHISELED_COPPER,
Ids::WAXED_CHISELED_COPPER,
Ids::EXPOSED_CHISELED_COPPER,
Ids::WAXED_EXPOSED_CHISELED_COPPER,
Ids::WEATHERED_CHISELED_COPPER,
Ids::WAXED_WEATHERED_CHISELED_COPPER,
Ids::OXIDIZED_CHISELED_COPPER,
Ids::WAXED_OXIDIZED_CHISELED_COPPER,
fn(Reader $in) => Blocks::CHISELED_COPPER()
);
$this->mapCopper(
Ids::COPPER_GRATE,
Ids::WAXED_COPPER_GRATE,
Ids::EXPOSED_COPPER_GRATE,
Ids::WAXED_EXPOSED_COPPER_GRATE,
Ids::WEATHERED_COPPER_GRATE,
Ids::WAXED_WEATHERED_COPPER_GRATE,
Ids::OXIDIZED_COPPER_GRATE,
Ids::WAXED_OXIDIZED_COPPER_GRATE,
fn(Reader $in) => Blocks::COPPER_GRATE()
);
$this->mapCopper(
Ids::CUT_COPPER,
Ids::WAXED_CUT_COPPER,
Ids::EXPOSED_CUT_COPPER,
Ids::WAXED_EXPOSED_CUT_COPPER,
Ids::WEATHERED_CUT_COPPER,
Ids::WAXED_WEATHERED_CUT_COPPER,
Ids::OXIDIZED_CUT_COPPER,
Ids::WAXED_OXIDIZED_CUT_COPPER,
fn(Reader $in) => Blocks::CUT_COPPER()
);
$this->mapCopper(
Ids::CUT_COPPER_STAIRS,
Ids::WAXED_CUT_COPPER_STAIRS,
Ids::EXPOSED_CUT_COPPER_STAIRS,
Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS,
Ids::WEATHERED_CUT_COPPER_STAIRS,
Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS,
Ids::OXIDIZED_CUT_COPPER_STAIRS,
Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS,
fn(Reader $in) => Helper::decodeStairs(Blocks::CUT_COPPER_STAIRS(), $in)
);
}
private function registerSimpleDeserializers() : void{
$this->mapSimple(Ids::AIR, fn() => Blocks::AIR());
$this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST());
@ -1220,18 +1386,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::COMPOUND_CREATOR, fn(Reader $in) => Blocks::COMPOUND_CREATOR()
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()))
);
$this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE));
$this->map(Ids::COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
$this->map(Ids::COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
$this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
$this->mapSlab(Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_RED_SANDSTONE_SLAB());
$this->mapSlab(Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_SANDSTONE_SLAB());
$this->mapSlab(Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB, fn() => Blocks::DARK_PRISMARINE_SLAB());
@ -1286,19 +1440,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::ENDER_CHEST()
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
$this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
$this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::FARMLAND, function(Reader $in) : Block{
return Blocks::FARMLAND()
->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7));
@ -1451,19 +1592,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSlab(Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB, fn() => Blocks::STONE_SLAB());
$this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS());
$this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis()));
$this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
$this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
$this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis()));
$this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB());
$this->map(Ids::PINK_PETALS, function(Reader $in) : Block{
@ -1736,71 +1864,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readHorizontalFacing());
});
$this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in));
$this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE));
$this->map(Ids::WAXED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE));
$this->map(Ids::WAXED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
$this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
$this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
$this->map(Ids::WAXED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::WAXED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
$this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
$this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
$this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
$this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
$this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
$this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
$this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
$this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{
return Blocks::WEEPING_VINES()
->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));

View File

@ -37,7 +37,6 @@ use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path;
use function array_key_last;
use function array_map;
use function array_values;
use function assert;
use function count;
use function get_debug_type;
@ -138,8 +137,8 @@ final class BlockStateUpgradeSchemaUtils{
$convertedRemappedValuesIndex = [];
foreach(Utils::stringifyKeys($model->remappedPropertyValuesIndex ?? []) as $mappingKey => $mappingValues){
foreach($mappingValues as $k => $oldNew){
$convertedRemappedValuesIndex[$mappingKey][$k] = new BlockStateUpgradeSchemaValueRemap(
foreach($mappingValues as $oldNew){
$convertedRemappedValuesIndex[$mappingKey][] = new BlockStateUpgradeSchemaValueRemap(
self::jsonModelToTag($oldNew->old),
self::jsonModelToTag($oldNew->new)
);
@ -361,7 +360,7 @@ final class BlockStateUpgradeSchemaUtils{
//remaps with the same number of criteria should be sorted alphabetically, but this is not strictly necessary
return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []);
});
$result->remappedStates[$oldBlockName] = array_values($keyedRemaps);
$result->remappedStates[$oldBlockName] = $keyedRemaps; //usort strips keys, so this is already a list
}
if(isset($result->remappedStates)){
ksort($result->remappedStates);

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