Compare commits

...

350 Commits

Author SHA1 Message Date
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
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
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
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
72f3c0b4b9 NetworkSession: fixed timings not being stopped when handling uncompressed packets 2024-03-01 17:36:40 +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
46307973e3 Release 5.9.0 2023-12-06 15:02:57 +00:00
2f1d6115a0 Merge branch 'legacy/pm4' into stable 2023-12-06 14:55:18 +00:00
ba89ae5bf2 4.26.1 is next 2023-12-06 14:53:00 +00:00
30433bba10 Release 4.26.0 2023-12-06 14:52:59 +00:00
ed3fe2b727 Update dependencies for 1.20.50 2023-12-06 14:47:44 +00:00
927f129c6e Fixed borked world loading 2023-12-06 14:41:19 +00:00
2a136c7804 Update composer dependencies 2023-12-06 14:37:27 +00:00
25cca1b63f Changes for 1.20.50 2023-12-06 14:29:23 +00:00
15574ec99a Update composer dependencies 2023-12-06 14:28:30 +00:00
2420dee8be AsyncTask: Fix retrieval of null data from the thread-local storage (#6176) 2023-12-06 13:40:09 +00:00
bd65948453 Bump phpstan/phpstan from 1.10.46 to 1.10.47 (#6189)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.46 to 1.10.47.
- [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.46...1.10.47)

---
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-06 13:29:40 +00:00
0984aa670d Bump phpstan/phpstan from 1.10.44 to 1.10.46 (#6182)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.44 to 1.10.46.
- [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.44...1.10.46)

---
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-11-29 16:33:04 +00:00
239f9ed83a Bump shivammathur/setup-php from 2.27.1 to 2.28.0 (#6179)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.27.1 to 2.28.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.27.1...2.28.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-29 16:32:42 +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
bc11894f0a Bump build/php from 6b4c9c7 to b0ffbdb (#6178)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `6b4c9c7` to `b0ffbdb`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](6b4c9c76bd...b0ffbdbe33)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-27 11:01:36 +00:00
d51475dc72 Bump docker/build-push-action from 5.0.0 to 5.1.0 (#6169)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.0.0 to 5.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.0.0...v5.1.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>
2023-11-24 16:31:34 +00:00
233c8b746d Bump phpstan/phpstan from 1.10.41 to 1.10.44 (#6172)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.41 to 1.10.44.
- [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.41...1.10.44)

---
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-11-24 16:31:18 +00:00
c1f0f13d5a Add PHP 8.3 to test matrix 2023-11-24 16:29:27 +00:00
06e2d36294 Bump build/php from 19f2ee6 to 6b4c9c7 (#6171)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `19f2ee6` to `6b4c9c7`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](19f2ee6d33...6b4c9c76bd)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-22 11:06:32 +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
67ad2bad17 World: fixed edge case that could lead to crash during block update sending 2023-11-17 13:24:06 +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
a25597ca30 Server: updated documentation for prepareBatch() 2023-11-17 12:01:19 +00:00
ace
89fbb3fd0d Fix loading of Item Frame item rotation (#6123) 2023-11-16 10:16:29 +00:00
e9c5846a06 World: simplify condition 2023-11-16 10:06:43 +00:00
886ed60e6a Bump build/php from 3c3c483 to 19f2ee6 (#6163)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `3c3c483` to `19f2ee6`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](3c3c483baa...19f2ee6d33)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 09:39:26 +00:00
8f107e785b Bump dessant/support-requests from 3 to 4 (#6160)
Bumps [dessant/support-requests](https://github.com/dessant/support-requests) from 3 to 4.
- [Release notes](https://github.com/dessant/support-requests/releases)
- [Changelog](https://github.com/dessant/support-requests/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dessant/support-requests/compare/v3...v4)

---
updated-dependencies:
- dependency-name: dessant/support-requests
  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-11-15 17:06:23 +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
0ad6429fee Fix RegistryTrait documentation (#6153) 2023-11-13 11:15:07 +00:00
923c922960 Merge branch 'stable' into minor-next 2023-11-13 11:13:12 +00:00
77590fb63a Server: fixed prepareBatch() not being marked as internal 2023-11-13 11:12:12 +00:00
bd43ff6579 Update README.md
[ci skip]
2023-11-10 16:27:57 +00:00
c2189bc2df Update README.md
[ci skip]
2023-11-10 16:16:49 +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
c44758f36c StringToItemParser: added pitcher_plant and pitcher_pod
it seems a bit weird to map pitcher_pod to PITCHER_CROP(). Perhaps this wasn't implemented correctly.
2023-11-10 15:26:02 +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
93699024da 5.8.3 is next 2023-11-09 18:51:20 +00:00
c3c81b09e8 Release 5.8.2 2023-11-09 18:51:17 +00:00
08f9873c32 Update build/php submodule to pmmp/PHP-Binaries@3c3c483baa 2023-11-09 18:30:03 +00:00
50592dc269 Merge branch 'stable' into minor-next 2023-11-09 18:05:38 +00:00
e3700cab50 ZlibCompressor: use libdeflate for level 0 compression
this is supported since libdeflate 1.15 and ext-libdeflate 0.2.0.
Everyone should be using these versions by now anyway, and if they aren't, they should update.

libdeflate's level 0 compression is over 20 times faster than zlib, so this is a nice performance improvement.
2023-11-09 18:05:07 +00:00
4103631bc1 Added Smithing Template items (#6132) 2023-11-09 14:25:49 +00:00
ace
c1ed182112 Fix loading of sign text from vanilla world (#6122) 2023-11-09 14:22:37 +00:00
5f3a2a5096 BlockStateUpgrader: fixed undefined array key when flattening a block whose new ID is the same as the old
this came up during 1.20.50 testing, where minecraft:stone[stone_type=stone] continues to have an ID minecraft:stone without the stone_type property.
This could have appeared in other ways, such as with an invalid legacy blockstate.
2023-11-08 18:52:22 +00:00
8ccaf907d1 tools/generate-blockstate-upgrade-schema: do not optimize state remaps with unchanged IDs
processRemappedStates() needs to know about the full set of states to generate reliable mappings.
Without it, it may generate flattening rules or state matching criteria that unintentionally match states that it's not aware of.
Sadly, this does make some schemas bigger, but it's for the best.
2023-11-08 15:19:02 +00:00
6b5c405939 Bump shivammathur/setup-php from 2.26.0 to 2.27.1 (#6142)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.26.0 to 2.27.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.26.0...2.27.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-08 12:06:43 +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
9fc9609694 Fix ancient debris not being fireproof (#6138) 2023-11-06 15:01:43 +00:00
1055b7580a Bump phpstan/phpstan from 1.10.40 to 1.10.41 (#6139)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.40 to 1.10.41.
- [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.40...1.10.41)

---
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-11-06 15:00:06 +00:00
3385087c56 Fix Iron & Gold ores not affected by fortune (#6134) 2023-11-04 10:57:53 +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
1e4a1565bb Stem: fixed not attaching to grown pumpkin/melon
no idea how this got missed ...
2023-11-02 14:48:45 +00:00
8aaa6dd176 Stem: do not disable ticking when the stem is not attached to a pumpkin
fixes #6131
2023-11-02 14:47:55 +00:00
07dff9c9e8 5.8.2 is next 2023-11-01 17:55:23 +00:00
75a39491be Release 5.8.1 2023-11-01 17:55:22 +00:00
a10e4b6481 Merge branch 'minor-next' into stable 2023-11-01 17:51:52 +00:00
68c6b87678 5.8.1 is next 2023-11-01 17:49:56 +00:00
e20c031aa1 Release 5.8.0 2023-11-01 17:49:53 +00:00
9832fe899f Merge branch 'stable' into minor-next 2023-11-01 16:39:44 +00:00
55f3477ed9 5.7.2 is next 2023-11-01 16:37:46 +00:00
2c17f82eb8 Release 5.7.1 2023-11-01 16:37:46 +00:00
e6e2c54ec9 Fixed various reentrant-unsafe 2D array element unsets (similar to previous commit)
this pattern was used in various places
2023-11-01 16:28:59 +00:00
abce512860 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-11-01 16:13:44 +00:00
0093732d49 PermissionManager: fixed non-reentrant-safe permission unsubscribing
during unset(), the destructors for other objects with cyclic references can get triggered, resulting in the functions being reentered before the count() call. This leads to a crash because the offset no longer exists.
Instead, we check if only the given PermissibleInternal is present, and clean everything up with a single unset instead of two.
This could also have been solved by adding extra isset() checks before checking the counts, but this way seemed more elegant.

This is similar to an issue with AsyncTask thread-local storage a few months ago, which was also caused by GC reentrancy.

closes #6119
2023-11-01 16:13:28 +00:00
9eb2a46942 World: remove useless isChunkLoaded checks
getChunkEntities() will return an empty array if the chunk isn't loaded anyway, so this is just wasting CPU cycles.
2023-11-01 15:53:37 +00:00
1402571055 Bump phpstan/phpstan from 1.10.39 to 1.10.40 (#6126)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.39 to 1.10.40.
- [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.39...1.10.40)

---
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-11-01 15:26:46 +00:00
34bb86d2bf Bump phpstan/phpstan-strict-rules from 1.5.1 to 1.5.2 (#6125)
Bumps [phpstan/phpstan-strict-rules](https://github.com/phpstan/phpstan-strict-rules) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/phpstan/phpstan-strict-rules/releases)
- [Commits](https://github.com/phpstan/phpstan-strict-rules/compare/1.5.1...1.5.2)

---
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>
2023-11-01 15:26:30 +00:00
b41960dfec Merge branch 'stable' into minor-next 2023-10-26 12:55:47 +01:00
0b2fc84827 5.7.1 is next 2023-10-26 12:51:45 +01:00
22b9e70372 Release 5.7.0 2023-10-26 12:51:45 +01:00
a222636476 Merge branch 'legacy/pm4' into stable 2023-10-26 12:47:19 +01:00
fb586cc562 4.25.1 is next 2023-10-26 12:43:16 +01:00
f3f22ba48b Release 4.25.0 2023-10-26 12:43:16 +01:00
a2e6e2e5b9 Update PHPStan 2023-10-26 12:39:45 +01:00
1aaaadb909 1.20.40 changes (PM4) 2023-10-26 12:36:12 +01:00
53a740433f Changes for 1.20.40 2023-10-26 12:32:59 +01:00
8491d3c6c0 Merge branch 'stable' into minor-next 2023-10-24 11:56:51 +01:00
d637370b83 Wipe internal block AABB cache only for specific blocks that need it
the vast majority of blocks don't need this cache erasing, so it's costing performance for no good reason.
2023-10-23 17:38:04 +01:00
f655eda3b3 Player: bypass slow function call
for some reason the isSpectator() call here can take upwards of 2 microseconds, for no obvious reason. Subsequent calls are much faster, so I think there's some weird cache thing going on here.
2023-10-23 16:05:13 +01:00
af432c1a7f Give neighbour block updates its own timer
this way we aren't conflating them with scheduled updates, which are usually caused by e.g. water
2023-10-23 12:33:36 +01:00
9fcc9f4338 StringToItemParser: added missing blocks
closes #6108
2023-10-23 12:24:02 +01:00
41c5f63565 World: remove dead code 2023-10-23 12:17:41 +01:00
73b1fba53c Fixed Promise<null> calling rejection handler given after being successfully resolved
closes #6110

this is a weird use case, but it should work nonetheless.
2023-10-23 11:46:08 +01:00
8e17aed4f4 Fix build 2023-10-20 17:43:04 +01:00
1f461977d4 Block: Avoid useless Vector3 allocations in getHorizontalSides and getAllSides 2023-10-20 17:28:19 +01:00
e4888d7102 ÂCONTRIBUTING.md: restructure, reword and reorganize
[ci skip]
2023-10-20 12:12:14 +01:00
450ad42202 Added some new recommendations to CONTRIBUTING.md 2023-10-20 11:37:58 +01:00
eb935ca80f ÂLeaves: use a constant for max distance from wood 2023-10-20 11:20:30 +01:00
1c5d3b43be 5.6.2 is next 2023-10-20 10:30:50 +01:00
decc188302 Release 5.6.1 2023-10-20 10:30:50 +01:00
8fa5c7cdab World: do not apply fake state properties from tile if the block doesn't expect this tile type
This was causing a variety of crashes due to incorrect tiles, presumably from PM3 worlds.
2023-10-20 10:28:46 +01:00
7dd3a70d2e Revert "World: discard tiles on load if they aren't the correct type or no tile is expected"
This reverts commit 8f804f6f34.

This change is too disruptive, since popular plugins like
ExtendedBlocks and ExtendedBlocksConverter relied on custom tiles.
Deleting them at this stage would prevent these plugins from working,
making it impossible to upgrade old data.

An alternative solution to this problem will need to be developed.
2023-10-20 10:16:49 +01:00
dbb5a32a96 Liquid: eliminate unnecessary Position allocations and getSide() calls 2023-10-19 17:09:13 +01:00
9474324f75 Liquid: simplify code 2023-10-19 17:02:40 +01:00
ada37899aa Liquid: improve code legibility and fix a bunch of PHPStan errors 2023-10-19 17:02:22 +01:00
f1440324a7 Update PHPStan baselines 2023-10-19 16:56:15 +01:00
73659318f6 World: Avoid unnecessary Vector3 usages in neighbour block update scheduling
The old code was allocating 6 Vector3s which were all immediately discarded. In addition, we didn't need to take the performance hit of reading Vector3 properties when we could have just passed integers directly.
The real performance difference of this is likely to be close to zero, but it's still worth doing.
2023-10-19 16:46:52 +01:00
f868c1d8c6 Liquid: Update legacy code with Facing::OFFSET 2023-10-19 16:38:34 +01:00
114f444ec3 Update PHPStan baseline 2023-10-19 13:28:40 +01:00
19a1792184 Fixed behavioural change on invalid Facing introduced by previous commit
while this shouldn't be given invalid facings anyway, Vector3::getSide() accepts them and just returns itself.
2023-10-19 13:27:44 +01:00
6a3ec70c72 ÂBlock: use Facing::OFFSET in getSide()
instead of the comically inefficient getBlock() + throwaway Position->getSide()
This improved the function's performance by 2.3x.
2023-10-19 13:25:32 +01:00
ccd2cdd324 World: improve performance of calculating non-cached AABBs for a cell
Avoiding getSide() improved the performance by 2x ...
2023-10-19 13:13:46 +01:00
c7a358a56f World: extract getBlockCollisionBoxes() from getCollisionBoxes()
closes #6104

This function has been a footgun for anyone using it, since it also returns entity AABBs by default.
In all core use cases, this functionality was disabled, and we were paying a needless (admittedly micro) performance penalty for passing the extra useless argument and useless condition check.
2023-10-19 12:52:24 +01:00
b3390458b4 Bump phpstan/phpstan from 1.10.38 to 1.10.39 (#6103)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.38 to 1.10.39.
- [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.38...1.10.39)

---
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-10-19 11:58:27 +01:00
b4d55e4384 Fixed dirt and grass block interactions when clicking on sides other than the top (#6071) 2023-10-18 10:40:01 +01:00
932116fa52 Server: re-added @see reference that somehow got deleted
this is useful when clicking through references in the call stack.
2023-10-18 10:23:02 +01:00
7b5c30bc2c WoodenDoor can be a fuel (#6101) 2023-10-17 22:10:08 +01:00
c14eb63f9b Wooden Button can be a fuel (#6099) 2023-10-17 16:30:35 +01:00
48dcf0e32c Farmland: Remember relative location of nearby water in blockstate data (#6090)
Farmland can end up scanning up to 162 blocks looking for water in the worst case. This is obviously not great for huge farms where there are thousands of blocks of the stuff.

In most farms, the water won't be moved, and nor will the farmland. This means that we can avoid this costly search on random updates.

This PR implements a cache using blockstate data (only possible in PM5) which stores an index mapping to a coordinate offset where water was previously found by this farmland block. This allows the farmland to avoid water searching entirely in most cases.

This is a colossal improvement as compared to scanning the whole 9x2x9 area every time, which, on average, scans about 40 blocks to find water if the water is at the same Y coordinate. In real terms this translates into about a 8x performance improvement for farmland (see timings below).
2023-10-17 16:25:13 +01:00
7f3de835e4 Block: private internal function 2023-10-17 15:23:39 +01:00
63fcf9879a Block: Use xxhash'd XOR mask to improve state data lower bits distribution
xxhash is generally well known for its hash key properties, so this is a suitable use case.
We XOR the state data with a partial hash of xxh3(typeID), which provides sufficient hash distribution regardless of the size of state data.
The previous method started to break down as the number of bits exceeded the number of significant bits of type ID (about 10 currently).

As well as being better for hash distribution regardless of state data size, this also reduces the load factor of RuntimeBlockRegistry to 1.08 (previously around 1.24), which is a nice bonus.
2023-10-17 15:21:22 +01:00
224a69b11a Sign can be a fuel (#6095) 2023-10-17 12:22:20 +01:00
d0d16cdeb7 RuntimeDataDescriber: Introduce boundedIntAuto, replacing boundedInt
closes #6096
boundedIntAuto automatically calculates the correct number of bits to use based on the given bounds. The bounds must be constant, of course.
2023-10-17 12:03:43 +01:00
18b711aca8 Apparently this function is no longer needed 2023-10-17 11:34:02 +01:00
b0936a50c1 Block: split generateStatePermutations into type and state parts
this makes it marginally faster, since we can skip all permutations containing invalid type data.
I measured a performance improvement of about 20% across all blocks.
In addition, this makes it easier to locate where a problem is coming from if invalid inputs are accepted.
2023-10-17 11:28:58 +01:00
ace
82d6fc3890 Fix loading of Chiseled Bookshelf tile from vanilla world (#6084)
Co-authored-by: Dylan T <dktapps@pmmp.io>
2023-10-17 11:10:36 +01:00
3c614b505d Merge branch 'stable' into minor-next 2023-10-16 21:28:59 +01:00
15ba642258 Merge branch 'legacy/pm4' into stable 2023-10-16 21:28:48 +01:00
edea793a98 Downgrade PHPUnit to 10.3 until sebastianbergmann/phpunit#5539 is fixed 2023-10-16 21:25:53 +01:00
1da7e3586b Updated composer dependencies 2023-10-16 20:45:44 +01:00
538b698a00 Revert "World: specialize nearby entity updating for block updates"
This reverts commit 128eb500eb.

This breaks when entities in neighbouring chunks overlap into the
current chunk without actually being tracked by it.
Perhaps it might be worth having entities tracked by all chunks their
AABB touches in the future, so that we don't have to check padding
chunks and waste CPU time.
2023-10-14 19:43:46 +01:00
128eb500eb World: specialize nearby entity updating for block updates
this slashes the cost of checking this with no entities by 50%, which should be the common case for farms and such.
once factoring in other things, this translates into a real-world performance gain of about 15% for block updates.
2023-10-13 17:35:47 +01:00
7d200247f8 Cactus: do not update if only age changed
I'd prefer a smarter solution for this that automatically disables updates depending on which type of property was changed, but for now, this will significantly improve the performance of cactus farms.
The newly placed cactus block at the top cannot have updates disabled, though, since it needs to check its surroundings in case it grew into a space with a solid block next to it.

Thanks @KingOfTurkey38 for bringing this to light.
2023-10-13 12:45:11 +01:00
ace
8b52a5cd9e Fix book placed in wrong slot in Chiseled Bookshelf (#6085) 2023-10-10 12:35:50 +01:00
ace
c9163a1505 ChiseledBookshelf: Fix duplication bug (#6086)
Also fixes being able to put more than 1 book in the slot
2023-10-10 10:59:39 +01:00
2d697c5f04 A random change that Composer 2.6 wanted to make 2023-10-09 18:27:20 +01:00
364f408eb1 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-10-09 18:22:54 +01:00
660e2b8173 Update build/php to pmmp/php-build-scripts@a34e48e7da 2023-10-09 18:22:28 +01:00
9facb98327 Bump phpunit/phpunit from 10.3.5 to 10.4.1 (#6082)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.3.5 to 10.4.1.
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.4.1/ChangeLog-10.4.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.3.5...10.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 17:16:15 +01:00
7e42a03db3 Bump phpstan/phpstan from 1.10.37 to 1.10.38 (#6081)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.37 to 1.10.38.
- [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.37...1.10.38)

---
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-10-09 17:15:50 +01:00
ee26d6d570 LightUpdate: avoid trying to propagate light into nodes with higher light levels
Track which direction the current node's light came from, and don't
check it again when we check the current node's adjacent blocks.

e.g. if this node was the eastern neighbour of a light source, we don't
need to check this node's western neighbour, as we already know it has
a higher light level than our own.

This improves performance of basic light spread in a void by about 6%,
which isn't a huge amount, but it's something.

I've yet to explore whether light removal could also benefit from this
change.
2023-10-09 17:06:02 +01:00
94a17f59d2 fix(Entity): broadcastSound() not firing WorldSoundEvent (#6069) 2023-10-08 19:25:19 +01:00
ed4088755f Bump phpstan/phpstan from 1.10.35 to 1.10.37 (#6073)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.35 to 1.10.37.
- [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.35...1.10.37)

---
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-10-05 22:23:13 +01:00
006f78c0a7 Updated block consistency check 2023-09-29 12:57:39 +01:00
55cc5a6651 NetherVines: optimize condition order 2023-09-29 12:45:55 +01:00
390cc3060a Disable random ticking for some blocks depending on their states
we don't need to waste CPU time attempting to tick fully grown crops.
2023-09-29 12:45:37 +01:00
ca69f08da0 ChorusFlower: move ticksRandomly() to a more appropriate place 2023-09-29 12:44:27 +01:00
eac0564792 Fix CS 2023-09-29 12:31:11 +01:00
628d77f8d7 Implemented pitcher plants, crops and pods 2023-09-29 12:30:52 +01:00
fe543a4789 AgeableTrait: fixed incorrect number of bits
the original method breaks if MAX_AGE is 0 or any power of 2.
2023-09-29 12:05:39 +01:00
31cd096b4b Implement torchflower, its seeds and its crop 2023-09-28 17:13:33 +01:00
78cc5ba635 CS again 2023-09-28 16:06:38 +01:00
4b9d170954 RuntimeDataDescriber: added dynamic method for reading and writing enum sets
this was previously only needed for brewing stands, but it's now become needed for chiselled bookshelves too.
2023-09-28 16:06:17 +01:00
d94391af57 Implement Chiseled Bookshelf (#5827) 2023-09-28 15:56:46 +01:00
ff89d4d055 Updated composer dependencies 2023-09-28 14:55:55 +01:00
a6b030f2b3 Fix CSÂ 2023-09-27 17:07:02 +01:00
56d7039086 Implemented budding amethyst and amethyst clusters 2023-09-27 17:02:37 +01:00
8f804f6f34 World: discard tiles on load if they aren't the correct type or no tile is expected
in many instances, remnants of improperly removed blocks from PM3 have been causing problems, such as flower pot tiles where there are no flower pots.

this change might break some plugins which are using tiles for custom purposes, but this is a misuse that was never supported properly in the first place.
2023-09-27 14:57:06 +01:00
4f13e446a1 StringToItemParser: clean up hardcoded potion aliases
these were needed in PM4, but the type is dynamic in PM5.
2023-09-27 13:18:02 +01:00
6ec340359b Implemented crimson and warped roots 2023-09-27 13:07:46 +01:00
c3bca9e172 tools/generate-bedrock-data-from-packets: fixed interpreting item metadata as blockstates when the item ID could be interpreted as a block ID
this broke crafting recipes which accepted skulls as inputs, as well as nether wart and bed recipes.
2023-09-27 12:37:26 +01:00
c028bb9055 Bump docker/setup-buildx-action from 2 to 3 (#6049)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 14:50:57 +01:00
f151047b5e Bump docker/login-action from 2 to 3 (#6050)
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 14:50:40 +01:00
77566db766 Bump docker/build-push-action from 4.1.1 to 5.0.0 (#6051)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.1.1 to 5.0.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4.1.1...v5.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 14:50:17 +01:00
dd2e6ea33f Bump shivammathur/setup-php from 2.25.5 to 2.26.0 (#6055)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.25.5 to 2.26.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.25.5...2.26.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 14:49:38 +01:00
ee6d551729 Include TitleID in PlayerInfo extraData (#6054) 2023-09-21 14:48:50 +01:00
04b815a87a Merge branch 'stable' into minor-next 2023-09-21 13:27:11 +01:00
d138a15a32 Merge branch 'legacy/pm4' into stable 2023-09-21 13:27:04 +01:00
7a2cfa92b6 Update composer dependencies 2023-09-21 13:23:09 +01:00
912fd3f5c6 PHPStan 1.10.35, plus workarounds 2023-09-21 13:22:14 +01:00
3906600d44 Fix CS 2023-09-20 19:44:53 +01:00
3f7abf29a8 Added PHPStan rule to flag usages of legacy enum accessors provided by LegacyEnumShimTrait
closes #6061
2023-09-20 19:42:21 +01:00
fe3e2cc90a Merge branch 'stable' into minor-next 2023-09-20 19:14:34 +01:00
5eca90d478 5.6.1 is next 2023-09-20 19:00:51 +01:00
338bb3fe6d Release 5.6.0 2023-09-20 19:00:51 +01:00
f485f7fb46 Updated composer dependencies 2023-09-20 18:57:36 +01:00
63eba3eb53 Merge branch 'legacy/pm4' into stable 2023-09-20 18:40:58 +01:00
914eb62e94 4.24.1 is next 2023-09-20 18:40:07 +01:00
a85814d0c9 Release 4.24.0 2023-09-20 18:40:07 +01:00
eb2e472b01 Merge branch 'legacy/pm4' into stable 2023-09-20 18:36:41 +01:00
6553852d99 Updated for 1.20.30 release 2023-09-20 18:34:12 +01:00
540476365f Updated for 1.20.30 release 2023-09-20 18:14:59 +01:00
e9169cfa67 ChorusPlant: change stupid code 2023-09-08 17:34:47 +01:00
537e194161 ChorusPlant: make use of StaticSupportTrait 2023-09-08 17:33:32 +01:00
f7f5af607c Merge branch 'stable' into minor-next 2023-09-08 17:20:45 +01:00
b293d7bf1f Static support trait (#6044)
Added StaticSupportTrait for blocks which require unconditional support

dynamic support requirements, such as those presented by item frames and torches, are not included.

in addition, double blocks, such as tallgrass, small dripleaf and doors, do not cooperate well with this, so they are also not included.

some blocks which could be migrated (such as chorus plant) were skipped due to unresolved problems.
2023-09-08 17:19:06 +01:00
efafc2c6ca DeadBush: updated support requirements
since 1.20 they can now be placed on grass and mud.
2023-09-08 16:41:06 +01:00
2a528b4afb SetupWizard: stop depending on Java gamemode IDs for option selection 2023-09-08 16:15:07 +01:00
999eab0c84 SetupWizard: use Server constants directly 2023-09-08 15:43:36 +01:00
33a0fb9061 SetupWizard: clean up crusty code that loads server.properties 3 times 2023-09-08 15:14:51 +01:00
904b0acfff SetupWizard: replace hardcoded config keys 2023-09-08 15:11:59 +01:00
093b1e1b18 Merge branch 'stable' into minor-next 2023-09-08 12:48:40 +01:00
d7f69c5e24 CaveVines: fixed incorrect support condition 2023-09-08 12:47:46 +01:00
d60fca0a1c Age blocks logic moved into dedicated trait (#5962) 2023-09-08 12:25:26 +01:00
0e87ee1e0e ÂHangingRoots: fixed incorrect support face 2023-09-08 12:22:00 +01:00
03ecc98a24 HangingRoots: fixed support conditions 2023-09-08 12:16:45 +01:00
a5aeabd836 RegistryTrait: fixed mishandling of self::$members
Since PHPStan doesn't warn about potential nulls on untyped properties, this flew under the radar.
2023-09-08 12:16:16 +01:00
d3ab516ba4 CS 2023-09-08 11:26:58 +01:00
aa916b2c49 WoodLikeBlockIdHelper: reduce obnoxious code 2023-09-08 11:17:47 +01:00
7ce33d9375 Migrate final remaining EnumTrait users to native enums 2023-09-08 10:34:12 +01:00
14f2368454 ToolTier: remove legacy EnumTrait annotations and doc comment info 2023-09-08 10:02:50 +01:00
07194e3884 CS again 2023-09-07 20:33:30 +01:00
58278f22f3 Remove deprecated API usages from tests 2023-09-07 20:30:45 +01:00
7dcd2592d4 RuntimeDataDescriber: Support dynamically describing arbitrary enums (#6039)
Previously, we were using codegen to support describing a fixed set of enums.

Instead, we implement an enum() function, allowing any native PHP enum to be described.
All enums used in runtime data have been migrated to native PHP 8.1 enums in minor-next to facilitate this.

This implementation:

- is faster (in extreme cases by 40x, such as with PotionType)
- requires way less code
- does not require a build step
- is way more flexible

This fixes #5877, increasing the range of stuff that plugins are now able to do.

EnumTrait enums are not supported, as it's easier and cleaner to just support native enums. Most core EnumTrait enums have been migrated to native enums by now to facilitate this.
2023-09-07 20:07:14 +01:00
6887fcd590 RuntimeEnum(De)SerializerTrait no longer uses legacy accessors for enum members 2023-09-07 19:40:14 +01:00
c168818311 missed one 2023-09-07 19:39:13 +01:00
b50efbc15a Fix CS 2023-09-07 19:38:29 +01:00
94d98fb5c4 Migrate all but two remaining legacy enums to native PHP 8.1 enums 2023-09-07 19:32:45 +01:00
ae564e445d Start migrating EnumTrait enums to PHP 8.1 native enums 2023-09-07 17:20:52 +01:00
fe94379a93 Fixed connection requirements for fences, glass, bars and walls
these connect to the back faces of stairs and to glass, for example.
2023-09-06 12:56:47 +01:00
79acc4fed4 5.5.1 is next 2023-09-06 12:13:26 +01:00
c8d357f4eb Release 5.5.0 2023-09-06 12:13:26 +01:00
ec1cd5967d Added private constructors for new internal classes 2023-09-06 12:12:11 +01:00
5a010e8213 Merge branch 'minor-next' into stable 2023-09-06 12:06:15 +01:00
73a44d50ee 5.4.5 is next 2023-09-06 11:53:04 +01:00
6aab07debd Release 5.4.4 2023-09-06 11:53:01 +01:00
b160b87e24 Server: stop discriminating against crashes caused by folder plugins
these are the de facto standard, which means that a lot of crashes aren't getting reported from servers with folder plugins.
2023-09-06 11:34:03 +01:00
690ee4c574 CrashDump: fixed empty elements in lastError trace
as shown by #6010
2023-09-06 11:24:08 +01:00
a0c7587b68 Update composer dependencies 2023-09-06 11:09:28 +01:00
be4e091d40 Update draft release notice 2023-09-06 11:03:15 +01:00
857c2edc2c Server: update obsoletion notice with new announcement and cutoff date 2023-09-06 10:58:41 +01:00
b1ab69ac6c Updated build/php submodule to pmmp/PHP-Binaries@3331f8c0d5 2023-09-06 10:51:22 +01:00
e95a920fb8 Update composer dependencies 2023-09-06 10:49:54 +01:00
67f399b238 Bump phpstan/phpstan-phpunit from 1.3.13 to 1.3.14 (#6021)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.3.13 to 1.3.14.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.3.13...1.3.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 10:24:41 +01:00
a7c806d549 Bump phpunit/phpunit from 10.3.2 to 10.3.3 (#6033)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.3.2 to 10.3.3.
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.3.3/ChangeLog-10.3.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.3.2...10.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 10:24:23 +01:00
0920c76a35 Bump build/php from 8884039 to 3331f8c (#6031)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `8884039` to `3331f8c`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](8884039bee...3331f8c0d5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 08:37:05 +01:00
a91ca999fe Bump actions/checkout from 3 to 4 (#6032)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  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-09-06 08:32:33 +01:00
ce04478395 Fix SmithingTableInventory size (#6035)
Since 1.20 SmithingTable has a new Template slot, size is now 3

Fix debug error from InventoryManager
2023-09-06 08:15:27 +01:00
28ce7ac5fd Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-09-04 18:10:41 +01:00
540f088eda tools/generate-bedrock-data-from-packets: make duplicate reporting less spammy 2023-09-01 20:51:45 +01:00
19e3d339f6 InGamePacketHandler: subtract from raw position before rounding it (#6022)
This allows better compensation for floating point errors introduced by the subtraction of the 1.62 height offset.

For example, if the player is at y=7 exactly, their Y coordinate will be reported as 8.62, which, because of floating point errors, will be something like `8.619999999`. Subtracting `1.62` from this (really something like `1.62000000000005...`) leads to the calculated Y coordinate being slightly below 7.

Rounding after subtracting this offset allows this to be rounded to 7 sharp. Similar errors appear in various other coordinates.
2023-08-29 11:43:21 +01:00
9fdb6ba5aa Mark some new things as internal 2023-08-25 14:02:49 +01:00
4a0a538278 CS 2023-08-25 13:27:40 +01:00
2912e7ca29 ... 2023-08-25 13:27:11 +01:00
31d8cc1cb5 Generate and use constants for pocketmine.yml constant names
a couple of usages of properties that no longer exist couldn't be migrated.
in addition, this revealed a couple of dead properties in the default file.

this is not an ideal solution (I'd much rather model the configs using classes and map them) but in the absence of a good and reliable library to do that, this is the next best thing.
2023-08-25 13:23:38 +01:00
506d8d1064 CS 2023-08-25 12:49:56 +01:00
d1a7c1d453 Constify server.properties references 2023-08-25 12:49:39 +01:00
b56f1b679e Deduplicate a bunch of repeated type ID map code 2023-08-25 12:30:54 +01:00
1a18e32011 Bump ncipollo/release-action from 1.12.0 to 1.13.0 (#6019)
Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/ncipollo/release-action/releases)
- [Commits](https://github.com/ncipollo/release-action/compare/v1.12.0...v1.13.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>
2023-08-25 09:43:31 +01:00
09c9dfb576 Bump build/php from d75f83e to 8884039 (#6018)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `d75f83e` to `8884039`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](d75f83e7ef...8884039bee)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-25 09:43:05 +01:00
f2b710c083 Bump build/php from a053f65 to d75f83e (#6017)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `a053f65` to `d75f83e`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](a053f65e18...d75f83e7ef)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-24 08:31:31 +01:00
c7a311c17a COPILOT 2023-08-23 17:14:56 +01:00
ce53a221a5 5.5.0-BETA2 is next 2023-08-23 17:09:34 +01:00
4cc858829f 5.4.4 is next 2023-08-21 18:31:45 +01:00
e852a43821 Release 5.4.3 2023-08-21 18:31:45 +01:00
05f40b1315 Merge branch 'legacy/pm4' into stable 2023-08-21 18:27:18 +01:00
7aaef8cb89 4.23.7 is next 2023-08-21 18:26:50 +01:00
9d4c37fc3a Release 4.23.6 2023-08-21 18:26:47 +01:00
379 changed files with 8462 additions and 6319 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

@ -12,23 +12,23 @@ jobs:
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Clone pmmp/PocketMine-Docker repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: pmmp/PocketMine-Docker
fetch-depth: 1
@ -53,7 +53,7 @@ jobs:
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v4.1.1
uses: docker/build-push-action@v5.2.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@v4.1.1
uses: docker/build-push-action@v5.2.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@v4.1.1
uses: docker/build-push-action@v5.2.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@v4.1.1
uses: docker/build-push-action@v5.2.0
with:
push: true
context: ./pocketmine-mp

View File

@ -10,15 +10,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.25.5
uses: shivammathur/setup-php@2.30.0
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,20 +12,20 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: [8.1]
php-version: [8.2]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.25.5
uses: shivammathur/setup-php@2.30.0
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.12.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 }}

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

@ -0,0 +1,165 @@
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: 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

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"]
php: ["8.1", "8.2", "8.3"]
steps:
- uses: actions/checkout@v3
- 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"]
steps:
- uses: actions/checkout@v3
- 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"]
steps:
- uses: actions/checkout@v3
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"]
steps:
- uses: actions/checkout@v3
- 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 RuntimeEnum(De)serializer
run: php build/generate-runtime-enum-serializers.php
- name: Regenerate BedrockData available files constants
run: php build/generate-bedrockdata-path-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
@ -170,13 +25,13 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.25.5
uses: shivammathur/setup-php@2.30.0
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

@ -8,7 +8,7 @@ jobs:
support:
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v3
- uses: dessant/support-requests@v4
with:
github-token: ${{ github.token }}
support-label: "Support request"

View File

@ -14,7 +14,7 @@ jobs:
- name: Install jq
run: sudo apt update && sudo apt install jq -y
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/update.pmmp.io
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}

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

@ -88,45 +88,58 @@ Depending on the changes, maintainers might ask you to make changes to the PR to
### Requirements
The following are required as a minimum for pull requests. PRs that don't meet these requirements will be declined unless updated to meet them.
#### Licensing
PocketMine-MP is licensed under [LGPLv3 license](LICENSE).
By proposing a pull request, you agree to your code being distributed within PocketMine-MP under the same license.
If you take code from other projects, that code MUST be licensed under an LGPL-compatible license.
#### PRs should be about exactly ONE thing
If you want to make multiple changes, those changes should each be contributed as separate pull requests. **DO NOT** mix unrelated changes.
#### PRs must not include unnecessary/unrelated changes
Do not include changes which aren't strictly necessary. This makes it harder to review a PR, because the code diff becomes larger and harder to review.
This means:
- don't reformat or rearrange existing code
- don't change things that aren't related to the PR's objective
- don't rewrite existing code just to make it "look nicer"
- don't change PhpDocs to native types in code you didn't write
#### Tests must be provided
Where possible, PHPUnit tests should be written for new or changed code.
If that's not possible (e.g. for in-game functionality), the code must be tested manually and details of the tests done must be provided.
**Simply saying "Tested" is not acceptable** and will lead to your PR being declined.
#### Comments and documentation must be written in American English
English is the shared languages of all current maintainers.
#### Code must be in the PocketMine-MP style
It's your responsibility to ensure your code matches the formatting and styling of the rest of the code.
If you use PhpStorm, a `Project` code style is provided, which you can use to automatically format new code.
You can also use [`php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to format your code.
- **All code must be licensed under the [LGPLv3 license](LICENSE)** as per PocketMine-MP's own license, or a compatible license.
- By proposing a pull request, you agree to your code being distributed within PocketMine-MP under the same license.
- If you take code from other projects, that code MUST be licensed under an LGPL-compatible license.
- **PRs should be about ONE thing**
- If you want to make multiple changes, those changes should each be contributed as separate pull requests. **DO NOT** mix unrelated changes.
- **Do not include unnecessary changes.** This makes the code diff larger and more noisy, making it harder to review.
- Don't change things that aren't related to the PR's objective
- Don't reformat or rearrange existing code without a good reason related to the PR's objective
- Don't rewrite existing code just to make it "look nicer"
- Don't change PhpDocs to native types in code you didn't write, unless that's the objective of the PR
- **Test code changes, and tell us what tests have been done.**
- Where possible, PHPUnit tests should be written for new or changed code. If that's not possible (e.g. for in-game functionality), the code must be tested manually and details of the tests done must be provided.
- **Simply saying "Tested" is not acceptable** and could lead to your PR being declined.
- **Code, comments and documentation must be written in American English.** English is the shared languages of all current maintainers.
- **Code must be in the PocketMine-MP style.**
- It's your responsibility to ensure your code matches the formatting and styling of the rest of the code.
- If you use PhpStorm, a `Project` code style is provided, which you can use to automatically format new code.
- You can also use [`php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to format your code.
- **Use `final` and `private` wherever possible**.
- Changing from `private` to `protected` or `final` to non-`final` doesn't break backwards compatibility, but the opposite does.
- `private` and `final` also enable certain performance optimizations which are otherwise not possible.
- `private` members can be freely changed, added and removed in the future, so it's ideal for internal functions. Abusing `protected` makes internal improvements inconvenient.
- "Let's leave it protected/public in case someone needs it for ... idk what" is **not a valid reason to expose things**. If there isn't a clear reason for something to be accessible from the outside, don't expose it.
- **This is a lesson learned through years of experience.** You may not like it, but it's for the best.
- **Immutable things are almost always preferred.**
- Do not add unnecessary setters or public writable properties to classes. As above, "Let's leave it in case someone needs it" is **not a valid reason to expose things**.
- Mutable classes and properties are unpredictable, since code has no way to know if the object it's working with might be randomly modified by another part of the code. This makes it harder to maintain code and debug issues.
- Most classes exist only to hold some data. These are called "data transfer objects" (DTOs). These types of classes should pretty much always be immutable.
- Make use of `final`, `private` and `readonly` modifiers.
### Recommendations
- **Be patient.** Reviewing pull requests takes a lot of time and energy, and maintainers are often unavailable or busy. Your PR might not receive attention for a while.
- Remember, PRs with small diffs are much easier to review. Small PRs are generally reviewed and merged much faster than large ones.
- **Start small.** Try fixing minor bugs or doing something isolated (e.g. adding a new block or item) before attempting larger changes.
- This helps you get familiar with the codebase, the contribution process, and the expectations of maintainers.
- Check out the [issues page]() for something that you could tackle without too much effort.
- **Do not copy-paste other people's code**. Many PRs involve discussion about the changes, and changes are often requested by reviewers. If you don't understand the code you're copy-pasting, your PR is likely to fail.
- **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE.
- **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode.
- **Do not make large pull requests without an RFC.**
- Large changes should be discussed beforehand using the [RFC / Change Proposal](#rfcs--change-proposals) process.
- Large changes are much harder to review, and are more likely to be declined if maintainers don't have a good idea what you're trying to do in advance.
- **Create a new branch on your fork for each pull request.** This allows you to use the same fork to make multiple pull requests at the same time.
- **Make your PR diff as small as possible.** Smaller PRs are **much more likely** to be accepted, as they are easier to review.
- Avoid moving code around in files if possible.
- Don't make random CS changes. This makes the diff noisier and harder to review.
- **Use descriptive commit titles.** You can see an example [here](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
- **Do not include multiple unrelated changes in one commit.** An atomic style for commits is preferred - this means that changes included in a commit should be part of a single distinct change set. See [this link](https://www.freshconsulting.com/atomic-commits/) for more information on atomic commits. See the [documentation on `git add`](https://git-scm.com/docs/git-add) for information on how to isolate local changes for committing.
- **Your pull request will be checked and discussed in due time.** Since the team is scattered all around the world, your PR may not receive any attention for some time.
- **Do not make large pull requests without an RFC.** Large changes should be discussed beforehand using the [RFC / Change Proposal](#rfcs--change-proposals) process. Large changes are much harder to review and are more likely to be declined if maintainers don't have a good idea what you're trying to do in advance.
- **Do not copy-paste code**. There are potential license issues implicit with copy-pasting, and copy-paste usually indicates a lack of understanding of the actual code. Copy-pasted code is obvious a mile off and **any PR like this is likely to be closed**. If you want to use somebody else's code from a Git repository, **use [GIT's cherry-pick feature](https://git-scm.com/docs/git-cherry-pick)** to cherry-pick the commit.
- **Split unrelated changes into multiple commits.**
- An atomic style for commits is preferred - this means that changes included in a commit should be part of a single distinct change set.
- If you need to use "and" or "multiple changes" in your commit message, the commit probably needs to be split up. There are exceptions, but this is a good rule of thumb.
- See [this link](https://www.freshconsulting.com/atomic-commits/) for more information on atomic commits.
- See the [documentation on `git add -i` or `git add -p`](https://git-scm.com/docs/git-add) for information on how to split up local changes for committing.
**Thanks for contributing to PocketMine-MP!**

View File

@ -20,31 +20,61 @@
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest by SemVer)" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/latest/total?sort=semver"></a>
</p>
## Getting started
## What is this?
PocketMine-MP is a highly customisable server software for Minecraft: Bedrock Edition, built from scratch in PHP, with over 10 years of history.
If you're looking to create a Minecraft: Bedrock server with **custom functionality**, look no further.
- 🧩 **Powerful plugin API** - extend and customise gameplay as you see fit
- 🗺️ **Rich ecosystem** and **large developer community** - find plugins easily and learn to develop your own
- 🌐 **Multi-world support** - offer a more varied game experience to players without transferring them to other server nodes
- 🏎️ **Performance** - get 100+ players onto one server (depending on hardware and plugins)
- ⤴️ **Continuously updated** - new Minecraft versions are usually supported within days
## :x: PocketMine-MP is NOT a vanilla Minecraft server software.
**It is poorly suited to hosting vanilla survival servers.**
It doesn't have many features from the vanilla game, such as vanilla world generation, redstone, mob AI, and various other things.
If you just want to play **vanilla survival multiplayer**, consider using the [official Minecraft: Bedrock server software](https://minecraft.net/download/server/bedrock) instead of PocketMine-MP.
If that's not an option for you, you may be able to add some of PocketMine-MP's missing features using plugins from [Poggit](https://poggit.pmmp.io/plugins), or write plugins to implement them yourself.
## Getting Started
- [Documentation](http://pmmp.readthedocs.org/)
- [Installation instructions](https://pmmp.readthedocs.io/en/rtfd/installation.html)
- [Docker image](https://github.com/pmmp/PocketMine-MP/pkgs/container/pocketmine-mp)
- [Plugin repository](https://poggit.pmmp.io/plugins)
## Community & Support
- [Forums](https://forums.pmmp.io/)
- [Discord](https://discord.gg/bmSAZBG)
- [StackOverflow](https://stackoverflow.com/tags/pocketmine)
Join our [Discord](https://discord.gg/bmSAZBG) server to chat with other users and developers.
You can also post questions on [StackOverflow](https://stackoverflow.com/tags/pocketmine) under the tag `pocketmine`.
## Developing Plugins
If you want to write your own plugins, the following resources may be useful.
Don't forget you can always ask our community if you need help.
## For developers
* [Building and running from source](BUILDING.md)
* [Developer documentation](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers
* [Latest release API documentation](https://apidoc.pmmp.io) - Doxygen API documentation generated for each release
* [Latest bleeding-edge API documentation](https://apidoc-dev.pmmp.io) - Doxygen API documentation generated weekly from `major-next` branch
* [DevTools](https://github.com/pmmp/DevTools/) - Development tools plugin for creating plugins
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
## Contributing to PocketMine-MP
PocketMine-MP accepts community contributions! The following resources will be useful if you want to contribute to PocketMine-MP.
* [Building and running PocketMine-MP from source](BUILDING.md)
* [Contributing Guidelines](CONTRIBUTING.md)
## Donate
- Bitcoin Cash (BCH): `qq3r46hn6ljnhnqnfwxt5pg3g447eq9jhvw5ddfear`
PocketMine-MP is free, but it requires a lot of time and effort from unpaid volunteers to develop. Donations enable us to keep delivering support for new versions and adding features your players love.
You can support development using the following methods:
- [Patreon](https://www.patreon.com/pocketminemp)
- Bitcoin (BTC): `171u8K9e4FtU6j3e5sqNoxKUgEw9qWQdRV`
- Stellar Lumens (XLM): `GAAC5WZ33HCTE3BFJFZJXONMEIBNHFLBXM2HJVAZHXXPYA3HP5XPPS7T`
- [Patreon](https://www.patreon.com/pocketminemp)
Thanks for your support!
## Licensing information
This project is licensed under LGPL-3.0. Please see the [LICENSE](/LICENSE) file for details.

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

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

@ -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);
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
require dirname(__DIR__) . '/vendor/autoload.php';
$defaultConfig = yaml_parse(Filesystem::fileGetContents(dirname(__DIR__) . '/resources/pocketmine.yml'));
if(!is_array($defaultConfig)){
fwrite(STDERR, "Invalid default pocketmine.yml\n");
exit(1);
}
$constants = [];
/**
* @param mixed[] $properties
* @param string[] $constants
* @phpstan-param array<string, string> $constants
* @phpstan-param-out array<string, string> $constants
*/
function collectProperties(string $prefix, array $properties, array &$constants) : void{
foreach($properties as $propertyName => $property){
$fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName;
$constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName));
$constants[$constName] = $fullPropertyName;
if(is_array($property)){
collectProperties($fullPropertyName, $property, $constants);
}
}
}
collectProperties("", $defaultConfig, $constants);
ksort($constants, SORT_STRING);
$file = fopen(dirname(__DIR__) . '/src/YmlServerProperties.php', 'wb');
if($file === false){
fwrite(STDERR, "Failed to open output file\n");
exit(1);
}
fwrite($file, "<?php\n");
fwrite($file, <<<'HEADER'
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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/
*
*
*/
HEADER
);
fwrite($file, "declare(strict_types=1);\n\n");
fwrite($file, "namespace pocketmine;\n\n");
fwrite($file, <<<'DOC'
/**
* @internal
* Constants for all properties available in pocketmine.yml.
* This is generated by build/generate-pocketmine-yml-property-consts.php.
* Do not edit this file manually.
*/
DOC
);
fwrite($file, "final class YmlServerProperties{\n");
fwrite($file, <<<'CONSTRUCTOR'
private function __construct(){
//NOOP
}
CONSTRUCTOR
);
foreach(Utils::stringifyKeys($constants) as $constName => $propertyName){
fwrite($file, "\tpublic const $constName = '$propertyName';\n");
}
fwrite($file, "}\n");
fclose($file);
echo "Done. Don't forget to run CS fixup after generating code.\n";

View File

@ -1,265 +0,0 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\build\generate_runtime_enum_serializers;
use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DirtType;
use pocketmine\block\utils\DripleafState;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\FroglightType;
use pocketmine\block\utils\LeverFacing;
use pocketmine\block\utils\MobHeadType;
use pocketmine\block\utils\MushroomBlockType;
use pocketmine\block\utils\SlabType;
use pocketmine\item\MedicineType;
use pocketmine\item\PotionType;
use pocketmine\item\SuspiciousStewType;
use function array_key_first;
use function array_keys;
use function array_map;
use function ceil;
use function count;
use function dirname;
use function file_put_contents;
use function implode;
use function ksort;
use function lcfirst;
use function log;
use function ob_get_clean;
use function ob_start;
use const SORT_STRING;
require dirname(__DIR__) . '/vendor/autoload.php';
/**
* @param string[] $memberNames
* @phpstan-param list<string> $memberNames
*
* @return string[]
* @phpstan-return list<string>
*/
function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
$bits = getBitsRequired($memberNames);
$lines = [];
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
$lines[] = "\t\$this->writeInt($bits, match(\$value){";
foreach($memberNames as $key => $memberName){
$lines[] = "\t\t$memberName => $key,";
}
$lines[] = "\t\tdefault => throw new \pocketmine\utils\AssumptionFailedError(\"All $virtualTypeName cases should be covered\")";
$lines[] = "\t});";
$lines[] = "}";
return $lines;
}
/**
* @param string[] $memberNames
* @phpstan-param list<string> $memberNames
*
* @return string[]
* @phpstan-return list<string>
*/
function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
$bits = getBitsRequired($memberNames);
$lines = [];
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
$lines[] = "\t\$value = match(\$this->readInt($bits)){";
foreach($memberNames as $key => $memberName){
$lines[] = "\t\t$key => $memberName,";
}
$lines[] = "\t\tdefault => throw new InvalidSerializedRuntimeDataException(\"Invalid serialized value for $virtualTypeName\")";
$lines[] = "\t};";
$lines[] = "}";
return $lines;
}
function buildInterfaceFunc(string $nativeTypeName, string $functionName) : string{
return "public function $functionName(\\$nativeTypeName &\$value) : void;";
}
/**
* @param string[] $memberNames
* @phpstan-param list<string> $memberNames
*
* @return string[]
* @phpstan-return list<string>
*/
function buildSizeCalculationFunc(string $nativeTypeName, string $functionName, array $memberNames) : array{
$lines = [];
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
$lines[] = "\t\$this->addBits(" . getBitsRequired($memberNames) . ");";
$lines[] = "}";
return $lines;
}
/**
* @param mixed[] $members
*/
function getBitsRequired(array $members) : int{
return (int) ceil(log(count($members), 2));
}
/**
* @param object[] $members
* @phpstan-param array<string, object> $members
*
* @return string[]
* @phpstan-return list<string>
*/
function stringifyEnumMembers(array $members, string $enumClass) : array{
ksort($members, SORT_STRING);
return array_map(fn(string $enumCaseName) => "\\$enumClass::$enumCaseName()", array_keys($members));
}
$enumsUsed = [
BellAttachmentType::getAll(),
CopperOxidation::getAll(),
CoralType::getAll(),
DirtType::getAll(),
DripleafState::getAll(),
DyeColor::getAll(),
FroglightType::getAll(),
LeverFacing::getAll(),
MedicineType::getAll(),
MushroomBlockType::getAll(),
MobHeadType::getAll(),
SlabType::getAll(),
SuspiciousStewType::getAll(),
PotionType::getAll()
];
$readerFuncs = [
"" => [
"abstract protected function readInt(int \$bits) : int;"
]
];
$writerFuncs = [
"" => [
"abstract protected function writeInt(int \$bits, int \$value) : void;"
]
];
$interfaceFuncs = [];
$sizeCalculationFuncs = [
"" => [
"abstract protected function addBits(int \$bits) : void;"
]
];
foreach($enumsUsed as $enumMembers){
if(count($enumMembers) === 0){
throw new \InvalidArgumentException("Enum members cannot be empty");
}
$reflect = new \ReflectionClass($enumMembers[array_key_first($enumMembers)]);
$virtualTypeName = $reflect->getShortName();
$nativeTypeName = $reflect->getName();
$functionName = lcfirst($virtualTypeName);
$stringifiedMembers = stringifyEnumMembers($enumMembers, $nativeTypeName);
$writerFuncs[$functionName] = buildWriterFunc(
$virtualTypeName,
$nativeTypeName,
$stringifiedMembers,
$functionName
);
$readerFuncs[$functionName] = buildReaderFunc(
$virtualTypeName,
$nativeTypeName,
$stringifiedMembers,
$functionName
);
$interfaceFuncs[$functionName] = [buildInterfaceFunc(
$nativeTypeName,
$functionName
)];
$sizeCalculationFuncs[$functionName] = buildSizeCalculationFunc(
$nativeTypeName,
$functionName,
$stringifiedMembers
);
}
/**
* @param string[][] $functions
* @phpstan-param array<string, list<string>> $functions
*/
function printFunctions(array $functions, string $className, string $classType) : void{
ksort($functions, SORT_STRING);
ob_start();
echo <<<'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\runtime;
/**
* This class is auto-generated. Do not edit it manually.
* @see build/generate-runtime-enum-serializers.php
*/
HEADER;
echo "$classType $className{\n\n";
echo implode("\n\n", array_map(fn(array $functionLines) => "\t" . implode("\n\t", $functionLines), $functions));
echo "\n\n}\n";
file_put_contents(dirname(__DIR__) . '/src/data/runtime/' . $className . '.php', ob_get_clean());
}
printFunctions($writerFuncs, "RuntimeEnumSerializerTrait", "trait");
printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait", "trait");
printFunctions($interfaceFuncs, "RuntimeEnumDescriber", "interface");
printFunctions($sizeCalculationFuncs, "RuntimeEnumSizeCalculatorTrait", "trait");
echo "Done. Don't forget to run CS fixup after generating code.\n";

View File

@ -32,6 +32,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 +148,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',

View File

@ -60,3 +60,9 @@ Released 9th August 2023.
- Fixed `PluginBase->saveResource()` leaking file resources when the data file already exists in the plugin's data folder. This bug existed since 2014 and was only discovered recently.
- Fixed coral blocks becoming dead after calling `getDropsForCompatibleTool()` on them.
- Fixed `BlockDeathEvent->getOldState()` returning a block which is already dead.
# 4.23.6
Released 21st August 2023.
## Fixes
- Added a workaround for armor and other inventories not working correctly after inventory sync. This is caused by a client bug.

17
changelogs/4.24.md Normal file
View File

@ -0,0 +1,17 @@
# 4.24.0
Released 20th September 2023.
**For Minecraft: Bedrock Edition 1.20.30**
This is a support release for Minecraft: Bedrock Edition 1.20.30.
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.30.
- Removed support for older versions.
- Updated 4.x obsoletion message.

16
changelogs/4.25.md Normal file
View File

@ -0,0 +1,16 @@
# 4.25.0
Released 26th October 2023.
**For Minecraft: Bedrock Edition 1.20.40**
This is a support release for Minecraft: Bedrock Edition 1.20.40.
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.40.
- Removed support for older versions.

16
changelogs/4.26.md Normal file
View File

@ -0,0 +1,16 @@
# 4.26.0
Released 6th December 2023.
**For Minecraft: Bedrock Edition 1.20.50**
This is a support release for Minecraft: Bedrock Edition 1.20.50.
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.50.
- Removed support for older versions.

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.

View File

@ -107,3 +107,27 @@ Released 9th August 2023.
## Fixes
- Fixed cake accepting candle placement when slices have already been eaten.
- Fixed fire charges not lighting candles.
# 5.4.3
Released 21st August 2023.
## Included releases
- [4.23.6](https://github.com/pmmp/PocketMine-MP/blob/4.23.6/changelogs/4.23.md#4236) - Armor inventory client bug workaround
## Fixes
- Fixed crashdumps not generating correctly on fatal errors.
- Fixed `PotionCauldron::setPotionItem()` not validating the item type.
- Fixed chorus fruit not considering teleport destinations below y=0.
- Fixed cake dropping itself when mined.
# 5.4.4
Released 6th September 2023.
## General
- Crashdumps caused by non-phar plugins are now submitted to the Crash Archive, the same as other plugins. Previously, non-phar plugin crashes would not be submitted, causing maintainers to potentially miss important issues.
## Fixes
- Fixed player Y coordinates sometimes being slightly below the top of the block they were standing on (floating point error due to subtracting eye height).
- Fixed template slot of smithing tables not accepting any items.
- `tools/generate-bedrock-data-from-packets.php` is now significantly less spammy when warning about duplicated recipes.
- Fixed empty stack traces in `lastError` data of crashdumps.

View File

@ -1,7 +1,7 @@
# 5.5.0-BETA1
Released 23rd August 2023.
**For Minecraft: Bedrock Edition 1.20.0**
**For Minecraft: Bedrock Edition 1.20.10**
This is a minor feature release, including performance improvements, new API methods, and new gameplay features.

162
changelogs/5.5.md Normal file
View File

@ -0,0 +1,162 @@
# 5.5.0
Released 6th September 2023.
**For Minecraft: Bedrock Edition 1.20.10**
This is a minor feature release, including performance improvements, new API methods, and new gameplay features.
**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.
## Dependencies
- Updated `pocketmine/math` dependency to [`1.0.0`](https://github.com/pmmp/Math/releases/tag/1.0.0).
- Updated `pocketmine/nbt` dependency to [`1.0.0`](https://github.com/pmmp/NBT/releases/tag/1.0.0).
## Performance
- Some events are now no longer fired if no handlers are registered.
- This improves performance by avoiding unnecessary object allocations and function calls.
- Events such as `DataPacketReceiveEvent`, `DataPacketSendEvent` and `PlayerMoveEvent` are optimized away almost completely by this change, offering some much-needed performance gains.
- Significantly improved performance of small moving entities, such as dropped items.
- This was achieved by a combination of changes, which together improved observed performance with 2000 item entities moving in water by 30-40%.
- The benefit of this will be most noticeable in SkyBlock servers, where large cactus farms can generate thousands of dropped items.
- `World->getCollisionBoxes()` now uses an improved search method, which reduces the work done by the function by almost 90% for small entities.
- This improves performance of collision detection for small entities, such as dropped items.
## Gameplay
### General
- Implemented enchanting using an enchanting table (yes, finally!)
- Thanks to [@S3v3Nice](https://github.com/S3v3Nice) for investing lots of time and effort into developing this.
- Since this feature is quite complex, it's possible there may be bugs. Please be vigilant and report any issues you find.
### Blocks
- The following new blocks have been implemented:
- Pink Petals
- Pressure plates are now functional, in the sense that they react when entities stand on them and perform the correct logic.
- Note that since redstone is not yet implemented, pressure plates do not activate any redstone devices, similar to buttons and levers.
- Signs can now be edited by right-clicking them.
- Signs can now be waxed using a honeycomb, which prevents them from being edited.
### Items
- The following new items have been implemented:
- Enchanted Book
## API
### `pocketmine\block`
- The following new API methods have been added:
- `public Block->getEnchantmentTags() : list<string>` returns a list of strings indicating which types of enchantment can be applied to the block when in item form
- `public BlockTypeInfo->getEnchantmentTags() : list<string>`
- `protected PressurePlate->getActivationBox() : AxisAlignedBB` - returns the AABB entities must intersect with in order to activate the pressure plate (not the same as the visual shape)
- `protected PressurePlate->hasOutputSignal() : bool` - returns whether the pressure plate has an output signal - this should be implemented by subclasses
- `protected PressurePlate->calculatePlateState() : array{Block, ?bool}` - returns the state the pressure plate will change to if the given list of entities are standing on it, and a bool indicating whether the plate activated or deactivated this tick
- `protected PressurePlate->filterIrrelevantEntities(list<Entity> $entities) : list<Entity>` - returns the given list filtered of entities that don't affect the plate's state (e.g. dropped items don't affect stone pressure plates)
- `public BaseSign->isWaxed() : bool`
- `public BaseSign->setWaxed(bool $waxed) : $this`
- `public inventory\EnchantInventory->getInput() : Item`
- `public inventory\EnchantInventory->getLapis() : Item`
- `public inventory\EnchantInventory->getOutput(int $optionId) : ?Item` - returns the item that would be produced if the input item was enchanted with the selected option, or `null` if the option is invalid
- `public inventory\EnchantInventory->getOption(int $optionId) : EnchantOption` - returns the enchanting option at the given index
- The following API methods have signature changes:
- `BlockTypeInfo->__construct()` now accepts an optional `list<string> $enchantmentTags` parameter
- `PressurePlate->__construct()` now accepts an optional `int $deactivationDelayTicks` parameter
- `WeightedPressurePlate->__construct()` now accepts optional `int $deactivationDelayTicks` and `float $signalStrengthFactor` parameters
- `SimplePressurePlate->__construct()` now accepts an optional `int $deactivationDelayTicks` parameter
- The following new classes have been added:
- `PinkPetals`
- `utils\BlockEventHelper` - provides helper methods for calling block-related events
- The following classes have been deprecated:
- `WeightedPressurePlateLight`
- `WeightedPressurePlateHeavy`
### `pocketmine\entity`
- The following new API methods have been added:
- `public Human->getEnchantmentSeed() : int` - returns the current seed used to randomize options shown on the enchanting table for this human
- `public Human->setEnchantmentSeed(int $seed) : void`
- `public Human->regenerateEnchantmentSeed() : void` - returns a new randomly generated seed which can be set with `setEnchantmentSeed()`
### `pocketmine\event`
- The following new classes have been added:
- `block\FarmlandHydrationChangeEvent` - called when farmland is hydrated or dehydrated
- `block\PressurePlateUpdateEvent` - called when a pressure plate is activated or changes its power output
- `player\PlayerEnchantingOptionsRequestEvent` - called when a player puts an item to be enchanted into an enchanting table, to allow plugins to modify the enchanting options shown
- `player\PlayerItemEnchantEvent` - called when a player enchants an item in an enchanting table
- `world\WorldDifficultyChangeEvent` - called when a world's difficulty is changed
- The following new API methods have been added:
- `public static Event::hasHandlers() : bool` - returns whether the event class has any registered handlers - used like `SomeEvent::hasHandlers()`
- `public HandlerListManager->getHandlersFor(class-string<? extends Event> $event) : list<RegisteredListener>` - returns a list of all registered listeners for the given event class, using cache if available
### `pocketmine\inventory\transaction`
- The following new classes have been added:
- `EnchantingTransaction` - used when a player enchants an item in an enchanting table
### `pocketmine\item`
- The following new API methods have been added:
- `public Armor->getMaterial() : ArmorMaterial` - returns an object containing properties shared by all items of the same armor material
- `public ArmorTypeInfo->getMaterial() : ArmorMaterial`
- `public Item->getEnchantability() : int` - returns the enchantability value of the item - higher values increase the chance of more powerful enchantments being offered by an enchanting table
- `public Item->getEnchantmentTags() : list<string>` - returns a list of strings indicating which types of enchantment can be applied to the item
- `public ToolTier->getEnchantability() : int`
- The following API methods have signature changes:
- `Item->__construct()` now accepts an optional `list<string> $enchantmentTags` parameter
- `ArmorTypeInfo->__construct()` now accepts an optional `?ArmorMaterial $material` parameter
- The following new classes have been added:
- `ArmorMaterial` - container for shared armor properties
- `VanillaArmorMaterials` - all vanilla armor materials
- `EnchantedBook` - represents an enchanted book item
### `pocketmine\item\enchantment`
- The following new classes have been added:
- `AvailableEnchantmentRegistry` - enchantments to be displayed on the enchanting table are selected from here - custom enchantments may be added
- `EnchantingHelper` - static class containing various helper methods for enchanting tables
- `EnchantingOption` - represents an option on the enchanting table menu
- `IncompatibleEnchantmentGroups` - list of constants naming groups of enchantments that are incompatible with each other - custom enchantments may be added using these group names to make them incompatible with existing enchantments in the same group
- `IncompatibleEnchantmentRegistry` - manages which enchantments are considered incompatible with each other - custom enchantments may be added using existing group names to make them incompatible with existing enchantments in the same group, or to entirely new groups
- `ItemEnchantmentTagRegistry` - manages item enchantment compatibility tags and which tags include which other tags
- `ItemEnchantmentTags` - list of constants naming item types for enchantment compatibility checks
- The following classes have been deprecated
- `ItemFlags`
- The following API methods have been added:
- `public Enchantment->isCompatibleWith(Enchantment $other) : bool`
- `public Enchantment->getMinEnchantingPower()` - returns the minimum enchanting power (derived from enchantability and number of bookshelves) needed to allow this enchantment to show on the enchanting table with a given level
- `public Enchantment->getMaxEnchantingPower()` - upper limit of enchanting power for this enchantment to be offered on the enchanting table with a given level
- The following API methods have signature changes:
- `Enchantment->__construct()` now accepts optional `(\Closure(int $level) : int)|null $minEnchantingPower` and `int $enchantingPowerRange` parameters
- `Enchantment->__construct()` parameters `$primaryItemFlags` and `$secondaryItemFlags` are now deprecated and no longer used
- `ProtectionEnchantment->__construct()` has extra parameters to reflect `Enchantment->__construct()` changes
- The following API methods have been deprecated:
- `Enchantment->getPrimaryItemFlags()` - use API methods provided by `AvailableEnchantmentRegistry` instead
- `Enchantment->getSecondaryItemFlags()` - use API methods provided by `AvailableEnchantmentRegistry` instead
- `Enchantment->hasPrimaryItemType()`
- `Enchantment->hasSecondaryItemType()`
### `pocketmine\plugin`
- The following new API methods have been added:
- `public PluginBase->getResourcePath(string $filename) : string` - returns a URI to an embedded resource file that can be used with `file_get_contents()` and similar functions
- `public PluginBase->getResourceFolder() : string` - returns a URI to the plugin's folder of embedded resources
- The following API methods have been deprecated:
- `PluginBase->getResource()` - prefer using `getResourcePath()` with `file_get_contents()` or other PHP built-in functions instead
### `pocketmine\resourcepacks`
- The following new API methods have been added:
- `public ResourcePackManager->setResourcePacksRequired(bool $value) : void` - sets whether players must accept resource packs in order to join
### `pocketmine\world\generator`
- The following new API methods have been added:
- `public GeneratorManager->addAlias(string $name, string $alias) : void` - allows registering a generator alias without copying the generator registration parameters
### `pocketmine\world\sound`
- The following new classes have been added:
- `PressurePlateActivateSound`
- `PressurePlateDeactivateSound`
### `pocketmine\utils`
- The following new API methods have been added:
- `public StringToTParser->registerAlias(string $existing, string $alias) : void` - allows registering a string alias without copying registration parameters
## Internals
- Various `TypeIdMap` classes in the `pocketmine\data\bedrock` package now use the new `IntSaveIdMapTrait` to reduce code duplication.
- Added a new `ServerProperties` class containing constants for all known `server.properties` keys.
- Added a new `YmlServerProperties` class containing generated constants for all known `pocketmine.yml` keys. These keys can be used with `Config->getNested()`.

34
changelogs/5.6.md Normal file
View File

@ -0,0 +1,34 @@
# 5.6.0
Released 20th September 2023.
**For Minecraft: Bedrock Edition 1.20.30**
This is a support release for Minecraft: Bedrock Edition 1.20.30.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.30.
- Removed support for older versions.
## Fixes
- Fixed support conditions for hanging roots, cave vines and dead bushes.
- Fixed connection conditions for fences, glass panes, iron bars, and walls.
# 5.6.1
Released 20th October 2023.
## Performance
- Improved performance of cactus growth by disabling neighbour updates when only the age property was updated. While this isn't a perfect solution, it provides significant performance gains for servers with large cactus farms.
## Fixes
- Fixed `tools/generate-bedrock-data-from-packets.php` incorrectly interpreting network meta as blockstates in some cases (broken crafting recipes).
- Fixed crafting recipes involving beds, skulls and some other items not working correctly (incorrectly interpreted data).
- Fixed crashes when flower pot or cauldron blockentities exist in places where they shouldn't (leftovers from upgraded PM3 worlds).
- Fixed `Entity->broadcastSound()` not firing `WorldSoundEvent` (bypassing internal sound system).
- Fixed wooden signs, buttons and doors not being able to be used as furnace fuel.
- Fixed bone meal and tools only working when used on the top side of dirt and grass. Bone meal now works from any side, and tools work on any side except the bottom.

27
changelogs/5.7.md Normal file
View File

@ -0,0 +1,27 @@
# 5.7.0
Released 26th October 2023.
**For Minecraft: Bedrock Edition 1.20.40**
This is a support release for Minecraft: Bedrock Edition 1.20.40.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.40.
- Removed support for older versions.
## Fixes
- Fixed `cartography_table`, `smithing_table`, `stripped_cherry_log` and `stripped_cherry_wood` not working in `StringToItemParser`.
- Fixed `Promise<null>::onCompletion()` always calling the reject handler if the promise was already completed.
# 5.7.1
Released 1st November 2023.
## Fixes
- Fixed non-reentrant-safe code in `PermissionManager` and various other subscriber subsystems.
- These issues caused server crashes when deleting a subscriber indirectly triggered the deletion of other subscribers (e.g. due to the GC activating in `unset()`).

138
changelogs/5.8.md Normal file
View File

@ -0,0 +1,138 @@
# 5.8.0
Released 1st November 2023.
**Borked release, forgot to merge branches.**
# 5.8.1
Released 1st November 2023.
**For Minecraft: Bedrock Edition 1.20.40**
This is a minor feature release, including new gameplay features, various performance improvements to internal `World` and `Block` systems, and changes to the API.
**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
- Neighbour block updates now have a separate timer for timings. Previously, these were counted under `Scheduled Block Updates`, which was misleading.
## Performance
- `LightUpdate` now avoids attempting to propagate back in the same direction the light came from. This produces a small performance improvement of around 6% in light propagation.
- Improved worst-case (non-cached) performance of `World::getCollisionBlocks()` (and its successor `World::getBlockCollisionBlocks()`).
- While 5.5.0 introduced caching at the `World` level for AABBs, the cache was rarely useful due to entity and player movement being too unpredictable. This meant that most users saw a performance degradation with lots of moving entities, except in specific situations.
- Performance for fetching non-cached AABBs for a cell is now improved by 2x. Overall performance benefit to a server depends on the number of entities and players.
- Added cache for hydrated farmland blocks to remember the last known location of nearby water.
- If nearby water sources are not changed, this cache allows hydrated farmland to completely avoid checking up to 161 nearby blocks for water after the first check.
- Tests with large wheat farms showed a 25% performance improvement in overall server performance compared to previous 5.x versions.
- Migrated various internal enums to native PHP 8.1 enums. Bypassing magic `__callStatic()` accessors improved performance in many areas, although it's hard to quantify the exact benefit.
- Made use of `Facing::OFFSET` constant in various places to avoid unnecessary `Vector3` and `Position` object allocations. Many pathways benefit from this, including neighbour block updates (due to faster `Block::getSide()` and less useless objects).
- Avoided clearing block AABB caches except when strictly necessary. Previously, the cache was wiped every time blocks were read from the world, making them mostly useless.
- Avoided random updates on blocks which have reached their final state, such as fully-grown crops. This produces a minimal performance improvement.
- Removed useless checks in some `World` hot paths.
## API
### General
- All enums have been migrated to native PHP 8.1 enums.
- For now, the old APIs and accessors are still usable (via `LegacyEnumShimTrait`), but these will be removed in the next major release.
- `EnumTrait` has been deprecated, and will be removed in the next major release.
- Migration for most plugin developers will simply involve deleting `()` from the end of enum case usages, which is a trivial change and also improves performance.
- Plugin usages of `EnumTrait` are encouraged to move to native enums, optionally using `LegacyEnumShimTrait` to provide backwards compatibility.
- See [this code](https://github.com/pmmp/PocketMine-MP/blob/9832fe899f13a8ea47cc9d73de7088f7775a12f5/src/block/utils/DyeColor.php#L85-L107) for an example of how to associate properties with enum cases (since native enums don't support this directly).
- Thanks to improvements in `RuntimeDataDescriber`, any native enum can now be used as a custom block's state property.
### `pocketmine\block`
- The following classes have been added:
- `utils\AgeableTrait` - used by blocks which have an age property, such as crops
- `utils\StaticSupportTrait` - used by blocks which have the same support requirements regardless of their state, such as crops
### `pocketmine\data\runtime`
- The following API methods have been added:
- `public RuntimeDataDescriber->boundedIntAuto(int $min, int $max, int &$value) : void` - similar to `boundedInt()`, but automatically calculates the needed number of bits based on the given min/max
- `public RuntimeDataDescriber->enum(T extends \UnitEnum &$case) : void` - describes any native PHP 8.1 enum case
- `public RuntimeDataDescriber->enumSet(array<int, T extends \UnitEnum> &$set, array<int, T extends \UnitEnum> $allCases) : void` - describes a set of enum cases (similar to bitflags)
- The following API methods have been deprecated:
- `RuntimeDataDescriber->bellAttachmentType()` - use `enum()` instead
- `RuntimeDataDescriber->boundedInt()` - use `boundedIntAuto()` instead
- `RuntimeDataDescriber->brewingStandSlots()` - use `enumSet()` instead
- `RuntimeDataDescriber->copperOxidation()` - use `enum()` instead
- `RuntimeDataDescriber->coralType()` - use `enum()` instead
- `RuntimeDataDescriber->dirtType()` - use `enum()` instead
- `RuntimeDataDescriber->dripleafState()` - use `enum()` instead
- `RuntimeDataDescriber->dyeColor()` - use `enum()` instead
- `RuntimeDataDescriber->froglightType()` - use `enum()` instead
- `RuntimeDataDescriber->leverFacing()` - use `enum()` instead
- `RuntimeDataDescriber->medicineType()` - use `enum()` instead
- `RuntimeDataDescriber->mobHeadType()` - use `enum()` instead
- `RuntimeDataDescriber->mushroomBlockType()` - use `enum()` instead
- `RuntimeDataDescriber->potionType()` - use `enum()` instead
- `RuntimeDataDescriber->slabType()` - use `enum()` instead
- `RuntimeDataDescriber->suspiciousStewType()` - use `enum()` instead
### `pocketmine\player`
- `TitleID` is now included in `PlayerInfo` metadata for plugin consumption.
### `pocketmine\world`
- The following API methods have been added:
- `public World->getBlockCollisionBoxes(AxisAlignedBB $bb) : list<AxisAlignedBB>` - similar to `getCollisionBoxes` but exclusively for blocks, avoiding the need for conditionally useless parameters
- The following API methods have been deprecated:
- `World->getCollisionBoxes()` - use `getBlockCollisionBoxes()` instead (alongside `getCollidingEntities()` if entity collision boxes are also required)
### `pocketmine\utils`
- The following classes have been deprecated:
- `EnumTrait` - use native PHP 8.1 enums instead
- The following classes have been added:
- `LegacyEnumShimTrait` - can be `use`d by native PHP 8.1 enums to provide the same API as `EnumTrait`
## Gameplay
### Blocks
- Implemented the following blocks:
- Amethyst
- Amethyst Cluster
- Chiseled Bookshelf
- Crimson Roots
- Pitcher Crop
- Pitcher Plant
- Torchflower
- Torchflower Crop
- Warped Roots
### Items
- Implemented the following items:
- Pitcher Pod
- Torchflower Seeds
## Internals
- `Farmland` block now has an extra property (`waterPositionIndex`) stored in its blockstate ID to track the position of nearby water. This property is not saved to disk, and is only used for caching.
- The format of internal blockstate ID has been updated.
- The lower `11` bits are now reserved for state data (previously `8` bits). This increase facilitates the new cache for `Farmland` blocks.
- The state data bits are now XOR'd with the `xxh3` of the block's type ID, instead of being directly XOR'd with the type ID.
- This XOR improves the distribution of the lower bits of the blockstate ID, which is important for hashtable indexing to minimize collisions.
- Previously, the lower bits were XOR'd with the type ID directly, but the effectiveness of this reduced as more state data bits were added.
- `xxh3` produces consistently good results for this purpose regardless of the number of state data bits allocated.
- Hash collisions with blockstate IDs are reduced by 50% with this change, which is a happy side effect.
- This is backwards-incompatible, so anyone saving internal blockstate IDs on disk or in a DB will be burned by this change (though they shouldn't have been doing that anyway).
- Removed code generation step for `RuntimeDataDescriber` enum serialization. All described enums now use PHP 8.1 native enums, which can be described without codegen using `RuntimeDataDescriber->enum()`.
- Added `DeprecatedLegacyEnumAccessRule` custom PHPStan rule to flag legacy `EnumTrait` case accessors.
- Cleaned up remaining hardcoded `Config` keys in `SetupWizard`. These usages now use auto-generated constants like the rest of the codebase.
# 5.8.2
Released 9th November 2023.
## Performance
- Improved performance of small packet zero-compression (unintended use of slow zlib compressor instead of fast libdeflate one).
- This affected the majority of outbound packets, as most packets are below the 256-byte threshold for compression.
- This faster method is over 20x faster than the old method, producing noticeable performance gains for large servers.
## Fixes
- Fixed melons and pumpkins not growing.
- Fixed melon and pumpkin stems not attaching to the grown melon/pumpkin.
- Fixed iron and gold ores not being affected by the Fortune enchantment.
- Fixed ancient debris burning in lava.
- Fixed sign (front) text loading from vanilla world saves (back text is not yet supported).
## Internals
- Removed bogus optimization from `tools/generate-blockstate-upgrade-schema.php` that could cause incorrect `remappedStates` generation when some of the states stayed under the old ID.
- Fixed possible crash in `BlockStateUpgrader` name flattening rule handling with invalid blockstate NBT data.

30
changelogs/5.9.md Normal file
View File

@ -0,0 +1,30 @@
# 5.9.0
Released 6th December 2023.
**For Minecraft: Bedrock Edition 1.20.50**
This is a support release for Minecraft: Bedrock Edition 1.20.50.
**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.50.
- Removed support for older versions.
## Fixes
- Fixed `pitcher_plant` and `pitcher_pod` not being accepted by `StringToItemParser` (and therefore not being usable in commands).
- Rotation of items in item frames in worlds from newer versions of Bedrock is now correctly loaded.
- Fixed possible crash in block update sending if a chunk was unloaded during a previous chunk's block update syncing.
- Fixed `AsyncTask::fetchLocal()` throwing an exception if `null` was stored in the local storage.
## Documentation
- `Server::prepareBatch()` is now correctly marked as `@internal`.
- Updated documentation for `Server::prepareBatch()` to accurately reflect its behaviour.
- Fixed incorrect path in doc comments of `EnumTrait` and `RegistryTrait`.
## Internals
- Added PHP 8.3 to the test matrix. This has not been thoroughly tested yet, so it should only be used for testing purposes.

View File

@ -32,11 +32,11 @@
"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.1.0+bedrock-1.20.10",
"pocketmine/bedrock-data": "~2.4.0+bedrock-1.20.10",
"pocketmine/bedrock-item-upgrade-schema": "~1.4.0+bedrock-1.20.10",
"pocketmine/bedrock-protocol": "~23.0.2+bedrock-1.20.10",
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
"pocketmine/bedrock-block-upgrade-schema": "~3.6.0+bedrock-1.20.70",
"pocketmine/bedrock-data": "~2.9.0+bedrock-1.20.70",
"pocketmine/bedrock-item-upgrade-schema": "~1.8.0+bedrock-1.20.70",
"pocketmine/bedrock-protocol": "~29.0.0+bedrock-1.20.70",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
@ -49,13 +49,13 @@
"pocketmine/raklib-ipc": "^0.2.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.16",
"phpstan/phpstan": "1.10.60",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^10.1"
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
},
"autoload": {
"psr-4": {

379
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
rules:
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule

50
src/BootstrapOptions.php Normal file
View File

@ -0,0 +1,50 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine;
/**
* 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";
}

View File

@ -30,6 +30,7 @@ use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings;
use pocketmine\utils\Process;
use pocketmine\utils\Utils;
use pocketmine\YmlServerProperties as Yml;
use Symfony\Component\Filesystem\Path;
use function arsort;
use function count;
@ -109,7 +110,7 @@ class MemoryManager{
}
private function init(ServerConfigGroup $config) : void{
$this->memoryLimit = $config->getPropertyInt("memory.main-limit", 0) * 1024 * 1024;
$this->memoryLimit = $config->getPropertyInt(Yml::MEMORY_MAIN_LIMIT, 0) * 1024 * 1024;
$defaultMemory = 1024;
@ -127,7 +128,7 @@ class MemoryManager{
}
}
$hardLimit = $config->getPropertyInt("memory.main-hard-limit", $defaultMemory);
$hardLimit = $config->getPropertyInt(Yml::MEMORY_MAIN_HARD_LIMIT, $defaultMemory);
if($hardLimit <= 0){
ini_set("memory_limit", '-1');
@ -135,22 +136,22 @@ class MemoryManager{
ini_set("memory_limit", $hardLimit . "M");
}
$this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024;
$this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE);
$this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true);
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
$this->globalMemoryLimit = $config->getPropertyInt(Yml::MEMORY_GLOBAL_LIMIT, 0) * 1024 * 1024;
$this->checkRate = $config->getPropertyInt(Yml::MEMORY_CHECK_RATE, self::DEFAULT_CHECK_RATE);
$this->continuousTrigger = $config->getPropertyBool(Yml::MEMORY_CONTINUOUS_TRIGGER, true);
$this->continuousTriggerRate = $config->getPropertyInt(Yml::MEMORY_CONTINUOUS_TRIGGER_RATE, self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEFAULT_TICKS_PER_GC);
$this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true);
$this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true);
$this->garbageCollectionPeriod = $config->getPropertyInt(Yml::MEMORY_GARBAGE_COLLECTION_PERIOD, self::DEFAULT_TICKS_PER_GC);
$this->garbageCollectionTrigger = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER, true);
$this->garbageCollectionAsync = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER, true);
$this->lowMemChunkRadiusOverride = $config->getPropertyInt("memory.max-chunks.chunk-radius", 4);
$this->lowMemChunkGC = $config->getPropertyBool("memory.max-chunks.trigger-chunk-collect", true);
$this->lowMemChunkRadiusOverride = $config->getPropertyInt(Yml::MEMORY_MAX_CHUNKS_CHUNK_RADIUS, 4);
$this->lowMemChunkGC = $config->getPropertyBool(Yml::MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT, true);
$this->lowMemDisableChunkCache = $config->getPropertyBool("memory.world-caches.disable-chunk-cache", true);
$this->lowMemClearWorldCache = $config->getPropertyBool("memory.world-caches.low-memory-trigger", true);
$this->lowMemDisableChunkCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE, true);
$this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true);
$this->dumpWorkers = $config->getPropertyBool("memory.memory-dump.dump-async-worker", true);
$this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true);
gc_enable();
}
@ -359,7 +360,7 @@ class MemoryManager{
'_SESSION' => true
];
foreach(Utils::stringifyKeys($GLOBALS) as $varName => $value){
foreach($GLOBALS as $varName => $value){
if(isset($ignoredGlobals[$varName])){
continue;
}

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';
@ -144,6 +148,13 @@ namespace pocketmine {
$messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
}
if(($libdeflate_version = phpversion("libdeflate")) !== false){
//make sure level 0 compression is available
if(version_compare($libdeflate_version, "0.2.0") < 0 || version_compare($libdeflate_version, "0.3.0") >= 0){
$messages[] = "php-libdeflate ^0.2.0 is required, while you have $libdeflate_version.";
}
}
if(extension_loaded("pocketmine")){
$messages[] = "The native PocketMine extension is no longer supported.";
}
@ -159,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)){
@ -266,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)){
@ -301,10 +317,10 @@ 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]);
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();
@ -317,7 +333,7 @@ JIT_WARNING
$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;
@ -80,7 +80,6 @@ use pocketmine\player\PlayerDataProvider;
use pocketmine\player\PlayerDataSaveException;
use pocketmine\player\PlayerInfo;
use pocketmine\plugin\PharPluginLoader;
use pocketmine\plugin\Plugin;
use pocketmine\plugin\PluginEnableOrder;
use pocketmine\plugin\PluginGraylist;
use pocketmine\plugin\PluginManager;
@ -120,11 +119,13 @@ use pocketmine\world\Position;
use pocketmine\world\World;
use pocketmine\world\WorldCreationOptions;
use pocketmine\world\WorldManager;
use pocketmine\YmlServerProperties as Yml;
use Ramsey\Uuid\UuidInterface;
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;
@ -357,15 +358,15 @@ class Server{
}
public function getPort() : int{
return $this->configGroup->getConfigInt("server-port", self::DEFAULT_PORT_IPV4);
return $this->configGroup->getConfigInt(ServerProperties::SERVER_PORT_IPV4, self::DEFAULT_PORT_IPV4);
}
public function getPortV6() : int{
return $this->configGroup->getConfigInt("server-portv6", self::DEFAULT_PORT_IPV6);
return $this->configGroup->getConfigInt(ServerProperties::SERVER_PORT_IPV6, self::DEFAULT_PORT_IPV6);
}
public function getViewDistance() : int{
return max(2, $this->configGroup->getConfigInt("view-distance", self::DEFAULT_MAX_VIEW_DISTANCE));
return max(2, $this->configGroup->getConfigInt(ServerProperties::VIEW_DISTANCE, self::DEFAULT_MAX_VIEW_DISTANCE));
}
/**
@ -376,12 +377,12 @@ class Server{
}
public function getIp() : string{
$str = $this->configGroup->getConfigString("server-ip");
$str = $this->configGroup->getConfigString(ServerProperties::SERVER_IPV4);
return $str !== "" ? $str : "0.0.0.0";
}
public function getIpV6() : string{
$str = $this->configGroup->getConfigString("server-ipv6");
$str = $this->configGroup->getConfigString(ServerProperties::SERVER_IPV6);
return $str !== "" ? $str : "::";
}
@ -390,30 +391,30 @@ class Server{
}
public function getGamemode() : GameMode{
return GameMode::fromString($this->configGroup->getConfigString("gamemode", GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL();
return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE)) ?? GameMode::SURVIVAL;
}
public function getForceGamemode() : bool{
return $this->configGroup->getConfigBool("force-gamemode", false);
return $this->configGroup->getConfigBool(ServerProperties::FORCE_GAME_MODE, false);
}
/**
* Returns Server global difficulty. Note that this may be overridden in individual worlds.
*/
public function getDifficulty() : int{
return $this->configGroup->getConfigInt("difficulty", World::DIFFICULTY_NORMAL);
return $this->configGroup->getConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_NORMAL);
}
public function hasWhitelist() : bool{
return $this->configGroup->getConfigBool("white-list", false);
return $this->configGroup->getConfigBool(ServerProperties::WHITELIST, false);
}
public function isHardcore() : bool{
return $this->configGroup->getConfigBool("hardcore", false);
return $this->configGroup->getConfigBool(ServerProperties::HARDCORE, false);
}
public function getMotd() : string{
return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME);
return $this->configGroup->getConfigString(ServerProperties::MOTD, self::DEFAULT_SERVER_NAME);
}
public function getLoader() : ThreadSafeClassLoader{
@ -496,7 +497,7 @@ class Server{
}
public function shouldSavePlayerData() : bool{
return $this->configGroup->getPropertyBool("player.save-player-data", true);
return $this->configGroup->getPropertyBool(Yml::PLAYER_SAVE_PLAYER_DATA, true);
}
public function getOfflinePlayer(string $name) : Player|OfflinePlayer|null{
@ -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);
}
});
@ -570,6 +571,7 @@ class Server{
$playerPromiseResolver = new PromiseResolver();
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
/** @see Player::__construct() */
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
if(!$player->hasPlayedBefore()){
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
@ -736,7 +738,7 @@ class Server{
* @return string[][]
*/
public function getCommandAliases() : array{
$section = $this->configGroup->getProperty("aliases");
$section = $this->configGroup->getProperty(Yml::ALIASES);
$result = [];
if(is_array($section)){
foreach($section as $key => $value){
@ -811,36 +813,36 @@ class Server{
$this->configGroup = new ServerConfigGroup(
new Config($pocketmineYmlPath, Config::YAML, []),
new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES, [
"motd" => self::DEFAULT_SERVER_NAME,
"server-port" => self::DEFAULT_PORT_IPV4,
"server-portv6" => self::DEFAULT_PORT_IPV6,
"enable-ipv6" => true,
"white-list" => false,
"max-players" => self::DEFAULT_MAX_PLAYERS,
"gamemode" => GameMode::SURVIVAL()->name(),
"force-gamemode" => false,
"hardcore" => false,
"pvp" => true,
"difficulty" => World::DIFFICULTY_NORMAL,
"generator-settings" => "",
"level-name" => "world",
"level-seed" => "",
"level-type" => "DEFAULT",
"enable-query" => true,
"auto-save" => true,
"view-distance" => self::DEFAULT_MAX_VIEW_DISTANCE,
"xbox-auth" => true,
"language" => "eng"
ServerProperties::MOTD => self::DEFAULT_SERVER_NAME,
ServerProperties::SERVER_PORT_IPV4 => self::DEFAULT_PORT_IPV4,
ServerProperties::SERVER_PORT_IPV6 => self::DEFAULT_PORT_IPV6,
ServerProperties::ENABLE_IPV6 => true,
ServerProperties::WHITELIST => false,
ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS,
ServerProperties::GAME_MODE => GameMode::SURVIVAL->name, //TODO: this probably shouldn't use the enum name directly
ServerProperties::FORCE_GAME_MODE => false,
ServerProperties::HARDCORE => false,
ServerProperties::PVP => true,
ServerProperties::DIFFICULTY => World::DIFFICULTY_NORMAL,
ServerProperties::DEFAULT_WORLD_GENERATOR_SETTINGS => "",
ServerProperties::DEFAULT_WORLD_NAME => "world",
ServerProperties::DEFAULT_WORLD_SEED => "",
ServerProperties::DEFAULT_WORLD_GENERATOR => "DEFAULT",
ServerProperties::ENABLE_QUERY => true,
ServerProperties::AUTO_SAVE => true,
ServerProperties::VIEW_DISTANCE => self::DEFAULT_MAX_VIEW_DISTANCE,
ServerProperties::XBOX_AUTH => true,
ServerProperties::LANGUAGE => "eng"
])
);
$debugLogLevel = $this->configGroup->getPropertyInt("debug.level", 1);
$debugLogLevel = $this->configGroup->getPropertyInt(Yml::DEBUG_LEVEL, 1);
if($this->logger instanceof MainLogger){
$this->logger->setLogDebug($debugLogLevel > 1);
}
$this->forceLanguage = $this->configGroup->getPropertyBool("settings.force-language", false);
$selectedLang = $this->configGroup->getConfigString("language", $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE));
$this->forceLanguage = $this->configGroup->getPropertyBool(Yml::SETTINGS_FORCE_LANGUAGE, false);
$selectedLang = $this->configGroup->getConfigString(ServerProperties::LANGUAGE, $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE));
try{
$this->language = new Language($selectedLang);
}catch(LanguageNotFoundException $e){
@ -853,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("settings.enable-dev-builds", false)){
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("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();
@ -876,9 +878,9 @@ 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("settings.async-workers", "auto")) === "auto"){
if(($poolSize = $this->configGroup->getPropertyString(Yml::SETTINGS_ASYNC_WORKERS, "auto")) === "auto"){
$poolSize = 2;
$processors = Utils::getCoreCount() - 2;
@ -889,32 +891,32 @@ class Server{
$poolSize = max(1, (int) $poolSize);
}
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt("memory.async-worker-hard-limit", 256)), $this->autoloader, $this->logger, $this->tickSleeper);
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper);
$netCompressionThreshold = -1;
if($this->configGroup->getPropertyInt("network.batch-threshold", 256) >= 0){
$netCompressionThreshold = $this->configGroup->getPropertyInt("network.batch-threshold", 256);
if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){
$netCompressionThreshold = $this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256);
}
if($netCompressionThreshold < 0){
$netCompressionThreshold = null;
}
$netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6);
$netCompressionLevel = $this->configGroup->getPropertyInt(Yml::NETWORK_COMPRESSION_LEVEL, 6);
if($netCompressionLevel < 1 || $netCompressionLevel > 9){
$this->logger->warning("Invalid network compression level $netCompressionLevel set, setting to default 6");
$netCompressionLevel = 6;
}
ZlibCompressor::setInstance(new ZlibCompressor($netCompressionLevel, $netCompressionThreshold, ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE));
$this->networkCompressionAsync = $this->configGroup->getPropertyBool("network.async-compression", true);
$this->networkCompressionAsync = $this->configGroup->getPropertyBool(Yml::NETWORK_ASYNC_COMPRESSION, true);
$this->networkCompressionAsyncThreshold = max(
$this->configGroup->getPropertyInt("network.async-compression-threshold", self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD),
$this->configGroup->getPropertyInt(Yml::NETWORK_ASYNC_COMPRESSION_THRESHOLD, self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD),
$netCompressionThreshold ?? self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD
);
EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool("network.enable-encryption", true);
EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool(Yml::NETWORK_ENABLE_ENCRYPTION, true);
$this->doTitleTick = $this->configGroup->getPropertyBool("console.title-tick", true) && Terminal::hasFormattingCodes();
$this->doTitleTick = $this->configGroup->getPropertyBool(Yml::CONSOLE_TITLE_TICK, true) && Terminal::hasFormattingCodes();
$this->operators = new Config(Path::join($this->dataPath, "ops.txt"), Config::ENUM);
$this->whitelist = new Config(Path::join($this->dataPath, "white-list.txt"), Config::ENUM);
@ -932,39 +934,39 @@ class Server{
$this->banByIP = new BanList($bannedIpsTxt);
$this->banByIP->load();
$this->maxPlayers = $this->configGroup->getConfigInt("max-players", self::DEFAULT_MAX_PLAYERS);
$this->maxPlayers = $this->configGroup->getConfigInt(ServerProperties::MAX_PLAYERS, self::DEFAULT_MAX_PLAYERS);
$this->onlineMode = $this->configGroup->getConfigBool("xbox-auth", true);
$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("hardcore", false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
$this->configGroup->setConfigInt("difficulty", World::DIFFICULTY_HARD);
if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
$this->configGroup->setConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_HARD);
}
@cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion());
$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("settings.enable-profiling", false));
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND);
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);
DefaultPermissions::registerCorePermissions();
@ -972,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");
@ -986,13 +988,13 @@ class Server{
$this->forceShutdownExit();
return;
}
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool("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());
$providerManager = new WorldProviderManager();
if(
($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null &&
($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString(Yml::LEVEL_SETTINGS_DEFAULT_FORMAT, ""))) !== null &&
$format instanceof WritableWorldProviderManagerEntry
){
$providerManager->setDefault($format);
@ -1001,10 +1003,10 @@ class Server{
}
$this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager);
$this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave()));
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval()));
$this->worldManager->setAutoSave($this->configGroup->getConfigBool(ServerProperties::AUTO_SAVE, $this->worldManager->getAutoSave()));
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt(Yml::TICKS_PER_AUTOSAVE, $this->worldManager->getAutoSaveInterval()));
$this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io"));
$this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString(Yml::AUTO_UPDATER_HOST, "update.pmmp.io"));
$this->queryInfo = new QueryInfo($this);
@ -1019,7 +1021,7 @@ class Server{
$this->forceShutdownExit();
return;
}
if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
if(!$this->enablePlugins(PluginEnableOrder::STARTUP)){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
$this->forceShutdownExit();
return;
@ -1030,7 +1032,7 @@ class Server{
return;
}
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD)){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
$this->forceShutdownExit();
return;
@ -1041,23 +1043,23 @@ class Server{
return;
}
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
if($this->configGroup->getPropertyBool(Yml::ANONYMOUS_STATISTICS_ENABLED, true)){
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
$this->sendUsage(SendUsageTask::TYPE_OPEN);
}
$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);
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder);
//TODO: move console parts to a separate component
if($this->configGroup->getPropertyBool("console.enable-input", true)){
if($this->configGroup->getPropertyBool(Yml::CONSOLE_ENABLE_INPUT, true)){
$this->console = new ConsoleReaderChildProcessDaemon($this->logger);
}
@ -1092,7 +1094,7 @@ class Server{
$anyWorldFailedToLoad = false;
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){
if($options === null){
$options = [];
}elseif(!is_array($options)){
@ -1136,30 +1138,30 @@ class Server{
}
if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString("level-name", "world");
$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("level-name", "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;
}
$generatorName = $this->configGroup->getConfigString("level-type");
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
$generatorName = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_GENERATOR);
$generatorOptions = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_GENERATOR_SETTINGS);
$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()
->setGeneratorClass($generatorClass)
->setGeneratorOptions($generatorOptions);
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_SEED));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
@ -1184,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,
@ -1199,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){
@ -1207,24 +1208,23 @@ 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;
}
private function startupPrepareNetworkInterfaces() : bool{
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
$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("enable-ipv6", true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter)
$this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter)
)
){
return false;
@ -1238,7 +1238,7 @@ class Server{
$this->network->blockAddress($entry->getName(), -1);
}
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
if($this->configGroup->getPropertyBool(Yml::NETWORK_UPNP_FORWARDING, false)){
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
}
@ -1258,9 +1258,10 @@ class Server{
*/
public function unsubscribeFromBroadcastChannel(string $channelId, CommandSender $subscriber) : void{
if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){
unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]);
if(count($this->broadcastSubscribers[$channelId]) === 0){
if(count($this->broadcastSubscribers[$channelId]) === 1){
unset($this->broadcastSubscribers[$channelId]);
}else{
unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]);
}
}
}
@ -1354,29 +1355,43 @@ class Server{
}
/**
* Broadcasts a list of packets in a batch to a list of players
* @internal
* Promises to compress the given batch buffer using the selected compressor, optionally on a separate thread.
*
* If the buffer is smaller than the batch-threshold (usually 256), the buffer will be compressed at level 0 if supported
* by the compressor. This means that the payload will be wrapped with the appropriate header and footer, but not
* actually compressed.
*
* If the buffer is larger than the async-compression-threshold (usually 10,000), the buffer may be compressed in
* a separate thread (if available).
*
* @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();
}
@ -1385,14 +1400,14 @@ class Server{
public function enablePlugins(PluginEnableOrder $type) : bool{
$allSuccess = true;
foreach($this->pluginManager->getPlugins() as $plugin){
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder() === $type){
if(!$this->pluginManager->enablePlugin($plugin)){
$allSuccess = false;
}
}
}
if($type->equals(PluginEnableOrder::POSTWORLD())){
if($type === PluginEnableOrder::POSTWORLD){
$this->commandMap->registerServerAliases();
}
@ -1453,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("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);
}
}
@ -1557,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);
}
@ -1588,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("auto-report.enabled", true)){
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;
@ -1606,15 +1621,6 @@ class Server{
}
@touch($stamp); //update file timestamp
$plugin = $dump->getData()->plugin;
if($plugin !== ""){
$p = $this->pluginManager->getPlugin($plugin);
if($p instanceof Plugin && !($p->getPluginLoader() instanceof PharPluginLoader)){
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
$report = false;
}
}
if($dump->getData()->error["type"] === \ParseError::class){
$report = false;
}
@ -1625,7 +1631,7 @@ class Server{
}
if($report){
$url = ($this->configGroup->getPropertyBool("auto-report.use-https", true) ? "https" : "http") . "://" . $this->configGroup->getPropertyString("auto-report.host", "crash.pmmp.io") . "/submit/api";
$url = ($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_USE_HTTPS, true) ? "https" : "http") . "://" . $this->configGroup->getPropertyString(Yml::AUTO_REPORT_HOST, "crash.pmmp.io") . "/submit/api";
$postUrlError = "Unknown error";
$reply = Internet::postURL($url, [
"report" => "yes",
@ -1638,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{
@ -1652,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){}
}
@ -1736,7 +1742,7 @@ class Server{
}
public function sendUsage(int $type = SendUsageTask::TYPE_STATUS) : void{
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
if($this->configGroup->getPropertyBool(Yml::ANONYMOUS_STATISTICS_ENABLED, true)){
$this->asyncPool->submitTask(new SendUsageTask($this, $type, $this->uniquePlayers));
}
$this->uniquePlayers = [];
@ -1770,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) .
@ -1835,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();

58
src/ServerProperties.php Normal file
View File

@ -0,0 +1,58 @@
<?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;
/**
* @internal
* Constants for all properties available in server.properties.
*/
final class ServerProperties{
private function __construct(){
//NOOP
}
public const AUTO_SAVE = "auto-save";
public const DEFAULT_WORLD_GENERATOR = "level-type";
public const DEFAULT_WORLD_GENERATOR_SETTINGS = "generator-settings";
public const DEFAULT_WORLD_NAME = "level-name";
public const DEFAULT_WORLD_SEED = "level-seed";
public const DIFFICULTY = "difficulty";
public const ENABLE_IPV6 = "enable-ipv6";
public const ENABLE_QUERY = "enable-query";
public const FORCE_GAME_MODE = "force-gamemode";
public const GAME_MODE = "gamemode";
public const HARDCORE = "hardcore";
public const LANGUAGE = "language";
public const MAX_PLAYERS = "max-players";
public const MOTD = "motd";
public const PVP = "pvp";
public const SERVER_IPV4 = "server-ip";
public const SERVER_IPV6 = "server-ipv6";
public const SERVER_PORT_IPV4 = "server-port";
public const SERVER_PORT_IPV6 = "server-portv6";
public const VIEW_DISTANCE = "view-distance";
public const WHITELIST = "white-list";
public const XBOX_AUTH = "xbox-auth";
}

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,9 +31,9 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.5.0-BETA1";
public const BASE_VERSION = "5.13.0";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "beta";
public const BUILD_CHANNEL = "stable";
/**
* PocketMine-MP-specific version ID for world data. Used to determine what fixes need to be applied to old world

118
src/YmlServerProperties.php Normal file
View File

@ -0,0 +1,118 @@
<?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;
/**
* @internal
* Constants for all properties available in pocketmine.yml.
* This is generated by build/generate-pocketmine-yml-property-consts.php.
* Do not edit this file manually.
*/
final class YmlServerProperties{
private function __construct(){
//NOOP
}
public const ALIASES = 'aliases';
public const ANONYMOUS_STATISTICS = 'anonymous-statistics';
public const ANONYMOUS_STATISTICS_ENABLED = 'anonymous-statistics.enabled';
public const ANONYMOUS_STATISTICS_HOST = 'anonymous-statistics.host';
public const AUTO_REPORT = 'auto-report';
public const AUTO_REPORT_ENABLED = 'auto-report.enabled';
public const AUTO_REPORT_HOST = 'auto-report.host';
public const AUTO_REPORT_SEND_CODE = 'auto-report.send-code';
public const AUTO_REPORT_SEND_PHPINFO = 'auto-report.send-phpinfo';
public const AUTO_REPORT_SEND_SETTINGS = 'auto-report.send-settings';
public const AUTO_REPORT_USE_HTTPS = 'auto-report.use-https';
public const AUTO_UPDATER = 'auto-updater';
public const AUTO_UPDATER_ENABLED = 'auto-updater.enabled';
public const AUTO_UPDATER_HOST = 'auto-updater.host';
public const AUTO_UPDATER_ON_UPDATE = 'auto-updater.on-update';
public const AUTO_UPDATER_ON_UPDATE_WARN_CONSOLE = 'auto-updater.on-update.warn-console';
public const AUTO_UPDATER_PREFERRED_CHANNEL = 'auto-updater.preferred-channel';
public const AUTO_UPDATER_SUGGEST_CHANNELS = 'auto-updater.suggest-channels';
public const CHUNK_GENERATION = 'chunk-generation';
public const CHUNK_GENERATION_POPULATION_QUEUE_SIZE = 'chunk-generation.population-queue-size';
public const CHUNK_SENDING = 'chunk-sending';
public const CHUNK_SENDING_PER_TICK = 'chunk-sending.per-tick';
public const CHUNK_SENDING_SPAWN_RADIUS = 'chunk-sending.spawn-radius';
public const CHUNK_TICKING = 'chunk-ticking';
public const CHUNK_TICKING_BLOCKS_PER_SUBCHUNK_PER_TICK = 'chunk-ticking.blocks-per-subchunk-per-tick';
public const CHUNK_TICKING_DISABLE_BLOCK_TICKING = 'chunk-ticking.disable-block-ticking';
public const CHUNK_TICKING_TICK_RADIUS = 'chunk-ticking.tick-radius';
public const CONSOLE = 'console';
public const CONSOLE_ENABLE_INPUT = 'console.enable-input';
public const CONSOLE_TITLE_TICK = 'console.title-tick';
public const DEBUG = 'debug';
public const DEBUG_LEVEL = 'debug.level';
public const LEVEL_SETTINGS = 'level-settings';
public const LEVEL_SETTINGS_DEFAULT_FORMAT = 'level-settings.default-format';
public const MEMORY = 'memory';
public const MEMORY_ASYNC_WORKER_HARD_LIMIT = 'memory.async-worker-hard-limit';
public const MEMORY_CHECK_RATE = 'memory.check-rate';
public const MEMORY_CONTINUOUS_TRIGGER = 'memory.continuous-trigger';
public const MEMORY_CONTINUOUS_TRIGGER_RATE = 'memory.continuous-trigger-rate';
public const MEMORY_GARBAGE_COLLECTION = 'memory.garbage-collection';
public const MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER = 'memory.garbage-collection.collect-async-worker';
public const MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER = 'memory.garbage-collection.low-memory-trigger';
public const MEMORY_GARBAGE_COLLECTION_PERIOD = 'memory.garbage-collection.period';
public const MEMORY_GLOBAL_LIMIT = 'memory.global-limit';
public const MEMORY_MAIN_HARD_LIMIT = 'memory.main-hard-limit';
public const MEMORY_MAIN_LIMIT = 'memory.main-limit';
public const MEMORY_MAX_CHUNKS = 'memory.max-chunks';
public const MEMORY_MAX_CHUNKS_CHUNK_RADIUS = 'memory.max-chunks.chunk-radius';
public const MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT = 'memory.max-chunks.trigger-chunk-collect';
public const MEMORY_MEMORY_DUMP = 'memory.memory-dump';
public const MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER = 'memory.memory-dump.dump-async-worker';
public const MEMORY_WORLD_CACHES = 'memory.world-caches';
public const MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE = 'memory.world-caches.disable-chunk-cache';
public const MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER = 'memory.world-caches.low-memory-trigger';
public const NETWORK = 'network';
public const NETWORK_ASYNC_COMPRESSION = 'network.async-compression';
public const NETWORK_ASYNC_COMPRESSION_THRESHOLD = 'network.async-compression-threshold';
public const NETWORK_BATCH_THRESHOLD = 'network.batch-threshold';
public const NETWORK_COMPRESSION_LEVEL = 'network.compression-level';
public const NETWORK_ENABLE_ENCRYPTION = 'network.enable-encryption';
public const NETWORK_MAX_MTU_SIZE = 'network.max-mtu-size';
public const NETWORK_UPNP_FORWARDING = 'network.upnp-forwarding';
public const PLAYER = 'player';
public const PLAYER_SAVE_PLAYER_DATA = 'player.save-player-data';
public const PLAYER_VERIFY_XUID = 'player.verify-xuid';
public const PLUGINS = 'plugins';
public const PLUGINS_LEGACY_DATA_DIR = 'plugins.legacy-data-dir';
public const SETTINGS = 'settings';
public const SETTINGS_ASYNC_WORKERS = 'settings.async-workers';
public const SETTINGS_ENABLE_DEV_BUILDS = 'settings.enable-dev-builds';
public const SETTINGS_ENABLE_PROFILING = 'settings.enable-profiling';
public const SETTINGS_FORCE_LANGUAGE = 'settings.force-language';
public const SETTINGS_PROFILE_REPORT_TRIGGER = 'settings.profile-report-trigger';
public const SETTINGS_QUERY_PLUGINS = 'settings.query-plugins';
public const SETTINGS_SHUTDOWN_MESSAGE = 'settings.shutdown-message';
public const TICKS_PER = 'ticks-per';
public const TICKS_PER_AUTOSAVE = 'ticks-per.autosave';
public const TIMINGS = 'timings';
public const TIMINGS_HOST = 'timings.host';
public const WORLDS = 'worlds';
}

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\block;
use pocketmine\block\utils\AmethystTrait;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
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\utils\AssumptionFailedError;
use pocketmine\world\BlockTransaction;
final class AmethystCluster extends Transparent{
use AmethystTrait;
use AnyFacingTrait;
public const STAGE_SMALL_BUD = 0;
public const STAGE_MEDIUM_BUD = 1;
public const STAGE_LARGE_BUD = 2;
public const STAGE_CLUSTER = 3;
private int $stage = self::STAGE_CLUSTER;
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
$w->boundedIntAuto(self::STAGE_SMALL_BUD, self::STAGE_CLUSTER, $this->stage);
}
public function getStage() : int{ return $this->stage; }
public function setStage(int $stage) : self{
if($stage < self::STAGE_SMALL_BUD || $stage > self::STAGE_CLUSTER){
throw new \InvalidArgumentException("Size must be in range " . self::STAGE_SMALL_BUD . " ... " . self::STAGE_CLUSTER);
}
$this->stage = $stage;
return $this;
}
public function getLightLevel() : int{
return match($this->stage){
self::STAGE_SMALL_BUD => 1,
self::STAGE_MEDIUM_BUD => 2,
self::STAGE_LARGE_BUD => 4,
self::STAGE_CLUSTER => 5,
default => throw new AssumptionFailedError("Invalid stage $this->stage"),
};
}
protected function recalculateCollisionBoxes() : array{
$myAxis = Facing::axis($this->facing);
$box = AxisAlignedBB::one();
foreach([Axis::Y, Axis::Z, Axis::X] as $axis){
if($axis === $myAxis){
continue;
}
$box->squash($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16);
}
$box->trim($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16));
return [$box];
}
private function canBeSupportedAt(Block $block, int $facing) : bool{
return $block->getAdjacentSupportType($facing) === SupportType::FULL;
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
return false;
}
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function isAffectedBySilkTouch() : bool{
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
if($this->stage === self::STAGE_CLUSTER){
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 4, maxBase: 4))];
}
return [];
}
public function getDropsForIncompatibleTool(Item $item) : array{
if($this->stage === self::STAGE_CLUSTER){
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 2))];
}
return [];
}
}

View File

@ -52,7 +52,7 @@ class Anvil extends Transparent implements Fallable{
private int $damage = self::UNDAMAGED;
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
$w->boundedInt(2, self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
$w->boundedIntAuto(self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
}
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@ -78,7 +78,7 @@ class Anvil extends Transparent implements Fallable{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
@ -46,6 +47,7 @@ use function mt_rand;
use const PHP_INT_MAX;
class Bamboo extends Transparent{
use StaticSupportTrait;
public const NO_LEAVES = 0;
public const SMALL_LEAVES = 1;
@ -56,7 +58,7 @@ class Bamboo extends Transparent{
protected int $leafSize = self::NO_LEAVES;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(2, self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
$w->boundedIntAuto(self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
$w->bool($this->thick);
$w->bool($this->ready);
}
@ -95,7 +97,7 @@ class Bamboo extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
private static function getOffsetSeed(int $x, int $y, int $z) : int{
@ -120,12 +122,14 @@ class Bamboo extends Transparent{
return new Vector3($retX, 0, $retZ);
}
private function canBeSupportedBy(Block $block) : bool{
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$block->getTypeId() === BlockTypeIds::GRAVEL ||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
$block->hasTypeTag(BlockTypeTags::MUD) ||
$block->hasTypeTag(BlockTypeTags::SAND);
$supportBlock->hasSameTypeId($this) ||
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
}
private function seekToTop() : Bamboo{
@ -153,14 +157,6 @@ class Bamboo extends Transparent{
return false;
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
$below = $world->getBlock($this->position->down());
if(!$this->canBeSupportedBy($below) && !$below->hasSameTypeId($this)){
$world->useBreakOn($this->position);
}
}
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->up())->canBeReplaced()){
@ -177,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

@ -23,17 +23,21 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Bamboo as ItemBamboo;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class BambooSapling extends Flowable{
use StaticSupportTrait;
private bool $ready = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@ -48,19 +52,13 @@ final class BambooSapling extends Flowable{
return $this;
}
private function canBeSupportedBy(Block $block) : bool{
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$block->getTypeId() === BlockTypeIds::GRAVEL ||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
$block->hasTypeTag(BlockTypeTags::MUD) ||
$block->hasTypeTag(BlockTypeTags::SAND);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->position->getWorld()->getBlock($blockReplace->position->down()))){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@ -73,13 +71,6 @@ final class BambooSapling extends Flowable{
return false;
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
$world->useBreakOn($this->position);
}
}
private function grow(?Player $player) : bool{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->up())->canBeReplaced()){

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

@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\block\tile\Banner as TileBanner;
use pocketmine\block\utils\BannerPatternLayer;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Banner as ItemBanner;
use pocketmine\item\Item;
@ -35,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;
@ -48,11 +46,6 @@ abstract class BaseBanner extends Transparent{
*/
protected array $patterns = [];
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::BLACK();
parent::__construct($idInfo, $name, $typeInfo);
}
public function readStateFromWorld() : Block{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
@ -95,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;
}
@ -111,7 +105,7 @@ abstract class BaseBanner extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
private function canBeSupportedBy(Block $block) : bool{

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();
@ -131,6 +131,6 @@ abstract class BaseBigDripleaf extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\FoodSource;
@ -31,27 +32,16 @@ use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
abstract class BaseCake extends Transparent implements FoodSource{
use StaticSupportTrait;
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getTypeId() !== BlockTypeIds::AIR){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){ //Replace with common break method
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
@ -33,11 +32,6 @@ use function mt_rand;
abstract class BaseCoral extends Transparent{
use CoralTypeTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->coralType = CoralType::TUBE();
parent::__construct($idInfo, $name, $typeInfo);
}
public function onNearbyBlockChange() : void{
if(!$this->dead){
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
@ -78,6 +72,6 @@ abstract class BaseCoral extends Transparent{
protected function recalculateCollisionBoxes() : array{ return []; }
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
}

View File

@ -103,7 +103,7 @@ abstract class BaseSign extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
abstract protected function getSupportingFace() : int;
@ -172,13 +172,13 @@ abstract class BaseSign extends Transparent{
}
$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
default => null
};
if($dyeColor !== null){
$color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
$color = $dyeColor === DyeColor::BLACK ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
if(
$color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
$this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
@ -271,4 +271,8 @@ abstract class BaseSign extends Transparent{
public function asItem() : Item{
return ($this->asItemCallback)();
}
public function getFuelTime() : int{
return $this->woodType->isFlammable() ? 200 : 0;
}
}

View File

@ -48,11 +48,6 @@ class Bed extends Transparent{
protected bool $occupied = false;
protected bool $head = false;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::RED();
parent::__construct($idInfo, $name, $typeInfo);
}
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->bool($this->occupied);
@ -65,6 +60,8 @@ class Bed extends Transparent{
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBed){
$this->color = $tile->getColor();
}else{
$this->color = DyeColor::RED; //legacy pre-1.1 beds don't have tiles
}
return $this;
@ -87,7 +84,7 @@ class Bed extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function isHeadPart() : bool{
@ -148,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;
@ -209,7 +206,7 @@ class Bed extends Transparent{
}
private function canBeSupportedAt(Block $block) : bool{
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
}
public function getMaxStackSize() : int{ return 1; }

View File

@ -35,32 +35,26 @@ use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\BellRingSound;
final class Bell extends Transparent{
use HorizontalFacingTrait;
private BellAttachmentType $attachmentType;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->attachmentType = BellAttachmentType::FLOOR();
parent::__construct($idInfo, $name, $typeInfo);
}
private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->bellAttachmentType($this->attachmentType);
$w->enum($this->attachmentType);
$w->horizontalFacing($this->facing);
}
protected function recalculateCollisionBoxes() : array{
if($this->attachmentType->equals(BellAttachmentType::FLOOR())){
if($this->attachmentType === BellAttachmentType::FLOOR){
return [
AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16)
];
}
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
if($this->attachmentType === BellAttachmentType::CEILING){
return [
AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4)
];
@ -72,12 +66,12 @@ final class Bell extends Transparent{
->trim(Facing::DOWN, 1 / 4);
return [
$this->attachmentType->equals(BellAttachmentType::ONE_WALL()) ? $box->trim($this->facing, 3 / 16) : $box
$this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trim($this->facing, 3 / 16) : $box
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
@ -89,7 +83,7 @@ final class Bell extends Transparent{
}
private function canBeSupportedAt(Block $block, int $face) : bool{
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
return $block->getAdjacentSupportType($face) !== SupportType::NONE;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@ -100,15 +94,15 @@ final class Bell extends Transparent{
if($player !== null){
$this->setFacing(Facing::opposite($player->getHorizontalFacing()));
}
$this->setAttachmentType(BellAttachmentType::FLOOR());
$this->setAttachmentType(BellAttachmentType::FLOOR);
}elseif($face === Facing::DOWN){
$this->setAttachmentType(BellAttachmentType::CEILING());
$this->setAttachmentType(BellAttachmentType::CEILING);
}else{
$this->setFacing($face);
$this->setAttachmentType(
$this->canBeSupportedAt($blockReplace, $face) ?
BellAttachmentType::TWO_WALLS() :
BellAttachmentType::ONE_WALL()
BellAttachmentType::TWO_WALLS :
BellAttachmentType::ONE_WALL
);
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
@ -116,11 +110,10 @@ final class Bell extends Transparent{
public function onNearbyBlockChange() : void{
foreach(match($this->attachmentType){
BellAttachmentType::CEILING() => [Facing::UP],
BellAttachmentType::FLOOR() => [Facing::DOWN],
BellAttachmentType::ONE_WALL() => [Facing::opposite($this->facing)],
BellAttachmentType::TWO_WALLS() => [$this->facing, Facing::opposite($this->facing)],
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
BellAttachmentType::CEILING => [Facing::UP],
BellAttachmentType::FLOOR => [Facing::DOWN],
BellAttachmentType::ONE_WALL => [Facing::opposite($this->facing)],
BellAttachmentType::TWO_WALLS => [$this->facing, Facing::opposite($this->facing)]
} as $supportBlockDirection){
if(!$this->canBeSupportedAt($this, $supportBlockDirection)){
$this->position->getWorld()->useBreakOn($this->position);
@ -159,10 +152,9 @@ final class Bell extends Transparent{
private function isValidFaceToRing(int $faceHit) : bool{
return match($this->attachmentType){
BellAttachmentType::CEILING() => true,
BellAttachmentType::FLOOR() => Facing::axis($faceHit) === Facing::axis($this->facing),
BellAttachmentType::ONE_WALL(), BellAttachmentType::TWO_WALLS() => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
BellAttachmentType::CEILING => true,
BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing),
BellAttachmentType::ONE_WALL, BellAttachmentType::TWO_WALLS => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
};
}
}

View File

@ -30,22 +30,16 @@ use pocketmine\entity\projectile\Projectile;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\world\sound\DripleafTiltDownSound;
use pocketmine\world\sound\DripleafTiltUpSound;
class BigDripleafHead extends BaseBigDripleaf{
protected DripleafState $leafState;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->leafState = DripleafState::STABLE();
parent::__construct($idInfo, $name, $typeInfo);
}
protected DripleafState $leafState = DripleafState::STABLE;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
parent::describeBlockOnlyState($w);
$w->dripleafState($this->leafState);
$w->enum($this->leafState);
}
protected function isHead() : bool{
@ -76,20 +70,20 @@ class BigDripleafHead extends BaseBigDripleaf{
private function getLeafTopOffset() : float{
return match($this->leafState){
DripleafState::STABLE(), DripleafState::UNSTABLE() => 1 / 16,
DripleafState::PARTIAL_TILT() => 3 / 16,
DripleafState::STABLE, DripleafState::UNSTABLE => 1 / 16,
DripleafState::PARTIAL_TILT => 3 / 16,
default => 0
};
}
public function onEntityInside(Entity $entity) : bool{
if(!$entity instanceof Projectile && $this->leafState->equals(DripleafState::STABLE())){
if(!$entity instanceof Projectile && $this->leafState === DripleafState::STABLE){
//the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath
$intersection = AxisAlignedBB::one()
->offset($this->position->x, $this->position->y, $this->position->z)
->trim(Facing::DOWN, 1 - $this->getLeafTopOffset());
if($entity->getBoundingBox()->intersectsWith($intersection)){
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE());
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE);
return false;
}
}
@ -97,22 +91,21 @@ class BigDripleafHead extends BaseBigDripleaf{
}
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT());
if($this->leafState !== DripleafState::FULL_TILT){
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT);
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
}
}
public function onScheduledUpdate() : void{
if(!$this->leafState->equals(DripleafState::STABLE())){
if($this->leafState->equals(DripleafState::FULL_TILT())){
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE()));
if($this->leafState !== DripleafState::STABLE){
if($this->leafState === DripleafState::FULL_TILT){
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE));
$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
}else{
$this->setTiltAndScheduleTick(match($this->leafState->id()){
DripleafState::UNSTABLE()->id() => DripleafState::PARTIAL_TILT(),
DripleafState::PARTIAL_TILT()->id() => DripleafState::FULL_TILT(),
default => throw new AssumptionFailedError("All types should be covered")
$this->setTiltAndScheduleTick(match($this->leafState){
DripleafState::UNSTABLE => DripleafState::PARTIAL_TILT,
DripleafState::PARTIAL_TILT => DripleafState::FULL_TILT,
});
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
}
@ -120,7 +113,7 @@ class BigDripleafHead extends BaseBigDripleaf{
}
protected function recalculateCollisionBoxes() : array{
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
if($this->leafState !== DripleafState::FULL_TILT){
return [
AxisAlignedBB::one()
->trim(Facing::DOWN, 11 / 16)

View File

@ -42,7 +42,6 @@ use pocketmine\item\enchantment\ItemEnchantmentTags;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\item\ItemBlock;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
@ -50,22 +49,26 @@ use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Binary;
use pocketmine\world\BlockTransaction;
use pocketmine\world\format\Chunk;
use pocketmine\world\Position;
use pocketmine\world\World;
use function count;
use function get_class;
use function hash;
use const PHP_INT_MAX;
class Block{
public const INTERNAL_STATE_DATA_BITS = 8;
public const INTERNAL_STATE_DATA_BITS = 11;
public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
/**
* @internal
* Hardcoded int is `Binary::readLong(hash('xxh3', Binary::writeLLong(BlockTypeIds::AIR), binary: true))`
* TODO: it would be much easier if we could just make this 0 or some other easy value
*/
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (BlockTypeIds::AIR & self::INTERNAL_STATE_DATA_MASK);
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (-7482769108513497636 & self::INTERNAL_STATE_DATA_MASK);
protected BlockIdentifier $idInfo;
protected string $fallbackName;
@ -80,6 +83,23 @@ class Block{
private Block $defaultState;
private int $stateIdXorMask;
/**
* Computes the mask to be XOR'd with the state data.
* This is to improve distribution of the state data bits, which occupy the least significant bits of the state ID.
* Improved distribution improves PHP array performance when using the state ID as a key, as PHP arrays use some of
* the lower bits of integer keys directly without hashing.
*
* The type ID is included in the XOR mask. This is not necessary to improve distribution, but it reduces the number
* of operations required to compute the state ID (micro optimization).
*/
private static function computeStateIdXorMask(int $typeId) : int{
return
$typeId << self::INTERNAL_STATE_DATA_BITS |
(Binary::readLong(hash('xxh3', Binary::writeLLong($typeId), binary: true)) & self::INTERNAL_STATE_DATA_MASK);
}
/**
* @param string $name English name of the block type (TODO: implement translations)
*/
@ -97,6 +117,9 @@ class Block{
$this->describeBlockOnlyState($calculator);
$this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
$this->stateIdXorMask = self::computeStateIdXorMask($idInfo->getBlockTypeId());
//this must be done last, otherwise the defaultState could have uninitialized fields
$defaultState = clone $this;
$this->defaultState = $defaultState;
$defaultState->defaultState = $defaultState;
@ -152,13 +175,7 @@ class Block{
* {@link RuntimeBlockStateRegistry::fromStateId()}.
*/
public function getStateId() : int{
$typeId = $this->getTypeId();
//TODO: this XOR mask improves hashtable distribution, but it's only effective if the number of unique block
//type IDs is larger than the number of available state data bits. We should probably hash (e.g. using xxhash)
//the type ID to create a better mask.
//Alternatively, we could hash the whole state ID, but this is currently problematic, since we currently need
//to be able to recover the state data from the state ID because of UnknownBlock.
return ($typeId << self::INTERNAL_STATE_DATA_BITS) | ($this->encodeFullState() ^ ($typeId & self::INTERNAL_STATE_DATA_MASK));
return $this->encodeFullState() ^ $this->stateIdXorMask;
}
/**
@ -228,12 +245,6 @@ class Block{
}
}
private function decodeFullState(int $data) : void{
$reader = new RuntimeDataReader($this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits, $data);
$this->decodeBlockItemState($reader->readInt($this->requiredBlockItemStateDataBits));
$this->decodeBlockOnlyState($reader->readInt($this->requiredBlockOnlyStateDataBits));
}
private function encodeBlockItemState() : int{
$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits);
@ -259,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;
}
/**
@ -304,34 +326,44 @@ class Block{
if($bits > Block::INTERNAL_STATE_DATA_BITS){
throw new \LogicException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits");
}
for($stateData = 0; $stateData < (1 << $bits); ++$stateData){
$v = clone $this;
for($blockItemStateData = 0; $blockItemStateData < (1 << $this->requiredBlockItemStateDataBits); ++$blockItemStateData){
$withType = clone $this;
try{
$v->decodeFullState($stateData);
if($v->encodeFullState() !== $stateData){
throw new \LogicException(static::class . "::decodeStateData() accepts invalid state data (returned " . $v->encodeFullState() . " for input $stateData)");
$withType->decodeBlockItemState($blockItemStateData);
$encoded = $withType->encodeBlockItemState();
if($encoded !== $blockItemStateData){
throw new \LogicException(static::class . "::decodeBlockItemState() accepts invalid inputs (returned $encoded for input $blockItemStateData)");
}
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
continue;
}
yield $v;
for($blockOnlyStateData = 0; $blockOnlyStateData < (1 << $this->requiredBlockOnlyStateDataBits); ++$blockOnlyStateData){
$withState = clone $withType;
try{
$withState->decodeBlockOnlyState($blockOnlyStateData);
$encoded = $withState->encodeBlockOnlyState();
if($encoded !== $blockOnlyStateData){
throw new \LogicException(static::class . "::decodeBlockOnlyState() accepts invalid inputs (returned $encoded for input $blockOnlyStateData)");
}
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
continue;
}
yield $withState;
}
}
}
/**
* Called when this block is created, set, or has a neighbouring block update, to re-detect dynamic properties which
* are not saved on the world.
*
* Clears any cached precomputed objects, such as bounding boxes. Remove any outdated precomputed things such as
* AABBs and force recalculation.
* are not saved in the blockstate ID.
* If any such properties are updated, don't forget to clear things like AABB caches if necessary.
*
* A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g.
* data from a block entity).
*/
public function readStateFromWorld() : Block{
$this->collisionBoxes = null;
return $this;
}
@ -370,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;
@ -540,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;
@ -571,6 +615,7 @@ class Block{
*/
final public function position(World $world, int $x, int $y, int $z) : void{
$this->position = new Position($x, $y, $z, $world);
$this->collisionBoxes = null;
}
/**
@ -721,8 +766,14 @@ class Block{
* @return Block
*/
public function getSide(int $side, int $step = 1){
if($this->position->isValid()){
return $this->position->getWorld()->getBlock($this->position->getSide($side, $step));
$position = $this->position;
if($position->isValid()){
[$dx, $dy, $dz] = Facing::OFFSET[$side] ?? [0, 0, 0];
return $position->getWorld()->getBlockAt(
$position->x + ($dx * $step),
$position->y + ($dy * $step),
$position->z + ($dz * $step)
);
}
throw new \LogicException("Block does not have a valid world");
@ -736,8 +787,14 @@ class Block{
*/
public function getHorizontalSides() : \Generator{
$world = $this->position->getWorld();
foreach($this->position->sidesAroundAxis(Axis::Y) as $vector3){
yield $world->getBlock($vector3);
foreach(Facing::HORIZONTAL as $facing){
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
//TODO: yield Facing as the key?
yield $world->getBlockAt(
$this->position->x + $dx,
$this->position->y + $dy,
$this->position->z + $dz
);
}
}
@ -749,8 +806,13 @@ class Block{
*/
public function getAllSides() : \Generator{
$world = $this->position->getWorld();
foreach($this->position->sides() as $vector3){
yield $world->getBlock($vector3);
foreach(Facing::OFFSET as [$dx, $dy, $dz]){
//TODO: yield Facing as the key?
yield $world->getBlockAt(
$this->position->x + $dx,
$this->position->y + $dy,
$this->position->z + $dz
);
}
}
@ -877,7 +939,7 @@ class Block{
* blocks placed on the given face can be supported by this block.
*/
public function getSupportType(int $facing) : SupportType{
return SupportType::FULL();
return SupportType::FULL;
}
protected function getAdjacentSupportType(int $facing) : SupportType{

View File

@ -562,10 +562,10 @@ final class BlockTypeIds{
public const WEIGHTED_PRESSURE_PLATE_HEAVY = 10532;
public const WEIGHTED_PRESSURE_PLATE_LIGHT = 10533;
public const WHEAT = 10534;
public const BUDDING_AMETHYST = 10535;
public const WHITE_TULIP = 10536;
public const WOOL = 10537;
public const AMETHYST_CLUSTER = 10538;
public const GLAZED_TERRACOTTA = 10539;
public const AMETHYST = 10540;
public const ANCIENT_DEBRIS = 10541;
@ -737,8 +737,16 @@ final class BlockTypeIds{
public const BIG_DRIPLEAF_HEAD = 10707;
public const BIG_DRIPLEAF_STEM = 10708;
public const PINK_PETALS = 10709;
public const CRIMSON_ROOTS = 10710;
public const WARPED_ROOTS = 10711;
public const CHISELED_BOOKSHELF = 10712;
public const TORCHFLOWER = 10713;
public const TORCHFLOWER_CROP = 10714;
public const PITCHER_PLANT = 10715;
public const PITCHER_CROP = 10716;
public const DOUBLE_PITCHER_CROP = 10717;
public const FIRST_UNUSED_BLOCK_ID = 10710;
public const FIRST_UNUSED_BLOCK_ID = 10718;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -34,6 +34,7 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function array_key_exists;
use function spl_object_id;
class BrewingStand extends Transparent{
@ -44,7 +45,7 @@ class BrewingStand extends Transparent{
protected array $slots = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->brewingStandSlots($this->slots);
$w->enumSet($this->slots, BrewingStandSlot::cases());
}
protected function recalculateCollisionBoxes() : array{
@ -61,18 +62,18 @@ class BrewingStand extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function hasSlot(BrewingStandSlot $slot) : bool{
return array_key_exists($slot->id(), $this->slots);
return array_key_exists(spl_object_id($slot), $this->slots);
}
public function setSlot(BrewingStandSlot $slot, bool $occupied) : self{
if($occupied){
$this->slots[$slot->id()] = $slot;
$this->slots[spl_object_id($slot)] = $slot;
}else{
unset($this->slots[$slot->id()]);
unset($this->slots[spl_object_id($slot)]);
}
return $this;
}
@ -89,7 +90,7 @@ class BrewingStand extends Transparent{
public function setSlots(array $slots) : self{
$this->slots = [];
foreach($slots as $slot){
$this->slots[$slot->id()] = $slot;
$this->slots[spl_object_id($slot)] = $slot;
}
return $this;
}
@ -114,7 +115,7 @@ class BrewingStand extends Transparent{
}
$changed = false;
foreach(BrewingStandSlot::getAll() as $slot){
foreach(BrewingStandSlot::cases() as $slot){
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
if($occupied !== $this->hasSlot($slot)){
$this->setSlot($slot, $occupied);

View File

@ -0,0 +1,68 @@
<?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;
use pocketmine\block\utils\AmethystTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use function array_rand;
use function mt_rand;
final class BuddingAmethyst extends Opaque{
use AmethystTrait;
public function ticksRandomly() : bool{
return true;
}
public function onRandomTick() : void{
if(mt_rand(1, 5) === 1){
$face = Facing::ALL[array_rand(Facing::ALL)];
$adjacent = $this->getSide($face);
//TODO: amethyst buds can spawn in water - we need waterlogging support for this
$newStage = null;
if($adjacent->getTypeId() === BlockTypeIds::AIR){
$newStage = AmethystCluster::STAGE_SMALL_BUD;
}elseif(
$adjacent->getTypeId() === BlockTypeIds::AMETHYST_CLUSTER &&
$adjacent instanceof AmethystCluster &&
$adjacent->getStage() < AmethystCluster::STAGE_CLUSTER &&
$adjacent->getFacing() === $face
){
$newStage = $adjacent->getStage() + 1;
}
if($newStage !== null){
BlockEventHelper::grow($adjacent, VanillaBlocks::AMETHYST_CLUSTER()->setStage($newStage)->setFacing($face), null);
}
}
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
}

View File

@ -23,39 +23,22 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Cactus extends Transparent{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 15;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function hasEntityCollision() : bool{
return true;
}
@ -69,7 +52,7 @@ class Cactus extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onEntityInside(Entity $entity) : bool{
@ -78,23 +61,18 @@ class Cactus extends Transparent{
return true;
}
private function canBeSupportedBy(Block $block) : bool{
return $block->hasSameTypeId($this) || $block->hasTypeTag(BlockTypeTags::SAND);
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$world->useBreakOn($this->position);
}else{
foreach(Facing::HORIZONTAL as $side){
$b = $this->getSide($side);
if($b->isSolid()){
$world->useBreakOn($this->position);
break;
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
if(!$supportBlock->hasSameTypeId($this) && !$supportBlock->hasTypeTag(BlockTypeTags::SAND)){
return false;
}
foreach(Facing::HORIZONTAL as $side){
if($block->getSide($side)->isSolid()){
return false;
}
}
return true;
}
public function ticksRandomly() : bool{
@ -117,25 +95,11 @@ class Cactus extends Transparent{
}
}
$this->age = 0;
$world->setBlock($this->position, $this);
$world->setBlock($this->position, $this, update: false);
}else{
++$this->age;
$world->setBlock($this->position, $this);
$world->setBlock($this->position, $this, update: false);
}
}
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
foreach(Facing::HORIZONTAL as $side){
if($this->getSide($side)->isSolid()){
return false;
}
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
}

View File

@ -37,7 +37,7 @@ class Cake extends BaseCake{
protected int $bites = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, 0, self::MAX_BITES, $this->bites);
$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
}
/**

View File

@ -30,7 +30,7 @@ class CakeWithDyedCandle extends CakeWithCandle{
use ColoredTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::WHITE();
$this->color = DyeColor::WHITE;
parent::__construct($idInfo, $name, $typeInfo);
}

View File

@ -48,7 +48,7 @@ class Candle extends Transparent{
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$this->encodeLitState($w);
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
}
public function getCount() : int{ return $this->count; }
@ -91,7 +91,7 @@ class Candle extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
protected function getCandleIfCompatibleType(Block $block) : ?Candle{

View File

@ -24,21 +24,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Carpet extends Flowable{
use ColoredTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::WHITE();
parent::__construct($idInfo, $name, $typeInfo);
}
use StaticSupportTrait;
public function isSolid() : bool{
return true;
@ -51,19 +43,8 @@ class Carpet extends Flowable{
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getTypeId() !== BlockTypeIds::AIR){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
}
public function getFlameEncouragement() : int{

View File

@ -61,7 +61,7 @@ final class Cauldron extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
}
/**
@ -83,7 +83,7 @@ final class Cauldron extends Transparent{
}elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){
//TODO: powder snow cauldron
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
if($item->getType()->equals(PotionType::WATER())){
if($item->getType() === PotionType::WATER){
$this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
}else{
$this->fill(PotionCauldron::POTION_FILL_AMOUNT, VanillaBlocks::POTION_CAULDRON()->setPotionItem($item), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
@ -38,14 +40,16 @@ use pocketmine\world\sound\GlowBerriesPickSound;
use function mt_rand;
class CaveVines extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 25;
protected int $age = 0;
protected bool $berries = false;
protected bool $head = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
$w->bool($this->berries);
$w->bool($this->head);
}
@ -66,19 +70,6 @@ class CaveVines extends Flowable{
return $this;
}
public function getAge() : int{
return $this->age;
}
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function canClimb() : bool{
return true;
}
@ -88,19 +79,11 @@ class CaveVines extends Flowable{
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::UP)->equals(SupportType::FULL()) || $block->hasSameTypeId($this);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
$supportBlock = $block->getSide(Facing::UP);
return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || $supportBlock->hasSameTypeId($this);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
$this->age = mt_rand(0, self::MAX_AGE);
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
@ -176,6 +159,6 @@ class CaveVines extends Flowable{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
}

View File

@ -33,7 +33,7 @@ final class Chain extends Transparent{
use PillarRotationTrait;
public function getSupportType(int $facing) : SupportType{
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
}
protected function recalculateCollisionBoxes() : array{

View File

@ -45,7 +45,7 @@ class Chest extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onPostPlace() : void{

View File

@ -0,0 +1,134 @@
<?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;
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Book;
use pocketmine\item\EnchantedBook;
use pocketmine\item\Item;
use pocketmine\item\WritableBookBase;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function spl_object_id;
class ChiseledBookshelf extends Opaque{
use HorizontalFacingTrait;
use FacesOppositePlacingPlayerTrait;
/**
* @var ChiseledBookshelfSlot[]
* @phpstan-var array<int, ChiseledBookshelfSlot>
*/
private array $slots = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
}
/**
* Returns whether the given slot is displayed as occupied.
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
*/
public function hasSlot(ChiseledBookshelfSlot $slot) : bool{
return isset($this->slots[spl_object_id($slot)]);
}
/**
* Sets whether the given slot is displayed as occupied.
*
* This doesn't modify the bookshelf's inventory, so you can use this to make invisible
* books or display books that aren't actually in the bookshelf.
*
* To modify the contents of the bookshelf inventory, access the tile inventory.
*
* @return $this
*/
public function setSlot(ChiseledBookshelfSlot $slot, bool $occupied) : self{
if($occupied){
$this->slots[spl_object_id($slot)] = $slot;
}else{
unset($this->slots[spl_object_id($slot)]);
}
return $this;
}
/**
* Returns which slots of the bookshelf are displayed as occupied.
* As above, these values do not necessarily reflect the contents of the bookshelf inventory,
* although they usually will unless modified by plugins.
*
* @return ChiseledBookshelfSlot[]
* @phpstan-return array<int, ChiseledBookshelfSlot>
*/
public function getSlots() : array{
return $this->slots;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($face !== $this->facing){
return false;
}
$x = Facing::axis($face) === Axis::X ? $clickVector->z : $clickVector->x;
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
$clickVector->y
);
$tile = $this->position->getWorld()->getTile($this->position);
if(!$tile instanceof TileChiseledBookshelf){
return false;
}
$inventory = $tile->getInventory();
if(!$inventory->isSlotEmpty($slot->value)){
$returnedItems[] = $inventory->getItem($slot->value);
$inventory->clear($slot->value);
$this->setSlot($slot, false);
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
//TODO: type tags like blocks would be better for this
$inventory->setItem($slot->value, $item->pop());
$this->setSlot($slot, true);
}else{
return true;
}
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
public function isAffectedBySilkTouch() : bool{
return true;
}
}

View File

@ -23,52 +23,38 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\entity\projectile\Projectile;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
use pocketmine\world\sound\ChorusFlowerDieSound;
use pocketmine\world\sound\ChorusFlowerGrowSound;
use pocketmine\world\World;
use function array_rand;
use function min;
use function mt_rand;
final class ChorusFlower extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MIN_AGE = 0;
public const MAX_AGE = 5;
private const MAX_STEM_HEIGHT = 5;
private int $age = self::MIN_AGE;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, self::MIN_AGE, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < self::MIN_AGE || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in the range " . self::MIN_AGE . " ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()];
}
private function canBeSupportedAt(Position $position) : bool{
private function canBeSupportedAt(Block $block) : bool{
$position = $block->position;
$world = $position->getWorld();
$down = $world->getBlock($position->down());
@ -93,25 +79,10 @@ final class ChorusFlower extends Flowable{
return $plantAdjacent;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace->getPosition())){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this->position)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
$this->position->getWorld()->useBreakOn($this->position);
}
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
/**
* @phpstan-return array{int, bool}
*/
@ -181,11 +152,13 @@ final class ChorusFlower extends Flowable{
if($tx === null){
$tx = new BlockTransaction($this->position->getWorld());
}
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge($this->getAge() + $ageChange));
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->age + $ageChange)));
return $tx;
}
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
public function onRandomTick() : void{
$world = $this->position->getWorld();

View File

@ -23,18 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
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 pocketmine\world\Position;
use function mt_rand;
final class ChorusPlant extends Flowable{
use StaticSupportTrait;
protected function recalculateCollisionBoxes() : array{
$bb = AxisAlignedBB::one();
@ -52,7 +50,8 @@ final class ChorusPlant extends Flowable{
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
}
private function canStay(Position $position) : bool{
private function canBeSupportedAt(Block $block) : bool{
$position = $block->position;
$world = $position->getWorld();
$down = $world->getBlock($position->down());
@ -72,24 +71,7 @@ final class ChorusPlant extends Flowable{
}
}
if($this->canBeSupportedBy($down)){
return true;
}
return false;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canStay($blockReplace->getPosition())){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canStay($this->position)){
$this->position->getWorld()->useBreakOn($this->position);
}
return $this->canBeSupportedBy($down);
}
public function getDropsForCompatibleTool(Item $item) : array{

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
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;
@ -39,27 +39,15 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
class CocoaBlock extends Transparent{
class CocoaBlock extends Flowable{
use HorizontalFacingTrait;
use AgeableTrait;
public const MAX_AGE = 2;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
}
/**
@ -76,12 +64,8 @@ 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()->equals(WoodType::JUNGLE());
return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@ -110,7 +94,7 @@ class CocoaBlock extends Transparent{
}
public function ticksRandomly() : bool{
return true;
return $this->age < self::MAX_AGE;
}
public function onRandomTick() : void{

View File

@ -24,13 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
class Concrete extends Opaque{
use ColoredTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::WHITE();
parent::__construct($idInfo, $name, $typeInfo);
}
}

View File

@ -25,7 +25,6 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
use pocketmine\math\Facing;
@ -36,11 +35,6 @@ class ConcretePowder extends Opaque implements Fallable{
onNearbyBlockChange as protected startFalling;
}
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::WHITE();
parent::__construct($idInfo, $name, $typeInfo);
}
public function onNearbyBlockChange() : void{
if(($water = $this->getAdjacentWater()) !== null){
BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);

View File

@ -23,14 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CopperTrait;
class CopperStairs extends Stair{
use CopperTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->oxidation = CopperOxidation::NONE();
parent::__construct($idInfo, $name, $typeInfo);
}
}

View File

@ -23,29 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class Coral extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedAt($this)){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
use StaticSupportTrait;
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\item\Item;
use function mt_rand;
@ -32,11 +31,6 @@ use function mt_rand;
final class CoralBlock extends Opaque{
use CoralTypeTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->coralType = CoralType::TUBE();
parent::__construct($idInfo, $name, $typeInfo);
}
public function onNearbyBlockChange() : void{
if(!$this->dead){
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));

View File

@ -23,51 +23,35 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
abstract class Crops extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 7;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockReplace->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
$block = clone $this;
$block->age += mt_rand(2, 5);
if($block->age > self::MAX_AGE){
$block->age = self::MAX_AGE;
$tempAge = $block->age + mt_rand(2, 5);
if($tempAge > self::MAX_AGE){
$tempAge = self::MAX_AGE;
}
$block->age = $tempAge;
if(BlockEventHelper::grow($this, $block, $player)){
$item->pop();
}
@ -78,18 +62,12 @@ abstract class Crops extends Flowable{
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::FARMLAND){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function ticksRandomly() : bool{
return true;
return $this->age < self::MAX_AGE;
}
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

@ -42,7 +42,7 @@ class DaylightSensor extends Transparent{
protected bool $inverted = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(4, 0, 15, $this->signalStrength);
$w->boundedIntAuto(0, 15, $this->signalStrength);
$w->bool($this->inverted);
}
@ -70,7 +70,7 @@ class DaylightSensor extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -23,29 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
class DeadBush extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
use StaticSupportTrait;
public function getDropsForIncompatibleTool(Item $item) : array{
return [
@ -65,14 +50,21 @@ class DeadBush extends Flowable{
return 100;
}
private function canBeSupportedBy(Block $block) : bool{
$blockId = $block->getTypeId();
return $blockId === BlockTypeIds::SAND
|| $blockId === BlockTypeIds::RED_SAND
|| $blockId === BlockTypeIds::PODZOL
|| $blockId === BlockTypeIds::MYCELIUM
|| $blockId === BlockTypeIds::DIRT
|| $blockId === BlockTypeIds::HARDENED_CLAY
|| $blockId === BlockTypeIds::STAINED_CLAY;
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$supportBlock->hasTypeTag(BlockTypeTags::SAND) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
match($supportBlock->getTypeId()){
//can't use DIRT tag here because it includes farmland
BlockTypeIds::PODZOL,
BlockTypeIds::MYCELIUM,
BlockTypeIds::DIRT,
BlockTypeIds::GRASS,
BlockTypeIds::HARDENED_CLAY,
BlockTypeIds::STAINED_CLAY => true,
//TODO: moss block
default => false,
};
}
}

View File

@ -38,15 +38,10 @@ use pocketmine\world\sound\ItemUseOnBlockSound;
use pocketmine\world\sound\WaterSplashSound;
class Dirt extends Opaque{
protected DirtType $dirtType;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->dirtType = DirtType::NORMAL();
parent::__construct($idInfo, $name, $typeInfo);
}
protected DirtType $dirtType = DirtType::NORMAL;
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
$w->dirtType($this->dirtType);
$w->enum($this->dirtType);
}
public function getDirtType() : DirtType{ return $this->dirtType; }
@ -59,19 +54,24 @@ class Dirt extends Opaque{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
$world = $this->position->getWorld();
if($face === Facing::UP && $item instanceof Hoe){
if($face !== Facing::DOWN && $item instanceof Hoe){
$up = $this->getSide(Facing::UP);
if($up->getTypeId() !== BlockTypeIds::AIR){
return true;
}
$item->applyDamage(1);
$newBlock = $this->dirtType->equals(DirtType::NORMAL()) ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
$newBlock = $this->dirtType === DirtType::NORMAL ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
$center = $this->position->add(0.5, 0.5, 0.5);
$world->addSound($center, new ItemUseOnBlockSound($newBlock));
$world->setBlock($this->position, $newBlock);
if($this->dirtType->equals(DirtType::ROOTED())){
if($this->dirtType === DirtType::ROOTED){
$world->dropItem($center, VanillaBlocks::HANGING_ROOTS()->asItem());
}
return true;
}elseif($this->dirtType->equals(DirtType::ROOTED()) && $item instanceof Fertilizer){
}elseif($this->dirtType === DirtType::ROOTED && $item instanceof Fertilizer){
$down = $this->getSide(Facing::DOWN);
if($down->getTypeId() !== BlockTypeIds::AIR){
return true;
@ -80,7 +80,7 @@ class Dirt extends Opaque{
$item->pop();
$world->setBlock($down->position, VanillaBlocks::HANGING_ROOTS());
//TODO: bonemeal particles, growth sounds
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType()->equals(PotionType::WATER())){
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType() === PotionType::WATER){
$item->pop();
$world->setBlock($this->position, VanillaBlocks::MUD());
$world->addSound($this->position, new WaterSplashSound(0.5));

View File

@ -51,6 +51,8 @@ class Door extends Transparent{
public function readStateFromWorld() : Block{
parent::readStateFromWorld();
$this->collisionBoxes = null;
//copy door properties from other half
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
if($other instanceof Door && $other->hasSameTypeId($this)){
@ -102,7 +104,7 @@ class Door extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onNearbyBlockChange() : void{

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;
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;
final class DoublePitcherCrop extends DoublePlant{
use AgeableTrait {
describeBlockOnlyState as describeAge;
}
public const MAX_AGE = 1;
public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
parent::describeBlockOnlyState($w);
$this->describeAge($w);
}
protected function recalculateCollisionBoxes() : array{
if($this->top){
return [];
}
//the pod exists only in the bottom half of the plant
return [
AxisAlignedBB::one()
->trim(Facing::UP, 11 / 16)
->squash(Axis::X, 3 / 16)
->squash(Axis::Z, 3 / 16)
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
];
}
private function grow(?Player $player) : bool{
if($this->age >= self::MAX_AGE){
return false;
}
$bottom = $this->top ? $this->getSide(Facing::DOWN) : $this;
$top = $this->top ? $this : $this->getSide(Facing::UP);
if($top->getTypeId() !== BlockTypeIds::AIR && !$top->hasSameTypeId($this)){
return false;
}
$newState = (clone $this)->setAge($this->age + 1);
$tx = new BlockTransaction($this->position->getWorld());
$tx->addBlock($bottom->position, (clone $newState)->setTop(false));
$tx->addBlock($top->position, (clone $newState)->setTop(true));
$ev = new StructureGrowEvent($bottom, $tx, $player);
$ev->call();
return !$ev->isCancelled() && $tx->apply();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer && $this->grow($player)){
$item->pop();
return true;
}
return false;
}
public function ticksRandomly() : bool{
return $this->age < self::MAX_AGE && !$this->top;
}
public function onRandomTick() : void{
//only the bottom half of the plant can grow randomly
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

@ -50,7 +50,7 @@ class DragonEgg extends Transparent implements Fallable{
}
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
if($player !== null && !$player->getGamemode()->equals(GameMode::CREATIVE())){
if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
$this->teleport();
return true;
}
@ -82,6 +82,6 @@ class DragonEgg extends Transparent implements Fallable{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
}

View File

@ -24,19 +24,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
class DyedCandle extends Candle{
use ColoredTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::WHITE();
parent::__construct($idInfo, $name, $typeInfo);
}
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
$result = parent::getCandleIfCompatibleType($block);
//different coloured candles can't be combined in the same block
return $result instanceof DyedCandle && $result->color->equals($this->color) ? $result : null;
return $result instanceof DyedCandle && $result->color === $this->color ? $result : null;
}
}

View File

@ -24,13 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
final class DyedShulkerBox extends ShulkerBox{
use ColoredTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->color = DyeColor::WHITE();
parent::__construct($idInfo, $name, $typeInfo);
}
}

View File

@ -41,7 +41,7 @@ class EnchantingTable extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -49,7 +49,7 @@ class EnderChest extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -31,15 +31,40 @@ use pocketmine\event\entity\EntityTrampleFarmlandEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use function intdiv;
use function lcg_value;
class Farmland extends Transparent{
public const MAX_WETNESS = 7;
private const WATER_SEARCH_HORIZONTAL_LENGTH = 9;
private const WATER_SEARCH_VERTICAL_LENGTH = 2;
private const WATER_POSITION_INDEX_UNKNOWN = -1;
/** Total possible options for water X/Z indexes */
private const WATER_POSITION_INDICES_TOTAL = (self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2) * 2;
protected int $wetness = 0; //"moisture" blockstate property in PC
/**
* Cached value indicating the relative coordinates of the most recently found water block.
*
* If this is set to a non-unknown value, the farmland block will check the relative coordinates indicated by
* this value for water, before searching the entire 9x2x9 grid around the farmland. This significantly benefits
* hydrating or fully hydrated farmland, avoiding the need for costly searches on every random tick.
*
* If the coordinates indicated don't contain water, the full 9x2x9 volume will be searched as before. A new index
* will be recorded if water is found, otherwise it will be set to unknown and future searches will search the full
* 9x2x9 volume again.
*
* This property is not exposed to the API or saved on disk. It is only used by PocketMine-MP at runtime as a cache.
*/
private int $waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, 0, self::MAX_WETNESS, $this->wetness);
$w->boundedIntAuto(0, self::MAX_WETNESS, $this->wetness);
$w->boundedIntAuto(-1, self::WATER_POSITION_INDICES_TOTAL - 1, $this->waterPositionIndex);
}
public function getWetness() : int{ return $this->wetness; }
@ -53,6 +78,22 @@ class Farmland extends Transparent{
return $this;
}
/**
* @internal
*/
public function getWaterPositionIndex() : int{ return $this->waterPositionIndex; }
/**
* @internal
*/
public function setWaterPositionIndex(int $waterPositionIndex) : self{
if($waterPositionIndex < -1 || $waterPositionIndex >= self::WATER_POSITION_INDICES_TOTAL){
throw new \InvalidArgumentException("Water XZ index must be in range -1 ... " . (self::WATER_POSITION_INDICES_TOTAL - 1));
}
$this->waterPositionIndex = $waterPositionIndex;
return $this;
}
/**
* @return AxisAlignedBB[]
*/
@ -72,6 +113,11 @@ class Farmland extends Transparent{
public function onRandomTick() : void{
$world = $this->position->getWorld();
//this property may be updated by canHydrate() - track this so we know if we need to set the block again
$oldWaterPositionIndex = $this->waterPositionIndex;
$changed = false;
if(!$this->canHydrate()){
if($this->wetness > 0){
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
@ -79,9 +125,11 @@ class Farmland extends Transparent{
if(!$event->isCancelled()){
$this->wetness = $event->getNewHydration();
$world->setBlock($this->position, $this, false);
$changed = true;
}
}else{
$world->setBlock($this->position, VanillaBlocks::DIRT());
$changed = true;
}
}elseif($this->wetness < self::MAX_WETNESS){
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
@ -89,8 +137,14 @@ class Farmland extends Transparent{
if(!$event->isCancelled()){
$this->wetness = $event->getNewHydration();
$world->setBlock($this->position, $this, false);
$changed = true;
}
}
if(!$changed && $oldWaterPositionIndex !== $this->waterPositionIndex){
//ensure the water square index is saved regardless of whether anything else happened
$world->setBlock($this->position, $this, false);
}
}
public function onEntityLand(Entity $entity) : ?float{
@ -98,26 +152,46 @@ 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;
}
protected function canHydrate() : bool{
//TODO: check rain
$start = $this->position->add(-4, 0, -4);
$end = $this->position->add(4, 1, 4);
for($y = $start->y; $y <= $end->y; ++$y){
for($z = $start->z; $z <= $end->z; ++$z){
for($x = $start->x; $x <= $end->x; ++$x){
if($this->position->getWorld()->getBlockAt($x, $y, $z) instanceof Water){
$world = $this->position->getWorld();
$startX = $this->position->getFloorX() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
$startY = $this->position->getFloorY();
$startZ = $this->position->getFloorZ() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
if($this->waterPositionIndex !== self::WATER_POSITION_INDEX_UNKNOWN){
$raw = $this->waterPositionIndex;
$x = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
$z = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
$y = $raw % self::WATER_SEARCH_VERTICAL_LENGTH;
if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
return true;
}
}
//no water found at cached position - search the whole area
//y will increment after x/z have been exhausted, as usually water will be at the same Y as the farmland
for($y = 0; $y < self::WATER_SEARCH_VERTICAL_LENGTH; $y++){
for($x = 0; $x < self::WATER_SEARCH_HORIZONTAL_LENGTH; $x++){
for($z = 0; $z < self::WATER_SEARCH_HORIZONTAL_LENGTH; $z++){
if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
$this->waterPositionIndex = $x + ($z * self::WATER_SEARCH_HORIZONTAL_LENGTH) + ($y * self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2);
return true;
}
}
}
}
$this->waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
return false;
}

View File

@ -40,9 +40,11 @@ class Fence extends Transparent{
public function readStateFromWorld() : Block{
parent::readStateFromWorld();
$this->collisionBoxes = null;
foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing);
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){
$this->connections[$facing] = true;
}else{
unset($this->connections[$facing]);
@ -98,6 +100,6 @@ class Fence extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
}
}

View File

@ -72,7 +72,7 @@ class FenceGate extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
private function checkInWall() : bool{

View File

@ -38,7 +38,7 @@ abstract class FillableCauldron extends Transparent{
private int $fillLevel = self::MIN_FILL_LEVEL;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
$w->boundedIntAuto(self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
}
public function getFillLevel() : int{ return $this->fillLevel; }
@ -64,7 +64,7 @@ abstract class FillableCauldron extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
}
protected function withFillLevel(int $fillLevel) : Block{

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockBurnEvent;
use pocketmine\math\Facing;
use pocketmine\world\format\Chunk;
@ -36,31 +36,16 @@ use function min;
use function mt_rand;
class Fire extends BaseFire{
use AgeableTrait;
public const MAX_AGE = 15;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
protected function getFireDamage() : int{
return 1;
}
private function canBeSupportedBy(Block $block) : bool{
return $block->getSupportType(Facing::UP)->equals(SupportType::FULL());
return $block->getSupportType(Facing::UP) === SupportType::FULL;
}
public function onNearbyBlockChange() : void{
@ -155,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

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
@ -35,6 +36,8 @@ use function atan2;
use function rad2deg;
final class FloorCoralFan extends BaseCoral{
use StaticSupportTrait;
private int $axis = Axis::X;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@ -53,12 +56,9 @@ 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(!$this->canBeSupportedAt($blockReplace)){
return false;
}
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)){
@ -73,15 +73,6 @@ final class FloorCoralFan extends BaseCoral{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedAt($this)){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
}

View File

@ -48,6 +48,6 @@ abstract class Flowable extends Transparent{
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
return SupportType::NONE;
}
}

View File

@ -23,28 +23,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Flower extends Flowable{
use StaticSupportTrait;
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
public function getFlameEncouragement() : int{

View File

@ -24,15 +24,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function assert;
class FlowerPot extends Flowable{
use StaticSupportTrait;
protected ?Block $plant = null;
@ -89,20 +90,6 @@ class FlowerPot extends Flowable{
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
}

View File

@ -28,15 +28,10 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
final class Froglight extends SimplePillar{
private FroglightType $froglightType;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
$this->froglightType = FroglightType::OCHRE();
parent::__construct($idInfo, $name, $typeInfo);
}
private FroglightType $froglightType = FroglightType::OCHRE;
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
$w->froglightType($this->froglightType);
$w->enum($this->froglightType);
}
public function getFroglightType() : FroglightType{ return $this->froglightType; }

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