Compare commits

..

156 Commits
4.0.9 ... 4.2.0

Author SHA1 Message Date
44818e6d14 Release 4.2.0 2022-02-08 20:14:19 +00:00
325131dd30 Protocol changes for 1.18.10 2022-02-08 20:08:15 +00:00
1eb59fb9b5 4.1.1 is next 2022-02-07 19:22:54 +00:00
1c60aa9769 Release 4.1.0 2022-02-07 19:22:53 +00:00
e9dd9df0a0 Merge remote-tracking branch 'origin/stable' into staging/4.1 2022-02-07 17:22:18 +00:00
032b15efe0 fix BlockFactory consistency check 2022-02-07 15:49:32 +00:00
256826d9c7 Fixed uninitialized color fields of stained glass, stained clay and stained hardened glass 2022-02-07 15:39:54 +00:00
c273b29dec BambooSapling: fixed wrong bit being used for readiness
closes #4809
2022-02-07 15:38:05 +00:00
7ddd547190 Merge remote-tracking branch 'origin/stable' into staging/4.1 2022-02-06 23:55:52 +00:00
38e34093cf ResourcePackManifest: Stop throwing exceptions on extra properties (#4804)
This code currently throws errors when properties other than the base required ones are added. This can be from resource packs created by "bridge.", where the IDE adds a "generated_with" property to the manifest. This leads to resource packs created by bridge., which are otherwise completely valid, not being loaded.
2022-02-06 23:22:22 +00:00
dd1ebb5915 4.0.10 is next 2022-02-05 16:08:23 +00:00
d16b6fe61e Merge branch 'stable' into staging/4.1 2022-01-28 20:40:07 +00:00
40c7497efe Updated BedrockProtocol to 7.3.1 2022-01-28 15:06:56 +00:00
6ccb1ff114 4.1.0-BETA3 is next 2022-01-27 00:55:09 +00:00
1d2593208a Release 4.1.0-BETA2 2022-01-27 00:55:02 +00:00
a7bdef69e2 Merge remote-tracking branch 'origin/stable' into next-minor 2022-01-27 00:40:34 +00:00
d9ea647925 InGamePacketHandler: add a hack for swimming AABB client bug 2022-01-27 00:28:54 +00:00
6cbc14f2b2 World: fixed block update bug introduced by 3faeb5a556 2022-01-25 21:53:15 +00:00
75d0fc4749 BlockFactory: Make stone slab registration a little less unpleasant to read 2022-01-25 19:23:04 +00:00
ea161af4e5 Added FurnaceType->getCookSound() 2022-01-25 19:01:49 +00:00
0bf5f97fe9 Implement furnace sound (#4755)
closes #4363 

The following classes have been added:
- BlastFurnaceSound
- SmokerSound
- FurnaceSound
2022-01-25 18:52:31 +00:00
b9f1bcf0e4 Implement PlayerViewDistanceChangeEvent (#4749)
closes #4550
2022-01-25 18:00:26 +00:00
22a4117109 Merge remote-tracking branch 'origin/stable' into next-minor 2022-01-23 15:47:13 +00:00
e0da99a973 feat: Implement Brewing (#4413)
The following API constants have been added:
- tile\BrewingStand::BREW_TIME_TICKS
The following public API methods have been added:
- utils\BrewingStandSlot->getSlotNumber() : int
- CraftingManager->getPotionTypeRecipes() : array<string, array<string, PotionTypeRecipe>>
- CraftingManager->getPotionContainerChangeRecipes() : array<int, array<string, PotionContainerChangeRecipe>>
- CraftingManager->registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void
- CraftingManager->registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void
The following classes have been added:
- BrewingRecipe
- PotionTypeRecipe
- PotionContainerChangeRecipe
- BrewItemEvent
- BrewingFuelUseEvent
- PotionFinishBrewingSound
2022-01-22 16:54:58 +00:00
b2630a0920 Merge remote-tracking branch 'origin/stable' into next-minor 2022-01-22 14:46:55 +00:00
5ae20459dd 4.1.0-BETA2 is next 2022-01-22 02:23:09 +00:00
587da478a6 Release 4.1.0-BETA1 2022-01-22 02:23:06 +00:00
419bb9eba6 Player: fixed parameter name inconsistency 2022-01-22 01:33:31 +00:00
82f1c2766c Merge branch 'stable' into next-minor 2022-01-22 01:00:40 +00:00
fc53f3721a Avoid direct mutations of Entity->location 2022-01-20 21:49:14 +00:00
345ac75aac Remove PHPStan rules (no longer needed)
this is enforced by php-cs-fixer now instead.
2022-01-20 19:25:34 +00:00
32db27af78 php-cs-fixer: add logical_operators rule 2022-01-20 19:23:33 +00:00
4e956d5d1d Replace remaining disallowed operators 2022-01-20 19:23:19 +00:00
61f8144280 Replace disallowed operators in src/updater/ 2022-01-20 19:21:04 +00:00
ae03c70dfc Replace disallowed operators in src/permission/ 2022-01-20 19:20:51 +00:00
e986a0a4f2 Replace disallowed operators in src/inventory/ 2022-01-20 19:20:32 +00:00
b85fe0e72a Replace disallowed operators in src/scheduler/ 2022-01-20 19:20:03 +00:00
03f47d0a78 Replace disallowed operators in src/plugin/ 2022-01-20 19:19:20 +00:00
3f8f5cd200 Replace disallowed operators in src/crafting/ 2022-01-20 19:18:26 +00:00
aa6bd4438a Replace disallowed operators in src/event/ 2022-01-20 19:17:17 +00:00
22fb02c4e6 Replace disallowed operators in src/item/ 2022-01-20 19:16:50 +00:00
373880e582 Replace disallowed operators in src/player/ 2022-01-20 19:16:00 +00:00
8f525ab399 Replace disallowed operators in src/entity/ 2022-01-20 19:14:28 +00:00
be1996752a Replace disallowed operators in src/network/ 2022-01-20 19:11:32 +00:00
2bcb629d78 Scrub baseline 2022-01-20 19:08:44 +00:00
aae5962f6a Replace disallowed operators in src/world/ 2022-01-20 19:05:23 +00:00
282b430b1f Replace disallowed operators in src/utils/ 2022-01-20 19:02:26 +00:00
c47dfa1fb8 Replace disallowed operators in build/ 2022-01-20 19:00:54 +00:00
8db137882c Scrub baseline 2022-01-20 16:58:38 +00:00
79d1feff9c Replace disallowed operators in src/block/ 2022-01-20 16:57:09 +00:00
2f32bd877a Replace disallowed operators in src/command/ 2022-01-20 16:49:58 +00:00
22bc3bc3f9 Replace disallowed operators in src/console/ 2022-01-20 16:49:04 +00:00
6846f1e78a Replace disallowed operators in tests/ 2022-01-20 16:48:36 +00:00
4d55935bd8 Replace disallowed operators in tools/ 2022-01-20 16:48:06 +00:00
9c328690f8 Baseline for new rules (for now) 2022-01-20 16:46:41 +00:00
b60dd1e9b4 Ban 'and' and 'or' operators via PHPStan 2022-01-20 16:44:59 +00:00
86bcc49972 Merge branch 'stable' into next-minor 2022-01-20 16:30:49 +00:00
088745cf3b Implemented ChestPairEvent
closes #2829
2022-01-19 22:08:06 +00:00
8cdfef7861 Added missing sound for creating grass path and farmland
closes #2776
2022-01-19 21:49:05 +00:00
a0bb7059c1 Merge remote-tracking branch 'origin/stable' into next-minor 2022-01-19 19:48:51 +00:00
853ecd2408 InGamePacketHandler: fix function ordering 2022-01-16 16:16:42 +00:00
33421258b6 Silence MovePlayerPacket debug spam 2022-01-16 15:40:18 +00:00
c221484fc3 fixed CS 2022-01-15 22:27:06 +00:00
d9deb571ed Added LecternPlaceBookSound 2022-01-15 22:26:56 +00:00
42d07c74d7 added missing redstone power flag logic 2022-01-15 22:19:47 +00:00
1366c49f1f Implemented Lectern (#4708)
Co-authored-by: Covered123 <58715544+JavierLeon9966@users.noreply.github.com>
Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
2022-01-15 21:21:29 +00:00
ee6548aa50 Merge branch 'stable' into next-minor 2022-01-14 00:45:49 +00:00
a5c0958adf Filesystem::safeFilePutContents() now consistently throws RuntimeException in all expected failure cases
unexpected cases may still throw ErrorException (such as undefined variables) but we don't want to capture those.
2022-01-09 16:33:31 +00:00
fd880d8465 Filesystem: Use ErrorToExceptionHandler to improve error output 2022-01-09 16:26:42 +00:00
a323fb7bb5 Updated pocketmine/errorhandler to 0.6.0 2022-01-09 16:22:59 +00:00
1948b00008 Merge branch 'stable' into next-minor 2022-01-07 21:51:05 +00:00
dbbbc4f9c9 updated phpstan baseline 2022-01-07 21:39:19 +00:00
51f2a78dcf World: break random tick blocks initializing out of constructor
and fix a variable clobber by foreach as a side effect
2022-01-07 21:36:49 +00:00
5128bc02bb Reduce code duplication between BaseCoral and CoralBlock 2022-01-07 21:32:44 +00:00
4f4aa62479 ConcretePowder: call BlockFormEvent when coming in contact with water 2022-01-07 21:19:08 +00:00
c267e7b3c2 Call BlockMeltEvent when frosted ice melts 2022-01-07 21:15:05 +00:00
3faeb5a556 disable-block-ticking directive now supports names a la /give 2022-01-07 21:06:06 +00:00
0bc578b8fc Block: added getTypeId() 2022-01-07 21:03:19 +00:00
661848c5e7 fix more EOF newlines 2022-01-07 20:39:43 +00:00
75fc7a2d1f Merge branch 'stable' into next-minor 2022-01-07 20:16:35 +00:00
4f8a0bad25 RegistryTrait: avoid overwriting parameter variables 2022-01-06 23:54:54 +00:00
fb29653ed7 Merge remote-tracking branch 'origin/stable' into next-minor 2022-01-06 22:43:57 +00:00
ffa8cf3ec3 Update to BedrockProtocol 7.3.0 2022-01-06 22:42:16 +00:00
8532e9c8e0 Merge remote-tracking branch 'origin/stable' into next-minor 2022-01-04 14:38:15 +00:00
e04dfe96af Merge branch 'stable' into next-minor 2022-01-01 17:55:17 +00:00
193a1b3f4e TextFormat: Added MINECOIN_GOLD (§g) color code support (#4670) 2021-12-30 23:53:05 +00:00
62afa2f28d Entity: extract getBlocksIntersected() from getBlocksAroundWithEntityInsideActions() 2021-12-29 23:04:54 +00:00
207f7ec309 Player: avoid unnecessary network updates on repeated calls to setAllowFLight(), setHasBlockCollision() and setAutoJump() 2021-12-29 20:22:16 +00:00
e0a6bc1d4a Lava: remove useless code, closes #4678 2021-12-29 20:13:07 +00:00
5c994e4a24 Player: removed an old hack for setFlying() feedback loop
this is no longer a concern, since we now check if the sent state matches the current state before doing anything, at multiple layers.
2021-12-29 18:41:11 +00:00
d94578a420 Player: remove dead TODO comment 2021-12-29 18:32:53 +00:00
0a0de018a5 InGamePacketHandler: fixed player jump handling 2021-12-29 18:28:22 +00:00
a1d217e12b InGamePacketHandler: fixed missing synchronization of metadata when plugins cancel PlayerToggle*Event 2021-12-29 18:23:05 +00:00
e102339637 InGamePacketHandler: remove dead code from PlayerActionPacket handling 2021-12-29 17:29:19 +00:00
7124d44b92 Player: prevent PlayerToggle(Sprint|Sneak|Fly|Glide|Swim)Events from firing multiple times with the same value
this happens with swimming due to bugs in the client.
2021-12-29 17:24:49 +00:00
767dfd9947 Merge branch 'stable' into next-minor 2021-12-27 21:55:13 +00:00
87170ab067 Player: move reach distances to constants 2021-12-27 17:32:04 +00:00
74ac0f5862 Player: move max chat length to constant 2021-12-27 17:06:19 +00:00
f5144d49b1 Merge branch 'stable' into next-minor 2021-12-27 16:52:22 +00:00
95b6cb21f2 Implement BlockMeltEvent (#4666) 2021-12-27 16:36:59 +00:00
c858c0dc79 Merge remote-tracking branch 'origin/stable' into next-minor 2021-12-27 16:09:20 +00:00
18e26d975b Fixed swimming and gliding for PlayerAuthInputPacket 2021-12-19 17:31:47 +00:00
d41f933e7b Implement swimming/gliding including AABB recalculation (#4446)
- The following events have been added:
  - PlayerToggleGlideEvent
  - PlayerToggleSwimEvent
- The following API methods have been added:
  - Entity->getSize()
  - Living->isSwimming()
  - Living->setSwimming()
  - Living->isGliding()
  - Living->setSwimming()
  - Player->toggleSwim()
  - Player->toggleGlide()
2021-12-19 17:10:41 +00:00
65dabefa3b Config: improve config loading and parsing error handling
closes #4654
closes #3454
2021-12-19 16:53:29 +00:00
44e8603a6d InGamePacketHandler: fixed borked sneak/sprint after switch to PlayerAuthInputPacket
closes #4659
2021-12-19 00:52:53 +00:00
16fd5456aa Merge branch 'stable' into next-minor 2021-12-18 00:39:58 +00:00
089f22d903 Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2021-12-16 23:39:14 +00:00
fc3a6c6984 Implemented fire spread (#4617) 2021-12-16 23:36:34 +00:00
1ab285f573 PrepareEncryptionTask: remove usage of no-op function 2021-12-16 18:47:50 +00:00
aa56c66a3c ProcessLoginTask: drop usage of no-op method
this is no longer useful since 8.0.
2021-12-16 18:46:34 +00:00
920462bdcc Merge branch 'stable' into next-minor 2021-12-16 01:46:52 +00:00
482bc462d3 VersionString: Use multiplication instead of bitshift for version IDs
this makes them more recognizable, and also fixes #4630.

This is technically a BC break (behavioural change), but since nothing appears to use this functionality anyway except PM itself, I don't think it matters.
2021-12-15 14:32:50 +00:00
de82424fb2 XpManager: add APIs to prevent owning Human from attracting XP orbs (#4623)
Fixes #4589

The following API methods are added:

- `XpManager->canAttractXpOrbs()`
- `XpManager->setCanAttractXpOrbs()`

Possible future scope: flip this on its head to allow spectator players to attract XP orbs, in case someone wants that for some reason ???

Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
2021-12-15 04:40:46 +00:00
d487e43766 InGamePacketHandler: fixed block breaking borked by enabling PlayerAuthInputPacket 2021-12-15 04:01:40 +00:00
6494375a53 SetupWizard: ask for max view distance 2021-12-15 03:15:04 +00:00
4466166f8b Merge branch 'stable' into next-minor 2021-12-15 03:12:41 +00:00
3aa34b59a5 Ask for IPv6 port in setup wizard 2021-12-15 02:22:04 +00:00
c04b00d09d Updated Language to 2.2.0 2021-12-15 02:15:24 +00:00
6e67c7532a Bump default max render radius to 16 chunks
It's 2021, this is making PM look bad to new users (as if we need something else to make PM look bad...)
2021-12-15 01:46:50 +00:00
5f8ebd81d7 it's MAX view distance, not fixed 2021-12-15 01:42:29 +00:00
79b5109953 Move some configuration constants to .. well .. constants 2021-12-15 01:40:29 +00:00
60938c8c9d Random: fixed nextSignedInt() not actually returning signed ints
closes #4646
closes #4645

Impact assessment by core usage search and poggit suggests that the impact of this change will be close to zero.
However, since it changes behaviour which plugins might be unknowingly relying on, it's going into 4.1 rather than a patch release.
2021-12-15 00:59:10 +00:00
49a8afd126 Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2021-12-14 23:16:40 +00:00
dbad5dd611 Merge branch 'stable' into next-minor 2021-12-14 23:16:01 +00:00
a41404bd8a Allow gamemode strings for gamemode property in server.properties (#4638)
closes #2692
2021-12-14 22:56:22 +00:00
4b06fe73f2 Merge branch 'stable' into next-minor 2021-12-14 22:54:39 +00:00
1c6907c636 Merge branch 'stable' into next-minor 2021-12-14 01:27:21 +00:00
7184c02bb6 Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2021-12-14 00:35:04 +00:00
8a94aa10a4 Merge branch 'stable' into next-minor 2021-12-14 00:34:54 +00:00
9e75c1463a Implement carving pumpkin (#4637) 2021-12-10 19:45:15 +00:00
a94b88424e Merge branch 'stable' into next-minor 2021-12-10 19:15:57 +00:00
7ad1afee89 Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2021-12-09 13:54:06 +00:00
292827a311 Switch to PlayerAuthInputPacket for movement handling
sticking with the non-rewind version for now, for simplicity's sake.

We do want the rewind version at some point for server side knockback, but that's a job for later.

For now, using this packet fixes various problems with slightly-incorrect positions and rotations (e.g. AimTP no longer requires you to jump to get the exact correct rotation; previously it would hit the wrong block at long distances due to errors of a fraction of a degree due to the client not sending its position.

Note that this might cause some performance degradation since the packet is sent every tick. This has yet to be assessed, but the advantages offered are undeniable in any case.
2021-12-09 13:53:53 +00:00
f8ed23cc1e ClearCommand: Add OffHandInventory to $inventories (#4631) 2021-12-09 11:19:33 +00:00
922ce2e312 Merge branch 'stable' into next-minor 2021-12-08 20:10:23 +00:00
7a385ddc8b simulate-chunk-selector: remove unused colour allocation 2021-12-08 20:04:03 +00:00
2254f31bec Use Utils::assumeNotFalse() in tools/ 2021-12-08 20:01:19 +00:00
77a74d84e2 CrashDump: phpversion() could return false for a loaded extension
if the extension wrote NULL into the zend_module_entry->version field, phpversion() will return false.
2021-12-08 19:58:28 +00:00
5b868e6d5e Merge branch 'stable' into next-minor 2021-12-08 19:40:25 +00:00
889d048ca3 Make use of Utils::assumeNotFalse() in a bunch of places
I've stuck to only doing this in the places where I'm sure we should never get false back. Other places I'm less sure of (and I found more bugs along the way).
2021-12-08 19:39:04 +00:00
8b73549355 Use JSON_THROW_ON_ERROR for json_encode() and json_decode() 2021-12-08 19:14:07 +00:00
c6466a6da9 Utils: added crutch assumeNotFalse()
this can be used to get PHPStan to shut up about stuff that will never return false in normal contexts.
It's more fine-grained than @phpstan-ignore-line and less hassle than ignoreErrors (and works in PhpStorm too).
In addition, it's easy to search for references.
2021-12-08 18:58:39 +00:00
45b4fa0e96 Server: improve confusing condition in crashDump() 2021-12-07 23:08:06 +00:00
bf29409a45 Server: fixed PHPStan level 7 error in crashDump() 2021-12-07 23:06:10 +00:00
503c888838 bootstrap: use phpversion() for checking extension presence
fixes 2 PHPStan errors on level 7
2021-12-07 22:50:16 +00:00
1d14c8cb6b Merge branch 'stable' into next-minor 2021-12-07 00:41:48 +00:00
49d0d01f9f Merge branch 'next-minor' of github.com:pmmp/PocketMine-MP into next-minor 2021-12-07 00:41:17 +00:00
ed4978c31b Added VanillaItems::AIR()
we don't usually add VanillaItems entries for blocks since they already exist in VanillaBlocks, but air has a special use case specifically as an itemstack, so we make an exception for this case.
2021-12-07 00:41:07 +00:00
3728ddbf24 ClearCommand: Cleanup logic & fix vanilla disparities (#4619) 2021-12-06 23:57:07 +00:00
8f2ca92f02 Implement dropped item merging (#4419)
- The following classes have been added:
  - `ItemMergeEvent`
  - `ItemEntityStackSizeChangeAnimation`
- The following API methods have been added:
  - `ItemEntity->isMergeable()`
  - `ItemEntity->tryMergeInto()`
  - `ItemEntity->setStackSize()`
2021-12-06 22:23:18 +00:00
ce54d268f2 Player: allow controlling client-sided block collisions irrespective of Spectator Mode (#4563)
- Added the following API methods:
  - `Player::hasBlockCollision()`
  - `Player::setHasBlockCollision()`

This enables spectator-like noclip behaviour in other gamemodes (could be useful for builders).
2021-12-06 21:14:22 +00:00
cd850b111d SplashPotion: added getType() (#4613) 2021-12-06 20:29:01 +00:00
2486dabd8a Fire: extract more unrelated changes from #4617 2021-12-06 01:04:59 +00:00
4f3a60ac90 Merge branch 'stable' into next-minor 2021-12-05 16:07:23 +00:00
98c31cf07b Update version number 2021-12-05 16:07:20 +00:00
9256afd439 Call BlockSpreadEvent when spreading fire to incinerated blocks 2021-12-05 16:06:29 +00:00
295 changed files with 3497 additions and 1301 deletions

View File

@ -43,6 +43,7 @@ return (new PhpCsFixer\Config)
'import_classes' => null,
],
'indentation_type' => true,
'logical_operators' => true,
'native_function_invocation' => [
'scope' => 'namespaced',
'include' => ['@all'],

View File

@ -40,4 +40,4 @@ echo json_encode([
"details_url" => "https://github.com/$argv[3]/releases/tag/$argv[2]",
"download_url" => "https://github.com/$argv[3]/releases/download/$argv[2]/PocketMine-MP.phar",
"source_url" => "https://github.com/$argv[3]/tree/$argv[2]",
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";

View File

@ -58,7 +58,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
$memberLines = [];
foreach($members as $name => $member){
$reflect = new \ReflectionClass($member);
while($reflect !== false and $reflect->isAnonymous()){
while($reflect !== false && $reflect->isAnonymous()){
$reflect = $reflect->getParentClass();
}
if($reflect === false){

View File

@ -50,7 +50,7 @@ use const STR_PAD_LEFT;
require_once dirname(__DIR__) . '/vendor/autoload.php';
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
$versionInfo = file_get_contents($versionInfoPath);
$versionInfo = Utils::assumeNotFalse(file_get_contents($versionInfoPath), $versionInfoPath . " should always exist");
$versionInfo = preg_replace(
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
'$1const BASE_VERSION = "' . $newVersion . '";',

166
changelogs/4.1-beta.md Normal file
View File

@ -0,0 +1,166 @@
**For Minecraft: Bedrock Edition 1.18.0**
### 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.1.0-BETA1
Released 22nd January 2022.
## General
- Game mode names (e.g. `survival`, `creative`) may now be used for the `gamemode` property in `server.properties`.
- Increased default maximum render distance to 16 chunks. Players with a render distance smaller than this will notice no difference.
- The setup wizard now prompts for a maximum render distance value.
- The setup wizard now prompts for an IPv6 port selection. Previously it would always use 19133.
- `chunk-ticking.disable-block-ticking` now accepts block names like those used in the `/give` command.
- The `/clear` command now behaves more like vanilla:
- The order of inventories is now the same as Bedrock.
- The cursor and offhand inventories are now cleared if necessary.
## Technical
- `PlayerAuthInputPacket` is now used instead of `MovePlayerPacket` for processing movements. This improves position and rotation accuracy.
- `&&` and `||` are now always used instead of `and` and `or`.
- New version of `pocketmine/errorhandler` is used by this version, adding support for `ErrorToExceptionHandler::trap()`. This enables reliably capturing `E_WARNING` and `E_NOTICE` from functions such as `yaml_parse()` and friends.
- New dependency versions are required by this version:
- `pocketmine/bedrock-protocol` has been updated from 7.1.0 to [7.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/7.3.0%2Bbedrock-1.18.0).
- `pocketmine/errorhandler` has been updated from 0.3.0 to [0.6.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.6.0).
## API
### Block
- The following classes have been added:
- `Lectern`
- `Pumpkin`
- The following public API methods have been added:
- `Block->getTypeId() : int` - returns an integer which uniquely identifies the block type, ignoring things like facing, colour etc.
- `VanillaBlocks::LECTERN()`
### Entity
- The following classes have been added:
- `animation\ItemEntityStackSizeChangeAnimation`
- The following public API methods have been added:
- `object\ItemEntity->isMergeable(object\ItemEntity $other) : bool`
- `object\ItemEntity->setStackSize(int $size) : void`
- `object\ItemEntity->tryMergeInto(object\ItemEntity $other) : bool`
- `ExperienceManager->canAttractXpOrbs() : bool`
- `ExperienceManager->setCanAttractXpOrbs(bool $v = true) : void`
- `Entity->getSize() : EntitySizeInfo`
- `Living->isGliding() : bool`
- `Living->isSwimming() : bool`
- `Living->setGliding(bool $value = true) : void`
- `Living->setSwimming(bool $value = true) : void`
- The following protected API methods have been added:
- `Entity->getBlocksIntersected(float $inset) : \Generator<int, Block, void, void>`
### Event
- `BlockSpreadEvent` is now called when fire spreads to the positions of blocks it burns away.
- `BlockFormEvent` is now called when concrete powder turns into concrete due to contact with water.
- The following classes have been added:
- `BlockMeltEvent` - called when ice or snow melts
- `ChestPairEvent` - called when two chests try to form a pair
- `PlayerToggleGlideEvent` - called when a player starts or stops gliding
- `PlayerToggleSwimEvent` - called when a player starts or stops swimming
### Item
- The following public API methods have been added:
- `SplashPotion->getType() : PotionType`
- `VanillaItems::AIR()`
- The following API methods have been deprecated:
- `ItemFactory::air()` - use `VanillaItems::AIR()` instead
### Player
- The following public API methods have been added:
- `Player->hasBlockCollision() : bool`
- `Player->setHasBlockCollision(bool $value)` - allows controlling spectator-like no-clip behaviour without changing game mode
- `Player->toggleSwim(bool $swim) : bool` - called by the network system when the client tries to start/stop swimming
- `Player->toggleGlide(bool $glide) : bool` - called by the network system when the client tries to start/stop gliding
### Server
- The following public API constants have been added:
- `Server::DEFAULT_SERVER_NAME`
- `Server::DEFAULT_MAX_PLAYERS`
- `Server::DEFAULT_PORT_IPV4`
- `Server::DEFAULT_PORT_IPV6`
- `Server::DEFAULT_MAX_VIEW_DISTANCE`
### Utils
- Config parsing errors are now always represented by `ConfigLoadException` and include the path to the file in the message.
- Added `TextFormat::MINECOIN_GOLD`, and support for it to the various `TextFormat` methods.
- The following public API methods have been added:
- `Utils::assumeNotFalse()` - static analysis crutch to silence PHPStan errors without using `ignoreErrors` or `@phpstan-ignore-line`, which are both too coarse.
- The following public API properties have been added:
- `Terminal::$COLOR_MINECOIN_GOLD`
- The following classes have been added:
- `ConfigLoadException`
- Fixed `Random->nextSignedInt()` to actually return a signed int. Previously it would return any integer value between 0 and 4,294,957,295.
- Fixed `Random->nextSignedFloat()` to return a float between `-1.0` and `1.0`. Previously it would return any value between `0.0` and `2.0`.
- `VersionString->getNumber()` output is now structured differently to fix overflow issues caused by the old format.
### World
- The following classes have been added:
- `sound\ItemUseOnBlockSound`
- `sound\LecternPlaceBookSound`
## Gameplay
### Blocks
- Fire now spreads.
- Implemented lectern blocks.
- Added missing sounds for hoeing grass and dirt.
- Added missing sounds for using a shovel on grass to create grass path.
- Pumpkins can now be carved using shears.
### Items
- Dropped items of the same type now merge with each other.
### Misc
- Implemented player swimming.
# 4.1.0-BETA2
Released 27th January 2022.
## API
### Block
- The following API methods have been added:
- `utils\BrewingStandSlot->getSlotNumber() : int`
- `utils\FurnaceType->getCookSound() : Sound`
- The following API constants have been added:
- `tile\BrewingStand::BREW_TIME_TICKS`
### Crafting
- The following API methods have been added:
- `CraftingManager->getPotionContainerChangeRecipes() : array<int, array<string, PotionContainerChangeRecipe>>`
- `CraftingManager->getPotionTypeRecipes() : array<string, array<string, PotionTypeRecipe>>`
- `CraftingManager->registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void`
- `CraftingManager->registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void`
- The following classes have been added:
- `BrewingRecipe`
- `PotionContainerChangeRecipe`
- `PotionTypeRecipe`
### Event
- The following classes have been added:
- `BrewItemEvent` - called when a brewing stand finishes brewing potions; this is called up to 3 times (once for each brewing slot, as needed)
- `BrewingFuelUseEvent` - called when a brewing stand consumes blaze powder
- `PlayerViewDistanceChangeEvent` - called whenever a player alters their render distance or requests one for the first time when connecting
### World
#### Sound
- The following classes have been added:
- `BlastFurnaceSound` - the sound made by a blast furnace during smelting
- `FurnaceSound` - the sound made by a regular furnace during cooking or smelting
- `PotionFinishBrewingSound` - the sound made by a brewing stand when a potion finishes being brewed
- `SmokerSound` - the sound made by a smoker during cooking
## Gameplay
### Blocks
- Brewing stands can now be used for brewing potions.
- The visual appearance of a brewing stand now updates correctly when the contents of its inventory changes (adding/removing potions).
- Added missing sounds for furnace, blast furnace and smoker.
- Fixed ender chest not dropping itself when mined with a Silk Touch pickaxe.
- Cobwebs now drop themselves when mined using shears.
- The correct amount of fall damage is now taken when falling from a height onto hay bales.
- Fixed block updating bug introduced by beta1 which caused crops and other plants to never grow.
### Misc
- Added a workaround for client hitbox size bug after swimming which caused the player to be able to fit into one-block-tall gaps.

142
changelogs/4.1.md Normal file
View File

@ -0,0 +1,142 @@
**For Minecraft: Bedrock Edition 1.18.0**
### 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.1.0
Released 7th February 2022.
## General
- Game mode names (e.g. `survival`, `creative`) may now be used for the `gamemode` property in `server.properties`.
- Increased default maximum render distance to 16 chunks. Players with a render distance smaller than this will notice no difference.
- The setup wizard now prompts for a maximum render distance value.
- The setup wizard now prompts for an IPv6 port selection. Previously it would always use 19133.
- `chunk-ticking.disable-block-ticking` now accepts block names like those used in the `/give` command.
- The `/clear` command now behaves more like vanilla:
- The order of inventories is now the same as Bedrock.
- The cursor and offhand inventories are now cleared if necessary.
## Technical
- `PlayerAuthInputPacket` is now used instead of `MovePlayerPacket` for processing movements. This improves position and rotation accuracy.
- `&&` and `||` are now always used instead of `and` and `or`.
- New version of `pocketmine/errorhandler` is used by this version, adding support for `ErrorToExceptionHandler::trap()`. This enables reliably capturing `E_WARNING` and `E_NOTICE` from functions such as `yaml_parse()` and friends.
- New dependency versions are required by this version:
- `pocketmine/bedrock-protocol` has been updated from 7.1.0 to [7.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/7.3.0%2Bbedrock-1.18.0).
- `pocketmine/errorhandler` has been updated from 0.3.0 to [0.6.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.6.0).
## API
### Block
- The following classes have been added:
- `Lectern`
- `Pumpkin`
- The following public API methods have been added:
- `Block->getTypeId() : int` - returns an integer which uniquely identifies the block type, ignoring things like facing, colour etc.
- `VanillaBlocks::LECTERN()`
- `utils\BrewingStandSlot->getSlotNumber() : int`
- `utils\FurnaceType->getCookSound() : Sound`
- The following API constants have been added:
- `tile\BrewingStand::BREW_TIME_TICKS`
### Crafting
- The following API methods have been added:
- `CraftingManager->getPotionContainerChangeRecipes() : array<int, array<string, PotionContainerChangeRecipe>>`
- `CraftingManager->getPotionTypeRecipes() : array<string, array<string, PotionTypeRecipe>>`
- `CraftingManager->registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void`
- `CraftingManager->registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void`
- The following classes have been added:
- `BrewingRecipe`
- `PotionContainerChangeRecipe`
- `PotionTypeRecipe`
### Entity
- The following classes have been added:
- `animation\ItemEntityStackSizeChangeAnimation`
- The following public API methods have been added:
- `object\ItemEntity->isMergeable(object\ItemEntity $other) : bool`
- `object\ItemEntity->setStackSize(int $size) : void`
- `object\ItemEntity->tryMergeInto(object\ItemEntity $other) : bool`
- `ExperienceManager->canAttractXpOrbs() : bool`
- `ExperienceManager->setCanAttractXpOrbs(bool $v = true) : void`
- `Entity->getSize() : EntitySizeInfo`
- `Living->isGliding() : bool`
- `Living->isSwimming() : bool`
- `Living->setGliding(bool $value = true) : void`
- `Living->setSwimming(bool $value = true) : void`
- The following protected API methods have been added:
- `Entity->getBlocksIntersected(float $inset) : \Generator<int, Block, void, void>`
### Event
- `BlockSpreadEvent` is now called when fire spreads to the positions of blocks it burns away.
- `BlockFormEvent` is now called when concrete powder turns into concrete due to contact with water.
- The following classes have been added:
- `BlockMeltEvent` - called when ice or snow melts
- `BrewItemEvent` - called when a brewing stand finishes brewing potions; this is called up to 3 times (once for each brewing slot, as needed)
- `BrewingFuelUseEvent` - called when a brewing stand consumes blaze powder
- `ChestPairEvent` - called when two chests try to form a pair
- `PlayerToggleGlideEvent` - called when a player starts or stops gliding
- `PlayerToggleSwimEvent` - called when a player starts or stops swimming
- `PlayerViewDistanceChangeEvent` - called whenever a player alters their render distance or requests one for the first time when connecting
### Item
- The following public API methods have been added:
- `SplashPotion->getType() : PotionType`
- `VanillaItems::AIR()`
- The following API methods have been deprecated:
- `ItemFactory::air()` - use `VanillaItems::AIR()` instead
### Player
- The following public API methods have been added:
- `Player->hasBlockCollision() : bool`
- `Player->setHasBlockCollision(bool $value)` - allows controlling spectator-like no-clip behaviour without changing game mode
- `Player->toggleSwim(bool $swim) : bool` - called by the network system when the client tries to start/stop swimming
- `Player->toggleGlide(bool $glide) : bool` - called by the network system when the client tries to start/stop gliding
### Server
- The following public API constants have been added:
- `Server::DEFAULT_SERVER_NAME`
- `Server::DEFAULT_MAX_PLAYERS`
- `Server::DEFAULT_PORT_IPV4`
- `Server::DEFAULT_PORT_IPV6`
- `Server::DEFAULT_MAX_VIEW_DISTANCE`
### Utils
- Config parsing errors are now always represented by `ConfigLoadException` and include the path to the file in the message.
- Added `TextFormat::MINECOIN_GOLD`, and support for it to the various `TextFormat` methods.
- The following public API methods have been added:
- `Utils::assumeNotFalse()` - static analysis crutch to silence PHPStan errors without using `ignoreErrors` or `@phpstan-ignore-line`, which are both too coarse.
- The following public API properties have been added:
- `Terminal::$COLOR_MINECOIN_GOLD`
- The following classes have been added:
- `ConfigLoadException`
- Fixed `Random->nextSignedInt()` to actually return a signed int. Previously it would return any integer value between 0 and 4,294,957,295.
- Fixed `Random->nextSignedFloat()` to return a float between `-1.0` and `1.0`. Previously it would return any value between `0.0` and `2.0`.
- `VersionString->getNumber()` output is now structured differently to fix overflow issues caused by the old format.
### World
- The following classes have been added:
- `sound\BlastFurnaceSound` - the sound made by a blast furnace during smelting
- `sound\FurnaceSound` - the sound made by a regular furnace during cooking or smelting
- `sound\ItemUseOnBlockSound`
- `sound\LecternPlaceBookSound`
- `sound\PotionFinishBrewingSound` - the sound made by a brewing stand when a potion finishes being brewed
- `sound\SmokerSound` - the sound made by a smoker during cooking
## Gameplay
### Blocks
- Fire now spreads.
- Implemented lectern blocks.
- Added missing sounds for hoeing grass and dirt.
- Added missing sounds for using a shovel on grass to create grass path.
- Pumpkins can now be carved using shears.
- Brewing stands can now be used for brewing potions.
- The visual appearance of a brewing stand now updates correctly when the contents of its inventory changes (adding/removing potions).
- Added missing sounds for furnace, blast furnace and smoker.
- Fixed ender chest not dropping itself when mined with a Silk Touch pickaxe.
- Cobwebs now drop themselves when mined using shears.
- The correct amount of fall damage is now taken when falling from a height onto hay bales.
### Items
- Dropped items of the same type now merge with each other.

10
changelogs/4.2.md Normal file
View File

@ -0,0 +1,10 @@
**For Minecraft: Bedrock Edition 1.18.10**
### 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.2.0
- Added support for Minecraft: Bedrock Edition 1.18.10.

View File

@ -34,13 +34,13 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.5.0+bedrock-1.18.0",
"pocketmine/bedrock-protocol": "~7.1.0+bedrock-1.18.0",
"pocketmine/bedrock-data": "~1.6.0+bedrock-1.18.10",
"pocketmine/bedrock-protocol": "~8.0.0+bedrock-1.18.10",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0",
"pocketmine/errorhandler": "^0.3.0",
"pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.4.2",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",

47
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": "aabbf0c3280f590f48d2edb622747341",
"content-hash": "33a1d10231e0bce92d724bb738b14359",
"packages": [
{
"name": "adhocore/json-comment",
@ -249,16 +249,16 @@
},
{
"name": "pocketmine/bedrock-data",
"version": "1.5.0+bedrock-1.18.0",
"version": "1.6.0+bedrock-1.18.10",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c"
"reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c",
"reference": "482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e98c511584a7bd58a95986374d2df4b04c6a2ba0",
"reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0",
"shasum": ""
},
"type": "library",
@ -269,22 +269,22 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.0"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.10"
},
"time": "2021-11-30T18:30:46+00:00"
"time": "2022-02-08T19:13:47+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "7.1.0+bedrock-1.18.0",
"version": "8.0.0+bedrock-1.18.10",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "42f2a00634c17c346fd98c05b2daf29d1fbf5805"
"reference": "017e57c8f8a74118bcbba7332e7ebac9e6c81693"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/42f2a00634c17c346fd98c05b2daf29d1fbf5805",
"reference": "42f2a00634c17c346fd98c05b2daf29d1fbf5805",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/017e57c8f8a74118bcbba7332e7ebac9e6c81693",
"reference": "017e57c8f8a74118bcbba7332e7ebac9e6c81693",
"shasum": ""
},
"require": {
@ -298,7 +298,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.2.0",
"phpstan/phpstan": "1.4.5",
"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/7.1.0+bedrock-1.18.0"
"source": "https://github.com/pmmp/BedrockProtocol/tree/8.0.0+bedrock-1.18.10"
},
"time": "2021-12-15T03:07:05+00:00"
"time": "2022-02-08T19:17:56+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -497,24 +497,25 @@
},
{
"name": "pocketmine/errorhandler",
"version": "0.3.0",
"version": "0.6.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/ErrorHandler.git",
"reference": "ec742b209e8056bbe855069c4eff94c9734ea19b"
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/ec742b209e8056bbe855069c4eff94c9734ea19b",
"reference": "ec742b209e8056bbe855069c4eff94c9734ea19b",
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/dae214a04348b911e8219ebf125ff1c5589cc878",
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
"php": "^8.0"
},
"require-dev": {
"phpstan/phpstan": "0.12.75",
"phpstan/phpstan-strict-rules": "^0.12.2"
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
@ -529,9 +530,9 @@
"description": "Utilities to handle nasty PHP E_* errors in a usable way",
"support": {
"issues": "https://github.com/pmmp/ErrorHandler/issues",
"source": "https://github.com/pmmp/ErrorHandler/tree/0.3.0"
"source": "https://github.com/pmmp/ErrorHandler/tree/0.6.0"
},
"time": "2021-02-12T18:56:22+00:00"
"time": "2022-01-08T21:05:46+00:00"
},
{
"name": "pocketmine/locale-data",

View File

@ -128,7 +128,9 @@ chunk-ticking:
blocks-per-subchunk-per-tick: 3
#IDs of blocks not to perform random ticking on.
disable-block-ticking:
#- 2 # grass
#- grass
#- ice
#- fire
chunk-generation:
#Max. amount of chunks in the waiting queue to be populated

View File

@ -28,7 +28,6 @@ use pocketmine\network\mcpe\cache\ChunkCache;
use pocketmine\scheduler\DumpWorkerMemoryTask;
use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Process;
use pocketmine\utils\Utils;
use Webmozart\PathUtil\Path;
@ -65,6 +64,7 @@ use function sprintf;
use function strlen;
use function substr;
use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;
use const SORT_NUMERIC;
@ -168,14 +168,14 @@ class MemoryManager{
}
public function canUseChunkCache() : bool{
return !$this->lowMemory or !$this->lowMemDisableChunkCache;
return !$this->lowMemory || !$this->lowMemDisableChunkCache;
}
/**
* Returns the allowed chunk radius based on the current memory usage.
*/
public function getViewDistance(int $distance) : int{
return ($this->lowMemory and $this->lowMemChunkRadiusOverride > 0) ? min($this->lowMemChunkRadiusOverride, $distance) : $distance;
return ($this->lowMemory && $this->lowMemChunkRadiusOverride > 0) ? min($this->lowMemChunkRadiusOverride, $distance) : $distance;
}
/**
@ -214,18 +214,18 @@ class MemoryManager{
public function check() : void{
Timings::$memoryManager->startTiming();
if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){
if(($this->memoryLimit > 0 || $this->globalMemoryLimit > 0) && ++$this->checkTicker >= $this->checkRate){
$this->checkTicker = 0;
$memory = Process::getAdvancedMemoryUsage();
$trigger = false;
if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){
if($this->memoryLimit > 0 && $memory[0] > $this->memoryLimit){
$trigger = 0;
}elseif($this->globalMemoryLimit > 0 and $memory[1] > $this->globalMemoryLimit){
}elseif($this->globalMemoryLimit > 0 && $memory[1] > $this->globalMemoryLimit){
$trigger = 1;
}
if($trigger !== false){
if($this->lowMemory and $this->continuousTrigger){
if($this->lowMemory && $this->continuousTrigger){
if(++$this->continuousTriggerTicker >= $this->continuousTriggerRate){
$this->continuousTriggerTicker = 0;
$this->trigger($memory[$trigger], $this->memoryLimit, $trigger > 0, ++$this->continuousTriggerCount);
@ -240,7 +240,7 @@ class MemoryManager{
}
}
if($this->garbageCollectionPeriod > 0 and ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
if($this->garbageCollectionPeriod > 0 && ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
$this->garbageCollectionTicker = 0;
$this->triggerGarbageCollector();
}
@ -291,8 +291,7 @@ class MemoryManager{
* @param mixed $startingObject
*/
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
$hardLimit = ini_get('memory_limit');
if($hardLimit === false) throw new AssumptionFailedError("memory_limit INI directive should always exist");
$hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
ini_set('memory_limit', '-1');
gc_disable();
@ -300,7 +299,7 @@ class MemoryManager{
mkdir($outputFolder, 0777, true);
}
$obData = fopen(Path::join($outputFolder, "objects.js"), "wb+");
$obData = Utils::assumeNotFalse(fopen(Path::join($outputFolder, "objects.js"), "wb+"));
$objects = [];
@ -318,7 +317,7 @@ class MemoryManager{
$reflection = new \ReflectionClass($className);
$staticProperties[$className] = [];
foreach($reflection->getProperties() as $property){
if(!$property->isStatic() or $property->getDeclaringClass()->getName() !== $className){
if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){
continue;
}
@ -352,7 +351,7 @@ class MemoryManager{
}
}
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$logger->info("Wrote $staticCount static properties");
$globalVariables = [];
@ -379,7 +378,7 @@ class MemoryManager{
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$logger->info("Wrote $globalCount global variables");
foreach(get_defined_functions()["user"] as $function){
@ -394,7 +393,7 @@ class MemoryManager{
$functionStaticVarsCount += count($vars);
}
}
file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$logger->info("Wrote $functionStaticVarsCount function static variables");
$data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
@ -460,7 +459,7 @@ class MemoryManager{
}
}
fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");
}
}while($continue);
@ -469,11 +468,11 @@ class MemoryManager{
fclose($obData);
file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
arsort($instanceCounts, SORT_NUMERIC);
file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$logger->info("Finished!");

View File

@ -32,14 +32,16 @@ namespace pocketmine {
use pocketmine\utils\ServerKiller;
use pocketmine\utils\Terminal;
use pocketmine\utils\Timezone;
use pocketmine\utils\Utils;
use pocketmine\wizard\SetupWizard;
use Webmozart\PathUtil\Path;
use function defined;
use function extension_loaded;
use function getcwd;
use function phpversion;
use function preg_match;
use function preg_quote;
use function strpos;
use function realpath;
use function version_compare;
require_once __DIR__ . '/VersionInfo.php';
@ -112,8 +114,7 @@ namespace pocketmine {
}
}
if(extension_loaded("pthreads")){
$pthreads_version = phpversion("pthreads");
if(($pthreads_version = phpversion("pthreads")) !== false){
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
@ -122,8 +123,7 @@ namespace pocketmine {
}
}
if(extension_loaded("leveldb")){
$leveldb_version = phpversion("leveldb");
if(($leveldb_version = phpversion("leveldb")) !== false){
if(version_compare($leveldb_version, "0.2.1") < 0){
$messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
}
@ -253,8 +253,9 @@ JIT_WARNING
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
$dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR;
$pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR;
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
$dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR;
$pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR;
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
if(!file_exists($dataPath)){
@ -286,7 +287,7 @@ JIT_WARNING
$exitCode = 0;
do{
if(!file_exists(Path::join($dataPath, "server.properties")) and !isset($opts["no-wizard"])){
if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts["no-wizard"])){
$installer = new SetupWizard($dataPath);
if(!$installer->run()){
$exitCode = -1;

View File

@ -36,7 +36,6 @@ use pocketmine\crafting\CraftingManager;
use pocketmine\crafting\CraftingManagerFromDataHelper;
use pocketmine\crash\CrashDump;
use pocketmine\crash\CrashDumpRenderer;
use pocketmine\data\java\GameModeIdMap;
use pocketmine\entity\EntityDataHelper;
use pocketmine\entity\Location;
use pocketmine\event\HandlerListManager;
@ -178,6 +177,12 @@ class Server{
public const BROADCAST_CHANNEL_ADMINISTRATIVE = "pocketmine.broadcast.admin";
public const BROADCAST_CHANNEL_USERS = "pocketmine.broadcast.user";
public const DEFAULT_SERVER_NAME = VersionInfo::NAME . " Server";
public const DEFAULT_MAX_PLAYERS = 20;
public const DEFAULT_PORT_IPV4 = 19132;
public const DEFAULT_PORT_IPV6 = 19133;
public const DEFAULT_MAX_VIEW_DISTANCE = 16;
private static ?Server $instance = null;
private SleeperHandler $tickSleeper;
@ -324,15 +329,15 @@ class Server{
}
public function getPort() : int{
return $this->configGroup->getConfigInt("server-port", 19132);
return $this->configGroup->getConfigInt("server-port", self::DEFAULT_PORT_IPV4);
}
public function getPortV6() : int{
return $this->configGroup->getConfigInt("server-portv6", 19133);
return $this->configGroup->getConfigInt("server-portv6", self::DEFAULT_PORT_IPV6);
}
public function getViewDistance() : int{
return max(2, $this->configGroup->getConfigInt("view-distance", 8));
return max(2, $this->configGroup->getConfigInt("view-distance", self::DEFAULT_MAX_VIEW_DISTANCE));
}
/**
@ -357,7 +362,7 @@ class Server{
}
public function getGamemode() : GameMode{
return GameModeIdMap::getInstance()->fromId($this->configGroup->getConfigInt("gamemode", 0)) ?? GameMode::SURVIVAL();
return GameMode::fromString($this->configGroup->getConfigString("gamemode", GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL();
}
public function getForceGamemode() : bool{
@ -380,7 +385,7 @@ class Server{
}
public function getMotd() : string{
return $this->configGroup->getConfigString("motd", VersionInfo::NAME . " Server");
return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME);
}
public function getLoader() : \DynamicClassLoader{
@ -537,13 +542,10 @@ class Server{
if(!$ev->isCancelled()){
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
$nbt = new BigEndianNbtSerializer();
$contents = zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP);
if($contents === false){
throw new AssumptionFailedError("zlib_encode() failed unexpectedly");
}
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
try{
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
}catch(\RuntimeException | \ErrorException $e){
}catch(\RuntimeException $e){
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
$this->logger->logException($e);
}
@ -559,7 +561,7 @@ class Server{
$ev->call();
$class = $ev->getPlayerClass();
if($offlinePlayerData !== null and ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
$spawn = $playerPos->asVector3();
}else{
@ -719,7 +721,7 @@ class Server{
}
public function isWhitelisted(string $name) : bool{
return !$this->hasWhitelist() or $this->operators->exists($name, true) or $this->whitelist->exists($name, true);
return !$this->hasWhitelist() || $this->operators->exists($name, true) || $this->whitelist->exists($name, true);
}
public function isOp(string $name) : bool{
@ -797,7 +799,7 @@ class Server{
$this->logger->info("Loading server configuration");
$pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml");
if(!file_exists($pocketmineYmlPath)){
$content = file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml"));
$content = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")), "Missing required resource file");
if(VersionInfo::IS_DEVELOPMENT_BUILD){
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
}
@ -807,13 +809,13 @@ class Server{
$this->configGroup = new ServerConfigGroup(
new Config($pocketmineYmlPath, Config::YAML, []),
new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES, [
"motd" => VersionInfo::NAME . " Server",
"server-port" => 19132,
"server-portv6" => 19133,
"motd" => self::DEFAULT_SERVER_NAME,
"server-port" => self::DEFAULT_PORT_IPV4,
"server-portv6" => self::DEFAULT_PORT_IPV6,
"enable-ipv6" => true,
"white-list" => false,
"max-players" => 20,
"gamemode" => 0,
"max-players" => self::DEFAULT_MAX_PLAYERS,
"gamemode" => GameMode::SURVIVAL()->name(),
"force-gamemode" => false,
"hardcore" => false,
"pvp" => true,
@ -824,7 +826,7 @@ class Server{
"level-type" => "DEFAULT",
"enable-query" => true,
"auto-save" => true,
"view-distance" => 8,
"view-distance" => self::DEFAULT_MAX_VIEW_DISTANCE,
"xbox-auth" => true,
"language" => "eng"
])
@ -893,7 +895,7 @@ class Server{
}
$netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6);
if($netCompressionLevel < 1 or $netCompressionLevel > 9){
if($netCompressionLevel < 1 || $netCompressionLevel > 9){
$this->logger->warning("Invalid network compression level $netCompressionLevel set, setting to default 6");
$netCompressionLevel = 6;
}
@ -910,7 +912,7 @@ class Server{
$bannedTxt = Path::join($this->dataPath, "banned.txt");
$bannedPlayersTxt = Path::join($this->dataPath, "banned-players.txt");
if(file_exists($bannedTxt) and !file_exists($bannedPlayersTxt)){
if(file_exists($bannedTxt) && !file_exists($bannedPlayersTxt)){
@rename($bannedTxt, $bannedPlayersTxt);
}
@touch($bannedPlayersTxt);
@ -921,7 +923,7 @@ class Server{
$this->banByIP = new BanList($bannedIpsTxt);
$this->banByIP->load();
$this->maxPlayers = $this->configGroup->getConfigInt("max-players", 20);
$this->maxPlayers = $this->configGroup->getConfigInt("max-players", self::DEFAULT_MAX_PLAYERS);
$this->onlineMode = $this->configGroup->getConfigBool("xbox-auth", true);
if($this->onlineMode){
@ -932,7 +934,7 @@ class Server{
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
}
if($this->configGroup->getConfigBool("hardcore", false) and $this->getDifficulty() < World::DIFFICULTY_HARD){
if($this->configGroup->getConfigBool("hardcore", false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
$this->configGroup->setConfigInt("difficulty", World::DIFFICULTY_HARD);
}
@ -982,7 +984,7 @@ class Server{
$providerManager = new WorldProviderManager();
if(
($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null and
($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null &&
$format instanceof WritableWorldProviderManagerEntry
){
$providerManager->setDefault($format);
@ -1379,7 +1381,7 @@ class Server{
public function enablePlugins(PluginEnableOrder $type) : void{
foreach($this->pluginManager->getPlugins() as $plugin){
if(!$plugin->isEnabled() and $plugin->getDescription()->getOrder()->equals($type)){
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
$this->pluginManager->enablePlugin($plugin);
}
}
@ -1570,7 +1572,8 @@ class Server{
$stamp = Path::join($this->getDataPath(), "crashdumps", ".last_crash");
$crashInterval = 120; //2 minutes
if(file_exists($stamp) and !($report = (filemtime($stamp) + $crashInterval < time()))){
if(($lastReportTime = @filemtime($stamp)) !== false && $lastReportTime + $crashInterval >= time()){
$report = false;
$this->logger->debug("Not sending crashdump due to last crash less than $crashInterval seconds ago");
}
@touch($stamp); //update file timestamp
@ -1578,7 +1581,7 @@ class Server{
$plugin = $dump->getData()->plugin;
if($plugin !== ""){
$p = $this->pluginManager->getPlugin($plugin);
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
if($p instanceof Plugin && !($p->getPluginLoader() instanceof PharPluginLoader)){
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
$report = false;
}
@ -1588,7 +1591,7 @@ class Server{
$report = false;
}
if(strrpos(VersionInfo::GIT_HASH(), "-dirty") !== false or VersionInfo::GIT_HASH() === str_repeat("00", 20)){
if(strrpos(VersionInfo::GIT_HASH(), "-dirty") !== false || VersionInfo::GIT_HASH() === str_repeat("00", 20)){
$this->logger->debug("Not sending crashdump due to locally modified");
$report = false; //Don't send crashdumps for locally modified builds
}
@ -1603,8 +1606,8 @@ class Server{
"reportPaste" => base64_encode($dump->getEncodedData())
], 10, [], $postUrlError);
if($reply !== null and ($data = json_decode($reply->getBody())) !== null){
if(isset($data->crashId) and isset($data->crashUrl)){
if($reply !== null && ($data = json_decode($reply->getBody())) !== null){
if(isset($data->crashId) && isset($data->crashUrl)){
$reportId = $data->crashId;
$reportUrl = $data->crashUrl;
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
@ -1661,7 +1664,7 @@ class Server{
public function addOnlinePlayer(Player $player) : bool{
$ev = new PlayerLoginEvent($player, "Plugin reason");
$ev->call();
if($ev->isCancelled() or !$player->isConnected()){
if($ev->isCancelled() || !$player->isConnected()){
$player->disconnect($ev->getKickMessage());
return false;
@ -1790,7 +1793,7 @@ class Server{
$this->network->getBandwidthTracker()->rotateAverageHistory();
}
if($this->sendUsageTicker > 0 and --$this->sendUsageTicker === 0){
if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){
$this->sendUsageTicker = 6000;
$this->sendUsage(SendUsageTask::TYPE_STATUS);
}

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.0.9";
public const BASE_VERSION = "4.2.0";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";

View File

@ -160,7 +160,7 @@ class Bamboo extends Transparent{
public function onNearbyBlockChange() : void{
$below = $this->position->getWorld()->getBlock($this->position->down());
if(!$this->canBeSupportedBy($below) and !$below->isSameType($this)){
if(!$this->canBeSupportedBy($below) && !$below->isSameType($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}

View File

@ -36,14 +36,14 @@ final class BambooSapling extends Flowable{
private bool $ready = false;
public function readStateFromData(int $id, int $stateMeta) : void{
$this->ready = ($stateMeta & BlockLegacyMetadata::SAPLING_FLAG_READY) !== 0;
$this->ready = ($stateMeta & BlockLegacyMetadata::BAMBOO_SAPLING_FLAG_READY) !== 0;
}
protected function writeStateToMeta() : int{
return $this->ready ? BlockLegacyMetadata::SAPLING_FLAG_READY : 0;
return $this->ready ? BlockLegacyMetadata::BAMBOO_SAPLING_FLAG_READY : 0;
}
public function getStateBitmask() : int{ return 0b1000; }
public function getStateBitmask() : int{ return 0b1; }
public function isReady() : bool{ return $this->ready; }

View File

@ -130,7 +130,7 @@ abstract class BaseBanner extends Transparent{
public function getDropsForCompatibleTool(Item $item) : array{
$drop = $this->asItem();
if($drop instanceof ItemBanner and count($this->patterns) > 0){
if($drop instanceof ItemBanner && count($this->patterns) > 0){
$drop->setPatterns($this->patterns);
}
@ -139,7 +139,7 @@ abstract class BaseBanner extends Transparent{
public function getPickedItem(bool $addUserData = false) : Item{
$result = $this->asItem();
if($addUserData and $result instanceof ItemBanner and count($this->patterns) > 0){
if($addUserData && $result instanceof ItemBanner && count($this->patterns) > 0){
$result->setPatterns($this->patterns);
}
return $result;

View File

@ -24,34 +24,17 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\item\Item;
abstract class BaseCoral extends Transparent{
protected CoralType $coralType;
protected bool $dead = false;
use CoralTypeTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
parent::__construct($idInfo, $name, $breakInfo);
$this->coralType = CoralType::TUBE();
}
public function getCoralType() : CoralType{ return $this->coralType; }
/** @return $this */
public function setCoralType(CoralType $coralType) : self{
$this->coralType = $coralType;
return $this;
}
public function isDead() : bool{ return $this->dead; }
/** @return $this */
public function setDead(bool $dead) : self{
$this->dead = $dead;
return $this;
}
public function onNearbyBlockChange() : void{
if(!$this->dead){
$world = $this->position->getWorld();

View File

@ -101,7 +101,7 @@ abstract class BaseRail extends Flowable{
}
if(
$other instanceof BaseRail and
$other instanceof BaseRail &&
in_array($otherConnection, $other->getCurrentShapeConnections(), true)
){
$connections[] = $connection;
@ -179,7 +179,7 @@ abstract class BaseRail extends Flowable{
$otherSide |= RailConnectionInfo::FLAG_ASCEND;
}
if(!($other instanceof BaseRail) or count($otherConnections = $other->getConnectedDirections()) >= 2){
if(!($other instanceof BaseRail) || count($otherConnections = $other->getConnectedDirections()) >= 2){
//we can only connect to a rail that has less than 2 connections
continue;
}
@ -224,7 +224,7 @@ abstract class BaseRail extends Flowable{
$this->position->getWorld()->useBreakOn($this->position);
}else{
foreach($this->getCurrentShapeConnections() as $connection){
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 and $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
$this->position->getWorld()->useBreakOn($this->position);
break;
}

View File

@ -120,7 +120,7 @@ class Bed extends Transparent{
public function getOtherHalf() : ?Bed{
$other = $this->getSide($this->getOtherHalfSide());
if($other instanceof Bed and $other->head !== $this->head and $other->facing === $this->facing){
if($other instanceof Bed && $other->head !== $this->head && $other->facing === $this->facing){
return $other;
}
@ -135,14 +135,14 @@ class Bed extends Transparent{
$player->sendMessage(TextFormat::GRAY . "This bed is incomplete");
return true;
}elseif($playerPos->distanceSquared($this->position) > 4 and $playerPos->distanceSquared($other->position) > 4){
}elseif($playerPos->distanceSquared($this->position) > 4 && $playerPos->distanceSquared($other->position) > 4){
$player->sendMessage(KnownTranslationFactory::tile_bed_tooFar()->prefix(TextFormat::GRAY));
return true;
}
$time = $this->position->getWorld()->getTimeOfDay();
$isNight = ($time >= World::TIME_NIGHT and $time < World::TIME_SUNRISE);
$isNight = ($time >= World::TIME_NIGHT && $time < World::TIME_SUNRISE);
if(!$isNight){
$player->sendMessage(KnownTranslationFactory::tile_bed_noSleep()->prefix(TextFormat::GRAY));
@ -166,7 +166,7 @@ class Bed extends Transparent{
}
public function onNearbyBlockChange() : void{
if(($other = $this->getOtherHalf()) !== null and $other->occupied !== $this->occupied){
if(($other = $this->getOtherHalf()) !== null && $other->occupied !== $this->occupied){
$this->occupied = $other->occupied;
$this->position->getWorld()->setBlock($this->position, $this);
}
@ -186,7 +186,7 @@ class Bed extends Transparent{
$this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH;
$next = $this->getSide($this->getOtherHalfSide());
if($next->canBeReplaced() and !$next->getSide(Facing::DOWN)->isTransparent()){
if($next->canBeReplaced() && !$next->getSide(Facing::DOWN)->isTransparent()){
$nextState = clone $this;
$nextState->head = true;
$tx->addBlock($blockReplace->position, $this)->addBlock($next->position, $nextState);

View File

@ -148,14 +148,14 @@ class Block{
$tileType = $this->idInfo->getTileClass();
$oldTile = $this->position->getWorld()->getTile($this->position);
if($oldTile !== null){
if($tileType === null or !($oldTile instanceof $tileType)){
if($tileType === null || !($oldTile instanceof $tileType)){
$oldTile->close();
$oldTile = null;
}elseif($oldTile instanceof Spawnable){
$oldTile->setDirty(); //destroy old network cache
}
}
if($oldTile === null and $tileType !== null){
if($oldTile === null && $tileType !== null){
/**
* @var Tile $tile
* @see Tile::__construct()
@ -166,13 +166,21 @@ class Block{
}
/**
* Returns whether the given block has an equivalent type to this one. This compares base legacy ID and variant.
* Returns a type ID that identifies this type of block. This does not include information like facing, colour,
* powered/unpowered, etc.
*/
public function getTypeId() : int{
return ($this->idInfo->getBlockId() << Block::INTERNAL_METADATA_BITS) | $this->idInfo->getVariant();
}
/**
* Returns whether the given block has an equivalent type to this one. This compares the type IDs.
*
* Note: This ignores additional IDs used to represent additional states. This means that, for example, a lit
* furnace and unlit furnace are considered the same type.
*/
public function isSameType(Block $other) : bool{
return $this->idInfo->getBlockId() === $other->idInfo->getBlockId() and $this->idInfo->getVariant() === $other->idInfo->getVariant();
return $this->getTypeId() === $other->getTypeId();
}
/**
@ -353,7 +361,7 @@ class Block{
*/
public function getDrops(Item $item) : array{
if($this->breakInfo->isToolCompatible($item)){
if($this->isAffectedBySilkTouch() and $item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
if($this->isAffectedBySilkTouch() && $item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
return $this->getSilkTouchDrops($item);
}
@ -394,7 +402,7 @@ class Block{
* Returns how much XP will be dropped by breaking this block with the given item.
*/
public function getXpDropForTool(Item $item) : int{
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH()) or !$this->breakInfo->isToolCompatible($item)){
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH()) || !$this->breakInfo->isToolCompatible($item)){
return 0;
}
@ -605,7 +613,7 @@ class Block{
public function isFullCube() : bool{
$bb = $this->getCollisionBoxes();
return count($bb) === 1 and $bb[0]->getAverageEdgeLength() >= 1 and $bb[0]->isCube();
return count($bb) === 1 && $bb[0]->getAverageEdgeLength() >= 1 && $bb[0]->isCube();
}
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{

View File

@ -119,8 +119,8 @@ class BlockBreakInfo{
return false;
}
return $this->toolType === BlockToolType::NONE or $this->toolHarvestLevel === 0 or (
($this->toolType & $tool->getBlockToolType()) !== 0 and $tool->getBlockToolHarvestLevel() >= $this->toolHarvestLevel);
return $this->toolType === BlockToolType::NONE || $this->toolHarvestLevel === 0 || (
($this->toolType & $tool->getBlockToolType()) !== 0 && $tool->getBlockToolHarvestLevel() >= $this->toolHarvestLevel);
}
/**

View File

@ -43,6 +43,7 @@ use pocketmine\block\tile\FlowerPot as TileFlowerPot;
use pocketmine\block\tile\Hopper as TileHopper;
use pocketmine\block\tile\ItemFrame as TileItemFrame;
use pocketmine\block\tile\Jukebox as TileJukebox;
use pocketmine\block\tile\Lectern as TileLectern;
use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
use pocketmine\block\tile\NormalFurnace as TileNormalFurnace;
use pocketmine\block\tile\Note as TileNote;
@ -260,6 +261,7 @@ class BlockFactory{
$this->registerAllMeta(new Opaque(new BID(Ids::LAPIS_BLOCK, 0), "Lapis Lazuli Block", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new LapisOre(new BID(Ids::LAPIS_ORE, 0), "Lapis Lazuli Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new Lava(new BIDFlattened(Ids::FLOWING_LAVA, [Ids::STILL_LAVA], 0), "Lava", BlockBreakInfo::indestructible(500.0)));
$this->registerAllMeta(new Lectern(new BID(Ids::LECTERN, 0, ItemIds::LECTERN, TileLectern::class), "Lectern", new BlockBreakInfo(2.0, BlockToolType::AXE)));
$this->registerAllMeta(new Lever(new BID(Ids::LEVER, 0), "Lever", new BlockBreakInfo(0.5)));
$this->registerAllMeta(new Loom(new BID(Ids::LOOM, 0), "Loom", new BlockBreakInfo(2.5, BlockToolType::AXE)));
$this->registerAllMeta(new Magma(new BID(Ids::MAGMA, 0), "Magma Block", new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
@ -298,7 +300,7 @@ class BlockFactory{
$this->registerAllMeta(new Stair(new BID(Ids::PRISMARINE_STAIRS, 0), "Prismarine Stairs", $prismarineBreakInfo));
$pumpkinBreakInfo = new BlockBreakInfo(1.0, BlockToolType::AXE);
$this->registerAllMeta($pumpkin = new Opaque(new BID(Ids::PUMPKIN, 0), "Pumpkin", $pumpkinBreakInfo));
$this->registerAllMeta(new Pumpkin(new BID(Ids::PUMPKIN, 0), "Pumpkin", $pumpkinBreakInfo));
$this->registerAllMeta(new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN, 0), "Carved Pumpkin", $pumpkinBreakInfo));
$this->registerAllMeta(new LitPumpkin(new BID(Ids::JACK_O_LANTERN, 0), "Jack o'Lantern", $pumpkinBreakInfo));
@ -396,35 +398,42 @@ class BlockFactory{
//TODO: in the future this won't be the same for all the types
$stoneSlabBreakInfo = new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_BRICK), "Brick", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_COBBLESTONE), "Cobblestone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_FAKE_WOODEN), "Fake Wooden", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_NETHER_BRICK), "Nether Brick", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_QUARTZ), "Quartz", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_SANDSTONE), "Sandstone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_SMOOTH_STONE), "Smooth Stone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(1, Meta::STONE_SLAB_STONE_BRICK), "Stone Brick", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_DARK_PRISMARINE), "Dark Prismarine", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_MOSSY_COBBLESTONE), "Mossy Cobblestone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_PRISMARINE), "Prismarine", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_PRISMARINE_BRICKS), "Prismarine Bricks", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_PURPUR), "Purpur", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_RED_NETHER_BRICK), "Red Nether Brick", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_RED_SANDSTONE), "Red Sandstone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(2, Meta::STONE_SLAB2_SMOOTH_SANDSTONE), "Smooth Sandstone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_ANDESITE), "Andesite", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_DIORITE), "Diorite", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_END_STONE_BRICK), "End Stone Brick", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_GRANITE), "Granite", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_POLISHED_ANDESITE), "Polished Andesite", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_POLISHED_DIORITE), "Polished Diorite", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_POLISHED_GRANITE), "Polished Granite", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(3, Meta::STONE_SLAB3_SMOOTH_RED_SANDSTONE), "Smooth Red Sandstone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_CUT_RED_SANDSTONE), "Cut Red Sandstone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_CUT_SANDSTONE), "Cut Sandstone", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_MOSSY_STONE_BRICK), "Mossy Stone Brick", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_SMOOTH_QUARTZ), "Smooth Quartz", $stoneSlabBreakInfo));
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_STONE), "Stone", $stoneSlabBreakInfo));
$getStoneSlabId = static fn(int $stoneSlabId, int $meta) => BlockLegacyIdHelper::getStoneSlabIdentifier($stoneSlabId, $meta);
foreach([
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_BRICK), "Brick", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_COBBLESTONE), "Cobblestone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_FAKE_WOODEN), "Fake Wooden", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_NETHER_BRICK), "Nether Brick", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_QUARTZ), "Quartz", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_SANDSTONE), "Sandstone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_SMOOTH_STONE), "Smooth Stone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(1, Meta::STONE_SLAB_STONE_BRICK), "Stone Brick", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_DARK_PRISMARINE), "Dark Prismarine", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_MOSSY_COBBLESTONE), "Mossy Cobblestone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_PRISMARINE), "Prismarine", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_PRISMARINE_BRICKS), "Prismarine Bricks", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_PURPUR), "Purpur", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_RED_NETHER_BRICK), "Red Nether Brick", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_RED_SANDSTONE), "Red Sandstone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(2, Meta::STONE_SLAB2_SMOOTH_SANDSTONE), "Smooth Sandstone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_ANDESITE), "Andesite", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_DIORITE), "Diorite", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_END_STONE_BRICK), "End Stone Brick", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_GRANITE), "Granite", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_POLISHED_ANDESITE), "Polished Andesite", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_POLISHED_DIORITE), "Polished Diorite", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_POLISHED_GRANITE), "Polished Granite", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(3, Meta::STONE_SLAB3_SMOOTH_RED_SANDSTONE), "Smooth Red Sandstone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(4, Meta::STONE_SLAB4_CUT_RED_SANDSTONE), "Cut Red Sandstone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(4, Meta::STONE_SLAB4_CUT_SANDSTONE), "Cut Sandstone", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(4, Meta::STONE_SLAB4_MOSSY_STONE_BRICK), "Mossy Stone Brick", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(4, Meta::STONE_SLAB4_SMOOTH_QUARTZ), "Smooth Quartz", $stoneSlabBreakInfo),
new Slab($getStoneSlabId(4, Meta::STONE_SLAB4_STONE), "Stone", $stoneSlabBreakInfo),
] as $slabType){
$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 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()));
@ -626,7 +635,6 @@ class BlockFactory{
//TODO: minecraft:jigsaw
//TODO: minecraft:kelp
//TODO: minecraft:lava_cauldron
//TODO: minecraft:lectern
//TODO: minecraft:movingBlock
//TODO: minecraft:observer
//TODO: minecraft:piston
@ -973,7 +981,7 @@ class BlockFactory{
}
foreach($block->getIdInfo()->getAllBlockIds() as $id){
if(!$override and $this->isRegistered($id, $variant)){
if(!$override && $this->isRegistered($id, $variant)){
throw new \InvalidArgumentException("Block registration $id:$variant conflicts with an existing block");
}
@ -982,7 +990,7 @@ class BlockFactory{
continue;
}
if(!$override and $this->isRegistered($id, $m)){
if(!$override && $this->isRegistered($id, $m)){
throw new \InvalidArgumentException("Block registration " . get_class($block) . " has states which conflict with other blocks");
}
@ -1035,7 +1043,7 @@ class BlockFactory{
* Deserializes a block from the provided legacy ID and legacy meta.
*/
public function get(int $id, int $meta) : Block{
if($meta < 0 or $meta >= (1 << Block::INTERNAL_METADATA_BITS)){
if($meta < 0 || $meta >= (1 << Block::INTERNAL_METADATA_BITS)){
throw new \InvalidArgumentException("Block meta value $meta is out of bounds");
}
@ -1061,7 +1069,7 @@ class BlockFactory{
*/
public function isRegistered(int $id, int $meta = 0) : bool{
$b = $this->fullList[($id << Block::INTERNAL_METADATA_BITS) | $meta];
return $b !== null and !($b instanceof UnknownBlock);
return $b !== null && !($b instanceof UnknownBlock);
}
/**

View File

@ -42,6 +42,8 @@ final class BlockLegacyMetadata{
public const BAMBOO_LEAF_SIZE_SHIFT = 1;
public const BAMBOO_LEAF_SIZE_MASK = 0x03;
public const BAMBOO_SAPLING_FLAG_READY = 0x01;
public const BARREL_FLAG_OPEN = 0x08;
public const BED_FLAG_HEAD = 0x08;
@ -142,6 +144,8 @@ final class BlockLegacyMetadata{
public const LEAVES_FLAG_NO_DECAY = 0x04;
public const LEAVES_FLAG_CHECK_DECAY = 0x08;
public const LECTERN_FLAG_POWERED = 0x04;
public const LEVER_FLAG_POWERED = 0x08;
public const LIQUID_FLAG_FALLING = 0x08;

View File

@ -116,7 +116,7 @@ class BrewingStand extends Transparent{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
$stand = $this->position->getWorld()->getTile($this->position);
if($stand instanceof TileBrewingStand and $stand->canOpenWith($item->getCustomName())){
if($stand instanceof TileBrewingStand && $stand->canOpenWith($item->getCustomName())){
$player->setCurrentWindow($stand->getInventory());
}
}
@ -125,6 +125,24 @@ class BrewingStand extends Transparent{
}
public function onScheduledUpdate() : void{
//TODO
$brewing = $this->position->getWorld()->getTile($this->position);
if($brewing instanceof TileBrewingStand){
if($brewing->onUpdate()){
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
}
$changed = false;
foreach(BrewingStandSlot::getAll() as $slot){
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
if($occupied !== $this->hasSlot($slot)){
$this->setSlot($slot, $occupied);
$changed = true;
}
}
if($changed){
$this->position->getWorld()->setBlock($this->position, $this);
}
}
}
}

View File

@ -82,7 +82,7 @@ class Cactus extends Transparent{
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if($down->getId() !== BlockLegacyIds::SAND and !$down->isSameType($this)){
if($down->getId() !== BlockLegacyIds::SAND && !$down->isSameType($this)){
$this->position->getWorld()->useBreakOn($this->position);
}else{
foreach(Facing::HORIZONTAL as $side){
@ -129,7 +129,7 @@ class Cactus 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->getId() === BlockLegacyIds::SAND or $down->isSameType($this)){
if($down->getId() === BlockLegacyIds::SAND || $down->isSameType($this)){
foreach(Facing::HORIZONTAL as $side){
if($this->getSide($side)->isSolid()){
return false;

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\event\block\ChestPairEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
@ -47,17 +48,21 @@ class Chest extends Transparent{
public function onPostPlace() : void{
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileChest){
foreach([
Facing::rotateY($this->facing, true),
Facing::rotateY($this->facing, false)
] as $side){
foreach([false, true] as $clockwise){
$side = Facing::rotateY($this->facing, $clockwise);
$c = $this->getSide($side);
if($c instanceof Chest and $c->isSameType($this) and $c->facing === $this->facing){
$pair = $this->position->getWorld()->getTile($c->position);
if($pair instanceof TileChest and !$pair->isPaired()){
$pair->pairWith($tile);
$tile->pairWith($pair);
break;
if($c instanceof Chest && $c->isSameType($this) && $c->facing === $this->facing){
$world = $this->position->getWorld();
$pair = $world->getTile($c->position);
if($pair instanceof TileChest && !$pair->isPaired()){
[$left, $right] = $clockwise ? [$c, $this] : [$this, $c];
$ev = new ChestPairEvent($left, $right);
$ev->call();
if(!$ev->isCancelled() && $world->getBlock($this->position)->isSameType($this) && $world->getBlock($c->position)->isSameType($c)){
$pair->pairWith($tile);
$tile->pairWith($pair);
break;
}
}
}
}
@ -70,8 +75,8 @@ class Chest extends Transparent{
$chest = $this->position->getWorld()->getTile($this->position);
if($chest instanceof TileChest){
if(
!$this->getSide(Facing::UP)->isTransparent() or
(($pair = $chest->getPair()) !== null and !$pair->getBlock()->getSide(Facing::UP)->isTransparent()) or
!$this->getSide(Facing::UP)->isTransparent() ||
(($pair = $chest->getPair()) !== null && !$pair->getBlock()->getSide(Facing::UP)->isTransparent()) ||
!$chest->canOpenWith($item->getCustomName())
){
return true;

View File

@ -86,7 +86,7 @@ class CocoaBlock extends Transparent{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(Facing::axis($face) !== Axis::Y and $this->canAttachTo($blockClicked)){
if(Facing::axis($face) !== Axis::Y && $this->canAttachTo($blockClicked)){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\utils\ColorInMetadataTrait;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
use pocketmine\event\block\BlockFormEvent;
use pocketmine\math\Facing;
class ConcretePowder extends Opaque implements Fallable{
@ -42,7 +43,11 @@ class ConcretePowder extends Opaque implements Fallable{
public function onNearbyBlockChange() : void{
if(($block = $this->checkAdjacentWater()) !== null){
$this->position->getWorld()->setBlock($this->position, $block);
$ev = new BlockFormEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
}else{
$this->startFalling();
}

View File

@ -24,15 +24,14 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\data\bedrock\CoralTypeIdMap;
use pocketmine\item\Item;
use function mt_rand;
final class CoralBlock extends Opaque{
private CoralType $coralType;
private bool $dead = false;
use CoralTypeTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
$this->coralType = CoralType::TUBE();
@ -60,22 +59,6 @@ final class CoralBlock extends Opaque{
return 0b1111;
}
public function getCoralType() : CoralType{ return $this->coralType; }
/** @return $this */
public function setCoralType(CoralType $coralType) : self{
$this->coralType = $coralType;
return $this;
}
public function isDead() : bool{ return $this->dead; }
/** @return $this */
public function setDead(bool $dead) : self{
$this->dead = $dead;
return $this;
}
public function onNearbyBlockChange() : void{
if(!$this->dead){
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));

View File

@ -69,7 +69,7 @@ abstract class Crops extends Flowable{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->age < 7 and $item instanceof Fertilizer){
if($this->age < 7 && $item instanceof Fertilizer){
$block = clone $this;
$block->age += mt_rand(2, 5);
if($block->age > 7){
@ -100,7 +100,7 @@ abstract class Crops extends Flowable{
}
public function onRandomTick() : void{
if($this->age < 7 and mt_rand(0, 2) === 1){
if($this->age < 7 && mt_rand(0, 2) === 1){
$block = clone $this;
++$block->age;
$ev = new BlockGrowEvent($this, $block);

View File

@ -28,6 +28,7 @@ use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\ItemUseOnBlockSound;
class Dirt extends Opaque{
@ -58,9 +59,12 @@ class Dirt extends Opaque{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face === Facing::UP and $item instanceof Hoe){
if($face === Facing::UP && $item instanceof Hoe){
$item->applyDamage(1);
$this->position->getWorld()->setBlock($this->position, $this->coarse ? VanillaBlocks::DIRT() : VanillaBlocks::FARMLAND());
$newBlock = $this->coarse ? VanillaBlocks::DIRT() : VanillaBlocks::FARMLAND();
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
$this->position->getWorld()->setBlock($this->position, $newBlock);
return true;
}

View File

@ -72,7 +72,7 @@ class Door extends Transparent{
//copy door properties from other half
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
if($other instanceof Door and $other->isSameType($this)){
if($other instanceof Door && $other->isSameType($this)){
if($this->top){
$this->facing = $other->facing;
$this->open = $other->open;
@ -129,7 +129,7 @@ class Door extends Transparent{
if($face === Facing::UP){
$blockUp = $this->getSide(Facing::UP);
$blockDown = $this->getSide(Facing::DOWN);
if(!$blockUp->canBeReplaced() or $blockDown->isTransparent()){
if(!$blockUp->canBeReplaced() || $blockDown->isTransparent()){
return false;
}
@ -140,7 +140,7 @@ class Door extends Transparent{
$next = $this->getSide(Facing::rotateY($this->facing, false));
$next2 = $this->getSide(Facing::rotateY($this->facing, true));
if($next->isSameType($this) or (!$next2->isTransparent() and $next->isTransparent())){ //Door hinge
if($next->isSameType($this) || (!$next2->isTransparent() && $next->isTransparent())){ //Door hinge
$this->hingeRight = true;
}
@ -158,7 +158,7 @@ class Door extends Transparent{
$this->open = !$this->open;
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
if($other instanceof Door and $other->isSameType($this)){
if($other instanceof Door && $other->isSameType($this)){
$other->open = $this->open;
$this->position->getWorld()->setBlock($other->position, $other);
}

View File

@ -55,7 +55,7 @@ class DoublePlant extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$id = $blockReplace->getSide(Facing::DOWN)->getId();
if(($id === BlockLegacyIds::GRASS or $id === BlockLegacyIds::DIRT) and $blockReplace->getSide(Facing::UP)->canBeReplaced()){
if(($id === BlockLegacyIds::GRASS || $id === BlockLegacyIds::DIRT) && $blockReplace->getSide(Facing::UP)->canBeReplaced()){
$top = clone $this;
$top->top = true;
$tx->addBlock($blockReplace->position, $this)->addBlock($blockReplace->position->getSide(Facing::UP), $top);
@ -72,14 +72,14 @@ class DoublePlant extends Flowable{
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
return (
$other instanceof DoublePlant and
$other->isSameType($this) and
$other instanceof DoublePlant &&
$other->isSameType($this) &&
$other->top !== $this->top
);
}
public function onNearbyBlockChange() : void{
if(!$this->isValidHalfPlant() or (!$this->top and $this->getSide(Facing::DOWN)->isTransparent())){
if(!$this->isValidHalfPlant() || (!$this->top && $this->getSide(Facing::DOWN)->isTransparent())){
$this->position->getWorld()->useBreakOn($this->position);
}
}

View File

@ -34,7 +34,7 @@ class DoubleTallGrass extends DoublePlant{
}
public function getDropsForIncompatibleTool(Item $item) : array{
if($this->top and mt_rand(0, 7) === 0){
if($this->top && mt_rand(0, 7) === 0){
return [VanillaItems::WHEAT_SEEDS()];
}
return [];

View File

@ -46,7 +46,7 @@ class EndRod extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
if($stateMeta !== 0 and $stateMeta !== 1){
if($stateMeta !== 0 && $stateMeta !== 1){
$stateMeta ^= 1;
}
@ -59,7 +59,7 @@ class EndRod extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$this->facing = $face;
if($blockClicked instanceof EndRod and $blockClicked->facing === $this->facing){
if($blockClicked instanceof EndRod && $blockClicked->facing === $this->facing){
$this->facing = Facing::opposite($face);
}

View File

@ -52,7 +52,7 @@ class EnderChest extends Transparent{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
$enderChest = $this->position->getWorld()->getTile($this->position);
if($enderChest instanceof TileEnderChest and $this->getSide(Facing::UP)->isTransparent()){
if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){
$enderChest->setViewerCount($enderChest->getViewerCount() + 1);
$player->setCurrentWindow(new EnderChestInventory($this->position, $player->getEnderInventory()));
}

View File

@ -41,7 +41,7 @@ class Fence extends Transparent{
foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing);
if($block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent())){
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
$this->connections[$facing] = true;
}else{
unset($this->connections[$facing]);
@ -61,7 +61,7 @@ class Fence extends Transparent{
$connectWest = isset($this->connections[Facing::WEST]);
$connectEast = isset($this->connections[Facing::EAST]);
if($connectWest or $connectEast){
if($connectWest || $connectEast){
//X axis (west/east)
$bbs[] = AxisAlignedBB::one()
->squash(Axis::Z, $inset)
@ -73,7 +73,7 @@ class Fence extends Transparent{
$connectNorth = isset($this->connections[Facing::NORTH]);
$connectSouth = isset($this->connections[Facing::SOUTH]);
if($connectNorth or $connectSouth){
if($connectNorth || $connectSouth){
//Z axis (north/south)
$bbs[] = AxisAlignedBB::one()
->squash(Axis::X, $inset)

View File

@ -80,7 +80,7 @@ class FenceGate extends Transparent{
private function checkInWall() : bool{
return (
$this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall or
$this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall ||
$this->getSide(Facing::rotateY($this->facing, true)) instanceof Wall
);
}
@ -105,7 +105,7 @@ class FenceGate extends Transparent{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$this->open = !$this->open;
if($this->open and $player !== null){
if($this->open && $player !== null){
$playerFacing = $player->getHorizontalFacing();
if($playerFacing === Facing::opposite($this->facing)){
$this->facing = $playerFacing;

View File

@ -27,11 +27,16 @@ use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\entity\Entity;
use pocketmine\entity\projectile\Arrow;
use pocketmine\event\block\BlockBurnEvent;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\event\entity\EntityCombustByBlockEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\world\format\Chunk;
use pocketmine\world\World;
use function intdiv;
use function max;
use function min;
use function mt_rand;
@ -94,7 +99,7 @@ class Fire extends Flowable{
}
public function onNearbyBlockChange() : void{
if(!$this->getSide(Facing::DOWN)->isSolid() and !$this->hasAdjacentFlammableBlocks()){
if(!$this->getSide(Facing::DOWN)->isSolid() && !$this->hasAdjacentFlammableBlocks()){
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
}else{
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
@ -109,7 +114,7 @@ class Fire extends Flowable{
$down = $this->getSide(Facing::DOWN);
$result = null;
if($this->age < 15 and mt_rand(0, 2) === 0){
if($this->age < 15 && mt_rand(0, 2) === 0){
$this->age++;
$result = $this;
}
@ -118,13 +123,13 @@ class Fire extends Flowable{
if(!$down->burnsForever()){
//TODO: check rain
if($this->age === 15){
if(!$down->isFlammable() and mt_rand(0, 3) === 3){ //1/4 chance to extinguish
if(!$down->isFlammable() && mt_rand(0, 3) === 3){ //1/4 chance to extinguish
$canSpread = false;
$result = VanillaBlocks::AIR();
}
}elseif(!$this->hasAdjacentFlammableBlocks()){
$canSpread = false;
if(!$down->isSolid() or $this->age > 3){
if(!$down->isSolid() || $this->age > 3){
$result = VanillaBlocks::AIR();
}
}
@ -137,17 +142,8 @@ class Fire extends Flowable{
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
if($canSpread){
//TODO: raise upper bound for chance in humid biomes
foreach($this->getHorizontalSides() as $side){
$this->burnBlock($side, 300);
}
//vanilla uses a 250 upper bound here, but I don't think they intended to increase the chance of incineration
$this->burnBlock($this->getSide(Facing::UP), 350);
$this->burnBlock($this->getSide(Facing::DOWN), 350);
//TODO: fire spread
$this->burnBlocksAround();
$this->spreadFire();
}
}
@ -165,6 +161,18 @@ class Fire extends Flowable{
return false;
}
private function burnBlocksAround() : void{
//TODO: raise upper bound for chance in humid biomes
foreach($this->getHorizontalSides() as $side){
$this->burnBlock($side, 300);
}
//vanilla uses a 250 upper bound here, but I don't think they intended to increase the chance of incineration
$this->burnBlock($this->getSide(Facing::UP), 350);
$this->burnBlock($this->getSide(Facing::DOWN), 350);
}
private function burnBlock(Block $block, int $chanceBound) : void{
if(mt_rand(0, $chanceBound) < $block->getFlammability()){
$ev = new BlockBurnEvent($block, $this);
@ -172,14 +180,85 @@ class Fire extends Flowable{
if(!$ev->isCancelled()){
$block->onIncinerate();
$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));
$this->position->getWorld()->setBlock($block->position, $fire);
}else{
$spreadedFire = $this->spreadBlock($block, $fire);
}
if(!$spreadedFire){
$this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR());
}
}
}
}
private function spreadFire() : void{
$world = $this->position->getWorld();
$difficultyChanceIncrease = $world->getDifficulty() * 7;
$ageDivisor = $this->age + 30;
for($y = -1; $y <= 4; ++$y){
$targetY = $y + (int) $this->position->y;
if($targetY < World::Y_MIN || $targetY >= World::Y_MAX){
continue;
}
//Higher blocks have a lower chance of catching fire
$randomBound = 100 + ($y > 1 ? ($y - 1) * 100 : 0);
for($z = -1; $z <= 1; ++$z){
$targetZ = $z + (int) $this->position->z;
for($x = -1; $x <= 1; ++$x){
if($x === 0 && $y === 0 && $z === 0){
continue;
}
$targetX = $x + (int) $this->position->x;
if(!$world->isInWorld($targetX, $targetY, $targetZ)){
continue;
}
if(!$world->isChunkLoaded($targetX >> Chunk::COORD_BIT_SIZE, $targetZ >> Chunk::COORD_BIT_SIZE)){
continue;
}
$block = $world->getBlockAt($targetX, $targetY, $targetZ);
if($block->getId() !== BlockLegacyIds::AIR){
continue;
}
//TODO: fire can't spread if it's raining in any horizontally adjacent block, or the current one
$encouragement = 0;
foreach($block->position->sides() as $vector3){
if($world->isInWorld($vector3->x, $vector3->y, $vector3->z)){
$encouragement = max($encouragement, $world->getBlockAt($vector3->x, $vector3->y, $vector3->z)->getFlameEncouragement());
}
}
if($encouragement <= 0){
continue;
}
$maxChance = intdiv($encouragement + 40 + $difficultyChanceIncrease, $ageDivisor);
//TODO: max chance is lowered by half in humid biomes
if($maxChance > 0 && mt_rand(0, $randomBound - 1) <= $maxChance){
$new = clone $this;
$new->age = min(15, $this->age + (mt_rand(0, 4) >> 2));
$this->spreadBlock($block, $new);
}
}
}
}
}
private function spreadBlock(Block $block, Block $newState) : bool{
$ev = new BlockSpreadEvent($block, $this, $newState);
$ev->call();
if(!$ev->isCancelled()){
$block->position->getWorld()->setBlock($block->position, $ev->getNewState());
return true;
}
return false;
}
}

View File

@ -33,7 +33,7 @@ class Flower extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getId() === BlockLegacyIds::GRASS or $down->getId() === BlockLegacyIds::DIRT or $down->getId() === BlockLegacyIds::FARMLAND){
if($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::FARMLAND){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -69,7 +69,7 @@ class FlowerPot extends Flowable{
/** @return $this */
public function setPlant(?Block $plant) : self{
if($plant === null or $plant instanceof Air){
if($plant === null || $plant instanceof Air){
$this->plant = null;
}else{
$this->plant = clone $plant;
@ -83,12 +83,12 @@ class FlowerPot extends Flowable{
}
return
$block instanceof Cactus or
$block instanceof DeadBush or
$block instanceof Flower or
$block instanceof RedMushroom or
$block instanceof Sapling or
($block instanceof TallGrass and $block->getIdInfo()->getVariant() === BlockLegacyMetadata::TALLGRASS_FERN); //TODO: clean up
$block instanceof Cactus ||
$block instanceof DeadBush ||
$block instanceof Flower ||
$block instanceof RedMushroom ||
$block instanceof Sapling ||
($block instanceof TallGrass && $block->getIdInfo()->getVariant() === BlockLegacyMetadata::TALLGRASS_FERN); //TODO: clean up
//TODO: bamboo
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\event\block\BlockMeltEvent;
use function mt_rand;
class FrostedIce extends Ice{
@ -62,7 +63,7 @@ class FrostedIce extends Ice{
}
public function onRandomTick() : void{
if((!$this->checkAdjacentBlocks(4) or mt_rand(0, 2) === 0) and
if((!$this->checkAdjacentBlocks(4) || mt_rand(0, 2) === 0) &&
$this->position->getWorld()->getHighestAdjacentFullLightAt($this->position->x, $this->position->y, $this->position->z) >= 12 - $this->age){
if($this->tryMelt()){
foreach($this->getAllSides() as $block){
@ -84,11 +85,11 @@ class FrostedIce extends Ice{
$found = 0;
for($x = -1; $x <= 1; ++$x){
for($z = -1; $z <= 1; ++$z){
if($x === 0 and $z === 0){
if($x === 0 && $z === 0){
continue;
}
if(
$this->position->getWorld()->getBlockAt($this->position->x + $x, $this->position->y, $this->position->z + $z) instanceof FrostedIce and
$this->position->getWorld()->getBlockAt($this->position->x + $x, $this->position->y, $this->position->z + $z) instanceof FrostedIce &&
++$found >= $requirement
){
return true;
@ -105,7 +106,11 @@ class FrostedIce extends Ice{
*/
private function tryMelt() : bool{
if($this->age >= 3){
$this->position->getWorld()->useBreakOn($this->position);
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER());
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
return true;
}

View File

@ -29,6 +29,7 @@ use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function mt_rand;
class Furnace extends Opaque{
use FacesOppositePlacingPlayerTrait;
@ -73,7 +74,7 @@ class Furnace extends Opaque{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
$furnace = $this->position->getWorld()->getTile($this->position);
if($furnace instanceof TileFurnace and $furnace->canOpenWith($item->getCustomName())){
if($furnace instanceof TileFurnace && $furnace->canOpenWith($item->getCustomName())){
$player->setCurrentWindow($furnace->getInventory());
}
}
@ -83,7 +84,10 @@ class Furnace extends Opaque{
public function onScheduledUpdate() : void{
$furnace = $this->position->getWorld()->getTile($this->position);
if($furnace instanceof TileFurnace and $furnace->onUpdate()){
if($furnace instanceof TileFurnace && $furnace->onUpdate()){
if(mt_rand(1, 60) === 1){ //in vanilla this is between 1 and 5 seconds; try to average about 3
$this->position->getWorld()->addSound($this->position, $furnace->getFurnaceType()->getCookSound());
}
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1); //TODO: check this
}
}

View File

@ -33,6 +33,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\Random;
use pocketmine\world\generator\object\TallGrass as TallGrassObject;
use pocketmine\world\sound\ItemUseOnBlockSound;
use function mt_rand;
class Grass extends Opaque{
@ -53,7 +54,7 @@ class Grass extends Opaque{
public function onRandomTick() : void{
$lightAbove = $this->position->getWorld()->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z);
if($lightAbove < 4 and $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){
if($lightAbove < 4 && $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){
//grass dies
$ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT());
$ev->call();
@ -69,9 +70,9 @@ class Grass extends Opaque{
$b = $this->position->getWorld()->getBlockAt($x, $y, $z);
if(
!($b instanceof Dirt) or
$b->isCoarse() or
$this->position->getWorld()->getFullLightAt($x, $y + 1, $z) < 4 or
!($b instanceof Dirt) ||
$b->isCoarse() ||
$this->position->getWorld()->getFullLightAt($x, $y + 1, $z) < 4 ||
$this->position->getWorld()->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2
){
continue;
@ -97,12 +98,16 @@ class Grass extends Opaque{
return true;
}elseif($item instanceof Hoe){
$item->applyDamage(1);
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::FARMLAND());
$newBlock = VanillaBlocks::FARMLAND();
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
$this->position->getWorld()->setBlock($this->position, $newBlock);
return true;
}elseif($item instanceof Shovel and $this->getSide(Facing::UP)->getId() === BlockLegacyIds::AIR){
}elseif($item instanceof Shovel && $this->getSide(Facing::UP)->getId() === BlockLegacyIds::AIR){
$item->applyDamage(1);
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::GRASS_PATH());
$newBlock = VanillaBlocks::GRASS_PATH();
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
$this->position->getWorld()->setBlock($this->position, $newBlock);
return true;
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\event\block\BlockMeltEvent;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\player\Player;
@ -38,7 +39,7 @@ class Ice extends Transparent{
}
public function onBreak(Item $item, ?Player $player = null) : bool{
if(($player === null or $player->isSurvival()) and !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
if(($player === null || $player->isSurvival()) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::WATER());
return true;
}
@ -51,7 +52,11 @@ class Ice extends Transparent{
public function onRandomTick() : void{
if($this->position->getWorld()->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){
$this->position->getWorld()->useBreakOn($this->position);
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER());
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
}
}

View File

@ -86,7 +86,7 @@ class ItemFrame extends Flowable{
/** @return $this */
public function setFramedItem(?Item $item) : self{
if($item === null or $item->isNull()){
if($item === null || $item->isNull()){
$this->framedItem = null;
$this->itemRotation = 0;
}else{
@ -161,7 +161,7 @@ class ItemFrame extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face === Facing::DOWN or $face === Facing::UP or !$blockClicked->isSolid()){
if($face === Facing::DOWN || $face === Facing::UP || !$blockClicked->isSolid()){
return false;
}
@ -172,7 +172,7 @@ class ItemFrame extends Flowable{
public function getDropsForCompatibleTool(Item $item) : array{
$drops = parent::getDropsForCompatibleTool($item);
if($this->framedItem !== null and lcg_value() <= $this->itemDropChance){
if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){
$drops[] = clone $this->framedItem;
}

View File

@ -65,7 +65,7 @@ class Ladder extends Transparent{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockClicked->isTransparent() and Facing::axis($face) !== Axis::Y){
if(!$blockClicked->isTransparent() && Facing::axis($face) !== Axis::Y){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -77,11 +77,11 @@ class Lantern extends Transparent{
}
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())) and !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()))){
if(!$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->up())) && !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()))){
return false;
}
$this->hanging = ($face === Facing::DOWN or !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down())));
$this->hanging = ($face === Facing::DOWN || !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down())));
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -91,8 +91,6 @@ class Lava extends Liquid{
}
public function onEntityInside(Entity $entity) : bool{
$entity->fallDistance *= 0.5;
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
$entity->attack($ev);

View File

@ -96,7 +96,7 @@ class Leaves extends Transparent{
return true;
}
if($block->getId() === $this->getId() and $distance <= 4){
if($block->getId() === $this->getId() && $distance <= 4){
foreach(Facing::ALL as $side){
if($this->findLog($pos->getSide($side), $visited, $distance + 1)){
return true;
@ -108,7 +108,7 @@ class Leaves extends Transparent{
}
public function onNearbyBlockChange() : void{
if(!$this->noDecay and !$this->checkDecay){
if(!$this->noDecay && !$this->checkDecay){
$this->checkDecay = true;
$this->position->getWorld()->setBlock($this->position, $this, false);
}
@ -119,10 +119,10 @@ class Leaves extends Transparent{
}
public function onRandomTick() : void{
if(!$this->noDecay and $this->checkDecay){
if(!$this->noDecay && $this->checkDecay){
$ev = new LeavesDecayEvent($this);
$ev->call();
if($ev->isCancelled() or $this->findLog($this->position)){
if($ev->isCancelled() || $this->findLog($this->position)){
$this->checkDecay = false;
$this->position->getWorld()->setBlock($this->position, $this, false);
}else{
@ -145,7 +145,7 @@ class Leaves extends Transparent{
if(mt_rand(1, 20) === 1){ //Saplings
$drops[] = ItemFactory::getInstance()->get(ItemIds::SAPLING, $this->treeType->getMagicNumber());
}
if(($this->treeType->equals(TreeType::OAK()) or $this->treeType->equals(TreeType::DARK_OAK())) and mt_rand(1, 200) === 1){ //Apples
if(($this->treeType->equals(TreeType::OAK()) || $this->treeType->equals(TreeType::DARK_OAK())) && mt_rand(1, 200) === 1){ //Apples
$drops[] = VanillaItems::APPLE();
}

166
src/block/Lectern.php Normal file
View File

@ -0,0 +1,166 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\Lectern as TileLectern;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\item\Item;
use pocketmine\item\WritableBookBase;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\LecternPlaceBookSound;
use function count;
class Lectern extends Transparent{
use FacesOppositePlacingPlayerTrait;
use HorizontalFacingTrait;
protected int $viewedPage = 0;
protected ?WritableBookBase $book = null;
protected bool $producingSignal = false;
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03);
$this->producingSignal = ($stateMeta & BlockLegacyMetadata::LECTERN_FLAG_POWERED) !== 0;
}
public function writeStateToMeta() : int{
return BlockDataSerializer::writeLegacyHorizontalFacing($this->facing) | ($this->producingSignal ? BlockLegacyMetadata::LECTERN_FLAG_POWERED : 0);
}
public function readStateFromWorld() : void{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileLectern){
$this->viewedPage = $tile->getViewedPage();
$this->book = $tile->getBook();
}
}
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileLectern){
$tile->setViewedPage($this->viewedPage);
$tile->setBook($this->book);
}
}
public function getStateBitmask() : int{
return 0b111;
}
public function getFlammability() : int{
return 30;
}
public function getDrops(Item $item) : array{
$drops = parent::getDrops($item);
if($this->book !== null){
$drops[] = clone $this->book;
}
return $drops;
}
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 0.1)];
}
public function isProducingSignal() : bool{ return $this->producingSignal; }
/** @return $this */
public function setProducingSignal(bool $producingSignal) : self{
$this->producingSignal = $producingSignal;
return $this;
}
public function getViewedPage() : int{
return $this->viewedPage;
}
/** @return $this */
public function setViewedPage(int $viewedPage) : self{
$this->viewedPage = $viewedPage;
return $this;
}
public function getBook() : ?WritableBookBase{
return $this->book !== null ? clone $this->book : null;
}
/** @return $this */
public function setBook(?WritableBookBase $book) : self{
$this->book = $book !== null && !$book->isNull() ? (clone $book)->setCount(1) : null;
$this->viewedPage = 0;
return $this;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->book === null && $item instanceof WritableBookBase){
$this->position->getWorld()->setBlock($this->position, $this->setBook($item));
$this->position->getWorld()->addSound($this->position, new LecternPlaceBookSound());
$item->pop();
}
return true;
}
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
if($this->book !== null){
$this->position->getWorld()->dropItem($this->position->up(), $this->book);
$this->position->getWorld()->setBlock($this->position, $this->setBook(null));
}
return false;
}
public function onPageTurn(int $newPage) : bool{
if($newPage === $this->viewedPage){
return true;
}
if($this->book === null || $newPage >= count($this->book->getPages()) || $newPage < 0){
return false;
}
$this->viewedPage = $newPage;
if(!$this->producingSignal){
$this->producingSignal = true;
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
}
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
public function onScheduledUpdate() : void{
if($this->producingSignal){
$this->producingSignal = false;
$this->position->getWorld()->setBlock($this->position, $this);
}
}
}

View File

@ -134,7 +134,7 @@ abstract class Liquid extends Transparent{
abstract public function getBucketEmptySound() : Sound;
public function isSource() : bool{
return !$this->falling and $this->decay === 0;
return !$this->falling && $this->decay === 0;
}
/**
@ -157,7 +157,7 @@ abstract class Liquid extends Transparent{
}
protected function getEffectiveFlowDecay(Block $block) : int{
if(!($block instanceof Liquid) or !$block->isSameType($this)){
if(!($block instanceof Liquid) || !$block->isSameType($this)){
return -1;
}
@ -282,7 +282,7 @@ abstract class Liquid extends Transparent{
$newDecay = $smallestFlowDecay + $multiplier;
$falling = false;
if($newDecay >= 8 or $smallestFlowDecay < 0){
if($newDecay >= 8 || $smallestFlowDecay < 0){
$newDecay = -1;
}
@ -293,14 +293,14 @@ abstract class Liquid extends Transparent{
$minAdjacentSources = $this->getMinAdjacentSourcesToFormSource();
if($minAdjacentSources !== null && $this->adjacentSources >= $minAdjacentSources){
$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
if($bottomBlock->isSolid() or ($bottomBlock instanceof Liquid and $bottomBlock->isSameType($this) and $bottomBlock->isSource())){
if($bottomBlock->isSolid() || ($bottomBlock instanceof Liquid && $bottomBlock->isSameType($this) && $bottomBlock->isSource())){
$newDecay = 0;
$falling = false;
}
}
if($falling !== $this->falling or (!$falling and $newDecay !== $this->decay)){
if(!$falling and $newDecay < 0){
if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
if(!$falling && $newDecay < 0){
$world->setBlock($this->position, VanillaBlocks::AIR());
return;
}
@ -315,7 +315,7 @@ abstract class Liquid extends Transparent{
$this->flowIntoBlock($bottomBlock, 0, true);
if($this->isSource() or !$bottomBlock->canBeFlowedInto()){
if($this->isSource() || !$bottomBlock->canBeFlowedInto()){
if($this->falling){
$adjacentDecay = 1; //falling liquid behaves like source block
}else{
@ -334,7 +334,7 @@ abstract class Liquid extends Transparent{
}
protected function flowIntoBlock(Block $block, int $newFlowDecay, bool $falling) : void{
if($this->canFlowInto($block) and !($block instanceof Liquid)){
if($this->canFlowInto($block) && !($block instanceof Liquid)){
$new = clone $this;
$new->falling = $falling;
$new->decay = $falling ? 0 : $newFlowDecay;
@ -353,7 +353,7 @@ abstract class Liquid extends Transparent{
/** @phpstan-impure */
private function getSmallestFlowDecay(Block $block, int $decay) : int{
if(!($block instanceof Liquid) or !$block->isSameType($this)){
if(!($block instanceof Liquid) || !$block->isSameType($this)){
return $decay;
}
@ -384,8 +384,8 @@ abstract class Liquid extends Transparent{
protected function canFlowInto(Block $block) : bool{
return
$this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) and
$block->canBeFlowedInto() and
!($block instanceof Liquid and $block->isSource()); //TODO: I think this should only be liquids of the same type
$this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) &&
$block->canBeFlowedInto() &&
!($block instanceof Liquid && $block->isSource()); //TODO: I think this should only be liquids of the same type
}
}

View File

@ -39,7 +39,7 @@ class Magma extends Opaque{
}
public function onEntityInside(Entity $entity) : bool{
if($entity instanceof Living and !$entity->isSneaking()){
if($entity instanceof Living && !$entity->isSneaking()){
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
$entity->attack($ev);
}

View File

@ -53,7 +53,7 @@ class NetherPortal extends Transparent{
* @return $this
*/
public function setAxis(int $axis) : self{
if($axis !== Axis::X and $axis !== Axis::Z){
if($axis !== Axis::X && $axis !== Axis::Z){
throw new \InvalidArgumentException("Invalid axis");
}
$this->axis = $axis;

View File

@ -79,7 +79,7 @@ class NetherWartPlant extends Flowable{
}
public function onRandomTick() : void{
if($this->age < 3 and mt_rand(0, 10) === 0){ //Still growing
if($this->age < 3 && mt_rand(0, 10) === 0){ //Still growing
$block = clone $this;
$block->age++;
$ev = new BlockGrowEvent($this, $block);

View File

@ -59,7 +59,7 @@ class Note extends Opaque{
/** @return $this */
public function setPitch(int $pitch) : self{
if($pitch < self::MIN_PITCH or $pitch > self::MAX_PITCH){
if($pitch < self::MIN_PITCH || $pitch > self::MAX_PITCH){
throw new \InvalidArgumentException("Pitch must be in range " . self::MIN_PITCH . " - " . self::MAX_PITCH);
}
$this->pitch = $pitch;

45
src/block/Pumpkin.php Normal file
View File

@ -0,0 +1,45 @@
<?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\item\Item;
use pocketmine\item\Shears;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function in_array;
class Pumpkin extends Opaque{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){
$item->applyDamage(1);
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face));
$this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), VanillaItems::PUMPKIN_SEEDS()->setCount(1));
return true;
}
return false;
}
}

View File

@ -57,7 +57,7 @@ class RedstoneComparator extends Flowable{
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03);
$this->isSubtractMode = ($stateMeta & BlockLegacyMetadata::REDSTONE_COMPARATOR_FLAG_SUBTRACT) !== 0;
$this->powered = ($id === $this->idInfoFlattened->getSecondId() or ($stateMeta & BlockLegacyMetadata::REDSTONE_COMPARATOR_FLAG_POWERED) !== 0);
$this->powered = ($id === $this->idInfoFlattened->getSecondId() || ($stateMeta & BlockLegacyMetadata::REDSTONE_COMPARATOR_FLAG_POWERED) !== 0);
}
public function writeStateToMeta() : int{

View File

@ -68,7 +68,7 @@ class Sapling extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getId() === BlockLegacyIds::GRASS or $down->getId() === BlockLegacyIds::DIRT or $down->getId() === BlockLegacyIds::FARMLAND){
if($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::FARMLAND){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
@ -96,7 +96,7 @@ class Sapling extends Flowable{
}
public function onRandomTick() : void{
if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 and mt_rand(1, 7) === 1){
if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 && mt_rand(1, 7) === 1){
if($this->ready){
$this->grow(null);
}else{

View File

@ -83,12 +83,12 @@ class SeaPickle extends Transparent{
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 and $blockReplace->count < 4) or parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
return ($blockReplace instanceof SeaPickle && $blockReplace->count < 4) || 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 and $blockReplace->count < 4){
if($blockReplace instanceof SeaPickle && $blockReplace->count < 4){
$this->count = $blockReplace->count + 1;
}

View File

@ -91,7 +91,7 @@ class ShulkerBox extends Opaque{
$shulker = $this->position->getWorld()->getTile($this->position);
if($shulker instanceof TileShulkerBox){
if(
$this->getSide($this->facing)->getId() !== BlockLegacyIds::AIR or
$this->getSide($this->facing)->getId() !== BlockLegacyIds::AIR ||
!$shulker->canOpenWith($item->getCustomName())
){
return true;

View File

@ -140,7 +140,7 @@ class Skull extends Flowable{
}
$this->facing = $face;
if($player !== null and $face === Facing::UP){
if($player !== null && $face === Facing::UP){
$this->rotation = ((int) floor(($player->getLocation()->getYaw() * 16 / 360) + 0.5)) & 0xf;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);

View File

@ -90,11 +90,11 @@ class Slab extends Transparent{
return true;
}
if($blockReplace instanceof Slab and !$blockReplace->slabType->equals(SlabType::DOUBLE()) and $blockReplace->isSameType($this)){
if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->isSameType($this)){
if($blockReplace->slabType->equals(SlabType::TOP())){ //Trying to combine with top slab
return $clickVector->y <= 0.5 or (!$isClickedBlock and $face === Facing::UP);
return $clickVector->y <= 0.5 || (!$isClickedBlock && $face === Facing::UP);
}else{
return $clickVector->y >= 0.5 or (!$isClickedBlock and $face === Facing::DOWN);
return $clickVector->y >= 0.5 || (!$isClickedBlock && $face === Facing::DOWN);
}
}
@ -102,9 +102,9 @@ class Slab extends Transparent{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockReplace instanceof Slab and !$blockReplace->slabType->equals(SlabType::DOUBLE()) and $blockReplace->isSameType($this) and (
($blockReplace->slabType->equals(SlabType::TOP()) and ($clickVector->y <= 0.5 or $face === Facing::UP)) or
($blockReplace->slabType->equals(SlabType::BOTTOM()) and ($clickVector->y >= 0.5 or $face === Facing::DOWN))
if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->isSameType($this) && (
($blockReplace->slabType->equals(SlabType::TOP()) && ($clickVector->y <= 0.5 || $face === Facing::UP)) ||
($blockReplace->slabType->equals(SlabType::BOTTOM()) && ($clickVector->y >= 0.5 || $face === Facing::DOWN))
)){
//Clicked in empty half of existing slab
$this->slabType = SlabType::DOUBLE();

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\event\block\BlockMeltEvent;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\AxisAlignedBB;
@ -77,7 +78,7 @@ class SnowLayer extends Flowable implements Fallable{
}
private function canBeSupportedBy(Block $b) : bool{
return $b->isSolid() or ($b instanceof SnowLayer and $b->isSameType($this) and $b->layers === 8);
return $b->isSolid() || ($b instanceof SnowLayer && $b->isSameType($this) && $b->layers === 8);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@ -100,7 +101,11 @@ class SnowLayer extends Flowable implements Fallable{
public function onRandomTick() : void{
if($this->position->getWorld()->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR(), false);
$ev = new BlockMeltEvent($this, VanillaBlocks::AIR());
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -96,9 +96,9 @@ class Stair extends Transparent{
->trim(Facing::opposite($topStepFace), 0.5)
->trim(Facing::opposite($this->facing), 0.5);
if($this->shape->equals(StairShape::OUTER_LEFT()) or $this->shape->equals(StairShape::OUTER_RIGHT())){
if($this->shape->equals(StairShape::OUTER_LEFT()) || $this->shape->equals(StairShape::OUTER_RIGHT())){
$topStep->trim(Facing::rotateY($this->facing, $this->shape->equals(StairShape::OUTER_LEFT())), 0.5);
}elseif($this->shape->equals(StairShape::INNER_LEFT()) or $this->shape->equals(StairShape::INNER_RIGHT())){
}elseif($this->shape->equals(StairShape::INNER_LEFT()) || $this->shape->equals(StairShape::INNER_RIGHT())){
//add an extra cube
$bbs[] = AxisAlignedBB::one()
->trim(Facing::opposite($topStepFace), 0.5)
@ -114,8 +114,8 @@ class Stair extends Transparent{
private function getPossibleCornerFacing(bool $oppositeFacing) : ?int{
$side = $this->getSide($oppositeFacing ? Facing::opposite($this->facing) : $this->facing);
return (
$side instanceof Stair and
$side->upsideDown === $this->upsideDown and
$side instanceof Stair &&
$side->upsideDown === $this->upsideDown &&
Facing::axis($side->facing) !== Facing::axis($this->facing) //perpendicular
) ? $side->facing : null;
}
@ -124,7 +124,7 @@ class Stair extends Transparent{
if($player !== null){
$this->facing = $player->getHorizontalFacing();
}
$this->upsideDown = (($clickVector->y > 0.5 and $face !== Facing::UP) or $face === Facing::DOWN);
$this->upsideDown = (($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN);
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -53,7 +53,7 @@ abstract class Stem extends Crops{
$side = $this->getSide(Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]);
$d = $side->getSide(Facing::DOWN);
if($side->getId() === BlockLegacyIds::AIR and ($d->getId() === BlockLegacyIds::FARMLAND or $d->getId() === BlockLegacyIds::GRASS or $d->getId() === BlockLegacyIds::DIRT)){
if($side->getId() === BlockLegacyIds::AIR && ($d->getId() === BlockLegacyIds::FARMLAND || $d->getId() === BlockLegacyIds::GRASS || $d->getId() === BlockLegacyIds::DIRT)){
$ev = new BlockGrowEvent($side, $grow);
$ev->call();
if(!$ev->isCancelled()){

View File

@ -97,7 +97,7 @@ class Sugarcane extends Flowable{
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if($down->isTransparent() and !$down->isSameType($this)){
if($down->isTransparent() && !$down->isSameType($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
@ -121,7 +121,7 @@ class Sugarcane extends Flowable{
$down = $this->getSide(Facing::DOWN);
if($down->isSameType($this)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($down->getId() === BlockLegacyIds::GRASS or $down->getId() === BlockLegacyIds::DIRT or $down->getId() === BlockLegacyIds::SAND or $down->getId() === BlockLegacyIds::PODZOL){
}elseif($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::SAND || $down->getId() === BlockLegacyIds::PODZOL){
foreach(Facing::HORIZONTAL as $side){
if($down->getSide($side) instanceof Water){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);

View File

@ -135,7 +135,7 @@ class SweetBerryBush extends Flowable{
}
public function onRandomTick() : void{
if($this->age < self::STAGE_MATURE and mt_rand(0, 2) === 1){
if($this->age < self::STAGE_MATURE && mt_rand(0, 2) === 1){
$block = clone $this;
++$block->age;
$ev = new BlockGrowEvent($this, $block);

View File

@ -86,7 +86,7 @@ class TNT extends Opaque{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($item instanceof FlintSteel or $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($item instanceof FlintSteel || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($item instanceof Durable){
$item->applyDamage(1);
}
@ -102,7 +102,7 @@ class TNT extends Opaque{
}
public function onEntityInside(Entity $entity) : bool{
if($entity instanceof Arrow and $entity->isOnFire()){
if($entity instanceof Arrow && $entity->isOnFire()){
$this->ignite();
return false;
}

View File

@ -39,7 +39,7 @@ class TallGrass extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN)->getId();
if($down === BlockLegacyIds::GRASS or $down === BlockLegacyIds::DIRT){
if($down === BlockLegacyIds::GRASS || $down === BlockLegacyIds::DIRT){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -37,7 +37,7 @@ class Thin extends Transparent{
foreach(Facing::HORIZONTAL as $facing){
$side = $this->getSide($facing);
if($side instanceof Thin or $side->isFullCube()){
if($side instanceof Thin || $side->isFullCube()){
$this->connections[$facing] = true;
}else{
unset($this->connections[$facing]);
@ -51,7 +51,7 @@ class Thin extends Transparent{
/** @var AxisAlignedBB[] $bbs */
$bbs = [];
if(isset($this->connections[Facing::WEST]) or isset($this->connections[Facing::EAST])){
if(isset($this->connections[Facing::WEST]) || isset($this->connections[Facing::EAST])){
$bb = AxisAlignedBB::one()->squash(Axis::Z, $inset);
if(!isset($this->connections[Facing::WEST])){
@ -62,7 +62,7 @@ class Thin extends Transparent{
$bbs[] = $bb;
}
if(isset($this->connections[Facing::NORTH]) or isset($this->connections[Facing::SOUTH])){
if(isset($this->connections[Facing::NORTH]) || isset($this->connections[Facing::SOUTH])){
$bb = AxisAlignedBB::one()->squash(Axis::X, $inset);
if(!isset($this->connections[Facing::NORTH])){

View File

@ -66,16 +66,16 @@ class Torch extends Flowable{
$below = $this->getSide(Facing::DOWN);
$face = Facing::opposite($this->facing);
if($this->getSide($face)->isTransparent() and !($face === Facing::DOWN and ($below->getId() === BlockLegacyIds::FENCE or $below->getId() === BlockLegacyIds::COBBLESTONE_WALL))){
if($this->getSide($face)->isTransparent() && !($face === Facing::DOWN && ($below->getId() === BlockLegacyIds::FENCE || $below->getId() === BlockLegacyIds::COBBLESTONE_WALL))){
$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() and !$blockClicked->getSide(Facing::DOWN)->isTransparent()){
if($blockClicked->canBeReplaced() && !$blockClicked->getSide(Facing::DOWN)->isTransparent()){
$this->facing = Facing::UP;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($face !== Facing::DOWN and (!$blockClicked->isTransparent() or ($face === Facing::UP and ($blockClicked->getId() === BlockLegacyIds::FENCE or $blockClicked->getId() === BlockLegacyIds::COBBLESTONE_WALL)))){
}elseif($face !== Facing::DOWN && (!$blockClicked->isTransparent() || ($face === Facing::UP && ($blockClicked->getId() === BlockLegacyIds::FENCE || $blockClicked->getId() === BlockLegacyIds::COBBLESTONE_WALL)))){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}else{

View File

@ -82,7 +82,7 @@ class Trapdoor extends Transparent{
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
if(($clickVector->y > 0.5 and $face !== Facing::UP) or $face === Facing::DOWN){
if(($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN){
$this->top = true;
}

View File

@ -363,6 +363,7 @@ use pocketmine\utils\CloningRegistryTrait;
* @method static LapisOre LAPIS_LAZULI_ORE()
* @method static DoubleTallGrass LARGE_FERN()
* @method static Lava LAVA()
* @method static Lectern LECTERN()
* @method static Opaque LEGACY_STONECUTTER()
* @method static Lever LEVER()
* @method static GlazedTerracotta LIGHT_BLUE_GLAZED_TERRACOTTA()
@ -445,7 +446,7 @@ use pocketmine\utils\CloningRegistryTrait;
* @method static Slab PRISMARINE_SLAB()
* @method static Stair PRISMARINE_STAIRS()
* @method static Wall PRISMARINE_WALL()
* @method static Opaque PUMPKIN()
* @method static Pumpkin PUMPKIN()
* @method static PumpkinStem PUMPKIN_STEM()
* @method static GlazedTerracotta PURPLE_GLAZED_TERRACOTTA()
* @method static Torch PURPLE_TORCH()
@ -924,6 +925,7 @@ final class VanillaBlocks{
self::register("lapis_lazuli_ore", $factory->get(21, 0));
self::register("large_fern", $factory->get(175, 3));
self::register("lava", $factory->get(10, 0));
self::register("lectern", $factory->get(449, 0));
self::register("legacy_stonecutter", $factory->get(245, 0));
self::register("lever", $factory->get(69, 0));
self::register("light_blue_glazed_terracotta", $factory->get(223, 2));

View File

@ -116,7 +116,7 @@ class Vine extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockClicked->isSolid() or Facing::axis($face) === Axis::Y){
if(!$blockClicked->isSolid() || Facing::axis($face) === Axis::Y){
return false;
}
@ -134,7 +134,7 @@ class Vine extends Flowable{
$supportedFaces = $up instanceof Vine ? array_intersect_key($this->faces, $up->faces) : [];
foreach($this->faces as $face){
if(!isset($supportedFaces[$face]) and !$this->getSide($face)->isSolid()){
if(!isset($supportedFaces[$face]) && !$this->getSide($face)->isSolid()){
unset($this->faces[$face]);
$changed = true;
}

View File

@ -37,7 +37,7 @@ class Wall extends Transparent{
foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing);
if($block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent())){
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
$this->connections[$facing] = $facing;
}else{
unset($this->connections[$facing]);
@ -57,10 +57,10 @@ class Wall extends Transparent{
$inset = 0.25;
if(
!$this->up and //if there is a block on top, it stays as a post
!$this->up && //if there is a block on top, it stays as a post
(
($north and $south and !$west and !$east) or
(!$north and !$south and $west and $east)
($north && $south && !$west && !$east) ||
(!$north && !$south && $west && $east)
)
){
//If connected to two sides on the same axis but not any others, AND there is not a block on top, there is no post and the wall is thinner

View File

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

View File

@ -24,16 +24,29 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\inventory\BrewingStandInventory;
use pocketmine\crafting\BrewingRecipe;
use pocketmine\event\block\BrewingFuelUseEvent;
use pocketmine\event\block\BrewItemEvent;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\player\Player;
use pocketmine\world\sound\PotionFinishBrewingSound;
use pocketmine\world\World;
use function array_map;
use function count;
class BrewingStand extends Spawnable implements Container, Nameable{
use NameableTrait {
addAdditionalSpawnData as addNameSpawnData;
}
use ContainerTrait;
use NameableTrait;
public const BREW_TIME_TICKS = 400; // Brew time in ticks
private const TAG_BREW_TIME = "BrewTime"; //TAG_Short
private const TAG_BREW_TIME_PE = "CookTime"; //TAG_Short
@ -41,15 +54,11 @@ class BrewingStand extends Spawnable implements Container, Nameable{
private const TAG_REMAINING_FUEL_TIME = "Fuel"; //TAG_Byte
private const TAG_REMAINING_FUEL_TIME_PE = "FuelAmount"; //TAG_Short
/** @var BrewingStandInventory */
private $inventory;
private BrewingStandInventory $inventory;
/** @var int */
private $brewTime = 0;
/** @var int */
private $maxFuelTime = 0;
/** @var int */
private $remainingFuelTime = 0;
private int $brewTime = 0;
private int $maxFuelTime = 0;
private int $remainingFuelTime = 0;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
@ -83,6 +92,14 @@ class BrewingStand extends Spawnable implements Container, Nameable{
$nbt->setShort(self::TAG_REMAINING_FUEL_TIME_PE, $this->remainingFuelTime);
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$this->addNameSpawnData($nbt);
$nbt->setShort(self::TAG_BREW_TIME_PE, $this->brewTime);
$nbt->setShort(self::TAG_MAX_FUEL_TIME, $this->maxFuelTime);
$nbt->setShort(self::TAG_REMAINING_FUEL_TIME_PE, $this->remainingFuelTime);
}
public function getDefaultName() : string{
return "Brewing Stand";
}
@ -108,4 +125,135 @@ class BrewingStand extends Spawnable implements Container, Nameable{
public function getRealInventory(){
return $this->inventory;
}
private function checkFuel(Item $item) : void{
$ev = new BrewingFuelUseEvent($this);
if(!$item->equals(VanillaItems::BLAZE_POWDER(), true, false)){
$ev->cancel();
}
$ev->call();
if($ev->isCancelled()){
return;
}
$item->pop();
$this->inventory->setItem(BrewingStandInventory::SLOT_FUEL, $item);
$this->maxFuelTime = $this->remainingFuelTime = $ev->getFuelTime();
}
/**
* @return BrewingRecipe[]
* @phpstan-return array<int, BrewingRecipe>
*/
private function getBrewableRecipes() : array{
if($this->inventory->getItem(BrewingStandInventory::SLOT_INGREDIENT)->isNull()){
return [];
}
$recipes = [];
foreach([BrewingStandInventory::SLOT_BOTTLE_LEFT, BrewingStandInventory::SLOT_BOTTLE_MIDDLE, BrewingStandInventory::SLOT_BOTTLE_RIGHT] as $slot){
$input = $this->inventory->getItem($slot);
if($input->isNull()){
continue;
}
if(($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->matchBrewingRecipe($input, $this->inventory->getItem(BrewingStandInventory::SLOT_INGREDIENT))) !== null){
$recipes[$slot] = $recipe;
}
}
return $recipes;
}
public function onUpdate() : bool{
if($this->closed){
return false;
}
$this->timings->startTiming();
$prevBrewTime = $this->brewTime;
$prevRemainingFuelTime = $this->remainingFuelTime;
$prevMaxFuelTime = $this->maxFuelTime;
$ret = false;
$fuel = $this->inventory->getItem(BrewingStandInventory::SLOT_FUEL);
$ingredient = $this->inventory->getItem(BrewingStandInventory::SLOT_INGREDIENT);
$recipes = $this->getBrewableRecipes();
$canBrew = count($recipes) !== 0;
if($this->remainingFuelTime <= 0 && $canBrew){
$this->checkFuel($fuel);
}
if($this->remainingFuelTime > 0){
if($canBrew){
if($this->brewTime === 0){
$this->brewTime = self::BREW_TIME_TICKS;
--$this->remainingFuelTime;
}
--$this->brewTime;
if($this->brewTime <= 0){
$anythingBrewed = false;
foreach($recipes as $slot => $recipe){
$input = $this->inventory->getItem($slot);
$output = $recipe->getResultFor($input);
if($output === null){
continue;
}
$ev = new BrewItemEvent($this, $slot, $input, $output, $recipe);
$ev->call();
if($ev->isCancelled()){
continue;
}
$this->inventory->setItem($slot, $ev->getResult());
$anythingBrewed = true;
}
if($anythingBrewed){
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new PotionFinishBrewingSound());
}
$ingredient->pop();
$this->inventory->setItem(BrewingStandInventory::SLOT_INGREDIENT, $ingredient);
$this->brewTime = 0;
}else{
$ret = true;
}
}else{
$this->brewTime = 0;
}
}else{
$this->brewTime = $this->remainingFuelTime = $this->maxFuelTime = 0;
}
$viewers = array_map(fn(Player $p) => $p->getNetworkSession()->getInvManager(), $this->inventory->getViewers());
foreach($viewers as $v){
if($v === null){
continue;
}
if($prevBrewTime !== $this->brewTime){
$v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_BREWING_STAND_BREW_TIME, $this->brewTime);
}
if($prevRemainingFuelTime !== $this->remainingFuelTime){
$v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_BREWING_STAND_FUEL_AMOUNT, $this->remainingFuelTime);
}
if($prevMaxFuelTime !== $this->maxFuelTime){
$v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_BREWING_STAND_FUEL_TOTAL, $this->maxFuelTime);
}
}
$this->timings->stopTiming();
return $ret;
}
}

View File

@ -60,12 +60,12 @@ class Chest extends Spawnable implements Container, Nameable{
}
public function readSaveData(CompoundTag $nbt) : void{
if(($pairXTag = $nbt->getTag(self::TAG_PAIRX)) instanceof IntTag and ($pairZTag = $nbt->getTag(self::TAG_PAIRZ)) instanceof IntTag){
if(($pairXTag = $nbt->getTag(self::TAG_PAIRX)) instanceof IntTag && ($pairZTag = $nbt->getTag(self::TAG_PAIRZ)) instanceof IntTag){
$pairX = $pairXTag->getValue();
$pairZ = $pairZTag->getValue();
if(
($this->position->x === $pairX and abs($this->position->z - $pairZ) === 1) or
($this->position->z === $pairZ and abs($this->position->x - $pairX) === 1)
($this->position->x === $pairX && abs($this->position->z - $pairZ) === 1) ||
($this->position->z === $pairZ && abs($this->position->x - $pairX) === 1)
){
$this->pairX = $pairX;
$this->pairZ = $pairZ;
@ -100,7 +100,7 @@ class Chest extends Spawnable implements Container, Nameable{
$this->inventory->removeAllViewers();
if($this->doubleInventory !== null){
if($this->isPaired() and $this->position->getWorld()->isChunkLoaded($this->pairX >> Chunk::COORD_BIT_SIZE, $this->pairZ >> Chunk::COORD_BIT_SIZE)){
if($this->isPaired() && $this->position->getWorld()->isChunkLoaded($this->pairX >> Chunk::COORD_BIT_SIZE, $this->pairZ >> Chunk::COORD_BIT_SIZE)){
$this->doubleInventory->removeAllViewers();
if(($pair = $this->getPair()) !== null){
$pair->doubleInventory = null;
@ -122,7 +122,7 @@ class Chest extends Spawnable implements Container, Nameable{
* @return ChestInventory|DoubleChestInventory
*/
public function getInventory(){
if($this->isPaired() and $this->doubleInventory === null){
if($this->isPaired() && $this->doubleInventory === null){
$this->checkPairing();
}
return $this->doubleInventory instanceof DoubleChestInventory ? $this->doubleInventory : $this->inventory;
@ -136,7 +136,7 @@ class Chest extends Spawnable implements Container, Nameable{
}
protected function checkPairing() : void{
if($this->isPaired() and !$this->position->getWorld()->isInLoadedTerrain(new Vector3($this->pairX, $this->position->y, $this->pairZ))){
if($this->isPaired() && !$this->position->getWorld()->isInLoadedTerrain(new Vector3($this->pairX, $this->position->y, $this->pairZ))){
//paired to a tile in an unloaded chunk
$this->doubleInventory = null;
@ -167,7 +167,7 @@ class Chest extends Spawnable implements Container, Nameable{
}
public function isPaired() : bool{
return $this->pairX !== null and $this->pairZ !== null;
return $this->pairX !== null && $this->pairZ !== null;
}
public function getPair() : ?Chest{
@ -182,7 +182,7 @@ class Chest extends Spawnable implements Container, Nameable{
}
public function pairWith(Chest $tile) : bool{
if($this->isPaired() or $tile->isPaired()){
if($this->isPaired() || $tile->isPaired()){
return false;
}

View File

@ -81,7 +81,7 @@ trait ContainerTrait{
* @see Container::canOpenWith()
*/
public function canOpenWith(string $key) : bool{
return $this->lock === null or $this->lock === $key;
return $this->lock === null || $this->lock === $key;
}
/**

View File

@ -42,7 +42,7 @@ class FlowerPot extends Spawnable{
private $plant = null;
public function readSaveData(CompoundTag $nbt) : void{
if(($itemIdTag = $nbt->getTag(self::TAG_ITEM)) instanceof ShortTag and ($itemMetaTag = $nbt->getTag(self::TAG_ITEM_DATA)) instanceof IntTag){
if(($itemIdTag = $nbt->getTag(self::TAG_ITEM)) instanceof ShortTag && ($itemMetaTag = $nbt->getTag(self::TAG_ITEM_DATA)) instanceof IntTag){
try{
$this->setPlant(BlockFactory::getInstance()->get($itemIdTag->getValue(), $itemMetaTag->getValue()));
}catch(\InvalidArgumentException $e){
@ -65,7 +65,7 @@ class FlowerPot extends Spawnable{
}
public function setPlant(?Block $plant) : void{
if($plant === null or $plant instanceof Air){
if($plant === null || $plant instanceof Air){
$this->plant = null;
}else{
$this->plant = clone $plant;

View File

@ -132,14 +132,14 @@ abstract class Furnace extends Spawnable implements Container, Nameable{
$this->maxFuelTime = $this->remainingFuelTime = $ev->getBurnTime();
$this->onStartSmelting();
if($this->remainingFuelTime > 0 and $ev->isBurning()){
if($this->remainingFuelTime > 0 && $ev->isBurning()){
$this->inventory->setFuel($fuel->getFuelResidue());
}
}
protected function onStartSmelting() : void{
$block = $this->getBlock();
if($block instanceof BlockFurnace and !$block->isLit()){
if($block instanceof BlockFurnace && !$block->isLit()){
$block->setLit(true);
$this->position->getWorld()->setBlock($block->getPosition(), $block);
}
@ -147,7 +147,7 @@ abstract class Furnace extends Spawnable implements Container, Nameable{
protected function onStopSmelting() : void{
$block = $this->getBlock();
if($block instanceof BlockFurnace and $block->isLit()){
if($block instanceof BlockFurnace && $block->isLit()){
$block->setLit(false);
$this->position->getWorld()->setBlock($block->getPosition(), $block);
}
@ -175,16 +175,16 @@ abstract class Furnace extends Spawnable implements Container, Nameable{
$furnaceType = $this->getFurnaceType();
$smelt = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($raw);
$canSmelt = ($smelt instanceof FurnaceRecipe and $raw->getCount() > 0 and (($smelt->getResult()->equals($product) and $product->getCount() < $product->getMaxStackSize()) or $product->isNull()));
$canSmelt = ($smelt instanceof FurnaceRecipe && $raw->getCount() > 0 && (($smelt->getResult()->equals($product) && $product->getCount() < $product->getMaxStackSize()) || $product->isNull()));
if($this->remainingFuelTime <= 0 and $canSmelt and $fuel->getFuelTime() > 0 and $fuel->getCount() > 0){
if($this->remainingFuelTime <= 0 && $canSmelt && $fuel->getFuelTime() > 0 && $fuel->getCount() > 0){
$this->checkFuel($fuel);
}
if($this->remainingFuelTime > 0){
--$this->remainingFuelTime;
if($smelt instanceof FurnaceRecipe and $canSmelt){
if($smelt instanceof FurnaceRecipe && $canSmelt){
++$this->cookTime;
if($this->cookTime >= $furnaceType->getCookDurationTicks()){

View File

@ -24,7 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
@ -46,7 +46,7 @@ class ItemFrame extends Spawnable{
private $itemDropChance = 1.0;
public function __construct(World $world, Vector3 $pos){
$this->item = ItemFactory::air();
$this->item = VanillaItems::AIR();
parent::__construct($world, $pos);
}
@ -73,10 +73,10 @@ class ItemFrame extends Spawnable{
}
public function setItem(?Item $item) : void{
if($item !== null and !$item->isNull()){
if($item !== null && !$item->isNull()){
$this->item = clone $item;
}else{
$this->item = ItemFactory::air();
$this->item = VanillaItems::AIR();
}
}

View File

@ -0,0 +1,87 @@
<?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\tile;
use pocketmine\item\Item;
use pocketmine\item\WritableBookBase;
use pocketmine\nbt\tag\CompoundTag;
use function count;
/**
* @deprecated
* @see \pocketmine\block\Lectern
*/
class Lectern extends Spawnable{
public const TAG_HAS_BOOK = "hasBook";
public const TAG_PAGE = "page";
public const TAG_TOTAL_PAGES = "totalPages";
public const TAG_BOOK = "book";
private int $viewedPage = 0;
private ?WritableBookBase $book = null;
public function readSaveData(CompoundTag $nbt) : void{
$this->viewedPage = $nbt->getInt(self::TAG_PAGE, 0);
if(($itemTag = $nbt->getCompoundTag(self::TAG_BOOK)) !== null){
$book = Item::nbtDeserialize($itemTag);
if($book instanceof WritableBookBase && !$book->isNull()){
$this->book = $book;
}
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_HAS_BOOK, $this->book !== null ? 1 : 0);
$nbt->setInt(self::TAG_PAGE, $this->viewedPage);
if($this->book !== null){
$nbt->setTag(self::TAG_BOOK, $this->book->nbtSerialize());
$nbt->setInt(self::TAG_TOTAL_PAGES, count($this->book->getPages()));
}
}
public function getViewedPage() : int{
return $this->viewedPage;
}
public function setViewedPage(int $viewedPage) : void{
$this->viewedPage = $viewedPage;
}
public function getBook() : ?WritableBookBase{
return $this->book !== null ? clone $this->book : null;
}
public function setBook(?WritableBookBase $book) : void{
$this->book = $book !== null && !$book->isNull() ? clone $book : null;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_HAS_BOOK, $this->book !== null ? 1 : 0);
$nbt->setInt(self::TAG_PAGE, $this->viewedPage);
if($this->book !== null){
$nbt->setTag(self::TAG_BOOK, $this->book->nbtSerialize());
$nbt->setInt(self::TAG_TOTAL_PAGES, count($this->book->getPages()));
}
}
}

View File

@ -34,7 +34,7 @@ class Note extends Tile{
private $pitch = 0;
public function readSaveData(CompoundTag $nbt) : void{
if(($pitch = $nbt->getByte("note", $this->pitch)) > BlockNote::MIN_PITCH and $pitch <= BlockNote::MAX_PITCH){
if(($pitch = $nbt->getByte("note", $this->pitch)) > BlockNote::MIN_PITCH && $pitch <= BlockNote::MAX_PITCH){
$this->pitch = $pitch;
}
}
@ -48,7 +48,7 @@ class Note extends Tile{
}
public function setPitch(int $pitch) : void{
if($pitch < BlockNote::MIN_PITCH or $pitch > BlockNote::MAX_PITCH){
if($pitch < BlockNote::MIN_PITCH || $pitch > BlockNote::MAX_PITCH){
throw new \InvalidArgumentException("Pitch must be in range " . BlockNote::MIN_PITCH . " - " . BlockNote::MAX_PITCH);
}
$this->pitch = $pitch;

View File

@ -59,7 +59,7 @@ class Skull extends Spawnable{
}
}
$rotation = $nbt->getByte(self::TAG_ROT, 0);
if($rotation >= 0 and $rotation <= 15){
if($rotation >= 0 && $rotation <= 15){
$this->skullRotation = $rotation;
}
}

View File

@ -67,6 +67,7 @@ final class TileFactory{
$this->register(Hopper::class, ["Hopper", "minecraft:hopper"]);
$this->register(ItemFrame::class, ["ItemFrame"]); //this is an entity in PC
$this->register(Jukebox::class, ["Jukebox", "RecordPlayer", "minecraft:jukebox"]);
$this->register(Lectern::class, ["Lectern", "minecraft:lectern"]);
$this->register(MonsterSpawner::class, ["MobSpawner", "minecraft:mob_spawner"]);
$this->register(Note::class, ["Music", "minecraft:noteblock"]);
$this->register(ShulkerBox::class, ["ShulkerBox", "minecraft:shulker_box"]);
@ -85,7 +86,6 @@ final class TileFactory{
//TODO: EndGateway
//TODO: EndPortal
//TODO: JigsawBlock
//TODO: Lectern
//TODO: MovingBlock
//TODO: NetherReactor
//TODO: PistonArm

View File

@ -150,7 +150,7 @@ final class BlockDataSerializer{
}
public static function readBoundedInt(string $name, int $v, int $min, int $max) : int{
if($v < $min or $v > $max){
if($v < $min || $v > $max){
throw new InvalidBlockStateException("$name should be in range $min - $max, got $v");
}
return $v;

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\inventory\BrewingStandInventory;
use pocketmine\utils\EnumTrait;
/**
@ -36,13 +37,24 @@ use pocketmine\utils\EnumTrait;
* @method static BrewingStandSlot SOUTHWEST()
*/
final class BrewingStandSlot{
use EnumTrait;
use EnumTrait {
__construct as Enum___construct;
}
protected static function setup() : void{
self::registerAll(
new self("east"),
new self("northwest"),
new self("southwest")
new self("east", BrewingStandInventory::SLOT_BOTTLE_LEFT),
new self("northwest", BrewingStandInventory::SLOT_BOTTLE_MIDDLE),
new self("southwest", BrewingStandInventory::SLOT_BOTTLE_RIGHT)
);
}
private function __construct(string $enumName, private int $slotNumber){
$this->Enum___construct($enumName);
}
/**
* Returns the brewing stand inventory slot number associated with this visual slot.
*/
public function getSlotNumber() : int{ return $this->slotNumber; }
}

View File

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

View File

@ -50,7 +50,7 @@ trait FallableTrait{
public function onNearbyBlockChange() : void{
$pos = $this->getPosition();
$down = $pos->getWorld()->getBlock($pos->getSide(Facing::DOWN));
if($down->getId() === BlockLegacyIds::AIR or $down instanceof Liquid or $down instanceof Fire){
if($down->getId() === BlockLegacyIds::AIR || $down instanceof Liquid || $down instanceof Fire){
$pos->getWorld()->setBlock($pos, VanillaBlocks::AIR());
$block = $this;

View File

@ -55,7 +55,7 @@ final class MinimumCostFlowCalculator{
$cost = 1000;
foreach(Facing::HORIZONTAL as $j){
if($j === $originOpposite or $j === $lastOpposite){
if($j === $originOpposite || $j === $lastOpposite){
continue;
}

View File

@ -89,7 +89,7 @@ class SignText{
if(!is_int($index)){
throw new \InvalidArgumentException("Index must be an integer");
}
if($index < 0 or $index >= self::LINE_COUNT){
if($index < 0 || $index >= self::LINE_COUNT){
throw new \InvalidArgumentException("Line index is out of bounds");
}
}

View File

@ -127,7 +127,7 @@ abstract class Command{
public function testPermissionSilent(CommandSender $target, ?string $permission = null) : bool{
$permission ??= $this->permission;
if($permission === null or $permission === ""){
if($permission === null || $permission === ""){
return true;
}
@ -182,7 +182,7 @@ abstract class Command{
}
private function allowChangesFrom(CommandMap $commandMap) : bool{
return $this->commandMap === null or $this->commandMap === $commandMap;
return $this->commandMap === null || $this->commandMap === $commandMap;
}
public function isRegistered() : bool{
@ -235,7 +235,7 @@ abstract class Command{
$result = KnownTranslationFactory::chat_type_admin($source->getName(), $message);
$colored = $result->prefix(TextFormat::GRAY . TextFormat::ITALIC);
if($sendToSource and !($source instanceof ConsoleCommandSender)){
if($sendToSource && !($source instanceof ConsoleCommandSender)){
$source->sendMessage($message);
}

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