Compare commits

..

137 Commits

Author SHA1 Message Date
414e8acf8c Release 5.20.0 2024-10-26 15:42:43 +01:00
d372af351a Fix changelog typo 2024-10-26 15:40:22 +01:00
4814db4fe7 Assemble 1.21.40 (#6471) 2024-10-25 14:21:51 +01:00
f1b1a7022d and a sanity check just in case 2024-10-17 20:55:12 +01:00
59d14de1d8 generate-blockstate-upgrade-schema: fallback to exact state match when encountering ambiguous filters
this popped up due to new changes in 1.20.40. Really we need to improve the way the filters are calculated, but this workaround solves the issue for now.
2024-10-17 20:51:17 +01:00
5cc1068cd4 Bump docker/build-push-action from 6.7.0 to 6.8.0 (#6462) 2024-10-03 21:29:22 +00:00
0a7cbdd56d Release 5.19.0 (1.21.30 support) 2024-09-21 11:50:04 -05:00
a5babb2c9f 5.19.1 is next 2024-09-20 19:00:23 -05:00
49c2f13cf0 Release 5.19.0 2024-09-20 18:59:45 -05:00
60cac18104 Assemble 1.21.30 (#6453) 2024-09-20 18:47:25 -05:00
00e39821f0 Release 5.18.1 (patch) 2024-09-03 06:46:21 -05:00
72d941fc1b Update 5.18.md 2024-09-03 11:33:05 +01:00
2a7b183ab8 5.18.2 is next 2024-09-02 11:25:50 -05:00
e9b597af6c Release 5.18.1 2024-09-02 11:24:42 -05:00
9381fc4172 Blue Ice: No longer emits light & it's now dropped when mined with a tool with silk touch enchantment (#6438) 2024-08-31 22:33:11 -06:00
281afb6838 Bump phpstan/phpstan in the development-patch-updates group (#6435) 2024-08-26 23:05:11 +00:00
ede363eb0f Fix shift crafting (#6433)
This field was added to the action in 1.21.20. Previously, the client would behave as if clicking the crafting result slot many times. Now it behaves more like recipe book shift-clicking.
2024-08-22 21:53:21 +01:00
bdbcfd10cc Add ShellCheck (#6407)
Co-authored-by: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com>
2024-08-19 22:52:51 +01:00
e6f9cdd990 Bump docker/build-push-action from 6.6.1 to 6.7.0 (#6432) 2024-08-19 18:12:06 +00:00
a7638cf914 Give an approving review to team member PRs automatically
Branch protection rules currently require 2 approving reviews to merge a PR. What we really want is for 2 team members to be aware of every change. If a team member makes a PR, only one other approval should be needed.

Since GitHub doesn't currently allow us to set different review thresholds for different users/teams, sending an automatic approval via GitHub Actions is the next best thing. This should reduce friction of team development work.
2024-08-19 19:02:17 +01:00
c32744ebc7 Release 5.18.0 (1.21.20 support) 2024-08-16 08:44:40 -05:00
e3baf3cddb 5.18.1 is next 2024-08-16 07:56:01 -05:00
9176b2494a Release 5.18.0 2024-08-16 07:54:59 -05:00
0f365886e0 Assemble 1.21.20 (#6423)
Co-authored-by: Dylan T. <dktapps@pmmp.io>
Co-authored-by: IvanCraft623 <ivancraft623@gmail.com>
2024-08-16 11:26:49 +01:00
8c3cf7a687 Use VISIBLE_MOB_EFFECTS actor metadata property to send effect bubbles (#6414)
Close #6402
2024-08-14 22:48:10 -05:00
3ed9615180 Merge pull request #6422 from pmmp/5.17.1
Release patch update 5.17.1
2024-08-13 17:28:27 +01:00
f5ab2979a0 5.17.2 is next 2024-08-13 10:56:03 -05:00
929cd63135 Release 5.17.1 2024-08-13 10:55:29 -05:00
585dc835e7 Fixed anvil placing rotation. (#6375)
Co-authored-by: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com>
2024-08-13 16:19:07 +01:00
d077bda30c Bump docker/build-push-action from 6.5.0 to 6.6.1 (#6419) 2024-08-13 14:35:01 +00:00
25c66e4c8b Bump phpstan/phpstan in the development-patch-updates group (#6420) 2024-08-13 13:36:14 +00:00
5a926a79cb Bump phpstan/phpstan in the development-patch-updates group (#6412) 2024-08-06 14:21:20 +00:00
54e7749c0b Bump docker/build-push-action from 6.3.0 to 6.5.0 (#6409) 2024-07-30 06:52:28 +00:00
237677c028 Bump phpstan/phpstan from 1.11.2 to 1.11.8 in the development-patch-updates group (#6410) 2024-07-30 06:47:56 +00:00
df4ada81e5 BedrockWorldData: Update version constants to 1.21.2 (#6399)
Co-authored-by: Dylan T <14214667+dktapps@users.noreply.github.com>
2024-07-16 00:51:06 -05:00
a96f1a5083 Bump docker/build-push-action from 5.3.0 to 6.3.0 (#6387) 2024-07-14 18:17:41 +00:00
1f510caf88 Bump the phpstan group with 2 updates (#6401) 2024-07-12 13:16:44 +00:00
32be474840 dependabot: group PHPStan dependency updates always
these typically need to be updated together anyway
2024-07-12 14:12:47 +01:00
f750b01d8b dependabot: group patch updates into a single PR 2024-07-12 14:10:14 +01:00
31484ee5c1 Merge pull request #6400 from pmmp/dependabot/github_actions/shivammathur/setup-php-2.31.1 2024-07-12 12:46:19 +00:00
786d84a9e1 Bump shivammathur/setup-php from 2.30.4 to 2.31.1
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.30.4 to 2.31.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.30.4...2.31.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-12 12:43:18 +00:00
85606925a1 BlockStateData: add a note about CURRENT_VERSION
this is not the same as current game version, as the revision is determined by some Mojang internal factors.
2024-07-12 11:26:51 +01:00
616f96a703 Release 5.17.0 (1.21.2 support) 2024-07-10 09:31:25 -05:00
824e270041 5.17.1 is next 2024-07-09 21:16:13 -05:00
37bf4bc0b0 Release 5.17.0 2024-07-09 21:15:18 -05:00
5d60ba36b7 Support for 1.21.2 2024-07-09 21:07:47 -05:00
68d2e2915e Bells always drops themselves (#4802) 2024-07-07 15:20:45 -05:00
20f5741ed7 Bowl: Add fuel return value (#6384) 2024-07-05 17:41:22 -05:00
23b2b75acf Merge pull request #6380 from pmmp/dependabot/composer/phpunit/phpunit-10.5.24 2024-07-04 21:16:19 +00:00
25ea9b2218 Bump phpunit/phpunit from 10.3.5 to 10.5.24
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.3.5 to 10.5.24.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.5.24/ChangeLog-10.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.3.5...10.5.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 10:53:54 +00:00
77db7a8941 Merge pull request #6381 from pmmp/dependabot/composer/symfony/filesystem-6.4.9 2024-07-01 10:53:02 +00:00
af4294295b Bump symfony/filesystem from 6.4.7 to 6.4.9
Bumps [symfony/filesystem](https://github.com/symfony/filesystem) from 6.4.7 to 6.4.9.
- [Release notes](https://github.com/symfony/filesystem/releases)
- [Changelog](https://github.com/symfony/filesystem/blob/7.1/CHANGELOG.md)
- [Commits](https://github.com/symfony/filesystem/compare/v6.4.7...v6.4.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 10:19:24 +00:00
585aee9386 Fixed an oopsie from 5ef247620a
I added it to the wrong set of artifacts :(
2024-06-13 18:58:32 +01:00
433bd6a8aa 5.16.1 is next 2024-06-13 18:55:21 +01:00
22a1549998 Release 5.16.0 2024-06-13 18:55:18 +01:00
0ec8465fcf shut! 2024-06-13 18:43:10 +01:00
f121654452 Assemble 1.21.0 2024-06-13 18:41:41 +01:00
08c6e63aac Bump phpstan/phpstan from 1.10.67 to 1.11.2 (#6352)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.67 to 1.11.2.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.67...1.11.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-31 17:01:51 +01:00
4c418b4318 Remove unnecessary return statement (#6350) 2024-05-31 16:54:50 +01:00
fb9a74e879 gitignore files generated by install-local-protocol.sh 2024-05-13 11:52:31 +01:00
554775841e Added script for linking local versions of Bedrock* deps for testing 2024-05-13 11:48:23 +01:00
373dd9938c Update composer dependencies 2024-05-13 11:37:32 +01:00
f772bb7384 WoodenStairs can be a fuel (#6345) 2024-05-13 09:34:18 +01:00
5ef247620a Attach permission doc to every release 2024-05-07 12:46:31 +01:00
1b082f99e9 DefaultPermissions: fixed typo 2024-05-07 12:34:42 +01:00
9b6a0c9945 Bump phpstan/phpstan-strict-rules from 1.5.3 to 1.5.5 (#6335)
Bumps [phpstan/phpstan-strict-rules](https://github.com/phpstan/phpstan-strict-rules) from 1.5.3 to 1.5.5.
- [Release notes](https://github.com/phpstan/phpstan-strict-rules/releases)
- [Commits](https://github.com/phpstan/phpstan-strict-rules/compare/1.5.3...1.5.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 15:41:59 +01:00
ab3be50b49 Bump phpstan/phpstan from 1.10.66 to 1.10.67 (#6337)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.66 to 1.10.67.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.66...1.10.67)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 15:41:50 +01:00
27dc43f131 Bump shivammathur/setup-php from 2.30.2 to 2.30.4 (#6339)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.30.2 to 2.30.4.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.30.2...2.30.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 15:41:40 +01:00
d67f5a5c6f Bump symfony/filesystem from 6.4.6 to 6.4.7 (#6342)
Bumps [symfony/filesystem](https://github.com/symfony/filesystem) from 6.4.6 to 6.4.7.
- [Release notes](https://github.com/symfony/filesystem/releases)
- [Changelog](https://github.com/symfony/filesystem/blob/7.0/CHANGELOG.md)
- [Commits](https://github.com/symfony/filesystem/compare/v6.4.6...v6.4.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 15:41:28 +01:00
ed158f8a1b Server: include uptime in crash restart throttle message
this makes it clearer why the wait duration is chosen instead of it looking random.
2024-04-29 16:36:14 +01:00
d70a7d34a7 Living: don't knockback or do hurt FX when attacked during cooldown
players were switching from a weaker tool to a stronger one to get double knockback in PvP.
while it's intended that we don't cancel the second attack during hit cooldown if the damage is
higher (the first damage is subtracted to prevent doubling up), we don't want them to get double
knockback.
this behaviour now matches vanilla to the best of my observations.

Come at me PvP community... I know some people are going to hate this change
2024-04-29 15:51:43 +01:00
be6754494f 5.15.1 is next 2024-04-25 11:52:31 +01:00
d273ccf87c Release 5.15.0 2024-04-25 11:52:30 +01:00
737f5066a0 Fully cover codegen in CI 2024-04-25 11:48:22 +01:00
10238d7934 Removed beta change 2024-04-25 11:39:09 +01:00
6077748caa Changes for 1.20.80 2024-04-25 11:31:41 +01:00
50e2c469a5 Bump phpstan/phpstan-strict-rules from 1.5.2 to 1.5.3 (#6326)
Bumps [phpstan/phpstan-strict-rules](https://github.com/phpstan/phpstan-strict-rules) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/phpstan/phpstan-strict-rules/releases)
- [Commits](https://github.com/phpstan/phpstan-strict-rules/compare/1.5.2...1.5.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-08 14:24:59 +01:00
fa87602661 Bump build/php from f9601e5 to 084822a (#6323)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `f9601e5` to `084822a`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](f9601e5313...084822aa9e)

---
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-04-08 14:24:48 +01:00
d3c9c137ad Bump symfony/filesystem from 6.4.3 to 6.4.6 (#6327)
Bumps [symfony/filesystem](https://github.com/symfony/filesystem) from 6.4.3 to 6.4.6.
- [Release notes](https://github.com/symfony/filesystem/releases)
- [Changelog](https://github.com/symfony/filesystem/blob/7.0/CHANGELOG.md)
- [Commits](https://github.com/symfony/filesystem/compare/v6.4.3...v6.4.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-08 14:24:11 +01:00
37322e0d50 Armor: only make sound when the item was equipped by player action
this ensures the greatest amount of consistency with vanilla.

in order to prevent the sounds being broadcasted on armor damage with the old method, we'd also have to sacrifice the sound when replacing one leather helmet with another, for example.
this approach minimizes the gameplay impact at the possible expense of plugins.

closes #6325
2024-04-08 14:05:47 +01:00
55cf24aa02 5.14.2 is next 2024-04-05 18:41:27 +01:00
3590d84d03 Release 5.14.1 2024-04-05 18:41:27 +01:00
68f8fa8caf Update pmmpthread required version 2024-04-05 18:40:43 +01:00
1ad190024a 5.14.1 is next 2024-04-05 18:16:22 +01:00
769a149057 Release 5.14.0 2024-04-05 18:16:19 +01:00
ea339355bb Merge branch 'minor-next' into stable 2024-04-05 17:30:59 +01:00
b9288c238b Update BedrockBlockUpgradeSchema 2024-04-05 17:29:16 +01:00
16f29c775e tools/generate-blockstate-upgrade-schema: added support for generating newFlattenedName with value transforms
as seen in pmmp/BedrockBlockUpgradeSchema@ebd768e5b2, this enables use of newFlattenedName in more places (by allowing the flattened values to be transformed before building the new ID), as well as reducing the number of remappedStates in general by compacting stuff which was partially transformed like color silver -> light_gray.
2024-04-05 17:13:38 +01:00
e30e27dd57 Fix CS 2024-04-03 15:43:43 +01:00
cd6634d34b Bump shivammathur/setup-php from 2.30.0 to 2.30.2 (#6315)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.30.0 to 2.30.2.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.30.0...2.30.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-03 15:37:13 +01:00
f013079ff6 Fixed MainLogger BC break 2024-04-03 15:31:37 +01:00
c4abac4606 Bump build/php from 6f619bf to f9601e5 (#6321)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `6f619bf` to `f9601e5`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](6f619bf7a0...f9601e5313)

---
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-04-03 14:03:46 +01:00
11fbc8db6f Make use of pmmpthread 6.1.0 for Thread::getRunningCount()
ThreadManager doesn't count these correctly anymore since pmmpthread since thread-safe statics aren't copied anymore.
2024-04-02 19:40:44 +01:00
022362a01a Update pocketmine/errorhandler to 0.7.0 2024-04-02 19:37:07 +01:00
98380e46bf Ignore non-fatal error types in crash handlers
ErrorToExceptionHandler currently prevents these from appearing by turning them into exceptions, but this won't always be the case.
For example, in the future we may not want to turn ALL types of E_* errors into exceptions (e.g. E_DEPRECATED).
2024-04-02 19:22:40 +01:00
dad9a7e6cd Merge branch 'stable' into minor-next 2024-04-02 16:35:31 +01:00
de6a91dabc Rework consistency check to tolerate dynamic type IDs
we don't actually care about the specific values, only whether all the blocks and their states have been correctly registered.
I'd prefer to track all of the state data permutations, but the APIs for that are private, so tracking the number of permutations will have to suffice (this should be good enough to detect bugs anyway, and also takes way less space).
2024-04-01 18:44:01 +01:00
0615afa766 Bump phpstan/phpstan from 1.10.65 to 1.10.66 (#6317)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.65 to 1.10.66.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.65...1.10.66)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 16:12:31 +01:00
d5919dc094 ... 2024-03-25 17:24:52 +00:00
09904dc519 workaround for callback-validator not understanding arrow functions 2024-03-25 14:58:21 +00:00
f799cfaba6 Implemented sound when equipping armor (#6303) 2024-03-25 14:15:54 +00:00
11f119551d Bump phpstan/phpstan from 1.10.62 to 1.10.65 (#6308)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.62 to 1.10.65.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.62...1.10.65)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 10:55:22 +00:00
2584314202 NetherRoots: fix placement on soul soil (#6299) 2024-03-25 10:53:16 +00:00
337e462c8f Added new banner patterns (#6298) 2024-03-25 10:52:21 +00:00
b680a1693c Added sound when picking sweet berries (#6287) 2024-03-19 10:18:30 +00:00
0e5395c59b PocketMine-MP.phar self-extraction to decompressed cache (#6217)
Because ext-phar sucks, tmp gets spammed by cache files for every thread when loading files from the phar on the fly.

Instead, we convert the `.phar` into a decompressed `.tar` in the tmp directory and require files from inside it. Surprisingly, this works because `ext-phar` supports `tar` and `zip` natively. No stream wrapper is required, as the `PocketMine.php` bootstrap loads files relative to its location, so the cache is automatically used for everything.

To be honest I would rather get rid of phars entirely, but they are still the easiest way to have PhpStorm load PocketMine-MP API information for now, and the alternatives are more complicated and inconvenient.

### Caveats
Everywhere that previously used `new Phar(Phar::running(false))` in the core code needs to be updated to use `PharData` for this to work correctly. Plugins don't need to do anything.

### Why not just use `Phar::decompressFiles()`?
This requires setting `phar.readonly` to `0`, which is a security issue. Technically, we could have used a subprocess to do this, but it just didn't seem right.

### WTF? `phar://` can be used on `tar` files???
Yup. I was just as surprised to find out that `require` works in such contexts.

### Relevant issues
- Closes #6214 

## Changes
### API changes
None.

### Behavioural changes
Server startup will be slightly slower, as the phar has to decompress and convert itself into a `.tar`. However, testing showed that this generally takes less than 200 ms, so it should be barely noticeable.

## Backwards compatibility
No BC issues.

## Tests
Locally tested and the CI will also verify
2024-03-18 16:48:17 +00:00
94e0bf954b Bump docker/build-push-action from 5.2.0 to 5.3.0 (#6288)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.2.0...v5.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 15:08:13 +00:00
556b00d11f Bump phpstan/phpstan from 1.10.60 to 1.10.62 (#6289)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.60 to 1.10.62.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.60...1.10.62)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 15:06:50 +00:00
981f49ff56 CS 2024-03-15 18:03:44 +00:00
f527a4c8fe Added --no-log-file command line option
while this would be more user-friendly as a config option, configs are a pain because they aren't initialized until after the server log has already been set up.
In any case, I foresee that people will likely want to bake this into Dockerfiles directly anyway.
2024-03-15 17:53:50 +00:00
7148c7a222 Log rotate (#4032)
`server.log` is not rotated by default and grows unmanageably large, to the point where it's so huge that it's not possible to read it with any standard text editor anymore.

This PR implements automatic log rotation. 
- When the `server.log` reaches 32MB in size, it's moved to the `log_archive/` folder of the server's data folder.
- The archive's file name will look something like this: `server.2024-03-15T15.26.24.0.log`
- The file's name contains the date and time when the file was archived. This may be useful if you're trying to find logs from a particular time frame.

This has several benefits:
- Much more easily find logs from a particular time frame without scrolling through GBs of logs
- Free up space without stopping the server - Archived log files in `log_archive/` can be safely deleted and/or modified while the server is running

If you want to automatically compress or clean up the log files, I suggest an external cron job or disk watcher.

Closes #4029.
2024-03-15 16:44:37 +00:00
e31fd122d9 BlockStateReader: micro optimize unread properties check
this has a pretty much insignificant performance impact, but reduces the cost of this check to basically 0.
2024-03-14 17:54:26 +00:00
a835069564 Merge remote-tracking branch 'origin/stable' into minor-next 2024-03-14 12:47:04 +00:00
b77193b987 ZlibCompressor: Increase max decompression size to accommodate larger skins
again, very annoying that we have to account for this (it makes it easier for attackers to waste CPU time and memory), but we don't really have much of a choice.
2024-03-14 12:34:30 +00:00
11ca208d93 RakLib: Allow larger number of split packet parts
some persona skins are insanely bloated and get split into hundreds of parts.
it's quite annoying that we have to accommodate this, but we can't keep allowing players to experience login timeouts without an obvious indication what's wrong.
2024-03-14 12:32:26 +00:00
8d7f1a8557 BlockStateUpgraderTest: fixed tests for 7ff0ae19d6 2024-03-13 18:35:07 +00:00
7ff0ae19d6 BlockStateUpgrader: a simple yet hard-to-explain optimization
Prior to this commit, upgrade schemas would be applied to blockstates with the same version, as there wasn't any way to know if they should be applied or not given Mojang's tendency to forget to bump the version.
However, it occurred to me that we only need to do this if there are multiple schemas present for the same version ID, which is rarely the case.
This allows skipping costly logic for blockstates on the newest version (the common case), reducing the time taken to process the blockstate for upgrading by over 30%.
Overall, this translates into less than 10% real performance improvement for chunk loading, but it's still a worthwhile improvement.
2024-03-13 18:19:51 +00:00
1de66cb0de RegistryTrait: added fast path optimization
this reduces VanillaBlocks access time from 360 ns to 230 ns on my machine - an improvement of about 35%.
2024-03-13 17:11:06 +00:00
9f3533d870 Improved logging for block decode errors
this is still noisy, but less so than before.
this also adds logging to places where it was previously missing.
2024-03-13 16:42:23 +00:00
2d24fac067 5.13.1 is next 2024-03-13 14:59:21 +00:00
f193a990b0 Release 5.13.0 2024-03-13 14:59:21 +00:00
c11c0679e3 Fix CS 2024-03-13 14:55:54 +00:00
ba48f258f3 Support for 1.20.70 2024-03-13 14:53:27 +00:00
e105c9bd76 5.12.2 is next 2024-03-13 13:48:59 +00:00
a6202d0442 BlockStateUpgrader: calculate output version ID in a less stupid way
this improves the performance by a conservative 10%.
2024-03-12 11:48:48 +00:00
8ec304e66e BlockStateUpgradeSchema: avoid unnecessary property access and calculation
this was costing a surprisingly large 5-10% of the processing time for blockstate data.
2024-03-12 11:45:08 +00:00
ac8dbf8640 BlockStateUpgrader: extract state remap to its own function 2024-03-06 12:56:49 +00:00
dbc7105e5b Merge branch 'resource-pack-ack-receipts' into minor-next 2024-03-04 15:46:31 +00:00
3b97d067a3 Merge remote-tracking branch 'origin/stable' into minor-next 2024-03-04 15:40:10 +00:00
781e3643dd Clean up 2024-03-04 14:25:47 +00:00
5ad63f27bb Update RakLib (again) 2024-03-02 01:02:57 +00:00
f13eaaab05 Update RakLib 2024-03-02 00:08:49 +00:00
b9a1ef1357 Throttle resource pack sending using ack receipts
this isn't the best solution, as it limits the download speed somewhat, but it's relatively simple and works quite well.
closes #3127
2024-03-01 17:07:19 +00:00
4abc36275c Remove newline 2024-03-01 17:02:44 +00:00
4b5ac53276 Fixes 2024-03-01 17:01:32 +00:00
90409b50d1 Allow offering different resource packs to different players (#6249)
closes #6248
2024-03-01 14:53:59 +00:00
bc2abf4b15 First shot at packet ack receipt support
this will be useful for preventing resource pack sending from overloading the network.
it's not the best solution for that (since it means the RTT will limit the pack download speed), but it's easier than implementing congestion control and will work fine in most cases.
2024-03-01 14:41:53 +00:00
116 changed files with 3684 additions and 1549 deletions

View File

@ -12,6 +12,22 @@ updates:
update-types:
- "version-update:semver-major"
- "version-update:semver-minor"
groups:
production-patch-updates:
dependency-type: production
patterns:
- "*"
update-types:
- "patch"
development-patch-updates:
dependency-type: development
patterns:
- "*"
update-types:
- "patch"
phpstan:
patterns:
- "phpstan/*"
- package-ecosystem: gitsubmodule
directory: "/"

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@v5.2.0
uses: docker/build-push-action@v6.8.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@v5.2.0
uses: docker/build-push-action@v6.8.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@v5.2.0
uses: docker/build-push-action@v6.8.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@v5.2.0
uses: docker/build-push-action@v6.8.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.30.0
uses: shivammathur/setup-php@2.31.1
with:
php-version: 8.2

View File

@ -20,7 +20,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.30.0
uses: shivammathur/setup-php@2.31.1
with:
php-version: ${{ matrix.php-version }}
@ -76,6 +76,9 @@ jobs:
${{ steps.php-binary-url.outputs.PHP_BINARY_URL }} \
> build_info.json
- name: Generate core permission doc for doc.pmmp.io
run: php tools/generate-permission-doc.php rst
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
@ -84,11 +87,12 @@ jobs:
${{ github.workspace }}/PocketMine-MP.phar
${{ github.workspace }}/start.*
${{ github.workspace }}/build_info.json
${{ github.workspace }}/core-permissions.rst
- name: Create draft release
uses: ncipollo/release-action@v1.14.0
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst
commit: ${{ github.sha }}
draft: true
prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }}

View File

@ -147,17 +147,8 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction
- name: Regenerate registry annotations
run: php build/generate-registry-annotations.php src
- name: Regenerate KnownTranslation APIs
run: php build/generate-known-translation-apis.php
- name: Regenerate BedrockData available files constants
run: php build/generate-bedrockdata-path-consts.php
- name: Regenerate YmlServerProperties constants
run: php build/generate-pocketmine-yml-property-consts.php
- name: Update generated code
run: composer update-codegen
- name: Verify code is unchanged
run: |

View File

@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.30.0
uses: shivammathur/setup-php@2.31.1
with:
php-version: 8.2
tools: php-cs-fixer:3.49
@ -37,3 +37,15 @@ jobs:
- name: Run PHP-CS-Fixer
run: php-cs-fixer fix --dry-run --diff --ansi
shellcheck:
name: ShellCheck
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@2.0.0

View File

@ -0,0 +1,42 @@
#Due to GitHub awkwardness, it's not easy to reduce the review requirement for collaborators.
#Our policy is that 2 collaborators should be aware of every change.
#For outside PRs, this means 2 collaborator reviews.
#For PRs made by collaborators, this means 1 reviewer + the author.
#We trust that collaborators don't need as much oversight.
name: Auto approve collaborator PRs
on:
pull_request_target:
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
pull-requests: write
jobs:
approve:
name: Auto approve
runs-on: ubuntu-latest
steps:
- name: Check if PR author has write access
id: check-permission
uses: actions-cool/check-user-permission@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
require: write
username: ${{ github.event.pull_request.user.login }}
#technically this would be fine for dependabot but generally bots don't count as team members
check-bot: true
#TODO: Some way to avoid unnecessary repeated reviews would be nice here
- name: Approve PR if authorized
if: steps.check-permission.outputs.require-result == 'true' && steps.check-permission.outputs.check-result == 'false'
uses: juliangruber/approve-pull-request-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number }}

3
.gitignore vendored
View File

@ -53,3 +53,6 @@ Documentation/*
# php-cs-fixer
/.php_cs.cache
/.php-cs-fixer.cache
# install-local-protocol.sh
/composer-local-protocol.*

View File

@ -102,6 +102,25 @@ function generateClassHeader(string $className) : string{
return <<<HEADER
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace $namespace;

View File

@ -61,6 +61,25 @@ function generateItemIds(ItemTypeDictionary $dictionary, BlockItemIdMap $blockIt
fwrite($file, <<<'HEADER'
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\data\bedrock\item;

168
build/server-phar-stub.php Normal file
View File

@ -0,0 +1,168 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\server_phar_stub;
use function clearstatcache;
use function copy;
use function fclose;
use function fflush;
use function flock;
use function fopen;
use function fwrite;
use function getmypid;
use function hrtime;
use function is_dir;
use function is_file;
use function mkdir;
use function number_format;
use function str_replace;
use function stream_get_contents;
use function sys_get_temp_dir;
use function tempnam;
use function unlink;
use const DIRECTORY_SEPARATOR;
use const LOCK_EX;
use const LOCK_NB;
use const LOCK_UN;
/**
* Finds the appropriate tmp directory to store the decompressed phar cache, accounting for potential file name
* collisions.
*/
function preparePharCacheDirectory() : string{
clearstatcache();
$i = 0;
do{
$tmpPath = sys_get_temp_dir() . '/PocketMine-MP-phar-cache.' . $i;
$i++;
}while(is_file($tmpPath));
if(!@mkdir($tmpPath) && !is_dir($tmpPath)){
throw new \RuntimeException("Failed to create temporary directory $tmpPath. Please ensure the disk has enough space and that the current user has permission to write to this location.");
}
return $tmpPath;
}
/**
* Deletes caches left behind by previous server instances.
* This ensures that the tmp directory doesn't get flooded by servers crashing in restart loops.
*/
function cleanupPharCache(string $tmpPath) : void{
clearstatcache();
/** @var string[] $matches */
foreach(new \RegexIterator(
new \FilesystemIterator(
$tmpPath,
\FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS
),
'/(.+)\.lock$/',
\RegexIterator::GET_MATCH
) as $matches){
$lockFilePath = $matches[0];
$baseTmpPath = $matches[1];
$file = @fopen($lockFilePath, "rb");
if($file === false){
//another process probably deleted the lock file already
continue;
}
if(flock($file, LOCK_EX | LOCK_NB)){
//this tmpfile is no longer in use
flock($file, LOCK_UN);
fclose($file);
unlink($lockFilePath);
unlink($baseTmpPath . ".tar");
unlink($baseTmpPath);
echo "Deleted stale phar cache at $baseTmpPath\n";
}else{
$pid = stream_get_contents($file);
fclose($file);
echo "Phar cache at $baseTmpPath is still in use by PID $pid\n";
}
}
}
function convertPharToTar(string $tmpName, string $pharPath) : string{
$tmpPharPath = $tmpName . ".phar";
copy($pharPath, $tmpPharPath);
$phar = new \Phar($tmpPharPath);
//phar requires phar.readonly=0, and zip doesn't support disabling compression - tar is the only viable option
//we don't need phar anyway since we don't need to directly execute the file, only require files from inside it
$phar->convertToData(\Phar::TAR, \Phar::NONE);
unset($phar);
\Phar::unlinkArchive($tmpPharPath);
return $tmpName . ".tar";
}
/**
* Locks a phar tmp cache to prevent it from being deleted by other server instances.
* This code looks similar to Filesystem::createLockFile(), but we can't use that because it's inside the compressed
* phar.
*/
function lockPharCache(string $lockFilePath) : void{
//this static variable will keep the file(s) locked until the process ends
static $lockFiles = [];
$lockFile = fopen($lockFilePath, "wb");
if($lockFile === false){
throw new \RuntimeException("Failed to open temporary file");
}
flock($lockFile, LOCK_EX); //this tells other server instances not to delete this cache file
fwrite($lockFile, (string) getmypid()); //maybe useful for debugging
fflush($lockFile);
$lockFiles[$lockFilePath] = $lockFile;
}
/**
* Prepares a decompressed .tar of PocketMine-MP.phar in the system temp directory for loading code from.
*
* @return string path to the temporary decompressed phar (actually a .tar)
*/
function preparePharCache(string $tmpPath, string $pharPath) : string{
clearstatcache();
$tmpName = tempnam($tmpPath, "PMMP");
if($tmpName === false){
throw new \RuntimeException("Failed to create temporary file");
}
lockPharCache($tmpName . ".lock");
return convertPharToTar($tmpName, $pharPath);
}
$tmpDir = preparePharCacheDirectory();
cleanupPharCache($tmpDir);
echo "Preparing PocketMine-MP.phar decompressed cache...\n";
$start = hrtime(true);
$cacheName = preparePharCache($tmpDir, __FILE__);
echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n";
require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php';

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\build\server_phar;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Git;
use Symfony\Component\Filesystem\Path;
use function array_map;
use function count;
use function dirname;
@ -169,21 +171,7 @@ function main() : void{
'git' => $gitHash,
'build' => $build
],
<<<'STUB'
<?php
$tmpDir = sys_get_temp_dir();
if(!is_readable($tmpDir) or !is_writable($tmpDir)){
echo "ERROR: tmpdir $tmpDir is not accessible." . PHP_EOL;
echo "Check that the directory exists, and that the current user has read/write permissions for it." . PHP_EOL;
echo "Alternatively, set 'sys_temp_dir' to a different directory in your php.ini file." . PHP_EOL;
exit(1);
}
require("phar://" . __FILE__ . "/src/PocketMine.php");
__HALT_COMPILER();
STUB
,
Filesystem::fileGetContents(Path::join(__DIR__, 'server-phar-stub.php')) . "\n__HALT_COMPILER();",
\Phar::SHA1,
\Phar::GZ
) as $line){

16
changelogs/5.13.md Normal file
View File

@ -0,0 +1,16 @@
# 5.13.0
Released 13th March 2024.
**For Minecraft: Bedrock Edition 1.20.70**
This is a support release for Minecraft: Bedrock Edition 1.20.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.
## General
- Added support for Minecraft: Bedrock Edition 1.20.70.
- Removed support for earlier versions.

94
changelogs/5.14.md Normal file
View File

@ -0,0 +1,94 @@
# 5.14.0
Released 5th April 2024.
**For Minecraft: Bedrock Edition 1.20.70**
This is a minor feature release, including performance improvements, minor gameplay features, new API features, and various internal 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.
## General
- Added support for a `--no-log-file` command-line option, which disables the creation of a `server.log` file.
- **Use this with caution.** If you don't have another mechanism for collecting logs (e.g. Docker), this may make debugging harder.
- Added support for automatic `server.log` rotation. When the `server.log` exceeds 32 MB, it will be renamed and moved to the `log_archive` folder in the server's data directory.
- Files in the `log_archive` folder can be safely modified or deleted without stopping the server.
- We suggest a cron job or similar to manage old log files (e.g. deleting or compressing them).
- Added a new cache mechanism for `PocketMine-MP.phar`. This has several advantages:
- Caches are now reused by all threads - this significantly reduces `/tmp` usage (previously every thread generated its own cache, wasting lots of space)
- Dead cache files are automatically cleaned up by new servers - this means that a server crash loop won't flood `/tmp` anymore
- `/status` now reports a more accurate number of threads on Windows.
- Large resource packs are now able to be properly downloaded from the server.
- Larger player skin sizes are now accepted by the server.
- Improved logging from world providers to reduce spam when chunks contain invalid data.
- Added more error logging for Anvil, PMAnvil and MCRegion worlds.
- PHP deprecation warnings no longer cause the server to crash. This should make it easier for server owners to update to newer PHP versions.
## Performance
- Improved world loading performance. This was achieved through a combination of changes:
- Improvements to `BlockStateUpgrader` to avoid unnecessary work
- Improvements to `BlockStateUpgradeSchema` to clean up stupid code
- Improvements to `BlockStateReader` unused state handling
- Optimizations to `RegistryTrait` (see below)
- Improved performance of `RegistryTrait::__callStatic()` accessor by introducing a fast-path optimization. Ensure that you access registries with the correct function name case to benefit from this.
- This improves the performance of `VanillaBlocks::WHATEVER()`, `VanillaItems`, etc.
## Tools
- `tools/generate-blockstate-upgrade-schema.php` now supports generating schemas using `flattenedValueRemaps` (described in [BlockStateUpgradeSchema](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/4.0.0)).
## Gameplay
- Added sounds for armour equipping and unequipping.
- Added sound for picking berries from a sweet berry bush.
## API
### `pocketmine\block\utils`
- The following enum cases have been added:
- `BannerPatternType::GLOBE`
- `BannerPatternType::PIGLIN`
### `pocketmine\event\player`
- The following classes have been added:
- `PlayerResourcePackOfferEvent` - called before the server tells a connecting client which resource packs are available to download - allows customizing the pack list and other options
### `pocketmine\item`
- The following API methods have been added:
- `public ArmorMaterial->getEquipSound() : ?\pocketmine\world\Sound` - returns the sound to play when this armour is equipped or unequipped
- The following API methods have signature changes:
- `ArmorMaterial->__construct()` now accepts an optional `?Sound $equipSound` parameter
### `pocketmine\utils`
- The following API methods have signature changes:
- `MainLogger->__construct()` now accepts `null` for the `$logFile` parameter - this disables the creation of a logger thread and log file
- `MainLogger->__construct()` now accepts an optional `?string $logArchiveDir` parameter. If set, this enables log archiving in the specified directory when the current log file exceeds 32 MB.
## Dependencies
- Now uses [`pocketmine/bedrock-block-upgrade-schema` version 4.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/4.0.0).
- Now uses [`pmmp/ext-pmmpthread` version 6.1.0](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.1.0).
- Now uses [`pocketmine/errorhandler` version 0.7.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.7.0).
- Now uses [`pocketmine/raklib` version 1.1.0](https://github.com/pmmp/RakLib/releases/tag/1.1.0).
- Now uses [`pocketmine/raklib-ipc` version 1.0.0](https://github.com/pmmp/RakLibIpc/releases/tag/1.0.0).
## Internals
- (Re)Added support for RakLib packet ACK receipts. This was used to throttle resource pack sending and prevent network overloading.
- Added `NetworkSession->sendDataPacketWithReceipt()` to make use of this feature.
- `PacketSender` now requires an additional `?int $receiptId` parameter.
- `ResourcePackPacketHandler` now uses `sendDataPacketWithReceipt()` to send resource packs, and delays sending the next chunk until the current one is acknowledged.
- `ResourcePackPacketHandler` now accepts resource pack info directly in the constructor, instead of `ResourcePackManager`. This eases the implementation of `PlayerResourcePackOfferEvent`.
- Increased `ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE` to 8 MB (previously 2 MB). While this weakens server security, it appears to be necessary to deal with extremely bloated Persona skins.
- Increased max split packet parts accepted by `RakLib` to 512 (previously 128). Again, this is necessary to deal with extremely bloated Persona skins.
- Added a new cache mechanism for `PocketMine-MP.phar`.
- `ext-phar`'s default mechanism is extremely wasteful (generating a separate cache file per thread), and doesn't clean up after itself.
- The new cache mechanism is shared between all threads, and automatically cleans up stale caches.
- The phar stub (`build/server-phar-stub.php`) now converts the phar contents into a `.tar`, and decompresses all the files into `$TMPDIR/PocketMine-MP-phar-cache.<random>/`.
- `phar://` URIs still work with this system, but `new Phar(__FILE__)` must be replaced by `new PharData(__FILE__)` within PocketMine-MP core code.
- Backtraces from a `phar`'d server will now point to a location in the extracted phar cache, rather than the phar itself.
- `block_factory_consistency_check` test (actually for `RuntimeBlockStateRegistry`) now stores less data, and is no longer affected by changes to internal state ID construction.
# 5.14.1
Released 5th April 2024.
## Fixes
- Fixed incorrect `pmmpthread` version check in server bootstrap.

16
changelogs/5.15.md Normal file
View File

@ -0,0 +1,16 @@
# 5.15.0
Released 25th April 2024.
**For Minecraft: Bedrock Edition 1.20.80**
This is a support release for Minecraft: Bedrock Edition 1.20.80.
**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.20.80.
- Removed support for earlier versions.

26
changelogs/5.16.md Normal file
View File

@ -0,0 +1,26 @@
# 5.16.0
Released 13th June 2024.
**For Minecraft: Bedrock Edition 1.21.0**
This is a support release for Minecraft: Bedrock Edition 1.21.0.
**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.0.
- Removed support for earlier versions.
- Generated permission docs are now included with every release.
- Crash throttle message (which appears when the server crashed after being up for less than 120 seconds) now shows the server uptime as well as the wait time. This should make it clearer how the wait time is decided.
## Tools
- Added `install-local-protocol.sh` script. This allows installing local copies of protocol dependencies without needing to create releases. Useful for integration testing when doing protocol updates.
## Fixes
- Attacking an entity with a higher damage weapon while it's on attack cooldown from a lower damage weapon (switching) no longer causes additional knockback to the victim.
- Wooden stairs can now be used as fuel in furnaces.
- Fixed incorrect description of the permission `pocketmine.command.save.perform`.

38
changelogs/5.17.md Normal file
View File

@ -0,0 +1,38 @@
# 5.17.0
Released 10th July 2024.
**For Minecraft: Bedrock Edition 1.21.2**
This is a support release for Minecraft: Bedrock Edition 1.21.2.
**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.2.
- Removed support for earlier versions.
## API
### `pocketmine\player`
- The following methods have been added:
- `public function closeAllForms() : void` - closes the current viewing form and forms in queue.
## Fixes
- Bowl can now be used as fuel.
- Bells always drops themselves even when using an incompatible tool.
# 5.17.1
Released 13th August 2024.
## Documentation
- Added a note about `BlockStateData::CURRENT_VERSION`.
## Fixes
- Fixed anvil placement rotation to match vanilla.
- Fixed outdated `BedrockWorldData` version, this was preventing use newer worlds.
## Internals
- Dependabot: PHPStan and patch updates are now grouped into a single PR.

30
changelogs/5.18.md Normal file
View File

@ -0,0 +1,30 @@
# 5.18.0
Released 16th August 2024.
**For Minecraft: Bedrock Edition 1.21.20**
This is a support release for Minecraft: Bedrock Edition 1.21.20.
**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.20.
- Removed support for earlier versions.
## Fixes
- Use `VISIBLE_MOB_EFFECTS` actor metadata property to send effect bubbles, this fixes effect bubbles not showing
# 5.18.1
Released 3rd September 2024.
## Fixes
- Fixed shift-crafting.
- Blue Ice block no longer emits light & it's now dropped when mined with a tool with silk touch enchantment.
## Internals
- Pull Requests from team members now get an approval automatically. This means that if a team member makes a PR, only one other approval should be needed.
- Added [ShellCheck](https://github.com/koalaman/shellcheck) to the CI tests.

16
changelogs/5.19.md Normal file
View File

@ -0,0 +1,16 @@
# 5.19.0
Released 21st September 2024.
**For Minecraft: Bedrock Edition 1.21.30**
This is a support release for Minecraft: Bedrock Edition 1.21.30.
**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.30.
- Removed support for earlier versions.

19
changelogs/5.20.md Normal file
View File

@ -0,0 +1,19 @@
# 5.20.0
Released 26th October 2024.
**For Minecraft: Bedrock Edition 1.21.40**
This is a support release for Minecraft: Bedrock Edition 1.21.40.
**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.40.
- Removed support for earlier versions.
## Fixes
- Fixed a bug in `tools/generate-blockstate-upgrade-schema.php` that caused it to fail on 1.21.40 with the new mushroom block changes.

View File

@ -22,7 +22,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pmmpthread": "^6.0.7",
"ext-pmmpthread": "^6.1.0",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
@ -33,29 +33,29 @@
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
"pocketmine/bedrock-block-upgrade-schema": "~3.5.0+bedrock-1.20.60",
"pocketmine/bedrock-data": "~2.8.0+bedrock-1.20.60",
"pocketmine/bedrock-item-upgrade-schema": "~1.7.0+bedrock-1.20.60",
"pocketmine/bedrock-protocol": "~28.0.0+bedrock-1.20.60",
"pocketmine/bedrock-block-upgrade-schema": "~4.5.0+bedrock-1.21.40",
"pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40",
"pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40",
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.6.0",
"pocketmine/errorhandler": "^0.7.0",
"pocketmine/locale-data": "~2.19.0",
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
"pocketmine/nbt": "~1.0.0",
"pocketmine/raklib": "^0.15.0",
"pocketmine/raklib-ipc": "^0.2.0",
"pocketmine/raklib": "~1.1.0",
"pocketmine/raklib-ipc": "~1.0.0",
"pocketmine/snooze": "^0.5.0",
"ramsey/uuid": "~4.7.0",
"symfony/filesystem": "~6.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.60",
"phpstan/phpstan": "1.11.11",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
"phpunit/phpunit": "^10.5.24"
},
"autoload": {
"psr-4": {
@ -83,11 +83,14 @@
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
"@php -dphar.readonly=0 build/server-phar.php"
],
"update-registry-annotations": [
"update-codegen": [
"@php build/generate-bedrockdata-path-consts.php",
"@php build/generate-biome-ids.php",
"@php build/generate-block-serializer-consts.php vendor/pocketmine/bedrock-data/canonical_block_states.nbt",
"@php build/generate-item-type-names.php vendor/pocketmine/bedrock-data/required_item_list.json",
"@php build/generate-known-translation-apis.php",
"@php build/generate-pocketmine-yml-property-consts.php",
"@php build/generate-registry-annotations.php src"
],
"update-translation-apis": [
"@php build/generate-known-translation-apis.php"
]
}
}

279
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": "a6b02985f32591475760c53e7104e2e4",
"content-hash": "5c5882370131d2ae3a043819c05e6f9c",
"packages": [
{
"name": "adhocore/json-comment",
@ -67,25 +67,25 @@
},
{
"name": "brick/math",
"version": "0.11.0",
"version": "0.12.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
"reference": "f510c0a40911935b77b86859eb5223d58d660df1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
"url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1",
"shasum": ""
},
"require": {
"php": "^8.0"
"php": "^8.1"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^9.0",
"vimeo/psalm": "5.0.0"
"phpunit/phpunit": "^10.1",
"vimeo/psalm": "5.16.0"
},
"type": "library",
"autoload": {
@ -105,12 +105,17 @@
"arithmetic",
"bigdecimal",
"bignum",
"bignumber",
"brick",
"math"
"decimal",
"integer",
"math",
"mathematics",
"rational"
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.11.0"
"source": "https://github.com/brick/math/tree/0.12.1"
},
"funding": [
{
@ -118,20 +123,20 @@
"type": "github"
}
],
"time": "2023-01-15T23:15:59+00:00"
"time": "2023-11-29T23:19:16+00:00"
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "3.5.0",
"version": "4.5.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3"
"reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/7943b894e050d68dd21b5c7fa609827a4e2e30f1",
"reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1",
"shasum": ""
},
"type": "library",
@ -142,22 +147,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/3.5.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.5.0"
},
"time": "2024-02-07T11:46:50+00:00"
"time": "2024-10-23T16:15:24+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.8.0+bedrock-1.20.60",
"version": "2.14.0+bedrock-1.21.40",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2"
"reference": "606d32ae426164b0615898b95d10e23293bed6ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d8ea0355b7c835564af9fe6e273e650ac62c84a2",
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/606d32ae426164b0615898b95d10e23293bed6ac",
"reference": "606d32ae426164b0615898b95d10e23293bed6ac",
"shasum": ""
},
"type": "library",
@ -168,22 +173,22 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.60"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.40"
},
"time": "2024-02-07T11:23:46+00:00"
"time": "2024-10-23T19:19:16+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
"version": "1.7.0",
"version": "1.13.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c"
"reference": "1dee9bbd0aaa65ed108b377b402746defe10b3b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/69772dd58e2b2c7b7513fa2bcdc46e782228641c",
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1dee9bbd0aaa65ed108b377b402746defe10b3b0",
"reference": "1dee9bbd0aaa65ed108b377b402746defe10b3b0",
"shasum": ""
},
"type": "library",
@ -194,22 +199,22 @@
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.7.0"
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.0"
},
"time": "2024-02-07T11:58:05+00:00"
"time": "2024-10-23T18:38:43+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "28.0.1+bedrock-1.20.60",
"version": "35.0.0+bedrock-1.21.40",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "fdb0d1ddee498f995fbd21b9077aac3d3518877e"
"reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/fdb0d1ddee498f995fbd21b9077aac3d3518877e",
"reference": "fdb0d1ddee498f995fbd21b9077aac3d3518877e",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
"reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
"shasum": ""
},
"require": {
@ -222,7 +227,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.10.59",
"phpstan/phpstan": "1.11.9",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5 || ^10.0"
@ -240,9 +245,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/28.0.1+bedrock-1.20.60"
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.40"
},
"time": "2024-03-01T21:49:48+00:00"
"time": "2024-10-24T15:45:43+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -376,25 +381,25 @@
},
{
"name": "pocketmine/errorhandler",
"version": "0.6.0",
"version": "0.7.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/ErrorHandler.git",
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878"
"reference": "cae94884368a74ece5294b9ff7fef18732dcd921"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/dae214a04348b911e8219ebf125ff1c5589cc878",
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878",
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/cae94884368a74ece5294b9ff7fef18732dcd921",
"reference": "cae94884368a74ece5294b9ff7fef18732dcd921",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpunit/phpunit": "^9.5"
"phpstan/phpstan": "~1.10.3",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
},
"type": "library",
"autoload": {
@ -409,9 +414,9 @@
"description": "Utilities to handle nasty PHP E_* errors in a usable way",
"support": {
"issues": "https://github.com/pmmp/ErrorHandler/issues",
"source": "https://github.com/pmmp/ErrorHandler/tree/0.6.0"
"source": "https://github.com/pmmp/ErrorHandler/tree/0.7.0"
},
"time": "2022-01-08T21:05:46+00:00"
"time": "2024-04-02T18:29:54+00:00"
},
{
"name": "pocketmine/locale-data",
@ -616,28 +621,28 @@
},
{
"name": "pocketmine/raklib",
"version": "0.15.1",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca"
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/79b7b4d1d7516dc6e322514453645ad9452b20ca",
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97",
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97",
"shasum": ""
},
"require": {
"ext-sockets": "*",
"php": "^8.0",
"php": "^8.1",
"php-64bit": "*",
"php-ipv6": "*",
"pocketmine/binaryutils": "^0.2.0",
"pocketmine/log": "^0.3.0 || ^0.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.9.17",
"phpstan/phpstan": "1.10.1",
"phpstan/phpstan-strict-rules": "^1.0"
},
"type": "library",
@ -653,32 +658,32 @@
"description": "A RakNet server implementation written in PHP",
"support": {
"issues": "https://github.com/pmmp/RakLib/issues",
"source": "https://github.com/pmmp/RakLib/tree/0.15.1"
"source": "https://github.com/pmmp/RakLib/tree/1.1.1"
},
"time": "2023-03-07T15:10:34+00:00"
"time": "2024-03-04T14:02:14+00:00"
},
{
"name": "pocketmine/raklib-ipc",
"version": "0.2.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLibIpc.git",
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c"
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/ce632ef2c6743e71eddb5dc329c49af6555f90bc",
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc",
"shasum": ""
},
"require": {
"php": "^8.0",
"php-64bit": "*",
"pocketmine/binaryutils": "^0.2.0",
"pocketmine/raklib": "^0.15.0"
"pocketmine/raklib": "^0.15.0 || ^1.0.0"
},
"require-dev": {
"phpstan/phpstan": "1.9.17",
"phpstan/phpstan": "1.10.1",
"phpstan/phpstan-strict-rules": "^1.0.0"
},
"type": "library",
@ -694,9 +699,9 @@
"description": "Channel-based protocols for inter-thread/inter-process communication with RakLib",
"support": {
"issues": "https://github.com/pmmp/RakLibIpc/issues",
"source": "https://github.com/pmmp/RakLibIpc/tree/0.2.0"
"source": "https://github.com/pmmp/RakLibIpc/tree/1.0.1"
},
"time": "2023-02-13T13:40:40+00:00"
"time": "2024-03-01T15:55:05+00:00"
},
{
"name": "pocketmine/snooze",
@ -829,20 +834,20 @@
},
{
"name": "ramsey/uuid",
"version": "4.7.5",
"version": "4.7.6",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e"
"reference": "91039bc1faa45ba123c4328958e620d382ec7088"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
@ -905,7 +910,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.5"
"source": "https://github.com/ramsey/uuid/tree/4.7.6"
},
"funding": [
{
@ -917,20 +922,20 @@
"type": "tidelift"
}
],
"time": "2023-11-08T05:53:05+00:00"
"time": "2024-04-27T21:32:50+00:00"
},
{
"name": "symfony/filesystem",
"version": "v6.4.3",
"version": "v6.4.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb"
"reference": "b51ef8059159330b74a4d52f68e671033c0fe463"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463",
"reference": "b51ef8059159330b74a4d52f68e671033c0fe463",
"shasum": ""
},
"require": {
@ -938,6 +943,9 @@
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"require-dev": {
"symfony/process": "^5.4|^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
@ -964,7 +972,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.4.3"
"source": "https://github.com/symfony/filesystem/tree/v6.4.9"
},
"funding": [
{
@ -980,20 +988,20 @@
"type": "tidelift"
}
],
"time": "2024-01-23T14:51:35+00:00"
"time": "2024-06-28T09:49:33+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.29.0",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
"shasum": ""
},
"require": {
@ -1043,7 +1051,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
},
"funding": [
{
@ -1059,20 +1067,20 @@
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
"time": "2024-05-31T15:07:36+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.29.0",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"shasum": ""
},
"require": {
@ -1123,7 +1131,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
},
"funding": [
{
@ -1139,22 +1147,22 @@
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
"time": "2024-06-19T12:30:46+00:00"
}
],
"packages-dev": [
{
"name": "myclabs/deep-copy",
"version": "1.11.1",
"version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"shasum": ""
},
"require": {
@ -1162,11 +1170,12 @@
},
"conflict": {
"doctrine/collections": "<1.6.8",
"doctrine/common": "<2.13.3 || >=3,<3.2.2"
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
},
"require-dev": {
"doctrine/collections": "^1.6.8",
"doctrine/common": "^2.13.3 || ^3.2.2",
"phpspec/prophecy": "^1.10",
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
},
"type": "library",
@ -1192,7 +1201,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
},
"funding": [
{
@ -1200,7 +1209,7 @@
"type": "tidelift"
}
],
"time": "2023-03-08T13:26:56+00:00"
"time": "2024-06-12T14:39:25+00:00"
},
{
"name": "nikic/php-parser",
@ -1380,16 +1389,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.60",
"version": "1.11.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe"
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3",
"shasum": ""
},
"require": {
@ -1432,31 +1441,27 @@
{
"url": "https://github.com/phpstan",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift"
}
],
"time": "2024-03-07T13:30:19+00:00"
"time": "2024-08-19T14:37:29+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.3.16",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95"
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d5242a59d035e46774f2e634b374bc39ff62cb95",
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.10"
"phpstan/phpstan": "^1.11"
},
"conflict": {
"phpunit/phpunit": "<7.0"
@ -1488,27 +1493,27 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.16"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0"
},
"time": "2024-02-23T09:51:20+00:00"
"time": "2024-04-20T06:39:00+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "1.5.2",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542"
"reference": "363f921dd8441777d4fc137deb99beb486c77df1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542",
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.10.34"
"phpstan/phpstan": "^1.11"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
@ -1537,22 +1542,22 @@
"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.5.2"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.0"
},
"time": "2023-10-30T14:35:06+00:00"
"time": "2024-04-20T06:37:51+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.14",
"version": "10.1.15",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
"reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae",
"reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae",
"shasum": ""
},
"require": {
@ -1609,7 +1614,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15"
},
"funding": [
{
@ -1617,7 +1622,7 @@
"type": "github"
}
],
"time": "2024-03-12T15:33:41+00:00"
"time": "2024-06-29T08:25:15+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1864,16 +1869,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.3.5",
"version": "10.5.24",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503"
"reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503",
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f124e3e3e561006047b532fd0431bf5bb6b9015",
"reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015",
"shasum": ""
},
"require": {
@ -1913,7 +1918,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "10.3-dev"
"dev-main": "10.5-dev"
}
},
"autoload": {
@ -1945,7 +1950,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.24"
},
"funding": [
{
@ -1961,7 +1966,7 @@
"type": "tidelift"
}
],
"time": "2023-09-19T05:42:37+00:00"
"time": "2024-06-20T13:09:54+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2335,16 +2340,16 @@
},
{
"name": "sebastian/environment",
"version": "6.0.1",
"version": "6.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951"
"reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951",
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
"reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
"shasum": ""
},
"require": {
@ -2359,7 +2364,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "6.0-dev"
"dev-main": "6.1-dev"
}
},
"autoload": {
@ -2387,7 +2392,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"security": "https://github.com/sebastianbergmann/environment/security/policy",
"source": "https://github.com/sebastianbergmann/environment/tree/6.0.1"
"source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
},
"funding": [
{
@ -2395,7 +2400,7 @@
"type": "github"
}
],
"time": "2023-04-11T05:39:26+00:00"
"time": "2024-03-23T08:47:14+00:00"
},
{
"name": "sebastian/exporter",
@ -2953,7 +2958,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pmmpthread": "^6.0.7",
"ext-pmmpthread": "^6.1.0",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",

21
install-local-protocol.sh Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
echo "--- Installing BedrockProtocol, BedrockData, BedrockBlockUpgradeSchema, BedrockItemUpgradeSchema dependencies from local repositories."
echo "--- This allows you to perform integration tests using PocketMine-MP, without immediately publishing new versions of these libraries."
cp composer.json composer-local-protocol.json
cp composer.lock composer-local-protocol.lock
export COMPOSER=composer-local-protocol.json
composer config repositories.bedrock-protocol path ../deps/BedrockProtocol
composer config repositories.bedrock-data path ../deps/BedrockData
composer config repositories.bedrock-block-upgrade-schema path ../deps/BedrockBlockUpgradeSchema
composer config repositories.bedrock-item-upgrade-schema path ../deps/BedrockItemUpgradeSchema
composer require pocketmine/bedrock-protocol:*@dev pocketmine/bedrock-data:*@dev pocketmine/bedrock-block-upgrade-schema:*@dev pocketmine/bedrock-item-upgrade-schema:*@dev
composer install
echo "--- Local dependencies have been successfully installed."
echo "--- This script does not modify composer.json. To go back to the original dependency versions, simply run 'composer install'."

View File

@ -47,4 +47,6 @@ final class BootstrapOptions{
public const DATA = "data";
/** Shows basic server version information and exits */
public const VERSION = "version";
/** Disables writing logs to server.log */
public const NO_LOG_FILE = "no-log-file";
}

View File

@ -124,8 +124,8 @@ namespace pocketmine {
}
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
if(version_compare($pmmpthread_version, "6.0.7") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
$messages[] = "pmmpthread ^6.0.7 is required, while you have $pmmpthread_version.";
if(version_compare($pmmpthread_version, "6.1.0") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
$messages[] = "pmmpthread ^6.1.0 is required, while you have $pmmpthread_version.";
}
}
@ -317,7 +317,7 @@ JIT_WARNING
//Logger has a dependency on timezone
Timezone::init();
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI]);
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI, BootstrapOptions::NO_LOG_FILE]);
if(isset($opts[BootstrapOptions::ENABLE_ANSI])){
Terminal::init(true);
}elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){
@ -325,8 +325,13 @@ JIT_WARNING
}else{
Terminal::init();
}
$logFile = isset($opts[BootstrapOptions::NO_LOG_FILE]) ? null : Path::join($dataPath, "server.log");
$logger = new MainLogger($logFile, Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()), false, Path::join($dataPath, "log_archive"));
if($logFile === null){
$logger->notice("Logging to file disabled. Ensure logs are collected by other means (e.g. Docker logs).");
}
$logger = new MainLogger(Path::join($dataPath, "server.log"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()));
\GlobalLogger::set($logger);
emit_performance_warnings($logger);

View File

@ -1666,9 +1666,11 @@ class Server{
$this->isRunning = false;
//Force minimum uptime to be >= 120 seconds, to reduce the impact of spammy crash loops
$spacing = ((int) $this->startTime) - time() + 120;
$uptime = time() - ((int) $this->startTime);
$minUptime = 120;
$spacing = $minUptime - $uptime;
if($spacing > 0){
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
echo "--- Uptime {$uptime}s - waiting {$spacing}s to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
sleep($spacing);
}
@Process::kill(Process::pid());

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.12.1";
public const BASE_VERSION = "5.20.0";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";
@ -63,7 +63,8 @@ final class VersionInfo{
if(\Phar::running(true) === ""){
$gitHash = Git::getRepositoryStatePretty(\pocketmine\PATH);
}else{
$phar = new \Phar(\Phar::running(false));
$pharPath = \Phar::running(false);
$phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath);
$meta = $phar->getMetadata();
if(isset($meta["git"])){
$gitHash = $meta["git"];
@ -82,7 +83,8 @@ final class VersionInfo{
if(self::$buildNumber === null){
self::$buildNumber = 0;
if(\Phar::running(true) !== ""){
$phar = new \Phar(\Phar::running(false));
$pharPath = \Phar::running(false);
$phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath);
$meta = $phar->getMetadata();
if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){
self::$buildNumber = $meta["build"];

View File

@ -91,7 +91,7 @@ class Anvil extends Transparent implements Fallable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){
$this->facing = Facing::rotateY($player->getHorizontalFacing(), true);
$this->facing = Facing::rotateY($player->getHorizontalFacing(), false);
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -150,6 +150,10 @@ final class Bell extends Transparent{
}
}
public function getDropsForIncompatibleTool(Item $item) : array{
return [$this->asItem()];
}
private function isValidFaceToRing(int $faceHit) : bool{
return match($this->attachmentType){
BellAttachmentType::CEILING => true,

View File

@ -27,10 +27,6 @@ use pocketmine\item\Item;
class BlueIce extends Opaque{
public function getLightLevel() : int{
return 1;
}
public function getFrictionFactor() : float{
return 0.99;
}
@ -38,4 +34,8 @@ class BlueIce extends Opaque{
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
public function isAffectedBySilkTouch() : bool{
return true;
}
}

View File

@ -34,6 +34,7 @@ final class NetherRoots extends Flowable{
$supportBlock = $block->getSide(Facing::DOWN);
return
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD);
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
$supportBlock->getTypeId() === BlockTypeIds::SOUL_SOIL;
}
}

View File

@ -36,6 +36,7 @@ use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\SweetBerriesPickSound;
use function mt_rand;
class SweetBerryBush extends Flowable{
@ -81,6 +82,7 @@ class SweetBerryBush extends Flowable{
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
$world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
$world->dropItem($this->position, $this->asItem()->setCount($dropAmount));
$world->addSound($this->position, new SweetBerriesPickSound());
}
return true;

View File

@ -28,6 +28,10 @@ use pocketmine\block\utils\WoodTypeTrait;
class WoodenStairs extends Stair{
use WoodTypeTrait;
public function getFuelTime() : int{
return $this->woodType->isFlammable() ? 300 : 0;
}
public function getFlameEncouragement() : int{
return 5;
}

View File

@ -82,6 +82,7 @@ enum BannerPatternType{
case DIAGONAL_UP_LEFT;
case DIAGONAL_UP_RIGHT;
case FLOWER;
case GLOBE;
case GRADIENT;
case GRADIENT_UP;
case HALF_HORIZONTAL;
@ -89,6 +90,7 @@ enum BannerPatternType{
case HALF_VERTICAL;
case HALF_VERTICAL_RIGHT;
case MOJANG;
case PIGLIN;
case RHOMBUS;
case SKULL;
case SMALL_STRIPES;

View File

@ -236,6 +236,7 @@ final class CraftingManagerFromDataHelper{
}
$outputs[] = $output;
}
//TODO: check unlocking requirements - our current system doesn't support this
$result->registerShapelessRecipe(new ShapelessRecipe(
$inputs,
$outputs,
@ -262,6 +263,7 @@ final class CraftingManagerFromDataHelper{
}
$outputs[] = $output;
}
//TODO: check unlocking requirements - our current system doesn't support this
$result->registerShapedRecipe(new ShapedRecipe(
$recipe->shape,
$inputs,

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\crafting\json;
final class ShapedRecipeData{
use function count;
final class ShapedRecipeData implements \JsonSerializable{
/**
* @required
* @var string[]
@ -51,22 +53,39 @@ final class ShapedRecipeData{
/** @required */
public int $priority;
/** @var RecipeIngredientData[] */
public array $unlockingIngredients = [];
/**
* TODO: convert this to use promoted properties - avoiding them for now since it would break JsonMapper
*
* @param string[] $shape
* @param RecipeIngredientData[] $input
* @param ItemStackData[] $output
* @param RecipeIngredientData[] $unlockingIngredients
*
* @phpstan-param list<string> $shape
* @phpstan-param array<string, RecipeIngredientData> $input
* @phpstan-param list<ItemStackData> $output
* @phpstan-param list<RecipeIngredientData> $unlockingIngredients
*/
public function __construct(array $shape, array $input, array $output, string $block, int $priority){
public function __construct(array $shape, array $input, array $output, string $block, int $priority, array $unlockingIngredients = []){
$this->block = $block;
$this->priority = $priority;
$this->shape = $shape;
$this->input = $input;
$this->output = $output;
$this->unlockingIngredients = $unlockingIngredients;
}
/**
* @return mixed[]
*/
public function jsonSerialize() : array{
$result = (array) $this;
if(count($this->unlockingIngredients) === 0){
unset($result["unlockingIngredients"]);
}
return $result;
}
}

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\crafting\json;
final class ShapelessRecipeData{
use function count;
final class ShapelessRecipeData implements \JsonSerializable{
/**
* @required
@ -45,17 +47,34 @@ final class ShapelessRecipeData{
/** @required */
public int $priority;
/** @var RecipeIngredientData[] */
public array $unlockingIngredients = [];
/**
* @param RecipeIngredientData[] $input
* @param ItemStackData[] $output
* @param RecipeIngredientData[] $unlockingIngredients
*
* @phpstan-param list<RecipeIngredientData> $input
* @phpstan-param list<ItemStackData> $output
* @phpstan-param list<RecipeIngredientData> $unlockingIngredients
*/
public function __construct(array $input, array $output, string $block, int $priority){
public function __construct(array $input, array $output, string $block, int $priority, array $unlockingIngredients = []){
$this->block = $block;
$this->priority = $priority;
$this->input = $input;
$this->output = $output;
$this->unlockingIngredients = $unlockingIngredients;
}
/**
* @return mixed[]
*/
public function jsonSerialize() : array{
$result = (array) $this;
if(count($this->unlockingIngredients) === 0){
unset($result["unlockingIngredients"]);
}
return $result;
}
}

View File

@ -63,6 +63,12 @@ use function strpos;
use function substr;
use function zend_version;
use function zlib_encode;
use const E_COMPILE_ERROR;
use const E_CORE_ERROR;
use const E_ERROR;
use const E_PARSE;
use const E_RECOVERABLE_ERROR;
use const E_USER_ERROR;
use const FILE_IGNORE_NEW_LINES;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;
@ -85,6 +91,9 @@ class CrashDump{
public const PLUGIN_INVOLVEMENT_DIRECT = "direct";
public const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
public const FATAL_ERROR_MASK =
E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
private CrashDumpData $data;
private string $encodedData;
@ -186,7 +195,7 @@ class CrashDump{
$error = $lastExceptionError;
}else{
$error = error_get_last();
if($error === null){
if($error === null || ($error["type"] & self::FATAL_ERROR_MASK) === 0){
throw new \RuntimeException("Crash error information missing - did something use exit()?");
}
$error["trace"] = Utils::printableTrace(Utils::currentTrace(3)); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump

View File

@ -56,6 +56,7 @@ final class BannerPatternTypeIdMap{
BannerPatternType::DIAGONAL_UP_LEFT => "ld",
BannerPatternType::DIAGONAL_UP_RIGHT => "rud",
BannerPatternType::FLOWER => "flo",
BannerPatternType::GLOBE => "glb",
BannerPatternType::GRADIENT => "gra",
BannerPatternType::GRADIENT_UP => "gru",
BannerPatternType::HALF_HORIZONTAL => "hh",
@ -63,6 +64,7 @@ final class BannerPatternTypeIdMap{
BannerPatternType::HALF_VERTICAL => "vh",
BannerPatternType::HALF_VERTICAL_RIGHT => "vhr",
BannerPatternType::MOJANG => "moj",
BannerPatternType::PIGLIN => "pig",
BannerPatternType::RHOMBUS => "mr",
BannerPatternType::SKULL => "sku",
BannerPatternType::SMALL_STRIPES => "ss",

View File

@ -35,6 +35,7 @@ final class BedrockDataFiles{
public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt';
public const BIOME_ID_MAP_JSON = BEDROCK_DATA_PATH . '/biome_id_map.json';
public const BLOCK_ID_TO_ITEM_ID_MAP_JSON = BEDROCK_DATA_PATH . '/block_id_to_item_id_map.json';
public const BLOCK_PROPERTIES_TABLE_JSON = BEDROCK_DATA_PATH . '/block_properties_table.json';
public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json';
public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt';
public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json';

View File

@ -38,11 +38,14 @@ use function implode;
final class BlockStateData{
/**
* Bedrock version of the most recent backwards-incompatible change to blockstates.
*
* This is *not* the same as current game version. It should match the numbers in the
* newest blockstate upgrade schema used in BedrockBlockUpgradeSchema.
*/
public const CURRENT_VERSION =
(1 << 24) | //major
(20 << 16) | //minor
(60 << 8) | //patch
(21 << 16) | //minor
(40 << 8) | //patch
(1); //revision
public const TAG_NAME = "name";

View File

@ -34,7 +34,6 @@ final class BlockStateNames{
public const ACTIVE = "active";
public const AGE = "age";
public const AGE_BIT = "age_bit";
public const ALLOW_UNDERWATER_BIT = "allow_underwater_bit";
public const ATTACHED_BIT = "attached_bit";
public const ATTACHMENT = "attachment";
public const BAMBOO_LEAF_SIZE = "bamboo_leaf_size";
@ -42,7 +41,6 @@ final class BlockStateNames{
public const BIG_DRIPLEAF_HEAD = "big_dripleaf_head";
public const BIG_DRIPLEAF_TILT = "big_dripleaf_tilt";
public const BITE_COUNTER = "bite_counter";
public const BLOCK_LIGHT_LEVEL = "block_light_level";
public const BLOOM = "bloom";
public const BOOKS_STORED = "books_stored";
public const BREWING_STAND_SLOT_A_BIT = "brewing_stand_slot_a_bit";
@ -53,27 +51,19 @@ final class BlockStateNames{
public const CAN_SUMMON = "can_summon";
public const CANDLES = "candles";
public const CAULDRON_LIQUID = "cauldron_liquid";
public const CHEMISTRY_TABLE_TYPE = "chemistry_table_type";
public const CHISEL_TYPE = "chisel_type";
public const CLUSTER_COUNT = "cluster_count";
public const COLOR_BIT = "color_bit";
public const COMPOSTER_FILL_LEVEL = "composter_fill_level";
public const CONDITIONAL_BIT = "conditional_bit";
public const CORAL_COLOR = "coral_color";
public const CORAL_DIRECTION = "coral_direction";
public const CORAL_FAN_DIRECTION = "coral_fan_direction";
public const CORAL_HANG_TYPE_BIT = "coral_hang_type_bit";
public const COVERED_BIT = "covered_bit";
public const CRACKED_STATE = "cracked_state";
public const CRAFTING = "crafting";
public const DAMAGE = "damage";
public const DEAD_BIT = "dead_bit";
public const DEPRECATED = "deprecated";
public const DIRECTION = "direction";
public const DIRT_TYPE = "dirt_type";
public const DISARMED_BIT = "disarmed_bit";
public const DOOR_HINGE_BIT = "door_hinge_bit";
public const DOUBLE_PLANT_TYPE = "double_plant_type";
public const DRAG_DOWN = "drag_down";
public const DRIPSTONE_THICKNESS = "dripstone_thickness";
public const END_PORTAL_EYE_BIT = "end_portal_eye_bit";
@ -81,7 +71,6 @@ final class BlockStateNames{
public const EXTINGUISHED = "extinguished";
public const FACING_DIRECTION = "facing_direction";
public const FILL_LEVEL = "fill_level";
public const FLOWER_TYPE = "flower_type";
public const GROUND_SIGN_DIRECTION = "ground_sign_direction";
public const GROWING_PLANT_AGE = "growing_plant_age";
public const GROWTH = "growth";
@ -103,11 +92,9 @@ final class BlockStateNames{
public const MC_FACING_DIRECTION = "minecraft:facing_direction";
public const MC_VERTICAL_HALF = "minecraft:vertical_half";
public const MOISTURIZED_AMOUNT = "moisturized_amount";
public const MONSTER_EGG_STONE_TYPE = "monster_egg_stone_type";
public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits";
public const NEW_LEAF_TYPE = "new_leaf_type";
public const OCCUPIED_BIT = "occupied_bit";
public const OLD_LEAF_TYPE = "old_leaf_type";
public const OMINOUS = "ominous";
public const OPEN_BIT = "open_bit";
public const ORIENTATION = "orientation";
public const OUTPUT_LIT_BIT = "output_lit_bit";
@ -116,7 +103,6 @@ final class BlockStateNames{
public const PILLAR_AXIS = "pillar_axis";
public const PORTAL_AXIS = "portal_axis";
public const POWERED_BIT = "powered_bit";
public const PRISMARINE_BLOCK_TYPE = "prismarine_block_type";
public const PROPAGULE_STAGE = "propagule_stage";
public const RAIL_DATA_BIT = "rail_data_bit";
public const RAIL_DIRECTION = "rail_direction";
@ -124,24 +110,12 @@ final class BlockStateNames{
public const REPEATER_DELAY = "repeater_delay";
public const RESPAWN_ANCHOR_CHARGE = "respawn_anchor_charge";
public const ROTATION = "rotation";
public const SAND_STONE_TYPE = "sand_stone_type";
public const SAND_TYPE = "sand_type";
public const SAPLING_TYPE = "sapling_type";
public const SCULK_SENSOR_PHASE = "sculk_sensor_phase";
public const SEA_GRASS_TYPE = "sea_grass_type";
public const SPONGE_TYPE = "sponge_type";
public const STABILITY = "stability";
public const STABILITY_CHECK = "stability_check";
public const STONE_BRICK_TYPE = "stone_brick_type";
public const STONE_SLAB_TYPE = "stone_slab_type";
public const STONE_SLAB_TYPE_2 = "stone_slab_type_2";
public const STONE_SLAB_TYPE_3 = "stone_slab_type_3";
public const STONE_SLAB_TYPE_4 = "stone_slab_type_4";
public const STRIPPED_BIT = "stripped_bit";
public const STRUCTURE_BLOCK_TYPE = "structure_block_type";
public const STRUCTURE_VOID_TYPE = "structure_void_type";
public const SUSPENDED_BIT = "suspended_bit";
public const TALL_GRASS_TYPE = "tall_grass_type";
public const TOGGLE_BIT = "toggle_bit";
public const TORCH_FACING_DIRECTION = "torch_facing_direction";
public const TRIAL_SPAWNER_STATE = "trial_spawner_state";
@ -151,8 +125,8 @@ final class BlockStateNames{
public const UPDATE_BIT = "update_bit";
public const UPPER_BLOCK_BIT = "upper_block_bit";
public const UPSIDE_DOWN_BIT = "upside_down_bit";
public const VAULT_STATE = "vault_state";
public const VINE_DIRECTION_BITS = "vine_direction_bits";
public const WALL_BLOCK_TYPE = "wall_block_type";
public const WALL_CONNECTION_TYPE_EAST = "wall_connection_type_east";
public const WALL_CONNECTION_TYPE_NORTH = "wall_connection_type_north";
public const WALL_CONNECTION_TYPE_SOUTH = "wall_connection_type_south";
@ -160,5 +134,4 @@ final class BlockStateNames{
public const WALL_POST_BIT = "wall_post_bit";
public const WEEPING_VINES_AGE = "weeping_vines_age";
public const WEIRDO_DIRECTION = "weirdo_direction";
public const WOOD_TYPE = "wood_type";
}

View File

@ -52,59 +52,16 @@ final class BlockStateStringValues{
public const CAULDRON_LIQUID_POWDER_SNOW = "powder_snow";
public const CAULDRON_LIQUID_WATER = "water";
public const CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR = "compound_creator";
public const CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR = "element_constructor";
public const CHEMISTRY_TABLE_TYPE_LAB_TABLE = "lab_table";
public const CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER = "material_reducer";
public const CHISEL_TYPE_CHISELED = "chiseled";
public const CHISEL_TYPE_DEFAULT = "default";
public const CHISEL_TYPE_LINES = "lines";
public const CHISEL_TYPE_SMOOTH = "smooth";
public const CORAL_COLOR_BLUE = "blue";
public const CORAL_COLOR_PINK = "pink";
public const CORAL_COLOR_PURPLE = "purple";
public const CORAL_COLOR_RED = "red";
public const CORAL_COLOR_YELLOW = "yellow";
public const CRACKED_STATE_CRACKED = "cracked";
public const CRACKED_STATE_MAX_CRACKED = "max_cracked";
public const CRACKED_STATE_NO_CRACKS = "no_cracks";
public const DAMAGE_BROKEN = "broken";
public const DAMAGE_SLIGHTLY_DAMAGED = "slightly_damaged";
public const DAMAGE_UNDAMAGED = "undamaged";
public const DAMAGE_VERY_DAMAGED = "very_damaged";
public const DIRT_TYPE_COARSE = "coarse";
public const DIRT_TYPE_NORMAL = "normal";
public const DOUBLE_PLANT_TYPE_FERN = "fern";
public const DOUBLE_PLANT_TYPE_GRASS = "grass";
public const DOUBLE_PLANT_TYPE_PAEONIA = "paeonia";
public const DOUBLE_PLANT_TYPE_ROSE = "rose";
public const DOUBLE_PLANT_TYPE_SUNFLOWER = "sunflower";
public const DOUBLE_PLANT_TYPE_SYRINGA = "syringa";
public const DRIPSTONE_THICKNESS_BASE = "base";
public const DRIPSTONE_THICKNESS_FRUSTUM = "frustum";
public const DRIPSTONE_THICKNESS_MERGE = "merge";
public const DRIPSTONE_THICKNESS_MIDDLE = "middle";
public const DRIPSTONE_THICKNESS_TIP = "tip";
public const FLOWER_TYPE_ALLIUM = "allium";
public const FLOWER_TYPE_CORNFLOWER = "cornflower";
public const FLOWER_TYPE_HOUSTONIA = "houstonia";
public const FLOWER_TYPE_LILY_OF_THE_VALLEY = "lily_of_the_valley";
public const FLOWER_TYPE_ORCHID = "orchid";
public const FLOWER_TYPE_OXEYE = "oxeye";
public const FLOWER_TYPE_POPPY = "poppy";
public const FLOWER_TYPE_TULIP_ORANGE = "tulip_orange";
public const FLOWER_TYPE_TULIP_PINK = "tulip_pink";
public const FLOWER_TYPE_TULIP_RED = "tulip_red";
public const FLOWER_TYPE_TULIP_WHITE = "tulip_white";
public const LEVER_DIRECTION_DOWN_EAST_WEST = "down_east_west";
public const LEVER_DIRECTION_DOWN_NORTH_SOUTH = "down_north_south";
public const LEVER_DIRECTION_EAST = "east";
@ -136,21 +93,6 @@ final class BlockStateStringValues{
public const MC_VERTICAL_HALF_BOTTOM = "bottom";
public const MC_VERTICAL_HALF_TOP = "top";
public const MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK = "chiseled_stone_brick";
public const MONSTER_EGG_STONE_TYPE_COBBLESTONE = "cobblestone";
public const MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK = "cracked_stone_brick";
public const MONSTER_EGG_STONE_TYPE_MOSSY_STONE_BRICK = "mossy_stone_brick";
public const MONSTER_EGG_STONE_TYPE_STONE = "stone";
public const MONSTER_EGG_STONE_TYPE_STONE_BRICK = "stone_brick";
public const NEW_LEAF_TYPE_ACACIA = "acacia";
public const NEW_LEAF_TYPE_DARK_OAK = "dark_oak";
public const OLD_LEAF_TYPE_BIRCH = "birch";
public const OLD_LEAF_TYPE_JUNGLE = "jungle";
public const OLD_LEAF_TYPE_OAK = "oak";
public const OLD_LEAF_TYPE_SPRUCE = "spruce";
public const ORIENTATION_DOWN_EAST = "down_east";
public const ORIENTATION_DOWN_NORTH = "down_north";
public const ORIENTATION_DOWN_SOUTH = "down_south";
@ -172,71 +114,10 @@ final class BlockStateStringValues{
public const PORTAL_AXIS_X = "x";
public const PORTAL_AXIS_Z = "z";
public const PRISMARINE_BLOCK_TYPE_BRICKS = "bricks";
public const PRISMARINE_BLOCK_TYPE_DARK = "dark";
public const PRISMARINE_BLOCK_TYPE_DEFAULT = "default";
public const SAND_STONE_TYPE_CUT = "cut";
public const SAND_STONE_TYPE_DEFAULT = "default";
public const SAND_STONE_TYPE_HEIROGLYPHS = "heiroglyphs";
public const SAND_STONE_TYPE_SMOOTH = "smooth";
public const SAND_TYPE_NORMAL = "normal";
public const SAND_TYPE_RED = "red";
public const SAPLING_TYPE_ACACIA = "acacia";
public const SAPLING_TYPE_BIRCH = "birch";
public const SAPLING_TYPE_DARK_OAK = "dark_oak";
public const SAPLING_TYPE_JUNGLE = "jungle";
public const SAPLING_TYPE_OAK = "oak";
public const SAPLING_TYPE_SPRUCE = "spruce";
public const SEA_GRASS_TYPE_DEFAULT = "default";
public const SEA_GRASS_TYPE_DOUBLE_BOT = "double_bot";
public const SEA_GRASS_TYPE_DOUBLE_TOP = "double_top";
public const SPONGE_TYPE_DRY = "dry";
public const SPONGE_TYPE_WET = "wet";
public const STONE_BRICK_TYPE_CHISELED = "chiseled";
public const STONE_BRICK_TYPE_CRACKED = "cracked";
public const STONE_BRICK_TYPE_DEFAULT = "default";
public const STONE_BRICK_TYPE_MOSSY = "mossy";
public const STONE_BRICK_TYPE_SMOOTH = "smooth";
public const STONE_SLAB_TYPE_BRICK = "brick";
public const STONE_SLAB_TYPE_COBBLESTONE = "cobblestone";
public const STONE_SLAB_TYPE_NETHER_BRICK = "nether_brick";
public const STONE_SLAB_TYPE_QUARTZ = "quartz";
public const STONE_SLAB_TYPE_SANDSTONE = "sandstone";
public const STONE_SLAB_TYPE_SMOOTH_STONE = "smooth_stone";
public const STONE_SLAB_TYPE_STONE_BRICK = "stone_brick";
public const STONE_SLAB_TYPE_WOOD = "wood";
public const STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE = "mossy_cobblestone";
public const STONE_SLAB_TYPE_2_PRISMARINE_BRICK = "prismarine_brick";
public const STONE_SLAB_TYPE_2_PRISMARINE_DARK = "prismarine_dark";
public const STONE_SLAB_TYPE_2_PRISMARINE_ROUGH = "prismarine_rough";
public const STONE_SLAB_TYPE_2_PURPUR = "purpur";
public const STONE_SLAB_TYPE_2_RED_NETHER_BRICK = "red_nether_brick";
public const STONE_SLAB_TYPE_2_RED_SANDSTONE = "red_sandstone";
public const STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE = "smooth_sandstone";
public const STONE_SLAB_TYPE_3_ANDESITE = "andesite";
public const STONE_SLAB_TYPE_3_DIORITE = "diorite";
public const STONE_SLAB_TYPE_3_END_STONE_BRICK = "end_stone_brick";
public const STONE_SLAB_TYPE_3_GRANITE = "granite";
public const STONE_SLAB_TYPE_3_POLISHED_ANDESITE = "polished_andesite";
public const STONE_SLAB_TYPE_3_POLISHED_DIORITE = "polished_diorite";
public const STONE_SLAB_TYPE_3_POLISHED_GRANITE = "polished_granite";
public const STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE = "smooth_red_sandstone";
public const STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE = "cut_red_sandstone";
public const STONE_SLAB_TYPE_4_CUT_SANDSTONE = "cut_sandstone";
public const STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK = "mossy_stone_brick";
public const STONE_SLAB_TYPE_4_SMOOTH_QUARTZ = "smooth_quartz";
public const STONE_SLAB_TYPE_4_STONE = "stone";
public const STRUCTURE_BLOCK_TYPE_CORNER = "corner";
public const STRUCTURE_BLOCK_TYPE_DATA = "data";
public const STRUCTURE_BLOCK_TYPE_EXPORT = "export";
@ -244,14 +125,6 @@ final class BlockStateStringValues{
public const STRUCTURE_BLOCK_TYPE_LOAD = "load";
public const STRUCTURE_BLOCK_TYPE_SAVE = "save";
public const STRUCTURE_VOID_TYPE_AIR = "air";
public const STRUCTURE_VOID_TYPE_VOID = "void";
public const TALL_GRASS_TYPE_DEFAULT = "default";
public const TALL_GRASS_TYPE_FERN = "fern";
public const TALL_GRASS_TYPE_SNOW = "snow";
public const TALL_GRASS_TYPE_TALL = "tall";
public const TORCH_FACING_DIRECTION_EAST = "east";
public const TORCH_FACING_DIRECTION_NORTH = "north";
public const TORCH_FACING_DIRECTION_SOUTH = "south";
@ -264,20 +137,10 @@ final class BlockStateStringValues{
public const TURTLE_EGG_COUNT_THREE_EGG = "three_egg";
public const TURTLE_EGG_COUNT_TWO_EGG = "two_egg";
public const WALL_BLOCK_TYPE_ANDESITE = "andesite";
public const WALL_BLOCK_TYPE_BRICK = "brick";
public const WALL_BLOCK_TYPE_COBBLESTONE = "cobblestone";
public const WALL_BLOCK_TYPE_DIORITE = "diorite";
public const WALL_BLOCK_TYPE_END_BRICK = "end_brick";
public const WALL_BLOCK_TYPE_GRANITE = "granite";
public const WALL_BLOCK_TYPE_MOSSY_COBBLESTONE = "mossy_cobblestone";
public const WALL_BLOCK_TYPE_MOSSY_STONE_BRICK = "mossy_stone_brick";
public const WALL_BLOCK_TYPE_NETHER_BRICK = "nether_brick";
public const WALL_BLOCK_TYPE_PRISMARINE = "prismarine";
public const WALL_BLOCK_TYPE_RED_NETHER_BRICK = "red_nether_brick";
public const WALL_BLOCK_TYPE_RED_SANDSTONE = "red_sandstone";
public const WALL_BLOCK_TYPE_SANDSTONE = "sandstone";
public const WALL_BLOCK_TYPE_STONE_BRICK = "stone_brick";
public const VAULT_STATE_ACTIVE = "active";
public const VAULT_STATE_EJECTING = "ejecting";
public const VAULT_STATE_INACTIVE = "inactive";
public const VAULT_STATE_UNLOCKING = "unlocking";
public const WALL_CONNECTION_TYPE_EAST_NONE = "none";
public const WALL_CONNECTION_TYPE_EAST_SHORT = "short";
@ -295,11 +158,4 @@ final class BlockStateStringValues{
public const WALL_CONNECTION_TYPE_WEST_SHORT = "short";
public const WALL_CONNECTION_TYPE_WEST_TALL = "tall";
public const WOOD_TYPE_ACACIA = "acacia";
public const WOOD_TYPE_BIRCH = "birch";
public const WOOD_TYPE_DARK_OAK = "dark_oak";
public const WOOD_TYPE_JUNGLE = "jungle";
public const WOOD_TYPE_OAK = "oak";
public const WOOD_TYPE_SPRUCE = "spruce";
}

View File

@ -33,28 +33,38 @@ final class BlockTypeNames{
public const ACACIA_BUTTON = "minecraft:acacia_button";
public const ACACIA_DOOR = "minecraft:acacia_door";
public const ACACIA_DOUBLE_SLAB = "minecraft:acacia_double_slab";
public const ACACIA_FENCE = "minecraft:acacia_fence";
public const ACACIA_FENCE_GATE = "minecraft:acacia_fence_gate";
public const ACACIA_HANGING_SIGN = "minecraft:acacia_hanging_sign";
public const ACACIA_LEAVES = "minecraft:acacia_leaves";
public const ACACIA_LOG = "minecraft:acacia_log";
public const ACACIA_PLANKS = "minecraft:acacia_planks";
public const ACACIA_PRESSURE_PLATE = "minecraft:acacia_pressure_plate";
public const ACACIA_SAPLING = "minecraft:acacia_sapling";
public const ACACIA_SLAB = "minecraft:acacia_slab";
public const ACACIA_STAIRS = "minecraft:acacia_stairs";
public const ACACIA_STANDING_SIGN = "minecraft:acacia_standing_sign";
public const ACACIA_TRAPDOOR = "minecraft:acacia_trapdoor";
public const ACACIA_WALL_SIGN = "minecraft:acacia_wall_sign";
public const ACACIA_WOOD = "minecraft:acacia_wood";
public const ACTIVATOR_RAIL = "minecraft:activator_rail";
public const AIR = "minecraft:air";
public const ALLIUM = "minecraft:allium";
public const ALLOW = "minecraft:allow";
public const AMETHYST_BLOCK = "minecraft:amethyst_block";
public const AMETHYST_CLUSTER = "minecraft:amethyst_cluster";
public const ANCIENT_DEBRIS = "minecraft:ancient_debris";
public const ANDESITE = "minecraft:andesite";
public const ANDESITE_DOUBLE_SLAB = "minecraft:andesite_double_slab";
public const ANDESITE_SLAB = "minecraft:andesite_slab";
public const ANDESITE_STAIRS = "minecraft:andesite_stairs";
public const ANDESITE_WALL = "minecraft:andesite_wall";
public const ANVIL = "minecraft:anvil";
public const AZALEA = "minecraft:azalea";
public const AZALEA_LEAVES = "minecraft:azalea_leaves";
public const AZALEA_LEAVES_FLOWERED = "minecraft:azalea_leaves_flowered";
public const AZURE_BLUET = "minecraft:azure_bluet";
public const BAMBOO = "minecraft:bamboo";
public const BAMBOO_BLOCK = "minecraft:bamboo_block";
public const BAMBOO_BUTTON = "minecraft:bamboo_button";
@ -88,16 +98,21 @@ final class BlockTypeNames{
public const BIG_DRIPLEAF = "minecraft:big_dripleaf";
public const BIRCH_BUTTON = "minecraft:birch_button";
public const BIRCH_DOOR = "minecraft:birch_door";
public const BIRCH_DOUBLE_SLAB = "minecraft:birch_double_slab";
public const BIRCH_FENCE = "minecraft:birch_fence";
public const BIRCH_FENCE_GATE = "minecraft:birch_fence_gate";
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
public const BIRCH_LEAVES = "minecraft:birch_leaves";
public const BIRCH_LOG = "minecraft:birch_log";
public const BIRCH_PLANKS = "minecraft:birch_planks";
public const BIRCH_PRESSURE_PLATE = "minecraft:birch_pressure_plate";
public const BIRCH_SAPLING = "minecraft:birch_sapling";
public const BIRCH_SLAB = "minecraft:birch_slab";
public const BIRCH_STAIRS = "minecraft:birch_stairs";
public const BIRCH_STANDING_SIGN = "minecraft:birch_standing_sign";
public const BIRCH_TRAPDOOR = "minecraft:birch_trapdoor";
public const BIRCH_WALL_SIGN = "minecraft:birch_wall_sign";
public const BIRCH_WOOD = "minecraft:birch_wood";
public const BLACK_CANDLE = "minecraft:black_candle";
public const BLACK_CANDLE_CAKE = "minecraft:black_candle_cake";
public const BLACK_CARPET = "minecraft:black_carpet";
@ -122,6 +137,7 @@ final class BlockTypeNames{
public const BLUE_CONCRETE_POWDER = "minecraft:blue_concrete_powder";
public const BLUE_GLAZED_TERRACOTTA = "minecraft:blue_glazed_terracotta";
public const BLUE_ICE = "minecraft:blue_ice";
public const BLUE_ORCHID = "minecraft:blue_orchid";
public const BLUE_SHULKER_BOX = "minecraft:blue_shulker_box";
public const BLUE_STAINED_GLASS = "minecraft:blue_stained_glass";
public const BLUE_STAINED_GLASS_PANE = "minecraft:blue_stained_glass_pane";
@ -131,9 +147,15 @@ final class BlockTypeNames{
public const BOOKSHELF = "minecraft:bookshelf";
public const BORDER_BLOCK = "minecraft:border_block";
public const BRAIN_CORAL = "minecraft:brain_coral";
public const BRAIN_CORAL_BLOCK = "minecraft:brain_coral_block";
public const BRAIN_CORAL_FAN = "minecraft:brain_coral_fan";
public const BRAIN_CORAL_WALL_FAN = "minecraft:brain_coral_wall_fan";
public const BREWING_STAND = "minecraft:brewing_stand";
public const BRICK_BLOCK = "minecraft:brick_block";
public const BRICK_DOUBLE_SLAB = "minecraft:brick_double_slab";
public const BRICK_SLAB = "minecraft:brick_slab";
public const BRICK_STAIRS = "minecraft:brick_stairs";
public const BRICK_WALL = "minecraft:brick_wall";
public const BROWN_CANDLE = "minecraft:brown_candle";
public const BROWN_CANDLE_CAKE = "minecraft:brown_candle_cake";
public const BROWN_CARPET = "minecraft:brown_carpet";
@ -149,6 +171,9 @@ final class BlockTypeNames{
public const BROWN_WOOL = "minecraft:brown_wool";
public const BUBBLE_COLUMN = "minecraft:bubble_column";
public const BUBBLE_CORAL = "minecraft:bubble_coral";
public const BUBBLE_CORAL_BLOCK = "minecraft:bubble_coral_block";
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 CACTUS = "minecraft:cactus";
public const CAKE = "minecraft:cake";
@ -168,7 +193,6 @@ final class BlockTypeNames{
public const CHAIN = "minecraft:chain";
public const CHAIN_COMMAND_BLOCK = "minecraft:chain_command_block";
public const CHEMICAL_HEAT = "minecraft:chemical_heat";
public const CHEMISTRY_TABLE = "minecraft:chemistry_table";
public const CHERRY_BUTTON = "minecraft:cherry_button";
public const CHERRY_DOOR = "minecraft:cherry_door";
public const CHERRY_DOUBLE_SLAB = "minecraft:cherry_double_slab";
@ -187,11 +211,16 @@ final class BlockTypeNames{
public const CHERRY_WALL_SIGN = "minecraft:cherry_wall_sign";
public const CHERRY_WOOD = "minecraft:cherry_wood";
public const CHEST = "minecraft:chest";
public const CHIPPED_ANVIL = "minecraft:chipped_anvil";
public const CHISELED_BOOKSHELF = "minecraft:chiseled_bookshelf";
public const CHISELED_COPPER = "minecraft:chiseled_copper";
public const CHISELED_DEEPSLATE = "minecraft:chiseled_deepslate";
public const CHISELED_NETHER_BRICKS = "minecraft:chiseled_nether_bricks";
public const CHISELED_POLISHED_BLACKSTONE = "minecraft:chiseled_polished_blackstone";
public const CHISELED_QUARTZ_BLOCK = "minecraft:chiseled_quartz_block";
public const CHISELED_RED_SANDSTONE = "minecraft:chiseled_red_sandstone";
public const CHISELED_SANDSTONE = "minecraft:chiseled_sandstone";
public const CHISELED_STONE_BRICKS = "minecraft:chiseled_stone_bricks";
public const CHISELED_TUFF = "minecraft:chiseled_tuff";
public const CHISELED_TUFF_BRICKS = "minecraft:chiseled_tuff_bricks";
public const CHORUS_FLOWER = "minecraft:chorus_flower";
@ -200,18 +229,24 @@ final class BlockTypeNames{
public const CLIENT_REQUEST_PLACEHOLDER_BLOCK = "minecraft:client_request_placeholder_block";
public const COAL_BLOCK = "minecraft:coal_block";
public const COAL_ORE = "minecraft:coal_ore";
public const COARSE_DIRT = "minecraft:coarse_dirt";
public const COBBLED_DEEPSLATE = "minecraft:cobbled_deepslate";
public const COBBLED_DEEPSLATE_DOUBLE_SLAB = "minecraft:cobbled_deepslate_double_slab";
public const COBBLED_DEEPSLATE_SLAB = "minecraft:cobbled_deepslate_slab";
public const COBBLED_DEEPSLATE_STAIRS = "minecraft:cobbled_deepslate_stairs";
public const COBBLED_DEEPSLATE_WALL = "minecraft:cobbled_deepslate_wall";
public const COBBLESTONE = "minecraft:cobblestone";
public const COBBLESTONE_DOUBLE_SLAB = "minecraft:cobblestone_double_slab";
public const COBBLESTONE_SLAB = "minecraft:cobblestone_slab";
public const COBBLESTONE_WALL = "minecraft:cobblestone_wall";
public const COCOA = "minecraft:cocoa";
public const COLORED_TORCH_BP = "minecraft:colored_torch_bp";
public const COLORED_TORCH_RG = "minecraft:colored_torch_rg";
public const COLORED_TORCH_BLUE = "minecraft:colored_torch_blue";
public const COLORED_TORCH_GREEN = "minecraft:colored_torch_green";
public const COLORED_TORCH_PURPLE = "minecraft:colored_torch_purple";
public const COLORED_TORCH_RED = "minecraft:colored_torch_red";
public const COMMAND_BLOCK = "minecraft:command_block";
public const COMPOSTER = "minecraft:composter";
public const COMPOUND_CREATOR = "minecraft:compound_creator";
public const CONDUIT = "minecraft:conduit";
public const COPPER_BLOCK = "minecraft:copper_block";
public const COPPER_BULB = "minecraft:copper_bulb";
@ -219,18 +254,15 @@ final class BlockTypeNames{
public const COPPER_GRATE = "minecraft:copper_grate";
public const COPPER_ORE = "minecraft:copper_ore";
public const COPPER_TRAPDOOR = "minecraft:copper_trapdoor";
public const CORAL_BLOCK = "minecraft:coral_block";
public const CORAL_FAN = "minecraft:coral_fan";
public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead";
public const CORAL_FAN_HANG = "minecraft:coral_fan_hang";
public const CORAL_FAN_HANG2 = "minecraft:coral_fan_hang2";
public const CORAL_FAN_HANG3 = "minecraft:coral_fan_hang3";
public const CORNFLOWER = "minecraft:cornflower";
public const CRACKED_DEEPSLATE_BRICKS = "minecraft:cracked_deepslate_bricks";
public const CRACKED_DEEPSLATE_TILES = "minecraft:cracked_deepslate_tiles";
public const CRACKED_NETHER_BRICKS = "minecraft:cracked_nether_bricks";
public const CRACKED_POLISHED_BLACKSTONE_BRICKS = "minecraft:cracked_polished_blackstone_bricks";
public const CRACKED_STONE_BRICKS = "minecraft:cracked_stone_bricks";
public const CRAFTER = "minecraft:crafter";
public const CRAFTING_TABLE = "minecraft:crafting_table";
public const CREEPER_HEAD = "minecraft:creeper_head";
public const CRIMSON_BUTTON = "minecraft:crimson_button";
public const CRIMSON_DOOR = "minecraft:crimson_door";
public const CRIMSON_DOUBLE_SLAB = "minecraft:crimson_double_slab";
@ -253,6 +285,12 @@ final class BlockTypeNames{
public const CUT_COPPER = "minecraft:cut_copper";
public const CUT_COPPER_SLAB = "minecraft:cut_copper_slab";
public const CUT_COPPER_STAIRS = "minecraft:cut_copper_stairs";
public const CUT_RED_SANDSTONE = "minecraft:cut_red_sandstone";
public const CUT_RED_SANDSTONE_DOUBLE_SLAB = "minecraft:cut_red_sandstone_double_slab";
public const CUT_RED_SANDSTONE_SLAB = "minecraft:cut_red_sandstone_slab";
public const CUT_SANDSTONE = "minecraft:cut_sandstone";
public const CUT_SANDSTONE_DOUBLE_SLAB = "minecraft:cut_sandstone_double_slab";
public const CUT_SANDSTONE_SLAB = "minecraft:cut_sandstone_slab";
public const CYAN_CANDLE = "minecraft:cyan_candle";
public const CYAN_CANDLE_CAKE = "minecraft:cyan_candle_cake";
public const CYAN_CARPET = "minecraft:cyan_carpet";
@ -264,26 +302,51 @@ final class BlockTypeNames{
public const CYAN_STAINED_GLASS_PANE = "minecraft:cyan_stained_glass_pane";
public const CYAN_TERRACOTTA = "minecraft:cyan_terracotta";
public const CYAN_WOOL = "minecraft:cyan_wool";
public const DAMAGED_ANVIL = "minecraft:damaged_anvil";
public const DANDELION = "minecraft:dandelion";
public const DARK_OAK_BUTTON = "minecraft:dark_oak_button";
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
public const DARK_OAK_DOUBLE_SLAB = "minecraft:dark_oak_double_slab";
public const DARK_OAK_FENCE = "minecraft:dark_oak_fence";
public const DARK_OAK_FENCE_GATE = "minecraft:dark_oak_fence_gate";
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
public const DARK_OAK_LEAVES = "minecraft:dark_oak_leaves";
public const DARK_OAK_LOG = "minecraft:dark_oak_log";
public const DARK_OAK_PLANKS = "minecraft:dark_oak_planks";
public const DARK_OAK_PRESSURE_PLATE = "minecraft:dark_oak_pressure_plate";
public const DARK_OAK_SAPLING = "minecraft:dark_oak_sapling";
public const DARK_OAK_SLAB = "minecraft:dark_oak_slab";
public const DARK_OAK_STAIRS = "minecraft:dark_oak_stairs";
public const DARK_OAK_TRAPDOOR = "minecraft:dark_oak_trapdoor";
public const DARK_OAK_WOOD = "minecraft:dark_oak_wood";
public const DARK_PRISMARINE = "minecraft:dark_prismarine";
public const DARK_PRISMARINE_DOUBLE_SLAB = "minecraft:dark_prismarine_double_slab";
public const DARK_PRISMARINE_SLAB = "minecraft:dark_prismarine_slab";
public const DARK_PRISMARINE_STAIRS = "minecraft:dark_prismarine_stairs";
public const DARKOAK_STANDING_SIGN = "minecraft:darkoak_standing_sign";
public const DARKOAK_WALL_SIGN = "minecraft:darkoak_wall_sign";
public const DAYLIGHT_DETECTOR = "minecraft:daylight_detector";
public const DAYLIGHT_DETECTOR_INVERTED = "minecraft:daylight_detector_inverted";
public const DEAD_BRAIN_CORAL = "minecraft:dead_brain_coral";
public const DEAD_BRAIN_CORAL_BLOCK = "minecraft:dead_brain_coral_block";
public const DEAD_BRAIN_CORAL_FAN = "minecraft:dead_brain_coral_fan";
public const DEAD_BRAIN_CORAL_WALL_FAN = "minecraft:dead_brain_coral_wall_fan";
public const DEAD_BUBBLE_CORAL = "minecraft:dead_bubble_coral";
public const DEAD_BUBBLE_CORAL_BLOCK = "minecraft:dead_bubble_coral_block";
public const DEAD_BUBBLE_CORAL_FAN = "minecraft:dead_bubble_coral_fan";
public const DEAD_BUBBLE_CORAL_WALL_FAN = "minecraft:dead_bubble_coral_wall_fan";
public const DEAD_FIRE_CORAL = "minecraft:dead_fire_coral";
public const DEAD_FIRE_CORAL_BLOCK = "minecraft:dead_fire_coral_block";
public const DEAD_FIRE_CORAL_FAN = "minecraft:dead_fire_coral_fan";
public const DEAD_FIRE_CORAL_WALL_FAN = "minecraft:dead_fire_coral_wall_fan";
public const DEAD_HORN_CORAL = "minecraft:dead_horn_coral";
public const DEAD_HORN_CORAL_BLOCK = "minecraft:dead_horn_coral_block";
public const DEAD_HORN_CORAL_FAN = "minecraft:dead_horn_coral_fan";
public const DEAD_HORN_CORAL_WALL_FAN = "minecraft:dead_horn_coral_wall_fan";
public const DEAD_TUBE_CORAL = "minecraft:dead_tube_coral";
public const DEAD_TUBE_CORAL_BLOCK = "minecraft:dead_tube_coral_block";
public const DEAD_TUBE_CORAL_FAN = "minecraft:dead_tube_coral_fan";
public const DEAD_TUBE_CORAL_WALL_FAN = "minecraft:dead_tube_coral_wall_fan";
public const DEADBUSH = "minecraft:deadbush";
public const DECORATED_POT = "minecraft:decorated_pot";
public const DEEPSLATE = "minecraft:deepslate";
@ -306,22 +369,23 @@ final class BlockTypeNames{
public const DEEPSLATE_TILE_WALL = "minecraft:deepslate_tile_wall";
public const DEEPSLATE_TILES = "minecraft:deepslate_tiles";
public const DENY = "minecraft:deny";
public const DEPRECATED_ANVIL = "minecraft:deprecated_anvil";
public const DEPRECATED_PURPUR_BLOCK_1 = "minecraft:deprecated_purpur_block_1";
public const DEPRECATED_PURPUR_BLOCK_2 = "minecraft:deprecated_purpur_block_2";
public const DETECTOR_RAIL = "minecraft:detector_rail";
public const DIAMOND_BLOCK = "minecraft:diamond_block";
public const DIAMOND_ORE = "minecraft:diamond_ore";
public const DIORITE = "minecraft:diorite";
public const DIORITE_DOUBLE_SLAB = "minecraft:diorite_double_slab";
public const DIORITE_SLAB = "minecraft:diorite_slab";
public const DIORITE_STAIRS = "minecraft:diorite_stairs";
public const DIORITE_WALL = "minecraft:diorite_wall";
public const DIRT = "minecraft:dirt";
public const DIRT_WITH_ROOTS = "minecraft:dirt_with_roots";
public const DISPENSER = "minecraft:dispenser";
public const DOUBLE_CUT_COPPER_SLAB = "minecraft:double_cut_copper_slab";
public const DOUBLE_PLANT = "minecraft:double_plant";
public const DOUBLE_STONE_BLOCK_SLAB = "minecraft:double_stone_block_slab";
public const DOUBLE_STONE_BLOCK_SLAB2 = "minecraft:double_stone_block_slab2";
public const DOUBLE_STONE_BLOCK_SLAB3 = "minecraft:double_stone_block_slab3";
public const DOUBLE_STONE_BLOCK_SLAB4 = "minecraft:double_stone_block_slab4";
public const DOUBLE_WOODEN_SLAB = "minecraft:double_wooden_slab";
public const DRAGON_EGG = "minecraft:dragon_egg";
public const DRAGON_HEAD = "minecraft:dragon_head";
public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block";
public const DRIPSTONE_BLOCK = "minecraft:dripstone_block";
public const DROPPER = "minecraft:dropper";
@ -444,6 +508,7 @@ final class BlockTypeNames{
public const ELEMENT_97 = "minecraft:element_97";
public const ELEMENT_98 = "minecraft:element_98";
public const ELEMENT_99 = "minecraft:element_99";
public const ELEMENT_CONSTRUCTOR = "minecraft:element_constructor";
public const EMERALD_BLOCK = "minecraft:emerald_block";
public const EMERALD_ORE = "minecraft:emerald_ore";
public const ENCHANTING_TABLE = "minecraft:enchanting_table";
@ -454,6 +519,9 @@ final class BlockTypeNames{
public const END_PORTAL_FRAME = "minecraft:end_portal_frame";
public const END_ROD = "minecraft:end_rod";
public const END_STONE = "minecraft:end_stone";
public const END_STONE_BRICK_DOUBLE_SLAB = "minecraft:end_stone_brick_double_slab";
public const END_STONE_BRICK_SLAB = "minecraft:end_stone_brick_slab";
public const END_STONE_BRICK_WALL = "minecraft:end_stone_brick_wall";
public const ENDER_CHEST = "minecraft:ender_chest";
public const EXPOSED_CHISELED_COPPER = "minecraft:exposed_chiseled_copper";
public const EXPOSED_COPPER = "minecraft:exposed_copper";
@ -467,8 +535,12 @@ final class BlockTypeNames{
public const EXPOSED_DOUBLE_CUT_COPPER_SLAB = "minecraft:exposed_double_cut_copper_slab";
public const FARMLAND = "minecraft:farmland";
public const FENCE_GATE = "minecraft:fence_gate";
public const FERN = "minecraft:fern";
public const FIRE = "minecraft:fire";
public const FIRE_CORAL = "minecraft:fire_coral";
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 FLETCHING_TABLE = "minecraft:fletching_table";
public const FLOWER_POT = "minecraft:flower_pot";
public const FLOWERING_AZALEA = "minecraft:flowering_azalea";
@ -489,8 +561,11 @@ final class BlockTypeNames{
public const GOLD_ORE = "minecraft:gold_ore";
public const GOLDEN_RAIL = "minecraft:golden_rail";
public const GRANITE = "minecraft:granite";
public const GRANITE_DOUBLE_SLAB = "minecraft:granite_double_slab";
public const GRANITE_SLAB = "minecraft:granite_slab";
public const GRANITE_STAIRS = "minecraft:granite_stairs";
public const GRASS = "minecraft:grass";
public const GRANITE_WALL = "minecraft:granite_wall";
public const GRASS_BLOCK = "minecraft:grass_block";
public const GRASS_PATH = "minecraft:grass_path";
public const GRAVEL = "minecraft:gravel";
public const GRAY_CANDLE = "minecraft:gray_candle";
@ -553,13 +628,23 @@ final class BlockTypeNames{
public const HARD_YELLOW_STAINED_GLASS_PANE = "minecraft:hard_yellow_stained_glass_pane";
public const HARDENED_CLAY = "minecraft:hardened_clay";
public const HAY_BLOCK = "minecraft:hay_block";
public const HEAVY_CORE = "minecraft:heavy_core";
public const HEAVY_WEIGHTED_PRESSURE_PLATE = "minecraft:heavy_weighted_pressure_plate";
public const HONEY_BLOCK = "minecraft:honey_block";
public const HONEYCOMB_BLOCK = "minecraft:honeycomb_block";
public const HOPPER = "minecraft:hopper";
public const HORN_CORAL = "minecraft:horn_coral";
public const HORN_CORAL_BLOCK = "minecraft:horn_coral_block";
public const HORN_CORAL_FAN = "minecraft:horn_coral_fan";
public const HORN_CORAL_WALL_FAN = "minecraft:horn_coral_wall_fan";
public const ICE = "minecraft:ice";
public const INFESTED_CHISELED_STONE_BRICKS = "minecraft:infested_chiseled_stone_bricks";
public const INFESTED_COBBLESTONE = "minecraft:infested_cobblestone";
public const INFESTED_CRACKED_STONE_BRICKS = "minecraft:infested_cracked_stone_bricks";
public const INFESTED_DEEPSLATE = "minecraft:infested_deepslate";
public const INFESTED_MOSSY_STONE_BRICKS = "minecraft:infested_mossy_stone_bricks";
public const INFESTED_STONE = "minecraft:infested_stone";
public const INFESTED_STONE_BRICKS = "minecraft:infested_stone_bricks";
public const INFO_UPDATE = "minecraft:info_update";
public const INFO_UPDATE2 = "minecraft:info_update2";
public const INVISIBLE_BEDROCK = "minecraft:invisible_bedrock";
@ -572,28 +657,48 @@ final class BlockTypeNames{
public const JUKEBOX = "minecraft:jukebox";
public const JUNGLE_BUTTON = "minecraft:jungle_button";
public const JUNGLE_DOOR = "minecraft:jungle_door";
public const JUNGLE_DOUBLE_SLAB = "minecraft:jungle_double_slab";
public const JUNGLE_FENCE = "minecraft:jungle_fence";
public const JUNGLE_FENCE_GATE = "minecraft:jungle_fence_gate";
public const JUNGLE_HANGING_SIGN = "minecraft:jungle_hanging_sign";
public const JUNGLE_LEAVES = "minecraft:jungle_leaves";
public const JUNGLE_LOG = "minecraft:jungle_log";
public const JUNGLE_PLANKS = "minecraft:jungle_planks";
public const JUNGLE_PRESSURE_PLATE = "minecraft:jungle_pressure_plate";
public const JUNGLE_SAPLING = "minecraft:jungle_sapling";
public const JUNGLE_SLAB = "minecraft:jungle_slab";
public const JUNGLE_STAIRS = "minecraft:jungle_stairs";
public const JUNGLE_STANDING_SIGN = "minecraft:jungle_standing_sign";
public const JUNGLE_TRAPDOOR = "minecraft:jungle_trapdoor";
public const JUNGLE_WALL_SIGN = "minecraft:jungle_wall_sign";
public const JUNGLE_WOOD = "minecraft:jungle_wood";
public const KELP = "minecraft:kelp";
public const LAB_TABLE = "minecraft:lab_table";
public const LADDER = "minecraft:ladder";
public const LANTERN = "minecraft:lantern";
public const LAPIS_BLOCK = "minecraft:lapis_block";
public const LAPIS_ORE = "minecraft:lapis_ore";
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
public const LARGE_FERN = "minecraft:large_fern";
public const LAVA = "minecraft:lava";
public const LEAVES = "minecraft:leaves";
public const LEAVES2 = "minecraft:leaves2";
public const LECTERN = "minecraft:lectern";
public const LEVER = "minecraft:lever";
public const LIGHT_BLOCK = "minecraft:light_block";
public const LIGHT_BLOCK_0 = "minecraft:light_block_0";
public const LIGHT_BLOCK_1 = "minecraft:light_block_1";
public const LIGHT_BLOCK_10 = "minecraft:light_block_10";
public const LIGHT_BLOCK_11 = "minecraft:light_block_11";
public const LIGHT_BLOCK_12 = "minecraft:light_block_12";
public const LIGHT_BLOCK_13 = "minecraft:light_block_13";
public const LIGHT_BLOCK_14 = "minecraft:light_block_14";
public const LIGHT_BLOCK_15 = "minecraft:light_block_15";
public const LIGHT_BLOCK_2 = "minecraft:light_block_2";
public const LIGHT_BLOCK_3 = "minecraft:light_block_3";
public const LIGHT_BLOCK_4 = "minecraft:light_block_4";
public const LIGHT_BLOCK_5 = "minecraft:light_block_5";
public const LIGHT_BLOCK_6 = "minecraft:light_block_6";
public const LIGHT_BLOCK_7 = "minecraft:light_block_7";
public const LIGHT_BLOCK_8 = "minecraft:light_block_8";
public const LIGHT_BLOCK_9 = "minecraft:light_block_9";
public const LIGHT_BLUE_CANDLE = "minecraft:light_blue_candle";
public const LIGHT_BLUE_CANDLE_CAKE = "minecraft:light_blue_candle_cake";
public const LIGHT_BLUE_CARPET = "minecraft:light_blue_carpet";
@ -617,6 +722,8 @@ final class BlockTypeNames{
public const LIGHT_GRAY_WOOL = "minecraft:light_gray_wool";
public const LIGHT_WEIGHTED_PRESSURE_PLATE = "minecraft:light_weighted_pressure_plate";
public const LIGHTNING_ROD = "minecraft:lightning_rod";
public const LILAC = "minecraft:lilac";
public const LILY_OF_THE_VALLEY = "minecraft:lily_of_the_valley";
public const LIME_CANDLE = "minecraft:lime_candle";
public const LIME_CANDLE_CAKE = "minecraft:lime_candle_cake";
public const LIME_CARPET = "minecraft:lime_carpet";
@ -667,16 +774,23 @@ final class BlockTypeNames{
public const MANGROVE_TRAPDOOR = "minecraft:mangrove_trapdoor";
public const MANGROVE_WALL_SIGN = "minecraft:mangrove_wall_sign";
public const MANGROVE_WOOD = "minecraft:mangrove_wood";
public const MATERIAL_REDUCER = "minecraft:material_reducer";
public const MEDIUM_AMETHYST_BUD = "minecraft:medium_amethyst_bud";
public const MELON_BLOCK = "minecraft:melon_block";
public const MELON_STEM = "minecraft:melon_stem";
public const MOB_SPAWNER = "minecraft:mob_spawner";
public const MONSTER_EGG = "minecraft:monster_egg";
public const MOSS_BLOCK = "minecraft:moss_block";
public const MOSS_CARPET = "minecraft:moss_carpet";
public const MOSSY_COBBLESTONE = "minecraft:mossy_cobblestone";
public const MOSSY_COBBLESTONE_DOUBLE_SLAB = "minecraft:mossy_cobblestone_double_slab";
public const MOSSY_COBBLESTONE_SLAB = "minecraft:mossy_cobblestone_slab";
public const MOSSY_COBBLESTONE_STAIRS = "minecraft:mossy_cobblestone_stairs";
public const MOSSY_COBBLESTONE_WALL = "minecraft:mossy_cobblestone_wall";
public const MOSSY_STONE_BRICK_DOUBLE_SLAB = "minecraft:mossy_stone_brick_double_slab";
public const MOSSY_STONE_BRICK_SLAB = "minecraft:mossy_stone_brick_slab";
public const MOSSY_STONE_BRICK_STAIRS = "minecraft:mossy_stone_brick_stairs";
public const MOSSY_STONE_BRICK_WALL = "minecraft:mossy_stone_brick_wall";
public const MOSSY_STONE_BRICKS = "minecraft:mossy_stone_bricks";
public const MOVING_BLOCK = "minecraft:moving_block";
public const MUD = "minecraft:mud";
public const MUD_BRICK_DOUBLE_SLAB = "minecraft:mud_brick_double_slab";
@ -685,10 +799,14 @@ final class BlockTypeNames{
public const MUD_BRICK_WALL = "minecraft:mud_brick_wall";
public const MUD_BRICKS = "minecraft:mud_bricks";
public const MUDDY_MANGROVE_ROOTS = "minecraft:muddy_mangrove_roots";
public const MUSHROOM_STEM = "minecraft:mushroom_stem";
public const MYCELIUM = "minecraft:mycelium";
public const NETHER_BRICK = "minecraft:nether_brick";
public const NETHER_BRICK_DOUBLE_SLAB = "minecraft:nether_brick_double_slab";
public const NETHER_BRICK_FENCE = "minecraft:nether_brick_fence";
public const NETHER_BRICK_SLAB = "minecraft:nether_brick_slab";
public const NETHER_BRICK_STAIRS = "minecraft:nether_brick_stairs";
public const NETHER_BRICK_WALL = "minecraft:nether_brick_wall";
public const NETHER_GOLD_ORE = "minecraft:nether_gold_ore";
public const NETHER_SPROUTS = "minecraft:nether_sprouts";
public const NETHER_WART = "minecraft:nether_wart";
@ -696,13 +814,20 @@ final class BlockTypeNames{
public const NETHERITE_BLOCK = "minecraft:netherite_block";
public const NETHERRACK = "minecraft:netherrack";
public const NETHERREACTOR = "minecraft:netherreactor";
public const NORMAL_STONE_DOUBLE_SLAB = "minecraft:normal_stone_double_slab";
public const NORMAL_STONE_SLAB = "minecraft:normal_stone_slab";
public const NORMAL_STONE_STAIRS = "minecraft:normal_stone_stairs";
public const NOTEBLOCK = "minecraft:noteblock";
public const OAK_DOUBLE_SLAB = "minecraft:oak_double_slab";
public const OAK_FENCE = "minecraft:oak_fence";
public const OAK_HANGING_SIGN = "minecraft:oak_hanging_sign";
public const OAK_LEAVES = "minecraft:oak_leaves";
public const OAK_LOG = "minecraft:oak_log";
public const OAK_PLANKS = "minecraft:oak_planks";
public const OAK_SAPLING = "minecraft:oak_sapling";
public const OAK_SLAB = "minecraft:oak_slab";
public const OAK_STAIRS = "minecraft:oak_stairs";
public const OAK_WOOD = "minecraft:oak_wood";
public const OBSERVER = "minecraft:observer";
public const OBSIDIAN = "minecraft:obsidian";
public const OCHRE_FROGLIGHT = "minecraft:ochre_froglight";
@ -716,7 +841,9 @@ final class BlockTypeNames{
public const ORANGE_STAINED_GLASS = "minecraft:orange_stained_glass";
public const ORANGE_STAINED_GLASS_PANE = "minecraft:orange_stained_glass_pane";
public const ORANGE_TERRACOTTA = "minecraft:orange_terracotta";
public const ORANGE_TULIP = "minecraft:orange_tulip";
public const ORANGE_WOOL = "minecraft:orange_wool";
public const OXEYE_DAISY = "minecraft:oxeye_daisy";
public const OXIDIZED_CHISELED_COPPER = "minecraft:oxidized_chiseled_copper";
public const OXIDIZED_COPPER = "minecraft:oxidized_copper";
public const OXIDIZED_COPPER_BULB = "minecraft:oxidized_copper_bulb";
@ -730,6 +857,10 @@ final class BlockTypeNames{
public const PACKED_ICE = "minecraft:packed_ice";
public const PACKED_MUD = "minecraft:packed_mud";
public const PEARLESCENT_FROGLIGHT = "minecraft:pearlescent_froglight";
public const PEONY = "minecraft:peony";
public const PETRIFIED_OAK_DOUBLE_SLAB = "minecraft:petrified_oak_double_slab";
public const PETRIFIED_OAK_SLAB = "minecraft:petrified_oak_slab";
public const PIGLIN_HEAD = "minecraft:piglin_head";
public const PINK_CANDLE = "minecraft:pink_candle";
public const PINK_CANDLE_CAKE = "minecraft:pink_candle_cake";
public const PINK_CARPET = "minecraft:pink_carpet";
@ -741,14 +872,18 @@ final class BlockTypeNames{
public const PINK_STAINED_GLASS = "minecraft:pink_stained_glass";
public const PINK_STAINED_GLASS_PANE = "minecraft:pink_stained_glass_pane";
public const PINK_TERRACOTTA = "minecraft:pink_terracotta";
public const PINK_TULIP = "minecraft:pink_tulip";
public const PINK_WOOL = "minecraft:pink_wool";
public const PISTON = "minecraft:piston";
public const PISTON_ARM_COLLISION = "minecraft:piston_arm_collision";
public const PITCHER_CROP = "minecraft:pitcher_crop";
public const PITCHER_PLANT = "minecraft:pitcher_plant";
public const PLAYER_HEAD = "minecraft:player_head";
public const PODZOL = "minecraft:podzol";
public const POINTED_DRIPSTONE = "minecraft:pointed_dripstone";
public const POLISHED_ANDESITE = "minecraft:polished_andesite";
public const POLISHED_ANDESITE_DOUBLE_SLAB = "minecraft:polished_andesite_double_slab";
public const POLISHED_ANDESITE_SLAB = "minecraft:polished_andesite_slab";
public const POLISHED_ANDESITE_STAIRS = "minecraft:polished_andesite_stairs";
public const POLISHED_BASALT = "minecraft:polished_basalt";
public const POLISHED_BLACKSTONE = "minecraft:polished_blackstone";
@ -769,22 +904,33 @@ final class BlockTypeNames{
public const POLISHED_DEEPSLATE_STAIRS = "minecraft:polished_deepslate_stairs";
public const POLISHED_DEEPSLATE_WALL = "minecraft:polished_deepslate_wall";
public const POLISHED_DIORITE = "minecraft:polished_diorite";
public const POLISHED_DIORITE_DOUBLE_SLAB = "minecraft:polished_diorite_double_slab";
public const POLISHED_DIORITE_SLAB = "minecraft:polished_diorite_slab";
public const POLISHED_DIORITE_STAIRS = "minecraft:polished_diorite_stairs";
public const POLISHED_GRANITE = "minecraft:polished_granite";
public const POLISHED_GRANITE_DOUBLE_SLAB = "minecraft:polished_granite_double_slab";
public const POLISHED_GRANITE_SLAB = "minecraft:polished_granite_slab";
public const POLISHED_GRANITE_STAIRS = "minecraft:polished_granite_stairs";
public const POLISHED_TUFF = "minecraft:polished_tuff";
public const POLISHED_TUFF_DOUBLE_SLAB = "minecraft:polished_tuff_double_slab";
public const POLISHED_TUFF_SLAB = "minecraft:polished_tuff_slab";
public const POLISHED_TUFF_STAIRS = "minecraft:polished_tuff_stairs";
public const POLISHED_TUFF_WALL = "minecraft:polished_tuff_wall";
public const POPPY = "minecraft:poppy";
public const PORTAL = "minecraft:portal";
public const POTATOES = "minecraft:potatoes";
public const POWDER_SNOW = "minecraft:powder_snow";
public const POWERED_COMPARATOR = "minecraft:powered_comparator";
public const POWERED_REPEATER = "minecraft:powered_repeater";
public const PRISMARINE = "minecraft:prismarine";
public const PRISMARINE_BRICK_DOUBLE_SLAB = "minecraft:prismarine_brick_double_slab";
public const PRISMARINE_BRICK_SLAB = "minecraft:prismarine_brick_slab";
public const PRISMARINE_BRICKS = "minecraft:prismarine_bricks";
public const PRISMARINE_BRICKS_STAIRS = "minecraft:prismarine_bricks_stairs";
public const PRISMARINE_DOUBLE_SLAB = "minecraft:prismarine_double_slab";
public const PRISMARINE_SLAB = "minecraft:prismarine_slab";
public const PRISMARINE_STAIRS = "minecraft:prismarine_stairs";
public const PRISMARINE_WALL = "minecraft:prismarine_wall";
public const PUMPKIN = "minecraft:pumpkin";
public const PUMPKIN_STEM = "minecraft:pumpkin_stem";
public const PURPLE_CANDLE = "minecraft:purple_candle";
@ -799,10 +945,16 @@ final class BlockTypeNames{
public const PURPLE_TERRACOTTA = "minecraft:purple_terracotta";
public const PURPLE_WOOL = "minecraft:purple_wool";
public const PURPUR_BLOCK = "minecraft:purpur_block";
public const PURPUR_DOUBLE_SLAB = "minecraft:purpur_double_slab";
public const PURPUR_PILLAR = "minecraft:purpur_pillar";
public const PURPUR_SLAB = "minecraft:purpur_slab";
public const PURPUR_STAIRS = "minecraft:purpur_stairs";
public const QUARTZ_BLOCK = "minecraft:quartz_block";
public const QUARTZ_BRICKS = "minecraft:quartz_bricks";
public const QUARTZ_DOUBLE_SLAB = "minecraft:quartz_double_slab";
public const QUARTZ_ORE = "minecraft:quartz_ore";
public const QUARTZ_PILLAR = "minecraft:quartz_pillar";
public const QUARTZ_SLAB = "minecraft:quartz_slab";
public const QUARTZ_STAIRS = "minecraft:quartz_stairs";
public const RAIL = "minecraft:rail";
public const RAW_COPPER_BLOCK = "minecraft:raw_copper_block";
@ -813,18 +965,25 @@ final class BlockTypeNames{
public const RED_CARPET = "minecraft:red_carpet";
public const RED_CONCRETE = "minecraft:red_concrete";
public const RED_CONCRETE_POWDER = "minecraft:red_concrete_powder";
public const RED_FLOWER = "minecraft:red_flower";
public const RED_GLAZED_TERRACOTTA = "minecraft:red_glazed_terracotta";
public const RED_MUSHROOM = "minecraft:red_mushroom";
public const RED_MUSHROOM_BLOCK = "minecraft:red_mushroom_block";
public const RED_NETHER_BRICK = "minecraft:red_nether_brick";
public const RED_NETHER_BRICK_DOUBLE_SLAB = "minecraft:red_nether_brick_double_slab";
public const RED_NETHER_BRICK_SLAB = "minecraft:red_nether_brick_slab";
public const RED_NETHER_BRICK_STAIRS = "minecraft:red_nether_brick_stairs";
public const RED_NETHER_BRICK_WALL = "minecraft:red_nether_brick_wall";
public const RED_SAND = "minecraft:red_sand";
public const RED_SANDSTONE = "minecraft:red_sandstone";
public const RED_SANDSTONE_DOUBLE_SLAB = "minecraft:red_sandstone_double_slab";
public const RED_SANDSTONE_SLAB = "minecraft:red_sandstone_slab";
public const RED_SANDSTONE_STAIRS = "minecraft:red_sandstone_stairs";
public const RED_SANDSTONE_WALL = "minecraft:red_sandstone_wall";
public const RED_SHULKER_BOX = "minecraft:red_shulker_box";
public const RED_STAINED_GLASS = "minecraft:red_stained_glass";
public const RED_STAINED_GLASS_PANE = "minecraft:red_stained_glass_pane";
public const RED_TERRACOTTA = "minecraft:red_terracotta";
public const RED_TULIP = "minecraft:red_tulip";
public const RED_WOOL = "minecraft:red_wool";
public const REDSTONE_BLOCK = "minecraft:redstone_block";
public const REDSTONE_LAMP = "minecraft:redstone_lamp";
@ -836,10 +995,13 @@ final class BlockTypeNames{
public const REPEATING_COMMAND_BLOCK = "minecraft:repeating_command_block";
public const RESERVED6 = "minecraft:reserved6";
public const RESPAWN_ANCHOR = "minecraft:respawn_anchor";
public const ROSE_BUSH = "minecraft:rose_bush";
public const SAND = "minecraft:sand";
public const SANDSTONE = "minecraft:sandstone";
public const SANDSTONE_DOUBLE_SLAB = "minecraft:sandstone_double_slab";
public const SANDSTONE_SLAB = "minecraft:sandstone_slab";
public const SANDSTONE_STAIRS = "minecraft:sandstone_stairs";
public const SAPLING = "minecraft:sapling";
public const SANDSTONE_WALL = "minecraft:sandstone_wall";
public const SCAFFOLDING = "minecraft:scaffolding";
public const SCULK = "minecraft:sculk";
public const SCULK_CATALYST = "minecraft:sculk_catalyst";
@ -849,19 +1011,31 @@ 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_GRASS = "minecraft:short_grass";
public const SHROOMLIGHT = "minecraft:shroomlight";
public const SILVER_GLAZED_TERRACOTTA = "minecraft:silver_glazed_terracotta";
public const SKULL = "minecraft:skull";
public const SKELETON_SKULL = "minecraft:skeleton_skull";
public const SLIME = "minecraft:slime";
public const SMALL_AMETHYST_BUD = "minecraft:small_amethyst_bud";
public const SMALL_DRIPLEAF_BLOCK = "minecraft:small_dripleaf_block";
public const SMITHING_TABLE = "minecraft:smithing_table";
public const SMOKER = "minecraft:smoker";
public const SMOOTH_BASALT = "minecraft:smooth_basalt";
public const SMOOTH_QUARTZ = "minecraft:smooth_quartz";
public const SMOOTH_QUARTZ_DOUBLE_SLAB = "minecraft:smooth_quartz_double_slab";
public const SMOOTH_QUARTZ_SLAB = "minecraft:smooth_quartz_slab";
public const SMOOTH_QUARTZ_STAIRS = "minecraft:smooth_quartz_stairs";
public const SMOOTH_RED_SANDSTONE = "minecraft:smooth_red_sandstone";
public const SMOOTH_RED_SANDSTONE_DOUBLE_SLAB = "minecraft:smooth_red_sandstone_double_slab";
public const SMOOTH_RED_SANDSTONE_SLAB = "minecraft:smooth_red_sandstone_slab";
public const SMOOTH_RED_SANDSTONE_STAIRS = "minecraft:smooth_red_sandstone_stairs";
public const SMOOTH_SANDSTONE = "minecraft:smooth_sandstone";
public const SMOOTH_SANDSTONE_DOUBLE_SLAB = "minecraft:smooth_sandstone_double_slab";
public const SMOOTH_SANDSTONE_SLAB = "minecraft:smooth_sandstone_slab";
public const SMOOTH_SANDSTONE_STAIRS = "minecraft:smooth_sandstone_stairs";
public const SMOOTH_STONE = "minecraft:smooth_stone";
public const SMOOTH_STONE_DOUBLE_SLAB = "minecraft:smooth_stone_double_slab";
public const SMOOTH_STONE_SLAB = "minecraft:smooth_stone_slab";
public const SNIFFER_EGG = "minecraft:sniffer_egg";
public const SNOW = "minecraft:snow";
public const SNOW_LAYER = "minecraft:snow_layer";
@ -875,53 +1049,64 @@ final class BlockTypeNames{
public const SPORE_BLOSSOM = "minecraft:spore_blossom";
public const SPRUCE_BUTTON = "minecraft:spruce_button";
public const SPRUCE_DOOR = "minecraft:spruce_door";
public const SPRUCE_DOUBLE_SLAB = "minecraft:spruce_double_slab";
public const SPRUCE_FENCE = "minecraft:spruce_fence";
public const SPRUCE_FENCE_GATE = "minecraft:spruce_fence_gate";
public const SPRUCE_HANGING_SIGN = "minecraft:spruce_hanging_sign";
public const SPRUCE_LEAVES = "minecraft:spruce_leaves";
public const SPRUCE_LOG = "minecraft:spruce_log";
public const SPRUCE_PLANKS = "minecraft:spruce_planks";
public const SPRUCE_PRESSURE_PLATE = "minecraft:spruce_pressure_plate";
public const SPRUCE_SAPLING = "minecraft:spruce_sapling";
public const SPRUCE_SLAB = "minecraft:spruce_slab";
public const SPRUCE_STAIRS = "minecraft:spruce_stairs";
public const SPRUCE_STANDING_SIGN = "minecraft:spruce_standing_sign";
public const SPRUCE_TRAPDOOR = "minecraft:spruce_trapdoor";
public const SPRUCE_WALL_SIGN = "minecraft:spruce_wall_sign";
public const SPRUCE_WOOD = "minecraft:spruce_wood";
public const STANDING_BANNER = "minecraft:standing_banner";
public const STANDING_SIGN = "minecraft:standing_sign";
public const STICKY_PISTON = "minecraft:sticky_piston";
public const STICKY_PISTON_ARM_COLLISION = "minecraft:sticky_piston_arm_collision";
public const STONE = "minecraft:stone";
public const STONE_BLOCK_SLAB = "minecraft:stone_block_slab";
public const STONE_BLOCK_SLAB2 = "minecraft:stone_block_slab2";
public const STONE_BLOCK_SLAB3 = "minecraft:stone_block_slab3";
public const STONE_BLOCK_SLAB4 = "minecraft:stone_block_slab4";
public const STONE_BRICK_DOUBLE_SLAB = "minecraft:stone_brick_double_slab";
public const STONE_BRICK_SLAB = "minecraft:stone_brick_slab";
public const STONE_BRICK_STAIRS = "minecraft:stone_brick_stairs";
public const STONE_BRICK_WALL = "minecraft:stone_brick_wall";
public const STONE_BRICKS = "minecraft:stone_bricks";
public const STONE_BUTTON = "minecraft:stone_button";
public const STONE_PRESSURE_PLATE = "minecraft:stone_pressure_plate";
public const STONE_STAIRS = "minecraft:stone_stairs";
public const STONEBRICK = "minecraft:stonebrick";
public const STONECUTTER = "minecraft:stonecutter";
public const STONECUTTER_BLOCK = "minecraft:stonecutter_block";
public const STRIPPED_ACACIA_LOG = "minecraft:stripped_acacia_log";
public const STRIPPED_ACACIA_WOOD = "minecraft:stripped_acacia_wood";
public const STRIPPED_BAMBOO_BLOCK = "minecraft:stripped_bamboo_block";
public const STRIPPED_BIRCH_LOG = "minecraft:stripped_birch_log";
public const STRIPPED_BIRCH_WOOD = "minecraft:stripped_birch_wood";
public const STRIPPED_CHERRY_LOG = "minecraft:stripped_cherry_log";
public const STRIPPED_CHERRY_WOOD = "minecraft:stripped_cherry_wood";
public const STRIPPED_CRIMSON_HYPHAE = "minecraft:stripped_crimson_hyphae";
public const STRIPPED_CRIMSON_STEM = "minecraft:stripped_crimson_stem";
public const STRIPPED_DARK_OAK_LOG = "minecraft:stripped_dark_oak_log";
public const STRIPPED_DARK_OAK_WOOD = "minecraft:stripped_dark_oak_wood";
public const STRIPPED_JUNGLE_LOG = "minecraft:stripped_jungle_log";
public const STRIPPED_JUNGLE_WOOD = "minecraft:stripped_jungle_wood";
public const STRIPPED_MANGROVE_LOG = "minecraft:stripped_mangrove_log";
public const STRIPPED_MANGROVE_WOOD = "minecraft:stripped_mangrove_wood";
public const STRIPPED_OAK_LOG = "minecraft:stripped_oak_log";
public const STRIPPED_OAK_WOOD = "minecraft:stripped_oak_wood";
public const STRIPPED_SPRUCE_LOG = "minecraft:stripped_spruce_log";
public const STRIPPED_SPRUCE_WOOD = "minecraft:stripped_spruce_wood";
public const STRIPPED_WARPED_HYPHAE = "minecraft:stripped_warped_hyphae";
public const STRIPPED_WARPED_STEM = "minecraft:stripped_warped_stem";
public const STRUCTURE_BLOCK = "minecraft:structure_block";
public const STRUCTURE_VOID = "minecraft:structure_void";
public const SUNFLOWER = "minecraft:sunflower";
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 TALLGRASS = "minecraft:tallgrass";
public const TALL_GRASS = "minecraft:tall_grass";
public const TARGET = "minecraft:target";
public const TINTED_GLASS = "minecraft:tinted_glass";
public const TNT = "minecraft:tnt";
@ -934,6 +1119,9 @@ final class BlockTypeNames{
public const TRIP_WIRE = "minecraft:trip_wire";
public const TRIPWIRE_HOOK = "minecraft:tripwire_hook";
public const TUBE_CORAL = "minecraft:tube_coral";
public const TUBE_CORAL_BLOCK = "minecraft:tube_coral_block";
public const TUBE_CORAL_FAN = "minecraft:tube_coral_fan";
public const TUBE_CORAL_WALL_FAN = "minecraft:tube_coral_wall_fan";
public const TUFF = "minecraft:tuff";
public const TUFF_BRICK_DOUBLE_SLAB = "minecraft:tuff_brick_double_slab";
public const TUFF_BRICK_SLAB = "minecraft:tuff_brick_slab";
@ -946,12 +1134,14 @@ final class BlockTypeNames{
public const TUFF_WALL = "minecraft:tuff_wall";
public const TURTLE_EGG = "minecraft:turtle_egg";
public const TWISTING_VINES = "minecraft:twisting_vines";
public const UNDERWATER_TNT = "minecraft:underwater_tnt";
public const UNDERWATER_TORCH = "minecraft:underwater_torch";
public const UNDYED_SHULKER_BOX = "minecraft:undyed_shulker_box";
public const UNKNOWN = "minecraft:unknown";
public const UNLIT_REDSTONE_TORCH = "minecraft:unlit_redstone_torch";
public const UNPOWERED_COMPARATOR = "minecraft:unpowered_comparator";
public const UNPOWERED_REPEATER = "minecraft:unpowered_repeater";
public const VAULT = "minecraft:vault";
public const VERDANT_FROGLIGHT = "minecraft:verdant_froglight";
public const VINE = "minecraft:vine";
public const WALL_BANNER = "minecraft:wall_banner";
@ -1029,6 +1219,7 @@ final class BlockTypeNames{
public const WEATHERED_DOUBLE_CUT_COPPER_SLAB = "minecraft:weathered_double_cut_copper_slab";
public const WEB = "minecraft:web";
public const WEEPING_VINES = "minecraft:weeping_vines";
public const WET_SPONGE = "minecraft:wet_sponge";
public const WHEAT = "minecraft:wheat";
public const WHITE_CANDLE = "minecraft:white_candle";
public const WHITE_CANDLE_CAKE = "minecraft:white_candle_cake";
@ -1040,23 +1231,23 @@ final class BlockTypeNames{
public const WHITE_STAINED_GLASS = "minecraft:white_stained_glass";
public const WHITE_STAINED_GLASS_PANE = "minecraft:white_stained_glass_pane";
public const WHITE_TERRACOTTA = "minecraft:white_terracotta";
public const WHITE_TULIP = "minecraft:white_tulip";
public const WHITE_WOOL = "minecraft:white_wool";
public const WITHER_ROSE = "minecraft:wither_rose";
public const WOOD = "minecraft:wood";
public const WITHER_SKELETON_SKULL = "minecraft:wither_skeleton_skull";
public const WOODEN_BUTTON = "minecraft:wooden_button";
public const WOODEN_DOOR = "minecraft:wooden_door";
public const WOODEN_PRESSURE_PLATE = "minecraft:wooden_pressure_plate";
public const WOODEN_SLAB = "minecraft:wooden_slab";
public const YELLOW_CANDLE = "minecraft:yellow_candle";
public const YELLOW_CANDLE_CAKE = "minecraft:yellow_candle_cake";
public const YELLOW_CARPET = "minecraft:yellow_carpet";
public const YELLOW_CONCRETE = "minecraft:yellow_concrete";
public const YELLOW_CONCRETE_POWDER = "minecraft:yellow_concrete_powder";
public const YELLOW_FLOWER = "minecraft:yellow_flower";
public const YELLOW_GLAZED_TERRACOTTA = "minecraft:yellow_glazed_terracotta";
public const YELLOW_SHULKER_BOX = "minecraft:yellow_shulker_box";
public const YELLOW_STAINED_GLASS = "minecraft:yellow_stained_glass";
public const YELLOW_STAINED_GLASS_PANE = "minecraft:yellow_stained_glass_pane";
public const YELLOW_TERRACOTTA = "minecraft:yellow_terracotta";
public const YELLOW_WOOL = "minecraft:yellow_wool";
public const ZOMBIE_HEAD = "minecraft:zombie_head";
}

View File

@ -151,6 +151,7 @@ use pocketmine\block\utils\DripleafState;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\FroglightType;
use pocketmine\block\utils\LeverFacing;
use pocketmine\block\utils\MobHeadType;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\block\Vine;
use pocketmine\block\Wall;
@ -203,8 +204,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerFlatCoralSerializers();
$this->registerCauldronSerializers();
$this->registerFlatWoodBlockSerializers();
$this->registerLegacyWoodBlockSerializers();
$this->registerLeavesSerializers();
$this->registerSaplingSerializers();
$this->registerMobHeadSerializers();
$this->registerSimpleSerializers();
$this->registerSerializers();
}
@ -538,6 +540,41 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL,
}
));
$this->map(Blocks::CORAL_FAN(), fn(FloorCoralFan $block) => Writer::create(
match($block->getCoralType()){
CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_FAN : Ids::BRAIN_CORAL_FAN,
CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_FAN : Ids::BUBBLE_CORAL_FAN,
CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_FAN : Ids::FIRE_CORAL_FAN,
CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_FAN : Ids::HORN_CORAL_FAN,
CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_FAN : Ids::TUBE_CORAL_FAN,
})
->writeInt(StateNames::CORAL_FAN_DIRECTION, match($axis = $block->getAxis()){
Axis::X => 0,
Axis::Z => 1,
default => throw new BlockStateSerializeException("Invalid axis {$axis}"),
}));
$this->map(Blocks::CORAL_BLOCK(), fn(CoralBlock $block) => Writer::create(
match($block->getCoralType()){
CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_BLOCK : Ids::BRAIN_CORAL_BLOCK,
CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_BLOCK : Ids::BUBBLE_CORAL_BLOCK,
CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_BLOCK : Ids::FIRE_CORAL_BLOCK,
CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_BLOCK : Ids::HORN_CORAL_BLOCK,
CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_BLOCK : Ids::TUBE_CORAL_BLOCK,
}
));
$this->map(Blocks::WALL_CORAL_FAN(), fn(WallCoralFan $block) => Writer::create(
match($block->getCoralType()){
CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_WALL_FAN : Ids::TUBE_CORAL_WALL_FAN,
CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_WALL_FAN : Ids::BRAIN_CORAL_WALL_FAN,
CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_WALL_FAN : Ids::BUBBLE_CORAL_WALL_FAN,
CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_WALL_FAN : Ids::FIRE_CORAL_WALL_FAN,
CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_WALL_FAN : Ids::HORN_CORAL_WALL_FAN,
})
->writeCoralFacing($block->getFacing())
);
}
private function registerCauldronSerializers() : void{
@ -558,9 +595,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
$this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG);
$this->mapLog(Blocks::ACACIA_WOOD(), Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD);
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE);
$this->mapSimple(Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS);
//wood and slabs still use the old way of storing wood type
$this->mapSlab(Blocks::ACACIA_SLAB(), Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB);
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
@ -570,10 +608,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
$this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG);
$this->mapLog(Blocks::BIRCH_WOOD(), Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD);
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE);
$this->mapSimple(Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS);
$this->mapSlab(Blocks::BIRCH_SLAB(), Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB);
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
//wood and slabs still use the old way of storing wood type
$this->map(Blocks::CHERRY_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CHERRY_BUTTON)));
$this->map(Blocks::CHERRY_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CHERRY_DOOR)));
@ -587,17 +626,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS);
$this->mapSlab(Blocks::CHERRY_SLAB(), Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB);
$this->mapStairs(Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS);
$this->map(Blocks::CHERRY_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because cherry_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::CHERRY_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_CHERRY_WOOD)
->writePillarAxis($block->getAxis());
}
});
$this->mapLog(Blocks::CHERRY_WOOD(), Ids::CHERRY_WOOD, Ids::STRIPPED_CHERRY_WOOD);
$this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON)));
$this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR)));
@ -621,10 +650,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
$this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG);
$this->mapLog(Blocks::DARK_OAK_WOOD(), Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD);
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE);
$this->mapSimple(Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS);
$this->mapSlab(Blocks::DARK_OAK_SLAB(), Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB);
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
//wood and slabs still use the old way of storing wood type
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
@ -634,10 +664,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
$this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG);
$this->mapLog(Blocks::JUNGLE_WOOD(), Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD);
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE);
$this->mapSimple(Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS);
$this->mapSlab(Blocks::JUNGLE_SLAB(), Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB);
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
//wood and slabs still use the old way of storing wood type
$this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON)));
$this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR)));
@ -651,17 +682,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS);
$this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB);
$this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS);
$this->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because mangrove_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::MANGROVE_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_MANGROVE_WOOD)
->writePillarAxis($block->getAxis());
}
});
$this->mapLog(Blocks::MANGROVE_WOOD(), Ids::MANGROVE_WOOD, Ids::STRIPPED_MANGROVE_WOOD);
$this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON)));
$this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR)));
@ -671,10 +692,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
$this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG);
$this->mapLog(Blocks::OAK_WOOD(), Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD);
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE);
$this->mapSimple(Blocks::OAK_PLANKS(), Ids::OAK_PLANKS);
$this->mapSlab(Blocks::OAK_SLAB(), Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB);
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
//wood and slabs still use the old way of storing wood type
$this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON)));
$this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR)));
@ -684,8 +706,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
$this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG);
$this->mapLog(Blocks::SPRUCE_WOOD(), Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD);
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE);
$this->mapSimple(Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS);
$this->mapSlab(Blocks::SPRUCE_SLAB(), Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB);
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
//wood and slabs still use the old way of storing wood type
@ -704,30 +728,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
}
private function registerLegacyWoodBlockSerializers() : void{
foreach([
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_SLAB(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_SLAB(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_SLAB(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_SLAB(),
StringValues::WOOD_TYPE_OAK => Blocks::OAK_SLAB(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_SLAB(),
] as $woodType => $block){
$this->map($block, fn(Slab $block) => Helper::encodeWoodenSlab($block, $woodType));
}
foreach([
Blocks::ACACIA_WOOD(),
Blocks::BIRCH_WOOD(),
Blocks::DARK_OAK_WOOD(),
Blocks::JUNGLE_WOOD(),
Blocks::OAK_WOOD(),
Blocks::SPRUCE_WOOD(),
] as $block){
$this->map($block, fn(Wood $block) => Helper::encodeAllSidedLog($block));
}
}
private function registerLeavesSerializers() : void{
//flattened IDs
$this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES)));
@ -736,12 +736,37 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES)));
//legacy mess
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_ACACIA));
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_BIRCH));
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_DARK_OAK));
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_JUNGLE));
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_OAK));
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_SPRUCE));
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::ACACIA_LEAVES)));
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::BIRCH_LEAVES)));
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::DARK_OAK_LEAVES)));
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::JUNGLE_LEAVES)));
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::OAK_LEAVES)));
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::SPRUCE_LEAVES)));
}
private function registerSaplingSerializers() : void{
foreach([
Ids::ACACIA_SAPLING => Blocks::ACACIA_SAPLING(),
Ids::BIRCH_SAPLING => Blocks::BIRCH_SAPLING(),
Ids::DARK_OAK_SAPLING => Blocks::DARK_OAK_SAPLING(),
Ids::JUNGLE_SAPLING => Blocks::JUNGLE_SAPLING(),
Ids::OAK_SAPLING => Blocks::OAK_SAPLING(),
Ids::SPRUCE_SAPLING => Blocks::SPRUCE_SAPLING(),
] as $id => $block){
$this->map($block, fn(Sapling $block) => Helper::encodeSapling($block, new Writer($id)));
}
}
private function registerMobHeadSerializers() : void{
$this->map(Blocks::MOB_HEAD(), fn(MobHead $block) => Writer::create(match ($block->getMobHeadType()){
MobHeadType::CREEPER => Ids::CREEPER_HEAD,
MobHeadType::DRAGON => Ids::DRAGON_HEAD,
MobHeadType::PIGLIN => Ids::PIGLIN_HEAD,
MobHeadType::PLAYER => Ids::PLAYER_HEAD,
MobHeadType::SKELETON => Ids::SKELETON_SKULL,
MobHeadType::WITHER_SKELETON => Ids::WITHER_SKELETON_SKULL,
MobHeadType::ZOMBIE => Ids::ZOMBIE_HEAD,
})->writeFacingWithoutDown($block->getFacing()));
}
private function registerSimpleSerializers() : void{
@ -763,6 +788,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::CHISELED_DEEPSLATE(), Ids::CHISELED_DEEPSLATE);
$this->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS);
$this->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE);
$this->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE);
$this->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE);
$this->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS);
$this->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT);
$this->mapSimple(Blocks::CLAY(), Ids::CLAY);
$this->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK);
@ -775,10 +803,14 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::CRACKED_DEEPSLATE_TILES(), Ids::CRACKED_DEEPSLATE_TILES);
$this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS);
$this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS);
$this->mapSimple(Blocks::CRACKED_STONE_BRICKS(), Ids::CRACKED_STONE_BRICKS);
$this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE);
$this->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS);
$this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN);
$this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER);
$this->mapSimple(Blocks::DANDELION(), Ids::DANDELION);
$this->mapSimple(Blocks::CUT_RED_SANDSTONE(), Ids::CUT_RED_SANDSTONE);
$this->mapSimple(Blocks::CUT_SANDSTONE(), Ids::CUT_SANDSTONE);
$this->mapSimple(Blocks::DARK_PRISMARINE(), Ids::DARK_PRISMARINE);
$this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH);
$this->mapSimple(Blocks::DEEPSLATE_BRICKS(), Ids::DEEPSLATE_BRICKS);
$this->mapSimple(Blocks::DEEPSLATE_COAL_ORE(), Ids::DEEPSLATE_COAL_ORE);
@ -918,6 +950,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::ENCHANTING_TABLE(), Ids::ENCHANTING_TABLE);
$this->mapSimple(Blocks::END_STONE(), Ids::END_STONE);
$this->mapSimple(Blocks::END_STONE_BRICKS(), Ids::END_BRICKS);
$this->mapSimple(Blocks::FERN(), Ids::FERN);
$this->mapSimple(Blocks::FLETCHING_TABLE(), Ids::FLETCHING_TABLE);
$this->mapSimple(Blocks::GILDED_BLACKSTONE(), Ids::GILDED_BLACKSTONE);
$this->mapSimple(Blocks::GLASS(), Ids::GLASS);
@ -927,7 +960,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK);
$this->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE);
$this->mapSimple(Blocks::GRANITE(), Ids::GRANITE);
$this->mapSimple(Blocks::GRASS(), Ids::GRASS);
$this->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK);
$this->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH);
$this->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL);
$this->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS);
@ -936,6 +969,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::HARDENED_GLASS_PANE(), Ids::HARD_GLASS_PANE);
$this->mapSimple(Blocks::HONEYCOMB(), Ids::HONEYCOMB_BLOCK);
$this->mapSimple(Blocks::ICE(), Ids::ICE);
$this->mapSimple(Blocks::INFESTED_CHISELED_STONE_BRICK(), Ids::INFESTED_CHISELED_STONE_BRICKS);
$this->mapSimple(Blocks::INFESTED_COBBLESTONE(), Ids::INFESTED_COBBLESTONE);
$this->mapSimple(Blocks::INFESTED_CRACKED_STONE_BRICK(), Ids::INFESTED_CRACKED_STONE_BRICKS);
$this->mapSimple(Blocks::INFESTED_MOSSY_STONE_BRICK(), Ids::INFESTED_MOSSY_STONE_BRICKS);
$this->mapSimple(Blocks::INFESTED_STONE(), Ids::INFESTED_STONE);
$this->mapSimple(Blocks::INFESTED_STONE_BRICK(), Ids::INFESTED_STONE_BRICKS);
$this->mapSimple(Blocks::INFO_UPDATE(), Ids::INFO_UPDATE);
$this->mapSimple(Blocks::INFO_UPDATE2(), Ids::INFO_UPDATE2);
$this->mapSimple(Blocks::INVISIBLE_BEDROCK(), Ids::INVISIBLE_BEDROCK);
@ -952,6 +991,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK);
$this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER);
$this->mapSimple(Blocks::MOSSY_COBBLESTONE(), Ids::MOSSY_COBBLESTONE);
$this->mapSimple(Blocks::MOSSY_STONE_BRICKS(), Ids::MOSSY_STONE_BRICKS);
$this->mapSimple(Blocks::MUD(), Ids::MUD);
$this->mapSimple(Blocks::MUD_BRICKS(), Ids::MUD_BRICKS);
$this->mapSimple(Blocks::MYCELIUM(), Ids::MYCELIUM);
@ -974,6 +1014,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE);
$this->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE);
$this->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE);
$this->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE);
$this->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS);
$this->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS);
$this->mapSimple(Blocks::RAW_COPPER(), Ids::RAW_COPPER_BLOCK);
$this->mapSimple(Blocks::RAW_GOLD(), Ids::RAW_GOLD_BLOCK);
@ -981,8 +1023,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::REDSTONE(), Ids::REDSTONE_BLOCK);
$this->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM);
$this->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK);
$this->mapSimple(Blocks::RED_SAND(), Ids::RED_SAND);
$this->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE);
$this->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE);
$this->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6);
$this->mapSimple(Blocks::SAND(), Ids::SAND);
$this->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE);
$this->mapSimple(Blocks::SCULK(), Ids::SCULK);
$this->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN);
$this->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT);
@ -990,29 +1036,43 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::SLIME(), Ids::SLIME);
$this->mapSimple(Blocks::SMITHING_TABLE(), Ids::SMITHING_TABLE);
$this->mapSimple(Blocks::SMOOTH_BASALT(), Ids::SMOOTH_BASALT);
$this->mapSimple(Blocks::SMOOTH_RED_SANDSTONE(), Ids::SMOOTH_RED_SANDSTONE);
$this->mapSimple(Blocks::SMOOTH_SANDSTONE(), Ids::SMOOTH_SANDSTONE);
$this->mapSimple(Blocks::SMOOTH_STONE(), Ids::SMOOTH_STONE);
$this->mapSimple(Blocks::SNOW(), Ids::SNOW);
$this->mapSimple(Blocks::SOUL_SAND(), Ids::SOUL_SAND);
$this->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL);
$this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM);
$this->mapSimple(Blocks::STONE(), Ids::STONE);
$this->mapSimple(Blocks::STONE_BRICKS(), Ids::STONE_BRICKS);
$this->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :(
$this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS);
$this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER);
$this->mapSimple(Blocks::TUFF(), Ids::TUFF);
$this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK);
$this->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS);
$this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE);
$this->mapSimple(Blocks::ALLIUM(), Ids::ALLIUM);
$this->mapSimple(Blocks::CORNFLOWER(), Ids::CORNFLOWER);
$this->mapSimple(Blocks::AZURE_BLUET(), Ids::AZURE_BLUET);
$this->mapSimple(Blocks::LILY_OF_THE_VALLEY(), Ids::LILY_OF_THE_VALLEY);
$this->mapSimple(Blocks::BLUE_ORCHID(), Ids::BLUE_ORCHID);
$this->mapSimple(Blocks::OXEYE_DAISY(), Ids::OXEYE_DAISY);
$this->mapSimple(Blocks::POPPY(), Ids::POPPY);
$this->mapSimple(Blocks::ORANGE_TULIP(), Ids::ORANGE_TULIP);
$this->mapSimple(Blocks::PINK_TULIP(), Ids::PINK_TULIP);
$this->mapSimple(Blocks::RED_TULIP(), Ids::RED_TULIP);
$this->mapSimple(Blocks::WHITE_TULIP(), Ids::WHITE_TULIP);
}
private function registerSerializers() : void{
$this->map(Blocks::ACACIA_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_ACACIA));
$this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{
return Writer::create(Ids::ACTIVATOR_RAIL)
->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered())
->writeInt(StateNames::RAIL_DIRECTION, $block->getShape());
});
$this->map(Blocks::ALLIUM(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_ALLIUM));
$this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
$this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::MUSHROOM_STEM)
->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM));
$this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create(
match($stage = $block->getStage()){
@ -1024,20 +1084,18 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
})
->writeBlockFace($block->getFacing())
);
$this->map(Blocks::ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_ANDESITE));
$this->mapSlab(Blocks::ANDESITE_SLAB(), Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB);
$this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS)));
$this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_ANDESITE));
$this->map(Blocks::ANVIL(), function(Anvil $block) : Writer{
return Writer::create(Ids::ANVIL)
->writeCardinalHorizontalFacing($block->getFacing())
->writeString(StateNames::DAMAGE, match($damage = $block->getDamage()){
0 => StringValues::DAMAGE_UNDAMAGED,
1 => StringValues::DAMAGE_SLIGHTLY_DAMAGED,
2 => StringValues::DAMAGE_VERY_DAMAGED,
default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"),
});
});
$this->map(Blocks::AZURE_BLUET(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_HOUSTONIA));
$this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::ANDESITE_WALL)));
$this->map(Blocks::ANVIL(), fn(Anvil $block) : Writer => Writer::create(
match($damage = $block->getDamage()){
0 => Ids::ANVIL,
1 => Ids::CHIPPED_ANVIL,
2 => Ids::DAMAGED_ANVIL,
default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"),
})
->writeCardinalHorizontalFacing($block->getFacing())
);
$this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{
return Writer::create(Ids::BAMBOO)
->writeBool(StateNames::AGE_BIT, $block->isReady())
@ -1051,10 +1109,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
});
$this->map(Blocks::BAMBOO_SAPLING(), function(BambooSapling $block) : Writer{
return Writer::create(Ids::BAMBOO_SAPLING)
->writeBool(StateNames::AGE_BIT, $block->isReady())
//TODO: bug in MCPE
->writeString(StateNames::SAPLING_TYPE, StringValues::SAPLING_TYPE_OAK);
->writeBool(StateNames::AGE_BIT, $block->isReady());
});
$this->map(Blocks::BANNER(), function(FloorBanner $block) : Writer{
return Writer::create(Ids::STANDING_BANNER)
@ -1104,13 +1159,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE)
->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false);
});
$this->map(Blocks::BIRCH_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_BIRCH));
$this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS);
$this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL)));
$this->map(Blocks::BLAST_FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::BLAST_FURNACE, Ids::LIT_BLAST_FURNACE));
$this->map(Blocks::BLUE_ORCHID(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_ORCHID));
$this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_BP)));
$this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_BLUE)));
$this->map(Blocks::BONE_BLOCK(), function(BoneBlock $block) : Writer{
return Writer::create(Ids::BONE_BLOCK)
->writeInt(StateNames::DEPRECATED, 0)
@ -1122,9 +1175,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeBool(StateNames::BREWING_STAND_SLOT_B_BIT, $block->hasSlot(BrewingStandSlot::SOUTHWEST))
->writeBool(StateNames::BREWING_STAND_SLOT_C_BIT, $block->hasSlot(BrewingStandSlot::NORTHWEST));
});
$this->map(Blocks::BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_BRICK));
$this->mapSlab(Blocks::BRICK_SLAB(), Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS);
$this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_BRICK));
$this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::BRICK_WALL)));
$this->map(Blocks::BROWN_MUSHROOM_BLOCK(), fn(BrownMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::BROWN_MUSHROOM_BLOCK)));
$this->map(Blocks::CACTUS(), function(Cactus $block) : Writer{
return Writer::create(Ids::CACTUS)
@ -1167,10 +1220,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeLegacyHorizontalFacing($block->getFacing())
->writeInt(StateNames::BOOKS_STORED, $flags);
});
$this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_CHISELED, $block->getAxis()));
$this->map(Blocks::CHISELED_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
$this->map(Blocks::CHISELED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
$this->map(Blocks::CHISELED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CHISELED));
$this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::CHISELED_QUARTZ_BLOCK)));
$this->map(Blocks::CHORUS_FLOWER(), function(ChorusFlower $block) : Writer{
return Writer::create(Ids::CHORUS_FLOWER)
->writeInt(StateNames::AGE, $block->getAge());
@ -1178,9 +1228,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB);
$this->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS);
$this->map(Blocks::COBBLED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::COBBLED_DEEPSLATE_WALL)));
$this->map(Blocks::COBBLESTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_COBBLESTONE));
$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::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_COBBLESTONE));
$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() ?
@ -1260,31 +1310,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeInt(StateNames::AGE, $block->getAge())
->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing()));
});
$this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::CORAL_BLOCK(), function(CoralBlock $block) : Writer{
return Writer::create(Ids::CORAL_BLOCK)
->writeBool(StateNames::DEAD_BIT, $block->isDead())
->writeCoralType($block->getCoralType());
});
$this->map(Blocks::CORAL_FAN(), function(FloorCoralFan $block) : Writer{
return Writer::create($block->isDead() ? Ids::CORAL_FAN_DEAD : Ids::CORAL_FAN)
->writeCoralType($block->getCoralType())
->writeInt(StateNames::CORAL_FAN_DIRECTION, match($axis = $block->getAxis()){
Axis::X => 0,
Axis::Z => 1,
default => throw new BlockStateSerializeException("Invalid axis {$axis}"),
});
});
$this->map(Blocks::CORNFLOWER(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_CORNFLOWER));
$this->map(Blocks::CRACKED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CRACKED));
$this->map(Blocks::CUT_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
$this->map(Blocks::CUT_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE));
$this->map(Blocks::CUT_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
$this->map(Blocks::CUT_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE));
$this->map(Blocks::DARK_OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_DARK_OAK));
$this->map(Blocks::DARK_PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE)
->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DARK));
$this->map(Blocks::DARK_PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK));
$this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::COMPOUND_CREATOR)));
$this->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB);
$this->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB);
$this->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB);
$this->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS);
$this->map(Blocks::DAYLIGHT_SENSOR(), function(DaylightSensor $block) : Writer{
return Writer::create($block->isInverted() ? Ids::DAYLIGHT_DETECTOR_INVERTED : Ids::DAYLIGHT_DETECTOR)
@ -1306,23 +1335,18 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeBool(StateNames::RAIL_DATA_BIT, $block->isActivated())
->writeInt(StateNames::RAIL_DIRECTION, $block->getShape());
});
$this->map(Blocks::DIORITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_DIORITE));
$this->mapSlab(Blocks::DIORITE_SLAB(), Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB);
$this->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS);
$this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_DIORITE));
$this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::DIORITE_WALL)));
$this->map(Blocks::DIRT(), function(Dirt $block) : Writer{
$dirtType = $block->getDirtType();
if($dirtType === DirtType::ROOTED){
return new Writer(Ids::DIRT_WITH_ROOTS);
}
return Writer::create(Ids::DIRT)
->writeString(StateNames::DIRT_TYPE, match($dirtType){
DirtType::COARSE => StringValues::DIRT_TYPE_COARSE,
DirtType::NORMAL => StringValues::DIRT_TYPE_NORMAL,
//ROOTED was already checked above
});
return Writer::create(match($block->getDirtType()){
DirtType::NORMAL => Ids::DIRT,
DirtType::COARSE => Ids::COARSE_DIRT,
DirtType::ROOTED => Ids::DIRT_WITH_ROOTS,
});
});
$this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_GRASS, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::TALL_GRASS)));
$this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::ELEMENT_CONSTRUCTOR)));
$this->map(Blocks::ENDER_CHEST(), function(EnderChest $block) : Writer{
return Writer::create(Ids::ENDER_CHEST)
->writeCardinalHorizontalFacing($block->getFacing());
@ -1336,16 +1360,14 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::END_ROD)
->writeEndRodFacingDirection($block->getFacing());
});
$this->map(Blocks::END_STONE_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_END_STONE_BRICK));
$this->mapSlab(Blocks::END_STONE_BRICK_SLAB(), Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS);
$this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_END_BRICK));
$this->map(Blocks::FAKE_WOODEN_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_WOOD));
$this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::END_STONE_BRICK_WALL)));
$this->mapSlab(Blocks::FAKE_WOODEN_SLAB(), Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB);
$this->map(Blocks::FARMLAND(), function(Farmland $block) : Writer{
return Writer::create(Ids::FARMLAND)
->writeInt(StateNames::MOISTURIZED_AMOUNT, $block->getWetness());
});
$this->map(Blocks::FERN(), fn() => Writer::create(Ids::TALLGRASS)
->writeString(StateNames::TALL_GRASS_TYPE, StringValues::TALL_GRASS_TYPE_FERN));
$this->map(Blocks::FIRE(), function(Fire $block) : Writer{
return Writer::create(Ids::FIRE)
->writeInt(StateNames::AGE, $block->getAge());
@ -1372,10 +1394,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeFacingFlags($block->getFaces());
});
$this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME));
$this->map(Blocks::GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_GRANITE));
$this->mapSlab(Blocks::GRANITE_SLAB(), Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB);
$this->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS);
$this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_GRANITE));
$this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, true, Writer::create(Ids::COLORED_TORCH_RG)));
$this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::GRANITE_WALL)));
$this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_GREEN)));
$this->map(Blocks::HAY_BALE(), function(HayBale $block) : Writer{
return Writer::create(Ids::HAY_BLOCK)
->writeInt(StateNames::DEPRECATED, 0)
@ -1386,23 +1408,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeBool(StateNames::TOGGLE_BIT, $block->isPowered())
->writeFacingWithoutUp($block->getFacing());
});
$this->map(Blocks::INFESTED_CHISELED_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG)
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK));
$this->map(Blocks::INFESTED_COBBLESTONE(), fn() => Writer::create(Ids::MONSTER_EGG)
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_COBBLESTONE));
$this->map(Blocks::INFESTED_CRACKED_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG)
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK));
$this->map(Blocks::INFESTED_MOSSY_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG)
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_MOSSY_STONE_BRICK));
$this->map(Blocks::INFESTED_STONE(), fn() => Writer::create(Ids::MONSTER_EGG)
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_STONE));
$this->map(Blocks::INFESTED_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG)
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_STONE_BRICK));
$this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR)));
$this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR)));
$this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME));
$this->map(Blocks::JUNGLE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_JUNGLE));
$this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::LAB_TABLE)));
$this->map(Blocks::LADDER(), function(Ladder $block) : Writer{
return Writer::create(Ids::LADDER)
->writeHorizontalFacing($block->getFacing());
@ -1411,7 +1420,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::LANTERN)
->writeBool(StateNames::HANGING, $block->isHanging());
});
$this->map(Blocks::LARGE_FERN(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_FERN, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::LARGE_FERN(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::LARGE_FERN)));
$this->map(Blocks::LAVA(), fn(Lava $block) => Helper::encodeLiquid($block, Ids::LAVA, Ids::FLOWING_LAVA));
$this->map(Blocks::LECTERN(), function(Lectern $block) : Writer{
return Writer::create(Ids::LECTERN)
@ -1433,15 +1442,31 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
});
});
$this->map(Blocks::LIGHT(), function(Light $block) : Writer{
return Writer::create(Ids::LIGHT_BLOCK)
->writeInt(StateNames::BLOCK_LIGHT_LEVEL, $block->getLightLevel());
return Writer::create(match($block->getLightLevel()){
0 => Ids::LIGHT_BLOCK_0,
1 => Ids::LIGHT_BLOCK_1,
2 => Ids::LIGHT_BLOCK_2,
3 => Ids::LIGHT_BLOCK_3,
4 => Ids::LIGHT_BLOCK_4,
5 => Ids::LIGHT_BLOCK_5,
6 => Ids::LIGHT_BLOCK_6,
7 => Ids::LIGHT_BLOCK_7,
8 => Ids::LIGHT_BLOCK_8,
9 => Ids::LIGHT_BLOCK_9,
10 => Ids::LIGHT_BLOCK_10,
11 => Ids::LIGHT_BLOCK_11,
12 => Ids::LIGHT_BLOCK_12,
13 => Ids::LIGHT_BLOCK_13,
14 => Ids::LIGHT_BLOCK_14,
15 => Ids::LIGHT_BLOCK_15,
default => throw new BlockStateSerializeException("Invalid light level " . $block->getLightLevel()),
});
});
$this->map(Blocks::LIGHTNING_ROD(), function(LightningRod $block) : Writer{
return Writer::create(Ids::LIGHTNING_ROD)
->writeFacingDirection($block->getFacing());
});
$this->map(Blocks::LILAC(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_SYRINGA, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::LILY_OF_THE_VALLEY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_LILY_OF_THE_VALLEY));
$this->map(Blocks::LILAC(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::LILAC)));
$this->map(Blocks::LIT_PUMPKIN(), function(LitPumpkin $block) : Writer{
return Writer::create(Ids::LIT_PUMPKIN)
->writeCardinalHorizontalFacing($block->getFacing());
@ -1450,29 +1475,24 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::LOOM)
->writeLegacyHorizontalFacing($block->getFacing());
});
$this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::MATERIAL_REDUCER)));
$this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM)));
$this->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{
return Writer::create(Ids::SKULL)
->writeFacingWithoutDown($block->getFacing());
});
$this->map(Blocks::MOSSY_COBBLESTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE));
$this->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS);
$this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_MOSSY_COBBLESTONE));
$this->map(Blocks::MOSSY_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_MOSSY));
$this->map(Blocks::MOSSY_STONE_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK));
$this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_COBBLESTONE_WALL)));
$this->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS);
$this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_MOSSY_STONE_BRICK));
$this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_STONE_BRICK_WALL)));
$this->mapSlab(Blocks::MUD_BRICK_SLAB(), Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS);
$this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL)));
$this->map(Blocks::MUDDY_MANGROVE_ROOTS(), fn(SimplePillar $block) => Writer::create(Ids::MUDDY_MANGROVE_ROOTS)
->writePillarAxis($block->getAxis()));
$this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
$this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::MUSHROOM_STEM)
->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM));
$this->map(Blocks::NETHER_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_NETHER_BRICK));
$this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS);
$this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_NETHER_BRICK));
$this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::NETHER_BRICK_WALL)));
$this->map(Blocks::NETHER_PORTAL(), function(NetherPortal $block) : Writer{
return Writer::create(Ids::PORTAL)
->writeString(StateNames::PORTAL_AXIS, match($block->getAxis()){
@ -1485,16 +1505,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::NETHER_WART)
->writeInt(StateNames::AGE, $block->getAge());
});
$this->map(Blocks::OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_OAK));
$this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE));
$this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE));
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::PEONY)));
$this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{
return Writer::create(Ids::PINK_PETALS)
->writeCardinalHorizontalFacing($block->getFacing())
->writeInt(StateNames::GROWTH, $block->getCount() - 1);
});
$this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK));
$this->map(Blocks::PITCHER_PLANT(), function(DoublePlant $block) : Writer{
return Writer::create(Ids::PITCHER_PLANT)
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
@ -1509,7 +1525,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE)
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
});
$this->map(Blocks::POLISHED_ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE));
$this->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB);
$this->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS);
$this->map(Blocks::POLISHED_BASALT(), function(SimplePillar $block) : Writer{
return Writer::create(Ids::POLISHED_BASALT)
@ -1526,47 +1542,37 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB);
$this->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS);
$this->map(Blocks::POLISHED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_DEEPSLATE_WALL)));
$this->map(Blocks::POLISHED_DIORITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_DIORITE));
$this->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB);
$this->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS);
$this->map(Blocks::POLISHED_GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_GRANITE));
$this->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB);
$this->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS);
$this->map(Blocks::POPPY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_POPPY));
$this->map(Blocks::POTATOES(), fn(Potato $block) => Helper::encodeCrops($block, new Writer(Ids::POTATOES)));
$this->map(Blocks::POWERED_RAIL(), function(PoweredRail $block) : Writer{
return Writer::create(Ids::GOLDEN_RAIL)
->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered())
->writeInt(StateNames::RAIL_DIRECTION, $block->getShape());
});
$this->map(Blocks::PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE)
->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DEFAULT));
$this->map(Blocks::PRISMARINE_BRICKS(), fn() => Writer::create(Ids::PRISMARINE)
->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_BRICKS));
$this->map(Blocks::PRISMARINE_BRICKS_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_BRICK));
$this->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS);
$this->map(Blocks::PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_ROUGH));
$this->mapSlab(Blocks::PRISMARINE_SLAB(), Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB);
$this->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS);
$this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_PRISMARINE));
$this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::PRISMARINE_WALL)));
$this->map(Blocks::PUMPKIN(), function() : Writer{
return Writer::create(Ids::PUMPKIN)
->writeCardinalHorizontalFacing(Facing::SOUTH); //no longer used
});
$this->map(Blocks::PUMPKIN_STEM(), fn(PumpkinStem $block) => Helper::encodeStem($block, new Writer(Ids::PUMPKIN_STEM)));
$this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, true, Writer::create(Ids::COLORED_TORCH_BP)));
$this->map(Blocks::PURPUR(), function() : Writer{
return Writer::create(Ids::PURPUR_BLOCK)
->writeString(StateNames::CHISEL_TYPE, StringValues::CHISEL_TYPE_DEFAULT)
->writePillarAxis(Axis::Y); //useless, but MCPE wants it
});
$this->map(Blocks::PURPUR(), fn() => Writer::create(Ids::PURPUR_BLOCK)->writePillarAxis(Axis::Y));
$this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_PURPLE)));
$this->map(Blocks::PURPUR_PILLAR(), function(SimplePillar $block) : Writer{
return Writer::create(Ids::PURPUR_BLOCK)
->writeString(StateNames::CHISEL_TYPE, StringValues::CHISEL_TYPE_LINES)
return Writer::create(Ids::PURPUR_PILLAR)
->writePillarAxis($block->getAxis());
});
$this->map(Blocks::PURPUR_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PURPUR));
$this->mapSlab(Blocks::PURPUR_SLAB(), Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB);
$this->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS);
$this->map(Blocks::QUARTZ(), fn() => Helper::encodeQuartz(StringValues::CHISEL_TYPE_DEFAULT, Axis::Y));
$this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_LINES, $block->getAxis()));
$this->map(Blocks::QUARTZ_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_QUARTZ));
$this->map(Blocks::QUARTZ(), fn() => Helper::encodeQuartz(Axis::Y, Writer::create(Ids::QUARTZ_BLOCK)));
$this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::QUARTZ_PILLAR)));
$this->mapSlab(Blocks::QUARTZ_SLAB(), Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB);
$this->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS);
$this->map(Blocks::RAIL(), function(Rail $block) : Writer{
return Writer::create(Ids::RAIL)
@ -1594,24 +1600,17 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength());
});
$this->map(Blocks::RED_MUSHROOM_BLOCK(), fn(RedMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::RED_MUSHROOM_BLOCK)));
$this->map(Blocks::RED_NETHER_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_RED_NETHER_BRICK));
$this->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS);
$this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_NETHER_BRICK));
$this->map(Blocks::RED_SAND(), fn() => Writer::create(Ids::SAND)
->writeString(StateNames::SAND_TYPE, StringValues::SAND_TYPE_RED));
$this->map(Blocks::RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_DEFAULT));
$this->map(Blocks::RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_RED_SANDSTONE));
$this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_NETHER_BRICK_WALL)));
$this->mapSlab(Blocks::RED_SANDSTONE_SLAB(), Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS);
$this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE));
$this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_RG)));
$this->map(Blocks::RED_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_RED));
$this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_ROSE, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::SAND(), fn() => Writer::create(Ids::SAND)
->writeString(StateNames::SAND_TYPE, StringValues::SAND_TYPE_NORMAL));
$this->map(Blocks::SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_DEFAULT));
$this->map(Blocks::SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_SANDSTONE));
$this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_SANDSTONE_WALL)));
$this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_RED)));
$this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH)));
$this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS);
$this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_SANDSTONE));
$this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::SANDSTONE_WALL)));
$this->map(Blocks::SEA_PICKLE(), function(SeaPickle $block) : Writer{
return Writer::create(Ids::SEA_PICKLE)
->writeBool(StateNames::DEAD_BIT, !$block->isUnderwater())
@ -1623,16 +1622,14 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
});
$this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER));
$this->map(Blocks::SMOOTH_QUARTZ(), fn() => Helper::encodeQuartz(StringValues::CHISEL_TYPE_SMOOTH, Axis::Y));
$this->map(Blocks::SMOOTH_QUARTZ_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_SMOOTH_QUARTZ));
$this->map(Blocks::SMOOTH_QUARTZ(), fn() => Helper::encodeQuartz(Axis::Y, Writer::create(Ids::SMOOTH_QUARTZ)));
$this->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB);
$this->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS);
$this->map(Blocks::SMOOTH_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_SMOOTH));
$this->map(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE));
$this->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS);
$this->map(Blocks::SMOOTH_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_SMOOTH));
$this->map(Blocks::SMOOTH_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE));
$this->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS);
$this->map(Blocks::SMOOTH_STONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_SMOOTH_STONE));
$this->mapSlab(Blocks::SMOOTH_STONE_SLAB(), Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB);
$this->map(Blocks::SNOW_LAYER(), function(SnowLayer $block) : Writer{
return Writer::create(Ids::SNOW_LAYER)
->writeBool(StateNames::COVERED_BIT, false)
@ -1650,37 +1647,28 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::SOUL_TORCH)
->writeTorchFacing($block->getFacing());
});
$this->map(Blocks::SPONGE(), function(Sponge $block) : Writer{
return Writer::create(Ids::SPONGE)
->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY);
});
$this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE));
$this->map(Blocks::SPONGE(), fn(Sponge $block) => Writer::create($block->isWet() ? Ids::WET_SPONGE : Ids::SPONGE));
$this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK)
->writeCardinalHorizontalFacing($block->getFacing()));
$this->map(Blocks::STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_DEFAULT));
$this->map(Blocks::STONE_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_STONE_BRICK));
$this->mapSlab(Blocks::STONE_BRICK_SLAB(), Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB);
$this->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS);
$this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_STONE_BRICK));
$this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::STONE_BRICK_WALL)));
$this->map(Blocks::STONE_BUTTON(), fn(StoneButton $block) => Helper::encodeButton($block, new Writer(Ids::STONE_BUTTON)));
$this->map(Blocks::STONE_PRESSURE_PLATE(), fn(StonePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::STONE_PRESSURE_PLATE)));
$this->map(Blocks::STONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_STONE));
$this->mapSlab(Blocks::STONE_SLAB(), Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS);
$this->map(Blocks::SUGARCANE(), function(Sugarcane $block) : Writer{
return Writer::create(Ids::REEDS)
->writeInt(StateNames::AGE, $block->getAge());
});
$this->map(Blocks::SUNFLOWER(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_SUNFLOWER, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::SUNFLOWER(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::SUNFLOWER)));
$this->map(Blocks::SWEET_BERRY_BUSH(), function(SweetBerryBush $block) : Writer{
return Writer::create(Ids::SWEET_BERRY_BUSH)
->writeInt(StateNames::GROWTH, $block->getAge());
});
$this->map(Blocks::TALL_GRASS(), fn() => Writer::create(Ids::TALLGRASS)
->writeString(StateNames::TALL_GRASS_TYPE, StringValues::TALL_GRASS_TYPE_TALL));
$this->map(Blocks::TNT(), function(TNT $block) : Writer{
return Writer::create(Ids::TNT)
->writeBool(StateNames::ALLOW_UNDERWATER_BIT, $block->worksUnderwater())
->writeBool(StateNames::EXPLODE_BIT, $block->isUnstable());
});
$this->map(Blocks::TNT(), fn(TNT $block) => Writer::create($block->worksUnderwater() ? Ids::UNDERWATER_TNT : Ids::TNT)
->writeBool(StateNames::EXPLODE_BIT, $block->isUnstable())
);
$this->map(Blocks::TORCH(), function(Torch $block) : Writer{
return Writer::create(Ids::TORCH)
->writeTorchFacing($block->getFacing());
@ -1722,17 +1710,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::WALL_BANNER)
->writeHorizontalFacing($block->getFacing());
});
$this->map(Blocks::WALL_CORAL_FAN(), function(WallCoralFan $block) : Writer{
$coralType = $block->getCoralType();
return Writer::create(match($coralType){
CoralType::TUBE, CoralType::BRAIN => Ids::CORAL_FAN_HANG,
CoralType::BUBBLE, CoralType::FIRE => Ids::CORAL_FAN_HANG2,
CoralType::HORN => Ids::CORAL_FAN_HANG3,
})
->writeBool(StateNames::CORAL_HANG_TYPE_BIT, $coralType === CoralType::BRAIN || $coralType === CoralType::FIRE)
->writeBool(StateNames::DEAD_BIT, $block->isDead())
->writeCoralFacing($block->getFacing());
});
$this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER));
$this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{
return Writer::create(Ids::WEEPING_VINES)
@ -1747,6 +1724,5 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength());
});
$this->map(Blocks::WHEAT(), fn(Wheat $block) => Helper::encodeCrops($block, new Writer(Ids::WHEAT)));
$this->map(Blocks::WHITE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_WHITE));
}
}

View File

@ -32,6 +32,7 @@ use pocketmine\block\CopperStairs;
use pocketmine\block\Crops;
use pocketmine\block\DaylightSensor;
use pocketmine\block\Door;
use pocketmine\block\DoublePlant;
use pocketmine\block\FenceGate;
use pocketmine\block\FloorCoralFan;
use pocketmine\block\FloorSign;
@ -41,15 +42,15 @@ use pocketmine\block\Liquid;
use pocketmine\block\RedMushroomBlock;
use pocketmine\block\RedstoneComparator;
use pocketmine\block\RedstoneRepeater;
use pocketmine\block\Sapling;
use pocketmine\block\SimplePressurePlate;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\Stem;
use pocketmine\block\Trapdoor;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\VanillaBlocks;
use pocketmine\block\utils\SlabType;
use pocketmine\block\Wall;
use pocketmine\block\WallCoralFan;
use pocketmine\block\WallSign;
use pocketmine\block\WeightedPressurePlate;
use pocketmine\block\Wood;
@ -57,7 +58,6 @@ use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateNames as StateNames;
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues;
use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
@ -138,6 +138,12 @@ final class BlockStateDeserializerHelper{
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}
/** @throws BlockStateDeserializeException */
public static function decodeDoublePlant(DoublePlant $block, BlockStateReader $in) : DoublePlant{
return $block
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT));
}
/** @throws BlockStateDeserializeException */
public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{
return $block
@ -149,7 +155,6 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{
return $block
->setCoralType($in->readCoralType())
->setAxis(match($in->readBoundedInt(BlockStateNames::CORAL_FAN_DIRECTION, 0, 1)){
0 => Axis::X,
1 => Axis::Z,
@ -204,8 +209,8 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeMushroomBlock(RedMushroomBlock $block, BlockStateReader $in) : Block{
switch($type = $in->readBoundedInt(BlockStateNames::HUGE_MUSHROOM_BITS, 0, 15)){
case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM: return VanillaBlocks::ALL_SIDED_MUSHROOM_STEM();
case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: return VanillaBlocks::MUSHROOM_STEM();
case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM:
case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: throw new BlockStateDeserializeException("This state does not exist");
default:
//invalid types get left as default
$type = MushroomBlockTypeIdMap::getInstance()->fromId($type);
@ -220,6 +225,12 @@ final class BlockStateDeserializerHelper{
->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1);
}
/** @throws BlockStateDeserializeException */
public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{
return $block
->setReady($in->readBool(BlockStateNames::AGE_BIT));
}
/** @throws BlockStateDeserializeException */
public static function decodeSimplePressurePlate(SimplePressurePlate $block, BlockStateReader $in) : SimplePressurePlate{
//TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation?
@ -227,6 +238,17 @@ final class BlockStateDeserializerHelper{
return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0);
}
/** @throws BlockStateDeserializeException */
public static function decodeSingleSlab(Slab $block, BlockStateReader $in) : Slab{
return $block->setSlabType($in->readSlabPosition());
}
/** @throws BlockStateDeserializeException */
public static function decodeDoubleSlab(Slab $block, BlockStateReader $in) : Slab{
$in->ignored(StateNames::MC_VERTICAL_HALF);
return $block->setSlabType(SlabType::DOUBLE);
}
/** @throws BlockStateDeserializeException */
public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{
return $block
@ -263,13 +285,6 @@ final class BlockStateDeserializerHelper{
return $block;
}
/** @throws BlockStateDeserializeException */
public static function decodeWallCoralFan(WallCoralFan $block, BlockStateReader $in) : WallCoralFan{
return $block
->setDead($in->readBool(BlockStateNames::DEAD_BIT))
->setFacing($in->readCoralFacing());
}
/** @throws BlockStateDeserializeException */
public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{
return $block
@ -280,100 +295,4 @@ final class BlockStateDeserializerHelper{
return $block
->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15));
}
/** @throws BlockStateDeserializeException */
public static function mapLegacyWallType(BlockStateReader $in) : Wall{
return self::decodeWall(match($type = $in->readString(BlockStateNames::WALL_BLOCK_TYPE)){
StringValues::WALL_BLOCK_TYPE_ANDESITE => VanillaBlocks::ANDESITE_WALL(),
StringValues::WALL_BLOCK_TYPE_BRICK => VanillaBlocks::BRICK_WALL(),
StringValues::WALL_BLOCK_TYPE_COBBLESTONE => VanillaBlocks::COBBLESTONE_WALL(),
StringValues::WALL_BLOCK_TYPE_DIORITE => VanillaBlocks::DIORITE_WALL(),
StringValues::WALL_BLOCK_TYPE_END_BRICK => VanillaBlocks::END_STONE_BRICK_WALL(),
StringValues::WALL_BLOCK_TYPE_GRANITE => VanillaBlocks::GRANITE_WALL(),
StringValues::WALL_BLOCK_TYPE_MOSSY_COBBLESTONE => VanillaBlocks::MOSSY_COBBLESTONE_WALL(),
StringValues::WALL_BLOCK_TYPE_MOSSY_STONE_BRICK => VanillaBlocks::MOSSY_STONE_BRICK_WALL(),
StringValues::WALL_BLOCK_TYPE_NETHER_BRICK => VanillaBlocks::NETHER_BRICK_WALL(),
StringValues::WALL_BLOCK_TYPE_PRISMARINE => VanillaBlocks::PRISMARINE_WALL(),
StringValues::WALL_BLOCK_TYPE_RED_NETHER_BRICK => VanillaBlocks::RED_NETHER_BRICK_WALL(),
StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE => VanillaBlocks::RED_SANDSTONE_WALL(),
StringValues::WALL_BLOCK_TYPE_SANDSTONE => VanillaBlocks::SANDSTONE_WALL(),
StringValues::WALL_BLOCK_TYPE_STONE_BRICK => VanillaBlocks::STONE_BRICK_WALL(),
default => throw $in->badValueException(BlockStateNames::WALL_BLOCK_TYPE, $type),
}, $in);
}
/** @throws BlockStateDeserializeException */
public static function mapStoneSlab1Type(BlockStateReader $in) : Slab{
//* stone_slab_type (StringTag) = brick, cobblestone, nether_brick, quartz, sandstone, smooth_stone, stone_brick, wood
return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE)){
StringValues::STONE_SLAB_TYPE_BRICK => VanillaBlocks::BRICK_SLAB(),
StringValues::STONE_SLAB_TYPE_COBBLESTONE => VanillaBlocks::COBBLESTONE_SLAB(),
StringValues::STONE_SLAB_TYPE_NETHER_BRICK => VanillaBlocks::NETHER_BRICK_SLAB(),
StringValues::STONE_SLAB_TYPE_QUARTZ => VanillaBlocks::QUARTZ_SLAB(),
StringValues::STONE_SLAB_TYPE_SANDSTONE => VanillaBlocks::SANDSTONE_SLAB(),
StringValues::STONE_SLAB_TYPE_SMOOTH_STONE => VanillaBlocks::SMOOTH_STONE_SLAB(),
StringValues::STONE_SLAB_TYPE_STONE_BRICK => VanillaBlocks::STONE_BRICK_SLAB(),
StringValues::STONE_SLAB_TYPE_WOOD => VanillaBlocks::FAKE_WOODEN_SLAB(),
default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE, $type),
};
}
/** @throws BlockStateDeserializeException */
public static function mapStoneSlab2Type(BlockStateReader $in) : Slab{
// * stone_slab_type_2 (StringTag) = mossy_cobblestone, prismarine_brick, prismarine_dark, prismarine_rough, purpur, red_nether_brick, red_sandstone, smooth_sandstone
return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_2)){
StringValues::STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE => VanillaBlocks::MOSSY_COBBLESTONE_SLAB(),
StringValues::STONE_SLAB_TYPE_2_PRISMARINE_BRICK => VanillaBlocks::PRISMARINE_BRICKS_SLAB(),
StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK => VanillaBlocks::DARK_PRISMARINE_SLAB(),
StringValues::STONE_SLAB_TYPE_2_PRISMARINE_ROUGH => VanillaBlocks::PRISMARINE_SLAB(),
StringValues::STONE_SLAB_TYPE_2_PURPUR => VanillaBlocks::PURPUR_SLAB(),
StringValues::STONE_SLAB_TYPE_2_RED_NETHER_BRICK => VanillaBlocks::RED_NETHER_BRICK_SLAB(),
StringValues::STONE_SLAB_TYPE_2_RED_SANDSTONE => VanillaBlocks::RED_SANDSTONE_SLAB(),
StringValues::STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE => VanillaBlocks::SMOOTH_SANDSTONE_SLAB(),
default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_2, $type),
};
}
/** @throws BlockStateDeserializeException */
public static function mapStoneSlab3Type(BlockStateReader $in) : Slab{
// * stone_slab_type_3 (StringTag) = andesite, diorite, end_stone_brick, granite, polished_andesite, polished_diorite, polished_granite, smooth_red_sandstone
return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_3)){
StringValues::STONE_SLAB_TYPE_3_ANDESITE => VanillaBlocks::ANDESITE_SLAB(),
StringValues::STONE_SLAB_TYPE_3_DIORITE => VanillaBlocks::DIORITE_SLAB(),
StringValues::STONE_SLAB_TYPE_3_END_STONE_BRICK => VanillaBlocks::END_STONE_BRICK_SLAB(),
StringValues::STONE_SLAB_TYPE_3_GRANITE => VanillaBlocks::GRANITE_SLAB(),
StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE => VanillaBlocks::POLISHED_ANDESITE_SLAB(),
StringValues::STONE_SLAB_TYPE_3_POLISHED_DIORITE => VanillaBlocks::POLISHED_DIORITE_SLAB(),
StringValues::STONE_SLAB_TYPE_3_POLISHED_GRANITE => VanillaBlocks::POLISHED_GRANITE_SLAB(),
StringValues::STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE => VanillaBlocks::SMOOTH_RED_SANDSTONE_SLAB(),
default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_3, $type),
};
}
/** @throws BlockStateDeserializeException */
public static function mapStoneSlab4Type(BlockStateReader $in) : Slab{
// * stone_slab_type_4 (StringTag) = cut_red_sandstone, cut_sandstone, mossy_stone_brick, smooth_quartz, stone
return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_4)){
StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE => VanillaBlocks::CUT_RED_SANDSTONE_SLAB(),
StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE => VanillaBlocks::CUT_SANDSTONE_SLAB(),
StringValues::STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK => VanillaBlocks::MOSSY_STONE_BRICK_SLAB(),
StringValues::STONE_SLAB_TYPE_4_SMOOTH_QUARTZ => VanillaBlocks::SMOOTH_QUARTZ_SLAB(),
StringValues::STONE_SLAB_TYPE_4_STONE => VanillaBlocks::STONE_SLAB(),
default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_4, $type),
};
}
/** @throws BlockStateDeserializeException */
public static function mapWoodenSlabType(BlockStateReader $in) : Slab{
// * wood_type (StringTag) = acacia, birch, dark_oak, jungle, oak, spruce
return match($type = $in->readString(BlockStateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_ACACIA => VanillaBlocks::ACACIA_SLAB(),
StringValues::WOOD_TYPE_BIRCH => VanillaBlocks::BIRCH_SLAB(),
StringValues::WOOD_TYPE_DARK_OAK => VanillaBlocks::DARK_OAK_SLAB(),
StringValues::WOOD_TYPE_JUNGLE => VanillaBlocks::JUNGLE_SLAB(),
StringValues::WOOD_TYPE_OAK => VanillaBlocks::OAK_SLAB(),
StringValues::WOOD_TYPE_SPRUCE => VanillaBlocks::SPRUCE_SLAB(),
default => throw $in->badValueException(BlockStateNames::WOOD_TYPE, $type),
};
}
}

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
@ -38,20 +37,24 @@ use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag;
use pocketmine\utils\Utils;
use function array_keys;
use function count;
use function get_class;
use function implode;
final class BlockStateReader{
/**
* @var true[]
* @phpstan-var array<string, true>
* @var Tag[]
* @phpstan-var array<string, Tag>
*/
private array $usedStates = [];
private array $unusedStates;
public function __construct(
private BlockStateData $data
){}
){
$this->unusedStates = $this->data->getStates();
}
public function missingOrWrongTypeException(string $name, ?Tag $tag) : BlockStateDeserializeException{
return new BlockStateDeserializeException("Property \"$name\" " . ($tag !== null ? "has unexpected type " . get_class($tag) : "is missing"));
@ -66,7 +69,7 @@ final class BlockStateReader{
/** @throws BlockStateDeserializeException */
public function readBool(string $name) : bool{
$this->usedStates[$name] = true;
unset($this->unusedStates[$name]);
$tag = $this->data->getState($name);
if($tag instanceof ByteTag){
switch($tag->getValue()){
@ -80,7 +83,7 @@ final class BlockStateReader{
/** @throws BlockStateDeserializeException */
public function readInt(string $name) : int{
$this->usedStates[$name] = true;
unset($this->unusedStates[$name]);
$tag = $this->data->getState($name);
if($tag instanceof IntTag){
return $tag->getValue();
@ -99,7 +102,7 @@ final class BlockStateReader{
/** @throws BlockStateDeserializeException */
public function readString(string $name) : string{
$this->usedStates[$name] = true;
unset($this->unusedStates[$name]);
//TODO: only allow a specific set of values (strings are primarily used for enums)
$tag = $this->data->getState($name);
if($tag instanceof StringTag){
@ -305,18 +308,6 @@ final class BlockStateReader{
};
}
/** @throws BlockStateDeserializeException */
public function readCoralType() : CoralType{
return match($type = $this->readString(BlockStateNames::CORAL_COLOR)){
StringValues::CORAL_COLOR_BLUE => CoralType::TUBE,
StringValues::CORAL_COLOR_PINK => CoralType::BRAIN,
StringValues::CORAL_COLOR_PURPLE => CoralType::BUBBLE,
StringValues::CORAL_COLOR_RED => CoralType::FIRE,
StringValues::CORAL_COLOR_YELLOW => CoralType::HORN,
default => throw $this->badValueException(BlockStateNames::CORAL_COLOR, $type),
};
}
/** @throws BlockStateDeserializeException */
public function readBellAttachmentType() : BellAttachmentType{
return match($type = $this->readString(BlockStateNames::ATTACHMENT)){
@ -346,7 +337,7 @@ final class BlockStateReader{
*/
public function ignored(string $name) : void{
if($this->data->getState($name) !== null){
$this->usedStates[$name] = true;
unset($this->unusedStates[$name]);
}else{
throw $this->missingOrWrongTypeException($name, null);
}
@ -363,10 +354,8 @@ final class BlockStateReader{
* @throws BlockStateDeserializeException
*/
public function checkUnreadProperties() : void{
foreach(Utils::stringifyKeys($this->data->getStates()) as $name => $tag){
if(!isset($this->usedStates[$name])){
throw new BlockStateDeserializeException("Unread property \"$name\"");
}
if(count($this->unusedStates) > 0){
throw new BlockStateDeserializeException("Unread properties: " . implode(", ", array_keys($this->unusedStates)));
}
}
}

View File

@ -56,14 +56,6 @@ use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Facing;
final class BlockStateSerializerHelper{
public static function encodeAllSidedLog(Wood $block) : Writer{
return Writer::create(Ids::WOOD)
->writeBool(BlockStateNames::STRIPPED_BIT, $block->isStripped())
->writePillarAxis($block->getAxis())
->writeLegacyWoodType($block->getWoodType());
}
public static function encodeButton(Button $block, Writer $out) : Writer{
return $out
->writeFacingDirection($block->getFacing())
@ -76,9 +68,8 @@ final class BlockStateSerializerHelper{
->writeInt(StateNames::CANDLES, $block->getCount() - 1);
}
public static function encodeChemistryTable(ChemistryTable $block, string $chemistryTableType, Writer $out) : Writer{
public static function encodeChemistryTable(ChemistryTable $block, Writer $out) : Writer{
return $out
->writeString(BlockStateNames::CHEMISTRY_TABLE_TYPE, $chemistryTableType)
->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing()));
}
@ -86,9 +77,8 @@ final class BlockStateSerializerHelper{
return $out->writeInt(BlockStateNames::GROWTH, $block->getAge());
}
public static function encodeColoredTorch(Torch $block, bool $highBit, Writer $out) : Writer{
public static function encodeTorch(Torch $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::COLOR_BIT, $highBit)
->writeTorchFacing($block->getFacing());
}
@ -115,10 +105,9 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
public static function encodeDoublePlant(DoublePlant $block, string $doublePlantType, Writer $out) : Writer{
public static function encodeDoublePlant(DoublePlant $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
->writeString(BlockStateNames::DOUBLE_PLANT_TYPE, $doublePlantType);
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop());
}
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
@ -151,16 +140,6 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::UPDATE_BIT, $block->isCheckDecay());
}
public static function encodeLeaves1(Leaves $block, string $type) : Writer{
return self::encodeLeaves($block, Writer::create(Ids::LEAVES)
->writeString(BlockStateNames::OLD_LEAF_TYPE, $type));
}
public static function encodeLeaves2(Leaves $block, string $type) : Writer{
return self::encodeLeaves($block, Writer::create(Ids::LEAVES2)
->writeString(BlockStateNames::NEW_LEAF_TYPE, $type));
}
public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : Writer{
return Writer::create($block->isStill() ? $stillId : $flowingId)
->writeInt(BlockStateNames::LIQUID_DEPTH, $block->getDecay() | ($block->isFalling() ? 0x8 : 0));
@ -179,24 +158,14 @@ final class BlockStateSerializerHelper{
->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType()));
}
public static function encodeQuartz(string $type, int $axis) : Writer{
return Writer::create(Ids::QUARTZ_BLOCK)
->writeString(BlockStateNames::CHISEL_TYPE, $type)
public static function encodeQuartz(int $axis, Writer $out) : Writer{
return $out
->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway
}
public static function encodeRedFlower(string $type) : Writer{
return Writer::create(Ids::RED_FLOWER)->writeString(BlockStateNames::FLOWER_TYPE, $type);
}
public static function encodeSandstone(string $id, string $type) : Writer{
return Writer::create($id)->writeString(BlockStateNames::SAND_STONE_TYPE, $type);
}
public static function encodeSapling(Sapling $block, string $type) : Writer{
return Writer::create(Ids::SAPLING)
->writeBool(BlockStateNames::AGE_BIT, $block->isReady())
->writeString(BlockStateNames::SAPLING_TYPE, $type);
public static function encodeSapling(Sapling $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::AGE_BIT, $block->isReady());
}
public static function encodeSimplePressurePlate(SimplePressurePlate $block, Writer $out) : Writer{
@ -206,11 +175,21 @@ final class BlockStateSerializerHelper{
->writeInt(BlockStateNames::REDSTONE_SIGNAL, $block->isPressed() ? 15 : 0);
}
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : Writer{
$slabType = $block->getSlabType();
return Writer::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
private static function encodeSingleSlab(Slab $block, string $id) : Writer{
return Writer::create($id)
->writeSlabPosition($block->getSlabType());
}
private static function encodeDoubleSlab(Slab $block, string $id) : Writer{
return Writer::create($id)
//this is (intentionally) also written for double slabs (as zero) to maintain bug parity with MCPE
->writeSlabPosition($slabType === SlabType::DOUBLE ? SlabType::BOTTOM : $slabType);
->writeSlabPosition(SlabType::BOTTOM);
}
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : Writer{
return $block->getSlabType() === SlabType::DOUBLE ?
self::encodeDoubleSlab($block, $doubleId) :
self::encodeSingleSlab($block, $singleId);
}
public static function encodeStairs(Stair $block, Writer $out) : Writer{
@ -228,32 +207,6 @@ final class BlockStateSerializerHelper{
->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing);
}
public static function encodeStoneBricks(string $type) : Writer{
return Writer::create(Ids::STONEBRICK)
->writeString(BlockStateNames::STONE_BRICK_TYPE, $type);
}
private static function encodeStoneSlab(Slab $block, string $singleId, string $doubleId, string $typeKey, string $typeValue) : Writer{
return self::encodeSlab($block, $singleId, $doubleId)
->writeString($typeKey, $typeValue);
}
public static function encodeStoneSlab1(Slab $block, string $typeValue) : Writer{
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB, Ids::DOUBLE_STONE_BLOCK_SLAB, BlockStateNames::STONE_SLAB_TYPE, $typeValue);
}
public static function encodeStoneSlab2(Slab $block, string $typeValue) : Writer{
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB2, Ids::DOUBLE_STONE_BLOCK_SLAB2, BlockStateNames::STONE_SLAB_TYPE_2, $typeValue);
}
public static function encodeStoneSlab3(Slab $block, string $typeValue) : Writer{
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB3, Ids::DOUBLE_STONE_BLOCK_SLAB3, BlockStateNames::STONE_SLAB_TYPE_3, $typeValue);
}
public static function encodeStoneSlab4(Slab $block, string $typeValue) : Writer{
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB4, Ids::DOUBLE_STONE_BLOCK_SLAB4, BlockStateNames::STONE_SLAB_TYPE_4, $typeValue);
}
public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{
return $out
->write5MinusHorizontalFacing($block->getFacing())
@ -270,18 +223,8 @@ final class BlockStateSerializerHelper{
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST));
}
public static function encodeLegacyWall(Wall $block, string $type) : Writer{
return self::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL))
->writeString(BlockStateNames::WALL_BLOCK_TYPE, $type);
}
public static function encodeWallSign(WallSign $block, Writer $out) : Writer{
return $out
->writeHorizontalFacing($block->getFacing());
}
public static function encodeWoodenSlab(Slab $block, string $typeValue) : Writer{
return self::encodeSlab($block, Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB)
->writeString(BlockStateNames::WOOD_TYPE, $typeValue);
}
}

View File

@ -24,12 +24,13 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\AmethystCluster;
use pocketmine\block\Anvil;
use pocketmine\block\Bamboo;
use pocketmine\block\Block;
use pocketmine\block\CaveVines;
use pocketmine\block\ChorusFlower;
use pocketmine\block\DoublePitcherCrop;
use pocketmine\block\Light;
use pocketmine\block\Opaque;
use pocketmine\block\PinkPetals;
use pocketmine\block\PitcherCrop;
use pocketmine\block\Slab;
@ -44,7 +45,7 @@ use pocketmine\block\utils\DripleafState;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\FroglightType;
use pocketmine\block\utils\LeverFacing;
use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\MobHeadType;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\block\Wood;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
@ -82,8 +83,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerFlatCoralDeserializers();
$this->registerCauldronDeserializers();
$this->registerFlatWoodBlockDeserializers();
$this->registerLegacyWoodBlockDeserializers();
$this->registerLeavesDeserializers();
$this->registerSaplingDeserializers();
$this->registerLightDeserializers();
$this->registerMobHeadDeserializers();
$this->registerSimpleDeserializers();
$this->registerDeserializers();
}
@ -115,11 +118,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
* @phpstan-param \Closure(Reader) : Slab $getBlock
*/
public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{
$this->map($singleId, fn(Reader $in) : Slab => $getBlock($in)->setSlabType($in->readSlabPosition()));
$this->map($doubleId, function(Reader $in) use ($getBlock) : Slab{
$in->ignored(StateNames::MC_VERTICAL_HALF);
return $getBlock($in)->setSlabType(SlabType::DOUBLE);
});
$this->map($singleId, fn(Reader $in) => Helper::decodeSingleSlab($getBlock($in), $in));
$this->map($doubleId, fn(Reader $in) => Helper::decodeDoubleSlab($getBlock($in), $in));
}
/**
@ -439,6 +439,39 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
] as $id => $coralType){
$this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(true));
}
foreach([
[CoralType::BRAIN, Ids::BRAIN_CORAL_FAN, Ids::DEAD_BRAIN_CORAL_FAN],
[CoralType::BUBBLE, Ids::BUBBLE_CORAL_FAN, Ids::DEAD_BUBBLE_CORAL_FAN],
[CoralType::FIRE, Ids::FIRE_CORAL_FAN, Ids::DEAD_FIRE_CORAL_FAN],
[CoralType::HORN, Ids::HORN_CORAL_FAN, Ids::DEAD_HORN_CORAL_FAN],
[CoralType::TUBE, Ids::TUBE_CORAL_FAN, Ids::DEAD_TUBE_CORAL_FAN],
] as [$coralType, $aliveId, $deadId]){
$this->map($aliveId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(false), $in));
$this->map($deadId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(true), $in));
}
foreach([
[CoralType::BRAIN, Ids::BRAIN_CORAL_BLOCK, Ids::DEAD_BRAIN_CORAL_BLOCK],
[CoralType::BUBBLE, Ids::BUBBLE_CORAL_BLOCK, Ids::DEAD_BUBBLE_CORAL_BLOCK],
[CoralType::FIRE, Ids::FIRE_CORAL_BLOCK, Ids::DEAD_FIRE_CORAL_BLOCK],
[CoralType::HORN, Ids::HORN_CORAL_BLOCK, Ids::DEAD_HORN_CORAL_BLOCK],
[CoralType::TUBE, Ids::TUBE_CORAL_BLOCK, Ids::DEAD_TUBE_CORAL_BLOCK],
] as [$coralType, $aliveId, $deadId]){
$this->map($aliveId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(false));
$this->map($deadId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(true));
}
foreach([
[CoralType::BRAIN, Ids::BRAIN_CORAL_WALL_FAN, Ids::DEAD_BRAIN_CORAL_WALL_FAN],
[CoralType::BUBBLE, Ids::BUBBLE_CORAL_WALL_FAN, Ids::DEAD_BUBBLE_CORAL_WALL_FAN],
[CoralType::FIRE, Ids::FIRE_CORAL_WALL_FAN, Ids::DEAD_FIRE_CORAL_WALL_FAN],
[CoralType::HORN, Ids::HORN_CORAL_WALL_FAN, Ids::DEAD_HORN_CORAL_WALL_FAN],
[CoralType::TUBE, Ids::TUBE_CORAL_WALL_FAN, Ids::DEAD_TUBE_CORAL_WALL_FAN],
] as [$coralType, $aliveId, $deadId]){
$this->map($aliveId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(false));
$this->map($deadId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(true));
}
}
private function registerCauldronDeserializers() : void{
@ -468,10 +501,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
$this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG());
$this->mapLog(Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD, fn() => Blocks::ACACIA_WOOD());
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE());
$this->mapSimple(Ids::ACACIA_PLANKS, fn() => Blocks::ACACIA_PLANKS());
$this->mapSlab(Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB, fn() => Blocks::ACACIA_SLAB());
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in));
$this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in));
@ -481,10 +515,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
$this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG());
$this->mapLog(Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD, fn() => Blocks::BIRCH_WOOD());
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE());
$this->mapSimple(Ids::BIRCH_PLANKS, fn() => Blocks::BIRCH_PLANKS());
$this->mapSlab(Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB, fn() => Blocks::BIRCH_SLAB());
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::CHERRY_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CHERRY_BUTTON(), $in));
$this->map(Ids::CHERRY_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CHERRY_DOOR(), $in));
@ -498,10 +533,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::CHERRY_PLANKS, fn() => Blocks::CHERRY_PLANKS());
$this->mapSlab(Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB, fn() => Blocks::CHERRY_SLAB());
$this->mapStairs(Ids::CHERRY_STAIRS, fn() => Blocks::CHERRY_STAIRS());
$this->map(Ids::CHERRY_WOOD, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in);
});
$this->map(Ids::CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in));
$this->map(Ids::STRIPPED_CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), true, $in));
$this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in));
@ -526,10 +558,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
$this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG());
$this->mapLog(Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD, fn() => Blocks::DARK_OAK_WOOD());
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE());
$this->mapSimple(Ids::DARK_OAK_PLANKS, fn() => Blocks::DARK_OAK_PLANKS());
$this->mapSlab(Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB, fn() => Blocks::DARK_OAK_SLAB());
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in));
$this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in));
@ -539,10 +572,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
$this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG());
$this->mapLog(Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD, fn() => Blocks::JUNGLE_WOOD());
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE());
$this->mapSimple(Ids::JUNGLE_PLANKS, fn() => Blocks::JUNGLE_PLANKS());
$this->mapSlab(Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB, fn() => Blocks::JUNGLE_SLAB());
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in));
$this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in));
@ -556,10 +590,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS());
$this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB());
$this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS());
$this->map(Ids::MANGROVE_WOOD, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in);
});
$this->map(Ids::MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in));
$this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in));
//oak - due to age, many of these don't specify "oak", making for confusing reading
@ -571,10 +602,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
$this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG());
$this->mapLog(Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD, fn() => Blocks::OAK_WOOD());
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE());
$this->mapSimple(Ids::OAK_PLANKS, fn() => Blocks::OAK_PLANKS());
$this->mapSlab(Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB, fn() => Blocks::OAK_SLAB());
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in));
$this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in));
@ -584,10 +616,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
$this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG());
$this->mapLog(Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD, fn() => Blocks::SPRUCE_WOOD());
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE());
$this->mapSimple(Ids::SPRUCE_PLANKS, fn() => Blocks::SPRUCE_PLANKS());
$this->mapSlab(Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB, fn() => Blocks::SPRUCE_SLAB());
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in));
$this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in));
@ -604,40 +637,67 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
}
private function registerLegacyWoodBlockDeserializers() : void{
$this->mapSlab(Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB, fn(Reader $in) => Helper::mapWoodenSlabType($in));
$this->map(Ids::WOOD, fn(Reader $in) : Block => Helper::decodeLog(match($woodType = $in->readString(StateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_WOOD(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_WOOD(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_WOOD(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_WOOD(),
StringValues::WOOD_TYPE_OAK => Blocks::OAK_WOOD(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_WOOD(),
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodType),
}, $in->readBool(StateNames::STRIPPED_BIT), $in));
}
private function registerLeavesDeserializers() : void{
//flattened IDs
$this->map(Ids::ACACIA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::ACACIA_LEAVES(), $in));
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
$this->map(Ids::BIRCH_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::BIRCH_LEAVES(), $in));
$this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in));
$this->map(Ids::DARK_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::DARK_OAK_LEAVES(), $in));
$this->map(Ids::JUNGLE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::JUNGLE_LEAVES(), $in));
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
$this->map(Ids::OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::OAK_LEAVES(), $in));
$this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in));
}
//legacy mess
$this->map(Ids::LEAVES, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::OLD_LEAF_TYPE)){
StringValues::OLD_LEAF_TYPE_BIRCH => Blocks::BIRCH_LEAVES(),
StringValues::OLD_LEAF_TYPE_JUNGLE => Blocks::JUNGLE_LEAVES(),
StringValues::OLD_LEAF_TYPE_OAK => Blocks::OAK_LEAVES(),
StringValues::OLD_LEAF_TYPE_SPRUCE => Blocks::SPRUCE_LEAVES(),
default => throw $in->badValueException(StateNames::OLD_LEAF_TYPE, $type),
}, $in));
$this->map(Ids::LEAVES2, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::NEW_LEAF_TYPE)){
StringValues::NEW_LEAF_TYPE_ACACIA => Blocks::ACACIA_LEAVES(),
StringValues::NEW_LEAF_TYPE_DARK_OAK => Blocks::DARK_OAK_LEAVES(),
default => throw $in->badValueException(StateNames::NEW_LEAF_TYPE, $type),
}, $in));
private function registerSaplingDeserializers() : void{
foreach([
Ids::ACACIA_SAPLING => fn() => Blocks::ACACIA_SAPLING(),
Ids::BIRCH_SAPLING => fn() => Blocks::BIRCH_SAPLING(),
Ids::DARK_OAK_SAPLING => fn() => Blocks::DARK_OAK_SAPLING(),
Ids::JUNGLE_SAPLING => fn() => Blocks::JUNGLE_SAPLING(),
Ids::OAK_SAPLING => fn() => Blocks::OAK_SAPLING(),
Ids::SPRUCE_SAPLING => fn() => Blocks::SPRUCE_SAPLING(),
] as $id => $getBlock){
$this->map($id, fn(Reader $in) => Helper::decodeSapling($getBlock(), $in));
}
}
private function registerLightDeserializers() : void{
foreach([
Ids::LIGHT_BLOCK_0 => 0,
Ids::LIGHT_BLOCK_1 => 1,
Ids::LIGHT_BLOCK_2 => 2,
Ids::LIGHT_BLOCK_3 => 3,
Ids::LIGHT_BLOCK_4 => 4,
Ids::LIGHT_BLOCK_5 => 5,
Ids::LIGHT_BLOCK_6 => 6,
Ids::LIGHT_BLOCK_7 => 7,
Ids::LIGHT_BLOCK_8 => 8,
Ids::LIGHT_BLOCK_9 => 9,
Ids::LIGHT_BLOCK_10 => 10,
Ids::LIGHT_BLOCK_11 => 11,
Ids::LIGHT_BLOCK_12 => 12,
Ids::LIGHT_BLOCK_13 => 13,
Ids::LIGHT_BLOCK_14 => 14,
Ids::LIGHT_BLOCK_15 => 15,
] as $id => $level){
$this->mapSimple($id, fn() => Blocks::LIGHT()->setLightLevel($level));
}
}
private function registerMobHeadDeserializers() : void{
foreach([
Ids::CREEPER_HEAD => MobHeadType::CREEPER,
Ids::DRAGON_HEAD => MobHeadType::DRAGON,
Ids::PIGLIN_HEAD => MobHeadType::PIGLIN,
Ids::PLAYER_HEAD => MobHeadType::PLAYER,
Ids::SKELETON_SKULL => MobHeadType::SKELETON,
Ids::WITHER_SKELETON_SKULL => MobHeadType::WITHER_SKELETON,
Ids::ZOMBIE_HEAD => MobHeadType::ZOMBIE
] as $id => $mobHeadType){
$this->map($id, fn(Reader $in) => Blocks::MOB_HEAD()->setMobHeadType($mobHeadType)->setFacing($in->readFacingWithoutDown()));
}
}
private function registerSimpleDeserializers() : void{
@ -659,6 +719,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::CHISELED_DEEPSLATE, fn() => Blocks::CHISELED_DEEPSLATE());
$this->mapSimple(Ids::CHISELED_NETHER_BRICKS, fn() => Blocks::CHISELED_NETHER_BRICKS());
$this->mapSimple(Ids::CHISELED_POLISHED_BLACKSTONE, fn() => Blocks::CHISELED_POLISHED_BLACKSTONE());
$this->mapSimple(Ids::CHISELED_RED_SANDSTONE, fn() => Blocks::CHISELED_RED_SANDSTONE());
$this->mapSimple(Ids::CHISELED_SANDSTONE, fn() => Blocks::CHISELED_SANDSTONE());
$this->mapSimple(Ids::CHISELED_STONE_BRICKS, fn() => Blocks::CHISELED_STONE_BRICKS());
$this->mapSimple(Ids::CHORUS_PLANT, fn() => Blocks::CHORUS_PLANT());
$this->mapSimple(Ids::CLAY, fn() => Blocks::CLAY());
$this->mapSimple(Ids::COAL_BLOCK, fn() => Blocks::COAL());
@ -670,9 +733,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::CRACKED_DEEPSLATE_TILES, fn() => Blocks::CRACKED_DEEPSLATE_TILES());
$this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS());
$this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
$this->mapSimple(Ids::CRACKED_STONE_BRICKS, fn() => Blocks::CRACKED_STONE_BRICKS());
$this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE());
$this->mapSimple(Ids::CRIMSON_ROOTS, fn() => Blocks::CRIMSON_ROOTS());
$this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN());
$this->mapSimple(Ids::CUT_RED_SANDSTONE, fn() => Blocks::CUT_RED_SANDSTONE());
$this->mapSimple(Ids::CUT_SANDSTONE, fn() => Blocks::CUT_SANDSTONE());
$this->mapSimple(Ids::DARK_PRISMARINE, fn() => Blocks::DARK_PRISMARINE());
$this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH());
$this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS());
$this->mapSimple(Ids::DEEPSLATE_COAL_ORE, fn() => Blocks::DEEPSLATE_COAL_ORE());
@ -812,6 +879,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::ENCHANTING_TABLE, fn() => Blocks::ENCHANTING_TABLE());
$this->mapSimple(Ids::END_BRICKS, fn() => Blocks::END_STONE_BRICKS());
$this->mapSimple(Ids::END_STONE, fn() => Blocks::END_STONE());
$this->mapSimple(Ids::FERN, fn() => Blocks::FERN());
$this->mapSimple(Ids::FLETCHING_TABLE, fn() => Blocks::FLETCHING_TABLE());
$this->mapSimple(Ids::GILDED_BLACKSTONE, fn() => Blocks::GILDED_BLACKSTONE());
$this->mapSimple(Ids::GLASS, fn() => Blocks::GLASS());
@ -821,7 +889,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::GOLD_BLOCK, fn() => Blocks::GOLD());
$this->mapSimple(Ids::GOLD_ORE, fn() => Blocks::GOLD_ORE());
$this->mapSimple(Ids::GRANITE, fn() => Blocks::GRANITE());
$this->mapSimple(Ids::GRASS, fn() => Blocks::GRASS());
$this->mapSimple(Ids::GRASS_BLOCK, fn() => Blocks::GRASS());
$this->mapSimple(Ids::GRASS_PATH, fn() => Blocks::GRASS_PATH());
$this->mapSimple(Ids::GRAVEL, fn() => Blocks::GRAVEL());
$this->mapSimple(Ids::HANGING_ROOTS, fn() => Blocks::HANGING_ROOTS());
@ -830,6 +898,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::HARDENED_CLAY, fn() => Blocks::HARDENED_CLAY());
$this->mapSimple(Ids::HONEYCOMB_BLOCK, fn() => Blocks::HONEYCOMB());
$this->mapSimple(Ids::ICE, fn() => Blocks::ICE());
$this->mapSimple(Ids::INFESTED_CHISELED_STONE_BRICKS, fn() => Blocks::INFESTED_CHISELED_STONE_BRICK());
$this->mapSimple(Ids::INFESTED_COBBLESTONE, fn() => Blocks::INFESTED_COBBLESTONE());
$this->mapSimple(Ids::INFESTED_CRACKED_STONE_BRICKS, fn() => Blocks::INFESTED_CRACKED_STONE_BRICK());
$this->mapSimple(Ids::INFESTED_MOSSY_STONE_BRICKS, fn() => Blocks::INFESTED_MOSSY_STONE_BRICK());
$this->mapSimple(Ids::INFESTED_STONE, fn() => Blocks::INFESTED_STONE());
$this->mapSimple(Ids::INFESTED_STONE_BRICKS, fn() => Blocks::INFESTED_STONE_BRICK());
$this->mapSimple(Ids::INFO_UPDATE, fn() => Blocks::INFO_UPDATE());
$this->mapSimple(Ids::INFO_UPDATE2, fn() => Blocks::INFO_UPDATE2());
$this->mapSimple(Ids::INVISIBLE_BEDROCK, fn() => Blocks::INVISIBLE_BEDROCK());
@ -844,6 +918,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON());
$this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER());
$this->mapSimple(Ids::MOSSY_COBBLESTONE, fn() => Blocks::MOSSY_COBBLESTONE());
$this->mapSimple(Ids::MOSSY_STONE_BRICKS, fn() => Blocks::MOSSY_STONE_BRICKS());
$this->mapSimple(Ids::MUD, fn() => Blocks::MUD());
$this->mapSimple(Ids::MUD_BRICKS, fn() => Blocks::MUD_BRICKS());
$this->mapSimple(Ids::MYCELIUM, fn() => Blocks::MYCELIUM());
@ -865,6 +940,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::POLISHED_DEEPSLATE, fn() => Blocks::POLISHED_DEEPSLATE());
$this->mapSimple(Ids::POLISHED_DIORITE, fn() => Blocks::POLISHED_DIORITE());
$this->mapSimple(Ids::POLISHED_GRANITE, fn() => Blocks::POLISHED_GRANITE());
$this->mapSimple(Ids::PRISMARINE, fn() => Blocks::PRISMARINE());
$this->mapSimple(Ids::PRISMARINE_BRICKS, fn() => Blocks::PRISMARINE_BRICKS());
$this->mapSimple(Ids::QUARTZ_BRICKS, fn() => Blocks::QUARTZ_BRICKS());
$this->mapSimple(Ids::QUARTZ_ORE, fn() => Blocks::NETHER_QUARTZ_ORE());
$this->mapSimple(Ids::RAW_COPPER_BLOCK, fn() => Blocks::RAW_COPPER());
@ -872,22 +949,31 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::RAW_IRON_BLOCK, fn() => Blocks::RAW_IRON());
$this->mapSimple(Ids::RED_MUSHROOM, fn() => Blocks::RED_MUSHROOM());
$this->mapSimple(Ids::RED_NETHER_BRICK, fn() => Blocks::RED_NETHER_BRICKS());
$this->mapSimple(Ids::RED_SAND, fn() => Blocks::RED_SAND());
$this->mapSimple(Ids::RED_SANDSTONE, fn() => Blocks::RED_SANDSTONE());
$this->mapSimple(Ids::REDSTONE_BLOCK, fn() => Blocks::REDSTONE());
$this->mapSimple(Ids::REINFORCED_DEEPSLATE, fn() => Blocks::REINFORCED_DEEPSLATE());
$this->mapSimple(Ids::RESERVED6, fn() => Blocks::RESERVED6());
$this->mapSimple(Ids::SAND, fn() => Blocks::SAND());
$this->mapSimple(Ids::SANDSTONE, fn() => Blocks::SANDSTONE());
$this->mapSimple(Ids::SCULK, fn() => Blocks::SCULK());
$this->mapSimple(Ids::SEA_LANTERN, fn() => Blocks::SEA_LANTERN());
$this->mapSimple(Ids::SHORT_GRASS, fn() => Blocks::TALL_GRASS()); //no, this is not a typo - tall_grass is now the double block, just to be confusing :(
$this->mapSimple(Ids::SHROOMLIGHT, fn() => Blocks::SHROOMLIGHT());
$this->mapSimple(Ids::SLIME, fn() => Blocks::SLIME());
$this->mapSimple(Ids::SMITHING_TABLE, fn() => Blocks::SMITHING_TABLE());
$this->mapSimple(Ids::SMOOTH_BASALT, fn() => Blocks::SMOOTH_BASALT());
$this->mapSimple(Ids::SMOOTH_RED_SANDSTONE, fn() => Blocks::SMOOTH_RED_SANDSTONE());
$this->mapSimple(Ids::SMOOTH_SANDSTONE, fn() => Blocks::SMOOTH_SANDSTONE());
$this->mapSimple(Ids::SMOOTH_STONE, fn() => Blocks::SMOOTH_STONE());
$this->mapSimple(Ids::SNOW, fn() => Blocks::SNOW());
$this->mapSimple(Ids::SOUL_SAND, fn() => Blocks::SOUL_SAND());
$this->mapSimple(Ids::SOUL_SOIL, fn() => Blocks::SOUL_SOIL());
$this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM());
$this->mapSimple(Ids::SPONGE, fn() => Blocks::SPONGE());
$this->mapSimple(Ids::STONE, fn() => Blocks::STONE());
$this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER());
$this->mapSimple(Ids::STONE_BRICKS, fn() => Blocks::STONE_BRICKS());
$this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS());
$this->mapSimple(Ids::TORCHFLOWER, fn() => Blocks::TORCHFLOWER());
$this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF());
@ -896,8 +982,21 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::WARPED_ROOTS, fn() => Blocks::WARPED_ROOTS());
$this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD());
$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
$this->mapSimple(Ids::WET_SPONGE, fn() => Blocks::SPONGE()->setWet(true));
$this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE());
$this->mapSimple(Ids::YELLOW_FLOWER, fn() => Blocks::DANDELION());
$this->mapSimple(Ids::DANDELION, fn() => Blocks::DANDELION());
$this->mapSimple(Ids::ALLIUM, fn() => Blocks::ALLIUM());
$this->mapSimple(Ids::CORNFLOWER, fn() => Blocks::CORNFLOWER());
$this->mapSimple(Ids::AZURE_BLUET, fn() => Blocks::AZURE_BLUET());
$this->mapSimple(Ids::LILY_OF_THE_VALLEY, fn() => Blocks::LILY_OF_THE_VALLEY());
$this->mapSimple(Ids::BLUE_ORCHID, fn() => Blocks::BLUE_ORCHID());
$this->mapSimple(Ids::OXEYE_DAISY, fn() => Blocks::OXEYE_DAISY());
$this->mapSimple(Ids::POPPY, fn() => Blocks::POPPY());
$this->mapSimple(Ids::ORANGE_TULIP, fn() => Blocks::ORANGE_TULIP());
$this->mapSimple(Ids::PINK_TULIP, fn() => Blocks::PINK_TULIP());
$this->mapSimple(Ids::RED_TULIP, fn() => Blocks::RED_TULIP());
$this->mapSimple(Ids::WHITE_TULIP, fn() => Blocks::WHITE_TULIP());
}
private function registerDeserializers() : void{
@ -911,16 +1010,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setStage(AmethystCluster::STAGE_CLUSTER)
->setFacing($in->readBlockFace());
});
$this->mapSlab(Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB, fn() => Blocks::ANDESITE_SLAB());
$this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS());
$this->map(Ids::ANDESITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::ANDESITE_WALL(), $in));
$this->map(Ids::ANVIL, function(Reader $in) : Block{
return Blocks::ANVIL()
->setDamage(match($value = $in->readString(StateNames::DAMAGE)){
StringValues::DAMAGE_UNDAMAGED => 0,
StringValues::DAMAGE_SLIGHTLY_DAMAGED => 1,
StringValues::DAMAGE_VERY_DAMAGED => 2,
StringValues::DAMAGE_BROKEN => 0,
default => throw $in->badValueException(StateNames::DAMAGE, $value),
})
->setDamage(Anvil::UNDAMAGED)
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::CHIPPED_ANVIL, function(Reader $in) : Block{
return Blocks::ANVIL()
->setDamage(Anvil::SLIGHTLY_DAMAGED)
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::DAMAGED_ANVIL, function(Reader $in) : Block{
return Blocks::ANVIL()
->setDamage(Anvil::VERY_DAMAGED)
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::BAMBOO, function(Reader $in) : Block{
@ -939,7 +1044,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
});
$this->map(Ids::BAMBOO_SAPLING, function(Reader $in) : Block{
$in->ignored(StateNames::SAPLING_TYPE); //bug in MCPE
return Blocks::BAMBOO_SAPLING()->setReady($in->readBool(StateNames::AGE_BIT));
});
$this->map(Ids::BARREL, function(Reader $in) : Block{
@ -1002,7 +1106,14 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setSlot(BrewingStandSlot::SOUTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_B_BIT))
->setSlot(BrewingStandSlot::NORTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_C_BIT));
});
$this->mapSlab(Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB, fn() => Blocks::BRICK_SLAB());
$this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS());
$this->map(Ids::BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BRICK_WALL(), $in));
$this->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)){
BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(),
BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(),
default => throw new BlockStateDeserializeException("This state does not exist"),
});
$this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in));
$this->map(Ids::CACTUS, function(Reader $in) : Block{
return Blocks::CACTUS()
@ -1051,14 +1162,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return $block;
});
$this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{
return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){
StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(),
StringValues::CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR => Blocks::ELEMENT_CONSTRUCTOR(),
StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE => Blocks::LAB_TABLE(),
StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER => Blocks::MATERIAL_REDUCER(),
default => throw $in->badValueException(StateNames::CHEMISTRY_TABLE_TYPE, $type),
})->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()));
$this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{
return Blocks::CHISELED_QUARTZ()
->setAxis($in->readPillarAxis());
});
$this->map(Ids::CHEST, function(Reader $in) : Block{
return Blocks::CHEST()
@ -1068,47 +1174,31 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::CHORUS_FLOWER()
->setAge($in->readBoundedInt(StateNames::AGE, ChorusFlower::MIN_AGE, ChorusFlower::MAX_AGE));
});
$this->map(Ids::COARSE_DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::COARSE));
$this->mapSlab(Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::COBBLED_DEEPSLATE_SLAB());
$this->mapStairs(Ids::COBBLED_DEEPSLATE_STAIRS, fn() => Blocks::COBBLED_DEEPSLATE_STAIRS());
$this->map(Ids::COBBLED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLED_DEEPSLATE_WALL(), $in));
$this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::mapLegacyWallType($in));
$this->mapSlab(Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::COBBLESTONE_SLAB());
$this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLESTONE_WALL(), $in));
$this->map(Ids::COCOA, function(Reader $in) : Block{
return Blocks::COCOA_POD()
->setAge($in->readBoundedInt(StateNames::AGE, 0, 2))
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()));
});
$this->map(Ids::COLORED_TORCH_BP, function(Reader $in) : Block{
return $in->readBool(StateNames::COLOR_BIT) ?
Blocks::PURPLE_TORCH()->setFacing($in->readTorchFacing()) :
Blocks::BLUE_TORCH()->setFacing($in->readTorchFacing());
});
$this->map(Ids::COLORED_TORCH_RG, function(Reader $in) : Block{
return $in->readBool(StateNames::COLOR_BIT) ?
Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing()) :
Blocks::RED_TORCH()->setFacing($in->readTorchFacing());
});
$this->map(Ids::COLORED_TORCH_BLUE, fn(Reader $in) => Blocks::BLUE_TORCH()->setFacing($in->readTorchFacing()));
$this->map(Ids::COLORED_TORCH_GREEN, fn(Reader $in) => Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing()));
$this->map(Ids::COLORED_TORCH_PURPLE, fn(Reader $in) => Blocks::PURPLE_TORCH()->setFacing($in->readTorchFacing()));
$this->map(Ids::COLORED_TORCH_RED, fn(Reader $in) => Blocks::RED_TORCH()->setFacing($in->readTorchFacing()));
$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::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->map(Ids::CORAL_BLOCK, function(Reader $in) : Block{
return Blocks::CORAL_BLOCK()
->setCoralType($in->readCoralType())
->setDead($in->readBool(StateNames::DEAD_BIT));
});
$this->map(Ids::CORAL_FAN, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN(), $in)
->setDead(false));
$this->map(Ids::CORAL_FAN_DEAD, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN(), $in)
->setDead(true));
$this->map(Ids::CORAL_FAN_HANG, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::BRAIN : CoralType::TUBE));
$this->map(Ids::CORAL_FAN_HANG2, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::FIRE : CoralType::BUBBLE));
$this->map(Ids::CORAL_FAN_HANG3, function(Reader $in) : Block{
$in->ignored(StateNames::CORAL_HANG_TYPE_BIT); //the game always writes this, even though it's not used
return Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
->setCoralType(CoralType::HORN);
});
$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());
$this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS());
$this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in)
->setInverted(false));
@ -1130,28 +1220,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setActivated($in->readBool(StateNames::RAIL_DATA_BIT))
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5));
});
$this->mapSlab(Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB, fn() => Blocks::DIORITE_SLAB());
$this->mapStairs(Ids::DIORITE_STAIRS, fn() => Blocks::DIORITE_STAIRS());
$this->map(Ids::DIRT, function(Reader $in) : Block{
return Blocks::DIRT()
->setDirtType(match($value = $in->readString(StateNames::DIRT_TYPE)){
StringValues::DIRT_TYPE_NORMAL => DirtType::NORMAL,
StringValues::DIRT_TYPE_COARSE => DirtType::COARSE,
default => throw $in->badValueException(StateNames::DIRT_TYPE, $value),
});
});
$this->map(Ids::DIORITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DIORITE_WALL(), $in));
$this->map(Ids::DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL));
$this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED));
$this->map(Ids::DOUBLE_PLANT, function(Reader $in) : Block{
return (match($type = $in->readString(StateNames::DOUBLE_PLANT_TYPE)){
StringValues::DOUBLE_PLANT_TYPE_FERN => Blocks::LARGE_FERN(),
StringValues::DOUBLE_PLANT_TYPE_GRASS => Blocks::DOUBLE_TALLGRASS(),
StringValues::DOUBLE_PLANT_TYPE_PAEONIA => Blocks::PEONY(),
StringValues::DOUBLE_PLANT_TYPE_ROSE => Blocks::ROSE_BUSH(),
StringValues::DOUBLE_PLANT_TYPE_SUNFLOWER => Blocks::SUNFLOWER(),
StringValues::DOUBLE_PLANT_TYPE_SYRINGA => Blocks::LILAC(),
default => throw $in->badValueException(StateNames::DOUBLE_PLANT_TYPE, $type),
})->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
});
$this->map(Ids::LARGE_FERN, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LARGE_FERN(), $in));
$this->map(Ids::TALL_GRASS, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::DOUBLE_TALLGRASS(), $in));
$this->map(Ids::PEONY, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::PEONY(), $in));
$this->map(Ids::ROSE_BUSH, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::ROSE_BUSH(), $in));
$this->map(Ids::SUNFLOWER, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::SUNFLOWER(), $in));
$this->map(Ids::LILAC, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LILAC(), $in));
$this->map(Ids::ELEMENT_CONSTRUCTOR, fn(Reader $in) => Blocks::ELEMENT_CONSTRUCTOR()
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()))
);
$this->mapStairs(Ids::END_BRICK_STAIRS, fn() => Blocks::END_STONE_BRICK_STAIRS());
$this->map(Ids::END_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::END_STONE_BRICK_WALL(), $in));
$this->map(Ids::END_PORTAL_FRAME, function(Reader $in) : Block{
return Blocks::END_PORTAL_FRAME()
->setEye($in->readBool(StateNames::END_PORTAL_EYE_BIT))
@ -1161,6 +1245,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::END_ROD()
->setFacing($in->readEndRodFacingDirection());
});
$this->mapSlab(Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::END_STONE_BRICK_SLAB());
$this->map(Ids::ENDER_CHEST, function(Reader $in) : Block{
return Blocks::ENDER_CHEST()
->setFacing($in->readCardinalHorizontalFacing());
@ -1200,7 +1285,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setPowered($in->readBool(StateNames::RAIL_DATA_BIT))
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5));
});
$this->mapSlab(Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB, fn() => Blocks::GRANITE_SLAB());
$this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS());
$this->map(Ids::GRANITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::GRANITE_WALL(), $in));
$this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{
$in->ignored(StateNames::DEPRECATED);
return Blocks::HAY_BALE()->setAxis($in->readPillarAxis());
@ -1213,6 +1300,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
$this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in));
$this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in));
$this->map(Ids::LAB_TABLE, fn(Reader $in) => Blocks::LAB_TABLE()
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()))
);
$this->map(Ids::LADDER, function(Reader $in) : Block{
return Blocks::LADDER()
->setFacing($in->readHorizontalFacing());
@ -1247,10 +1337,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
default => throw $in->badValueException(StateNames::LEVER_DIRECTION, $value),
});
});
$this->map(Ids::LIGHT_BLOCK, function(Reader $in) : Block{
return Blocks::LIGHT()
->setLightLevel($in->readBoundedInt(StateNames::BLOCK_LIGHT_LEVEL, Light::MIN_LIGHT_LEVEL, Light::MAX_LIGHT_LEVEL));
});
$this->map(Ids::LIGHTNING_ROD, function(Reader $in) : Block{
return Blocks::LIGHTNING_ROD()
->setFacing($in->readFacingDirection());
@ -1288,25 +1374,21 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::LOOM()
->setFacing($in->readLegacyHorizontalFacing());
});
$this->map(Ids::MATERIAL_REDUCER, fn(Reader $in) => Blocks::MATERIAL_REDUCER()
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()))
);
$this->map(Ids::MEDIUM_AMETHYST_BUD, function(Reader $in) : Block{
return Blocks::AMETHYST_CLUSTER()
->setStage(AmethystCluster::STAGE_MEDIUM_BUD)
->setFacing($in->readBlockFace());
});
$this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in));
$this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){
StringValues::MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK => Blocks::INFESTED_CHISELED_STONE_BRICK(),
StringValues::MONSTER_EGG_STONE_TYPE_COBBLESTONE => Blocks::INFESTED_COBBLESTONE(),
StringValues::MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK => Blocks::INFESTED_CRACKED_STONE_BRICK(),
StringValues::MONSTER_EGG_STONE_TYPE_MOSSY_STONE_BRICK => Blocks::INFESTED_MOSSY_STONE_BRICK(),
StringValues::MONSTER_EGG_STONE_TYPE_STONE => Blocks::INFESTED_STONE(),
StringValues::MONSTER_EGG_STONE_TYPE_STONE_BRICK => Blocks::INFESTED_STONE_BRICK(),
default => throw $in->badValueException(StateNames::MONSTER_EGG_STONE_TYPE, $type),
};
});
$this->mapSlab(Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::MOSSY_COBBLESTONE_SLAB());
$this->mapStairs(Ids::MOSSY_COBBLESTONE_STAIRS, fn() => Blocks::MOSSY_COBBLESTONE_STAIRS());
$this->map(Ids::MOSSY_COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_COBBLESTONE_WALL(), $in));
$this->mapSlab(Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::MOSSY_STONE_BRICK_SLAB());
$this->mapStairs(Ids::MOSSY_STONE_BRICK_STAIRS, fn() => Blocks::MOSSY_STONE_BRICK_STAIRS());
$this->map(Ids::MOSSY_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_STONE_BRICK_WALL(), $in));
$this->mapSlab(Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB, fn() => Blocks::MUD_BRICK_SLAB());
$this->mapStairs(Ids::MUD_BRICK_STAIRS, fn() => Blocks::MUD_BRICK_STAIRS());
$this->map(Ids::MUD_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MUD_BRICK_WALL(), $in));
@ -1314,11 +1396,14 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::MUDDY_MANGROVE_ROOTS()
->setAxis($in->readPillarAxis());
});
$this->mapSlab(Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::NETHER_BRICK_SLAB());
$this->mapStairs(Ids::NETHER_BRICK_STAIRS, fn() => Blocks::NETHER_BRICK_STAIRS());
$this->map(Ids::NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::NETHER_BRICK_WALL(), $in));
$this->map(Ids::NETHER_WART, function(Reader $in) : Block{
return Blocks::NETHER_WART()
->setAge($in->readBoundedInt(StateNames::AGE, 0, 3));
});
$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));
@ -1326,6 +1411,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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::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{
//Pink petals only uses 0-3, but GROWTH state can go up to 7
$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
@ -1349,6 +1435,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::PITCHER_PLANT()
->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
});
$this->mapSlab(Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_ANDESITE_SLAB());
$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
$this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{
return Blocks::POLISHED_BASALT()
@ -1365,7 +1452,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSlab(Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DEEPSLATE_SLAB());
$this->mapStairs(Ids::POLISHED_DEEPSLATE_STAIRS, fn() => Blocks::POLISHED_DEEPSLATE_STAIRS());
$this->map(Ids::POLISHED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_DEEPSLATE_WALL(), $in));
$this->mapSlab(Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DIORITE_SLAB());
$this->mapStairs(Ids::POLISHED_DIORITE_STAIRS, fn() => Blocks::POLISHED_DIORITE_STAIRS());
$this->mapSlab(Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_GRANITE_SLAB());
$this->mapStairs(Ids::POLISHED_GRANITE_STAIRS, fn() => Blocks::POLISHED_GRANITE_STAIRS());
$this->map(Ids::PORTAL, function(Reader $in) : Block{
return Blocks::NETHER_PORTAL()
@ -1380,15 +1469,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::POWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in));
$this->map(Ids::POWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in)
->setPowered(true));
$this->map(Ids::PRISMARINE, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::PRISMARINE_BLOCK_TYPE)){
StringValues::PRISMARINE_BLOCK_TYPE_BRICKS => Blocks::PRISMARINE_BRICKS(),
StringValues::PRISMARINE_BLOCK_TYPE_DARK => Blocks::DARK_PRISMARINE(),
StringValues::PRISMARINE_BLOCK_TYPE_DEFAULT => Blocks::PRISMARINE(),
default => throw $in->badValueException(StateNames::PRISMARINE_BLOCK_TYPE, $type),
};
});
$this->mapSlab(Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_BRICKS_SLAB());
$this->mapStairs(Ids::PRISMARINE_BRICKS_STAIRS, fn() => Blocks::PRISMARINE_BRICKS_STAIRS());
$this->map(Ids::PRISMARINE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::PRISMARINE_WALL(), $in));
$this->mapSlab(Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_SLAB());
$this->mapStairs(Ids::PRISMARINE_STAIRS, fn() => Blocks::PRISMARINE_STAIRS());
$this->map(Ids::PUMPKIN, function(Reader $in) : Block{
$in->ignored(StateNames::MC_CARDINAL_DIRECTION); //obsolete
@ -1396,69 +1480,33 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
$this->map(Ids::PUMPKIN_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::PUMPKIN_STEM(), $in));
$this->map(Ids::PURPUR_BLOCK, function(Reader $in) : Block{
$type = $in->readString(StateNames::CHISEL_TYPE);
if($type === StringValues::CHISEL_TYPE_LINES){
return Blocks::PURPUR_PILLAR()->setAxis($in->readPillarAxis());
}else{
$in->ignored(StateNames::PILLAR_AXIS); //axis only applies to pillars
return match($type){
StringValues::CHISEL_TYPE_CHISELED, //TODO: bug in MCPE
StringValues::CHISEL_TYPE_SMOOTH, //TODO: bug in MCPE
StringValues::CHISEL_TYPE_DEFAULT => Blocks::PURPUR(),
default => throw $in->badValueException(StateNames::CHISEL_TYPE, $type),
};
}
$in->ignored(StateNames::PILLAR_AXIS); //???
return Blocks::PURPUR();
});
$this->map(Ids::PURPUR_PILLAR, fn(Reader $in) => Blocks::PURPUR_PILLAR()->setAxis($in->readPillarAxis()));
$this->mapSlab(Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB, fn() => Blocks::PURPUR_SLAB());
$this->mapStairs(Ids::PURPUR_STAIRS, fn() => Blocks::PURPUR_STAIRS());
$this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Block{
switch($type = $in->readString(StateNames::CHISEL_TYPE)){
case StringValues::CHISEL_TYPE_CHISELED:
return Blocks::CHISELED_QUARTZ()->setAxis($in->readPillarAxis());
case StringValues::CHISEL_TYPE_DEFAULT:
$in->ignored(StateNames::PILLAR_AXIS);
return Blocks::QUARTZ();
case StringValues::CHISEL_TYPE_LINES:
return Blocks::QUARTZ_PILLAR()->setAxis($in->readPillarAxis());
case StringValues::CHISEL_TYPE_SMOOTH:
$in->ignored(StateNames::PILLAR_AXIS);
return Blocks::SMOOTH_QUARTZ();
default:
return throw $in->badValueException(StateNames::CHISEL_TYPE, $type);
}
$this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Opaque{
$in->ignored(StateNames::PILLAR_AXIS);
return Blocks::QUARTZ();
});
$this->map(Ids::QUARTZ_PILLAR, function(Reader $in) : Block{
return Blocks::QUARTZ_PILLAR()
->setAxis($in->readPillarAxis());
});
$this->mapSlab(Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB, fn() => Blocks::QUARTZ_SLAB());
$this->mapStairs(Ids::QUARTZ_STAIRS, fn() => Blocks::QUARTZ_STAIRS());
$this->map(Ids::RAIL, function(Reader $in) : Block{
return Blocks::RAIL()
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 9));
});
$this->map(Ids::RED_FLOWER, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::FLOWER_TYPE)){
StringValues::FLOWER_TYPE_ALLIUM => Blocks::ALLIUM(),
StringValues::FLOWER_TYPE_CORNFLOWER => Blocks::CORNFLOWER(),
StringValues::FLOWER_TYPE_HOUSTONIA => Blocks::AZURE_BLUET(), //wtf ???
StringValues::FLOWER_TYPE_LILY_OF_THE_VALLEY => Blocks::LILY_OF_THE_VALLEY(),
StringValues::FLOWER_TYPE_ORCHID => Blocks::BLUE_ORCHID(),
StringValues::FLOWER_TYPE_OXEYE => Blocks::OXEYE_DAISY(),
StringValues::FLOWER_TYPE_POPPY => Blocks::POPPY(),
StringValues::FLOWER_TYPE_TULIP_ORANGE => Blocks::ORANGE_TULIP(),
StringValues::FLOWER_TYPE_TULIP_PINK => Blocks::PINK_TULIP(),
StringValues::FLOWER_TYPE_TULIP_RED => Blocks::RED_TULIP(),
StringValues::FLOWER_TYPE_TULIP_WHITE => Blocks::WHITE_TULIP(),
default => throw $in->badValueException(StateNames::FLOWER_TYPE, $type),
};
});
$this->map(Ids::RED_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::RED_MUSHROOM_BLOCK(), $in));
$this->mapSlab(Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::RED_NETHER_BRICK_SLAB());
$this->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS());
$this->map(Ids::RED_SANDSTONE, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::SAND_STONE_TYPE)){
StringValues::SAND_STONE_TYPE_CUT => Blocks::CUT_RED_SANDSTONE(),
StringValues::SAND_STONE_TYPE_DEFAULT => Blocks::RED_SANDSTONE(),
StringValues::SAND_STONE_TYPE_HEIROGLYPHS => Blocks::CHISELED_RED_SANDSTONE(),
StringValues::SAND_STONE_TYPE_SMOOTH => Blocks::SMOOTH_RED_SANDSTONE(),
default => throw $in->badValueException(StateNames::SAND_STONE_TYPE, $type),
};
});
$this->map(Ids::RED_NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_NETHER_BRICK_WALL(), $in));
$this->mapSlab(Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::RED_SANDSTONE_SLAB());
$this->mapStairs(Ids::RED_SANDSTONE_STAIRS, fn() => Blocks::RED_SANDSTONE_STAIRS());
$this->map(Ids::RED_SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_SANDSTONE_WALL(), $in));
$this->map(Ids::REDSTONE_LAMP, function() : Block{
return Blocks::REDSTONE_LAMP()
->setPowered(false);
@ -1480,44 +1528,14 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::SUGARCANE()
->setAge($in->readBoundedInt(StateNames::AGE, 0, 15));
});
$this->map(Ids::SAND, function(Reader $in) : Block{
return match($value = $in->readString(StateNames::SAND_TYPE)){
StringValues::SAND_TYPE_NORMAL => Blocks::SAND(),
StringValues::SAND_TYPE_RED => Blocks::RED_SAND(),
default => throw $in->badValueException(StateNames::SAND_TYPE, $value),
};
});
$this->map(Ids::SANDSTONE, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::SAND_STONE_TYPE)){
StringValues::SAND_STONE_TYPE_CUT => Blocks::CUT_SANDSTONE(),
StringValues::SAND_STONE_TYPE_DEFAULT => Blocks::SANDSTONE(),
StringValues::SAND_STONE_TYPE_HEIROGLYPHS => Blocks::CHISELED_SANDSTONE(),
StringValues::SAND_STONE_TYPE_SMOOTH => Blocks::SMOOTH_SANDSTONE(),
default => throw $in->badValueException(StateNames::SAND_STONE_TYPE, $type),
};
});
$this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB());
$this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS());
$this->map(Ids::SAPLING, function(Reader $in) : Block{
return (match($type = $in->readString(StateNames::SAPLING_TYPE)){
StringValues::SAPLING_TYPE_ACACIA => Blocks::ACACIA_SAPLING(),
StringValues::SAPLING_TYPE_BIRCH => Blocks::BIRCH_SAPLING(),
StringValues::SAPLING_TYPE_DARK_OAK => Blocks::DARK_OAK_SAPLING(),
StringValues::SAPLING_TYPE_JUNGLE => Blocks::JUNGLE_SAPLING(),
StringValues::SAPLING_TYPE_OAK => Blocks::OAK_SAPLING(),
StringValues::SAPLING_TYPE_SPRUCE => Blocks::SPRUCE_SAPLING(),
default => throw $in->badValueException(StateNames::SAPLING_TYPE, $type),
})
->setReady($in->readBool(StateNames::AGE_BIT));
});
$this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in));
$this->map(Ids::SEA_PICKLE, function(Reader $in) : Block{
return Blocks::SEA_PICKLE()
->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1)
->setUnderwater(!$in->readBool(StateNames::DEAD_BIT));
});
$this->map(Ids::SKULL, function(Reader $in) : Block{
return Blocks::MOB_HEAD()
->setFacing($in->readFacingWithoutDown());
});
$this->map(Ids::SMOKER, function(Reader $in) : Block{
return Blocks::SMOKER()
->setFacing($in->readCardinalHorizontalFacing())
@ -1533,9 +1551,17 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readCardinalHorizontalFacing())
->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
});
$this->map(Ids::SMOOTH_QUARTZ, function(Reader $in) : Block{
$in->ignored(StateNames::PILLAR_AXIS);
return Blocks::SMOOTH_QUARTZ();
});
$this->mapSlab(Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB, fn() => Blocks::SMOOTH_QUARTZ_SLAB());
$this->mapStairs(Ids::SMOOTH_QUARTZ_STAIRS, fn() => Blocks::SMOOTH_QUARTZ_STAIRS());
$this->mapSlab(Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_RED_SANDSTONE_SLAB());
$this->mapStairs(Ids::SMOOTH_RED_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_RED_SANDSTONE_STAIRS());
$this->mapSlab(Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_SANDSTONE_SLAB());
$this->mapStairs(Ids::SMOOTH_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_SANDSTONE_STAIRS());
$this->mapSlab(Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_STONE_SLAB());
$this->map(Ids::SNOW_LAYER, function(Reader $in) : Block{
$in->ignored(StateNames::COVERED_BIT); //seems to be useless
return Blocks::SNOW_LAYER()->setLayers($in->readBoundedInt(StateNames::HEIGHT, 0, 7) + 1);
@ -1552,35 +1578,16 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::SOUL_TORCH()
->setFacing($in->readTorchFacing());
});
$this->map(Ids::SPONGE, function(Reader $in) : Block{
return Blocks::SPONGE()->setWet(match($type = $in->readString(StateNames::SPONGE_TYPE)){
StringValues::SPONGE_TYPE_DRY => false,
StringValues::SPONGE_TYPE_WET => true,
default => throw $in->badValueException(StateNames::SPONGE_TYPE, $type),
});
});
$this->map(Ids::STANDING_BANNER, function(Reader $in) : Block{
return Blocks::BANNER()
->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15));
});
$this->mapSlab(Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::STONE_BRICK_SLAB());
$this->mapStairs(Ids::STONE_BRICK_STAIRS, fn() => Blocks::STONE_BRICK_STAIRS());
$this->map(Ids::STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::STONE_BRICK_WALL(), $in));
$this->map(Ids::STONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::STONE_BUTTON(), $in));
$this->map(Ids::STONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::STONE_PRESSURE_PLATE(), $in));
$this->mapSlab(Ids::STONE_BLOCK_SLAB, Ids::DOUBLE_STONE_BLOCK_SLAB, fn(Reader $in) => Helper::mapStoneSlab1Type($in));
$this->mapSlab(Ids::STONE_BLOCK_SLAB2, Ids::DOUBLE_STONE_BLOCK_SLAB2, fn(Reader $in) => Helper::mapStoneSlab2Type($in));
$this->mapSlab(Ids::STONE_BLOCK_SLAB3, Ids::DOUBLE_STONE_BLOCK_SLAB3, fn(Reader $in) => Helper::mapStoneSlab3Type($in));
$this->mapSlab(Ids::STONE_BLOCK_SLAB4, Ids::DOUBLE_STONE_BLOCK_SLAB4, fn(Reader $in) => Helper::mapStoneSlab4Type($in));
$this->mapStairs(Ids::STONE_STAIRS, fn() => Blocks::COBBLESTONE_STAIRS());
$this->map(Ids::STONEBRICK, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::STONE_BRICK_TYPE)){
StringValues::STONE_BRICK_TYPE_SMOOTH, //TODO: bug in vanilla
StringValues::STONE_BRICK_TYPE_DEFAULT => Blocks::STONE_BRICKS(),
StringValues::STONE_BRICK_TYPE_CHISELED => Blocks::CHISELED_STONE_BRICKS(),
StringValues::STONE_BRICK_TYPE_CRACKED => Blocks::CRACKED_STONE_BRICKS(),
StringValues::STONE_BRICK_TYPE_MOSSY => Blocks::MOSSY_STONE_BRICKS(),
default => throw $in->badValueException(StateNames::STONE_BRICK_TYPE, $type),
};
});
$this->map(Ids::STONECUTTER_BLOCK, function(Reader $in) : Block{
return Blocks::STONECUTTER()
->setFacing($in->readCardinalHorizontalFacing());
@ -1591,17 +1598,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::SWEET_BERRY_BUSH()
->setAge(min($growth, SweetBerryBush::STAGE_MATURE));
});
$this->map(Ids::TALLGRASS, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::TALL_GRASS_TYPE)){
StringValues::TALL_GRASS_TYPE_DEFAULT, StringValues::TALL_GRASS_TYPE_SNOW, StringValues::TALL_GRASS_TYPE_TALL => Blocks::TALL_GRASS(),
StringValues::TALL_GRASS_TYPE_FERN => Blocks::FERN(),
default => throw $in->badValueException(StateNames::TALL_GRASS_TYPE, $type),
};
});
$this->map(Ids::TNT, function(Reader $in) : Block{
return Blocks::TNT()
->setUnstable($in->readBool(StateNames::EXPLODE_BIT))
->setWorksUnderwater($in->readBool(StateNames::ALLOW_UNDERWATER_BIT));
->setWorksUnderwater(false);
});
$this->map(Ids::TORCH, function(Reader $in) : Block{
return Blocks::TORCH()
@ -1633,6 +1633,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::TWISTING_VINES()
->setAge($in->readBoundedInt(StateNames::TWISTING_VINES_AGE, 0, 25));
});
$this->map(Ids::UNDERWATER_TNT, function(Reader $in) : Block{
return Blocks::TNT()
->setUnstable($in->readBool(StateNames::EXPLODE_BIT))
->setWorksUnderwater(true);
});
$this->map(Ids::UNDERWATER_TORCH, function(Reader $in) : Block{
return Blocks::UNDERWATER_TORCH()
->setFacing($in->readTorchFacing());

View File

@ -24,10 +24,8 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\block\utils\WoodType;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateNames;
@ -257,32 +255,6 @@ final class BlockStateWriter{
return $this;
}
/** @return $this */
public function writeLegacyWoodType(WoodType $treeType) : self{
$this->writeString(BlockStateNames::WOOD_TYPE, match($treeType){
WoodType::OAK => StringValues::WOOD_TYPE_OAK,
WoodType::SPRUCE => StringValues::WOOD_TYPE_SPRUCE,
WoodType::BIRCH => StringValues::WOOD_TYPE_BIRCH,
WoodType::JUNGLE => StringValues::WOOD_TYPE_JUNGLE,
WoodType::ACACIA => StringValues::WOOD_TYPE_ACACIA,
WoodType::DARK_OAK => StringValues::WOOD_TYPE_DARK_OAK,
default => throw new BlockStateSerializeException("Invalid legacy wood type " . $treeType->name)
});
return $this;
}
/** @return $this */
public function writeCoralType(CoralType $coralType) : self{
$this->writeString(BlockStateNames::CORAL_COLOR, match($coralType){
CoralType::TUBE => StringValues::CORAL_COLOR_BLUE,
CoralType::BRAIN => StringValues::CORAL_COLOR_PINK,
CoralType::BUBBLE => StringValues::CORAL_COLOR_PURPLE,
CoralType::FIRE => StringValues::CORAL_COLOR_RED,
CoralType::HORN => StringValues::CORAL_COLOR_YELLOW,
});
return $this;
}
/** @return $this */
public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{
$this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType){

View File

@ -64,20 +64,24 @@ final class BlockStateUpgradeSchema{
*/
public array $remappedStates = [];
public readonly int $versionId;
public function __construct(
public int $maxVersionMajor,
public int $maxVersionMinor,
public int $maxVersionPatch,
public int $maxVersionRevision,
public readonly int $maxVersionMajor,
public readonly int $maxVersionMinor,
public readonly int $maxVersionPatch,
public readonly int $maxVersionRevision,
private int $schemaId
){}
){
$this->versionId = ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision;
}
/**
* @deprecated This is defined by Mojang, and therefore cannot be relied on. Use getSchemaId() instead for
* internal version management.
*/
public function getVersionId() : int{
return ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision;
return $this->versionId;
}
public function getSchemaId() : int{ return $this->schemaId; }

View File

@ -23,17 +23,28 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade;
use function ksort;
use const SORT_STRING;
final class BlockStateUpgradeSchemaFlattenedName{
/**
* @param string[] $flattenedValueRemaps
* @phpstan-param array<string, string> $flattenedValueRemaps
*/
public function __construct(
public string $prefix,
public string $flattenedProperty,
public string $suffix
){}
public string $suffix,
public array $flattenedValueRemaps
){
ksort($this->flattenedValueRemaps, SORT_STRING);
}
public function equals(self $that) : bool{
return $this->prefix === $that->prefix &&
$this->flattenedProperty === $that->flattenedProperty &&
$this->suffix === $that->suffix;
$this->suffix === $that->suffix &&
$this->flattenedValueRemaps === $that->flattenedValueRemaps;
}
}

View File

@ -166,7 +166,8 @@ final class BlockStateUpgradeSchemaUtils{
$remap->newName ?? new BlockStateUpgradeSchemaFlattenedName(
$remap->newFlattenedName->prefix,
$remap->newFlattenedName->flattenedProperty,
$remap->newFlattenedName->suffix
$remap->newFlattenedName->suffix,
$remap->newFlattenedName->flattenedValueRemaps ?? [],
),
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
$remap->copiedState ?? []
@ -301,7 +302,8 @@ final class BlockStateUpgradeSchemaUtils{
new BlockStateUpgradeSchemaModelFlattenedName(
$remap->newName->prefix,
$remap->newName->flattenedProperty,
$remap->newName->suffix
$remap->newName->suffix,
$remap->newName->flattenedValueRemaps
),
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
$remap->copiedState

View File

@ -35,9 +35,14 @@ use function sprintf;
use const SORT_NUMERIC;
final class BlockStateUpgrader{
/** @var BlockStateUpgradeSchema[] */
/**
* @var BlockStateUpgradeSchema[][] versionId => [schemaId => schema]
* @phpstan-var array<int, array<int, BlockStateUpgradeSchema>>
*/
private array $upgradeSchemas = [];
private int $outputVersion = 0;
/**
* @param BlockStateUpgradeSchema[] $upgradeSchemas
* @phpstan-param array<int, BlockStateUpgradeSchema> $upgradeSchemas
@ -50,87 +55,116 @@ final class BlockStateUpgrader{
public function addSchema(BlockStateUpgradeSchema $schema) : void{
$schemaId = $schema->getSchemaId();
if(isset($this->upgradeSchemas[$schemaId])){
throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID");
$versionId = $schema->getVersionId();
if(isset($this->upgradeSchemas[$versionId][$schemaId])){
throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID and version ID");
}
$this->upgradeSchemas[$schemaId] = $schema;
//schema ID tells us the order when multiple schemas use the same version ID
$this->upgradeSchemas[$versionId][$schemaId] = $schema;
ksort($this->upgradeSchemas, SORT_NUMERIC);
ksort($this->upgradeSchemas[$versionId], SORT_NUMERIC);
$this->outputVersion = max($this->outputVersion, $schema->getVersionId());
}
public function upgrade(BlockStateData $blockStateData) : BlockStateData{
$version = $blockStateData->getVersion();
$highestVersion = $version;
foreach($this->upgradeSchemas as $schema){
$resultVersion = $schema->getVersionId();
$highestVersion = max($highestVersion, $resultVersion);
if($version > $resultVersion){
//even if this is actually the same version, we have to apply it anyway because mojang are dumb and
//didn't always bump the blockstate version when changing it :(
foreach($this->upgradeSchemas as $resultVersion => $schemaList){
/*
* Sometimes Mojang made changes without bumping the version ID.
* A notable example is 0131_1.18.20.27_beta_to_1.18.30.json, which renamed a bunch of blockIDs.
* When this happens, all the schemas must be applied even if the version is the same, because the input
* version doesn't tell us which of the schemas have already been applied.
* If there's only one schema for a version (the norm), we can safely assume it's already been applied if
* the version is the same, and skip over it.
*/
if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
continue;
}
$oldName = $blockStateData->getName();
$oldState = $blockStateData->getStates();
if(isset($schema->remappedStates[$oldName])){
foreach($schema->remappedStates[$oldName] as $remap){
if(count($remap->oldState) > count($oldState)){
//match criteria has more requirements than we have state properties
continue; //try next state
}
foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){
continue 2; //try next state
}
}
if(is_string($remap->newName)){
$newName = $remap->newName;
}else{
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
if($flattenedValue instanceof StringTag){
$newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix);
unset($oldState[$remap->newName->flattenedProperty]);
}else{
//flattened property is not a TAG_String, so this transformation is not applicable
continue;
}
}
$newState = $remap->newState;
foreach($remap->copiedState as $stateName){
if(isset($oldState[$stateName])){
$newState[$stateName] = $oldState[$stateName];
}
}
$blockStateData = new BlockStateData($newName, $newState, $resultVersion);
continue 2; //try next schema
}
}
$newName = $schema->renamedIds[$oldName] ?? null;
$stateChanges = 0;
$states = $blockStateData->getStates();
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
if($newName !== null || $stateChanges > 0){
$blockStateData = new BlockStateData($newName ?? $oldName, $states, $resultVersion);
//don't break out; we may need to further upgrade the state
foreach($schemaList as $schema){
$blockStateData = $this->applySchema($schema, $blockStateData);
}
}
if($highestVersion > $version){
if($this->outputVersion > $version){
//always update the version number of the blockstate, even if it didn't change - this is needed for
//external tools
$blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $highestVersion);
$blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion);
}
return $blockStateData;
}
private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{
$newStateData = $this->applyStateRemapped($schema, $blockStateData);
if($newStateData !== null){
return $newStateData;
}
$oldName = $blockStateData->getName();
$newName = $schema->renamedIds[$oldName] ?? null;
$stateChanges = 0;
$states = $blockStateData->getStates();
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
if($newName !== null || $stateChanges > 0){
return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId());
}
return $blockStateData;
}
private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{
$oldName = $blockStateData->getName();
$oldState = $blockStateData->getStates();
if(isset($schema->remappedStates[$oldName])){
foreach($schema->remappedStates[$oldName] as $remap){
if(count($remap->oldState) > count($oldState)){
//match criteria has more requirements than we have state properties
continue; //try next state
}
foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){
continue 2; //try next state
}
}
if(is_string($remap->newName)){
$newName = $remap->newName;
}else{
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
if($flattenedValue instanceof StringTag){
$embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue();
$newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix);
unset($oldState[$remap->newName->flattenedProperty]);
}else{
//flattened property is not a TAG_String, so this transformation is not applicable
continue;
}
}
$newState = $remap->newState;
foreach($remap->copiedState as $stateName){
if(isset($oldState[$stateName])){
$newState[$stateName] = $oldState[$stateName];
}
}
return new BlockStateData($newName, $newState, $schema->getVersionId());
}
}
return null;
}
/**
* @param Tag[] $states
* @phpstan-param array<string, Tag> $states

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade\model;
final class BlockStateUpgradeSchemaModelFlattenedName{
use function count;
final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{
/** @required */
public string $prefix;
@ -31,10 +33,31 @@ final class BlockStateUpgradeSchemaModelFlattenedName{
public string $flattenedProperty;
/** @required */
public string $suffix;
/**
* @var string[]
* @phpstan-var array<string, string>
*/
public array $flattenedValueRemaps;
public function __construct(string $prefix, string $flattenedProperty, string $suffix){
/**
* @param string[] $flattenedValueRemaps
* @phpstan-param array<string, string> $flattenedValueRemaps
*/
public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps){
$this->prefix = $prefix;
$this->flattenedProperty = $flattenedProperty;
$this->suffix = $suffix;
$this->flattenedValueRemaps = $flattenedValueRemaps;
}
/**
* @return mixed[]
*/
public function jsonSerialize() : array{
$result = (array) $this;
if(count($this->flattenedValueRemaps) === 0){
unset($result["flattenedValueRemaps"]);
}
return $result;
}
}

View File

@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\item;
use pocketmine\block\Bed;
use pocketmine\block\Block;
use pocketmine\block\MobHead;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\data\bedrock\CompoundTypeIds;
@ -33,7 +32,6 @@ use pocketmine\data\bedrock\DyeColorIdMap;
use pocketmine\data\bedrock\item\ItemTypeNames as Ids;
use pocketmine\data\bedrock\item\SavedItemData as Data;
use pocketmine\data\bedrock\MedicineTypeIdMap;
use pocketmine\data\bedrock\MobHeadTypeIdMap;
use pocketmine\data\bedrock\PotionTypeIdMap;
use pocketmine\data\bedrock\SuspiciousStewTypeIdMap;
use pocketmine\item\Banner;
@ -464,14 +462,6 @@ final class ItemSerializerDeserializerRegistrar{
},
fn(Bed $block) => DyeColorIdMap::getInstance()->toId($block->getColor())
);
$this->map1to1BlockWithMeta(
Ids::SKULL,
Blocks::MOB_HEAD(),
function(MobHead $block, int $meta) : void{
$block->setMobHeadType(MobHeadTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown mob head type ID $meta"));
},
fn(MobHead $block) => MobHeadTypeIdMap::getInstance()->toId($block->getMobHeadType())
);
}
/**

View File

@ -66,14 +66,18 @@ final class ItemTypeNames{
public const BIRCH_DOOR = "minecraft:birch_door";
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
public const BIRCH_SIGN = "minecraft:birch_sign";
public const BLACK_BUNDLE = "minecraft:black_bundle";
public const BLACK_DYE = "minecraft:black_dye";
public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd";
public const BLAZE_POWDER = "minecraft:blaze_powder";
public const BLAZE_ROD = "minecraft:blaze_rod";
public const BLAZE_SPAWN_EGG = "minecraft:blaze_spawn_egg";
public const BLEACH = "minecraft:bleach";
public const BLUE_BUNDLE = "minecraft:blue_bundle";
public const BLUE_DYE = "minecraft:blue_dye";
public const BOAT = "minecraft:boat";
public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg";
public const BOLT_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:bolt_armor_trim_smithing_template";
public const BONE = "minecraft:bone";
public const BONE_MEAL = "minecraft:bone_meal";
public const BOOK = "minecraft:book";
@ -81,13 +85,16 @@ final class ItemTypeNames{
public const BOW = "minecraft:bow";
public const BOWL = "minecraft:bowl";
public const BREAD = "minecraft:bread";
public const BREEZE_ROD = "minecraft:breeze_rod";
public const BREEZE_SPAWN_EGG = "minecraft:breeze_spawn_egg";
public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd";
public const BREWING_STAND = "minecraft:brewing_stand";
public const BRICK = "minecraft:brick";
public const BROWN_BUNDLE = "minecraft:brown_bundle";
public const BROWN_DYE = "minecraft:brown_dye";
public const BRUSH = "minecraft:brush";
public const BUCKET = "minecraft:bucket";
public const BUNDLE = "minecraft:bundle";
public const BURN_POTTERY_SHERD = "minecraft:burn_pottery_sherd";
public const CAKE = "minecraft:cake";
public const CAMEL_SPAWN_EGG = "minecraft:camel_spawn_egg";
@ -105,6 +112,7 @@ final class ItemTypeNames{
public const CHAINMAIL_HELMET = "minecraft:chainmail_helmet";
public const CHAINMAIL_LEGGINGS = "minecraft:chainmail_leggings";
public const CHARCOAL = "minecraft:charcoal";
public const CHEMISTRY_TABLE = "minecraft:chemistry_table";
public const CHERRY_BOAT = "minecraft:cherry_boat";
public const CHERRY_CHEST_BOAT = "minecraft:cherry_chest_boat";
public const CHERRY_DOOR = "minecraft:cherry_door";
@ -123,6 +131,8 @@ final class ItemTypeNames{
public const COD = "minecraft:cod";
public const COD_BUCKET = "minecraft:cod_bucket";
public const COD_SPAWN_EGG = "minecraft:cod_spawn_egg";
public const COLORED_TORCH_BP = "minecraft:colored_torch_bp";
public const COLORED_TORCH_RG = "minecraft:colored_torch_rg";
public const COMMAND_BLOCK_MINECART = "minecraft:command_block_minecart";
public const COMPARATOR = "minecraft:comparator";
public const COMPASS = "minecraft:compass";
@ -140,6 +150,9 @@ final class ItemTypeNames{
public const COPPER_DOOR = "minecraft:copper_door";
public const COPPER_INGOT = "minecraft:copper_ingot";
public const CORAL = "minecraft:coral";
public const CORAL_BLOCK = "minecraft:coral_block";
public const CORAL_FAN = "minecraft:coral_fan";
public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead";
public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg";
public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern";
public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg";
@ -147,6 +160,7 @@ final class ItemTypeNames{
public const CRIMSON_HANGING_SIGN = "minecraft:crimson_hanging_sign";
public const CRIMSON_SIGN = "minecraft:crimson_sign";
public const CROSSBOW = "minecraft:crossbow";
public const CYAN_BUNDLE = "minecraft:cyan_bundle";
public const CYAN_DYE = "minecraft:cyan_dye";
public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd";
public const DARK_OAK_BOAT = "minecraft:dark_oak_boat";
@ -169,6 +183,11 @@ final class ItemTypeNames{
public const DISC_FRAGMENT_5 = "minecraft:disc_fragment_5";
public const DOLPHIN_SPAWN_EGG = "minecraft:dolphin_spawn_egg";
public const DONKEY_SPAWN_EGG = "minecraft:donkey_spawn_egg";
public const DOUBLE_PLANT = "minecraft:double_plant";
public const DOUBLE_STONE_BLOCK_SLAB = "minecraft:double_stone_block_slab";
public const DOUBLE_STONE_BLOCK_SLAB2 = "minecraft:double_stone_block_slab2";
public const DOUBLE_STONE_BLOCK_SLAB3 = "minecraft:double_stone_block_slab3";
public const DOUBLE_STONE_BLOCK_SLAB4 = "minecraft:double_stone_block_slab4";
public const DRAGON_BREATH = "minecraft:dragon_breath";
public const DRIED_KELP = "minecraft:dried_kelp";
public const DROWNED_SPAWN_EGG = "minecraft:drowned_spawn_egg";
@ -204,6 +223,9 @@ final class ItemTypeNames{
public const FISHING_ROD = "minecraft:fishing_rod";
public const FLINT = "minecraft:flint";
public const FLINT_AND_STEEL = "minecraft:flint_and_steel";
public const FLOW_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:flow_armor_trim_smithing_template";
public const FLOW_BANNER_PATTERN = "minecraft:flow_banner_pattern";
public const FLOW_POTTERY_SHERD = "minecraft:flow_pottery_sherd";
public const FLOWER_BANNER_PATTERN = "minecraft:flower_banner_pattern";
public const FLOWER_POT = "minecraft:flower_pot";
public const FOX_SPAWN_EGG = "minecraft:fox_spawn_egg";
@ -237,10 +259,14 @@ final class ItemTypeNames{
public const GOLDEN_PICKAXE = "minecraft:golden_pickaxe";
public const GOLDEN_SHOVEL = "minecraft:golden_shovel";
public const GOLDEN_SWORD = "minecraft:golden_sword";
public const GRAY_BUNDLE = "minecraft:gray_bundle";
public const GRAY_DYE = "minecraft:gray_dye";
public const GREEN_BUNDLE = "minecraft:green_bundle";
public const GREEN_DYE = "minecraft:green_dye";
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
public const GUNPOWDER = "minecraft:gunpowder";
public const GUSTER_BANNER_PATTERN = "minecraft:guster_banner_pattern";
public const GUSTER_POTTERY_SHERD = "minecraft:guster_pottery_sherd";
public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass";
public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane";
public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea";
@ -286,14 +312,22 @@ final class ItemTypeNames{
public const LEATHER_HELMET = "minecraft:leather_helmet";
public const LEATHER_HORSE_ARMOR = "minecraft:leather_horse_armor";
public const LEATHER_LEGGINGS = "minecraft:leather_leggings";
public const LEAVES = "minecraft:leaves";
public const LEAVES2 = "minecraft:leaves2";
public const LIGHT_BLOCK = "minecraft:light_block";
public const LIGHT_BLUE_BUNDLE = "minecraft:light_blue_bundle";
public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye";
public const LIGHT_GRAY_BUNDLE = "minecraft:light_gray_bundle";
public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye";
public const LIME_BUNDLE = "minecraft:lime_bundle";
public const LIME_DYE = "minecraft:lime_dye";
public const LINGERING_POTION = "minecraft:lingering_potion";
public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg";
public const LODESTONE_COMPASS = "minecraft:lodestone_compass";
public const LOG = "minecraft:log";
public const LOG2 = "minecraft:log2";
public const MACE = "minecraft:mace";
public const MAGENTA_BUNDLE = "minecraft:magenta_bundle";
public const MAGENTA_DYE = "minecraft:magenta_dye";
public const MAGMA_CREAM = "minecraft:magma_cream";
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
@ -309,6 +343,7 @@ final class ItemTypeNames{
public const MINECART = "minecraft:minecart";
public const MINER_POTTERY_SHERD = "minecraft:miner_pottery_sherd";
public const MOJANG_BANNER_PATTERN = "minecraft:mojang_banner_pattern";
public const MONSTER_EGG = "minecraft:monster_egg";
public const MOOSHROOM_SPAWN_EGG = "minecraft:mooshroom_spawn_egg";
public const MOURNER_POTTERY_SHERD = "minecraft:mourner_pottery_sherd";
public const MULE_SPAWN_EGG = "minecraft:mule_spawn_egg";
@ -319,11 +354,14 @@ final class ItemTypeNames{
public const MUSIC_DISC_BLOCKS = "minecraft:music_disc_blocks";
public const MUSIC_DISC_CAT = "minecraft:music_disc_cat";
public const MUSIC_DISC_CHIRP = "minecraft:music_disc_chirp";
public const MUSIC_DISC_CREATOR = "minecraft:music_disc_creator";
public const MUSIC_DISC_CREATOR_MUSIC_BOX = "minecraft:music_disc_creator_music_box";
public const MUSIC_DISC_FAR = "minecraft:music_disc_far";
public const MUSIC_DISC_MALL = "minecraft:music_disc_mall";
public const MUSIC_DISC_MELLOHI = "minecraft:music_disc_mellohi";
public const MUSIC_DISC_OTHERSIDE = "minecraft:music_disc_otherside";
public const MUSIC_DISC_PIGSTEP = "minecraft:music_disc_pigstep";
public const MUSIC_DISC_PRECIPICE = "minecraft:music_disc_precipice";
public const MUSIC_DISC_RELIC = "minecraft:music_disc_relic";
public const MUSIC_DISC_STAL = "minecraft:music_disc_stal";
public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad";
@ -354,6 +392,9 @@ final class ItemTypeNames{
public const OAK_HANGING_SIGN = "minecraft:oak_hanging_sign";
public const OAK_SIGN = "minecraft:oak_sign";
public const OCELOT_SPAWN_EGG = "minecraft:ocelot_spawn_egg";
public const OMINOUS_BOTTLE = "minecraft:ominous_bottle";
public const OMINOUS_TRIAL_KEY = "minecraft:ominous_trial_key";
public const ORANGE_BUNDLE = "minecraft:orange_bundle";
public const ORANGE_DYE = "minecraft:orange_dye";
public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door";
public const PAINTING = "minecraft:painting";
@ -367,6 +408,7 @@ final class ItemTypeNames{
public const PIGLIN_BRUTE_SPAWN_EGG = "minecraft:piglin_brute_spawn_egg";
public const PIGLIN_SPAWN_EGG = "minecraft:piglin_spawn_egg";
public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg";
public const PINK_BUNDLE = "minecraft:pink_bundle";
public const PINK_DYE = "minecraft:pink_dye";
public const PITCHER_POD = "minecraft:pitcher_pod";
public const PLANKS = "minecraft:planks";
@ -386,6 +428,7 @@ final class ItemTypeNames{
public const PUFFERFISH_SPAWN_EGG = "minecraft:pufferfish_spawn_egg";
public const PUMPKIN_PIE = "minecraft:pumpkin_pie";
public const PUMPKIN_SEEDS = "minecraft:pumpkin_seeds";
public const PURPLE_BUNDLE = "minecraft:purple_bundle";
public const PURPLE_DYE = "minecraft:purple_dye";
public const QUARTZ = "minecraft:quartz";
public const RABBIT = "minecraft:rabbit";
@ -400,7 +443,9 @@ final class ItemTypeNames{
public const RAW_GOLD = "minecraft:raw_gold";
public const RAW_IRON = "minecraft:raw_iron";
public const RECOVERY_COMPASS = "minecraft:recovery_compass";
public const RED_BUNDLE = "minecraft:red_bundle";
public const RED_DYE = "minecraft:red_dye";
public const RED_FLOWER = "minecraft:red_flower";
public const REDSTONE = "minecraft:redstone";
public const REPEATER = "minecraft:repeater";
public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:rib_armor_trim_smithing_template";
@ -409,6 +454,8 @@ final class ItemTypeNames{
public const SALMON = "minecraft:salmon";
public const SALMON_BUCKET = "minecraft:salmon_bucket";
public const SALMON_SPAWN_EGG = "minecraft:salmon_spawn_egg";
public const SAPLING = "minecraft:sapling";
public const SCRAPE_POTTERY_SHERD = "minecraft:scrape_pottery_sherd";
public const SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:sentry_armor_trim_smithing_template";
public const SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:shaper_armor_trim_smithing_template";
public const SHEAF_POTTERY_SHERD = "minecraft:sheaf_pottery_sherd";
@ -452,10 +499,15 @@ final class ItemTypeNames{
public const STAINED_HARDENED_CLAY = "minecraft:stained_hardened_clay";
public const STICK = "minecraft:stick";
public const STONE_AXE = "minecraft:stone_axe";
public const STONE_BLOCK_SLAB = "minecraft:stone_block_slab";
public const STONE_BLOCK_SLAB2 = "minecraft:stone_block_slab2";
public const STONE_BLOCK_SLAB3 = "minecraft:stone_block_slab3";
public const STONE_BLOCK_SLAB4 = "minecraft:stone_block_slab4";
public const STONE_HOE = "minecraft:stone_hoe";
public const STONE_PICKAXE = "minecraft:stone_pickaxe";
public const STONE_SHOVEL = "minecraft:stone_shovel";
public const STONE_SWORD = "minecraft:stone_sword";
public const STONEBRICK = "minecraft:stonebrick";
public const STRAY_SPAWN_EGG = "minecraft:stray_spawn_egg";
public const STRIDER_SPAWN_EGG = "minecraft:strider_spawn_egg";
public const STRING = "minecraft:string";
@ -465,6 +517,7 @@ final class ItemTypeNames{
public const SWEET_BERRIES = "minecraft:sweet_berries";
public const TADPOLE_BUCKET = "minecraft:tadpole_bucket";
public const TADPOLE_SPAWN_EGG = "minecraft:tadpole_spawn_egg";
public const TALLGRASS = "minecraft:tallgrass";
public const TIDE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:tide_armor_trim_smithing_template";
public const TNT_MINECART = "minecraft:tnt_minecart";
public const TORCHFLOWER_SEEDS = "minecraft:torchflower_seeds";
@ -498,22 +551,27 @@ final class ItemTypeNames{
public const WEATHERED_COPPER_DOOR = "minecraft:weathered_copper_door";
public const WHEAT = "minecraft:wheat";
public const WHEAT_SEEDS = "minecraft:wheat_seeds";
public const WHITE_BUNDLE = "minecraft:white_bundle";
public const WHITE_DYE = "minecraft:white_dye";
public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template";
public const WIND_CHARGE = "minecraft:wind_charge";
public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg";
public const WITHER_SKELETON_SPAWN_EGG = "minecraft:wither_skeleton_spawn_egg";
public const WITHER_SPAWN_EGG = "minecraft:wither_spawn_egg";
public const WOLF_ARMOR = "minecraft:wolf_armor";
public const WOLF_SPAWN_EGG = "minecraft:wolf_spawn_egg";
public const WOOD = "minecraft:wood";
public const WOODEN_AXE = "minecraft:wooden_axe";
public const WOODEN_DOOR = "minecraft:wooden_door";
public const WOODEN_HOE = "minecraft:wooden_hoe";
public const WOODEN_PICKAXE = "minecraft:wooden_pickaxe";
public const WOODEN_SHOVEL = "minecraft:wooden_shovel";
public const WOODEN_SLAB = "minecraft:wooden_slab";
public const WOODEN_SWORD = "minecraft:wooden_sword";
public const WOOL = "minecraft:wool";
public const WRITABLE_BOOK = "minecraft:writable_book";
public const WRITTEN_BOOK = "minecraft:written_book";
public const YELLOW_BUNDLE = "minecraft:yellow_bundle";
public const YELLOW_DYE = "minecraft:yellow_dye";
public const ZOGLIN_SPAWN_EGG = "minecraft:zoglin_spawn_egg";
public const ZOMBIE_HORSE_SPAWN_EGG = "minecraft:zombie_horse_spawn_egg";

View File

@ -800,7 +800,7 @@ abstract class Entity{
}
protected function broadcastMotion() : void{
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion(), tick: 0)]);
}
public function getGravity() : float{

View File

@ -68,6 +68,7 @@ use function atan2;
use function ceil;
use function count;
use function floor;
use function ksort;
use function lcg_value;
use function max;
use function min;
@ -76,6 +77,7 @@ use function mt_rand;
use function round;
use function sqrt;
use const M_PI;
use const SORT_NUMERIC;
abstract class Living extends Entity{
protected const DEFAULT_BREATH_TICKS = 300;
@ -555,26 +557,33 @@ abstract class Living extends Entity{
return;
}
$this->attackTime = $source->getAttackCooldown();
if($this->attackTime <= 0){
//this logic only applies if the entity was cold attacked
if($source instanceof EntityDamageByChildEntityEvent){
$e = $source->getChild();
if($e !== null){
$motion = $e->getMotion();
$this->knockBack($motion->x, $motion->z, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
$this->attackTime = $source->getAttackCooldown();
if($source instanceof EntityDamageByChildEntityEvent){
$e = $source->getChild();
if($e !== null){
$motion = $e->getMotion();
$this->knockBack($motion->x, $motion->z, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
}
}elseif($source instanceof EntityDamageByEntityEvent){
$e = $source->getDamager();
if($e !== null){
$deltaX = $this->location->x - $e->location->x;
$deltaZ = $this->location->z - $e->location->z;
$this->knockBack($deltaX, $deltaZ, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
}
}
}elseif($source instanceof EntityDamageByEntityEvent){
$e = $source->getDamager();
if($e !== null){
$deltaX = $this->location->x - $e->location->x;
$deltaZ = $this->location->z - $e->location->z;
$this->knockBack($deltaX, $deltaZ, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
if($this->isAlive()){
$this->doHitAnimation();
}
}
if($this->isAlive()){
$this->applyPostDamageEffects($source);
$this->doHitAnimation();
}
}
@ -876,8 +885,30 @@ abstract class Living extends Entity{
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
parent::syncNetworkData($properties);
$properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectManager->hasOnlyAmbientEffects() ? 1 : 0);
$properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->effectManager->getBubbleColor()->toARGB()));
$visibleEffects = [];
foreach ($this->effectManager->all() as $effect) {
if (!$effect->isVisible() || !$effect->getType()->hasBubbles()) {
continue;
}
$visibleEffects[EffectIdMap::getInstance()->toId($effect->getType())] = $effect->isAmbient();
}
//TODO: HACK! the client may not be able to identify effects if they are not sorted.
ksort($visibleEffects, SORT_NUMERIC);
$effectsData = 0;
$packedEffectsCount = 0;
foreach ($visibleEffects as $effectId => $isAmbient) {
$effectsData = ($effectsData << 7) |
(($effectId & 0x3f) << 1) | //Why not use 7 bits instead of only 6? mojang...
($isAmbient ? 1 : 0);
if (++$packedEffectsCount >= 8) {
break;
}
}
$properties->setLong(EntityMetadataProperties::VISIBLE_MOB_EFFECTS, $effectsData);
$properties->setShort(EntityMetadataProperties::AIR, $this->breathTicks);
$properties->setShort(EntityMetadataProperties::MAX_AIR, $this->maxBreathTicks);

View File

@ -0,0 +1,106 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\player;
use pocketmine\event\Event;
use pocketmine\player\PlayerInfo;
use pocketmine\resourcepacks\ResourcePack;
use function array_unshift;
/**
* Called after a player authenticates and is being offered resource packs to download.
*
* This event should be used to decide which resource packs to offer the player and whether to require the player to
* download the packs before they can join the server.
*/
class PlayerResourcePackOfferEvent extends Event{
/**
* @param ResourcePack[] $resourcePacks
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
*
* @phpstan-param list<ResourcePack> $resourcePacks
* @phpstan-param array<string, string> $encryptionKeys
*/
public function __construct(
private readonly PlayerInfo $playerInfo,
private array $resourcePacks,
private array $encryptionKeys,
private bool $mustAccept
){}
public function getPlayerInfo() : PlayerInfo{
return $this->playerInfo;
}
/**
* Adds a resource pack to the top of the stack.
* The resources in this pack will be applied over the top of any existing packs.
*/
public function addResourcePack(ResourcePack $entry, ?string $encryptionKey = null) : void{
array_unshift($this->resourcePacks, $entry);
if($encryptionKey !== null){
$this->encryptionKeys[$entry->getPackId()] = $encryptionKey;
}
}
/**
* Sets the resource packs to offer. Packs are applied from the highest key to the lowest, with each pack
* overwriting any resources from the previous pack. This means that the pack at index 0 gets the final say on which
* resources are used.
*
* @param ResourcePack[] $resourcePacks
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
*
* @phpstan-param list<ResourcePack> $resourcePacks
* @phpstan-param array<string, string> $encryptionKeys
*/
public function setResourcePacks(array $resourcePacks, array $encryptionKeys) : void{
$this->resourcePacks = $resourcePacks;
$this->encryptionKeys = $encryptionKeys;
}
/**
* @return ResourcePack[]
* @phpstan-return list<ResourcePack>
*/
public function getResourcePacks() : array{
return $this->resourcePacks;
}
/**
* @return string[]
* @phpstan-return array<string, string>
*/
public function getEncryptionKeys() : array{
return $this->encryptionKeys;
}
public function setMustAccept(bool $mustAccept) : void{
$this->mustAccept = $mustAccept;
}
public function mustAccept() : bool{
return $this->mustAccept;
}
}

View File

@ -146,6 +146,10 @@ class Armor extends Durable{
$new = $thisCopy->pop();
$player->getArmorInventory()->setItem($this->getArmorSlot(), $new);
$player->getInventory()->setItemInHand($existing);
$sound = $new->getMaterial()->getEquipSound();
if($sound !== null){
$player->broadcastSound($sound);
}
if(!$thisCopy->isNull()){
//if the stack size was bigger than 1 (usually won't happen, but might be caused by plugins)
$returnedItems[] = $thisCopy;

View File

@ -23,10 +23,13 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\world\sound\Sound;
class ArmorMaterial{
public function __construct(
private readonly int $enchantability
private readonly int $enchantability,
private readonly ?Sound $equipSound = null
){
}
@ -39,4 +42,11 @@ class ArmorMaterial{
public function getEnchantability() : int{
return $this->enchantability;
}
/**
* Returns the sound that plays when equipping the armor
*/
public function getEquipSound() : ?Sound{
return $this->equipSound;
}
}

View File

@ -25,5 +25,7 @@ namespace pocketmine\item;
class Bowl extends Item{
//TODO: check fuel
public function getFuelTime() : int{
return 200;
}
}

View File

@ -24,6 +24,13 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\utils\RegistryTrait;
use pocketmine\world\sound\ArmorEquipChainSound;
use pocketmine\world\sound\ArmorEquipDiamondSound;
use pocketmine\world\sound\ArmorEquipGenericSound;
use pocketmine\world\sound\ArmorEquipGoldSound;
use pocketmine\world\sound\ArmorEquipIronSound;
use pocketmine\world\sound\ArmorEquipLeatherSound;
use pocketmine\world\sound\ArmorEquipNetheriteSound;
/**
* This doc-block is generated automatically, do not modify it manually.
@ -62,12 +69,12 @@ final class VanillaArmorMaterials{
}
protected static function setup() : void{
self::register("leather", new ArmorMaterial(15));
self::register("chainmail", new ArmorMaterial(12));
self::register("iron", new ArmorMaterial(9));
self::register("turtle", new ArmorMaterial(9));
self::register("gold", new ArmorMaterial(25));
self::register("diamond", new ArmorMaterial(10));
self::register("netherite", new ArmorMaterial(15));
self::register("leather", new ArmorMaterial(15, new ArmorEquipLeatherSound()));
self::register("chainmail", new ArmorMaterial(12, new ArmorEquipChainSound()));
self::register("iron", new ArmorMaterial(9, new ArmorEquipIronSound()));
self::register("turtle", new ArmorMaterial(9, new ArmorEquipGenericSound()));
self::register("gold", new ArmorMaterial(25, new ArmorEquipGoldSound()));
self::register("diamond", new ArmorMaterial(10, new ArmorEquipDiamondSound()));
self::register("netherite", new ArmorMaterial(15, new ArmorEquipNetheriteSound()));
}
}

View File

@ -54,6 +54,7 @@ use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\Enchant;
use pocketmine\network\mcpe\protocol\types\EnchantOption as ProtocolEnchantOption;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
@ -96,6 +97,7 @@ class InventoryManager{
private array $complexSlotToInventoryMap = [];
private int $lastInventoryNetworkId = ContainerIds::FIRST;
private int $currentWindowType = WindowTypes::CONTAINER;
private int $clientSelectedHotbarSlot = -1;
@ -327,9 +329,15 @@ class InventoryManager{
foreach($this->containerOpenCallbacks as $callback){
$pks = $callback($windowId, $inventory);
if($pks !== null){
$windowType = null;
foreach($pks as $pk){
if($pk instanceof ContainerOpenPacket){
//workaround useless bullshit in 1.21 - ContainerClose requires a type now for some reason
$windowType = $pk->windowType;
}
$this->session->sendDataPacket($pk);
}
$this->currentWindowType = $windowType ?? WindowTypes::CONTAINER;
$this->syncContents($inventory);
return;
}
@ -378,10 +386,11 @@ class InventoryManager{
$this->openWindowDeferred(function() : void{
$windowId = $this->getNewWindowId();
$this->associateIdWithInventory($windowId, $this->player->getInventory());
$this->currentWindowType = WindowTypes::INVENTORY;
$this->session->sendDataPacket(ContainerOpenPacket::entityInv(
$windowId,
WindowTypes::INVENTORY,
$this->currentWindowType,
$this->player->getId()
));
});
@ -390,7 +399,7 @@ class InventoryManager{
public function onCurrentWindowRemove() : void{
if(isset($this->networkIdToInventoryMap[$this->lastInventoryNetworkId])){
$this->remove($this->lastInventoryNetworkId);
$this->session->sendDataPacket(ContainerClosePacket::create($this->lastInventoryNetworkId, true));
$this->session->sendDataPacket(ContainerClosePacket::create($this->lastInventoryNetworkId, $this->currentWindowType, true));
if($this->pendingCloseWindowId !== null){
throw new AssumptionFailedError("We should not have opened a new window while a window was waiting to be closed");
}
@ -411,7 +420,7 @@ class InventoryManager{
//Always send this, even if no window matches. If we told the client to close a window, it will behave as if it
//initiated the close and expect an ack.
$this->session->sendDataPacket(ContainerClosePacket::create($id, false));
$this->session->sendDataPacket(ContainerClosePacket::create($id, $this->currentWindowType, false));
if($this->pendingCloseWindowId === $id){
$this->pendingCloseWindowId = null;
@ -492,6 +501,8 @@ class InventoryManager{
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
$netSlot,
new FullContainerName($this->lastInventoryNetworkId),
new ItemStackWrapper(0, ItemStack::null()),
new ItemStackWrapper(0, ItemStack::null())
));
}
@ -499,6 +510,8 @@ class InventoryManager{
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
$netSlot,
new FullContainerName($this->lastInventoryNetworkId),
new ItemStackWrapper(0, ItemStack::null()),
$itemStackWrapper
));
}
@ -517,10 +530,12 @@ class InventoryManager{
*/
$this->session->sendDataPacket(InventoryContentPacket::create(
$windowId,
array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null()))
array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null())),
new FullContainerName($this->lastInventoryNetworkId),
new ItemStackWrapper(0, ItemStack::null())
));
//now send the real contents
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers));
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers, new FullContainerName($this->lastInventoryNetworkId), new ItemStackWrapper(0, ItemStack::null())));
}
public function syncSlot(Inventory $inventory, int $slot, ItemStack $itemStack) : void{

View File

@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\event\player\PlayerDuplicateLoginEvent;
use pocketmine\event\player\PlayerResourcePackOfferEvent;
use pocketmine\event\server\DataPacketDecodeEvent;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\event\server\DataPacketSendEvent;
@ -53,6 +54,7 @@ use pocketmine\network\mcpe\handler\SessionStartPacketHandler;
use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundCloseFormPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\DisconnectPacket;
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
@ -100,6 +102,8 @@ use pocketmine\player\Player;
use pocketmine\player\PlayerInfo;
use pocketmine\player\UsedChunkStatus;
use pocketmine\player\XboxLivePlayerInfo;
use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
@ -158,15 +162,24 @@ class NetworkSession{
/** @var string[] */
private array $sendBuffer = [];
/**
* @var \SplQueue|CompressBatchPromise[]|string[]
* @phpstan-var \SplQueue<CompressBatchPromise|string>
* @var PromiseResolver[]
* @phpstan-var list<PromiseResolver<true>>
*/
private array $sendBufferAckPromises = [];
/** @phpstan-var \SplQueue<array{CompressBatchPromise|string, list<PromiseResolver<true>>}> */
private \SplQueue $compressedQueue;
private bool $forceAsyncCompression = true;
private bool $enableCompression = false; //disabled until handshake completed
private int $nextAckReceiptId = 0;
/**
* @var PromiseResolver[][]
* @phpstan-var array<int, list<PromiseResolver<true>>>
*/
private array $ackPromisesByReceiptId = [];
private ?InventoryManager $invManager = null;
/**
@ -464,7 +477,23 @@ class NetworkSession{
}
}
public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{
public function handleAckReceipt(int $receiptId) : void{
if(!$this->connected){
return;
}
if(isset($this->ackPromisesByReceiptId[$receiptId])){
$promises = $this->ackPromisesByReceiptId[$receiptId];
unset($this->ackPromisesByReceiptId[$receiptId]);
foreach($promises as $promise){
$promise->resolve(true);
}
}
}
/**
* @phpstan-param PromiseResolver<true>|null $ackReceiptResolver
*/
private function sendDataPacketInternal(ClientboundPacket $packet, bool $immediate, ?PromiseResolver $ackReceiptResolver) : bool{
if(!$this->connected){
return false;
}
@ -487,6 +516,9 @@ class NetworkSession{
$packets = [$packet];
}
if($ackReceiptResolver !== null){
$this->sendBufferAckPromises[] = $ackReceiptResolver;
}
foreach($packets as $evPacket){
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder(), $evPacket));
}
@ -500,6 +532,23 @@ class NetworkSession{
}
}
public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{
return $this->sendDataPacketInternal($packet, $immediate, null);
}
/**
* @phpstan-return Promise<true>
*/
public function sendDataPacketWithReceipt(ClientboundPacket $packet, bool $immediate = false) : Promise{
$resolver = new PromiseResolver();
if(!$this->sendDataPacketInternal($packet, $immediate, $resolver)){
$resolver->reject();
}
return $resolver->getPromise();
}
/**
* @internal
*/
@ -541,7 +590,9 @@ class NetworkSession{
$batch = $stream->getBuffer();
}
$this->sendBuffer = [];
$this->queueCompressedNoBufferFlush($batch, $immediate);
$ackPromises = $this->sendBufferAckPromises;
$this->sendBufferAckPromises = [];
$this->queueCompressedNoBufferFlush($batch, $immediate, $ackPromises);
}finally{
Timings::$playerNetworkSend->stopTiming();
}
@ -568,22 +619,27 @@ class NetworkSession{
}
}
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false) : void{
/**
* @param PromiseResolver[] $ackPromises
*
* @phpstan-param list<PromiseResolver<true>> $ackPromises
*/
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false, array $ackPromises = []) : void{
Timings::$playerNetworkSend->startTiming();
try{
if(is_string($batch)){
if($immediate){
//Skips all queues
$this->sendEncoded($batch, true);
$this->sendEncoded($batch, true, $ackPromises);
}else{
$this->compressedQueue->enqueue($batch);
$this->compressedQueue->enqueue([$batch, $ackPromises]);
$this->flushCompressedQueue();
}
}elseif($immediate){
//Skips all queues
$this->sendEncoded($batch->getResult(), true);
$this->sendEncoded($batch->getResult(), true, $ackPromises);
}else{
$this->compressedQueue->enqueue($batch);
$this->compressedQueue->enqueue([$batch, $ackPromises]);
$batch->onResolve(function() : void{
if($this->connected){
$this->flushCompressedQueue();
@ -600,14 +656,14 @@ class NetworkSession{
try{
while(!$this->compressedQueue->isEmpty()){
/** @var CompressBatchPromise|string $current */
$current = $this->compressedQueue->bottom();
[$current, $ackPromises] = $this->compressedQueue->bottom();
if(is_string($current)){
$this->compressedQueue->dequeue();
$this->sendEncoded($current);
$this->sendEncoded($current, false, $ackPromises);
}elseif($current->hasResult()){
$this->compressedQueue->dequeue();
$this->sendEncoded($current->getResult());
$this->sendEncoded($current->getResult(), false, $ackPromises);
}else{
//can't send any more queued until this one is ready
@ -619,13 +675,24 @@ class NetworkSession{
}
}
private function sendEncoded(string $payload, bool $immediate = false) : void{
/**
* @param PromiseResolver[] $ackPromises
* @phpstan-param list<PromiseResolver<true>> $ackPromises
*/
private function sendEncoded(string $payload, bool $immediate, array $ackPromises) : void{
if($this->cipher !== null){
Timings::$playerNetworkSendEncrypt->startTiming();
$payload = $this->cipher->encrypt($payload);
Timings::$playerNetworkSendEncrypt->stopTiming();
}
$this->sender->send($payload, $immediate);
if(count($ackPromises) > 0){
$ackReceiptId = $this->nextAckReceiptId++;
$this->ackPromisesByReceiptId[$ackReceiptId] = $ackPromises;
}else{
$ackReceiptId = null;
}
$this->sender->send($payload, $immediate, $ackReceiptId);
}
/**
@ -645,6 +712,19 @@ class NetworkSession{
$this->setHandler(null);
$this->connected = false;
$ackPromisesByReceiptId = $this->ackPromisesByReceiptId;
$this->ackPromisesByReceiptId = [];
foreach($ackPromisesByReceiptId as $resolvers){
foreach($resolvers as $resolver){
$resolver->reject();
}
}
$sendBufferAckPromises = $this->sendBufferAckPromises;
$this->sendBufferAckPromises = [];
foreach($sendBufferAckPromises as $resolver){
$resolver->reject();
}
$this->logger->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_network_session_close($reason)));
}
}
@ -663,7 +743,7 @@ class NetworkSession{
}else{
$translated = $message;
}
$this->sendDataPacket(DisconnectPacket::create(0, $translated));
$this->sendDataPacket(DisconnectPacket::create(0, $translated, ""));
}
/**
@ -707,7 +787,7 @@ class NetworkSession{
public function transfer(string $ip, int $port, Translatable|string|null $reason = null) : void{
$reason ??= KnownTranslationFactory::pocketmine_disconnect_transfer();
$this->tryDisconnect(function() use ($ip, $port, $reason) : void{
$this->sendDataPacket(TransferPacket::create($ip, $port), true);
$this->sendDataPacket(TransferPacket::create($ip, $port, false), true);
if($this->player !== null){
$this->player->onPostDisconnect($reason, null);
}
@ -840,7 +920,19 @@ class NetworkSession{
$this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_SUCCESS));
$this->logger->debug("Initiating resource packs phase");
$this->setHandler(new ResourcePacksPacketHandler($this, $this->server->getResourcePackManager(), function() : void{
$packManager = $this->server->getResourcePackManager();
$resourcePacks = $packManager->getResourceStack();
$keys = [];
foreach($resourcePacks as $resourcePack){
$key = $packManager->getPackEncryptionKey($resourcePack->getPackId());
if($key !== null){
$keys[$resourcePack->getPackId()] = $key;
}
}
$event = new PlayerResourcePackOfferEvent($this->info, $resourcePacks, $keys, $packManager->resourcePacksRequired());
$event->call();
$this->setHandler(new ResourcePacksPacketHandler($this, $event->getResourcePacks(), $event->getEncryptionKeys(), $event->mustAccept(), function() : void{
$this->createPlayer();
}));
}
@ -1079,6 +1171,10 @@ class NetworkSession{
return $this->sendDataPacket(ModalFormRequestPacket::create($id, json_encode($form, JSON_THROW_ON_ERROR)));
}
public function onCloseAllForms() : void{
$this->sendDataPacket(ClientboundCloseFormPacket::create());
}
/**
* Instructs the networksession to start using the chunk at the given coordinates. This may occur asynchronously.
* @param \Closure $onCompletion To be called when chunk sending has completed.

View File

@ -28,7 +28,7 @@ interface PacketSender{
/**
* Pushes a packet into the channel to be processed.
*/
public function send(string $payload, bool $immediate) : void;
public function send(string $payload, bool $immediate, ?int $receiptId) : void;
/**
* Closes the channel, terminating the connection.

View File

@ -38,9 +38,10 @@ use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\SetActorDataPacket;
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute;
use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\network\mcpe\protocol\types\entity\UpdateAttribute;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use function array_map;
@ -66,7 +67,7 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
if(count($attributes) > 0){
$this->sendDataPacket($recipients, UpdateAttributesPacket::create(
$entity->getId(),
array_map(fn(Attribute $attr) => new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []), $attributes),
array_map(fn(Attribute $attr) => new UpdateAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getDefaultValue(), []), $attributes),
0
));
}
@ -87,12 +88,13 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
EffectIdMap::getInstance()->toId($effect->getType()),
$effect->getAmplifier(),
$effect->isVisible(),
$effect->getDuration()
$effect->getDuration(),
tick: 0
));
}
public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void{
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType())));
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType()), tick: 0));
}
public function onEntityRemoved(array $recipients, Entity $entity) : void{
@ -130,7 +132,8 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots()))
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots())),
new ItemStackWrapper(0, ItemStack::null())
));
}
@ -139,6 +142,13 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
}
public function onEmote(array $recipients, Human $from, string $emoteId) : void{
$this->sendDataPacket($recipients, EmotePacket::create($from->getId(), $emoteId, "", "", EmotePacket::FLAG_SERVER | EmotePacket::FLAG_MUTE_ANNOUNCEMENT));
$this->sendDataPacket($recipients, EmotePacket::create(
$from->getId(),
$emoteId,
0, //seems to be irrelevant for the client, we cannot risk rebroadcasting random values received
"",
"",
EmotePacket::FLAG_SERVER | EmotePacket::FLAG_MUTE_ANNOUNCEMENT
));
}
}

View File

@ -36,6 +36,7 @@ use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipeBlockName;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\PotionContainerChangeRecipe as ProtocolPotionContainerChangeRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\PotionTypeRecipe as ProtocolPotionTypeRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\RecipeUnlockingRequirement;
use pocketmine\network\mcpe\protocol\types\recipe\ShapedRecipe as ProtocolShapedRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\ShapelessRecipe as ProtocolShapelessRecipe;
use pocketmine\timings\Timings;
@ -79,6 +80,7 @@ final class CraftingDataCache{
$converter = TypeConverter::getInstance();
$recipesWithTypeIds = [];
$noUnlockingRequirement = new RecipeUnlockingRequirement(null);
foreach($manager->getCraftingRecipeIndex() as $index => $recipe){
if($recipe instanceof ShapelessRecipe){
$typeTag = match($recipe->getType()){
@ -95,6 +97,7 @@ final class CraftingDataCache{
$nullUUID,
$typeTag,
50,
$noUnlockingRequirement,
$index
);
}elseif($recipe instanceof ShapedRecipe){
@ -113,7 +116,9 @@ final class CraftingDataCache{
$nullUUID,
CraftingRecipeBlockName::CRAFTING_TABLE,
50,
$index
true,
$noUnlockingRequirement,
$index,
);
}else{
//TODO: probably special recipe types

View File

@ -38,7 +38,7 @@ final class ZlibCompressor implements Compressor{
public const DEFAULT_LEVEL = 7;
public const DEFAULT_THRESHOLD = 256;
public const DEFAULT_MAX_DECOMPRESSION_SIZE = 2 * 1024 * 1024;
public const DEFAULT_MAX_DECOMPRESSION_SIZE = 8 * 1024 * 1024;
/**
* @see SingletonTrait::make()

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\handler;
use pocketmine\block\BaseSign;
use pocketmine\block\ItemFrame;
use pocketmine\block\Lectern;
use pocketmine\block\tile\Sign;
use pocketmine\block\utils\SignText;
@ -60,7 +59,6 @@ use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\EmotePacket;
use pocketmine\network\mcpe\protocol\InteractPacket;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
use pocketmine\network\mcpe\protocol\LabTablePacket;
@ -808,15 +806,6 @@ class InGamePacketHandler extends PacketHandler{
return true;
}
public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{
$blockPosition = $packet->blockPosition;
$block = $this->player->getWorld()->getBlockAt($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ());
if($block instanceof ItemFrame && $block->getFramedItem() !== null){
return $this->player->attackBlock(new Vector3($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ()), $block->getFacing());
}
return false;
}
public function handleBossEvent(BossEventPacket $packet) : bool{
return false; //TODO
}
@ -998,11 +987,6 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleLecternUpdate(LecternUpdatePacket $packet) : bool{
if($packet->dropBook){
//Drop book is handled with an interact event on use item transaction
return true;
}
$pos = $packet->blockPosition;
$chunkX = $pos->getX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $pos->getZ() >> Chunk::COORD_BIT_SIZE;

View File

@ -112,10 +112,10 @@ class ItemStackRequestExecutor{
* @throws ItemStackRequestProcessException
*/
protected function getBuilderInventoryAndSlot(ItemStackRequestSlotInfo $info) : array{
[$windowId, $slotId] = ItemStackContainerIdTranslator::translate($info->getContainerId(), $this->inventoryManager->getCurrentWindowId(), $info->getSlotId());
[$windowId, $slotId] = ItemStackContainerIdTranslator::translate($info->getContainerName()->getContainerId(), $this->inventoryManager->getCurrentWindowId(), $info->getSlotId());
$windowAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slotId);
if($windowAndSlot === null){
throw new ItemStackRequestProcessException("No open inventory matches container UI ID: " . $info->getContainerId() . ", slot ID: " . $info->getSlotId());
throw new ItemStackRequestProcessException("No open inventory matches container UI ID: " . $info->getContainerName()->getContainerId() . ", slot ID: " . $info->getSlotId());
}
[$inventory, $slot] = $windowAndSlot;
if(!$inventory->slotExists($slot)){
@ -142,7 +142,7 @@ class ItemStackRequestExecutor{
* @throws ItemStackRequestProcessException
*/
protected function removeItemFromSlot(ItemStackRequestSlotInfo $slotInfo, int $count) : Item{
if($slotInfo->getContainerId() === ContainerUIIds::CREATED_OUTPUT && $slotInfo->getSlotId() === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){
if($slotInfo->getContainerName()->getContainerId() === ContainerUIIds::CREATED_OUTPUT && $slotInfo->getSlotId() === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){
//special case for the "created item" output slot
//TODO: do we need to send a response for this slot info?
return $this->takeCreatedItem($count);
@ -343,7 +343,7 @@ class ItemStackRequestExecutor{
$this->setNextCreatedItem($window->getOutput($optionId));
}
}else{
$this->beginCrafting($action->getRecipeId(), 1);
$this->beginCrafting($action->getRecipeId(), $action->getRepetitions());
}
}elseif($action instanceof CraftRecipeAutoStackRequestAction){
$this->beginCrafting($action->getRecipeId(), $action->getRepetitions());
@ -391,7 +391,7 @@ class ItemStackRequestExecutor{
public function buildItemStackResponse() : ItemStackResponse{
$builder = new ItemStackResponseBuilder($this->request->getRequestId(), $this->inventoryManager);
foreach($this->requestSlotInfos as $requestInfo){
$builder->addSlot($requestInfo->getContainerId(), $requestInfo->getSlotId());
$builder->addSlot($requestInfo->getContainerName()->getContainerId(), $requestInfo->getSlotId());
}
return $builder->build();

View File

@ -27,6 +27,7 @@ use pocketmine\inventory\Inventory;
use pocketmine\item\Durable;
use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds;
use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName;
use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponse;
use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponseContainerInfo;
use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponseSlotInfo;
@ -99,7 +100,7 @@ final class ItemStackResponseBuilder{
$responseContainerInfos = [];
foreach($responseInfosByContainer as $containerInterfaceId => $responseInfos){
$responseContainerInfos[] = new ItemStackResponseContainerInfo($containerInterfaceId, $responseInfos);
$responseContainerInfos[] = new ItemStackResponseContainerInfo(new FullContainerName($containerInterfaceId), $responseInfos);
}
return new ItemStackResponse(ItemStackResponse::RESULT_OK, $this->requestId, $responseContainerInfos);

View File

@ -39,7 +39,7 @@ use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\LevelSettings;
use pocketmine\network\mcpe\protocol\types\NetworkPermissions;
use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings;
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
use pocketmine\network\mcpe\protocol\types\ServerAuthMovementMode;
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
use pocketmine\player\Player;
use pocketmine\Server;
@ -98,7 +98,7 @@ class PreSpawnPacketHandler extends PacketHandler{
$this->server->getMotd(),
"",
false,
new PlayerMovementSettings(PlayerMovementType::SERVER_AUTHORITATIVE_V1, 0, false),
new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false),
0,
0,
"",

View File

@ -37,12 +37,13 @@ use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
use pocketmine\resourcepacks\ResourcePack;
use pocketmine\resourcepacks\ResourcePackManager;
use function array_keys;
use function array_map;
use function ceil;
use function count;
use function implode;
use function strpos;
use function strtolower;
use function substr;
/**
@ -50,37 +51,74 @@ use function substr;
* packs to the client.
*/
class ResourcePacksPacketHandler extends PacketHandler{
private const PACK_CHUNK_SIZE = 128 * 1024; //128KB
private const PACK_CHUNK_SIZE = 256 * 1024; //256KB
/**
* Larger values allow downloading more chunks at the same time, increasing download speed, but the client may choke
* and cause the download speed to drop (due to ACKs taking too long to arrive).
*/
private const MAX_CONCURRENT_CHUNK_REQUESTS = 1;
/**
* @var ResourcePack[]
* @phpstan-var array<string, ResourcePack>
*/
private array $resourcePacksById = [];
/** @var bool[][] uuid => [chunk index => hasSent] */
private array $downloadedChunks = [];
/** @phpstan-var \SplQueue<array{ResourcePack, int}> */
private \SplQueue $requestQueue;
private int $activeRequests = 0;
/**
* @phpstan-param \Closure() : void $completionCallback
* @param ResourcePack[] $resourcePackStack
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
*
* @phpstan-param list<ResourcePack> $resourcePackStack
* @phpstan-param array<string, string> $encryptionKeys
* @phpstan-param \Closure() : void $completionCallback
*/
public function __construct(
private NetworkSession $session,
private ResourcePackManager $resourcePackManager,
private array $resourcePackStack,
private array $encryptionKeys,
private bool $mustAccept,
private \Closure $completionCallback
){}
){
$this->requestQueue = new \SplQueue();
foreach($resourcePackStack as $pack){
$this->resourcePacksById[$pack->getPackId()] = $pack;
}
}
private function getPackById(string $id) : ?ResourcePack{
return $this->resourcePacksById[strtolower($id)] ?? null;
}
public function setUp() : void{
$resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{
//TODO: more stuff
$encryptionKey = $this->resourcePackManager->getPackEncryptionKey($pack->getPackId());
return new ResourcePackInfoEntry(
$pack->getPackId(),
$pack->getPackVersion(),
$pack->getPackSize(),
$encryptionKey ?? "",
$this->encryptionKeys[$pack->getPackId()] ?? "",
"",
$pack->getPackId(),
false
);
}, $this->resourcePackManager->getResourceStack());
}, $this->resourcePackStack);
//TODO: support forcing server packs
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false, []));
$this->session->sendDataPacket(ResourcePacksInfoPacket::create(
resourcePackEntries: $resourcePackEntries,
mustAccept: $this->mustAccept,
hasAddons: false,
hasScripts: false
));
$this->session->getLogger()->debug("Waiting for client to accept resource packs");
}
@ -104,11 +142,11 @@ class ResourcePacksPacketHandler extends PacketHandler{
if($splitPos !== false){
$uuid = substr($uuid, 0, $splitPos);
}
$pack = $this->resourcePackManager->getPackById($uuid);
$pack = $this->getPackById($uuid);
if(!($pack instanceof ResourcePack)){
//Client requested a resource pack but we don't have it available on the server
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
return false;
}
@ -128,7 +166,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
$stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{
return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks
}, $this->resourcePackManager->getResourceStack());
}, $this->resourcePackStack);
//we support chemistry blocks by default, the client should already have this installed
$stack[] = new ResourcePackStackEntry("0fba4063-dba1-4281-9b89-ff9390653530", "1.0.0", "");
@ -136,7 +174,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
//we don't force here, because it doesn't have user-facing effects
//but it does have an annoying side-effect when true: it makes
//the client remove its own non-server-supplied resource packs.
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, ProtocolInfo::MINECRAFT_VERSION_NETWORK, new Experiments([], false)));
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, ProtocolInfo::MINECRAFT_VERSION_NETWORK, new Experiments([], false), false));
$this->session->getLogger()->debug("Applying resource pack stack");
break;
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
@ -151,9 +189,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
}
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
$pack = $this->resourcePackManager->getPackById($packet->packId);
$pack = $this->getPackById($packet->packId);
if(!($pack instanceof ResourcePack)){
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
return false;
}
@ -176,8 +214,37 @@ class ResourcePacksPacketHandler extends PacketHandler{
$this->downloadedChunks[$packId][$packet->chunkIndex] = true;
}
$this->session->sendDataPacket(ResourcePackChunkDataPacket::create($packId, $packet->chunkIndex, $offset, $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE)));
$this->requestQueue->enqueue([$pack, $packet->chunkIndex]);
$this->processChunkRequestQueue();
return true;
}
private function processChunkRequestQueue() : void{
if($this->activeRequests >= self::MAX_CONCURRENT_CHUNK_REQUESTS || $this->requestQueue->isEmpty()){
return;
}
/**
* @var ResourcePack $pack
* @var int $chunkIndex
*/
[$pack, $chunkIndex] = $this->requestQueue->dequeue();
$packId = $pack->getPackId();
$offset = $chunkIndex * self::PACK_CHUNK_SIZE;
$chunkData = $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE);
$this->activeRequests++;
$this->session
->sendDataPacketWithReceipt(ResourcePackChunkDataPacket::create($packId, $chunkIndex, $offset, $chunkData))
->onCompletion(
function() : void{
$this->activeRequests--;
$this->processChunkRequestQueue();
},
function() : void{
//this may have been rejected because of a disconnection - this will do nothing in that case
$this->disconnectWithError("Plugin interrupted sending of resource packs");
}
);
}
}

View File

@ -252,7 +252,9 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
}
public function onPacketAck(int $sessionId, int $identifierACK) : void{
if(isset($this->sessions[$sessionId])){
$this->sessions[$sessionId]->handleAckReceipt($identifierACK);
}
}
public function setName(string $name) : void{
@ -289,12 +291,13 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->network->getBandwidthTracker()->add($bytesSentDiff, $bytesReceivedDiff);
}
public function putPacket(int $sessionId, string $payload, bool $immediate = true) : void{
public function putPacket(int $sessionId, string $payload, bool $immediate = true, ?int $receiptId = null) : void{
if(isset($this->sessions[$sessionId])){
$pk = new EncapsulatedPacket();
$pk->buffer = self::MCPE_RAKNET_PACKET_ID . $payload;
$pk->reliability = PacketReliability::RELIABLE_ORDERED;
$pk->orderChannel = 0;
$pk->identifierACK = $receiptId;
$this->interface->sendEncapsulated($sessionId, $pk, $immediate);
}

View File

@ -33,9 +33,9 @@ class RakLibPacketSender implements PacketSender{
private RakLibInterface $handler
){}
public function send(string $payload, bool $immediate) : void{
public function send(string $payload, bool $immediate, ?int $receiptId) : void{
if(!$this->closed){
$this->handler->putPacket($this->sessionId, $payload, $immediate);
$this->handler->putPacket($this->sessionId, $payload, $immediate, $receiptId);
}
}

View File

@ -96,7 +96,8 @@ class RakLibServer extends Thread{
new SimpleProtocolAcceptor($this->protocolVersion),
new UserToRakLibThreadMessageReceiver(new PthreadsChannelReader($this->mainToThreadBuffer)),
new RakLibToUserThreadMessageSender(new SnoozeAwarePthreadsChannelWriter($this->threadToMainBuffer, $this->sleeperEntry->createNotifier())),
new ExceptionTraceCleaner($this->mainPath)
new ExceptionTraceCleaner($this->mainPath),
recvMaxSplitParts: 512
);
$this->synchronized(function() : void{
$this->ready = true;

View File

@ -83,7 +83,7 @@ abstract class DefaultPermissions{
self::registerPermission(new Permission(Names::COMMAND_PLUGINS, l10n::pocketmine_permission_command_plugins()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SAVE_DISABLE, l10n::pocketmine_permission_command_save_disable()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SAVE_ENABLE, l10n::pocketmine_permission_command_save_enable()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SAVE_PERFORM, l10n::pocketmine_permission_command_save_enable()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SAVE_PERFORM, l10n::pocketmine_permission_command_save_perform()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SAY, l10n::pocketmine_permission_command_say()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SEED, l10n::pocketmine_permission_command_seed()), [$operatorRoot]);
self::registerPermission(new Permission(Names::COMMAND_SETWORLDSPAWN, l10n::pocketmine_permission_command_setworldspawn()), [$operatorRoot]);

View File

@ -1393,7 +1393,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
public function setMotion(Vector3 $motion) : bool{
if(parent::setMotion($motion)){
$this->broadcastMotion();
$this->getNetworkSession()->sendDataPacket(SetActorMotionPacket::create($this->id, $motion));
$this->getNetworkSession()->sendDataPacket(SetActorMotionPacket::create($this->id, $motion, tick: 0));
return true;
}
@ -2151,6 +2151,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
return true;
}
/**
* Closes the current viewing form and forms in queue.
*/
public function closeAllForms() : void{
$this->getNetworkSession()->onCloseAllForms();
}
/**
* Transfers a player to another server.
*

View File

@ -60,6 +60,8 @@ interface ResourcePack{
* @param int $start Offset to start reading the chunk from
* @param int $length Maximum length of data to return.
*
* @phpstan-param positive-int $length
*
* @return string byte-array
* @throws \InvalidArgumentException if the chunk does not exist
*/

View File

@ -154,6 +154,9 @@ class ZippedResourcePack implements ResourcePack{
}
public function getPackChunk(int $start, int $length) : string{
if($length < 1){
throw new \InvalidArgumentException("Pack length must be positive");
}
fseek($this->fileResource, $start);
if(feof($this->fileResource)){
throw new \InvalidArgumentException("Requested a resource pack chunk with invalid start offset");

View File

@ -25,6 +25,7 @@ namespace pocketmine\thread;
use pmmp\thread\Thread as NativeThread;
use pmmp\thread\ThreadSafeArray;
use pocketmine\crash\CrashDump;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\Server;
use function error_get_last;
@ -150,7 +151,7 @@ trait CommonThreadPartsTrait{
$this->synchronized(function() : void{
if($this->isTerminated() && $this->crashInfo === null){
$last = error_get_last();
if($last !== null){
if($last !== null && ($last["type"] & CrashDump::FATAL_ERROR_MASK) !== 0){
//fatal error
$crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName());
}else{

View File

@ -39,12 +39,12 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
private bool $useFormattingCodes = false;
private string $mainThreadName;
private string $timezone;
private MainLoggerThread $logWriterThread;
private ?MainLoggerThread $logWriterThread = null;
/**
* @throws \RuntimeException
*/
public function __construct(string $logFile, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){
public function __construct(?string $logFile, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false, ?string $logArchiveDir = null){
parent::__construct();
$this->logDebug = $logDebug;
@ -52,8 +52,10 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
$this->mainThreadName = $mainThreadName;
$this->timezone = $timezone->getName();
$this->logWriterThread = new MainLoggerThread($logFile);
$this->logWriterThread->start(NativeThread::INHERIT_NONE);
if($logFile !== null){
$this->logWriterThread = new MainLoggerThread($logFile, $logArchiveDir);
$this->logWriterThread->start(NativeThread::INHERIT_NONE);
}
}
/**
@ -166,10 +168,12 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
}
public function shutdownLogWriterThread() : void{
if(NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
$this->logWriterThread->shutdown();
}else{
throw new \LogicException("Only the creator thread can shutdown the logger thread");
if($this->logWriterThread !== null){
if(NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
$this->logWriterThread->shutdown();
}else{
throw new \LogicException("Only the creator thread can shutdown the logger thread");
}
}
}
@ -193,7 +197,9 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
$this->synchronized(function() use ($message, $level, $time) : void{
Terminal::writeLine($message);
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
if($this->logWriterThread !== null){
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
}
/**
* @var ThreadSafeLoggerAttachment $attachment
@ -205,11 +211,11 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
}
public function syncFlushBuffer() : void{
$this->logWriterThread->syncFlushBuffer();
$this->logWriterThread?->syncFlushBuffer();
}
public function __destruct(){
if(!$this->logWriterThread->isJoined() && NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
if($this->logWriterThread !== null && !$this->logWriterThread->isJoined() && NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
$this->shutdownLogWriterThread();
}
}

View File

@ -25,23 +25,42 @@ namespace pocketmine\utils;
use pmmp\thread\Thread;
use pmmp\thread\ThreadSafeArray;
use function clearstatcache;
use function date;
use function fclose;
use function file_exists;
use function fopen;
use function fstat;
use function fwrite;
use function is_dir;
use function is_file;
use function is_resource;
use function mkdir;
use function pathinfo;
use function rename;
use function strlen;
use function touch;
use const PATHINFO_EXTENSION;
use const PATHINFO_FILENAME;
final class MainLoggerThread extends Thread{
/** @phpstan-var ThreadSafeArray<int, string> */
private ThreadSafeArray $buffer;
private bool $syncFlush = false;
private bool $shutdown = false;
public function __construct(
private string $logFile
private string $logFile,
private ?string $archiveDir,
private readonly int $maxFileSize = 32 * 1024 * 1024 //32 MB
){
$this->buffer = new ThreadSafeArray();
touch($this->logFile);
if($this->archiveDir !== null && !@mkdir($this->archiveDir) && !is_dir($this->archiveDir)){
throw new \RuntimeException("Unable to create archive directory: " . (
is_file($this->archiveDir) ? "it already exists and is not a directory" : "permission denied"));
}
}
public function write(string $line) : void{
@ -71,12 +90,64 @@ final class MainLoggerThread extends Thread{
$this->join();
}
/** @return resource */
private function openLogFile(string $file, int &$size){
$logResource = fopen($file, "ab");
if(!is_resource($logResource)){
throw new \RuntimeException("Couldn't open log file");
}
$stat = fstat($logResource);
if($stat === false){
throw new AssumptionFailedError("fstat() should not fail here");
}
$size = $stat['size'];
return $logResource;
}
/**
* @param resource $logResource
* @return resource
*/
private function archiveLogFile($logResource, int &$size, string $archiveDir){
fclose($logResource);
clearstatcache();
$i = 0;
$date = date("Y-m-d\TH.i.s");
$baseName = pathinfo($this->logFile, PATHINFO_FILENAME);
$extension = pathinfo($this->logFile, PATHINFO_EXTENSION);
do{
//this shouldn't be necessary, but in case the user messes with the system time for some reason ...
$fileName = "$baseName.$date.$i.$extension";
$out = $this->archiveDir . "/" . $fileName;
$i++;
}while(file_exists($out));
//the user may have externally deleted the whole directory - make sure it exists before we do anything
@mkdir($archiveDir);
rename($this->logFile, $out);
$logResource = $this->openLogFile($this->logFile, $size);
fwrite($logResource, "--- Starting new log file - old log file archived as $fileName ---\n");
return $logResource;
}
private function logFileReadyToArchive(int $size) : bool{
return $size >= $this->maxFileSize;
}
/**
* @param resource $logResource
*/
private function writeLogStream($logResource) : void{
private function writeLogStream(&$logResource, int &$size, ?string $archiveDir) : void{
while(($chunk = $this->buffer->shift()) !== null){
fwrite($logResource, $chunk);
$size += strlen($chunk);
if($archiveDir !== null && $this->logFileReadyToArchive($size)){
$logResource = $this->archiveLogFile($logResource, $size, $archiveDir);
}
}
$this->synchronized(function() : void{
@ -88,13 +159,15 @@ final class MainLoggerThread extends Thread{
}
public function run() : void{
$logResource = fopen($this->logFile, "ab");
if(!is_resource($logResource)){
throw new \RuntimeException("Couldn't open log file");
$size = 0;
$logResource = $this->openLogFile($this->logFile, $size);
$archiveDir = $this->archiveDir;
if($archiveDir !== null && $this->logFileReadyToArchive($size)){
$logResource = $this->archiveLogFile($logResource, $size, $archiveDir);
}
while(!$this->shutdown){
$this->writeLogStream($logResource);
$this->writeLogStream($logResource, $size, $archiveDir);
$this->synchronized(function() : void{
if(!$this->shutdown && !$this->syncFlush){
$this->wait();
@ -102,7 +175,7 @@ final class MainLoggerThread extends Thread{
});
}
$this->writeLogStream($logResource);
$this->writeLogStream($logResource, $size, $archiveDir);
fclose($logResource);
}

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine\utils;
use pocketmine\thread\ThreadManager;
use pocketmine\thread\Thread;
use function count;
use function exec;
use function fclose;
@ -122,7 +122,7 @@ final class Process{
//TODO: more OS
return count(ThreadManager::getInstance()->getAll()) + 2; //MainLogger + Main Thread
return Thread::getRunningCount() + 1; //pmmpthread doesn't count the main thread
}
/**

View File

@ -114,6 +114,13 @@ trait RegistryTrait{
if(count($arguments) > 0){
throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed");
}
//fast path
if(self::$members !== null && isset(self::$members[$name])){
return self::preprocessMember(self::$members[$name]);
}
//fallback
try{
return self::_registryFromString($name);
}catch(\InvalidArgumentException $e){

View File

@ -31,7 +31,9 @@ use pocketmine\world\format\io\exception\CorruptedWorldException;
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
use pocketmine\world\format\PalettedBlockArray;
use pocketmine\world\WorldException;
use function count;
use function file_exists;
use function implode;
abstract class BaseWorldProvider implements WorldProvider{
protected WorldData $worldData;
@ -62,27 +64,35 @@ abstract class BaseWorldProvider implements WorldProvider{
*/
abstract protected function loadLevelData() : WorldData;
private function translatePalette(PalettedBlockArray $blockArray) : PalettedBlockArray{
private function translatePalette(PalettedBlockArray $blockArray, \Logger $logger) : PalettedBlockArray{
$palette = $blockArray->getPalette();
$newPalette = [];
$blockDecodeErrors = [];
foreach($palette as $k => $legacyIdMeta){
//TODO: remember data for unknown states so we can implement them later
$id = $legacyIdMeta >> 4;
$meta = $legacyIdMeta & 0xf;
try{
$newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
$newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($id, $meta);
}catch(BlockStateDeserializeException $e){
$blockDecodeErrors[] = "Palette offset $k / Failed to upgrade legacy ID/meta $id:$meta: " . $e->getMessage();
$newStateData = GlobalBlockStateHandlers::getUnknownBlockStateData();
}
try{
$newPalette[$k] = $this->blockStateDeserializer->deserialize($newStateData);
}catch(BlockStateDeserializeException){
//TODO: this needs to be logged
//TODO: maybe we can remember unknown states for later saving instead of discarding them and destroying maps...
}catch(BlockStateDeserializeException $e){
//this should never happen anyway - if the upgrader returned an invalid state, we have bigger problems
$blockDecodeErrors[] = "Palette offset $k / Failed to deserialize upgraded state $id:$meta: " . $e->getMessage();
$newPalette[$k] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
}
}
if(count($blockDecodeErrors) > 0){
$logger->error("Errors decoding/upgrading blocks:\n - " . implode("\n - ", $blockDecodeErrors));
}
//TODO: this is sub-optimal since it reallocates the offset table multiple times
return PalettedBlockArray::fromData(
$blockArray->getBitsPerBlock(),
@ -91,16 +101,16 @@ abstract class BaseWorldProvider implements WorldProvider{
);
}
protected function palettizeLegacySubChunkXZY(string $idArray, string $metaArray) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkXZY($idArray, $metaArray));
protected function palettizeLegacySubChunkXZY(string $idArray, string $metaArray, \Logger $logger) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkXZY($idArray, $metaArray), $logger);
}
protected function palettizeLegacySubChunkYZX(string $idArray, string $metaArray) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkYZX($idArray, $metaArray));
protected function palettizeLegacySubChunkYZX(string $idArray, string $metaArray, \Logger $logger) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkYZX($idArray, $metaArray), $logger);
}
protected function palettizeLegacySubChunkFromColumn(string $idArray, string $metaArray, int $yOffset) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkFromLegacyColumn($idArray, $metaArray, $yOffset));
protected function palettizeLegacySubChunkFromColumn(string $idArray, string $metaArray, int $yOffset, \Logger $logger) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkFromLegacyColumn($idArray, $metaArray, $yOffset), $logger);
}
public function getPath() : string{

View File

@ -51,12 +51,12 @@ use function time;
class BedrockWorldData extends BaseNbtWorldData{
public const CURRENT_STORAGE_VERSION = 10;
public const CURRENT_STORAGE_NETWORK_VERSION = 649;
public const CURRENT_STORAGE_NETWORK_VERSION = 729;
public const CURRENT_CLIENT_VERSION_TARGET = [
1, //major
20, //minor
60, //patch
4, //revision
21, //minor
30, //patch
3, //revision
0 //is beta
];

View File

@ -26,6 +26,7 @@ namespace pocketmine\world\format\io\leveldb;
use pocketmine\block\Block;
use pocketmine\data\bedrock\BiomeIds;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\convert\UnsupportedBlockStateException;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NbtDataException;
@ -58,6 +59,7 @@ use function count;
use function defined;
use function extension_loaded;
use function file_exists;
use function implode;
use function is_dir;
use function mkdir;
use function ord;
@ -184,6 +186,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$paletteSize = $stream->getLInt();
}
$blockDecodeErrors = [];
for($i = 0; $i < $paletteSize; ++$i){
try{
$offset = $stream->getOffset();
@ -199,18 +203,25 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
}catch(BlockStateDeserializeException $e){
//while not ideal, this is not a fatal error
$logger->error("Failed to upgrade blockstate: " . $e->getMessage() . " offset $i in palette, blockstate NBT: " . $blockStateNbt->toString());
$blockDecodeErrors[] = "Palette offset $i / Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
continue;
}
try{
$palette[] = $this->blockStateDeserializer->deserialize($blockStateData);
}catch(UnsupportedBlockStateException $e){
$blockDecodeErrors[] = "Palette offset $i / " . $e->getMessage();
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
}catch(BlockStateDeserializeException $e){
$logger->error("Failed to deserialize blockstate: " . $e->getMessage() . " offset $i in palette, blockstate NBT: " . $blockStateNbt->toString());
$blockDecodeErrors[] = "Palette offset $i / Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
}
}
if(count($blockDecodeErrors) > 0){
$logger->error("Errors decoding blocks:\n - " . implode("\n - ", $blockDecodeErrors));
}
//TODO: exceptions
return PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
}
@ -443,7 +454,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$subChunks = [];
for($yy = 0; $yy < 8; ++$yy){
$storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy)];
$storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"))];
if(isset($convertedLegacyExtraData[$yy])){
$storages[] = $convertedLegacyExtraData[$yy];
}
@ -482,7 +493,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
}
$storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData)];
$storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData, $logger)];
if($convertedLegacyExtraData !== null){
$storages[] = $convertedLegacyExtraData;
}

View File

@ -31,10 +31,11 @@ use pocketmine\world\format\SubChunk;
class Anvil extends RegionWorldProvider{
use LegacyAnvilChunkTrait;
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk{
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkYZX(
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
self::readFixedSizeByteArray($subChunk, "Data", 2048)
self::readFixedSizeByteArray($subChunk, "Data", 2048),
$logger
)], $biomes3d);
//ignore legacy light information
}

View File

@ -54,7 +54,7 @@ trait LegacyAnvilChunkTrait{
/**
* @throws CorruptedChunkException
*/
protected function deserializeChunk(string $data) : ?LoadedChunkData{
protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData{
$decompressed = @zlib_decode($data);
if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT");
@ -90,7 +90,8 @@ trait LegacyAnvilChunkTrait{
$subChunksTag = $chunk->getListTag("Sections") ?? [];
foreach($subChunksTag as $subChunk){
if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->getByte("Y")] = $this->deserializeSubChunk($subChunk, clone $biomes3d);
$y = $subChunk->getByte("Y");
$subChunks[$y] = $this->deserializeSubChunk($subChunk, clone $biomes3d, new \PrefixedLogger($logger, "Subchunk y=$y"));
}
}
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
@ -111,6 +112,6 @@ trait LegacyAnvilChunkTrait{
);
}
abstract protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk;
abstract protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk;
}

View File

@ -46,7 +46,7 @@ class McRegion extends RegionWorldProvider{
/**
* @throws CorruptedChunkException
*/
protected function deserializeChunk(string $data) : ?LoadedChunkData{
protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData{
$decompressed = @zlib_decode($data);
if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT");
@ -90,7 +90,12 @@ class McRegion extends RegionWorldProvider{
$fullData = self::readFixedSizeByteArray($chunk, "Data", 16384);
for($y = 0; $y < 8; ++$y){
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $y)], clone $biomes3d);
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn(
$fullIds,
$fullData,
$y,
new \PrefixedLogger($logger, "Subchunk y=$y"),
)], clone $biomes3d);
}
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
if(!isset($subChunks[$y])){

View File

@ -35,10 +35,11 @@ use pocketmine\world\format\SubChunk;
class PMAnvil extends RegionWorldProvider{
use LegacyAnvilChunkTrait;
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk{
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkXZY(
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
self::readFixedSizeByteArray($subChunk, "Data", 2048)
self::readFixedSizeByteArray($subChunk, "Data", 2048),
$logger
)], $biomes3d);
}

View File

@ -27,6 +27,7 @@ use function range;
class RegionLocationTableEntry{
private int $firstSector;
/** @phpstan-var positive-int */
private int $sectorCount;
private int $timestamp;
@ -61,6 +62,9 @@ class RegionLocationTableEntry{
return range($this->getFirstSector(), $this->getLastSector());
}
/**
* @phpstan-return positive-int
*/
public function getSectorCount() : int{
return $this->sectorCount;
}

View File

@ -147,7 +147,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
/**
* @throws CorruptedChunkException
*/
abstract protected function deserializeChunk(string $data) : ?LoadedChunkData;
abstract protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData;
/**
* @return CompoundTag[]
@ -200,7 +200,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
$chunkData = $this->loadRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f);
if($chunkData !== null){
return $this->deserializeChunk($chunkData);
return $this->deserializeChunk($chunkData, new \PrefixedLogger($this->logger, "Loading chunk x=$chunkX z=$chunkZ"));
}
return null;

View File

@ -0,0 +1,35 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\sound;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
class ArmorEquipChainSound implements Sound{
public function encode(Vector3 $pos) : array{
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_CHAIN, $pos, false)];
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\sound;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
class ArmorEquipDiamondSound implements Sound{
public function encode(Vector3 $pos) : array{
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_DIAMOND, $pos, false)];
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\sound;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
class ArmorEquipGenericSound implements Sound{
public function encode(Vector3 $pos) : array{
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_GENERIC, $pos, false)];
}
}

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