Compare commits

..

189 Commits

Author SHA1 Message Date
8a2ba584ab Release 4.4.0-BETA1 2022-05-25 16:44:17 +01:00
812d5c3f58 Merge branch 'stable' into next-minor 2022-05-24 14:17:53 +01:00
9a5a03ef83 Bump phpstan/phpstan from 1.6.8 to 1.7.1 (#5067)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.6.8 to 1.7.1.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.7.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.6.8...1.7.1)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 13:46:09 +01:00
38d75f3aab Player: Be more explicit about not parsing chat messages when messageCounter is zero
the length check technically accounts for this, but future readers might not realize this
2022-05-22 16:46:53 +01:00
179cac45f5 Merge branch 'stable' into next-minor 2022-05-22 16:21:05 +01:00
1591881bf2 4.3.5 is next 2022-05-22 16:12:24 +01:00
7bbb2617c8 Release 4.3.4 2022-05-22 16:12:12 +01:00
2ec65ba799 ExplosionPrimeEvent: require a positive explosion radius 2022-05-21 23:22:04 +01:00
3dd7c09351 Improve documentation of ExplosionPrimeEvent and EntityExplodeEvent 2022-05-21 23:00:34 +01:00
afc6e8878d EntityExplodeEvent: clamp yield in range 0-100 2022-05-21 22:44:45 +01:00
7eaf1246a0 Explosion: cap yield at 100%
this could overflow if the explosion size is less than 1.
While this currently doesn't have any negative effects, if we decided to support >100% yield, this would cause some issues.
2022-05-21 22:43:26 +01:00
645c44ae9c Explosion: remove unused variables 2022-05-21 22:41:28 +01:00
331b05c681 Improve documentation of EntitySpawnEvent and EntityDespawnEvent 2022-05-21 22:11:37 +01:00
3abe80184a Update ItemSpawnEvent doc (#5059) 2022-05-21 22:09:21 +01:00
7618b13c6e ItemDespawnEvent: improve documentation 2022-05-21 21:57:14 +01:00
351cd4bfd7 Improve documentation of world-related events 2022-05-21 21:46:53 +01:00
1154c7c1ab PlayerQuitEvent: improve documentation 2022-05-21 21:23:13 +01:00
4a3e42f82e PlayerTransferEvent: add documentation 2022-05-21 21:21:51 +01:00
8ecf9717d6 PlayerItemHeldEvent: add documentation
since some nuances of the intended behaviour were previously unclear...
2022-05-21 21:21:13 +01:00
3ca80b353b InventoryTransactionEvent: rewrite documentation 2022-05-21 21:20:11 +01:00
7d172e2211 PlayerCommandPreprocessEvent: updated documentation 2022-05-21 21:07:29 +01:00
e934e2bd26 PlayerDropItemEvent: updated documentation
this can be triggered by dropping an item outside of the inventory menu while the inventory window is open.
2022-05-21 21:05:27 +01:00
3177d19730 PlayerKickEvent: improved woefully inadequate documentation 2022-05-21 21:03:56 +01:00
82104a8251 UpdateNotifyEvent: updated documentation 2022-05-21 20:32:30 +01:00
e9a17374d1 Rewrite documentation of CommandEvent
no wonder people get the idea to use this shit for banning commands ...
2022-05-21 20:29:25 +01:00
a31bd19b5a Deprecated PlayerCommandPreProcessEvent
to be removed in PM5

see #4284
2022-05-21 20:20:09 +01:00
c82dfef9b0 Player: fix weird behaviour when teleporting sleeping players
closes #4672
2022-05-21 20:08:12 +01:00
1181b13b5d Player: call InventoryCloseEvent after doing the necessary business
closes #4626

this does change the behaviour of getCurrentWindow() during InventoryCloseEvent, but no one should be using that anyway, since InventoryCloseEvent->getInventory() exists.
2022-05-21 19:23:59 +01:00
688be0a404 Server: apply server.properties difficulty to newly generated worlds
see #5057

jury is out on whether or not this should override difficulty on preexisting worlds, but it's non-controversial that it should apply at least to newly generated worlds.
2022-05-21 18:46:38 +01:00
33e6b63fe5 Explosion: remove unused local variable in explodeB() 2022-05-21 17:39:11 +01:00
5d92eddc82 InventoryTransaction: provide object ID of failed action for debugging identification
closes #3235
2022-05-21 17:35:21 +01:00
0324392cd8 Clean PHPStan baseline 2022-05-21 17:12:47 +01:00
01b0742bd4 LightUpdate: account for virtual nodes in prepareNodes()
fixes #3959
2022-05-21 17:07:49 +01:00
02cf6ae46c Added PlayerDeathEvent->setKeepXp(), closes #4002 (#4015) 2022-05-21 16:41:14 +01:00
fcb2ccab99 NetworkSession: Sync all attributes on respawn 2022-05-21 16:22:35 +01:00
a38a5c67f1 GiveCommand: show the search term in audit message, instead of the ID
this is more useful in the longer term.
2022-05-21 15:49:17 +01:00
a45a96b3ee ShulkerBoxInventory: fixed inappropriate usage of BlockLegacyIds when comparing item IDS 2022-05-21 15:40:25 +01:00
86efa0aae6 Merge remote-tracking branch 'origin/stable' into next-minor 2022-05-20 17:49:04 +01:00
2b84cb7be4 DisablePluginException now can be used to disable plugins (#4780)
closes #2671
2022-05-20 17:01:34 +01:00
26df37e6ef Minecart: fixed max stack size to match vanilla (#5051) 2022-05-20 16:57:48 +01:00
554f96bc24 InventoryManager: Defer opening new windows to the client until the window close handshake has been completed
fixes #5021 and probably a bunch of other inventory related glitches

When the server initiates a window close, it does so by sending a ContainerClose to the client, which causes the
client to behave as if it initiated the close itself. It responds by sending a ContainerClose back to the server,
which the server is then expected to respond to.

Sending the client a new window before sending this final response creates buggy behaviour on the client, which
is problematic when switching windows. Therefore, we defer sending any new windows until after the client
responds to our window close instruction, so that we can complete the window handshake correctly.

This is a pile of complicated garbage that only exists because Mojang overengineered the process of opening and
closing inventory windows.
2022-05-20 16:54:15 +01:00
6482aa7c64 Block: introduce logic for face support types (#4886)
fixes #4856
fixes #458
fixes #4529
fixes #3299

Added API method Block::getSupportType(Facing) : SupportType
Added SupportType enum
fixes torch, lantern, door etc. placement on slabs and upside-down stairs
2022-05-20 15:18:34 +01:00
6d941640a9 Merge branch 'stable' into next-minor 2022-05-20 11:14:59 +01:00
0ea3861d43 Fixed paintings not working in newly generated worlds
Paintings would not work unless at least one entity was loaded from disk
(or saved).
2022-05-20 11:13:14 +01:00
a323a5e56d Bump docker/login-action from 1 to 2 (#5048)
Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1...v2)

---
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>
2022-05-18 13:30:39 +01:00
79caba22a7 Bump docker/build-push-action from 2.10.0 to 3.0.0 (#5046)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2.10.0 to 3.0.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v2.10.0...v3.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>
2022-05-18 13:29:44 +01:00
30815bc8a2 Bump actions/upload-artifact from 2 to 3 (#5047)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
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>
2022-05-18 13:29:19 +01:00
df3a69dcb7 Modernize private property declarations in Threaded classes
I previously avoided this due to being unsure of the effects; however, it's clear that we already use typed properties on Threaded things in other places anyway, and the only known issues are with uninit properties, and arrays.
2022-05-17 22:42:18 +01:00
d4b7f66e15 Promote some constructors 2022-05-17 22:34:58 +01:00
8e767da29e Modernize some final remaining private property declarations 2022-05-17 21:55:57 +01:00
c0b15de504 Modernize private static property declarations 2022-05-17 21:55:16 +01:00
1d5430937f Modernize private property declarations in src/timings 2022-05-17 21:54:45 +01:00
9de88aa734 Modernize private property declarations in src/scheduler 2022-05-17 21:53:38 +01:00
9e59819f06 Modernize private property declarations in src/resourcepacks 2022-05-17 21:52:07 +01:00
c60311617d Modernize private property declarations in src/plugin 2022-05-17 21:37:45 +01:00
7bc3dcdefd Modernize private property declarations in src/player 2022-05-17 21:36:51 +01:00
22edca610c Modernize private property declarations in src/permission 2022-05-17 21:28:42 +01:00
6eac2ea7a5 Modernize private property declarations in src/network 2022-05-17 21:22:33 +01:00
cd016bedce Network: Improve typeinfo for PHPStan on bannedIps 2022-05-17 21:19:48 +01:00
95ad3f16e1 Modernize private property declarations in src/item 2022-05-17 20:59:24 +01:00
fb4d332d1a Modernize private property declarations in src/inventory/transaction 2022-05-17 20:51:22 +01:00
a06b9294df Modernize private property declarations in src/inventory 2022-05-17 20:49:12 +01:00
221c6b8570 Remove useless @var 2022-05-17 20:46:24 +01:00
eb95e2a97e Modernize private property declarations in src/event 2022-05-17 20:45:50 +01:00
ec6769a6fc Modernize private property declarations in src/entity 2022-05-17 20:42:17 +01:00
343a12626e Modernize private property declarations in src/data 2022-05-17 20:35:59 +01:00
d11d77d328 Modernize private property declarations in src/crash 2022-05-17 20:35:24 +01:00
20eb80fc9a Modernize private property declarations in src/console 2022-05-17 20:34:55 +01:00
071067effb Fixed flower pot accepting any block 2022-05-17 20:30:02 +01:00
89cc449808 Update exhaustion values for 1.18.30 (#5034) 2022-05-17 18:28:19 +01:00
1e59679ec2 Implemented Stonecutter (#4732) 2022-05-17 16:01:03 +01:00
8b8560a701 Added PlayerPostChunkSendEvent (#4937)
this is primarily useful for debugging plugins, but could also be useful for other things, such as spawning fake blocks, particles, etc.
2022-05-17 15:40:01 +01:00
161ab5af16 Bump docker/setup-buildx-action from 1 to 2 (#5042)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 2.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v2)

---
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>
2022-05-17 15:24:47 +01:00
ab41594da2 Bump actions/checkout from 2 to 3 (#5041)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [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/v2...v3)

---
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>
2022-05-17 15:24:23 +01:00
80d4c11061 Bump ncipollo/release-action from 1.8.6 to 1.10.0 (#5040)
Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.8.6 to 1.10.0.
- [Release notes](https://github.com/ncipollo/release-action/releases)
- [Commits](https://github.com/ncipollo/release-action/compare/v1.8.6...v1.10.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>
2022-05-17 15:17:33 +01:00
e767796986 Bump shivammathur/setup-php from 2.12.0 to 2.18.1 (#5043)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.12.0 to 2.18.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.12.0...2.18.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>
2022-05-17 15:16:08 +01:00
dea7031b86 Bump actions/cache from 2 to 3 (#5044)
Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3.
- [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/v2...v3)

---
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>
2022-05-17 15:15:52 +01:00
7e3d099d5b Update dependabot.yml 2022-05-17 15:08:52 +01:00
8b1bd5b7ff World: do not hardcode length of day in computeSunAnglePercentage() 2022-05-16 18:17:32 +01:00
4dbac79e86 Merge branch 'stable' into next-minor 2022-05-16 18:02:25 +01:00
5abc7f866a 4.3.4 is next 2022-05-16 17:50:41 +01:00
9dc2a01c2e Release 4.3.3 2022-05-16 17:50:29 +01:00
6f0aa360d1 Bump build/php from 19222cf to 8138c6a (#5039)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `19222cf` to `8138c6a`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](19222cfb28...8138c6a4a4)

---
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>
2022-05-16 17:20:24 +01:00
7c3b78b0a0 ItemFactory: fix missing registration for dead coral fans
closes #5032

this is evidently not an ideal solution, and something more dynamic would be preferred so that we don't have to manually register an item for every permutation.
2022-05-13 13:53:28 +01:00
12e4724b19 Merge remote-tracking branch 'origin/stable' into next-minor 2022-05-11 20:45:16 +01:00
52e74296de Stop the server if any plugin failed to load or enable (#4951)
closes #3080 

If plugins fail to load for some reason, it's highly likely that some critical functionality of the server is compromised. For example:
- if an NPC plugin fails to load, all custom entities added by that plugin will be deleted from worlds
- if a world protection plugin fails, players will be able to grief your otherwise immutable lobby map
- if a worldgen plugin fails, worlds using custom generators won't load
- if a permission plugin fails, players might have access to commands and features they aren't supposed to have
- the list goes on...

This change makes the server commit graceful suicide if any plugin fails to load for error-related reasons, including (but not limited to):
- Incompatible API version
- Missing dependencies
- Invalid plugin.yml
- Invalid main class

Plugins prevented from loading by `plugin_list.yml` are not considered errors and **are not** included in this change. If a plugin is disallowed from loading due to the `plugin_list`, the server will continue to run as if the plugin was not present.
2022-05-11 20:43:38 +01:00
3b7e274c34 Server: localize 'forcing server shutdown' message 2022-05-11 14:54:29 +01:00
db6abfb227 Stop the server if generators specified for new worlds are not valid
I thought I did this already in eff856d8e5, but it looks like my brain slipped a gear.

Without this change, it's possible to crash the server by specifying an invalid generator for the default world if it doesn't yet exist.
2022-05-11 14:35:20 +01:00
2adaca2521 README: remove unnecessary things
[ci skip]
2022-05-11 13:53:52 +01:00
b6f39035f8 README: fix logo URLs
[ci skip]
2022-05-11 13:52:00 +01:00
cfd550451f Added dark mode variant of the logo (#5014)
Since the text is barely visible on dark mode (black on black), i added an inverted version that only shows with dark mode using the [picture](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) tag.
The tag is supported in all browsers except IE since ~2015
I added an exception block for old IE versions, new versions dropped support for `[if IE]` though
2022-05-11 13:44:20 +01:00
9ff1bf6deb Merge remote-tracking branch 'origin/stable' into next-minor 2022-05-11 13:12:45 +01:00
212c94ce98 PluginManager: Log an error message when a plugin disables itself during enabling 2022-05-11 13:07:45 +01:00
77530b0c24 Bump build/php from 0b5760b to 19222cf (#5027)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `0b5760b` to `19222cf`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](0b5760bb3b...19222cfb28)

---
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>
2022-05-11 12:55:36 +01:00
3e1246acff FormattedCommandAlias: Invoke commands directly with pre-parsed arguments
this resolves a range of issues with quoted arguments when using placeholders, as well as improving performance (no redundant combine -> re-parse needed).
2022-05-10 16:03:09 +01:00
4c29f98292 "Fix CS" are going to be my final words ... 2022-05-10 15:39:44 +01:00
5cc0d92eff Fixed PHPStan errors 2022-05-10 15:38:26 +01:00
b875b68fc7 Fix PHPStan error in CommandStringHelperTest 2022-05-10 15:24:14 +01:00
a5ebbf8adb Fix CS again 2022-05-10 15:23:55 +01:00
217385efb9 CommandStringHelper::parse() returns a non-empty list of strings 2022-05-10 15:22:46 +01:00
f70c36baf9 SimpleCommandMap: parse config-defined commands according to the same rules as manually typed commands 2022-05-10 15:21:39 +01:00
eda4ae9181 Added unit tests for CommandStringHelper 2022-05-10 15:11:29 +01:00
d2e421c424 CommandStringHelper: fixed backslashes not being removed from escaped quotes
this time, without breaking eval commands ... stripslashes likes to strip ALL backslashes, whether they are actually escaping something or not, which is super annoying.
2022-05-10 15:09:57 +01:00
6da53536ca Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2022-05-10 14:30:21 +01:00
1da6aa40f8 Leaves: drop sticks with a 2% chance (#5019)
as per vanilla
2022-05-10 14:17:18 +01:00
a913736235 fix CS 2022-05-10 14:13:01 +01:00
124edeacaf fix build 2022-05-10 14:11:46 +01:00
a216f4d089 Utils: improve representation of int, float and null in stack trace parameters 2022-05-10 14:07:07 +01:00
17b0e0be84 Utils: use match to clean up stringifyValueForTrace() 2022-05-10 14:04:52 +01:00
81d8aed2e2 Utils: account for named variadic arguments in printableTrace() 2022-05-10 13:59:42 +01:00
69418084bc Boat: fixed max stack size to match vanilla (#5018) 2022-05-10 13:16:04 +01:00
593a4b65ea World: fixed crash when using unloadChunk() during ChunkPopulateEvent, ChunkLoadEvent or when using ChunkListeners
I sure hope there isn't any other cases where this edge case can appear ...

closes #5022
2022-05-10 13:09:26 +01:00
869d340f10 Regenerate PHPStan baselines 2022-05-10 12:38:20 +01:00
5d64d4a1e3 Updated phpstan-strict-rules 2022-05-10 12:34:27 +01:00
cc3c5bdb8d Bump phpstan/phpstan from 1.6.7 to 1.6.8 (#5025)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.6.7 to 1.6.8.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.7.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.6.7...1.6.8)

---
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>
2022-05-10 12:32:22 +01:00
e1c4150dff 4.3.3 is next 2022-05-10 00:57:50 +01:00
755ca1af9b Release 4.3.2 2022-05-10 00:57:50 +01:00
7d78b9cb2c Bump build/php from f5d7b45 to 0b5760b (#5023)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `f5d7b45` to `0b5760b`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](f5d7b45990...0b5760bb3b)

---
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>
2022-05-10 00:55:07 +01:00
91f802ac7a Bump phpstan/phpstan from 1.6.3 to 1.6.7 (#5015)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.6.3 to 1.6.7.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.7.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.6.3...1.6.7)

---
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>
2022-05-05 11:49:27 +01:00
72cfea3a63 SimpleCommandMap: extract command string parsing code into its own unit 2022-05-01 22:05:38 +01:00
a353872327 FormattedCommandAlias: match placeholders using regex 2022-05-01 21:16:13 +01:00
f4d71d0b48 FormattedCommandAlias: reduce complexity of buildCommand() 2022-05-01 21:04:38 +01:00
a6299b0927 pocketmine.yml: moar docs for command aliases hidden features 2022-05-01 16:16:32 +01:00
6e372d9e36 Entity: fixed setNameTagVisible not having immediately visible effect 2022-05-01 14:54:41 +01:00
ad8132ae11 Bump build/php from 7b357f8 to f5d7b45 (#5004)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `7b357f8` to `f5d7b45`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](7b357f8cf9...f5d7b45990)

---
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>
2022-05-01 14:04:38 +01:00
5d39d7a1c8 Version bump to 4.4 2022-04-28 21:00:49 +01:00
937bb4c6ce Merge branch 'stable' into next-minor 2022-04-28 21:00:23 +01:00
cf15a0913d World: fixed a corner case assertion failure in generation system
This required the following:
- A generation task (taskA) to already be running for any chunk (chunkA)
- A chunk (chunkB) is requested for generation, and the task (taskB) to do the generation
  is commenced immediately
- chunkB generation promise is aborted (e.g. due to chunk unload) and
  taskB is orphaned
- chunkB is subsequently re-requested, but ends up in the generation
  queue because taskB is still running
- taskA completes and drains the generation queue
- chunkB attempts to be populated a second time, but taskB has not yet
  been collected, resulting in an assertion failure.

This bug has been appearing intermittently ever since PM 4.0 release.
For most users there is no obvious effect since production servers don't
have assertions enabled; however, it's unclear what kind of weird side
effects this bug may have had.
2022-04-28 20:23:23 +01:00
33cf085692 PHPStan 1.6.3 2022-04-28 16:06:20 +01:00
3752225ed5 World: fix CS 2022-04-28 16:00:34 +01:00
5cf572892f WorldManager: fixed missing initializer for $defaultWorld 2022-04-28 15:38:20 +01:00
20ff5d5a3d Modernize property declarations in src/entity/animation 2022-04-28 15:33:13 +01:00
b88a47929f Modernize property declarations in src/world/* 2022-04-28 15:06:17 +01:00
159392e738 wtf is this shitbox code 2022-04-28 14:57:57 +01:00
2f03af51dd Modernize property declarations in src/world/sound 2022-04-28 14:57:01 +01:00
cb76c149e1 Modernize property declarations in src/world/particle 2022-04-28 14:55:53 +01:00
6d7bf1c5d8 Utils: fixed missing property initializer 2022-04-28 14:52:21 +01:00
ed2a239334 Modernize property declarations in src/world/generator 2022-04-28 13:16:21 +01:00
46c504e529 Modernize property declarations in src/ 2022-04-28 13:14:23 +01:00
0e7e776862 Modernize property declarations in src/utils 2022-04-28 13:12:12 +01:00
de12b701ac Modernize type declarations in src/world/format 2022-04-28 13:07:58 +01:00
b402df8b91 Modernize property declarations in pocketmine\crafting namespace 2022-04-25 13:15:48 +01:00
dca457b1e0 Modernize property declarations in pocketmine\command namespace 2022-04-25 13:09:14 +01:00
72cff0ee11 Modernize property declarations in pocketmine\block namespace 2022-04-25 13:00:29 +01:00
09778e3f1b Fixed build failure 2022-04-25 12:56:16 +01:00
9f5d4180c9 Spawnable: remove dead code, deprecate isDirty() and setDirty() 2022-04-25 12:46:01 +01:00
c8a7a53d70 event: modernize property declarations where possible
only private fields are modified; protected ones can't be changed in case someone extended the classes
2022-04-25 00:06:26 +01:00
223893fd5c PlayerChangeSkinEvent: remove obsolete doc comment 2022-04-24 21:56:49 +01:00
a58551af5b 4.3.2 is next 2022-04-23 16:03:15 +01:00
f7dce4af88 Release 4.3.1 2022-04-23 16:03:14 +01:00
dff5c30172 Updated BedrockProtocol to 9.0.1 2022-04-23 16:01:08 +01:00
e8994dbc17 Bump build/php from 1fae6b8 to 7b357f8 (#4987)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `1fae6b8` to `7b357f8`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](1fae6b8d4a...7b357f8cf9)

---
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>
2022-04-23 14:20:47 +01:00
aaba8a2a9a Bump phpstan/phpstan from 1.5.6 to 1.5.7 (#4975)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.5.6 to 1.5.7.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.6.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.5.6...1.5.7)

---
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>
2022-04-21 16:45:16 +01:00
27056b6c37 Player: creative players should not be damaged by the void (#4978) 2022-04-21 16:39:36 +01:00
9930de01b9 Bump phpstan/phpstan-phpunit from 1.1.0 to 1.1.1 (#4979)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.1.0...1.1.1)

---
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>
2022-04-21 16:38:36 +01:00
cd021f49cd 4.3.1 is next 2022-04-20 14:15:12 +01:00
e4ce467c0b Release 4.3.0 2022-04-20 14:15:06 +01:00
6b4687a36b RuntimeBlockMapping: unseal constructor to facilitate easier testing of new versions 2022-04-20 14:00:20 +01:00
c085bf0db4 Changes for 1.18.30 2022-04-20 13:59:50 +01:00
e0d4bd985e 4.2.11 is next 2022-04-20 13:03:25 +01:00
ded7e24f67 Merge branch 'stable' into next-minor 2022-04-19 15:27:55 +01:00
e73bb07da0 Updated locale data 2022-04-19 15:27:00 +01:00
453bf6d73b fix build 2022-04-10 21:37:10 +01:00
3353a00641 List command aliases in /help <commandName> 2022-04-10 21:32:46 +01:00
e388cb1643 Merge branch 'stable' into next-minor 2022-04-10 21:23:04 +01:00
4fc712119f FlowerPot: allow removing the planted item
closes #4896
closes #4898

this is not completely consistent with client-side predictions due to a bug in the client, which I believe is a problem limited to the legacy transaction system.
2022-04-08 23:07:08 +01:00
7d1464f0a1 Merge branch 'stable' into next-minor 2022-03-28 17:18:54 +01:00
f25beab6cb Use constants for compound meta values 2022-03-23 15:58:27 +00:00
a9f2766a8b Merge branch 'stable' into next-minor 2022-03-23 13:39:41 +00:00
9963fcf849 PluginManager: return the created RegisteredListener from registerEvent()
this allows the handler to be later unregistered when used with a plain closure instead of a magic `Listener`.
2022-03-22 16:20:37 +00:00
ab93135b84 Merge branch 'stable' into next-minor 2022-03-22 15:49:58 +00:00
2b81b53dfa Merge branch 'stable' into next-minor 2022-03-03 18:50:12 +00:00
eff856d8e5 Stop the server if any world listed by config fails to load or be generated during startup
closes #3162

this does not affect worlds loaded by plugins; they may continue to handle errors as they see fit
2022-03-03 18:40:11 +00:00
90a369f0b6 Humans can now do emotes (#4610)
added the following API methods:
- Human::emote()
2022-03-03 18:19:30 +00:00
ba05b4f024 Whitelist command now kicks non-whitelisted online players (#4774)
closes #3868
2022-03-03 18:15:21 +00:00
70f923714c Merge branch 'stable' into next-minor 2022-03-02 18:43:34 +00:00
03fa5387ac StringToItemParser: recognize cod, raw_cod and cooked_cod 2022-03-01 21:49:36 +00:00
1ea0b8a9df VanillaBlocks generator now reverse-lookups ID constants instead of baking numeric IDs into the code
this makes it easier to spot errors.
similar treatment as VanillaItems received in the previous commit
2022-02-26 16:17:23 +00:00
566e4a4196 VanillaItems generator now reverse-lookups ID constants instead of baking numeric IDs into the code
this makes it easier to spot errors.
there's nothing we can do about the metadata values, but IDs can be quite easily constified.
there might be some weird artifacts where constants are aliased (e.g. appleenchanted) , but this is inarguably better than baking numeric IDs.
2022-02-26 16:10:34 +00:00
3ed336fa0e Merge branch 'stable' into next-minor 2022-02-19 20:16:49 +00:00
d702113fb5 Merge branch 'stable' into next-minor 2022-02-08 22:23:00 +00:00
38eeda6e8b Merge branch 'stable' into next-minor 2022-02-07 19:32:07 +00:00
735e4cc3bc Merge branch 'staging/4.1' into next-minor 2022-02-07 17:22:42 +00:00
c19a038d47 LevelDB: Added a whole bunch of constants 2022-02-07 00:54:00 +00:00
95bc013e97 Merge branch 'staging/4.1' into next-minor 2022-02-06 23:56:47 +00:00
15425d01bc BanEntry: clean up getString() 2022-01-28 21:53:28 +00:00
0642364a44 block: added constants for various integer property bounds 2022-01-28 21:27:30 +00:00
466b018319 Merge branch 'staging/4.1' into next-minor 2022-01-28 20:40:20 +00:00
363a9689b4 Prepare 4.2.0+dev 2022-01-28 15:10:19 +00:00
442 changed files with 4364 additions and 4030 deletions

View File

@ -11,3 +11,8 @@ updates:
directory: "/"
schedule:
interval: daily
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily

BIN
.github/readme/pocketmine-dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
.github/readme/pocketmine.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -12,16 +12,16 @@ jobs:
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Clone pmmp/PocketMine-Docker repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
repository: pmmp/PocketMine-Docker
fetch-depth: 1
@ -46,7 +46,7 @@ jobs:
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
- name: Build image for tag
uses: docker/build-push-action@v2.10.0
uses: docker/build-push-action@v3.0.0
with:
push: true
context: ./pocketmine-mp
@ -59,7 +59,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v2.10.0
uses: docker/build-push-action@v3.0.0
with:
push: true
context: ./pocketmine-mp
@ -72,7 +72,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v2.10.0
uses: docker/build-push-action@v3.0.0
with:
push: true
context: ./pocketmine-mp
@ -85,7 +85,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v2.10.0
uses: docker/build-push-action@v3.0.0
with:
push: true
context: ./pocketmine-mp

View File

@ -13,17 +13,17 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.12.0
uses: shivammathur/setup-php@2.18.1
with:
php-version: 8.0
- name: Restore Composer package cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
@ -60,7 +60,7 @@ jobs:
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} > build_info.json
- name: Upload release artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: release_artifacts
path: |
@ -69,7 +69,7 @@ jobs:
${{ github.workspace }}/build_info.json
- name: Create draft release
uses: ncipollo/release-action@v1.8.6
uses: ncipollo/release-action@v1.10.0
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}

View File

@ -34,7 +34,7 @@ jobs:
php: [8.0.18]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
@ -46,7 +46,7 @@ jobs:
run: curl -sS https://getcomposer.org/installer | php
- name: Restore Composer package cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
@ -72,7 +72,7 @@ jobs:
php: [8.0.18]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
@ -84,7 +84,7 @@ jobs:
run: curl -sS https://getcomposer.org/installer | php
- name: Restore Composer package cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
@ -110,7 +110,7 @@ jobs:
php: [8.0.18]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: true
@ -124,7 +124,7 @@ jobs:
run: curl -sS https://getcomposer.org/installer | php
- name: Restore Composer package cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
@ -150,7 +150,7 @@ jobs:
php: [8.0.18]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
@ -162,7 +162,7 @@ jobs:
run: curl -sS https://getcomposer.org/installer | php
- name: Restore Composer package cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
@ -192,10 +192,10 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.15.0
uses: shivammathur/setup-php@2.18.1
with:
php-version: 8.0
tools: php-cs-fixer:3.2

View File

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

View File

@ -1,5 +1,13 @@
<p align="center">
<a href="https://pmmp.io"><img src="http://cdn.pocketmine.net/img/PocketMine-MP-h.png"></img></a><br>
<a href="https://pmmp.io">
<!--[if IE]>
<img src="https://github.com/pmmp/PocketMine-MP/blob/stable/.github/readme/pocketmine.png" alt="The PocketMine-MP logo" title="PocketMine" loading="eager" />
<![endif]-->
<picture>
<source srcset="https://github.com/pmmp/PocketMine-MP/raw/stable/.github/readme/pocketmine-dark.png" media="(prefers-color-scheme: dark)">
<img src="https://github.com/pmmp/PocketMine-MP/raw/stable/.github/readme/pocketmine.png" loading="eager" />
</picture>
</a><br>
<b>A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP</b>
</p>

77
changelogs/4.3.md Normal file
View File

@ -0,0 +1,77 @@
**For Minecraft: Bedrock Edition 1.18.30**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.3.0
Released 20th April 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.18.30.
- Removed support for older versions.
# 4.3.1
Released 23rd April 2022.
## Fixes
- Updated BedrockProtocol dependency to fix incorrect command argument types.
- Creative players no longer die in the void.
# 4.3.2
Released 10th May 2022.
## Fixes
- Fixed an assertion failure in certain edge cases during world generation.
- Fixed `Entity::setNameTagVisible()` not immediately showing results to players already online.
## Documentation
- Added more documentation in the template `pocketmine.yml` for the `aliases` config section.
- Removed useless doc comment in `PlayerChangeSkinEvent`.
# 4.3.3
Released 16th May 2022.
## General
- Improved display of parameters in exception stack trace logs.
- Exception stack traces now include names for dynamic named arguments (i.e. arguments that don't match any parameter of the function), if any were given.
- Note: Named arguments which do match parameters are not shown this way, since PHP reduces them to positional arguments for performance reasons.
## Fixes
- Fixed server crash when chunks are unloaded during chunk generation callbacks
- Fixed dead coral fan items placing coral fans in the wrong orientation.
- Fixed max stack size of boat items.
# 4.3.4
Released 22nd May 2022.
## Fixes
- Fixed `difficulty` in `server.properties` having no effect - it's now applied to newly generated worlds.
- Note: this setting still doesn't behave the same way as vanilla due to potential disruption to existing servers.
- Fixed paintings not working in newly generated worlds and some other cases.
- Fixed inventory window switching breaking the inventory UI in some cases (e.g. pressing E while clicking a chest).
- Fixed minecart items incorrectly stacking.
- Fixed incorrect light levels in translucent blocks at the top of the world.
- Fixed teleporting sleeping players causing broken behaviour on the sleeping player's client.
- Fixed `EntityExplodeEvent->setYield()` accepting values outside the range 0-100.
- Fixed `ExplosionPrimeEvent->setForce()` accepting negative values (later resulting in crashes).
## Documentation
- Updated documentation for the following events:
- `CommandEvent`
- `EntityDespawnEvent`
- `EntityExplodeEvent`
- `EntitySpawnEvent`
- `ExplosionPrimeEvent`
- `InventoryTransactionEvent`
- `ItemDespawnEvent`
- `ItemSpawnEvent`
- `PlayerCommandPreprocessEvent`
- `PlayerDropItemEvent`
- `PlayerItemHeldEvent`
- `PlayerKickEvent`
- `PlayerQuitEvent`
- `PlayerTransferEvent`
- `UpdateNotifyEvent`

77
changelogs/4.4-beta.md Normal file
View File

@ -0,0 +1,77 @@
**For Minecraft: Bedrock Edition 1.18.30**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.4.0-BETA1
Released 25th May 2022.
## General
- The server will now shut itself down if any of the following errors occur during startup:
- Any plugin fails to load or enable (plugins loaded by other plugins post-startup are **not** affected by this change) (**PR [#4951](https://github.com/pmmp/PocketMine-MP/pull/4951)**)
- The motivation for this change is to prevent situations where plugins failing to load could result in adverse outcomes, such as a world protection plugin leaving a lobby unprotected from griefing.
- If you encounter this problem, remove the offending plugin(s) or prevent it from loading using `plugin_list.yml`.
- See **PR [#4951](https://github.com/pmmp/PocketMine-MP/pull/4951)** for more detail on this change.
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to load (worlds loaded by plugins are **not** affected by this change)
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to generate (e.g. due to invalid generator settings)
- Enabling the server whitelist while the server is running (e.g. using `/whitelist on`) will now kick any non-whitelisted players currently on the server (**PR [#4774](https://github.com/pmmp/PocketMine-MP/pull/4774)**).
- Help for commands (`/help <name of command>`) now displays a list of aliases of that command.
- A CRITICIAL log message is now generated if a plugin disables itself when enabling, in case the plugin doesn't emit any error of its own.
- The `/give` command now shows the alias used to find the given item in the success message, instead of the item ID/meta.
## Fixes
- Block placement has been fixed in many places where it previously didn't work correctly (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**):
- torches on top of slabs, upside-down stairs
- torches on the back face of stairs
- flower pots on top of fences
- the list goes on and on ...
- Fixed backslash escapes not getting properly removed from commands in some cases.
- Fixed aliases defined in the `aliases` section of `pocketmine.yml` not being treated as quote-aware.
## Gameplay
- Plants in flower pots can now be removed by right-clicking on the flower pot.
- Leaves now have a 2% chance of dropping sticks when destroyed by hand (**PR [#5019](https://github.com/pmmp/PocketMine-MP/pull/5019)**).
- Food exhaustion now matches Bedrock 1.18.30 (**PR [#5034](https://github.com/pmmp/PocketMine-MP/pull/5034)**).
- Implemented Stonecutter block (**PR [#4732](https://github.com/pmmp/PocketMine-MP/pull/4732)**).
## API
### Block
- Added `Block->getSupportType(Facing) : SupportType` (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**).
- This is used to determine the kind of support a block face can provide to a block (e.g. a torch) placed on it.
- Added `utils\SupportType` enum (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**).
- `tile\Spawnable->isDirty()` and `tile\Spawnable->setDirty()` are now `@deprecated`.
### Command
- Added `CommandStringHelper::parseQuoteAware()`. This static method contains the code used by `SimpleCommandMap` used to parse quoted command arguments.
### Entity
- Added `Human::emote()` (**PR [#4610](https://github.com/pmmp/PocketMine-MP/pull/4610)**)
### Event
- `PlayerCommandPreprocessEvent` is now `@deprecated`, since its functionality is entirely replaced by other, general-purpose events.
- Use `CommandEvent` to intercept commands.
- Use `PlayerChatEvent` to intercept chat messages.
- To convert a chat message into a command, pass it directly to `Server->dispatchCommand()` with the player as sender.
- Added `PlayerPostChunkSendEvent` (**PR [#4937](https://github.com/pmmp/PocketMine-MP/pull/4937)**).
- Added `PlayerDeathEvent->setKeepXp()` (**PR [#4015](https://github.com/pmmp/PocketMine-MP/pull/4015)**).
- `InventoryCloseEvent` is now called **after** the target window has been removed. This fixes various feedback loops caused by trying to open new windows to a player while there was one still active.
- As a side effect, this now means that `Player->getCurrentWindow()` will return `null` during `InventoryCloseEvent`. Use `InventoryCloseEvent->getInventory()` instead.
### Item
- `StringToItemParser` now recognizes `cod`, `raw_cod` and `cooked_cod` aliases.
### Plugin
- `DisablePluginException` may now be thrown from `Plugin::onEnable()` to make the server gracefully disable the plugin (without crashing) (**PR [#4780](https://github.com/pmmp/PocketMine-MP/pull/4780)**).
- `PluginManager->registerEvent()` now returns the `RegisteredListener` created for the handler, to permit unregistering it later.
## Internals
- Private property declarations now use typed properties (PHP 7.4) and promoted constructor properties (PHP 8.0) wherever possible.
- Protected and public properties remain unchanged, since they can't be changed without breaking subclasses.
- Promoted constructor properties are only used when it's consistently possible to promote most or all properties in a class.
- Simplified and improved legibility of `FormattedCommandAlias`.
- Added unit tests for the quote-aware command parser used by `SimpleCommandMap`.
- Various hardcoded values in `block` package classes have been moved to private constants to improve readability.
- Added various constants used in the `LevelDB` world provider.

View File

@ -34,14 +34,14 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.6.0+bedrock-1.18.10",
"pocketmine/bedrock-protocol": "~8.0.2+bedrock-1.18.10",
"pocketmine/bedrock-data": "~1.7.0+bedrock-1.18.30",
"pocketmine/bedrock-protocol": "~9.0.0+bedrock-1.18.30",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0",
"pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.5.0",
"pocketmine/locale-data": "~2.8.0",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0",
@ -53,9 +53,9 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "1.5.6",
"phpstan/phpstan": "1.7.1",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"
},
"autoload": {

87
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b20ae069f5f467084bcbaae90c893bc9",
"content-hash": "940fa92ddd657ea8f093234660bf7be5",
"packages": [
{
"name": "adhocore/json-comment",
@ -249,42 +249,42 @@
},
{
"name": "pocketmine/bedrock-data",
"version": "1.6.0+bedrock-1.18.10",
"version": "1.7.0+bedrock-1.18.30",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0"
"reference": "c8f323ff0cbdb36a5d95e7e4a23969f562445be0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e98c511584a7bd58a95986374d2df4b04c6a2ba0",
"reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/c8f323ff0cbdb36a5d95e7e4a23969f562445be0",
"reference": "c8f323ff0cbdb36a5d95e7e4a23969f562445be0",
"shasum": ""
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
"CC0-1.0"
],
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.10"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.30"
},
"time": "2022-02-08T19:13:47+00:00"
"time": "2022-04-20T12:40:59+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "8.0.2+bedrock-1.18.10",
"version": "9.0.1+bedrock-1.18.30",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "d1f1afdbb4ea61ea52eb511a79ee1ca561da349c"
"reference": "9d3cc87c4d26c002dd42aa9af20c0cd47a72018e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/d1f1afdbb4ea61ea52eb511a79ee1ca561da349c",
"reference": "d1f1afdbb4ea61ea52eb511a79ee1ca561da349c",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/9d3cc87c4d26c002dd42aa9af20c0cd47a72018e",
"reference": "9d3cc87c4d26c002dd42aa9af20c0cd47a72018e",
"shasum": ""
},
"require": {
@ -298,7 +298,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.5.3",
"phpstan/phpstan": "1.5.7",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5"
@ -316,9 +316,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/8.0.2+bedrock-1.18.10"
"source": "https://github.com/pmmp/BedrockProtocol/tree/9.0.1+bedrock-1.18.30"
},
"time": "2022-04-01T21:55:10+00:00"
"time": "2022-04-23T14:48:16+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -536,16 +536,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.5.0",
"version": "2.8.3",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "df6fc7f2b48850b306c60466a11f7a27bb8fe1de"
"reference": "113c115a3b8976917eb22b74dccab464831b6483"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/df6fc7f2b48850b306c60466a11f7a27bb8fe1de",
"reference": "df6fc7f2b48850b306c60466a11f7a27bb8fe1de",
"url": "https://api.github.com/repos/pmmp/Language/zipball/113c115a3b8976917eb22b74dccab464831b6483",
"reference": "113c115a3b8976917eb22b74dccab464831b6483",
"shasum": ""
},
"type": "library",
@ -553,9 +553,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.5.0"
"source": "https://github.com/pmmp/Language/tree/2.8.3"
},
"time": "2022-04-01T22:36:58+00:00"
"time": "2022-05-11T13:51:37+00:00"
},
{
"name": "pocketmine/log",
@ -1819,16 +1819,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.5.6",
"version": "1.7.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "799dd8c2d2c9c704bb55d2078078cb970cf0f6d1"
"reference": "e3baed2ee2ef322e0f9b8fe8f87fdbe024c7c719"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/799dd8c2d2c9c704bb55d2078078cb970cf0f6d1",
"reference": "799dd8c2d2c9c704bb55d2078078cb970cf0f6d1",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e3baed2ee2ef322e0f9b8fe8f87fdbe024c7c719",
"reference": "e3baed2ee2ef322e0f9b8fe8f87fdbe024c7c719",
"shasum": ""
},
"require": {
@ -1854,7 +1854,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.5.6"
"source": "https://github.com/phpstan/phpstan/tree/1.7.1"
},
"funding": [
{
@ -1874,20 +1874,20 @@
"type": "tidelift"
}
],
"time": "2022-04-15T11:13:37+00:00"
"time": "2022-05-24T09:05:09+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.1.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "09133ce914f1388a8bb8c7f8573aaa3723cff52a"
"reference": "4a3c437c09075736285d1cabb5c75bf27ed0bc84"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/09133ce914f1388a8bb8c7f8573aaa3723cff52a",
"reference": "09133ce914f1388a8bb8c7f8573aaa3723cff52a",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4a3c437c09075736285d1cabb5c75bf27ed0bc84",
"reference": "4a3c437c09075736285d1cabb5c75bf27ed0bc84",
"shasum": ""
},
"require": {
@ -1924,27 +1924,27 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.1.0"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.1.1"
},
"time": "2022-03-28T09:20:49+00:00"
"time": "2022-04-20T15:24:25+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "1.1.0",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "e12d55f74a8cca18c6e684c6450767e055ba7717"
"reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/e12d55f74a8cca18c6e684c6450767e055ba7717",
"reference": "e12d55f74a8cca18c6e684c6450767e055ba7717",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
"reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"phpstan/phpstan": "^1.2.0"
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.6.3"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
@ -1954,9 +1954,6 @@
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
"rules.neon"
@ -1975,9 +1972,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.1.0"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.2.3"
},
"time": "2021-11-18T09:30:29+00:00"
"time": "2022-05-04T15:20:40+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -3452,5 +3449,5 @@
"platform-overrides": {
"php": "8.0.0"
},
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.3.0"
}

View File

@ -195,6 +195,12 @@ aliases:
#kill: [suicide, say "I tried to kill $1"] ## `kill alex` -> `suicide` + `say "I tried to kill alex"`
#giverandom: [give $1 $2, say "Someone has just received a $2!"] ## `giverandom alex diamond` -> `give alex diamond` + `say "Someone has just received a diamond!"`
##To make arguments mandatory (so that the command fails if they are not provided), use $$, e.g. $$1, $$2:
#makeadmin: [op $$1] ## `makeadmin alex` -> `op alex`, `makeadmin` with no arguments = error
##To pass through a range of arguments, put a - (hyphen) after the index:
#tpalias: [tp $1-] ## `tpalias 256 70 256` -> `tp 256 70 256` - this passes arguments 1 and everything after it to the `tp` command
##To change an existing command alias and make it do something else:
#tp: [suicide]

View File

@ -69,9 +69,6 @@ use const JSON_UNESCAPED_SLASHES;
use const SORT_NUMERIC;
class MemoryManager{
private Server $server;
private int $memoryLimit;
private int $globalMemoryLimit;
private int $checkRate;
@ -98,8 +95,9 @@ class MemoryManager{
private \Logger $logger;
public function __construct(Server $server){
$this->server = $server;
public function __construct(
private Server $server
){
$this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager");
$this->init($server->getConfigGroup());

View File

@ -223,8 +223,6 @@ class Server{
private int $sendUsageTicker = 0;
private \AttachableThreadedLogger $logger;
private MemoryManager $memoryManager;
private ConsoleReaderThread $console;
@ -249,7 +247,6 @@ class Server{
private UuidInterface $serverID;
private \DynamicClassLoader $autoloader;
private string $dataPath;
private string $pluginPath;
@ -766,7 +763,12 @@ class Server{
return self::$instance;
}
public function __construct(\DynamicClassLoader $autoloader, \AttachableThreadedLogger $logger, string $dataPath, string $pluginPath){
public function __construct(
private \DynamicClassLoader $autoloader,
private \AttachableThreadedLogger $logger,
string $dataPath,
string $pluginPath
){
if(self::$instance !== null){
throw new \LogicException("Only one server instance can exist at once");
}
@ -774,8 +776,6 @@ class Server{
$this->startTime = microtime(true);
$this->tickSleeper = new SleeperHandler();
$this->autoloader = $autoloader;
$this->logger = $logger;
$this->signalHandler = new SignalHandler(function() : void{
$this->logger->info("Received signal interrupt, stopping the server");
@ -1003,13 +1003,29 @@ class Server{
register_shutdown_function([$this, "crashDump"]);
$this->pluginManager->loadPlugins($this->pluginPath);
$this->enablePlugins(PluginEnableOrder::STARTUP());
if(!$this->startupPrepareWorlds()){
$loadErrorCount = 0;
$this->pluginManager->loadPlugins($this->pluginPath, $loadErrorCount);
if($loadErrorCount > 0){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someLoadErrors()));
$this->forceShutdown();
return;
}
if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
$this->forceShutdown();
return;
}
if(!$this->startupPrepareWorlds()){
$this->forceShutdown();
return;
}
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
$this->forceShutdown();
return;
}
$this->enablePlugins(PluginEnableOrder::POSTWORLD());
if(!$this->startupPrepareNetworkInterfaces()){
$this->forceShutdown();
@ -1073,13 +1089,21 @@ class Server{
return $generatorEntry->getGeneratorClass();
};
$anyWorldFailedToLoad = false;
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
if($options === null){
$options = [];
}elseif(!is_array($options)){
//TODO: this probably should be an error
continue;
}
if(!$this->worldManager->loadWorld($name, true) && !$this->worldManager->isWorldGenerated($name)){
if(!$this->worldManager->loadWorld($name, true)){
if($this->worldManager->isWorldGenerated($name)){
//allow checking if other worlds are loadable, so the user gets all the errors in one go
$anyWorldFailedToLoad = true;
continue;
}
$creationOptions = WorldCreationOptions::create();
//TODO: error checking
@ -1088,11 +1112,13 @@ class Server{
$generatorClass = $getGenerator($generatorName, $generatorOptions, $name);
if($generatorClass === null){
$anyWorldFailedToLoad = true;
continue;
}
$creationOptions->setGeneratorClass($generatorClass);
$creationOptions->setGeneratorOptions($generatorOptions);
$creationOptions->setDifficulty($this->getDifficulty());
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
}
@ -1115,33 +1141,39 @@ class Server{
$default = "world";
$this->configGroup->setConfigString("level-name", "world");
}
if(!$this->worldManager->loadWorld($default, true) && !$this->worldManager->isWorldGenerated($default)){
if(!$this->worldManager->loadWorld($default, true)){
if($this->worldManager->isWorldGenerated($default)){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
return false;
}
$generatorName = $this->configGroup->getConfigString("level-type");
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
if($generatorClass !== null){
$creationOptions = WorldCreationOptions::create()
->setGeneratorClass($generatorClass)
->setGeneratorOptions($generatorOptions);
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
$this->worldManager->generateWorld($default, $creationOptions);
if($generatorClass === null){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
return false;
}
$creationOptions = WorldCreationOptions::create()
->setGeneratorClass($generatorClass)
->setGeneratorOptions($generatorOptions);
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
$creationOptions->setDifficulty($this->getDifficulty());
$this->worldManager->generateWorld($default, $creationOptions);
}
$world = $this->worldManager->getWorldByName($default);
if($world === null){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
$this->forceShutdown();
return false;
throw new AssumptionFailedError("We just loaded/generated the default world, so it must exist");
}
$this->worldManager->setDefaultWorld($world);
}
return true;
return !$anyWorldFailedToLoad;
}
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
@ -1380,16 +1412,21 @@ class Server{
}
}
public function enablePlugins(PluginEnableOrder $type) : void{
public function enablePlugins(PluginEnableOrder $type) : bool{
$allSuccess = true;
foreach($this->pluginManager->getPlugins() as $plugin){
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
$this->pluginManager->enablePlugin($plugin);
if(!$this->pluginManager->enablePlugin($plugin)){
$allSuccess = false;
}
}
}
if($type->equals(PluginEnableOrder::POSTWORLD())){
$this->commandMap->registerServerAliases();
}
return $allSuccess;
}
/**
@ -1429,7 +1466,7 @@ class Server{
}
if($this->isRunning){
$this->logger->emergency("Forcing server shutdown");
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_forcingShutdown()));
}
try{
if(!$this->isRunning()){

View File

@ -32,22 +32,16 @@ use function is_string;
use function strtolower;
final class ServerConfigGroup{
/** @var Config */
private $pocketmineYml;
/** @var Config */
private $serverProperties;
/**
* @var mixed[]
* @phpstan-var array<string, mixed>
*/
private $propertyCache = [];
private array $propertyCache = [];
public function __construct(Config $pocketmineYml, Config $serverProperties){
$this->pocketmineYml = $pocketmineYml;
$this->serverProperties = $serverProperties;
}
public function __construct(
private Config $pocketmineYml,
private Config $serverProperties
){}
/**
* @param mixed $defaultValue

View File

@ -31,16 +31,15 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.2.10";
public const BASE_VERSION = "4.4.0-BETA1";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";
public const BUILD_CHANNEL = "beta";
private function __construct(){
//NOOP
}
/** @var string|null */
private static $gitHash = null;
private static ?string $gitHash = null;
public static function GIT_HASH() : string{
if(self::$gitHash === null){
@ -79,8 +78,7 @@ final class VersionInfo{
return self::$buildNumber;
}
/** @var VersionString|null */
private static $fullVersion = null;
private static ?VersionString $fullVersion = null;
public static function VERSION() : VersionString{
if(self::$fullVersion === null){

View File

@ -23,16 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\math\AxisAlignedBB;
/**
* Air block
*/
class Air extends Transparent{
public function canBeFlowedInto() : bool{
return true;
}
class Air extends Flowable{
public function canBeReplaced() : bool{
return true;
@ -41,15 +35,4 @@ class Air extends Transparent{
public function canBePlaced() : bool{
return false;
}
public function isSolid() : bool{
return false;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}
}

View File

@ -28,6 +28,7 @@ use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -39,7 +40,11 @@ class Anvil extends Transparent implements Fallable{
use FallableTrait;
use HorizontalFacingTrait;
private int $damage = 0;
public const UNDAMAGED = 0;
public const SLIGHTLY_DAMAGED = 1;
public const VERY_DAMAGED = 2;
private int $damage = self::UNDAMAGED;
protected function writeStateToMeta() : int{
return BlockDataSerializer::writeLegacyHorizontalFacing($this->facing) | ($this->damage << 2);
@ -47,7 +52,7 @@ class Anvil extends Transparent implements Fallable{
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x3);
$this->damage = BlockDataSerializer::readBoundedInt("damage", $stateMeta >> 2, 0, 2);
$this->damage = BlockDataSerializer::readBoundedInt("damage", $stateMeta >> 2, self::UNDAMAGED, self::VERY_DAMAGED);
}
public function getStateBitmask() : int{
@ -62,8 +67,8 @@ class Anvil extends Transparent implements Fallable{
/** @return $this */
public function setDamage(int $damage) : self{
if($damage < 0 || $damage > 2){
throw new \InvalidArgumentException("Damage must be in range 0-2");
if($damage < self::UNDAMAGED || $damage > self::VERY_DAMAGED){
throw new \InvalidArgumentException("Damage must be in range " . self::UNDAMAGED . " ... " . self::VERY_DAMAGED);
}
$this->damage = $damage;
return $this;
@ -76,6 +81,10 @@ class Anvil extends Transparent implements Fallable{
return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
$player->setCurrentWindow(new AnvilInventory($this->position));

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Bamboo as ItemBamboo;
use pocketmine\item\Fertilizer;
@ -100,6 +101,10 @@ class Bamboo extends Transparent{
return [AxisAlignedBB::one()->trim(Facing::SOUTH, $inset)->trim(Facing::EAST, $inset)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
private static function getOffsetSeed(int $x, int $y, int $z) : int{
$p1 = gmp_mul($z, 0x6ebfff5);
$p2 = gmp_mul($x, 0x2fc20f);

View File

@ -27,6 +27,7 @@ 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\data\bedrock\DyeColorIdMap;
use pocketmine\item\Banner as ItemBanner;
use pocketmine\item\Item;
@ -107,6 +108,10 @@ abstract class BaseBanner extends Transparent{
return [];
}
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($item instanceof ItemBanner){
$this->color = $item->getColor();

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
abstract class BaseCoral extends Transparent{
@ -65,4 +66,8 @@ abstract class BaseCoral extends Transparent{
public function isSolid() : bool{ return false; }
protected function recalculateCollisionBoxes() : array{ return []; }
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -38,7 +38,7 @@ use function in_array;
abstract class BaseRail extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
if($blockReplace->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
@ -220,11 +220,11 @@ abstract class BaseRail extends Flowable{
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->isTransparent()){
if(!$this->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
$this->position->getWorld()->useBreakOn($this->position);
}else{
foreach($this->getCurrentShapeConnections() as $connection){
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->getSupportType(Facing::UP)->hasEdgeSupport()){
$this->position->getWorld()->useBreakOn($this->position);
break;
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Sign as TileSign;
use pocketmine\block\utils\SignText;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\SignChangeEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
@ -77,6 +78,10 @@ abstract class BaseSign extends Transparent{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
abstract protected function getSupportingFace() : int;
public function onNearbyBlockChange() : void{

View File

@ -28,6 +28,7 @@ use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\bedrock\DyeColorIdMap;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
@ -94,6 +95,10 @@ class Bed extends Transparent{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function isHeadPart() : bool{
return $this->head;
}
@ -181,12 +186,11 @@ class Bed extends Transparent{
}
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->isTransparent()){
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH;
$next = $this->getSide($this->getOtherHalfSide());
if($next->canBeReplaced() && !$next->getSide(Facing::DOWN)->isTransparent()){
if($next->canBeReplaced() && $this->canBeSupportedBy($next->getSide(Facing::DOWN))){
$nextState = clone $this;
$nextState->head = true;
$tx->addBlock($blockReplace->position, $this)->addBlock($next->position, $nextState);
@ -216,4 +220,8 @@ class Bed extends Transparent{
return parent::getAffectedBlocks();
}
private function canBeSupportedBy(Block $block) : bool{
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
}
}

View File

@ -30,7 +30,7 @@ use function mt_rand;
class Beetroot extends Crops{
public function getDropsForCompatibleTool(Item $item) : array{
if($this->age >= 7){
if($this->age >= self::MAX_AGE){
return [
VanillaItems::BEETROOT(),
VanillaItems::BEETROOT_SEEDS()->setCount(mt_rand(0, 3))

View File

@ -28,6 +28,7 @@ use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -101,6 +102,10 @@ final class Bell extends Transparent{
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
/** @return $this */

View File

@ -29,6 +29,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Spawnable;
use pocketmine\block\tile\Tile;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
@ -152,7 +153,7 @@ class Block{
$oldTile->close();
$oldTile = null;
}elseif($oldTile instanceof Spawnable){
$oldTile->setDirty(); //destroy old network cache
$oldTile->clearSpawnCompoundCache(); //destroy old network cache
}
}
if($oldTile === null && $tileType !== null){
@ -610,6 +611,10 @@ class Block{
return [AxisAlignedBB::one()];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::FULL();
}
public function isFullCube() : bool{
$bb = $this->getCollisionBoxes();

View File

@ -38,18 +38,17 @@ class BlockBreakInfo{
*/
public const INCOMPATIBLE_TOOL_MULTIPLIER = 5.0;
private float $hardness;
private float $blastResistance;
private int $toolType;
private int $toolHarvestLevel;
/**
* @param float|null $blastResistance default 5x hardness
*/
public function __construct(float $hardness, int $toolType = BlockToolType::NONE, int $toolHarvestLevel = 0, ?float $blastResistance = null){
$this->hardness = $hardness;
$this->toolType = $toolType;
$this->toolHarvestLevel = $toolHarvestLevel;
public function __construct(
private float $hardness,
private int $toolType = BlockToolType::NONE,
private int $toolHarvestLevel = 0,
?float $blastResistance = null
){
$this->blastResistance = $blastResistance ?? $hardness * 5;
}

View File

@ -74,7 +74,7 @@ class BlockFactory{
* @var \SplFixedArray|Block[]
* @phpstan-var \SplFixedArray<Block>
*/
private $fullList;
private \SplFixedArray $fullList;
/**
* @var \SplFixedArray|int[]
@ -86,22 +86,22 @@ class BlockFactory{
* @var \SplFixedArray|int[]
* @phpstan-var \SplFixedArray<int>
*/
public $light;
public \SplFixedArray $light;
/**
* @var \SplFixedArray|int[]
* @phpstan-var \SplFixedArray<int>
*/
public $lightFilter;
public \SplFixedArray $lightFilter;
/**
* @var \SplFixedArray|bool[]
* @phpstan-var \SplFixedArray<bool>
*/
public $blocksDirectSkyLight;
public \SplFixedArray $blocksDirectSkyLight;
/**
* @var \SplFixedArray|float[]
* @phpstan-var \SplFixedArray<float>
*/
public $blastResistance;
public \SplFixedArray $blastResistance;
public function __construct(){
$this->fullList = new \SplFixedArray(1024 << Block::INTERNAL_METADATA_BITS);
@ -394,6 +394,7 @@ class BlockFactory{
$this->registerAllMeta(new Stair(new BID(Ids::STONE_BRICK_STAIRS, 0), "Stone Brick Stairs", $stoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS, 0), "Mossy Stone Brick Stairs", $stoneBreakInfo));
$this->registerAllMeta(new StoneButton(new BID(Ids::STONE_BUTTON, 0), "Stone Button", new BlockBreakInfo(0.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new Stonecutter(new BID(Ids::STONECUTTER_BLOCK, 0, ItemIds::STONECUTTER_BLOCK), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE, 0), "Stone Pressure Plate", new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
//TODO: in the future this won't be the same for all the types
@ -434,7 +435,7 @@ class BlockFactory{
$this->registerSlabWithDoubleHighBitsRemapping($slabType);
}
$this->registerAllMeta(new Opaque(new BID(Ids::STONECUTTER, 0), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Opaque(new BID(Ids::STONECUTTER, 0), "Legacy Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Sugarcane(new BID(Ids::REEDS_BLOCK, 0, ItemIds::REEDS), "Sugarcane", BlockBreakInfo::instant()));
$this->registerAllMeta(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
$this->registerAllMeta(new TNT(new BID(Ids::TNT, 0), "TNT", BlockBreakInfo::instant()));
@ -644,7 +645,6 @@ class BlockFactory{
//TODO: minecraft:seagrass
//TODO: minecraft:smithing_table
//TODO: minecraft:sticky_piston
//TODO: minecraft:stonecutter_block
//TODO: minecraft:structure_block
//TODO: minecraft:turtle_egg
//endregion

View File

@ -27,25 +27,18 @@ use pocketmine\block\tile\Tile;
use pocketmine\utils\Utils;
class BlockIdentifier{
private int $blockId;
private int $variant;
private ?int $itemId;
/** @phpstan-var class-string<Tile>|null */
private ?string $tileClass;
/**
* @phpstan-param class-string<Tile>|null $tileClass
*/
public function __construct(int $blockId, int $variant, ?int $itemId = null, ?string $tileClass = null){
$this->blockId = $blockId;
$this->variant = $variant;
$this->itemId = $itemId;
public function __construct(
private int $blockId,
private int $variant,
private ?int $itemId = null,
private ?string $tileClass = null
){
if($tileClass !== null){
Utils::testValidInstance($tileClass, Tile::class);
}
$this->tileClass = $tileClass;
}
public function getBlockId() : int{

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
@ -83,6 +84,10 @@ class BrewingStand extends Transparent{
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function hasSlot(BrewingStandSlot $slot) : bool{
return array_key_exists($slot->id(), $this->slots);
}

View File

@ -61,9 +61,11 @@ abstract class Button extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
//TODO: check valid target block
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
if($this->canBeSupportedBy($blockClicked, $face)){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
abstract protected function getActivationTime() : int;
@ -86,4 +88,14 @@ abstract class Button extends Flowable{
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound());
}
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $support, int $face) : bool{
return $support->getSupportType($face)->hasCenterSupport();
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent;
@ -36,6 +37,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Cactus extends Transparent{
public const MAX_AGE = 15;
protected int $age = 0;
@ -44,7 +46,7 @@ class Cactus extends Transparent{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 15);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
}
public function getStateBitmask() : int{
@ -55,8 +57,8 @@ class Cactus extends Transparent{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 15){
throw new \InvalidArgumentException("Age must be in range 0-15");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -74,6 +76,10 @@ class Cactus extends Transparent{
return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onEntityInside(Entity $entity) : bool{
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_CONTACT, 1);
$entity->attack($ev);
@ -101,7 +107,7 @@ class Cactus extends Transparent{
public function onRandomTick() : void{
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
if($this->age === 15){
if($this->age === self::MAX_AGE){
for($y = 1; $y < 3; ++$y){
if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){
break;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\FoodSource;
use pocketmine\entity\Living;
@ -35,6 +36,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Cake extends Transparent implements FoodSource{
public const MAX_BITES = 6;
protected int $bites = 0;
@ -43,7 +45,7 @@ class Cake extends Transparent implements FoodSource{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->bites = BlockDataSerializer::readBoundedInt("bites", $stateMeta, 0, 6);
$this->bites = BlockDataSerializer::readBoundedInt("bites", $stateMeta, 0, self::MAX_BITES);
}
public function getStateBitmask() : int{
@ -62,12 +64,16 @@ class Cake extends Transparent implements FoodSource{
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function getBites() : int{ return $this->bites; }
/** @return $this */
public function setBites(int $bites) : self{
if($bites < 0 || $bites > 6){
throw new \InvalidArgumentException("Bites must be in range 0-6");
if($bites < 0 || $bites > self::MAX_BITES){
throw new \InvalidArgumentException("Bites must be in range 0 ... " . self::MAX_BITES);
}
$this->bites = $bites;
return $this;
@ -118,7 +124,7 @@ class Cake extends Transparent implements FoodSource{
public function getResidue(){
$clone = clone $this;
$clone->bites++;
if($clone->bites > 6){
if($clone->bites > self::MAX_BITES){
$clone = VanillaBlocks::AIR();
}
return $clone;

View File

@ -31,7 +31,7 @@ class Carrot extends Crops{
public function getDropsForCompatibleTool(Item $item) : array{
return [
VanillaItems::CARROT()->setCount($this->age >= 7 ? mt_rand(1, 4) : 1)
VanillaItems::CARROT()->setCount($this->age >= self::MAX_AGE ? mt_rand(1, 4) : 1)
];
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Chest as TileChest;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\ChestPairEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
@ -45,6 +46,10 @@ class Chest extends Transparent{
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onPostPlace() : void{
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileChest){

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\block\utils\TreeType;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Fertilizer;
@ -41,6 +42,8 @@ use function mt_rand;
class CocoaBlock extends Transparent{
use HorizontalFacingTrait;
public const MAX_AGE = 2;
protected int $age = 0;
protected function writeStateToMeta() : int{
@ -49,7 +52,7 @@ class CocoaBlock extends Transparent{
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = Facing::opposite(BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03));
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta >> 2, 0, 2);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta >> 2, 0, self::MAX_AGE);
}
public function getStateBitmask() : int{
@ -60,8 +63,8 @@ class CocoaBlock extends Transparent{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 2){
throw new \InvalidArgumentException("Age must be in range 0-2");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -81,6 +84,10 @@ class CocoaBlock extends Transparent{
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
private function canAttachTo(Block $block) : bool{
return $block instanceof Wood && $block->getTreeType()->equals(TreeType::JUNGLE());
}
@ -121,7 +128,7 @@ class CocoaBlock extends Transparent{
}
private function grow() : bool{
if($this->age < 2){
if($this->age < self::MAX_AGE){
$block = clone $this;
$block->age++;
$ev = new BlockGrowEvent($this, $block);
@ -136,7 +143,7 @@ class CocoaBlock extends Transparent{
public function getDropsForCompatibleTool(Item $item) : array{
return [
VanillaItems::COCOA_BEANS()->setCount($this->age === 2 ? mt_rand(2, 3) : 1)
VanillaItems::COCOA_BEANS()->setCount($this->age === self::MAX_AGE ? mt_rand(2, 3) : 1)
];
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\data\bedrock\CoralTypeIdMap;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
@ -65,7 +66,7 @@ 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(!$tx->fetchBlock($blockReplace->getPosition()->down())->isSolid()){
if(!$this->canBeSupportedBy($tx->fetchBlock($blockReplace->getPosition()->down()))){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
@ -73,10 +74,14 @@ final class Coral extends BaseCoral{
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->down())->isSolid()){
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
private function canBeSupportedBy(Block $block) : bool{
return $block->getSupportType(Facing::UP)->hasCenterSupport();
}
}

View File

@ -34,6 +34,7 @@ use pocketmine\world\BlockTransaction;
use function mt_rand;
abstract class Crops extends Flowable{
public const MAX_AGE = 7;
protected int $age = 0;
@ -42,7 +43,7 @@ abstract class Crops extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 7);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
}
public function getStateBitmask() : int{
@ -53,8 +54,8 @@ abstract class Crops extends Flowable{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 7){
throw new \InvalidArgumentException("Age must be in range 0-7");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -69,11 +70,11 @@ abstract class Crops extends Flowable{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->age < 7 && $item instanceof Fertilizer){
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
$block = clone $this;
$block->age += mt_rand(2, 5);
if($block->age > 7){
$block->age = 7;
if($block->age > self::MAX_AGE){
$block->age = self::MAX_AGE;
}
$ev = new BlockGrowEvent($this, $block);
@ -100,7 +101,7 @@ abstract class Crops extends Flowable{
}
public function onRandomTick() : void{
if($this->age < 7 && mt_rand(0, 2) === 1){
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){
$block = clone $this;
++$block->age;
$ev = new BlockGrowEvent($this, $block);

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -87,6 +88,10 @@ class DaylightSensor extends Transparent{
return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$this->inverted = !$this->inverted;
$this->signalStrength = $this->recalculateSignalStrength();

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -119,8 +120,12 @@ class Door extends Transparent{
return [AxisAlignedBB::one()->trim($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getId() === BlockLegacyIds::AIR){ //Replace with common break method
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN)) && !$this->getSide(Facing::DOWN) instanceof Door){ //Replace with common break method
$this->position->getWorld()->useBreakOn($this->position); //this will delete both halves if they exist
}
}
@ -129,7 +134,7 @@ class Door extends Transparent{
if($face === Facing::UP){
$blockUp = $this->getSide(Facing::UP);
$blockDown = $this->getSide(Facing::DOWN);
if(!$blockUp->canBeReplaced() || $blockDown->isTransparent()){
if(!$blockUp->canBeReplaced() || !$this->canBeSupportedBy($blockDown)){
return false;
}
@ -184,4 +189,8 @@ class Door extends Transparent{
}
return parent::getAffectedBlocks();
}
private function canBeSupportedBy(Block $block) : bool{
return $block->getSupportType(Facing::UP)->hasEdgeSupport();
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\BlockTeleportEvent;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
@ -82,4 +83,8 @@ class DragonEgg extends Transparent implements Fallable{
}
}
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -24,16 +24,15 @@ declare(strict_types=1);
namespace pocketmine\block;
class Element extends Opaque{
private int $atomicWeight;
private int $group;
private string $symbol;
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo, string $symbol, int $atomicWeight, int $group){
public function __construct(
BlockIdentifier $idInfo,
string $name,
BlockBreakInfo $breakInfo,
private string $symbol,
private int $atomicWeight,
private int $group
){
parent::__construct($idInfo, $name, $breakInfo);
$this->atomicWeight = $atomicWeight;
$this->group = $group;
$this->symbol = $symbol;
}
public function getAtomicWeight() : int{

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\EnchantInventory;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -39,6 +40,10 @@ class EnchantingTable extends Transparent{
return [AxisAlignedBB::one()->trim(Facing::UP, 0.25)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
//TODO lock

View File

@ -27,6 +27,7 @@ use pocketmine\block\inventory\EnderChestInventory;
use pocketmine\block\tile\EnderChest as TileEnderChest;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -49,6 +50,10 @@ class EnderChest extends Transparent{
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
$enderChest = $this->position->getWorld()->getTile($this->position);

View File

@ -33,6 +33,7 @@ use pocketmine\math\Facing;
use function lcg_value;
class Farmland extends Transparent{
public const MAX_WETNESS = 7;
protected int $wetness = 0; //"moisture" blockstate property in PC
@ -41,7 +42,7 @@ class Farmland extends Transparent{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->wetness = BlockDataSerializer::readBoundedInt("wetness", $stateMeta, 0, 7);
$this->wetness = BlockDataSerializer::readBoundedInt("wetness", $stateMeta, 0, self::MAX_WETNESS);
}
public function getStateBitmask() : int{
@ -52,8 +53,8 @@ class Farmland extends Transparent{
/** @return $this */
public function setWetness(int $wetness) : self{
if($wetness < 0 || $wetness > 7){
throw new \InvalidArgumentException("Wetness must be in range 0-7");
if($wetness < 0 || $wetness > self::MAX_WETNESS){
throw new \InvalidArgumentException("Wetness must be in range 0 ... " . self::MAX_WETNESS);
}
$this->wetness = $wetness;
return $this;
@ -84,8 +85,8 @@ class Farmland extends Transparent{
}else{
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
}
}elseif($this->wetness < 7){
$this->wetness = 7;
}elseif($this->wetness < self::MAX_WETNESS){
$this->wetness = self::MAX_WETNESS;
$this->position->getWorld()->setBlock($this->position, $this, false);
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -93,4 +94,8 @@ class Fence extends Transparent{
return $bbs;
}
public function getSupportType(int $facing) : SupportType{
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -78,6 +79,10 @@ class FenceGate extends Transparent{
return $this->open ? [] : [AxisAlignedBB::one()->extend(Facing::UP, 0.5)->squash(Facing::axis($this->facing), 6 / 16)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
private function checkInWall() : bool{
return (
$this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall ||

View File

@ -41,6 +41,7 @@ use function min;
use function mt_rand;
class Fire extends Flowable{
public const MAX_AGE = 15;
protected int $age = 0;
@ -49,7 +50,7 @@ class Fire extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 15);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
}
public function getStateBitmask() : int{
@ -60,8 +61,8 @@ class Fire extends Flowable{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 15){
throw new \InvalidArgumentException("Age must be in range 0-15");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -114,7 +115,7 @@ class Fire extends Flowable{
$down = $this->getSide(Facing::DOWN);
$result = null;
if($this->age < 15 && mt_rand(0, 2) === 0){
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 0){
$this->age++;
$result = $this;
}
@ -122,7 +123,7 @@ class Fire extends Flowable{
if(!$down->burnsForever()){
//TODO: check rain
if($this->age === 15){
if($this->age === self::MAX_AGE){
if(!$down->isFlammable() && mt_rand(0, 3) === 3){ //1/4 chance to extinguish
$canSpread = false;
$result = VanillaBlocks::AIR();
@ -184,7 +185,7 @@ class Fire extends Flowable{
$spreadedFire = false;
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
$fire = clone $this;
$fire->age = min(15, $fire->age + (mt_rand(0, 4) >> 2));
$fire->age = min(self::MAX_AGE, $fire->age + (mt_rand(0, 4) >> 2));
$spreadedFire = $this->spreadBlock($block, $fire);
}
if(!$spreadedFire){
@ -245,7 +246,7 @@ class Fire extends Flowable{
if($maxChance > 0 && mt_rand(0, $randomBound - 1) <= $maxChance){
$new = clone $this;
$new->age = min(15, $this->age + (mt_rand(0, 4) >> 2));
$new->age = min(self::MAX_AGE, $this->age + (mt_rand(0, 4) >> 2));
$this->spreadBlock($block, $new);
}
}

View File

@ -29,6 +29,7 @@ use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\ItemIds;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
@ -93,7 +94,7 @@ final class FloorCoralFan extends BaseCoral{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$tx->fetchBlock($blockReplace->getPosition()->down())->isSolid()){
if(!$this->canBeSupportedBy($tx->fetchBlock($blockReplace->getPosition()->down()))){
return false;
}
if($player !== null){
@ -112,11 +113,15 @@ final class FloorCoralFan extends BaseCoral{
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->down())->isSolid()){
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
private function canBeSupportedBy(Block $block) : bool{
return $block->getSupportType(Facing::UP)->hasCenterSupport();
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\AxisAlignedBB;
abstract class Flowable extends Transparent{
@ -41,4 +42,8 @@ abstract class Flowable extends Transparent{
protected function recalculateCollisionBoxes() : array{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -82,6 +82,10 @@ class FlowerPot extends Flowable{
return false;
}
return $this->isValidPlant($block);
}
private function isValidPlant(Block $block) : bool{
return
$block instanceof Cactus ||
$block instanceof DeadBush ||
@ -100,7 +104,7 @@ class FlowerPot extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->getSide(Facing::DOWN)->isTransparent()){
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
return false;
}
@ -108,22 +112,46 @@ class FlowerPot extends Flowable{
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->isTransparent()){
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $block) : bool{
return $block->getSupportType(Facing::UP)->hasCenterSupport();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$plant = $item->getBlock();
if(!$this->canAddPlant($plant)){
return false;
if($this->plant !== null){
if($this->isValidPlant($plant)){
//for some reason, vanilla doesn't remove the contents of the pot if the held item is plantable
//and will also cause a new plant to be placed if clicking on the side
return false;
}
$removedItems = [$this->plant->asItem()];
if($player !== null){
//this one just has to be a weirdo :(
//this is the only block that directly adds items to the player inventory instead of just dropping items
$removedItems = $player->getInventory()->addItem(...$removedItems);
}
foreach($removedItems as $drops){
$this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), $drops);
}
$this->setPlant(null);
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}elseif($this->isValidPlant($plant)){
$this->setPlant($plant);
$item->pop();
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
$this->setPlant($plant);
$item->pop();
$this->position->getWorld()->setBlock($this->position, $this);
return true;
return false;
}
public function getDropsForCompatibleTool(Item $item) : array{

View File

@ -28,11 +28,12 @@ use pocketmine\event\block\BlockMeltEvent;
use function mt_rand;
class FrostedIce extends Ice{
public const MAX_AGE = 3;
protected int $age = 0;
public function readStateFromData(int $id, int $stateMeta) : void{
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 3);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
}
protected function writeStateToMeta() : int{
@ -47,8 +48,8 @@ class FrostedIce extends Ice{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 3){
throw new \InvalidArgumentException("Age must be in range 0-3");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -105,7 +106,7 @@ class FrostedIce extends Ice{
* @return bool Whether the ice was destroyed.
*/
private function tryMelt() : bool{
if($this->age >= 3){
if($this->age >= self::MAX_AGE){
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER());
$ev->call();
if(!$ev->isCancelled()){

View File

@ -27,6 +27,7 @@ use pocketmine\block\tile\Hopper as TileHopper;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -78,6 +79,14 @@ class Hopper extends Transparent{
return $result;
}
public function getSupportType(int $facing) : SupportType{
return match($facing){
Facing::UP => SupportType::FULL(),
Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER() : SupportType::NONE(),
default => SupportType::NONE()
};
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$this->facing = $face === Facing::DOWN ? Facing::DOWN : Facing::opposite($face);

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\item\Item;
@ -64,8 +65,12 @@ class Ladder extends Transparent{
return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)];
}
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(!$blockClicked->isTransparent() && Facing::axis($face) !== Axis::Y){
if($this->canBeSupportedBy($blockClicked, $face) && Facing::axis($face) !== Axis::Y){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
@ -74,8 +79,12 @@ class Ladder extends Transparent{
}
public function onNearbyBlockChange() : void{
if(!$this->getSide(Facing::opposite($this->facing))->isSolid()){ //Replace with common break method
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){ //Replace with common break method
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $block, int $face) : bool{
return $block->getSupportType($face)->equals(SupportType::FULL());
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
@ -72,22 +73,27 @@ class Lantern extends Transparent{
];
}
protected function canAttachTo(Block $b) : bool{
return !$b->isTransparent();
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->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->up())) && !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()))){
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::UP), Facing::DOWN) && !$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN), Facing::UP)){
return false;
}
$this->hanging = ($face === Facing::DOWN || !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down())));
$this->hanging = ($face === Facing::DOWN || !$this->canBeSupportedBy($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()), Facing::UP));
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canAttachTo($this->position->getWorld()->getBlock($this->hanging ? $this->position->up() : $this->position->down()))){
$face = $this->hanging ? Facing::UP : Facing::DOWN;
if(!$this->canBeSupportedBy($this->getSide($face), Facing::opposite($face))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $block, int $face) : bool{
return $block->getSupportType($face)->hasCenterSupport();
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\block\utils\TreeType;
use pocketmine\event\block\LeavesDecayEvent;
use pocketmine\item\Item;
@ -148,6 +149,9 @@ class Leaves extends Transparent{
if(($this->treeType->equals(TreeType::OAK()) || $this->treeType->equals(TreeType::DARK_OAK())) && mt_rand(1, 200) === 1){ //Apples
$drops[] = VanillaItems::APPLE();
}
if(mt_rand(1, 50) === 1){
$drops[] = VanillaItems::STICK()->setCount(mt_rand(1, 2));
}
return $drops;
}
@ -163,4 +167,8 @@ class Leaves extends Transparent{
public function getFlammability() : int{
return 60;
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\tile\Lectern as TileLectern;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\item\WritableBookBase;
use pocketmine\math\AxisAlignedBB;
@ -92,6 +93,10 @@ class Lectern extends Transparent{
return [AxisAlignedBB::one()->trim(Facing::UP, 0.1)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function isProducingSignal() : bool{ return $this->producingSignal; }
/** @return $this */

View File

@ -96,7 +96,7 @@ class Lever extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockClicked->isSolid()){
if(!$this->canBeSupportedBy($blockClicked, $face)){
return false;
}
@ -120,7 +120,8 @@ class Lever extends Flowable{
}
public function onNearbyBlockChange() : void{
if(!$this->getSide(Facing::opposite($this->facing->getFacing()))->isSolid()){
$facing = $this->facing->getFacing();
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($facing)), $facing)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
@ -135,5 +136,9 @@ class Lever extends Flowable{
return true;
}
private function canBeSupportedBy(Block $block, int $face) : bool{
return $block->getSupportType($face)->hasCenterSupport();
}
//TODO
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\MinimumCostFlowCalculator;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\event\block\BlockFormEvent;
use pocketmine\event\block\BlockSpreadEvent;
@ -37,6 +38,7 @@ use pocketmine\world\sound\Sound;
use function lcg_value;
abstract class Liquid extends Transparent{
public const MAX_DECAY = 7;
protected BlockIdentifierFlattened $idInfoFlattened;
@ -62,7 +64,7 @@ abstract class Liquid extends Transparent{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->decay = BlockDataSerializer::readBoundedInt("decay", $stateMeta & 0x07, 0, 7);
$this->decay = BlockDataSerializer::readBoundedInt("decay", $stateMeta & 0x07, 0, self::MAX_DECAY);
$this->falling = ($stateMeta & BlockLegacyMetadata::LIQUID_FLAG_FALLING) !== 0;
$this->still = $id === $this->idInfoFlattened->getSecondId();
}
@ -83,8 +85,8 @@ abstract class Liquid extends Transparent{
/** @return $this */
public function setDecay(int $decay) : self{
if($decay < 0 || $decay > 7){
throw new \InvalidArgumentException("Decay must be in range 0-7");
if($decay < 0 || $decay > self::MAX_DECAY){
throw new \InvalidArgumentException("Decay must be in range 0 ... " . self::MAX_DECAY);
}
$this->decay = $decay;
return $this;
@ -113,6 +115,10 @@ abstract class Liquid extends Transparent{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
@ -282,7 +288,7 @@ abstract class Liquid extends Transparent{
$newDecay = $smallestFlowDecay + $multiplier;
$falling = false;
if($newDecay >= 8 || $smallestFlowDecay < 0){
if($newDecay > self::MAX_DECAY || $smallestFlowDecay < 0){
$newDecay = -1;
}
@ -322,7 +328,7 @@ abstract class Liquid extends Transparent{
$adjacentDecay = $this->decay + $multiplier;
}
if($adjacentDecay < 8){
if($adjacentDecay <= self::MAX_DECAY){
$calculator = new MinimumCostFlowCalculator($this->position->getWorld(), $this->getFlowDecayPerBlock(), \Closure::fromCallable([$this, 'canFlowInto']));
foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){
$this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false);
@ -342,7 +348,7 @@ abstract class Liquid extends Transparent{
$ev = new BlockSpreadEvent($block, $this, $new);
$ev->call();
if(!$ev->isCancelled()){
if($block->getId() !== BlockLegacyIds::AIR){
if($block->getId() > 0){
$this->position->getWorld()->useBreakOn($block->position);
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use function mt_rand;
@ -39,4 +40,8 @@ class MonsterSpawner extends Transparent{
public function onScheduledUpdate() : void{
//TODO
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\item\Item;
use pocketmine\math\Axis;
@ -75,6 +76,10 @@ class NetherPortal extends Transparent{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function getDrops(Item $item) : array{
return [];
}

View File

@ -33,6 +33,7 @@ use pocketmine\world\BlockTransaction;
use function mt_rand;
class NetherWartPlant extends Flowable{
public const MAX_AGE = 3;
protected int $age = 0;
@ -41,7 +42,7 @@ class NetherWartPlant extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 3);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
}
public function getStateBitmask() : int{
@ -52,8 +53,8 @@ class NetherWartPlant extends Flowable{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 3){
throw new \InvalidArgumentException("Age must be in range 0-3");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ..." . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -79,7 +80,7 @@ class NetherWartPlant extends Flowable{
}
public function onRandomTick() : void{
if($this->age < 3 && mt_rand(0, 10) === 0){ //Still growing
if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing
$block = clone $this;
$block->age++;
$ev = new BlockGrowEvent($this, $block);
@ -92,7 +93,7 @@ class NetherWartPlant extends Flowable{
public function getDropsForCompatibleTool(Item $item) : array{
return [
$this->asItem()->setCount($this->age === 3 ? mt_rand(2, 4) : 1)
$this->asItem()->setCount($this->age === self::MAX_AGE ? mt_rand(2, 4) : 1)
];
}
}

View File

@ -31,9 +31,9 @@ class Potato extends Crops{
public function getDropsForCompatibleTool(Item $item) : array{
$result = [
VanillaItems::POTATO()->setCount($this->age >= 7 ? mt_rand(1, 5) : 1)
VanillaItems::POTATO()->setCount($this->age >= self::MAX_AGE ? mt_rand(1, 5) : 1)
];
if($this->age >= 7 && mt_rand(0, 49) === 0){
if($this->age >= self::MAX_AGE && mt_rand(0, 49) === 0){
$result[] = VanillaItems::POISONOUS_POTATO();
}
return $result;

View File

@ -23,6 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
abstract class PressurePlate extends Transparent{
public function isSolid() : bool{
@ -33,5 +40,20 @@ abstract class PressurePlate extends Transparent{
return [];
}
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->canBeSupportedBy($blockClicked)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
private function canBeSupportedBy(Block $block) : bool{
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
}
//TODO
}

View File

@ -28,6 +28,7 @@ use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -103,7 +104,7 @@ class RedstoneComparator extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
@ -120,10 +121,14 @@ class RedstoneComparator extends Flowable{
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->isTransparent()){
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $block) : bool{
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
}
//TODO: redstone functionality
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -37,9 +38,12 @@ class RedstoneRepeater extends Flowable{
use HorizontalFacingTrait;
use PoweredByRedstoneTrait;
public const MIN_DELAY = 1;
public const MAX_DELAY = 4;
protected BlockIdentifierFlattened $idInfoFlattened;
protected int $delay = 1;
protected int $delay = self::MIN_DELAY;
public function __construct(BlockIdentifierFlattened $idInfo, string $name, BlockBreakInfo $breakInfo){
$this->idInfoFlattened = $idInfo;
@ -52,7 +56,7 @@ class RedstoneRepeater extends Flowable{
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03);
$this->delay = BlockDataSerializer::readBoundedInt("delay", ($stateMeta >> 2) + 1, 1, 4);
$this->delay = BlockDataSerializer::readBoundedInt("delay", ($stateMeta >> 2) + 1, self::MIN_DELAY, self::MAX_DELAY);
$this->powered = $id === $this->idInfoFlattened->getSecondId();
}
@ -68,8 +72,8 @@ class RedstoneRepeater extends Flowable{
/** @return $this */
public function setDelay(int $delay) : self{
if($delay < 1 || $delay > 4){
throw new \InvalidArgumentException("Delay must be in range 1-4");
if($delay < self::MIN_DELAY || $delay > self::MAX_DELAY){
throw new \InvalidArgumentException("Delay must be in range " . self::MIN_DELAY . " ... " . self::MAX_DELAY);
}
$this->delay = $delay;
return $this;
@ -83,7 +87,7 @@ class RedstoneRepeater extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
@ -95,18 +99,22 @@ class RedstoneRepeater extends Flowable{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(++$this->delay > 4){
$this->delay = 1;
if(++$this->delay > self::MAX_DELAY){
$this->delay = self::MIN_DELAY;
}
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->isTransparent()){
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $block) : bool{
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
}
//TODO: redstone functionality
}

View File

@ -25,10 +25,22 @@ namespace pocketmine\block;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class RedstoneWire extends Flowable{
use AnalogRedstoneSignalEmitterTrait;
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 readStateFromData(int $id, int $stateMeta) : void{
$this->signalStrength = BlockDataSerializer::readBoundedInt("signalStrength", $stateMeta, 0, 15);
}
@ -45,4 +57,14 @@ class RedstoneWire extends Flowable{
parent::readStateFromWorld();
//TODO: check connections to nearby redstone components
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedBy(Block $block) : bool{
return $block->getSupportType(Facing::UP)->hasCenterSupport();
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
@ -30,8 +31,10 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class SeaPickle extends Transparent{
public const MIN_COUNT = 1;
public const MAX_COUNT = 4;
protected int $count = 1;
protected int $count = self::MIN_COUNT;
protected bool $underwater = false;
public function readStateFromData(int $id, int $stateMeta) : void{
@ -51,8 +54,8 @@ class SeaPickle extends Transparent{
/** @return $this */
public function setCount(int $count) : self{
if($count < 1 || $count > 4){
throw new \InvalidArgumentException("Count must be in range 1-4");
if($count < self::MIN_COUNT || $count > self::MAX_COUNT){
throw new \InvalidArgumentException("Count must be in range " . self::MIN_COUNT . " ... " . self::MAX_COUNT);
}
$this->count = $count;
return $this;
@ -81,14 +84,18 @@ class SeaPickle extends Transparent{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
//TODO: proper placement logic (needs a supporting face below)
return ($blockReplace instanceof SeaPickle && $blockReplace->count < 4) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
return ($blockReplace instanceof SeaPickle && $blockReplace->count < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$this->underwater = false; //TODO: implement this once we have new water logic in place
if($blockReplace instanceof SeaPickle && $blockReplace->count < 4){
if($blockReplace instanceof SeaPickle && $blockReplace->count < self::MAX_COUNT){
$this->count = $blockReplace->count + 1;
}

View File

@ -36,12 +36,14 @@ use function assert;
use function floor;
class Skull extends Flowable{
public const MIN_ROTATION = 0;
public const MAX_ROTATION = 15;
protected SkullType $skullType;
protected int $facing = Facing::NORTH;
protected bool $noDrops = false;
protected int $rotation = 0; //TODO: split this into floor skull and wall skull handling
protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
$this->skullType = SkullType::SKELETON(); //TODO: this should be a parameter
@ -105,8 +107,8 @@ class Skull extends Flowable{
/** @return $this */
public function setRotation(int $rotation) : self{
if($rotation < 0 || $rotation > 15){
throw new \InvalidArgumentException("Rotation must be a value between 0 and 15");
if($rotation < self::MIN_ROTATION || $rotation > self::MAX_ROTATION){
throw new \InvalidArgumentException("Rotation must be in range " . self::MIN_ROTATION . " ... " . self::MAX_ROTATION);
}
$this->rotation = $rotation;
return $this;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -125,6 +126,15 @@ class Slab extends Transparent{
return [AxisAlignedBB::one()->trim($this->slabType->equals(SlabType::TOP()) ? Facing::DOWN : Facing::UP, 0.5)];
}
public function getSupportType(int $facing) : SupportType{
if($this->slabType->equals(SlabType::DOUBLE())){
return SupportType::FULL();
}elseif(($facing === Facing::UP && $this->slabType->equals(SlabType::TOP())) || ($facing === Facing::DOWN && $this->slabType->equals(SlabType::BOTTOM()))){
return SupportType::FULL();
}
return SupportType::NONE();
}
public function getDropsForCompatibleTool(Item $item) : array{
return [$this->asItem()->setCount($this->slabType->equals(SlabType::DOUBLE()) ? 2 : 1)];
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\BlockMeltEvent;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
@ -40,14 +41,17 @@ use function max;
class SnowLayer extends Flowable implements Fallable{
use FallableTrait;
protected int $layers = 1;
public const MIN_LAYERS = 1;
public const MAX_LAYERS = 8;
protected int $layers = self::MIN_LAYERS;
protected function writeStateToMeta() : int{
return $this->layers - 1;
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->layers = BlockDataSerializer::readBoundedInt("layers", $stateMeta + 1, 1, 8);
$this->layers = BlockDataSerializer::readBoundedInt("layers", $stateMeta + 1, self::MIN_LAYERS, self::MAX_LAYERS);
}
public function getStateBitmask() : int{
@ -58,15 +62,15 @@ class SnowLayer extends Flowable implements Fallable{
/** @return $this */
public function setLayers(int $layers) : self{
if($layers < 1 || $layers > 8){
throw new \InvalidArgumentException("Layers must be in range 1-8");
if($layers < self::MIN_LAYERS || $layers > self::MAX_LAYERS){
throw new \InvalidArgumentException("Layers must be in range " . self::MIN_LAYERS . " ... " . self::MAX_LAYERS);
}
$this->layers = $layers;
return $this;
}
public function canBeReplaced() : bool{
return $this->layers < 8;
return $this->layers < self::MAX_LAYERS;
}
/**
@ -77,13 +81,20 @@ class SnowLayer extends Flowable implements Fallable{
return [AxisAlignedBB::one()->trim(Facing::UP, $this->layers >= 4 ? 0.5 : 1)];
}
public function getSupportType(int $facing) : SupportType{
if(!$this->canBeReplaced()){
return SupportType::FULL();
}
return SupportType::NONE();
}
private function canBeSupportedBy(Block $b) : bool{
return $b->isSolid() || ($b instanceof SnowLayer && $b->isSameType($this) && $b->layers === 8);
return $b->getSupportType(Facing::UP)->equals(SupportType::FULL());
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockReplace instanceof SnowLayer){
if($blockReplace->layers >= 8){
if($blockReplace->layers >= self::MAX_LAYERS){
return false;
}
$this->layers = $blockReplace->layers + 1;

View File

@ -26,7 +26,9 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\StairShape;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
@ -111,6 +113,19 @@ class Stair extends Transparent{
return $bbs;
}
public function getSupportType(int $facing) : SupportType{
if(
$facing === Facing::UP && $this->isUpsideDown() ||
$facing === Facing::DOWN && !$this->isUpsideDown() ||
($facing === $this->facing && !$this->shape->equals(StairShape::OUTER_LEFT()) && !$this->shape->equals(StairShape::OUTER_RIGHT())) ||
($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape->equals(StairShape::INNER_LEFT())) ||
($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape->equals(StairShape::INNER_RIGHT()))
){
return SupportType::FULL();
}
return SupportType::NONE();
}
private function getPossibleCornerFacing(bool $oppositeFacing) : ?int{
$side = $this->getSide($oppositeFacing ? Facing::opposite($this->facing) : $this->facing);
return (

View File

@ -35,7 +35,7 @@ abstract class Stem extends Crops{
public function onRandomTick() : void{
if(mt_rand(0, 2) === 1){
if($this->age < 7){
if($this->age < self::MAX_AGE){
$block = clone $this;
++$block->age;
$ev = new BlockGrowEvent($this, $block);

67
src/block/Stonecutter.php Normal file
View File

@ -0,0 +1,67 @@
<?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\inventory\StonecutterInventory;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
class Stonecutter extends Transparent{
use FacesOppositePlacingPlayerTrait;
use HorizontalFacingTrait;
public function writeStateToMeta() : int{
return BlockDataSerializer::writeHorizontalFacing($this->facing);
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::readHorizontalFacing($stateMeta);
}
public function getStateBitmask() : int{
return 0b111;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){
$player->setCurrentWindow(new StonecutterInventory($this->position));
}
return true;
}
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -33,6 +33,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Sugarcane extends Flowable{
public const MAX_AGE = 15;
protected int $age = 0;
@ -41,7 +42,7 @@ class Sugarcane extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 15);
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
}
public function getStateBitmask() : int{
@ -76,8 +77,8 @@ class Sugarcane extends Flowable{
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > 15){
throw new \InvalidArgumentException("Age must be in range 0-15");
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
@ -108,7 +109,7 @@ class Sugarcane extends Flowable{
public function onRandomTick() : void{
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
if($this->age === 15){
if($this->age === self::MAX_AGE){
$this->grow();
}else{
++$this->age;

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -82,4 +83,8 @@ class Thin extends Transparent{
return $bbs;
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
}

View File

@ -24,7 +24,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
@ -66,16 +68,16 @@ class Torch extends Flowable{
$below = $this->getSide(Facing::DOWN);
$face = Facing::opposite($this->facing);
if($this->getSide($face)->isTransparent() && !($face === Facing::DOWN && ($below->getId() === BlockLegacyIds::FENCE || $below->getId() === BlockLegacyIds::COBBLESTONE_WALL))){
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockClicked->canBeReplaced() && !$blockClicked->getSide(Facing::DOWN)->isTransparent()){
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){
$this->facing = Facing::UP;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($face !== Facing::DOWN && (!$blockClicked->isTransparent() || ($face === Facing::UP && ($blockClicked->getId() === BlockLegacyIds::FENCE || $blockClicked->getId() === BlockLegacyIds::COBBLESTONE_WALL)))){
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}else{
@ -87,7 +89,7 @@ class Torch extends Flowable{
Facing::DOWN
] as $side){
$block = $this->getSide($side);
if(!$block->isTransparent()){
if($this->canBeSupportedBy($block, Facing::opposite($side))){
$this->facing = Facing::opposite($side);
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
@ -95,4 +97,9 @@ class Torch extends Flowable{
}
return false;
}
private function canBeSupportedBy(Block $support, int $face) : bool{
return ($face === Facing::UP && $support->getSupportType($face)->hasCenterSupport()) ||
(Facing::axis($face) !== Axis::Y && $support->getSupportType($face)->equals(SupportType::FULL()));
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -78,6 +79,10 @@ class Trapdoor extends Transparent{
return [AxisAlignedBB::one()->trim($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)];
}
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($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -76,4 +78,8 @@ class Wall extends Transparent{
->trim(Facing::EAST, $east ? 0 : $inset)
];
}
public function getSupportType(int $facing) : SupportType{
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
}
}

View File

@ -113,7 +113,7 @@ final class WallCoralFan extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$axis = Facing::axis($face);
if(($axis !== Axis::X && $axis !== Axis::Z) || !$blockClicked->isSolid()){
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockClicked, $face)){
return false;
}
$this->facing = $face;
@ -122,10 +122,14 @@ final class WallCoralFan extends BaseCoral{
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->getSide(Facing::opposite($this->facing)))->isSolid()){
if(!$this->canBeSupportedBy($world->getBlock($this->position->getSide(Facing::opposite($this->facing))), $this->facing)){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
private function canBeSupportedBy(Block $block, int $face) : bool{
return $block->getSupportType($face)->hasCenterSupport();
}
}

View File

@ -30,7 +30,7 @@ use function mt_rand;
class Wheat extends Crops{
public function getDropsForCompatibleTool(Item $item) : array{
if($this->age >= 7){
if($this->age >= self::MAX_AGE){
return [
VanillaItems::WHEAT(),
VanillaItems::WHEAT_SEEDS()->setCount(mt_rand(0, 3))

View File

@ -33,14 +33,10 @@ use pocketmine\world\sound\Sound;
class DoubleChestInventory extends BaseInventory implements BlockInventory, InventoryHolder{
use AnimatedBlockInventoryTrait;
/** @var ChestInventory */
private $left;
/** @var ChestInventory */
private $right;
public function __construct(ChestInventory $left, ChestInventory $right){
$this->left = $left;
$this->right = $right;
public function __construct(
private ChestInventory $left,
private ChestInventory $right
){
$this->holder = $this->left->getHolder();
parent::__construct();
}

View File

@ -43,12 +43,12 @@ class EnderChestInventory extends DelegateInventory implements BlockInventory{
onClose as animatedBlockInventoryTrait_onClose;
}
private PlayerEnderInventory $inventory;
public function __construct(Position $holder, PlayerEnderInventory $inventory){
public function __construct(
Position $holder,
private PlayerEnderInventory $inventory
){
parent::__construct($inventory);
$this->holder = $holder;
$this->inventory = $inventory;
}
public function getEnderInventory() : PlayerEnderInventory{

View File

@ -35,11 +35,11 @@ class FurnaceInventory extends SimpleInventory implements BlockInventory{
public const SLOT_FUEL = 1;
public const SLOT_RESULT = 2;
private FurnaceType $furnaceType;
public function __construct(Position $holder, FurnaceType $furnaceType){
public function __construct(
Position $holder,
private FurnaceType $furnaceType
){
$this->holder = $holder;
$this->furnaceType = $furnaceType;
parent::__construct(3);
}

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockLegacyIds;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\item\ItemIds;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\world\Position;
@ -50,7 +50,7 @@ class ShulkerBoxInventory extends SimpleInventory implements BlockInventory{
}
public function canAddItem(Item $item) : bool{
if($item->getId() === BlockLegacyIds::UNDYED_SHULKER_BOX || $item->getId() === BlockLegacyIds::SHULKER_BOX){
if($item->getId() === ItemIds::UNDYED_SHULKER_BOX || $item->getId() === ItemIds::SHULKER_BOX){
return false;
}
return parent::canAddItem($item);

View File

@ -0,0 +1,39 @@
<?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\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
class StonecutterInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public const SLOT_INPUT = 0;
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(1);
}
}

View File

@ -44,14 +44,13 @@ class Banner extends Spawnable{
public const TAG_PATTERN_COLOR = "Color";
public const TAG_PATTERN_NAME = "Pattern";
/** @var DyeColor */
private $baseColor;
private DyeColor $baseColor;
/**
* @var BannerPatternLayer[]
* @phpstan-var list<BannerPatternLayer>
*/
private $patterns = [];
private array $patterns = [];
public function __construct(World $world, Vector3 $pos){
$this->baseColor = DyeColor::BLACK();

View File

@ -29,10 +29,8 @@ final class Beacon extends Spawnable{
private const TAG_PRIMARY = "primary"; //TAG_Int
private const TAG_SECONDARY = "secondary"; //TAG_Int
/** @var int */
private $primaryEffect = 0;
/** @var int */
private $secondaryEffect = 0;
private int $primaryEffect = 0;
private int $secondaryEffect = 0;
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setInt(self::TAG_PRIMARY, $this->primaryEffect);

View File

@ -32,8 +32,8 @@ use pocketmine\world\World;
class Bed extends Spawnable{
public const TAG_COLOR = "color";
/** @var DyeColor */
private $color;
private DyeColor $color;
public function __construct(World $world, Vector3 $pos){
$this->color = DyeColor::RED();

View File

@ -49,10 +49,8 @@ class Chest extends Spawnable implements Container, Nameable{
/** @var DoubleChestInventory|null */
protected $doubleInventory = null;
/** @var int|null */
private $pairX;
/** @var int|null */
private $pairZ;
private ?int $pairX = null;
private ?int $pairZ = null;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
@ -188,8 +186,8 @@ class Chest extends Spawnable implements Container, Nameable{
$this->createPair($tile);
$this->setDirty();
$tile->setDirty();
$this->clearSpawnCompoundCache();
$tile->clearSpawnCompoundCache();
$this->checkPairing();
return true;
@ -211,12 +209,12 @@ class Chest extends Spawnable implements Container, Nameable{
$tile = $this->getPair();
$this->pairX = $this->pairZ = null;
$this->setDirty();
$this->clearSpawnCompoundCache();
if($tile instanceof Chest){
$tile->pairX = $tile->pairZ = null;
$tile->checkPairing();
$tile->setDirty();
$tile->clearSpawnCompoundCache();
}
$this->checkPairing();

View File

@ -38,8 +38,7 @@ class FlowerPot extends Spawnable{
private const TAG_ITEM = "item";
private const TAG_ITEM_DATA = "mData";
/** @var Block|null */
private $plant = null;
private ?Block $plant = null;
public function readSaveData(CompoundTag $nbt) : void{
if(($itemIdTag = $nbt->getTag(self::TAG_ITEM)) instanceof ShortTag && ($itemMetaTag = $nbt->getTag(self::TAG_ITEM_DATA)) instanceof IntTag){

View File

@ -50,12 +50,9 @@ abstract class Furnace extends Spawnable implements Container, Nameable{
/** @var FurnaceInventory */
protected $inventory;
/** @var int */
private $remainingFuelTime = 0;
/** @var int */
private $cookTime = 0;
/** @var int */
private $maxFuelTime = 0;
private int $remainingFuelTime = 0;
private int $cookTime = 0;
private int $maxFuelTime = 0;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);

View File

@ -35,11 +35,8 @@ class Hopper extends Spawnable implements Container, Nameable{
private const TAG_TRANSFER_COOLDOWN = "TransferCooldown";
/** @var HopperInventory */
private $inventory;
/** @var int */
private $transferCooldown = 0;
private HopperInventory $inventory;
private int $transferCooldown = 0;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);

View File

@ -38,12 +38,9 @@ class ItemFrame extends Spawnable{
public const TAG_ITEM_DROP_CHANCE = "ItemDropChance";
public const TAG_ITEM = "Item";
/** @var Item */
private $item;
/** @var int */
private $itemRotation = 0;
/** @var float */
private $itemDropChance = 1.0;
private Item $item;
private int $itemRotation = 0;
private float $itemDropChance = 1.0;
public function __construct(World $world, Vector3 $pos){
$this->item = VanillaItems::AIR();

View File

@ -30,8 +30,7 @@ use pocketmine\nbt\tag\CompoundTag;
class Jukebox extends Spawnable{
private const TAG_RECORD = "RecordItem"; //Item CompoundTag
/** @var Record|null */
private $record = null;
private ?Record $record = null;
public function getRecord() : ?Record{
return $this->record;

View File

@ -56,43 +56,24 @@ class MonsterSpawner extends Spawnable{
public const DEFAULT_SPAWN_RANGE = 4; //blocks
public const DEFAULT_REQUIRED_PLAYER_RANGE = 16;
/**
* @var string
* TODO: replace this with a cached entity or something of that nature
*/
private $entityTypeId = ":";
/**
* @var ListTag|null
* TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun)
*/
private $spawnPotentials = null;
/**
* @var CompoundTag|null
* TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun)
*/
private $spawnData = null;
/** TODO: replace this with a cached entity or something of that nature */
private string $entityTypeId = ":";
/** TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun) */
private ?ListTag $spawnPotentials = null;
/** TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun) */
private ?CompoundTag $spawnData = null;
/** @var float */
private $displayEntityWidth = 1;
/** @var float */
private $displayEntityHeight = 1;
/** @var float */
private $displayEntityScale = 1;
private float $displayEntityWidth = 1.0;
private float $displayEntityHeight = 1.0;
private float $displayEntityScale = 1.0;
/** @var int */
private $spawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
/** @var int */
private $minSpawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
/** @var int */
private $maxSpawnDelay = self::DEFAULT_MAX_SPAWN_DELAY;
/** @var int */
private $spawnPerAttempt = 1;
/** @var int */
private $maxNearbyEntities = self::DEFAULT_MAX_NEARBY_ENTITIES;
/** @var int */
private $spawnRange = self::DEFAULT_SPAWN_RANGE;
/** @var int */
private $requiredPlayerRange = self::DEFAULT_REQUIRED_PLAYER_RANGE;
private int $spawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
private int $minSpawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
private int $maxSpawnDelay = self::DEFAULT_MAX_SPAWN_DELAY;
private int $spawnPerAttempt = 1;
private int $maxNearbyEntities = self::DEFAULT_MAX_NEARBY_ENTITIES;
private int $spawnRange = self::DEFAULT_SPAWN_RANGE;
private int $requiredPlayerRange = self::DEFAULT_REQUIRED_PLAYER_RANGE;
public function readSaveData(CompoundTag $nbt) : void{
if(($legacyIdTag = $nbt->getTag(self::TAG_LEGACY_ENTITY_TYPE_ID)) instanceof IntTag){

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