Compare commits

..

185 Commits

Author SHA1 Message Date
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
23f4632409 Release 5.12.1 2024-03-13 13:48:56 +00:00
264ce06cbf Updated composer dependencies 2024-03-12 16:51:12 +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
cbffbd23f9 Bump docker/build-push-action from 5.1.0 to 5.2.0 (#6281)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.1.0...v5.2.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-12 09:30:29 +00:00
9d7aec5891 Bump phpstan/phpstan from 1.10.59 to 1.10.60 (#6282)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.59 to 1.10.60.
- [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.59...1.10.60)

---
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-12 09:30:03 +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
b0390a39fd Update composer dependencies 2024-03-04 15:17:48 +00:00
5cb69e00d0 NetworkSession: remove hardcoded limit
this is already covered by the dynamic Game Packets rate limit, which is much more effective at dealing with this anyway.
2024-03-04 14:36:40 +00:00
781e3643dd Clean up 2024-03-04 14:25:47 +00:00
2ca50ecd36 Bump shivammathur/setup-php from 2.29.0 to 2.30.0 (#6273)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.29.0 to 2.30.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.29.0...2.30.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 11:54:53 +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
72f3c0b4b9 NetworkSession: fixed timings not being stopped when handling uncompressed packets 2024-03-01 17:36:40 +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
b2c97cf2f1 5.12.1 is next 2024-02-28 18:14:41 +00:00
a35c3406a8 Release 5.12.0 2024-02-28 18:14:37 +00:00
54ea404d80 Merge branch 'minor-next' into stable 2024-02-28 17:39:25 +00:00
98042f844f Merge remote-tracking branch 'origin/stable' into minor-next 2024-02-28 17:36:21 +00:00
a0cca53f52 Fixed mismatched predictions due to NBT key order differences
this is a pain :(
It appears the client always sorts the keys in alphabetical order due to use of std::map. However I'm not sure of the exact ordering behaviour, so it needs to be investigated.
2024-02-27 16:07:43 +00:00
6872118355 Update BedrockProtocol to release version 2024-02-27 14:15:31 +00:00
efd113bdc8 Integrate pmmp/BedrockProtocol@65b3d0b341 2024-02-26 17:09:09 +00:00
34a5f91aa9 5.11.3 is next 2024-02-26 14:45:48 +00:00
aee3656415 Release 5.11.2 2024-02-26 14:45:47 +00:00
c58e599eb2 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2024-02-26 14:35:40 +00:00
47f0119660 InGamePacketHandler: added an extra check 2024-02-26 14:35:21 +00:00
561ffd3da3 Bump pmmp/setup-php-action from 3.0.0 to 3.1.0 (#6267)
Bumps [pmmp/setup-php-action](https://github.com/pmmp/setup-php-action) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/pmmp/setup-php-action/releases)
- [Commits](https://github.com/pmmp/setup-php-action/compare/3.0.0...3.1.0)

---
updated-dependencies:
- dependency-name: pmmp/setup-php-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-02-26 14:27:56 +00:00
b744e09352 5.11.2 is next 2024-02-23 14:19:02 +00:00
7b89dda420 Release 5.11.1 2024-02-23 14:19:01 +00:00
db665fefdb Harden JsonMapper defaults in general 2024-02-23 14:10:24 +00:00
6872661fd0 Harden JsonMapper on login JSON handling 2024-02-23 14:10:02 +00:00
920341668f Implemented working Name tag (#5209) 2024-02-19 18:46:48 +00:00
4fab518384 PluginManager: do not accept generator functions as event handlers
closes #4912

I didn't merge the original PR because this needs to be checked for explicitly registered handlers as well as auto-detected ones from listeners.
2024-02-19 16:53:53 +00:00
e06b042cd0 Bump pmmp/setup-php-action from 2.0.0 to 3.0.0 (#6259)
Bumps [pmmp/setup-php-action](https://github.com/pmmp/setup-php-action) from 2.0.0 to 3.0.0.
- [Release notes](https://github.com/pmmp/setup-php-action/releases)
- [Commits](https://github.com/pmmp/setup-php-action/compare/2.0.0...3.0.0)

---
updated-dependencies:
- dependency-name: pmmp/setup-php-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-19 16:16:28 +00:00
44ce9ca610 Bump phpstan/phpstan from 1.10.57 to 1.10.58 (#6260)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.57 to 1.10.58.
- [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.57...1.10.58)

---
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-02-19 16:16:16 +00:00
2616d8c5ad New biome IDs, courtesy of build/generate-biome-ids 2024-02-19 16:10:46 +00:00
61d0181bfd Added script to generate biome IDs
this has been sitting in my local workspace for a very long time
2024-02-19 16:10:22 +00:00
db894e3a4a Fixed Utils::cloneObjectArray() template signature (#6255) 2024-02-16 16:52:10 +00:00
53cbc44d70 Update PULL_REQUEST_TEMPLATE.md
[ci skip]
2024-02-15 14:51:34 +00:00
be102dc95f Bump ncipollo/release-action from 1.13.0 to 1.14.0 (#6250)
Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/ncipollo/release-action/releases)
- [Commits](https://github.com/ncipollo/release-action/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: ncipollo/release-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-02-12 11:51:31 +00:00
d211392b67 Merge remote-tracking branch 'origin/stable' into minor-next 2024-02-12 11:46:48 +00:00
eaab1a8784 ChunkSerializer: fixed count calculation for non-overworld chunks
plugins that implement dimensions can't change the number of subchunks used by Chunk, they can only choose to use a subset of them.
2024-02-07 18:58:41 +00:00
169d3e0de8 5.11.1 is next 2024-02-07 13:01:30 +00:00
ce8fecc6ec Release 5.11.0 2024-02-07 13:01:30 +00:00
4fcb644c51 Added missing imports 2024-02-07 12:51:39 +00:00
826cbea0bc Use newer php-cs-fixer on Actions 2024-02-07 12:51:27 +00:00
fe06bfcda0 Import alias cleanup courtesy of newest php-cs-fixer 2024-02-07 12:50:37 +00:00
f54ed8362d tools/convert-world: add a namespace
not sure why this was left in the global namespace to its own devices
2024-02-07 12:49:05 +00:00
8c7a4d720a Fixed inconsistent global namespace reference 2024-02-07 12:46:11 +00:00
6492e7f4a2 1.20.60 support 2024-02-07 12:33:44 +00:00
6bb84bc46c Add Promise::all (#6152) 2024-02-06 12:42:24 +00:00
20837c9894 Merge remote-tracking branch 'origin/stable' into minor-next 2024-02-05 12:43:39 +00:00
f207d1bbf2 Make CocoaBlock Flowable (#6218) 2024-02-05 12:36:09 +00:00
5709d727a2 Bump phpstan/phpstan from 1.10.55 to 1.10.57 (#6235)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.55 to 1.10.57.
- [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.55...1.10.57)

---
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-02-05 12:30:19 +00:00
234199d241 Bump actions/cache from 3 to 4 (#6229)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 12:29:49 +00:00
e28d20a68e Bump shivammathur/setup-php from 2.28.0 to 2.29.0 (#6228)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.28.0 to 2.29.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.28.0...2.29.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 12:29:34 +00:00
81d5b9ba06 tools/generate-bedrock-data-from-packets: add more exception detail for unexpected block runtimeIDs 2024-01-22 18:49:06 +00:00
d97c8e2fd2 tools/generate-item-upgrade-schema: filter old IDs that were already renamed by previous schemas
this caused weird outputs if an item was renamed multiple times.
2024-01-22 18:48:32 +00:00
c7c20d4d79 tools/generate-block-palette-spec: fixed sorting 2024-01-09 16:43:11 +00:00
c6a09e5ed8 Merge branch 'stable' into minor-next 2024-01-09 16:17:55 +00:00
e77cd39316 ItemBlock: add a workaround for air items with a stack size bigger than 0
In the future we should look into making empty slots be represented by null or a different, special item type, instead of breaking the air block for this purpose.

closes #6185
closes #6016
2024-01-09 15:55:41 +00:00
a459e3c1a9 Block: improve some documentation 2024-01-09 13:35:36 +00:00
288bd4018b Block: deprecate isSolid()
As discussed many years ago in #2551, no one actually knows what this property actually means. It definitely isn't the conventionally expected definition of 'solid' found in the real world, as signs are solid but flower pots are not.
2024-01-09 13:35:10 +00:00
9b03b082ab Added --version option 2024-01-09 13:04:14 +00:00
db3bb55a2b Change PHP_DEBUG constant usage to ZEND_DEBUG_BUILD
In PHP 8.4, the type of `PHP_DEBUG` changes from `int` to `bool`.
See [PHP.Watch: PHP 8.4: `PHP_ZTS` and `PHP_DEBUG` constant value type changed from `int` to `bool`](https://php.watch/versions/8.4/PHP_ZTS-PHP_DEBUG-const-type-change).

This changes the constants to `ZEND_DEBUG_BUILD`, which contains the same value but as a `bool` across all PHP versions.

closes #6222
2024-01-09 12:30:24 +00:00
8372c9efc2 Merge branch 'stable' into minor-next 2024-01-09 12:27:25 +00:00
4db38ee452 Updated PHPStan 2024-01-08 16:20:42 +00:00
ee977c8001 Updated composer dependencies 2024-01-08 16:18:13 +00:00
5b5c73f660 Matrixify jobs that run on all PHP versions
this allows us to specify PHP versions in just one place instead of 4,
and also makes the display of jobs in the UI nicer.
2024-01-08 16:13:09 +00:00
f83280ece6 Bump tests/plugins/DevTools from 411fd5b to c6dca35 (#6216)
Bumps [tests/plugins/DevTools](https://github.com/pmmp/DevTools) from `411fd5b` to `c6dca35`.
- [Release notes](https://github.com/pmmp/DevTools/releases)
- [Commits](411fd5bdc0...c6dca357c7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 16:11:18 +00:00
19556634e3 Bump build/php from 73e5950 to 6f619bf (#6220)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `73e5950` to `6f619bf`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](73e5950eb9...6f619bf7a0)

---
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-01-08 16:08:54 +00:00
5718a1a20e Reduce frequency of annoying Dependabot updates
phpstan is releasing sometimes 4-5 times a week at this point, generating lots of noise.
2024-01-08 11:38:29 +00:00
5386e86079 ProcessLoginTask: remove old root key (#6211) 2024-01-03 12:50:05 +00:00
1b0ef468f3 CommonThreadPartsTrait: remove outdated documentation
This is now automatically called by the final run(), and the user now only needs to implement onRun(), so they have no business calling this function.
2023-12-21 13:09:05 +00:00
b69843a8bd CommonThreadPartsTrait: add common implementation of quit()
there's no need for the worker specialization here (isShutdown and shutdown are aliased to isJoined and join respectively), and the unstacking is not really desirable either as we previously learned with AsyncPool.
2023-12-21 12:56:51 +00:00
03619ebca9 Thread/Worker: Remove duplicated code
Despite the comments, there doesn't seem to be an obvious reason for these to be copy-pasted. Perhaps there was some legacy reason for this with legacy pthreads.
In fact, it looks likely that quit() will probably be able to be traitified too.
2023-12-21 12:44:03 +00:00
fd1bc1b845 AsyncWorker: deprecate ThreadStore methods
these are inconvenient and don't make any sense. It's far easier and more static-analysis-friendly to just use static properties.
2023-12-21 12:39:55 +00:00
c05116849a AsyncWorker: clean up nonsensical sleeper notifier handling code 2023-12-21 12:39:12 +00:00
7a55a6e6b6 ServerKiller: harden against spurious wakeups
If awakened by spurious wakeup, the thread would immediately exit without doing anything, rendering it useless.
Not sure how it took so long for this to be found...
2023-12-20 17:22:11 +00:00
bf99917f2a ThreadSafeClassLoader: add native property types 2023-12-20 17:01:20 +00:00
57f3a04bc5 data: Use statically analyzable ways of ensuring all cases are registered
PHPStan will verify that these matches cover all cases, which guarantees that all cases will be covered.
In addition, if PHPStan is not used, the constructors will immediately bail out when they hit a case that isn't covered.
The only downside is the extra indentation :(
2023-12-20 16:07:05 +00:00
c51b1b2812 Create LightableTrait and remove repetitive code (#6111) 2023-12-20 15:21:11 +00:00
80125f9b19 Modernize single-use phpstan-template tags 2023-12-20 15:20:28 +00:00
8dc28b7ea8 RuntimeDataDescriber: remove useless template parameter 2023-12-20 15:15:43 +00:00
58ce746ae1 Remove dead PHPStan ignored error 2023-12-20 14:44:24 +00:00
74cb0be868 Noise: give PHPStan some help understanding SplFixedArray 2023-12-20 14:43:36 +00:00
4d9b97d2bb Merge branch 'stable' into minor-next 2023-12-20 14:30:38 +00:00
90af8cfd69 Update composer dependencies 2023-12-20 14:29:02 +00:00
c8da9dea95 WorldManager: Remove unused if in unloadWorld() (#6203) 2023-12-20 10:38:00 +00:00
e1f4fd3048 ProcessLoginTask: remove dead comments
This is no longer an issue since b2df405cc0.
2023-12-15 16:01:43 +00:00
d3d7f24015 Noise: make calls with many parameters less nauseating to read 2023-12-15 15:32:54 +00:00
944dd7d3e4 BaseBanner: remove unnecessary array_filter() usage 2023-12-15 15:19:44 +00:00
a03013d582 Bump phpstan/phpstan from 1.10.47 to 1.10.50 (#6198)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.47 to 1.10.50.
- [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.47...1.10.50)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-15 14:49:32 +00:00
053abfbb6f Bump actions/upload-artifact from 3 to 4 (#6199)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-15 12:23:49 +00:00
00a8ea267c 5.10.1 is next 2023-12-14 16:54:42 +00:00
daeba95101 Release 5.10.0 2023-12-14 16:54:38 +00:00
a750af72db Updated build/php submodule to pmmp/PHP-Binaries@73e5950eb9 2023-12-14 16:05:38 +00:00
61decaa2f8 Merge branch 'minor-next' into stable 2023-12-14 16:04:51 +00:00
06b2e61d3c Merge remote-tracking branch 'origin/stable' into minor-next 2023-12-14 14:02:15 +00:00
b4838f5b4e 5.9.1 is next 2023-12-06 15:02:57 +00:00
b2df405cc0 NetworkSession: Send less information to clients on error disconnects
in particular, the information from VerifyLoginTask shouldn't be sent to clients, as it could contain sensitive information.
This change only affects disconnection screens. The server log shows the same amount of information as before (though formatted differently in some cases).
2023-11-29 16:31:59 +00:00
ace
d596dc571d Fix pitcher pod wrongly registered as a block (#6162) 2023-11-27 14:46:20 +00:00
a1748a92ca Avoid unnecessary TypeConverter::getInstance() calls
we already have it available within these contexts
2023-11-17 14:00:25 +00:00
fbcf4649eb Avoid unnecessary Entity::getWorld() calls in loops 2023-11-17 13:58:41 +00:00
0f620fad94 ChunkCache: inline unnecessary function 2023-11-17 13:30:44 +00:00
bc07778434 Avoid unnecessary CompressBatchPromise allocations for sync-prepared batches
Sync-prepared batches account for the vast majority of outbound packets. Avoiding these useless objects further reduces the overhead of zero-compressed packets, as the creation of these objects is a significant part of the overhead for these cases.

closes #6157
2023-11-17 12:35:42 +00:00
519784460f Merge branch 'stable' into minor-next 2023-11-17 12:04:10 +00:00
e9c5846a06 World: simplify condition 2023-11-16 10:06:43 +00:00
69f197dbec PluginBase: fixed erroneous replacement 2023-11-14 13:04:14 +00:00
13f34a500c PluginBase: clean up inconsistent getter vs property access usages 2023-11-14 12:59:38 +00:00
e5c96faa4b Server: clean up inconsistent getter vs property access usages 2023-11-14 12:59:05 +00:00
dd98e4aaed block: clean up unnecessary getter usages
with the assistance of a custom phpstan rule
this inconsistent mess has been bothering me for a long time
2023-11-14 12:47:33 +00:00
e525699dd4 TimeTrackingSleeperHandler: record time spent in each Snooze handler 2023-11-13 11:35:39 +00:00
923c922960 Merge branch 'stable' into minor-next 2023-11-13 11:13:12 +00:00
58ea94bab8 ... 2023-11-10 15:41:17 +00:00
22b10e4cb0 Timings: Stop using BREAKDOWN group
with tree timings, the breakdown is actually pretty annoying, since it makes it hard to find a timer in the aggregate lists.
2023-11-10 15:36:35 +00:00
7a4cf8ef68 Prepare for PHP 8.2 as primary version 2023-11-09 19:04:53 +00:00
269b3d89a2 Update build/php submodule to pmmp/PHP-Binaries@39885cf248 2023-11-09 19:03:12 +00:00
b3766834c6 Merge branch 'stable' into minor-next 2023-11-09 19:02:08 +00:00
50592dc269 Merge branch 'stable' into minor-next 2023-11-09 18:05:38 +00:00
4103631bc1 Added Smithing Template items (#6132) 2023-11-09 14:25:49 +00:00
d09af2e30d World: don't assume that random Vector3 are int vectors
we can safely assume this for blocks (though the type info doesn't reflect it) but this is not safe to assume for random APIs that might be used by plugins.
2023-11-06 17:15:17 +00:00
bbe66e8e09 Block: Improve performance of encodeFullState()
if there's no state data to encode, we can avoid useless calls and object allocations.
For the best cases (blocks which don't use state data at all) this improves the performance of getStateId() by more than 10x.
Blocks which use one or the other benefit by a smaller but still significant margin.
2023-11-06 17:04:39 +00:00
457660235e Crops must have access to a light level of at least 9 2023-11-06 16:02:57 +00:00
d3b7861d1a Constify bootstrap options 2023-11-02 16:15:57 +00:00
a6b36d6c3c CropGrowthHelper: avoid unnecessary checks 2023-11-02 15:32:22 +00:00
109673382d Implemented modifiers for crop growth speed
closes #6070

there are some unresolved questions about the growth speed of beetroots, pitcher plants and torchflower crops, but that's a topic for another commit.
this change also doesn't account for the light levels.
2023-11-02 15:16:11 +00:00
187 changed files with 4811 additions and 1845 deletions

View File

@ -34,7 +34,10 @@ Requires translations:
## Tests
<!--
Details should be provided of tests done. Simply saying "tested" or equivalent is not acceptable.
Attach scripts or actions to test this pull request, as well as the result
PRs which have not been tested MUST be marked as draft.
-->
I tested this PR by doing the following (tick all that apply):
- [ ] Writing PHPUnit tests (commit these in the `tests/phpunit` folder)
- [ ] Playtesting using a Minecraft client (provide screenshots or a video)
- [ ] Writing a test plugin (provide the code and sample output)
- [ ] Other (provide details)

View File

@ -3,7 +3,7 @@ updates:
- package-ecosystem: composer
directory: "/"
schedule:
interval: daily
interval: weekly
time: "10:00"
open-pull-requests-limit: 10
ignore:
@ -21,4 +21,4 @@ updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
interval: weekly

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.1.0
uses: docker/build-push-action@v5.3.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.1.0
uses: docker/build-push-action@v5.3.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.1.0
uses: docker/build-push-action@v5.3.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.1.0
uses: docker/build-push-action@v5.3.0
with:
push: true
context: ./pocketmine-mp

View File

@ -13,12 +13,12 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.28.0
uses: shivammathur/setup-php@2.30.2
with:
php-version: 8.1
php-version: 8.2
- name: Restore Composer package cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files

View File

@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: [8.1]
php-version: [8.2]
steps:
- uses: actions/checkout@v4
@ -20,12 +20,12 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.28.0
uses: shivammathur/setup-php@2.30.2
with:
php-version: ${{ matrix.php-version }}
- name: Restore Composer package cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files
@ -77,7 +77,7 @@ jobs:
> build_info.json
- name: Upload release artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: release_artifacts
path: |
@ -86,7 +86,7 @@ jobs:
${{ github.workspace }}/build_info.json
- name: Create draft release
uses: ncipollo/release-action@v1.13.0
uses: ncipollo/release-action@v1.14.0
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}

156
.github/workflows/main-php-matrix.yml vendored Normal file
View File

@ -0,0 +1,156 @@
name: CI (all supported PHP versions)
on:
workflow_call:
inputs:
php:
description: 'PHP version in X.Y format'
required: true
type: string
#these are parameterized to ease updating
pm-version-major:
description: 'PocketMine-MP major version'
default: 5
type: number
image:
description: 'Runner image to use'
default: 'ubuntu-20.04'
type: string
jobs:
phpstan:
name: PHPStan analysis
runs-on: ${{ inputs.image }}
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
pm-version-major: ${{ inputs.pm-version-major }}
- name: Restore Composer package cache
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction
- name: Run PHPStan
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
phpunit:
name: PHPUnit tests
runs-on: ${{ inputs.image }}
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
pm-version-major: ${{ inputs.pm-version-major }}
- name: Restore Composer package cache
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction
- name: Run PHPUnit tests
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
integration:
name: Integration tests
runs-on: ${{ inputs.image }}
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
pm-version-major: ${{ inputs.pm-version-major }}
- name: Restore Composer package cache
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction
- name: Run integration tests
run: ./tests/travis.sh -t4
codegen:
name: Generated Code consistency checks
runs-on: ${{ inputs.image }}
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
pm-version-major: ${{ inputs.pm-version-major }}
- name: Restore Composer package cache
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction
- name: Update generated code
run: composer update-codegen
- name: Verify code is unchanged
run: |
git diff
git diff --quiet

View File

@ -6,162 +6,17 @@ on:
workflow_dispatch:
jobs:
phpstan:
name: PHPStan analysis
runs-on: ${{ matrix.image }}
all-php-versions:
name: PHP ${{ matrix.php }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: ["8.1", "8.2", "8.3"]
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Restore Composer package cache
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction
- name: Run PHPStan
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
phpunit:
name: PHPUnit tests
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: ["8.1", "8.2", "8.3"]
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Restore Composer package cache
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction
- name: Run PHPUnit tests
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
integration:
name: Integration tests
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: ["8.1", "8.2", "8.3"]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Restore Composer package cache
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction
- name: Run integration tests
run: ./tests/travis.sh -t4
codegen:
name: Generated Code consistency checks
runs-on: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: ["8.1", "8.2", "8.3"]
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@2.0.0
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Restore Composer package cache
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- 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: Verify code is unchanged
run: |
git diff
git diff --quiet
uses: ./.github/workflows/main-php-matrix.yml
with:
php: ${{ matrix.php }}
secrets: inherit
codestyle:
name: Code Style checks
@ -173,10 +28,10 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.28.0
uses: shivammathur/setup-php@2.30.2
with:
php-version: 8.1
tools: php-cs-fixer:3.17
php-version: 8.2
tools: php-cs-fixer:3.49
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -2,7 +2,7 @@
## Pre-requisites
- A bash shell (git bash is sufficient for Windows)
- [`git`](https://git-scm.com) available in your shell
- PHP 8.1 or newer available in your shell
- PHP 8.2 or newer available in your shell
- [`composer`](https://getcomposer.org) available in your shell
## Custom PHP binaries

View File

@ -0,0 +1,133 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\build\generate_biome_ids;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use function asort;
use function dirname;
use function fclose;
use function fopen;
use function fwrite;
use function is_array;
use function is_int;
use function is_string;
use function json_decode;
use function str_replace;
use function strtoupper;
use const SORT_NUMERIC;
require dirname(__DIR__) . '/vendor/autoload.php';
const HEADER = <<<'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);
HEADER;
/** @return resource */
function safe_fopen(string $file, string $flags){
$result = fopen($file, $flags);
if($result === false){
throw new \RuntimeException("Failed to open file");
}
return $result;
}
function make_const_name(string $name) : string{
return strtoupper(str_replace(['.', 'minecraft:'], ['_', ''], $name));
}
/**
* @param int[] $map
* @phpstan-param array<string, int> $map
*/
function generate(array $map, string $outputFile) : void{
$file = safe_fopen($outputFile, 'wb');
fwrite($file, HEADER);
fwrite($file, <<<'CLASSHEADER'
namespace pocketmine\data\bedrock;
final class BiomeIds{
private function __construct(){
//NOOP
}
CLASSHEADER
);
$list = $map;
asort($list, SORT_NUMERIC);
$lastId = -1;
foreach(Utils::stringifyKeys($list) as $name => $id){
if($name === ""){
continue;
}
if($id !== $lastId + 1){
fwrite($file, "\n");
}
$lastId = $id;
fwrite($file, "\tpublic const " . make_const_name($name) . ' = ' . $id . ';' . "\n");
}
fwrite($file, "}\n");
fclose($file);
}
$ids = json_decode(Filesystem::fileGetContents(BedrockDataFiles::BIOME_ID_MAP_JSON), true);
if(!is_array($ids)){
throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object");
}
$cleanedIds = [];
foreach($ids as $name => $id){
if(!is_string($name) || !is_int($id)){
throw new \RuntimeException("Invalid biome ID map, expected string => int map");
}
$cleanedIds[$name] = $id;
}
generate($cleanedIds, dirname(__DIR__) . '/src/data/bedrock/BiomeIds.php');
echo "Done. Don't forget to run CS fixup after generating code.\n";

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

@ -21,6 +21,9 @@
declare(strict_types=1);
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\VersionInfo;
require dirname(__DIR__) . '/vendor/autoload.php';
if(count($argv) !== 7){
@ -30,12 +33,12 @@ if(count($argv) !== 7){
echo json_encode([
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION), //deprecated
"base_version" => \pocketmine\VersionInfo::BASE_VERSION,
"base_version" => VersionInfo::BASE_VERSION,
"build" => (int) $argv[4],
"is_dev" => \pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD,
"channel" => \pocketmine\VersionInfo::BUILD_CHANNEL,
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
"channel" => VersionInfo::BUILD_CHANNEL,
"git_commit" => $argv[1],
"mcpe_version" => \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"date" => time(), //TODO: maybe we should embed this in VersionInfo?
"details_url" => "https://github.com/$argv[3]/releases/tag/$argv[2]",
"download_url" => "https://github.com/$argv[3]/releases/download/$argv[2]/PocketMine-MP.phar",

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;
@ -32,6 +34,7 @@ use function getcwd;
use function getopt;
use function implode;
use function ini_get;
use function is_string;
use function microtime;
use function preg_quote;
use function realpath;
@ -147,8 +150,17 @@ function main() : void{
}else{
$build = 0;
}
if(isset($opts["out"])){
if(!is_string($opts["out"])){
echo "--out cannot be specified multiple times" . PHP_EOL;
exit(1);
}
$pharPath = $opts["out"];
}else{
$pharPath = getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar";
}
foreach(buildPhar(
$opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar",
$pharPath,
dirname(__DIR__) . DIRECTORY_SEPARATOR,
[
'resources',
@ -159,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){

46
changelogs/5.10.md Normal file
View File

@ -0,0 +1,46 @@
# 5.10.0
Released 14th December 2023.
**For Minecraft: Bedrock Edition 1.20.50**
This is a minor feature release, including new gameplay features and minor performance improvements.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- PHP 8.2 is now used by default. PHP 8.1 is still supported, but will be removed in a future 5.x release.
- Improved timings reports by removing `Breakdown` timings group. This group serves no purpose with tree timings and made for confusing reading.
## Performance
- Improved performance of `Block::encodeFullState()` in most conditions. This in turn improves performance of `World::setBlock()` and `World::setBlockAt()`.
- Improved network compression performance by avoiding unnecessary object allocations.
- Timings now report time spent in individual `Snooze` handlers, making it easier to debug performance issues.
## Gameplay
### Blocks
- Implemented crop growth speed modifiers.
- The following things now positively affect crop growth speed:
- Being planted on or being adjacent to farmland (hydrated farmland offers a larger benefit than dry farmland)
- Potential light level of at least 9
- Being planted in rows with space between them (or a different type of crop)
- The following things now negatively affect crop growth speed:
- Improper arrangement (e.g. the same crop on all sides)
- Insufficient light level (below 9)
- Poorly arranged crops will grow slower in this version. Past versions behaved as if crops were always planted in ideal conditions.
- Crops planted in ideal conditions will grow at the same speed as before.
### Items
- Added the following new items:
- All types of Smithing Template
- Pitcher Pod is now correctly registered. In previous versions, it was mapped to the Pitcher Crop block, causing incorrect name display in commands.
## Internals
- Cleaned up various getter usages where direct property access is possible.
- Avoided unnecessary repeated getter calls in some loops.
- `NetworkSession` may now track `string` instead of `CompressBatchPromise` when a batch was synchronously compressed. This significantly reduces object allocations and improves performance.
- `NetworkSession` now sends less information to clients on login validation failure. This avoids leaking potentially sensitive error information to clients.
- Clients can correlate their disconnects with server-side logs using the `Error ID` shown on the disconnect screen.

45
changelogs/5.11.md Normal file
View File

@ -0,0 +1,45 @@
# 5.11.0
Released 7th February 2024.
**For Minecraft: Bedrock Edition 1.20.60**
This is a support release for Minecraft: Bedrock Edition 1.20.60.
**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.60.
- Removed support for earlier versions.
## Fixes
- Fixed `tools/generate-item-upgrade-schema.php` not correctly handling items whose IDs were changed multiple times.
- Fixed `ServerKiller` not working correctly in some cases (incorrectly handled wake-up conditions).
- `ItemBlock`s of `Air` blocks are now always considered as "null" items regardless of count, and don't occupy inventory slots.
## Internals
- Restructured GitHub Actions CI workflows to make them easier to maintain (no need to update PHP versions in multiple places anymore).
- GitHub Actions CodeStyle workflow now uses php-cs-fixer 3.49.x.
- Dependabot updates are now processed weekly instead of daily.
# 5.11.1
Released 23rd February 2024.
## Fixes
- Fixed subchunk count calculation in `ChunkSerializer` for non-overworld dimension (useful for dimension plugins).
- Harden options used for processing JSON data, particularly on the network, to close security issues.
## Documentation
- Fixed PHPStan signature for `Utils::cloneObjectArray()`.
## Internals
- Updated GitHub Actions versions to get rid of deprecation warnings.
# 5.11.2
Released 26th February 2024.
## Fixes
- Added extra checks for `BookEditPacket` handling.

63
changelogs/5.12.md Normal file
View File

@ -0,0 +1,63 @@
# 5.12.0
Released 28th February 2024
**For Minecraft: Bedrock Edition 1.20.60**
This is a minor feature release, with a few new features and 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 a `--version` command-line option to display the server version and exit.
## Tools
- Added `tools/generate-biome-ids.php` to generate `pocketmine\data\bedrock\BiomeIds`.
- Fixed ordering of property values generated by `tools/generate-block-palette-spec.php`.
## API
### `pocketmine\block`
- The following new classes have been added:
- `utils\LightableTrait` - used by blocks with `getLit()` and `setLit()` methods
- The following methods have been deprecated:
- `Block->isSolid()` - this method returns confusing results which don't match expectations and no one really knows what it actually means
- `CocoaBlock` now extends `Flowable` to match vanilla Minecraft behaviour.
### `pocketmine\plugin`
- `PluginManager->registerEvent()` now throws an exception when given a generator function for the event handler.
- `PluginManager->registerEvents()` now throws an exception if any of the detected event handlers are generator functions. Use `@notHandler` to have the function ignored if intended.
### `pocketmine\promise`
- The following methods have been added:
- `public static Promise::all(list<Promise> $promises) : Promise` - returns a promise that is resolved once all given promises are resolved, or is rejected if any of the promises are rejected.
### `pocketmine\scheduler`
- The following methods have been deprecated:
- `AsyncWorker->getFromThreadStore()` - use class static properties for thread-local storage
- `AsyncWorker->removeFromThreadStore()`
- `AsyncWorker->saveToThreadStore()`
## Documentation
- Improved documentation of various methods in `Block`.
## Gameplay
- The following new items have been added:
- Name Tag
## Internals
- Removed specialization of shutdown logic for `Thread` vs `Worker` (no specialization is required).
- Authentication system no longer accepts logins signed with the old Mojang root public key.
- ID to enum mappings in `pocketmine\data` now use a new `match` convention to allow static analysis to ensure that all enum cases are handled.
- Updated version of `pocketmine/bedrock-protocol` allows avoiding decoding of some itemstack data from the client in most cases, improving performance.
# 5.12.1
Released 13th March 2024.
## Fixes
- Fixed `Player Network Receive - Decompression` timings not being stopped correctly when receiving an uncompressed packet.
## Internals
- Removed hardcoded batch packet size limit. This was already covered by other limits anyway.

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.

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": "*",
@ -32,27 +32,27 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
"pocketmine/bedrock-block-upgrade-schema": "~3.4.0+bedrock-1.20.50",
"pocketmine/bedrock-data": "~2.7.0+bedrock-1.20.50",
"pocketmine/bedrock-item-upgrade-schema": "~1.6.0+bedrock-1.20.50",
"pocketmine/bedrock-protocol": "~26.0.0+bedrock-1.20.50",
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
"pocketmine/bedrock-block-upgrade-schema": "~4.1.0+bedrock-1.20.80",
"pocketmine/bedrock-data": "~2.10.0+bedrock-1.20.80",
"pocketmine/bedrock-item-upgrade-schema": "~1.9.0+bedrock-1.20.80",
"pocketmine/bedrock-protocol": "~30.0.0+bedrock-1.20.80",
"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.3.0"
"symfony/filesystem": "~6.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.47",
"phpstan/phpstan": "1.10.66",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
@ -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"
]
}
}

377
composer.lock generated

File diff suppressed because it is too large Load Diff

52
src/BootstrapOptions.php Normal file
View File

@ -0,0 +1,52 @@
<?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;
/**
* Constants for all the command-line options that PocketMine-MP supports.
* Other options not listed here can be used to override server.properties and pocketmine.yml values temporarily.
*
* @internal
*/
final class BootstrapOptions{
private function __construct(){
//NOOP
}
/** Disables the setup wizard on first startup */
public const NO_WIZARD = "no-wizard";
/** Force-disables console text colour and formatting */
public const DISABLE_ANSI = "disable-ansi";
/** Force-enables console text colour and formatting */
public const ENABLE_ANSI = "enable-ansi";
/** Path to look in for plugins */
public const PLUGINS = "plugins";
/** Path to store and load server data */
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

@ -25,6 +25,7 @@ namespace pocketmine {
use Composer\InstalledVersions;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\thread\ThreadManager;
use pocketmine\thread\ThreadSafeClassLoader;
use pocketmine\utils\Filesystem;
@ -40,14 +41,17 @@ namespace pocketmine {
use function extension_loaded;
use function function_exists;
use function getcwd;
use function getopt;
use function is_dir;
use function mkdir;
use function phpversion;
use function preg_match;
use function preg_quote;
use function printf;
use function realpath;
use function version_compare;
use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
require_once __DIR__ . '/VersionInfo.php';
@ -120,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.";
}
}
@ -166,7 +170,7 @@ namespace pocketmine {
* @return void
*/
function emit_performance_warnings(\Logger $logger){
if(PHP_DEBUG !== 0){
if(ZEND_DEBUG_BUILD){
$logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
}
if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){
@ -273,9 +277,14 @@ JIT_WARNING
ErrorToExceptionHandler::set();
if(count(getopt("", [BootstrapOptions::VERSION])) > 0){
printf("%s %s (git hash %s) for Minecraft: Bedrock Edition %s\n", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true), VersionInfo::GIT_HASH(), ProtocolInfo::MINECRAFT_VERSION);
exit(0);
}
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
$dataPath = getopt_string("data") ?? $cwd;
$pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
$dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd;
$pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
if(!@mkdir($dataPath, 0777, true) && !is_dir($dataPath)){
@ -308,23 +317,28 @@ JIT_WARNING
//Logger has a dependency on timezone
Timezone::init();
$opts = getopt("", ["no-wizard", "enable-ansi", "disable-ansi"]);
if(isset($opts["enable-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["disable-ansi"])){
}elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){
Terminal::init(false);
}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);
$exitCode = 0;
do{
if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts["no-wizard"])){
if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts[BootstrapOptions::NO_WIZARD])){
$installer = new SetupWizard($dataPath);
if(!$installer->run()){
$exitCode = -1;

View File

@ -59,7 +59,7 @@ use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\PacketBroadcaster;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
use pocketmine\network\mcpe\raklib\RakLibInterface;
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
use pocketmine\network\mcpe\StandardPacketBroadcaster;
@ -125,6 +125,7 @@ use Symfony\Component\Filesystem\Path;
use function array_fill;
use function array_sum;
use function base64_encode;
use function chr;
use function cli_set_process_title;
use function copy;
use function count;
@ -523,7 +524,7 @@ class Server{
return $this->playerDataProvider->loadData($name);
}catch(PlayerDataLoadException $e){
$this->logger->debug("Failed to load player data for $name: " . $e->getMessage());
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
return null;
}
});
@ -542,7 +543,7 @@ class Server{
try{
$this->playerDataProvider->saveData($name, $ev->getSaveData());
}catch(PlayerDataSaveException $e){
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
$this->logger->critical($this->language->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
$this->logger->logException($e);
}
});
@ -737,7 +738,7 @@ class Server{
* @return string[][]
*/
public function getCommandAliases() : array{
$section = $this->configGroup->getProperty(YmlServerProperties::ALIASES);
$section = $this->configGroup->getProperty(Yml::ALIASES);
$result = [];
if(is_array($section)){
foreach($section as $key => $value){
@ -854,14 +855,14 @@ class Server{
}
}
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::language_selected($this->getLanguage()->getName(), $this->getLanguage()->getLang())));
$this->logger->info($this->language->translate(KnownTranslationFactory::language_selected($this->language->getName(), $this->language->getLang())));
if(VersionInfo::IS_DEVELOPMENT_BUILD){
if(!$this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_DEV_BUILDS, false)){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error1(VersionInfo::NAME)));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error2()));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error3()));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4(YmlServerProperties::SETTINGS_ENABLE_DEV_BUILDS)));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4(Yml::SETTINGS_ENABLE_DEV_BUILDS)));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error5("https://github.com/pmmp/PocketMine-MP/releases")));
$this->forceShutdownExit();
@ -877,7 +878,7 @@ class Server{
$this->memoryManager = new MemoryManager($this);
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_start(TextFormat::AQUA . $this->getVersion() . TextFormat::RESET)));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_start(TextFormat::AQUA . $this->getVersion() . TextFormat::RESET)));
if(($poolSize = $this->configGroup->getPropertyString(Yml::SETTINGS_ASYNC_WORKERS, "auto")) === "auto"){
$poolSize = 2;
@ -937,11 +938,11 @@ class Server{
$this->onlineMode = $this->configGroup->getConfigBool(ServerProperties::XBOX_AUTH, true);
if($this->onlineMode){
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_auth_enabled()));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_auth_enabled()));
}else{
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_auth_disabled()));
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authWarning()));
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_auth_disabled()));
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_authWarning()));
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
}
if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
@ -952,17 +953,17 @@ class Server{
$this->serverID = Utils::getMachineUniqueId($this->getIp() . $this->getPort());
$this->getLogger()->debug("Server unique id: " . $this->getServerUniqueId());
$this->getLogger()->debug("Machine unique id: " . Utils::getMachineUniqueId());
$this->logger->debug("Server unique id: " . $this->getServerUniqueId());
$this->logger->debug("Machine unique id: " . Utils::getMachineUniqueId());
$this->network = new Network($this->logger);
$this->network->setName($this->getMotd());
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_info(
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_info(
$this->getName(),
(VersionInfo::IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET
)));
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
@ -973,7 +974,7 @@ class Server{
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes"));
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
$this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger);
$pluginGraylist = null;
$graylistFile = Path::join($this->dataPath, "plugin_list.yml");
@ -987,7 +988,7 @@ class Server{
$this->forceShutdownExit();
return;
}
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist);
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->dataPath, "plugin_data"), $pluginGraylist);
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
$this->pluginManager->registerInterface(new ScriptPluginLoader());
@ -1049,9 +1050,9 @@ class Server{
$this->configGroup->save();
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_defaultGameMode($this->getGamemode()->getTranslatableName())));
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET)));
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3)))));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_defaultGameMode($this->getGamemode()->getTranslatableName())));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET)));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3)))));
$forwarder = new BroadcastLoggerForwarder($this, $this->logger, $this->language);
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder);
@ -1139,13 +1140,13 @@ class Server{
if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
if(trim($default) == ""){
$this->getLogger()->warning("level-name cannot be null, using default");
$this->logger->warning("level-name cannot be null, using default");
$default = "world";
$this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
}
if(!$this->worldManager->loadWorld($default, true)){
if($this->worldManager->isWorldGenerated($default)){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
return false;
}
@ -1154,7 +1155,7 @@ class Server{
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
if($generatorClass === null){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
return false;
}
$creationOptions = WorldCreationOptions::create()
@ -1185,12 +1186,11 @@ class Server{
bool $useQuery,
PacketBroadcaster $packetBroadcaster,
EntityEventBroadcaster $entityEventBroadcaster,
PacketSerializerContext $packetSerializerContext,
TypeConverter $typeConverter
) : bool{
$prettyIp = $ipV6 ? "[$ip]" : $ip;
try{
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter));
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $typeConverter));
}catch(NetworkInterfaceStartException $e){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
$ip,
@ -1200,7 +1200,7 @@ class Server{
return false;
}
if($rakLibRegistered){
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
}
if($useQuery){
if(!$rakLibRegistered){
@ -1208,7 +1208,7 @@ class Server{
//if it's not registered we need to make sure Query still works
$this->network->registerInterface(new DedicatedQueryNetworkInterface($ip, $port, $ipV6, new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
}
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_query_running($prettyIp, (string) $port)));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_query_running($prettyIp, (string) $port)));
}
return true;
}
@ -1217,15 +1217,14 @@ class Server{
$useQuery = $this->configGroup->getConfigBool(ServerProperties::ENABLE_QUERY, true);
$typeConverter = TypeConverter::getInstance();
$packetSerializerContext = new PacketSerializerContext($typeConverter->getItemTypeDictionary());
$packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext);
$packetBroadcaster = new StandardPacketBroadcaster($this);
$entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster, $typeConverter);
if(
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) ||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter) ||
(
$this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter)
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter)
)
){
return false;
@ -1368,25 +1367,31 @@ class Server{
*
* @param bool|null $sync Compression on the main thread (true) or workers (false). Default is automatic (null).
*/
public function prepareBatch(string $buffer, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise{
public function prepareBatch(string $buffer, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise|string{
$timings ??= Timings::$playerNetworkSendCompress;
try{
$timings->startTiming();
if($sync === null){
$threshold = $compressor->getCompressionThreshold();
$sync = !$this->networkCompressionAsync || $threshold === null || strlen($buffer) < $threshold;
}
$threshold = $compressor->getCompressionThreshold();
if($threshold === null || strlen($buffer) < $compressor->getCompressionThreshold()){
$compressionType = CompressionAlgorithm::NONE;
$compressed = $buffer;
$promise = new CompressBatchPromise();
if(!$sync && strlen($buffer) >= $this->networkCompressionAsyncThreshold){
$task = new CompressBatchTask($buffer, $promise, $compressor);
$this->asyncPool->submitTask($task);
}else{
$promise->resolve($compressor->compress($buffer));
$sync ??= !$this->networkCompressionAsync;
if(!$sync && strlen($buffer) >= $this->networkCompressionAsyncThreshold){
$promise = new CompressBatchPromise();
$task = new CompressBatchTask($buffer, $promise, $compressor);
$this->asyncPool->submitTask($task);
return $promise;
}
$compressionType = $compressor->getNetworkId();
$compressed = $compressor->compress($buffer);
}
return $promise;
return chr($compressionType) . $compressed;
}finally{
$timings->stopTiming();
}
@ -1463,43 +1468,43 @@ class Server{
$this->shutdown();
if(isset($this->pluginManager)){
$this->getLogger()->debug("Disabling all plugins");
$this->logger->debug("Disabling all plugins");
$this->pluginManager->disablePlugins();
}
if(isset($this->network)){
$this->network->getSessionManager()->close($this->configGroup->getPropertyString(YmlServerProperties::SETTINGS_SHUTDOWN_MESSAGE, "Server closed"));
$this->network->getSessionManager()->close($this->configGroup->getPropertyString(Yml::SETTINGS_SHUTDOWN_MESSAGE, "Server closed"));
}
if(isset($this->worldManager)){
$this->getLogger()->debug("Unloading all worlds");
$this->logger->debug("Unloading all worlds");
foreach($this->worldManager->getWorlds() as $world){
$this->worldManager->unloadWorld($world, true);
}
}
$this->getLogger()->debug("Removing event handlers");
$this->logger->debug("Removing event handlers");
HandlerListManager::global()->unregisterAll();
if(isset($this->asyncPool)){
$this->getLogger()->debug("Shutting down async task worker pool");
$this->logger->debug("Shutting down async task worker pool");
$this->asyncPool->shutdown();
}
if(isset($this->configGroup)){
$this->getLogger()->debug("Saving properties");
$this->logger->debug("Saving properties");
$this->configGroup->save();
}
if($this->console !== null){
$this->getLogger()->debug("Closing console");
$this->logger->debug("Closing console");
$this->console->quit();
}
if(isset($this->network)){
$this->getLogger()->debug("Stopping network interfaces");
$this->logger->debug("Stopping network interfaces");
foreach($this->network->getInterfaces() as $interface){
$this->getLogger()->debug("Stopping network interface " . get_class($interface));
$this->logger->debug("Stopping network interface " . get_class($interface));
$this->network->unregisterInterface($interface);
}
}
@ -1567,7 +1572,7 @@ class Server{
}
private function writeCrashDumpFile(CrashDump $dump) : string{
$crashFolder = Path::join($this->getDataPath(), "crashdumps");
$crashFolder = Path::join($this->dataPath, "crashdumps");
if(!is_dir($crashFolder)){
mkdir($crashFolder);
}
@ -1598,17 +1603,17 @@ class Server{
ini_set("error_reporting", '0');
ini_set("memory_limit", '-1'); //Fix error dump not dumped on memory problems
try{
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_create()));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_crash_create()));
$dump = new CrashDump($this, $this->pluginManager ?? null);
$crashDumpPath = $this->writeCrashDumpFile($dump);
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath)));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath)));
if($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_ENABLED, true)){
$report = true;
$stamp = Path::join($this->getDataPath(), "crashdumps", ".last_crash");
$stamp = Path::join($this->dataPath, "crashdumps", ".last_crash");
$crashInterval = 120; //2 minutes
if(($lastReportTime = @filemtime($stamp)) !== false && $lastReportTime + $crashInterval >= time()){
$report = false;
@ -1639,7 +1644,7 @@ class Server{
if(isset($data->crashId) && is_int($data->crashId) && isset($data->crashUrl) && is_string($data->crashUrl)){
$reportId = $data->crashId;
$reportUrl = $data->crashUrl;
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
}elseif(isset($data->error) && is_string($data->error)){
$this->logger->emergency("Automatic crash report submission failed: $data->error");
}else{
@ -1653,7 +1658,7 @@ class Server{
}catch(\Throwable $e){
$this->logger->logException($e);
try{
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_error($e->getMessage())));
$this->logger->critical($this->language->translate(KnownTranslationFactory::pocketmine_crash_error($e->getMessage())));
}catch(\Throwable $e){}
}
@ -1771,7 +1776,7 @@ class Server{
echo "\x1b]0;" . $this->getName() . " " .
$this->getPocketMineVersion() .
" | Online $online/" . $this->getMaxPlayers() .
" | Online $online/" . $this->maxPlayers .
($connecting > 0 ? " (+$connecting connecting)" : "") .
" | Memory " . $usage .
" | U " . round($bandwidthStats->getSend()->getAverageBytes() / 1024, 2) .
@ -1836,10 +1841,10 @@ class Server{
}
if(($this->tickCounter % self::TICKS_PER_TPS_OVERLOAD_WARNING) === 0 && $this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
}
$this->getMemoryManager()->check();
$this->memoryManager->check();
if($this->console !== null){
Timings::$serverCommand->startTiming();

View File

@ -24,7 +24,9 @@ declare(strict_types=1);
namespace pocketmine;
use pocketmine\snooze\SleeperHandler;
use pocketmine\snooze\SleeperHandlerEntry;
use pocketmine\timings\TimingsHandler;
use pocketmine\utils\Utils;
use function hrtime;
/**
@ -35,12 +37,29 @@ final class TimeTrackingSleeperHandler extends SleeperHandler{
private int $notificationProcessingTimeNs = 0;
/**
* @var TimingsHandler[]
* @phpstan-var array<string, TimingsHandler>
*/
private static array $handlerTimings = [];
public function __construct(
private TimingsHandler $timings
){
parent::__construct();
}
public function addNotifier(\Closure $handler) : SleeperHandlerEntry{
$name = Utils::getNiceClosureName($handler);
$timings = self::$handlerTimings[$name] ??= new TimingsHandler("Snooze Handler: " . $name, $this->timings);
return parent::addNotifier(function() use ($timings, $handler) : void{
$timings->startTiming();
$handler();
$timings->stopTiming();
});
}
/**
* Returns the time in nanoseconds spent processing notifications since the last reset.
*/

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.9.0";
public const BASE_VERSION = "5.15.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

@ -173,7 +173,7 @@ class Bamboo extends Transparent{
$newHeight = $height + $growAmount;
$stemBlock = (clone $this)->setReady(false)->setLeafSize(self::NO_LEAVES);
if($newHeight >= 4 && !$stemBlock->isThick()){ //don't change it to false if height is less, because it might have been chopped
if($newHeight >= 4 && !$stemBlock->thick){ //don't change it to false if height is less, because it might have been chopped
$stemBlock = $stemBlock->setThick(true);
}
$smallLeavesBlock = (clone $stemBlock)->setLeafSize(self::SMALL_LEAVES);

View File

@ -55,12 +55,12 @@ class Barrel extends Opaque{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){
if(abs($player->getPosition()->getX() - $this->position->getX()) < 2 && abs($player->getPosition()->getZ() - $this->position->getZ()) < 2){
$y = $player->getEyePos()->getY();
if(abs($player->getPosition()->x - $this->position->x) < 2 && abs($player->getPosition()->z - $this->position->z) < 2){
$y = $player->getEyePos()->y;
if($y - $this->position->getY() > 2){
if($y - $this->position->y > 2){
$this->facing = Facing::UP;
}elseif($this->position->getY() - $y > 0){
}elseif($this->position->y - $y > 0){
$this->facing = Facing::DOWN;
}else{
$this->facing = Facing::opposite($player->getHorizontalFacing());

View File

@ -34,7 +34,6 @@ use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function array_filter;
use function assert;
use function count;
@ -89,11 +88,12 @@ abstract class BaseBanner extends Transparent{
* @return $this
*/
public function setPatterns(array $patterns) : self{
$checked = array_filter($patterns, fn($v) => $v instanceof BannerPatternLayer);
if(count($checked) !== count($patterns)){
throw new \TypeError("Deque must only contain " . BannerPatternLayer::class . " objects");
foreach($patterns as $pattern){
if(!$pattern instanceof BannerPatternLayer){
throw new \TypeError("Array must only contain " . BannerPatternLayer::class . " objects");
}
}
$this->patterns = $checked;
$this->patterns = $patterns;
return $this;
}

View File

@ -65,8 +65,8 @@ abstract class BaseBigDripleaf extends Transparent{
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
if($block instanceof BaseBigDripleaf){
$this->facing = $block->getFacing();
$tx->addBlock($block->getPosition(), VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($this->facing));
$this->facing = $block->facing;
$tx->addBlock($block->position, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($this->facing));
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
@ -98,7 +98,7 @@ abstract class BaseBigDripleaf extends Transparent{
if($head === null){
return false;
}
$pos = $head->getPosition();
$pos = $head->position;
$up = $pos->up();
$world = $pos->getWorld();
if(
@ -110,8 +110,8 @@ abstract class BaseBigDripleaf extends Transparent{
$tx = new BlockTransaction($world);
$tx->addBlock($pos, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($head->getFacing()));
$tx->addBlock($up, VanillaBlocks::BIG_DRIPLEAF_HEAD()->setFacing($head->getFacing()));
$tx->addBlock($pos, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($head->facing));
$tx->addBlock($up, VanillaBlocks::BIG_DRIPLEAF_HEAD()->setFacing($head->facing));
$ev = new StructureGrowEvent($head, $tx, $player);
$ev->call();

View File

@ -145,7 +145,7 @@ class Bed extends Transparent{
$b = ($this->isHeadPart() ? $this : $other);
if($b->isOccupied()){
if($b->occupied){
$player->sendMessage(KnownTranslationFactory::tile_bed_occupied()->prefix(TextFormat::GRAY));
return true;

View File

@ -270,11 +270,22 @@ class Block{
}
private function encodeFullState() : int{
$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits);
$writer->writeInt($this->requiredBlockItemStateDataBits, $this->encodeBlockItemState());
$writer->writeInt($this->requiredBlockOnlyStateDataBits, $this->encodeBlockOnlyState());
$blockItemBits = $this->requiredBlockItemStateDataBits;
$blockOnlyBits = $this->requiredBlockOnlyStateDataBits;
return $writer->getValue();
if($blockOnlyBits === 0 && $blockItemBits === 0){
return 0;
}
$result = 0;
if($blockItemBits > 0){
$result |= $this->encodeBlockItemState();
}
if($blockOnlyBits > 0){
$result |= $this->encodeBlockOnlyState() << $blockItemBits;
}
return $result;
}
/**
@ -391,7 +402,7 @@ class Block{
}
/**
* AKA: Block->isPlaceable
* Returns whether this block can be placed when obtained as an item.
*/
public function canBePlaced() : bool{
return true;
@ -561,16 +572,28 @@ class Block{
return $this->getLightFilter() > 0;
}
/**
* Returns whether this block allows any light to pass through it.
*/
public function isTransparent() : bool{
return false;
}
/**
* @deprecated TL;DR: Don't use this function. Its results are confusing and inconsistent.
*
* No one is sure what the meaning of this property actually is. It's borrowed from Minecraft Java Edition, and is
* used by various blocks for support checks.
*
* Things like signs and banners are considered "solid" despite having no collision box, and things like skulls and
* flower pots are considered non-solid despite obviously being "solid" in the conventional, real-world sense.
*/
public function isSolid() : bool{
return true;
}
/**
* AKA: Block->isFlowable
* Returns whether this block can be destroyed by liquid flowing into its cell.
*/
public function canBeFlowedInto() : bool{
return false;

View File

@ -97,7 +97,7 @@ class ChiseledBookshelf extends Opaque{
return false;
}
$x = Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX();
$x = Facing::axis($face) === Axis::X ? $clickVector->z : $clickVector->x;
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
$clickVector->y

View File

@ -54,7 +54,7 @@ final class ChorusFlower extends Flowable{
}
private function canBeSupportedAt(Block $block) : bool{
$position = $block->getPosition();
$position = $block->position;
$world = $position->getWorld();
$down = $world->getBlock($position->down());
@ -152,7 +152,7 @@ final class ChorusFlower extends Flowable{
if($tx === null){
$tx = new BlockTransaction($this->position->getWorld());
}
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->getAge() + $ageChange)));
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->age + $ageChange)));
return $tx;
}

View File

@ -51,7 +51,7 @@ final class ChorusPlant extends Flowable{
}
private function canBeSupportedAt(Block $block) : bool{
$position = $block->getPosition();
$position = $block->position;
$world = $position->getWorld();
$down = $world->getBlock($position->down());

View File

@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\block\utils\WoodType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Fertilizer;
@ -40,7 +39,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
class CocoaBlock extends Transparent{
class CocoaBlock extends Flowable{
use HorizontalFacingTrait;
use AgeableTrait;
@ -65,10 +64,6 @@ class CocoaBlock extends Transparent{
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE;
}
private function canAttachTo(Block $block) : bool{
return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE;
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
@ -66,7 +67,7 @@ abstract class Crops extends Flowable{
}
public function onRandomTick() : void{
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){
if($this->age < self::MAX_AGE && CropGrowthHelper::canGrow($this)){
$block = clone $this;
++$block->age;
BlockEventHelper::grow($this, $block, null);

View File

@ -24,17 +24,18 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
final class DoublePitcherCrop extends DoublePlant{
use AgeableTrait {
@ -101,10 +102,19 @@ final class DoublePitcherCrop extends DoublePlant{
}
public function onRandomTick() : void{
//TODO: the growth speed is influenced by farmland and nearby crops
//only the bottom half of the plant can grow randomly
if(mt_rand(0, 2) === 0 && !$this->top){
if(CropGrowthHelper::canGrow($this) && !$this->top){
$this->grow(null);
}
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
$this->age >= self::MAX_AGE ? VanillaBlocks::PITCHER_PLANT()->asItem() : VanillaItems::PITCHER_POD()
];
}
public function asItem() : Item{
return VanillaItems::PITCHER_POD();
}
}

View File

@ -152,7 +152,7 @@ class Farmland extends Transparent{
$ev = new EntityTrampleFarmlandEvent($entity, $this);
$ev->call();
if(!$ev->isCancelled()){
$this->getPosition()->getWorld()->setBlock($this->getPosition(), VanillaBlocks::DIRT());
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
}
}
return null;

View File

@ -140,7 +140,7 @@ class Fire extends BaseFire{
$block->onIncinerate();
$world = $this->position->getWorld();
if($world->getBlock($block->getPosition())->isSameState($block)){
if($world->getBlock($block->position)->isSameState($block)){
$spreadedFire = false;
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
$fire = clone $this;

View File

@ -58,7 +58,7 @@ final class FloorCoralFan extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){
$playerBlockPos = $player->getPosition()->floor();
$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
$directionVector = $blockReplace->position->subtractVector($playerBlockPos)->normalize();
$angle = rad2deg(atan2($directionVector->getZ(), $directionVector->getX()));
if($angle <= 45 || 315 <= $angle || (135 <= $angle && $angle <= 225)){

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Furnace as TileFurnace;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\LightableTrait;
use pocketmine\crafting\FurnaceType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@ -34,11 +35,10 @@ use function mt_rand;
class Furnace extends Opaque{
use FacesOppositePlacingPlayerTrait;
use LightableTrait;
protected FurnaceType $furnaceType;
protected bool $lit = false;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, FurnaceType $furnaceType){
$this->furnaceType = $furnaceType;
parent::__construct($idInfo, $name, $typeInfo);
@ -57,18 +57,6 @@ class Furnace extends Opaque{
return $this->lit ? 13 : 0;
}
public function isLit() : bool{
return $this->lit;
}
/**
* @return $this
*/
public function setLit(bool $lit = true) : self{
$this->lit = $lit;
return $this;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$furnace = $this->position->getWorld()->getTile($this->position);

View File

@ -61,7 +61,7 @@ class Jukebox extends Opaque{
public function ejectRecord() : void{
if($this->record !== null){
$this->getPosition()->getWorld()->dropItem($this->getPosition()->add(0.5, 1, 0.5), $this->record);
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $this->record);
$this->record = null;
$this->stopSound();
}
@ -76,12 +76,12 @@ class Jukebox extends Opaque{
public function startSound() : void{
if($this->record !== null){
$this->getPosition()->getWorld()->addSound($this->getPosition(), new RecordSound($this->record->getRecordType()));
$this->position->getWorld()->addSound($this->position, new RecordSound($this->record->getRecordType()));
}
}
public function stopSound() : void{
$this->getPosition()->getWorld()->addSound($this->getPosition(), new RecordStopSound());
$this->position->getWorld()->addSound($this->position, new RecordStopSound());
}
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{

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

@ -108,8 +108,8 @@ class NetherVines extends Flowable{
private function grow(?Player $player, int $growthAmount = 1) : bool{
$top = $this->seekToTip();
$age = $top->getAge();
$pos = $top->getPosition();
$age = $top->age;
$pos = $top->position;
$world = $pos->getWorld();
$changedBlocks = 0;

View File

@ -70,13 +70,13 @@ class PinkPetals extends Flowable{
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
return ($blockReplace instanceof PinkPetals && $blockReplace->count < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
$this->count = $blockReplace->getCount() + 1;
$this->facing = $blockReplace->getFacing();
if($blockReplace instanceof PinkPetals && $blockReplace->count < self::MAX_COUNT){
$this->count = $blockReplace->count + 1;
$this->facing = $blockReplace->facing;
}elseif($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}

View File

@ -25,17 +25,18 @@ namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
final class PitcherCrop extends Flowable{
use AgeableTrait;
@ -97,9 +98,12 @@ final class PitcherCrop extends Flowable{
}
public function onRandomTick() : void{
//TODO: the growth speed is influenced by farmland and nearby crops
if(mt_rand(0, 2) === 0){
if(CropGrowthHelper::canGrow($this)){
$this->grow(null);
}
}
public function asItem() : Item{
return VanillaItems::PITCHER_POD();
}
}

View File

@ -43,7 +43,7 @@ class RedMushroom extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
$position = $this->getPosition();
$position = $this->position;
$lightLevel = $position->getWorld()->getFullLightAt($position->x, $position->y, $position->z);
$downId = $down->getTypeId();
//TODO: nylium support

View File

@ -24,7 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\LightableTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
@ -32,23 +32,7 @@ use pocketmine\player\Player;
use function mt_rand;
class RedstoneOre extends Opaque{
protected bool $lit = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->bool($this->lit);
}
public function isLit() : bool{
return $this->lit;
}
/**
* @return $this
*/
public function setLit(bool $lit = true) : self{
$this->lit = $lit;
return $this;
}
use LightableTrait;
public function getLightLevel() : int{
return $this->lit ? 9 : 0;

View File

@ -23,28 +23,22 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\LightableTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
class RedstoneTorch extends Torch{
protected bool $lit = true;
use LightableTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->lit = true;
parent::__construct($idInfo, $name, $typeInfo);
}
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
parent::describeBlockOnlyState($w);
$w->bool($this->lit);
}
public function isLit() : bool{
return $this->lit;
}
/**
* @return $this
*/
public function setLit(bool $lit = true) : self{
$this->lit = $lit;
return $this;
}
public function getLightLevel() : int{
return $this->lit ? 7 : 0;
}

View File

@ -83,7 +83,7 @@ class SmallDripleaf extends Transparent{
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
$tx->addBlock($block->getPosition(), VanillaBlocks::SMALL_DRIPLEAF()
$tx->addBlock($block->position, VanillaBlocks::SMALL_DRIPLEAF()
->setFacing($this->facing)
->setTop(true)
);
@ -117,7 +117,7 @@ class SmallDripleaf extends Transparent{
$height = mt_rand(2, 5);
$grown = 0;
for($i = 0; $i < $height; $i++){
$pos = $bottomBlock->getSide(Facing::UP, $i)->getPosition();
$pos = $bottomBlock->getSide(Facing::UP, $i)->position;
if(!$this->canGrowTo($pos)){
break;
}

View File

@ -106,8 +106,8 @@ class Stair extends Transparent{
public function getSupportType(int $facing) : SupportType{
if(
$facing === Facing::UP && $this->isUpsideDown() ||
$facing === Facing::DOWN && !$this->isUpsideDown() ||
$facing === Facing::UP && $this->upsideDown ||
$facing === Facing::DOWN && !$this->upsideDown ||
($facing === $this->facing && $this->shape !== StairShape::OUTER_LEFT && $this->shape !== StairShape::OUTER_RIGHT) ||
($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape === StairShape::INNER_LEFT) ||
($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape === StairShape::INNER_RIGHT)

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\math\Facing;
@ -63,7 +64,7 @@ abstract class Stem extends Crops{
}
public function onRandomTick() : void{
if($this->facing === Facing::UP && mt_rand(0, 2) === 1){
if($this->facing === Facing::UP && CropGrowthHelper::canGrow($this)){
$world = $this->position->getWorld();
if($this->age < self::MAX_AGE){
$block = clone $this;

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

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Fertilizer;
@ -32,7 +33,6 @@ use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function mt_rand;
final class TorchflowerCrop extends Flowable{
use StaticSupportTrait;
@ -79,7 +79,7 @@ final class TorchflowerCrop extends Flowable{
}
public function onRandomTick() : void{
if(mt_rand(0, 2) === 1){
if(CropGrowthHelper::canGrow($this)){
BlockEventHelper::grow($this, $this->getNextState(), null);
}
}

View File

@ -788,7 +788,7 @@ final class VanillaBlocks{
}
protected static function setup() : void{
$railBreakInfo = new Info(new BlockBreakInfo(0.7));
$railBreakInfo = new Info(new BreakInfo(0.7));
self::register("activator_rail", new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL), "Activator Rail", $railBreakInfo));
self::register("air", new Air(new BID(Ids::AIR), "Air", new Info(BreakInfo::indestructible(-1.0))));
self::register("anvil", new Anvil(new BID(Ids::ANVIL), "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))));
@ -1638,7 +1638,7 @@ final class VanillaBlocks{
self::register("cave_vines", new CaveVines(new BID(Ids::CAVE_VINES), "Cave Vines", new Info(BreakInfo::instant())));
self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(BlockToolType::SHEARS, toolHarvestLevel: 1))));
self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1))));
self::register("big_dripleaf_head", new BigDripleafHead(new BID(Ids::BIG_DRIPLEAF_HEAD), "Big Dripleaf", new Info(BreakInfo::instant())));
self::register("big_dripleaf_stem", new BigDripleafStem(new BID(Ids::BIG_DRIPLEAF_STEM), "Big Dripleaf Stem", new Info(BreakInfo::instant())));
}

View File

@ -171,7 +171,7 @@ final class WoodLikeBlockIdHelper{
};
}
public static function getTrapdoorIdentifier(WoodType $treeType) : BlockIdentifier{
public static function getTrapdoorIdentifier(WoodType $treeType) : BID{
return new BID(match($treeType){
WoodType::OAK => Ids::OAK_TRAPDOOR,
WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR,
@ -186,7 +186,7 @@ final class WoodLikeBlockIdHelper{
});
}
public static function getButtonIdentifier(WoodType $treeType) : BlockIdentifier{
public static function getButtonIdentifier(WoodType $treeType) : BID{
return new BID(match($treeType){
WoodType::OAK => Ids::OAK_BUTTON,
WoodType::SPRUCE => Ids::SPRUCE_BUTTON,
@ -201,7 +201,7 @@ final class WoodLikeBlockIdHelper{
});
}
public static function getPressurePlateIdentifier(WoodType $treeType) : BlockIdentifier{
public static function getPressurePlateIdentifier(WoodType $treeType) : BID{
return new BID(match($treeType){
WoodType::OAK => Ids::OAK_PRESSURE_PLATE,
WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE,
@ -216,7 +216,7 @@ final class WoodLikeBlockIdHelper{
});
}
public static function getDoorIdentifier(WoodType $treeType) : BlockIdentifier{
public static function getDoorIdentifier(WoodType $treeType) : BID{
return new BID(match($treeType){
WoodType::OAK => Ids::OAK_DOOR,
WoodType::SPRUCE => Ids::SPRUCE_DOOR,
@ -231,7 +231,7 @@ final class WoodLikeBlockIdHelper{
});
}
public static function getFenceGateIdentifier(WoodType $treeType) : BlockIdentifier{
public static function getFenceGateIdentifier(WoodType $treeType) : BID{
return new BID(match($treeType){
WoodType::OAK => Ids::OAK_FENCE_GATE,
WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE,
@ -246,7 +246,7 @@ final class WoodLikeBlockIdHelper{
});
}
public static function getStairsIdentifier(WoodType $treeType) : BlockIdentifier{
public static function getStairsIdentifier(WoodType $treeType) : BID{
return new BID(match($treeType){
WoodType::OAK => Ids::OAK_STAIRS,
WoodType::SPRUCE => Ids::SPRUCE_STAIRS,

View File

@ -47,20 +47,20 @@ trait AnimatedBlockInventoryTrait{
public function onOpen(Player $who) : void{
parent::onOpen($who);
if($this->getHolder()->isValid() && $this->getViewerCount() === 1){
if($this->holder->isValid() && $this->getViewerCount() === 1){
//TODO: this crap really shouldn't be managed by the inventory
$this->animateBlock(true);
$this->getHolder()->getWorld()->addSound($this->getHolder()->add(0.5, 0.5, 0.5), $this->getOpenSound());
$this->holder->getWorld()->addSound($this->holder->add(0.5, 0.5, 0.5), $this->getOpenSound());
}
}
abstract protected function animateBlock(bool $isOpen) : void;
public function onClose(Player $who) : void{
if($this->getHolder()->isValid() && $this->getViewerCount() === 1){
if($this->holder->isValid() && $this->getViewerCount() === 1){
//TODO: this crap really shouldn't be managed by the inventory
$this->animateBlock(false);
$this->getHolder()->getWorld()->addSound($this->getHolder()->add(0.5, 0.5, 0.5), $this->getCloseSound());
$this->holder->getWorld()->addSound($this->holder->add(0.5, 0.5, 0.5), $this->getCloseSound());
}
parent::onClose($who);
}

View File

@ -139,7 +139,7 @@ class Chest extends Spawnable implements Container, Nameable{
if($pair->doubleInventory !== null){
$this->doubleInventory = $pair->doubleInventory;
}else{
if(($pair->getPosition()->x + ($pair->getPosition()->z << 15)) > ($this->position->x + ($this->position->z << 15))){ //Order them correctly
if(($pair->position->x + ($pair->position->z << 15)) > ($this->position->x + ($this->position->z << 15))){ //Order them correctly
$this->doubleInventory = $pair->doubleInventory = new DoubleChestInventory($pair->inventory, $this->inventory);
}else{
$this->doubleInventory = $pair->doubleInventory = new DoubleChestInventory($this->inventory, $pair->inventory);

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

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\Block;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\projectile\Projectile;
use pocketmine\item\Durable;
use pocketmine\item\enchantment\VanillaEnchantments;
@ -38,24 +37,12 @@ use pocketmine\world\sound\FireExtinguishSound;
use pocketmine\world\sound\FlintSteelSound;
trait CandleTrait{
private bool $lit = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->bool($this->lit);
}
use LightableTrait;
public function getLightLevel() : int{
return $this->lit ? 3 : 0;
}
public function isLit() : bool{ return $this->lit; }
/** @return $this */
public function setLit(bool $lit) : self{
$this->lit = $lit;
return $this;
}
/** @see Block::onInteract() */
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){

View File

@ -0,0 +1,120 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\Block;
use pocketmine\block\Farmland;
use function mt_rand;
final class CropGrowthHelper{
private const ON_HYDRATED_FARMLAND_BONUS = 3;
private const ON_DRY_FARMLAND_BONUS = 1;
private const ADJACENT_HYDRATED_FARMLAND_BONUS = 3 / 4;
private const ADJACENT_DRY_FARMLAND_BONUS = 1 / 4;
private const IMPROPER_ARRANGEMENT_DIVISOR = 2;
private const MIN_LIGHT_LEVEL = 9;
private function __construct(){
//NOOP
}
/**
* Returns the speed at which this crop will grow, depending on its surroundings.
* The default is once every 26 random ticks.
*
* Things which influence this include nearby farmland (bonus for hydrated farmland) and the position of other
* nearby crops of the same type (nearby crops of the same type will negatively influence growth speed unless
* planted in rows and properly spaced apart).
*/
public static function calculateMultiplier(Block $block) : float{
$result = 1;
$position = $block->getPosition();
$world = $position->getWorld();
$baseX = $position->getFloorX();
$baseY = $position->getFloorY();
$baseZ = $position->getFloorZ();
$farmland = $world->getBlockAt($baseX, $baseY - 1, $baseZ);
if($farmland instanceof Farmland){
$result += $farmland->getWetness() > 0 ? self::ON_HYDRATED_FARMLAND_BONUS : self::ON_DRY_FARMLAND_BONUS;
}
$xRow = false;
$zRow = false;
$improperArrangement = false;
for($x = -1; $x <= 1; $x++){
for($z = -1; $z <= 1; $z++){
if($x === 0 && $z === 0){
continue;
}
$nextFarmland = $world->getBlockAt($baseX + $x, $baseY - 1, $baseZ + $z);
if(!$nextFarmland instanceof Farmland){
continue;
}
$result += $nextFarmland->getWetness() > 0 ? self::ADJACENT_HYDRATED_FARMLAND_BONUS : self::ADJACENT_DRY_FARMLAND_BONUS;
if(!$improperArrangement){
$nextCrop = $world->getBlockAt($baseX + $x, $baseY, $baseZ + $z);
if($nextCrop->hasSameTypeId($block)){
match(0){
$x => $zRow ? $improperArrangement = true : $xRow = true,
$z => $xRow ? $improperArrangement = true : $zRow = true,
default => $improperArrangement = true,
};
}
}
}
}
//crops can be arranged in rows, but the rows must not cross and must be spaced apart by at least one block
if($improperArrangement){
$result /= self::IMPROPER_ARRANGEMENT_DIVISOR;
}
return $result;
}
public static function hasEnoughLight(Block $block, int $minLevel = self::MIN_LIGHT_LEVEL) : bool{
$position = $block->getPosition();
$world = $position->getWorld();
//crop growth is not affected by time of day since 1.11 or so
return $world->getPotentialLightAt($position->x, $position->y, $position->z) >= $minLevel;
}
public static function canGrow(Block $block) : bool{
//while it may be tempting to use mt_rand(0, 25) < multiplier, this would make crops grow a bit faster than
//vanilla in most cases due to the remainder of 25 / multiplier not being discarded
return mt_rand(0, (int) (25 / self::calculateMultiplier($block))) === 0 && self::hasEnoughLight($block);
}
}

View File

@ -0,0 +1,46 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\data\runtime\RuntimeDataDescriber;
trait LightableTrait{
protected bool $lit = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->bool($this->lit);
}
public function isLit() : bool{
return $this->lit;
}
/**
* @return $this
*/
public function setLit(bool $lit = true) : self{
$this->lit = $lit;
return $this;
}
}

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

@ -43,44 +43,50 @@ final class BannerPatternTypeIdMap{
private array $enumToId = [];
public function __construct(){
$this->register("bo", BannerPatternType::BORDER);
$this->register("bri", BannerPatternType::BRICKS);
$this->register("mc", BannerPatternType::CIRCLE);
$this->register("cre", BannerPatternType::CREEPER);
$this->register("cr", BannerPatternType::CROSS);
$this->register("cbo", BannerPatternType::CURLY_BORDER);
$this->register("lud", BannerPatternType::DIAGONAL_LEFT);
$this->register("rd", BannerPatternType::DIAGONAL_RIGHT);
$this->register("ld", BannerPatternType::DIAGONAL_UP_LEFT);
$this->register("rud", BannerPatternType::DIAGONAL_UP_RIGHT);
$this->register("flo", BannerPatternType::FLOWER);
$this->register("gra", BannerPatternType::GRADIENT);
$this->register("gru", BannerPatternType::GRADIENT_UP);
$this->register("hh", BannerPatternType::HALF_HORIZONTAL);
$this->register("hhb", BannerPatternType::HALF_HORIZONTAL_BOTTOM);
$this->register("vh", BannerPatternType::HALF_VERTICAL);
$this->register("vhr", BannerPatternType::HALF_VERTICAL_RIGHT);
$this->register("moj", BannerPatternType::MOJANG);
$this->register("mr", BannerPatternType::RHOMBUS);
$this->register("sku", BannerPatternType::SKULL);
$this->register("ss", BannerPatternType::SMALL_STRIPES);
$this->register("bl", BannerPatternType::SQUARE_BOTTOM_LEFT);
$this->register("br", BannerPatternType::SQUARE_BOTTOM_RIGHT);
$this->register("tl", BannerPatternType::SQUARE_TOP_LEFT);
$this->register("tr", BannerPatternType::SQUARE_TOP_RIGHT);
$this->register("sc", BannerPatternType::STRAIGHT_CROSS);
$this->register("bs", BannerPatternType::STRIPE_BOTTOM);
$this->register("cs", BannerPatternType::STRIPE_CENTER);
$this->register("dls", BannerPatternType::STRIPE_DOWNLEFT);
$this->register("drs", BannerPatternType::STRIPE_DOWNRIGHT);
$this->register("ls", BannerPatternType::STRIPE_LEFT);
$this->register("ms", BannerPatternType::STRIPE_MIDDLE);
$this->register("rs", BannerPatternType::STRIPE_RIGHT);
$this->register("ts", BannerPatternType::STRIPE_TOP);
$this->register("bt", BannerPatternType::TRIANGLE_BOTTOM);
$this->register("tt", BannerPatternType::TRIANGLE_TOP);
$this->register("bts", BannerPatternType::TRIANGLES_BOTTOM);
$this->register("tts", BannerPatternType::TRIANGLES_TOP);
foreach(BannerPatternType::cases() as $case){
$this->register(match($case){
BannerPatternType::BORDER => "bo",
BannerPatternType::BRICKS => "bri",
BannerPatternType::CIRCLE => "mc",
BannerPatternType::CREEPER => "cre",
BannerPatternType::CROSS => "cr",
BannerPatternType::CURLY_BORDER => "cbo",
BannerPatternType::DIAGONAL_LEFT => "lud",
BannerPatternType::DIAGONAL_RIGHT => "rd",
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",
BannerPatternType::HALF_HORIZONTAL_BOTTOM => "hhb",
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",
BannerPatternType::SQUARE_BOTTOM_LEFT => "bl",
BannerPatternType::SQUARE_BOTTOM_RIGHT => "br",
BannerPatternType::SQUARE_TOP_LEFT => "tl",
BannerPatternType::SQUARE_TOP_RIGHT => "tr",
BannerPatternType::STRAIGHT_CROSS => "sc",
BannerPatternType::STRIPE_BOTTOM => "bs",
BannerPatternType::STRIPE_CENTER => "cs",
BannerPatternType::STRIPE_DOWNLEFT => "dls",
BannerPatternType::STRIPE_DOWNRIGHT => "drs",
BannerPatternType::STRIPE_LEFT => "ls",
BannerPatternType::STRIPE_MIDDLE => "ms",
BannerPatternType::STRIPE_RIGHT => "rs",
BannerPatternType::STRIPE_TOP => "ts",
BannerPatternType::TRIANGLE_BOTTOM => "bt",
BannerPatternType::TRIANGLE_TOP => "tt",
BannerPatternType::TRIANGLES_BOTTOM => "bts",
BannerPatternType::TRIANGLES_TOP => "tts",
}, $case);
}
}
public function register(string $stringId, BannerPatternType $type) : void{

View File

@ -111,4 +111,15 @@ final class BiomeIds{
public const CRIMSON_FOREST = 179;
public const WARPED_FOREST = 180;
public const BASALT_DELTAS = 181;
public const JAGGED_PEAKS = 182;
public const FROZEN_PEAKS = 183;
public const SNOWY_SLOPES = 184;
public const GROVE = 185;
public const MEADOW = 186;
public const LUSH_CAVES = 187;
public const DRIPSTONE_CAVES = 188;
public const STONY_PEAKS = 189;
public const DEEP_DARK = 190;
public const MANGROVE_SWAMP = 191;
public const CHERRY_GROVE = 192;
}

View File

@ -48,22 +48,28 @@ final class DyeColorIdMap{
private array $enumToItemId = [];
private function __construct(){
$this->register(0, ItemTypeNames::WHITE_DYE, DyeColor::WHITE);
$this->register(1, ItemTypeNames::ORANGE_DYE, DyeColor::ORANGE);
$this->register(2, ItemTypeNames::MAGENTA_DYE, DyeColor::MAGENTA);
$this->register(3, ItemTypeNames::LIGHT_BLUE_DYE, DyeColor::LIGHT_BLUE);
$this->register(4, ItemTypeNames::YELLOW_DYE, DyeColor::YELLOW);
$this->register(5, ItemTypeNames::LIME_DYE, DyeColor::LIME);
$this->register(6, ItemTypeNames::PINK_DYE, DyeColor::PINK);
$this->register(7, ItemTypeNames::GRAY_DYE, DyeColor::GRAY);
$this->register(8, ItemTypeNames::LIGHT_GRAY_DYE, DyeColor::LIGHT_GRAY);
$this->register(9, ItemTypeNames::CYAN_DYE, DyeColor::CYAN);
$this->register(10, ItemTypeNames::PURPLE_DYE, DyeColor::PURPLE);
$this->register(11, ItemTypeNames::BLUE_DYE, DyeColor::BLUE);
$this->register(12, ItemTypeNames::BROWN_DYE, DyeColor::BROWN);
$this->register(13, ItemTypeNames::GREEN_DYE, DyeColor::GREEN);
$this->register(14, ItemTypeNames::RED_DYE, DyeColor::RED);
$this->register(15, ItemTypeNames::BLACK_DYE, DyeColor::BLACK);
foreach(DyeColor::cases() as $case){
[$colorId, $dyeItemId] = match($case){
DyeColor::WHITE => [0, ItemTypeNames::WHITE_DYE],
DyeColor::ORANGE => [1, ItemTypeNames::ORANGE_DYE],
DyeColor::MAGENTA => [2, ItemTypeNames::MAGENTA_DYE],
DyeColor::LIGHT_BLUE => [3, ItemTypeNames::LIGHT_BLUE_DYE],
DyeColor::YELLOW => [4, ItemTypeNames::YELLOW_DYE],
DyeColor::LIME => [5, ItemTypeNames::LIME_DYE],
DyeColor::PINK => [6, ItemTypeNames::PINK_DYE],
DyeColor::GRAY => [7, ItemTypeNames::GRAY_DYE],
DyeColor::LIGHT_GRAY => [8, ItemTypeNames::LIGHT_GRAY_DYE],
DyeColor::CYAN => [9, ItemTypeNames::CYAN_DYE],
DyeColor::PURPLE => [10, ItemTypeNames::PURPLE_DYE],
DyeColor::BLUE => [11, ItemTypeNames::BLUE_DYE],
DyeColor::BROWN => [12, ItemTypeNames::BROWN_DYE],
DyeColor::GREEN => [13, ItemTypeNames::GREEN_DYE],
DyeColor::RED => [14, ItemTypeNames::RED_DYE],
DyeColor::BLACK => [15, ItemTypeNames::BLACK_DYE],
};
$this->register($colorId, $dyeItemId, $case);
}
}
private function register(int $id, string $itemId, DyeColor $color) : void{

View File

@ -32,9 +32,13 @@ final class MedicineTypeIdMap{
use IntSaveIdMapTrait;
private function __construct(){
$this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE);
$this->register(MedicineTypeIds::ELIXIR, MedicineType::ELIXIR);
$this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS);
$this->register(MedicineTypeIds::TONIC, MedicineType::TONIC);
foreach(MedicineType::cases() as $case){
$this->register(match($case){
MedicineType::ANTIDOTE => MedicineTypeIds::ANTIDOTE,
MedicineType::ELIXIR => MedicineTypeIds::ELIXIR,
MedicineType::EYE_DROPS => MedicineTypeIds::EYE_DROPS,
MedicineType::TONIC => MedicineTypeIds::TONIC,
}, $case);
}
}
}

View File

@ -32,12 +32,16 @@ final class MobHeadTypeIdMap{
use IntSaveIdMapTrait;
private function __construct(){
$this->register(0, MobHeadType::SKELETON);
$this->register(1, MobHeadType::WITHER_SKELETON);
$this->register(2, MobHeadType::ZOMBIE);
$this->register(3, MobHeadType::PLAYER);
$this->register(4, MobHeadType::CREEPER);
$this->register(5, MobHeadType::DRAGON);
$this->register(6, MobHeadType::PIGLIN);
foreach(MobHeadType::cases() as $case){
$this->register(match($case){
MobHeadType::SKELETON => 0,
MobHeadType::WITHER_SKELETON => 1,
MobHeadType::ZOMBIE => 2,
MobHeadType::PLAYER => 3,
MobHeadType::CREEPER => 4,
MobHeadType::DRAGON => 5,
MobHeadType::PIGLIN => 6,
}, $case);
}
}
}

View File

@ -33,16 +33,20 @@ final class MushroomBlockTypeIdMap{
use IntSaveIdMapTrait;
public function __construct(){
$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, MushroomBlockType::CAP_NORTHWEST);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, MushroomBlockType::CAP_NORTH);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, MushroomBlockType::CAP_NORTHEAST);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, MushroomBlockType::CAP_WEST);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, MushroomBlockType::CAP_MIDDLE);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, MushroomBlockType::CAP_EAST);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, MushroomBlockType::CAP_SOUTHWEST);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, MushroomBlockType::CAP_SOUTH);
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST);
$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP);
foreach(MushroomBlockType::cases() as $case){
$this->register(match($case){
MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES,
MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER,
MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE,
MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER,
MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE,
MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY,
MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE,
MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER,
MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE,
MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER,
MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP,
}, $case);
}
}
}

View File

@ -32,21 +32,25 @@ final class NoteInstrumentIdMap{
use IntSaveIdMapTrait;
private function __construct(){
$this->register(0, NoteInstrument::PIANO);
$this->register(1, NoteInstrument::BASS_DRUM);
$this->register(2, NoteInstrument::SNARE);
$this->register(3, NoteInstrument::CLICKS_AND_STICKS);
$this->register(4, NoteInstrument::DOUBLE_BASS);
$this->register(5, NoteInstrument::BELL);
$this->register(6, NoteInstrument::FLUTE);
$this->register(7, NoteInstrument::CHIME);
$this->register(8, NoteInstrument::GUITAR);
$this->register(9, NoteInstrument::XYLOPHONE);
$this->register(10, NoteInstrument::IRON_XYLOPHONE);
$this->register(11, NoteInstrument::COW_BELL);
$this->register(12, NoteInstrument::DIDGERIDOO);
$this->register(13, NoteInstrument::BIT);
$this->register(14, NoteInstrument::BANJO);
$this->register(15, NoteInstrument::PLING);
foreach(NoteInstrument::cases() as $case){
$this->register(match($case){
NoteInstrument::PIANO => 0,
NoteInstrument::BASS_DRUM => 1,
NoteInstrument::SNARE => 2,
NoteInstrument::CLICKS_AND_STICKS => 3,
NoteInstrument::DOUBLE_BASS => 4,
NoteInstrument::BELL => 5,
NoteInstrument::FLUTE => 6,
NoteInstrument::CHIME => 7,
NoteInstrument::GUITAR => 8,
NoteInstrument::XYLOPHONE => 9,
NoteInstrument::IRON_XYLOPHONE => 10,
NoteInstrument::COW_BELL => 11,
NoteInstrument::DIDGERIDOO => 12,
NoteInstrument::BIT => 13,
NoteInstrument::BANJO => 14,
NoteInstrument::PLING => 15,
}, $case);
}
}
}

View File

@ -32,48 +32,52 @@ final class PotionTypeIdMap{
use IntSaveIdMapTrait;
private function __construct(){
$this->register(PotionTypeIds::WATER, PotionType::WATER);
$this->register(PotionTypeIds::MUNDANE, PotionType::MUNDANE);
$this->register(PotionTypeIds::LONG_MUNDANE, PotionType::LONG_MUNDANE);
$this->register(PotionTypeIds::THICK, PotionType::THICK);
$this->register(PotionTypeIds::AWKWARD, PotionType::AWKWARD);
$this->register(PotionTypeIds::NIGHT_VISION, PotionType::NIGHT_VISION);
$this->register(PotionTypeIds::LONG_NIGHT_VISION, PotionType::LONG_NIGHT_VISION);
$this->register(PotionTypeIds::INVISIBILITY, PotionType::INVISIBILITY);
$this->register(PotionTypeIds::LONG_INVISIBILITY, PotionType::LONG_INVISIBILITY);
$this->register(PotionTypeIds::LEAPING, PotionType::LEAPING);
$this->register(PotionTypeIds::LONG_LEAPING, PotionType::LONG_LEAPING);
$this->register(PotionTypeIds::STRONG_LEAPING, PotionType::STRONG_LEAPING);
$this->register(PotionTypeIds::FIRE_RESISTANCE, PotionType::FIRE_RESISTANCE);
$this->register(PotionTypeIds::LONG_FIRE_RESISTANCE, PotionType::LONG_FIRE_RESISTANCE);
$this->register(PotionTypeIds::SWIFTNESS, PotionType::SWIFTNESS);
$this->register(PotionTypeIds::LONG_SWIFTNESS, PotionType::LONG_SWIFTNESS);
$this->register(PotionTypeIds::STRONG_SWIFTNESS, PotionType::STRONG_SWIFTNESS);
$this->register(PotionTypeIds::SLOWNESS, PotionType::SLOWNESS);
$this->register(PotionTypeIds::LONG_SLOWNESS, PotionType::LONG_SLOWNESS);
$this->register(PotionTypeIds::WATER_BREATHING, PotionType::WATER_BREATHING);
$this->register(PotionTypeIds::LONG_WATER_BREATHING, PotionType::LONG_WATER_BREATHING);
$this->register(PotionTypeIds::HEALING, PotionType::HEALING);
$this->register(PotionTypeIds::STRONG_HEALING, PotionType::STRONG_HEALING);
$this->register(PotionTypeIds::HARMING, PotionType::HARMING);
$this->register(PotionTypeIds::STRONG_HARMING, PotionType::STRONG_HARMING);
$this->register(PotionTypeIds::POISON, PotionType::POISON);
$this->register(PotionTypeIds::LONG_POISON, PotionType::LONG_POISON);
$this->register(PotionTypeIds::STRONG_POISON, PotionType::STRONG_POISON);
$this->register(PotionTypeIds::REGENERATION, PotionType::REGENERATION);
$this->register(PotionTypeIds::LONG_REGENERATION, PotionType::LONG_REGENERATION);
$this->register(PotionTypeIds::STRONG_REGENERATION, PotionType::STRONG_REGENERATION);
$this->register(PotionTypeIds::STRENGTH, PotionType::STRENGTH);
$this->register(PotionTypeIds::LONG_STRENGTH, PotionType::LONG_STRENGTH);
$this->register(PotionTypeIds::STRONG_STRENGTH, PotionType::STRONG_STRENGTH);
$this->register(PotionTypeIds::WEAKNESS, PotionType::WEAKNESS);
$this->register(PotionTypeIds::LONG_WEAKNESS, PotionType::LONG_WEAKNESS);
$this->register(PotionTypeIds::WITHER, PotionType::WITHER);
$this->register(PotionTypeIds::TURTLE_MASTER, PotionType::TURTLE_MASTER);
$this->register(PotionTypeIds::LONG_TURTLE_MASTER, PotionType::LONG_TURTLE_MASTER);
$this->register(PotionTypeIds::STRONG_TURTLE_MASTER, PotionType::STRONG_TURTLE_MASTER);
$this->register(PotionTypeIds::SLOW_FALLING, PotionType::SLOW_FALLING);
$this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING);
$this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS);
foreach(PotionType::cases() as $case){
$this->register(match($case){
PotionType::WATER => PotionTypeIds::WATER,
PotionType::MUNDANE => PotionTypeIds::MUNDANE,
PotionType::LONG_MUNDANE => PotionTypeIds::LONG_MUNDANE,
PotionType::THICK => PotionTypeIds::THICK,
PotionType::AWKWARD => PotionTypeIds::AWKWARD,
PotionType::NIGHT_VISION => PotionTypeIds::NIGHT_VISION,
PotionType::LONG_NIGHT_VISION => PotionTypeIds::LONG_NIGHT_VISION,
PotionType::INVISIBILITY => PotionTypeIds::INVISIBILITY,
PotionType::LONG_INVISIBILITY => PotionTypeIds::LONG_INVISIBILITY,
PotionType::LEAPING => PotionTypeIds::LEAPING,
PotionType::LONG_LEAPING => PotionTypeIds::LONG_LEAPING,
PotionType::STRONG_LEAPING => PotionTypeIds::STRONG_LEAPING,
PotionType::FIRE_RESISTANCE => PotionTypeIds::FIRE_RESISTANCE,
PotionType::LONG_FIRE_RESISTANCE => PotionTypeIds::LONG_FIRE_RESISTANCE,
PotionType::SWIFTNESS => PotionTypeIds::SWIFTNESS,
PotionType::LONG_SWIFTNESS => PotionTypeIds::LONG_SWIFTNESS,
PotionType::STRONG_SWIFTNESS => PotionTypeIds::STRONG_SWIFTNESS,
PotionType::SLOWNESS => PotionTypeIds::SLOWNESS,
PotionType::LONG_SLOWNESS => PotionTypeIds::LONG_SLOWNESS,
PotionType::WATER_BREATHING => PotionTypeIds::WATER_BREATHING,
PotionType::LONG_WATER_BREATHING => PotionTypeIds::LONG_WATER_BREATHING,
PotionType::HEALING => PotionTypeIds::HEALING,
PotionType::STRONG_HEALING => PotionTypeIds::STRONG_HEALING,
PotionType::HARMING => PotionTypeIds::HARMING,
PotionType::STRONG_HARMING => PotionTypeIds::STRONG_HARMING,
PotionType::POISON => PotionTypeIds::POISON,
PotionType::LONG_POISON => PotionTypeIds::LONG_POISON,
PotionType::STRONG_POISON => PotionTypeIds::STRONG_POISON,
PotionType::REGENERATION => PotionTypeIds::REGENERATION,
PotionType::LONG_REGENERATION => PotionTypeIds::LONG_REGENERATION,
PotionType::STRONG_REGENERATION => PotionTypeIds::STRONG_REGENERATION,
PotionType::STRENGTH => PotionTypeIds::STRENGTH,
PotionType::LONG_STRENGTH => PotionTypeIds::LONG_STRENGTH,
PotionType::STRONG_STRENGTH => PotionTypeIds::STRONG_STRENGTH,
PotionType::WEAKNESS => PotionTypeIds::WEAKNESS,
PotionType::LONG_WEAKNESS => PotionTypeIds::LONG_WEAKNESS,
PotionType::WITHER => PotionTypeIds::WITHER,
PotionType::TURTLE_MASTER => PotionTypeIds::TURTLE_MASTER,
PotionType::LONG_TURTLE_MASTER => PotionTypeIds::LONG_TURTLE_MASTER,
PotionType::STRONG_TURTLE_MASTER => PotionTypeIds::STRONG_TURTLE_MASTER,
PotionType::SLOW_FALLING => PotionTypeIds::SLOW_FALLING,
PotionType::LONG_SLOW_FALLING => PotionTypeIds::LONG_SLOW_FALLING,
PotionType::STRONG_SLOWNESS => PotionTypeIds::STRONG_SLOWNESS,
}, $case);
}
}
}

View File

@ -32,15 +32,20 @@ final class SuspiciousStewTypeIdMap{
use IntSaveIdMapTrait;
private function __construct(){
$this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY);
$this->register(SuspiciousStewTypeIds::CORNFLOWER, SuspiciousStewType::CORNFLOWER);
$this->register(SuspiciousStewTypeIds::TULIP, SuspiciousStewType::TULIP);
$this->register(SuspiciousStewTypeIds::AZURE_BLUET, SuspiciousStewType::AZURE_BLUET);
$this->register(SuspiciousStewTypeIds::LILY_OF_THE_VALLEY, SuspiciousStewType::LILY_OF_THE_VALLEY);
$this->register(SuspiciousStewTypeIds::DANDELION, SuspiciousStewType::DANDELION);
$this->register(SuspiciousStewTypeIds::BLUE_ORCHID, SuspiciousStewType::BLUE_ORCHID);
$this->register(SuspiciousStewTypeIds::ALLIUM, SuspiciousStewType::ALLIUM);
$this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY);
$this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE);
foreach(SuspiciousStewType::cases() as $case){
$this->register(match($case){
SuspiciousStewType::POPPY => SuspiciousStewTypeIds::POPPY,
SuspiciousStewType::CORNFLOWER => SuspiciousStewTypeIds::CORNFLOWER,
SuspiciousStewType::TULIP => SuspiciousStewTypeIds::TULIP,
SuspiciousStewType::AZURE_BLUET => SuspiciousStewTypeIds::AZURE_BLUET,
SuspiciousStewType::LILY_OF_THE_VALLEY => SuspiciousStewTypeIds::LILY_OF_THE_VALLEY,
SuspiciousStewType::DANDELION => SuspiciousStewTypeIds::DANDELION,
SuspiciousStewType::BLUE_ORCHID => SuspiciousStewTypeIds::BLUE_ORCHID,
SuspiciousStewType::ALLIUM => SuspiciousStewTypeIds::ALLIUM,
SuspiciousStewType::OXEYE_DAISY => SuspiciousStewTypeIds::OXEYE_DAISY,
SuspiciousStewType::WITHER_ROSE => SuspiciousStewTypeIds::WITHER_ROSE,
}, $case);
}
}
}

View File

@ -42,8 +42,8 @@ final class BlockStateData{
public const CURRENT_VERSION =
(1 << 24) | //major
(20 << 16) | //minor
(50 << 8) | //patch
(1); //revision
(80 << 8) | //patch
(3); //revision
public const TAG_NAME = "name";
public const TAG_STATES = "states";

View File

@ -56,7 +56,6 @@ final class BlockStateNames{
public const CHEMISTRY_TABLE_TYPE = "chemistry_table_type";
public const CHISEL_TYPE = "chisel_type";
public const CLUSTER_COUNT = "cluster_count";
public const COLOR = "color";
public const COLOR_BIT = "color_bit";
public const COMPOSTER_FILL_LEVEL = "composter_fill_level";
public const CONDITIONAL_BIT = "conditional_bit";
@ -82,7 +81,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";
@ -106,9 +104,7 @@ final class BlockStateNames{
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 OPEN_BIT = "open_bit";
public const ORIENTATION = "orientation";
public const OUTPUT_LIT_BIT = "output_lit_bit";
@ -127,7 +123,6 @@ final class BlockStateNames{
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";
@ -145,12 +140,14 @@ final class BlockStateNames{
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";
public const TRIGGERED_BIT = "triggered_bit";
public const TURTLE_EGG_COUNT = "turtle_egg_count";
public const TWISTING_VINES_AGE = "twisting_vines_age";
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";
@ -160,5 +157,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

@ -62,23 +62,6 @@ final class BlockStateStringValues{
public const CHISEL_TYPE_LINES = "lines";
public const CHISEL_TYPE_SMOOTH = "smooth";
public const COLOR_BLACK = "black";
public const COLOR_BLUE = "blue";
public const COLOR_BROWN = "brown";
public const COLOR_CYAN = "cyan";
public const COLOR_GRAY = "gray";
public const COLOR_GREEN = "green";
public const COLOR_LIGHT_BLUE = "light_blue";
public const COLOR_LIME = "lime";
public const COLOR_MAGENTA = "magenta";
public const COLOR_ORANGE = "orange";
public const COLOR_PINK = "pink";
public const COLOR_PURPLE = "purple";
public const COLOR_RED = "red";
public const COLOR_SILVER = "silver";
public const COLOR_WHITE = "white";
public const COLOR_YELLOW = "yellow";
public const CORAL_COLOR_BLUE = "blue";
public const CORAL_COLOR_PINK = "pink";
public const CORAL_COLOR_PURPLE = "purple";
@ -110,18 +93,6 @@ final class BlockStateStringValues{
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";
@ -160,14 +131,6 @@ final class BlockStateStringValues{
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";
@ -201,13 +164,6 @@ final class BlockStateStringValues{
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";
@ -281,6 +237,11 @@ final class BlockStateStringValues{
public const TURTLE_EGG_COUNT_THREE_EGG = "three_egg";
public const TURTLE_EGG_COUNT_TWO_EGG = "two_egg";
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_BLOCK_TYPE_ANDESITE = "andesite";
public const WALL_BLOCK_TYPE_BRICK = "brick";
public const WALL_BLOCK_TYPE_COBBLESTONE = "cobblestone";
@ -312,11 +273,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,18 +33,24 @@ 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";
@ -55,6 +61,7 @@ final class BlockTypeNames{
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 +95,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 +134,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,6 +144,7 @@ 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_FAN = "minecraft:brain_coral_fan";
public const BREWING_STAND = "minecraft:brewing_stand";
public const BRICK_BLOCK = "minecraft:brick_block";
public const BRICK_STAIRS = "minecraft:brick_stairs";
@ -149,6 +163,7 @@ 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_FAN = "minecraft:bubble_coral_fan";
public const BUDDING_AMETHYST = "minecraft:budding_amethyst";
public const CACTUS = "minecraft:cactus";
public const CAKE = "minecraft:cake";
@ -220,11 +235,10 @@ final class BlockTypeNames{
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";
@ -266,24 +280,34 @@ final class BlockTypeNames{
public const CYAN_WOOL = "minecraft:cyan_wool";
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_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_FAN = "minecraft:dead_brain_coral_fan";
public const DEAD_BUBBLE_CORAL = "minecraft:dead_bubble_coral";
public const DEAD_BUBBLE_CORAL_FAN = "minecraft:dead_bubble_coral_fan";
public const DEAD_FIRE_CORAL = "minecraft:dead_fire_coral";
public const DEAD_FIRE_CORAL_FAN = "minecraft:dead_fire_coral_fan";
public const DEAD_HORN_CORAL = "minecraft:dead_horn_coral";
public const DEAD_HORN_CORAL_FAN = "minecraft:dead_horn_coral_fan";
public const DEAD_TUBE_CORAL = "minecraft:dead_tube_coral";
public const DEAD_TUBE_CORAL_FAN = "minecraft:dead_tube_coral_fan";
public const DEADBUSH = "minecraft:deadbush";
public const DECORATED_POT = "minecraft:decorated_pot";
public const DEEPSLATE = "minecraft:deepslate";
@ -320,7 +344,6 @@ final class BlockTypeNames{
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 DRIED_KELP_BLOCK = "minecraft:dried_kelp_block";
public const DRIPSTONE_BLOCK = "minecraft:dripstone_block";
@ -469,6 +492,7 @@ final class BlockTypeNames{
public const FENCE_GATE = "minecraft:fence_gate";
public const FIRE = "minecraft:fire";
public const FIRE_CORAL = "minecraft:fire_coral";
public const FIRE_CORAL_FAN = "minecraft:fire_coral_fan";
public const FLETCHING_TABLE = "minecraft:fletching_table";
public const FLOWER_POT = "minecraft:flower_pot";
public const FLOWERING_AZALEA = "minecraft:flowering_azalea";
@ -490,7 +514,7 @@ final class BlockTypeNames{
public const GOLDEN_RAIL = "minecraft:golden_rail";
public const GRANITE = "minecraft:granite";
public const GRANITE_STAIRS = "minecraft:granite_stairs";
public const GRASS = "minecraft:grass";
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";
@ -517,17 +541,49 @@ final class BlockTypeNames{
public const GREEN_WOOL = "minecraft:green_wool";
public const GRINDSTONE = "minecraft:grindstone";
public const HANGING_ROOTS = "minecraft:hanging_roots";
public const HARD_BLACK_STAINED_GLASS = "minecraft:hard_black_stained_glass";
public const HARD_BLACK_STAINED_GLASS_PANE = "minecraft:hard_black_stained_glass_pane";
public const HARD_BLUE_STAINED_GLASS = "minecraft:hard_blue_stained_glass";
public const HARD_BLUE_STAINED_GLASS_PANE = "minecraft:hard_blue_stained_glass_pane";
public const HARD_BROWN_STAINED_GLASS = "minecraft:hard_brown_stained_glass";
public const HARD_BROWN_STAINED_GLASS_PANE = "minecraft:hard_brown_stained_glass_pane";
public const HARD_CYAN_STAINED_GLASS = "minecraft:hard_cyan_stained_glass";
public const HARD_CYAN_STAINED_GLASS_PANE = "minecraft:hard_cyan_stained_glass_pane";
public const HARD_GLASS = "minecraft:hard_glass";
public const HARD_GLASS_PANE = "minecraft:hard_glass_pane";
public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass";
public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane";
public const HARD_GRAY_STAINED_GLASS = "minecraft:hard_gray_stained_glass";
public const HARD_GRAY_STAINED_GLASS_PANE = "minecraft:hard_gray_stained_glass_pane";
public const HARD_GREEN_STAINED_GLASS = "minecraft:hard_green_stained_glass";
public const HARD_GREEN_STAINED_GLASS_PANE = "minecraft:hard_green_stained_glass_pane";
public const HARD_LIGHT_BLUE_STAINED_GLASS = "minecraft:hard_light_blue_stained_glass";
public const HARD_LIGHT_BLUE_STAINED_GLASS_PANE = "minecraft:hard_light_blue_stained_glass_pane";
public const HARD_LIGHT_GRAY_STAINED_GLASS = "minecraft:hard_light_gray_stained_glass";
public const HARD_LIGHT_GRAY_STAINED_GLASS_PANE = "minecraft:hard_light_gray_stained_glass_pane";
public const HARD_LIME_STAINED_GLASS = "minecraft:hard_lime_stained_glass";
public const HARD_LIME_STAINED_GLASS_PANE = "minecraft:hard_lime_stained_glass_pane";
public const HARD_MAGENTA_STAINED_GLASS = "minecraft:hard_magenta_stained_glass";
public const HARD_MAGENTA_STAINED_GLASS_PANE = "minecraft:hard_magenta_stained_glass_pane";
public const HARD_ORANGE_STAINED_GLASS = "minecraft:hard_orange_stained_glass";
public const HARD_ORANGE_STAINED_GLASS_PANE = "minecraft:hard_orange_stained_glass_pane";
public const HARD_PINK_STAINED_GLASS = "minecraft:hard_pink_stained_glass";
public const HARD_PINK_STAINED_GLASS_PANE = "minecraft:hard_pink_stained_glass_pane";
public const HARD_PURPLE_STAINED_GLASS = "minecraft:hard_purple_stained_glass";
public const HARD_PURPLE_STAINED_GLASS_PANE = "minecraft:hard_purple_stained_glass_pane";
public const HARD_RED_STAINED_GLASS = "minecraft:hard_red_stained_glass";
public const HARD_RED_STAINED_GLASS_PANE = "minecraft:hard_red_stained_glass_pane";
public const HARD_WHITE_STAINED_GLASS = "minecraft:hard_white_stained_glass";
public const HARD_WHITE_STAINED_GLASS_PANE = "minecraft:hard_white_stained_glass_pane";
public const HARD_YELLOW_STAINED_GLASS = "minecraft:hard_yellow_stained_glass";
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_FAN = "minecraft:horn_coral_fan";
public const ICE = "minecraft:ice";
public const INFESTED_DEEPSLATE = "minecraft:infested_deepslate";
public const INFO_UPDATE = "minecraft:info_update";
@ -542,16 +598,21 @@ 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 LADDER = "minecraft:ladder";
public const LANTERN = "minecraft:lantern";
@ -559,8 +620,6 @@ final class BlockTypeNames{
public const LAPIS_ORE = "minecraft:lapis_ore";
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
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";
@ -587,6 +646,7 @@ 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 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";
@ -668,11 +728,16 @@ final class BlockTypeNames{
public const NETHERREACTOR = "minecraft:netherreactor";
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";
@ -686,7 +751,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";
@ -711,6 +778,7 @@ 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";
@ -747,6 +815,7 @@ final class BlockTypeNames{
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";
@ -783,7 +852,6 @@ 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";
@ -795,6 +863,7 @@ final class BlockTypeNames{
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";
@ -809,7 +878,6 @@ final class BlockTypeNames{
public const SAND = "minecraft:sand";
public const SANDSTONE = "minecraft:sandstone";
public const SANDSTONE_STAIRS = "minecraft:sandstone_stairs";
public const SAPLING = "minecraft:sapling";
public const SCAFFOLDING = "minecraft:scaffolding";
public const SCULK = "minecraft:sculk";
public const SCULK_CATALYST = "minecraft:sculk_catalyst";
@ -845,16 +913,21 @@ 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";
@ -872,18 +945,24 @@ final class BlockTypeNames{
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";
@ -900,9 +979,11 @@ final class BlockTypeNames{
public const TORCHFLOWER_CROP = "minecraft:torchflower_crop";
public const TRAPDOOR = "minecraft:trapdoor";
public const TRAPPED_CHEST = "minecraft:trapped_chest";
public const TRIAL_SPAWNER = "minecraft:trial_spawner";
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_FAN = "minecraft:tube_coral_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";
@ -921,6 +1002,7 @@ final class BlockTypeNames{
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";
@ -1009,13 +1091,12 @@ 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 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";

View File

@ -203,8 +203,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerFlatCoralSerializers();
$this->registerCauldronSerializers();
$this->registerFlatWoodBlockSerializers();
$this->registerLegacyWoodBlockSerializers();
$this->registerLeavesSerializers();
$this->registerSaplingSerializers();
$this->registerSimpleSerializers();
$this->registerSerializers();
}
@ -315,6 +315,44 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
}
public function registerFlatColorBlockSerializers() : void{
$this->map(Blocks::STAINED_HARDENED_GLASS(), fn(StainedHardenedGlass $block) => Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS,
DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS,
DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS,
DyeColor::CYAN => Ids::HARD_CYAN_STAINED_GLASS,
DyeColor::GRAY => Ids::HARD_GRAY_STAINED_GLASS,
DyeColor::GREEN => Ids::HARD_GREEN_STAINED_GLASS,
DyeColor::LIGHT_BLUE => Ids::HARD_LIGHT_BLUE_STAINED_GLASS,
DyeColor::LIGHT_GRAY => Ids::HARD_LIGHT_GRAY_STAINED_GLASS,
DyeColor::LIME => Ids::HARD_LIME_STAINED_GLASS,
DyeColor::MAGENTA => Ids::HARD_MAGENTA_STAINED_GLASS,
DyeColor::ORANGE => Ids::HARD_ORANGE_STAINED_GLASS,
DyeColor::PINK => Ids::HARD_PINK_STAINED_GLASS,
DyeColor::PURPLE => Ids::HARD_PURPLE_STAINED_GLASS,
DyeColor::RED => Ids::HARD_RED_STAINED_GLASS,
DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS,
DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS,
}));
$this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), fn(StainedHardenedGlassPane $block) => Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS_PANE,
DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS_PANE,
DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS_PANE,
DyeColor::CYAN => Ids::HARD_CYAN_STAINED_GLASS_PANE,
DyeColor::GRAY => Ids::HARD_GRAY_STAINED_GLASS_PANE,
DyeColor::GREEN => Ids::HARD_GREEN_STAINED_GLASS_PANE,
DyeColor::LIGHT_BLUE => Ids::HARD_LIGHT_BLUE_STAINED_GLASS_PANE,
DyeColor::LIGHT_GRAY => Ids::HARD_LIGHT_GRAY_STAINED_GLASS_PANE,
DyeColor::LIME => Ids::HARD_LIME_STAINED_GLASS_PANE,
DyeColor::MAGENTA => Ids::HARD_MAGENTA_STAINED_GLASS_PANE,
DyeColor::ORANGE => Ids::HARD_ORANGE_STAINED_GLASS_PANE,
DyeColor::PINK => Ids::HARD_PINK_STAINED_GLASS_PANE,
DyeColor::PURPLE => Ids::HARD_PURPLE_STAINED_GLASS_PANE,
DyeColor::RED => Ids::HARD_RED_STAINED_GLASS_PANE,
DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS_PANE,
DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS_PANE,
}));
$this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{
return Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::BLACK_GLAZED_TERRACOTTA,
@ -500,6 +538,20 @@ 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}"),
}));
}
private function registerCauldronSerializers() : void{
@ -520,9 +572,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)));
@ -532,10 +585,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)));
@ -583,10 +637,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)));
@ -596,10 +651,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)));
@ -633,10 +689,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)));
@ -646,8 +703,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
@ -666,30 +725,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)));
@ -698,12 +733,25 @@ 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 registerSimpleSerializers() : void{
@ -889,7 +937,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);
@ -964,16 +1012,26 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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)
->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM));
$this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create(
@ -999,7 +1057,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"),
});
});
$this->map(Blocks::AZURE_BLUET(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_HOUSTONIA));
$this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{
return Writer::create(Ids::BAMBOO)
->writeBool(StateNames::AGE_BIT, $block->isReady())
@ -1013,10 +1070,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)
@ -1066,12 +1120,10 @@ 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::BONE_BLOCK(), function(BoneBlock $block) : Writer{
return Writer::create(Ids::BONE_BLOCK)
@ -1228,22 +1280,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->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));
@ -1363,7 +1404,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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::LADDER(), function(Ladder $block) : Writer{
return Writer::create(Ids::LADDER)
@ -1403,7 +1443,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->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::LIT_PUMPKIN(), function(LitPumpkin $block) : Writer{
return Writer::create(Ids::LIT_PUMPKIN)
->writeCardinalHorizontalFacing($block->getFacing());
@ -1447,16 +1486,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::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());
@ -1492,7 +1527,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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->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)
@ -1566,7 +1600,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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));
@ -1616,15 +1649,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
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::STAINED_HARDENED_GLASS(), function(StainedHardenedGlass $block) : Writer{
return Writer::create(Ids::HARD_STAINED_GLASS)
->writeColor($block->getColor());
});
$this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), function(StainedHardenedGlassPane $block) : Writer{
return Writer::create(Ids::HARD_STAINED_GLASS_PANE)
->writeColor($block->getColor());
});
$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));
@ -1717,6 +1741,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

@ -41,6 +41,7 @@ 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;
@ -149,7 +150,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,
@ -220,6 +220,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?
@ -362,18 +368,4 @@ final class BlockStateDeserializerHelper{
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

@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
@ -39,20 +38,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"));
@ -67,7 +70,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()){
@ -81,7 +84,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();
@ -100,7 +103,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){
@ -236,30 +239,6 @@ final class BlockStateReader{
};
}
/** @throws BlockStateDeserializeException */
public function readColor() : DyeColor{
// * color (StringTag) = black, blue, brown, cyan, gray, green, light_blue, lime, magenta, orange, pink, purple, red, silver, white, yellow
return match($color = $this->readString(BlockStateNames::COLOR)){
StringValues::COLOR_BLACK => DyeColor::BLACK,
StringValues::COLOR_BLUE => DyeColor::BLUE,
StringValues::COLOR_BROWN => DyeColor::BROWN,
StringValues::COLOR_CYAN => DyeColor::CYAN,
StringValues::COLOR_GRAY => DyeColor::GRAY,
StringValues::COLOR_GREEN => DyeColor::GREEN,
StringValues::COLOR_LIGHT_BLUE => DyeColor::LIGHT_BLUE,
StringValues::COLOR_LIME => DyeColor::LIME,
StringValues::COLOR_MAGENTA => DyeColor::MAGENTA,
StringValues::COLOR_ORANGE => DyeColor::ORANGE,
StringValues::COLOR_PINK => DyeColor::PINK,
StringValues::COLOR_PURPLE => DyeColor::PURPLE,
StringValues::COLOR_RED => DyeColor::RED,
StringValues::COLOR_SILVER => DyeColor::LIGHT_GRAY,
StringValues::COLOR_WHITE => DyeColor::WHITE,
StringValues::COLOR_YELLOW => DyeColor::YELLOW,
default => throw $this->badValueException(BlockStateNames::COLOR, $color),
};
}
/** @throws BlockStateDeserializeException */
public function readCoralFacing() : int{
return $this->parseFacingValue($this->readInt(BlockStateNames::CORAL_DIRECTION), [
@ -371,7 +350,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);
}
@ -388,10 +367,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,43 +56,35 @@ use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Facing;
final class BlockStateSerializerHelper{
public static function encodeAllSidedLog(Wood $block) : BlockStateWriter{
return BlockStateWriter::create(Ids::WOOD)
->writeBool(BlockStateNames::STRIPPED_BIT, $block->isStripped())
->writePillarAxis($block->getAxis())
->writeLegacyWoodType($block->getWoodType());
}
public static function encodeButton(Button $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeButton(Button $block, Writer $out) : Writer{
return $out
->writeFacingDirection($block->getFacing())
->writeBool(BlockStateNames::BUTTON_PRESSED_BIT, $block->isPressed());
}
public static function encodeCandle(Candle $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeCandle(Candle $block, Writer $out) : Writer{
return $out
->writeBool(StateNames::LIT, $block->isLit())
->writeInt(StateNames::CANDLES, $block->getCount() - 1);
}
public static function encodeChemistryTable(ChemistryTable $block, string $chemistryTableType, BlockStateWriter $out) : BlockStateWriter{
public static function encodeChemistryTable(ChemistryTable $block, string $chemistryTableType, Writer $out) : Writer{
return $out
->writeString(BlockStateNames::CHEMISTRY_TABLE_TYPE, $chemistryTableType)
->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing()));
}
public static function encodeCrops(Crops $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeCrops(Crops $block, Writer $out) : Writer{
return $out->writeInt(BlockStateNames::GROWTH, $block->getAge());
}
public static function encodeColoredTorch(Torch $block, bool $highBit, BlockStateWriter $out) : BlockStateWriter{
public static function encodeColoredTorch(Torch $block, bool $highBit, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::COLOR_BIT, $highBit)
->writeTorchFacing($block->getFacing());
}
public static function encodeCauldron(string $liquid, int $fillLevel) : BlockStateWriter{
public static function encodeCauldron(string $liquid, int $fillLevel) : Writer{
return Writer::create(Ids::CAULDRON)
->writeString(BlockStateNames::CAULDRON_LIQUID, $liquid)
->writeInt(BlockStateNames::FILL_LEVEL, $fillLevel);
@ -107,7 +99,7 @@ final class BlockStateSerializerHelper{
};
}
public static function encodeDoor(Door $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeDoor(Door $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
->writeLegacyHorizontalFacing(Facing::rotateY($block->getFacing(), true))
@ -115,111 +107,96 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
public static function encodeDoublePlant(DoublePlant $block, string $doublePlantType, BlockStateWriter $out) : BlockStateWriter{
public static function encodeDoublePlant(DoublePlant $block, string $doublePlantType, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
->writeString(BlockStateNames::DOUBLE_PLANT_TYPE, $doublePlantType);
}
public static function encodeFenceGate(FenceGate $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
return $out
->writeLegacyHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
public static function encodeFloorSign(FloorSign $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{
return $out
->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation());
}
public static function encodeFurnace(Furnace $block, string $unlitId, string $litId) : BlockStateWriter{
return BlockStateWriter::create($block->isLit() ? $litId : $unlitId)
public static function encodeFurnace(Furnace $block, string $unlitId, string $litId) : Writer{
return Writer::create($block->isLit() ? $litId : $unlitId)
->writeCardinalHorizontalFacing($block->getFacing());
}
public static function encodeItemFrame(ItemFrame $block, string $id) : BlockStateWriter{
public static function encodeItemFrame(ItemFrame $block, string $id) : Writer{
return Writer::create($id)
->writeBool(StateNames::ITEM_FRAME_MAP_BIT, $block->hasMap())
->writeBool(StateNames::ITEM_FRAME_PHOTO_BIT, false)
->writeFacingDirection($block->getFacing());
}
public static function encodeLeaves(Leaves $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeLeaves(Leaves $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay())
->writeBool(BlockStateNames::UPDATE_BIT, $block->isCheckDecay());
}
public static function encodeLeaves1(Leaves $block, string $type) : BlockStateWriter{
return self::encodeLeaves($block, BlockStateWriter::create(Ids::LEAVES)
->writeString(BlockStateNames::OLD_LEAF_TYPE, $type));
}
public static function encodeLeaves2(Leaves $block, string $type) : BlockStateWriter{
return self::encodeLeaves($block, BlockStateWriter::create(Ids::LEAVES2)
->writeString(BlockStateNames::NEW_LEAF_TYPE, $type));
}
public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : BlockStateWriter{
return BlockStateWriter::create($block->isStill() ? $stillId : $flowingId)
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));
}
public static function encodeLog(Wood $block, string $unstrippedId, string $strippedId) : BlockStateWriter{
public static function encodeLog(Wood $block, string $unstrippedId, string $strippedId) : Writer{
$out = $block->isStripped() ?
BlockStateWriter::create($strippedId) :
BlockStateWriter::create($unstrippedId);
Writer::create($strippedId) :
Writer::create($unstrippedId);
return $out
->writePillarAxis($block->getAxis());
}
public static function encodeMushroomBlock(RedMushroomBlock $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeMushroomBlock(RedMushroomBlock $block, Writer $out) : Writer{
return $out
->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType()));
}
public static function encodeQuartz(string $type, int $axis) : BlockStateWriter{
return BlockStateWriter::create(Ids::QUARTZ_BLOCK)
public static function encodeQuartz(string $type, int $axis) : Writer{
return Writer::create(Ids::QUARTZ_BLOCK)
->writeString(BlockStateNames::CHISEL_TYPE, $type)
->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway
}
public static function encodeRedFlower(string $type) : BlockStateWriter{
return BlockStateWriter::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 encodeSandstone(string $id, string $type) : BlockStateWriter{
return BlockStateWriter::create($id)->writeString(BlockStateNames::SAND_STONE_TYPE, $type);
public static function encodeSapling(Sapling $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::AGE_BIT, $block->isReady());
}
public static function encodeSapling(Sapling $block, string $type) : BlockStateWriter{
return BlockStateWriter::create(Ids::SAPLING)
->writeBool(BlockStateNames::AGE_BIT, $block->isReady())
->writeString(BlockStateNames::SAPLING_TYPE, $type);
}
public static function encodeSimplePressurePlate(SimplePressurePlate $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeSimplePressurePlate(SimplePressurePlate $block, Writer $out) : Writer{
//TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation?
//best to keep this separate from weighted plates anyway...
return $out
->writeInt(BlockStateNames::REDSTONE_SIGNAL, $block->isPressed() ? 15 : 0);
}
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : BlockStateWriter{
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : Writer{
$slabType = $block->getSlabType();
return BlockStateWriter::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
return Writer::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
//this is (intentionally) also written for double slabs (as zero) to maintain bug parity with MCPE
->writeSlabPosition($slabType === SlabType::DOUBLE ? SlabType::BOTTOM : $slabType);
}
public static function encodeStairs(Stair $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeStairs(Stair $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown())
->writeWeirdoHorizontalFacing($block->getFacing());
}
public static function encodeStem(Stem $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeStem(Stem $block, Writer $out) : Writer{
//In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the
//most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which
//is absurd, and I refuse to make our API similarly absurd.
@ -228,40 +205,40 @@ final class BlockStateSerializerHelper{
->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing);
}
public static function encodeStoneBricks(string $type) : BlockStateWriter{
return BlockStateWriter::create(Ids::STONEBRICK)
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) : BlockStateWriter{
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) : BlockStateWriter{
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) : BlockStateWriter{
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) : BlockStateWriter{
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) : BlockStateWriter{
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, BlockStateWriter $out) : BlockStateWriter{
public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{
return $out
->write5MinusHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isTop())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
public static function encodeWall(Wall $block, BlockStateWriter $out) : BlockStateWriter{
public static function encodeWall(Wall $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::WALL_POST_BIT, $block->isPost())
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST, $block->getConnection(Facing::EAST))
@ -270,18 +247,13 @@ final class BlockStateSerializerHelper{
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST));
}
public static function encodeLegacyWall(Wall $block, string $type) : BlockStateWriter{
return self::encodeWall($block, BlockStateWriter::create(Ids::COBBLESTONE_WALL))
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, BlockStateWriter $out) : BlockStateWriter{
public static function encodeWallSign(WallSign $block, Writer $out) : Writer{
return $out
->writeHorizontalFacing($block->getFacing());
}
public static function encodeWoodenSlab(Slab $block, string $typeValue) : BlockStateWriter{
return self::encodeSlab($block, Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB)
->writeString(BlockStateNames::WOOD_TYPE, $typeValue);
}
}

View File

@ -82,8 +82,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerFlatCoralDeserializers();
$this->registerCauldronDeserializers();
$this->registerFlatWoodBlockDeserializers();
$this->registerLegacyWoodBlockDeserializers();
$this->registerLeavesDeserializers();
$this->registerSaplingDeserializers();
$this->registerSimpleDeserializers();
$this->registerDeserializers();
}
@ -185,6 +185,48 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}
private function registerFlatColorBlockDeserializers() : void{
foreach([
Ids::HARD_BLACK_STAINED_GLASS => DyeColor::BLACK,
Ids::HARD_BLUE_STAINED_GLASS => DyeColor::BLUE,
Ids::HARD_BROWN_STAINED_GLASS => DyeColor::BROWN,
Ids::HARD_CYAN_STAINED_GLASS => DyeColor::CYAN,
Ids::HARD_GRAY_STAINED_GLASS => DyeColor::GRAY,
Ids::HARD_GREEN_STAINED_GLASS => DyeColor::GREEN,
Ids::HARD_LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE,
Ids::HARD_LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY,
Ids::HARD_LIME_STAINED_GLASS => DyeColor::LIME,
Ids::HARD_MAGENTA_STAINED_GLASS => DyeColor::MAGENTA,
Ids::HARD_ORANGE_STAINED_GLASS => DyeColor::ORANGE,
Ids::HARD_PINK_STAINED_GLASS => DyeColor::PINK,
Ids::HARD_PURPLE_STAINED_GLASS => DyeColor::PURPLE,
Ids::HARD_RED_STAINED_GLASS => DyeColor::RED,
Ids::HARD_WHITE_STAINED_GLASS => DyeColor::WHITE,
Ids::HARD_YELLOW_STAINED_GLASS => DyeColor::YELLOW,
] as $id => $color){
$this->map($id, fn(Reader $in) => Blocks::STAINED_HARDENED_GLASS()->setColor($color));
}
foreach([
Ids::HARD_BLACK_STAINED_GLASS_PANE => DyeColor::BLACK,
Ids::HARD_BLUE_STAINED_GLASS_PANE => DyeColor::BLUE,
Ids::HARD_BROWN_STAINED_GLASS_PANE => DyeColor::BROWN,
Ids::HARD_CYAN_STAINED_GLASS_PANE => DyeColor::CYAN,
Ids::HARD_GRAY_STAINED_GLASS_PANE => DyeColor::GRAY,
Ids::HARD_GREEN_STAINED_GLASS_PANE => DyeColor::GREEN,
Ids::HARD_LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE,
Ids::HARD_LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY,
Ids::HARD_LIME_STAINED_GLASS_PANE => DyeColor::LIME,
Ids::HARD_MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA,
Ids::HARD_ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE,
Ids::HARD_PINK_STAINED_GLASS_PANE => DyeColor::PINK,
Ids::HARD_PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE,
Ids::HARD_RED_STAINED_GLASS_PANE => DyeColor::RED,
Ids::HARD_WHITE_STAINED_GLASS_PANE => DyeColor::WHITE,
Ids::HARD_YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW,
] as $id => $color){
$this->map($id, fn(Reader $in) => Blocks::STAINED_HARDENED_GLASS_PANE()->setColor($color));
}
foreach([
Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK,
Ids::BLUE_GLAZED_TERRACOTTA => DyeColor::BLUE,
@ -397,6 +439,17 @@ 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));
}
}
private function registerCauldronDeserializers() : void{
@ -426,10 +479,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));
@ -439,10 +493,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));
@ -484,10 +539,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));
@ -497,10 +553,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));
@ -529,10 +586,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));
@ -542,10 +600,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));
@ -562,40 +621,30 @@ 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 registerSimpleDeserializers() : void{
@ -779,7 +828,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());
@ -856,6 +905,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
$this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE());
$this->mapSimple(Ids::YELLOW_FLOWER, 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{
@ -897,7 +958,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{
@ -1054,10 +1114,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->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)
@ -1159,14 +1215,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5));
});
$this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS());
$this->map(Ids::HARD_STAINED_GLASS, function(Reader $in) : Block{
return Blocks::STAINED_HARDENED_GLASS()
->setColor($in->readColor());
});
$this->map(Ids::HARD_STAINED_GLASS_PANE, function(Reader $in) : Block{
return Blocks::STAINED_HARDENED_GLASS_PANE()
->setColor($in->readColor());
});
$this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{
$in->ignored(StateNames::DEPRECATED);
return Blocks::HAY_BALE()->setAxis($in->readPillarAxis());
@ -1397,22 +1445,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
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->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS());
$this->map(Ids::RED_SANDSTONE, function(Reader $in) : Block{
@ -1463,18 +1495,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
};
});
$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::SEA_PICKLE, function(Reader $in) : Block{
return Blocks::SEA_PICKLE()
->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1)

View File

@ -25,10 +25,8 @@ namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor;
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;
@ -193,29 +191,6 @@ final class BlockStateWriter{
});
}
/** @return $this */
public function writeColor(DyeColor $color) : self{
$this->writeString(BlockStateNames::COLOR, match($color){
DyeColor::BLACK => StringValues::COLOR_BLACK,
DyeColor::BLUE => StringValues::COLOR_BLUE,
DyeColor::BROWN => StringValues::COLOR_BROWN,
DyeColor::CYAN => StringValues::COLOR_CYAN,
DyeColor::GRAY => StringValues::COLOR_GRAY,
DyeColor::GREEN => StringValues::COLOR_GREEN,
DyeColor::LIGHT_BLUE => StringValues::COLOR_LIGHT_BLUE,
DyeColor::LIGHT_GRAY => StringValues::COLOR_SILVER,
DyeColor::LIME => StringValues::COLOR_LIME,
DyeColor::MAGENTA => StringValues::COLOR_MAGENTA,
DyeColor::ORANGE => StringValues::COLOR_ORANGE,
DyeColor::PINK => StringValues::COLOR_PINK,
DyeColor::PURPLE => StringValues::COLOR_PURPLE,
DyeColor::RED => StringValues::COLOR_RED,
DyeColor::WHITE => StringValues::COLOR_WHITE,
DyeColor::YELLOW => StringValues::COLOR_YELLOW,
});
return $this;
}
/** @return $this */
public function writeCoralFacing(int $value) : self{
$this->writeInt(BlockStateNames::CORAL_DIRECTION, match($value){
@ -281,20 +256,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){

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
@ -389,6 +391,9 @@ final class BlockStateUpgradeSchemaUtils{
}
$jsonMapper = new \JsonMapper();
$jsonMapper->bExceptionOnMissingData = true;
$jsonMapper->bExceptionOnUndefinedProperty = true;
$jsonMapper->bStrictObjectTypeChecking = true;
try{
$model = $jsonMapper->map($json, new BlockStateUpgradeSchemaModel());
}catch(\JsonMapper_Exception $e){

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

@ -147,7 +147,6 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Block(Ids::JUNGLE_DOOR, Blocks::JUNGLE_DOOR());
$this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR());
$this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART());
$this->map1to1Block(Ids::PITCHER_POD, Blocks::PITCHER_CROP());
$this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER());
$this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR());
$this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE());
@ -193,6 +192,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::CLAY_BALL, Items::CLAY());
$this->map1to1Item(Ids::CLOCK, Items::CLOCK());
$this->map1to1Item(Ids::COAL, Items::COAL());
$this->map1to1Item(Ids::COAST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::COAST_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::COCOA_BEANS, Items::COCOA_BEANS());
$this->map1to1Item(Ids::COD, Items::RAW_FISH());
$this->map1to1Item(Ids::COMPASS, Items::COMPASS());
@ -221,6 +221,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::DISC_FRAGMENT_5, Items::DISC_FRAGMENT_5());
$this->map1to1Item(Ids::DRAGON_BREATH, Items::DRAGON_BREATH());
$this->map1to1Item(Ids::DRIED_KELP, Items::DRIED_KELP());
$this->map1to1Item(Ids::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::ECHO_SHARD, Items::ECHO_SHARD());
$this->map1to1Item(Ids::EGG, Items::EGG());
$this->map1to1Item(Ids::EMERALD, Items::EMERALD());
@ -228,6 +229,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE());
$this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL());
$this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE());
$this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::FEATHER, Items::FEATHER());
$this->map1to1Item(Ids::FERMENTED_SPIDER_EYE, Items::FERMENTED_SPIDER_EYE());
$this->map1to1Item(Ids::FIRE_CHARGE, Items::FIRE_CHARGE());
@ -257,6 +259,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::HEART_OF_THE_SEA, Items::HEART_OF_THE_SEA());
$this->map1to1Item(Ids::HONEY_BOTTLE, Items::HONEY_BOTTLE());
$this->map1to1Item(Ids::HONEYCOMB, Items::HONEYCOMB());
$this->map1to1Item(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::INK_SAC, Items::INK_SAC());
$this->map1to1Item(Ids::IRON_AXE, Items::IRON_AXE());
$this->map1to1Item(Ids::IRON_BOOTS, Items::IRON_BOOTS());
@ -302,6 +305,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::MUSIC_DISC_WAIT, Items::RECORD_WAIT());
$this->map1to1Item(Ids::MUSIC_DISC_WARD, Items::RECORD_WARD());
$this->map1to1Item(Ids::MUTTON, Items::RAW_MUTTON());
$this->map1to1Item(Ids::NAME_TAG, Items::NAME_TAG());
$this->map1to1Item(Ids::NAUTILUS_SHELL, Items::NAUTILUS_SHELL());
$this->map1to1Item(Ids::NETHER_STAR, Items::NETHER_STAR());
$this->map1to1Item(Ids::NETHERBRICK, Items::NETHER_BRICK());
@ -316,11 +320,13 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::NETHERITE_SCRAP, Items::NETHERITE_SCRAP());
$this->map1to1Item(Ids::NETHERITE_SHOVEL, Items::NETHERITE_SHOVEL());
$this->map1to1Item(Ids::NETHERITE_SWORD, Items::NETHERITE_SWORD());
$this->map1to1Item(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::OAK_BOAT, Items::OAK_BOAT());
$this->map1to1Item(Ids::OAK_SIGN, Items::OAK_SIGN());
$this->map1to1Item(Ids::PAINTING, Items::PAINTING());
$this->map1to1Item(Ids::PAPER, Items::PAPER());
$this->map1to1Item(Ids::PHANTOM_MEMBRANE, Items::PHANTOM_MEMBRANE());
$this->map1to1Item(Ids::PITCHER_POD, Items::PITCHER_POD());
$this->map1to1Item(Ids::POISONOUS_POTATO, Items::POISONOUS_POTATO());
$this->map1to1Item(Ids::POPPED_CHORUS_FRUIT, Items::POPPED_CHORUS_FRUIT());
$this->map1to1Item(Ids::PORKCHOP, Items::RAW_PORKCHOP());
@ -335,18 +341,25 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::RABBIT_FOOT, Items::RABBIT_FOOT());
$this->map1to1Item(Ids::RABBIT_HIDE, Items::RABBIT_HIDE());
$this->map1to1Item(Ids::RABBIT_STEW, Items::RABBIT_STEW());
$this->map1to1Item(Ids::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::RAW_COPPER, Items::RAW_COPPER());
$this->map1to1Item(Ids::RAW_GOLD, Items::RAW_GOLD());
$this->map1to1Item(Ids::RAW_IRON, Items::RAW_IRON());
$this->map1to1Item(Ids::REDSTONE, Items::REDSTONE_DUST());
$this->map1to1Item(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::ROTTEN_FLESH, Items::ROTTEN_FLESH());
$this->map1to1Item(Ids::SALMON, Items::RAW_SALMON());
$this->map1to1Item(Ids::SCUTE, Items::SCUTE());
$this->map1to1Item(Ids::TURTLE_SCUTE, Items::SCUTE());
$this->map1to1Item(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SHEARS, Items::SHEARS());
$this->map1to1Item(Ids::SHULKER_SHELL, Items::SHULKER_SHELL());
$this->map1to1Item(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SLIME_BALL, Items::SLIMEBALL());
$this->map1to1Item(Ids::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SNOWBALL, Items::SNOWBALL());
$this->map1to1Item(Ids::SPIDER_EYE, Items::SPIDER_EYE());
$this->map1to1Item(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SPRUCE_BOAT, Items::SPRUCE_BOAT());
$this->map1to1Item(Ids::SPRUCE_SIGN, Items::SPRUCE_SIGN());
$this->map1to1Item(Ids::SPYGLASS, Items::SPYGLASS());
@ -361,14 +374,19 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::SUGAR, Items::SUGAR());
$this->map1to1Item(Ids::SWEET_BERRIES, Items::SWEET_BERRIES());
$this->map1to1Item(Ids::TORCHFLOWER_SEEDS, Items::TORCHFLOWER_SEEDS());
$this->map1to1Item(Ids::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::TOTEM_OF_UNDYING, Items::TOTEM());
$this->map1to1Item(Ids::TROPICAL_FISH, Items::CLOWNFISH());
$this->map1to1Item(Ids::TURTLE_HELMET, Items::TURTLE_HELMET());
$this->map1to1Item(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE, Items::VEX_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::VILLAGER_SPAWN_EGG, Items::VILLAGER_SPAWN_EGG());
$this->map1to1Item(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::WARPED_SIGN, Items::WARPED_SIGN());
$this->map1to1Item(Ids::WATER_BUCKET, Items::WATER_BUCKET());
$this->map1to1Item(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::WHEAT, Items::WHEAT());
$this->map1to1Item(Ids::WHEAT_SEEDS, Items::WHEAT_SEEDS());
$this->map1to1Item(Ids::WILD_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WILD_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::WOODEN_AXE, Items::WOODEN_AXE());
$this->map1to1Item(Ids::WOODEN_HOE, Items::WOODEN_HOE());
$this->map1to1Item(Ids::WOODEN_PICKAXE, Items::WOODEN_PICKAXE());

View File

@ -38,6 +38,8 @@ final class ItemTypeNames{
public const ANGLER_POTTERY_SHERD = "minecraft:angler_pottery_sherd";
public const APPLE = "minecraft:apple";
public const ARCHER_POTTERY_SHERD = "minecraft:archer_pottery_sherd";
public const ARMADILLO_SCUTE = "minecraft:armadillo_scute";
public const ARMADILLO_SPAWN_EGG = "minecraft:armadillo_spawn_egg";
public const ARMOR_STAND = "minecraft:armor_stand";
public const ARMS_UP_POTTERY_SHERD = "minecraft:arms_up_pottery_sherd";
public const ARROW = "minecraft:arrow";
@ -72,6 +74,8 @@ final class ItemTypeNames{
public const BLEACH = "minecraft:bleach";
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";
@ -79,6 +83,8 @@ 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";
@ -137,6 +143,8 @@ 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_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";
@ -151,7 +159,6 @@ final class ItemTypeNames{
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
public const DARK_OAK_SIGN = "minecraft:dark_oak_sign";
public const DEBUG_STICK = "minecraft:debug_stick";
public const DIAMOND = "minecraft:diamond";
public const DIAMOND_AXE = "minecraft:diamond_axe";
public const DIAMOND_BOOTS = "minecraft:diamond_boots";
@ -201,6 +208,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";
@ -238,6 +248,10 @@ final class ItemTypeNames{
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";
public const HEART_POTTERY_SHERD = "minecraft:heart_pottery_sherd";
public const HEARTBREAK_POTTERY_SHERD = "minecraft:heartbreak_pottery_sherd";
@ -281,6 +295,8 @@ 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_BLUE_DYE = "minecraft:light_blue_dye";
public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye";
public const LIME_DYE = "minecraft:lime_dye";
@ -289,6 +305,7 @@ final class ItemTypeNames{
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_DYE = "minecraft:magenta_dye";
public const MAGMA_CREAM = "minecraft:magma_cream";
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
@ -396,6 +413,7 @@ final class ItemTypeNames{
public const RAW_IRON = "minecraft:raw_iron";
public const RECOVERY_COMPASS = "minecraft:recovery_compass";
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";
@ -404,7 +422,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 SCUTE = "minecraft:scute";
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";
@ -466,11 +485,13 @@ final class ItemTypeNames{
public const TORCHFLOWER_SEEDS = "minecraft:torchflower_seeds";
public const TOTEM_OF_UNDYING = "minecraft:totem_of_undying";
public const TRADER_LLAMA_SPAWN_EGG = "minecraft:trader_llama_spawn_egg";
public const TRIAL_KEY = "minecraft:trial_key";
public const TRIDENT = "minecraft:trident";
public const TROPICAL_FISH = "minecraft:tropical_fish";
public const TROPICAL_FISH_BUCKET = "minecraft:tropical_fish_bucket";
public const TROPICAL_FISH_SPAWN_EGG = "minecraft:tropical_fish_spawn_egg";
public const TURTLE_HELMET = "minecraft:turtle_helmet";
public const TURTLE_SCUTE = "minecraft:turtle_scute";
public const TURTLE_SPAWN_EGG = "minecraft:turtle_spawn_egg";
public const VEX_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:vex_armor_trim_smithing_template";
public const VEX_SPAWN_EGG = "minecraft:vex_spawn_egg";
@ -494,15 +515,19 @@ final class ItemTypeNames{
public const WHEAT_SEEDS = "minecraft:wheat_seeds";
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";

View File

@ -88,6 +88,9 @@ final class ItemIdMetaUpgradeSchemaUtils{
}
$jsonMapper = new \JsonMapper();
$jsonMapper->bExceptionOnMissingData = true;
$jsonMapper->bExceptionOnUndefinedProperty = true;
$jsonMapper->bStrictObjectTypeChecking = true;
try{
$model = $jsonMapper->map($json, new ItemIdMetaUpgradeSchemaModel());
}catch(\JsonMapper_Exception $e){

View File

@ -44,10 +44,14 @@ final class GameModeIdMap{
private array $enumToId = [];
public function __construct(){
$this->register(0, GameMode::SURVIVAL);
$this->register(1, GameMode::CREATIVE);
$this->register(2, GameMode::ADVENTURE);
$this->register(3, GameMode::SPECTATOR);
foreach(GameMode::cases() as $case){
$this->register(match($case){
GameMode::SURVIVAL => 0,
GameMode::CREATIVE => 1,
GameMode::ADVENTURE => 2,
GameMode::SPECTATOR => 3,
}, $case);
}
}
private function register(int $id, GameMode $type) : void{

View File

@ -29,11 +29,6 @@ namespace pocketmine\data\runtime;
* @deprecated
*/
trait LegacyRuntimeEnumDescriberTrait{
/**
* @phpstan-template T of \UnitEnum
* @phpstan-param T $case
*/
abstract protected function enum(\UnitEnum &$case) : void;
public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{

View File

@ -89,10 +89,6 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{
public function straightOnlyRailShape(int &$railShape) : void;
/**
* @phpstan-template T of \UnitEnum
* @phpstan-param T $case
*/
public function enum(\UnitEnum &$case) : void;
/**

View File

@ -252,6 +252,14 @@ abstract class Entity{
return $this->alwaysShowNameTag;
}
/**
* Returns whether players can rename this entity using a name tag.
* Note that plugins can still name entities using setNameTag().
*/
public function canBeRenamed() : bool{
return false;
}
public function setNameTag(string $name) : void{
$this->nameTag = $name;
$this->networkPropertiesDirty = true;
@ -792,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

@ -132,6 +132,10 @@ abstract class Living extends Entity{
abstract public function getName() : string;
public function canBeRenamed() : bool{
return true;
}
protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt);

View File

@ -173,8 +173,9 @@ abstract class Projectile extends Entity{
$entityHit = null;
$hitResult = null;
$world = $this->getWorld();
foreach(VoxelRayTrace::betweenPoints($start, $end) as $vector3){
$block = $this->getWorld()->getBlockAt($vector3->x, $vector3->y, $vector3->z);
$block = $world->getBlockAt($vector3->x, $vector3->y, $vector3->z);
$blockHitResult = $this->calculateInterceptWithBlock($block, $start, $end);
if($blockHitResult !== null){
@ -188,7 +189,7 @@ abstract class Projectile extends Entity{
$entityDistance = PHP_INT_MAX;
$newDiff = $end->subtractVector($start);
foreach($this->getWorld()->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expand(1, 1, 1), $this) as $entity){
foreach($world->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expand(1, 1, 1), $this) as $entity){
if($entity->getId() === $this->getOwningEntityId() && $this->ticksLived < 5){
continue;
}
@ -256,7 +257,7 @@ abstract class Projectile extends Entity{
);
}
$this->getWorld()->onEntityMoved($this);
$world->onEntityMoved($this);
$this->checkBlockIntersections();
Timings::$projectileMove->stopTiming();

View File

@ -31,8 +31,9 @@ class Snowball extends Throwable{
public static function getNetworkTypeId() : string{ return EntityIds::SNOWBALL; }
protected function onHit(ProjectileHitEvent $event) : void{
$world = $this->getWorld();
for($i = 0; $i < 6; ++$i){
$this->getWorld()->addParticle($this->location, new SnowballPoofParticle());
$world->addParticle($this->location, new SnowballPoofParticle());
}
}
}

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