Compare commits

..

482 Commits

Author SHA1 Message Date
e7d17eb4d3 Release 4.0.5 2022-01-04 20:51:36 +00:00
73168a0e39 Merge branch 'legacy/pm3' into stable 2022-01-04 20:49:32 +00:00
e8893dd91f 3.26.6 is next 2022-01-04 20:47:31 +00:00
a4af1609ea Release 3.26.5 2022-01-04 20:47:31 +00:00
8c4b8a9042 CS 2022-01-04 20:44:10 +00:00
6492cac5c1 Merge pull request from GHSA-c6fg-99pr-25m9 2022-01-04 20:40:55 +00:00
958a9dbf0f Merge pull request from GHSA-c6fg-99pr-25m9
* Skin: impose length limits on skinID, geometryName and geometryData fields

* Skin: remove extra newline
2022-01-04 20:40:55 +00:00
3ed57ce49a Merge pull request from GHSA-p62j-hrxm-xcxf
This checks the following things:
- Validity of UTF-8 encoding of title, author, and page content
- Maximum soft and hard lengths of title, author, and page content (soft
  limits may be bypassed by uncancelling PlayerEditBookEvent; hard
  limits may not be bypassed)
- Maximum number of pages. Books with more than 50 pages may still be
  edited, but may not have new pages added.
2022-01-04 20:39:02 +00:00
68f3399cfd Merge pull request from GHSA-p62j-hrxm-xcxf
This checks the following things:
- Validity of UTF-8 encoding of title, author, and page content
- Maximum soft and hard lengths of title, author, and page content (soft
  limits may be bypassed by uncancelling PlayerEditBookEvent; hard
  limits may not be bypassed)
- Maximum number of pages. Books with more than 50 pages may still be
  edited, but may not have new pages added.
2022-01-04 20:39:02 +00:00
aeab19a616 Fixed world spawn point not updating to players (#4699)
closes #4383
2022-01-04 20:31:27 +00:00
7bee72ef2d Use ~ instead of ^ for constraints on BedrockData and BedrockProtocol
I got these two mixed up - they are exactly the opposite of what I thought. ~ is the stricter operator.
2022-01-04 00:54:09 +00:00
0d595e4324 Update Language dependency 2022-01-04 00:47:04 +00:00
e43e0189df InGamePacketHandler: do not pass bare integers from BookEditPacket directly into event
while these currently happen to be identical, they may not be in the future.

Really this should be represented by an enum.
2022-01-03 20:20:32 +00:00
decd1da2d0 BaseSign: remove dead TODO comment 2022-01-03 19:33:03 +00:00
bcc0f1e733 Fixed desynchronization of hunger when cancelling food-related events (#4691) 2022-01-03 19:11:32 +00:00
f62cfe8ae3 4.0.5 is next 2022-01-01 16:50:03 +00:00
b903e90dc2 Release 4.0.4 2022-01-01 16:50:02 +00:00
c8247786d7 Player: check chat length check with strlen() before mb_strlen()
mb_strlen() is O(n), whereas strlen() is O(1). If we receive very large chat messages (e.g. 2 MB), mb_strlen() will take a very long time to return a result (around 8ms on my machine).
Since the max size of a UTF-8 character is 4 bytes (according to standard), we can use strlen() with 4x the char limit to gate it and prevent this from happening.
2022-01-01 16:46:00 +00:00
f486b5f4a7 Player: fixed fall damage when sprinting down stairs (#4685)
Due to the way positions are updated over the network, we only see the end result of a movement and not its preceding actions. In addition, we don't know for sure whether the MCPE collision checks work the same exact way as PM.

TL;DR: It's possible for the client to capture and send a movement frame after they collided with a step and then already moved forward from it some distance, resulting in a weird arc pattern.

This PR checks the range between the old and new positions for collision boxes to ensure that all possible areas are checked for detecting fall damage.

This has been tested and successfully resolves various issues involving running down stairs:
- missing sounds
- random fall damage
2022-01-01 15:41:19 +00:00
54d6b83fc2 Entity: pass the appropriate value for AFFECTED_BY_GRAVITY 2022-01-01 15:39:46 +00:00
eedea38669 Improve performance of loading player inventories 2022-01-01 15:26:42 +00:00
3c6146b5e0 ContainerTrait: avoid absurdly inefficient use of setItem()
this substantially improves the performance of loading containers such as chests.
2022-01-01 15:05:32 +00:00
72f2c794ab SimpleInventory: improved performance of setContents()
avoid the overhead incurred by clear() and setItem(), because in internalSetContents(), we already have no listeners or viewers to talk to anyway, so this is just spamming shit into /dev/null.
2021-12-31 18:32:19 +00:00
38b6b39cb3 Filesystem: workaround a stupid Windows issue in safeFilePutContents()
occasionally Windows will randomly decide to deny us access to rename the file for no reason whatsoever. If this happens, we attempt an old-style copy and delete.
If the rename failed for a legit reason, the copy and delete should also fail and generate an error message. If it was Windows being a spaz, it should work normally without errors.
2021-12-29 15:26:34 +00:00
fcc4757209 Merge branch 'legacy/pm3' into stable 2021-12-27 21:54:56 +00:00
d9c70cb176 start.cmd: prevent idiotic behaviour when paths contain characters such as brackets
god I hate this shit so much
2021-12-27 21:54:32 +00:00
4aab0565c0 ChunkCache: fixed corner case in cache restart on AsyncTask error
the cache may have been destroyed since the task inception, leading to an exception being thrown.
2021-12-27 18:11:55 +00:00
8943d8a2a7 Player: fixed maximum message size limits to match vanilla bugrock 2021-12-27 16:51:47 +00:00
0da29beb1d Bump pocketmine/locale-data from 2.2.0 to 2.2.1 (#4667)
Bumps [pocketmine/locale-data](https://github.com/pmmp/Language) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/pmmp/Language/releases)
- [Commits](https://github.com/pmmp/Language/compare/2.2.0...2.2.1)

---
updated-dependencies:
- dependency-name: pocketmine/locale-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-27 16:43:32 +00:00
157048264c Bump phpunit/phpunit from 9.5.10 to 9.5.11 (#4675)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.10 to 9.5.11.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/master/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.10...9.5.11)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-27 16:43:19 +00:00
b55aa78aec Changelog: Replaced non-existent method (#4676) 2021-12-27 15:33:02 +00:00
091673d8f1 Fixed "You can only sleep at night" message (#4671) 2021-12-23 23:52:07 +00:00
e3614d1a82 Entity: fixed game performance issue with large scale entities
this->size refers to the scaled height, but the client wants the base (unscaled) size in these properties.
This caused immense lag when, for example, setting the scale of a player to 10, because their collision box would become 180 by 60, instead of the expected 18 by 6.
2021-12-18 22:38:45 +00:00
93caf72f34 KickCommand: Add missing space
closes #4660
closes #4661
2021-12-17 21:09:14 +00:00
e6e1bca676 4.0.4 is next 2021-12-16 01:35:43 +00:00
795ebd1824 Release 4.0.3 2021-12-16 01:35:42 +00:00
5f03887b47 Merge branch 'legacy/pm3' into stable 2021-12-16 01:34:10 +00:00
9979a64ad2 3.26.5 is next 2021-12-16 01:23:22 +00:00
75a72786f9 Release 3.26.4 2021-12-16 01:23:21 +00:00
3d205c6e5f Updated transient dependency junk 2021-12-16 01:20:05 +00:00
2955a92837 Updated pocketmine/nbt to 0.2.19 2021-12-16 01:19:30 +00:00
e70f81a111 Updated pocketmine/nbt to 0.3.2 2021-12-16 01:08:23 +00:00
57e1509c3a Updated translation APIs 2021-12-15 03:24:13 +00:00
0da1810aaa Updated composer dependencies 2021-12-15 03:12:26 +00:00
4d37b79ff7 Server: fixed not being able to deop players whose names were added to ops.txt with uppercase letters in them
same as iTXTech/Genisys#1204

why didn't anyone report this???
2021-12-15 01:08:59 +00:00
ea1fceece2 Merge branch 'legacy/pm3' into stable 2021-12-14 23:15:53 +00:00
7fb1669c6d php-cs-fixer: added binary_operator_spaces and unary_operator_spaces rules 2021-12-14 23:14:39 +00:00
929abb04be Merge branch 'legacy/pm3' into stable 2021-12-14 22:54:17 +00:00
a09817864b php-cs-fixer: add return_type_declaration space_before 2021-12-14 22:50:43 +00:00
45c4a9673d Player: fixed arm swing animation not showing during attack cooldown of victim
closes #4650
2021-12-14 19:03:42 +00:00
4ad8cb02a5 BlockIdentifier: ensure that the tile class given is valid 2021-12-14 17:36:25 +00:00
7e6bbcc393 Sync composer deps 2021-12-14 01:27:11 +00:00
c334e6dec7 Updated locale-data dependency 2021-12-14 00:31:44 +00:00
89a766b799 Bump fgrosse/phpasn1 from 2.3.1 to 2.4.0 (#4644)
Bumps [fgrosse/phpasn1](https://github.com/fgrosse/PHPASN1) from 2.3.1 to 2.4.0.
- [Release notes](https://github.com/fgrosse/PHPASN1/releases)
- [Changelog](https://github.com/fgrosse/PHPASN1/blob/master/CHANGELOG.md)
- [Commits](https://github.com/fgrosse/PHPASN1/compare/v2.3.1...v2.4.0)

---
updated-dependencies:
- dependency-name: fgrosse/phpasn1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-13 21:39:44 +00:00
7e99e5167c Merge branch 'legacy/pm3' into stable 2021-12-13 12:36:26 +00:00
f5bbd30dbb Fixed skins appearing black when using RTX resource packs, closes #4537 2021-12-13 12:35:55 +00:00
3be8472ae2 MemoryManager: fixed dumping of uninitialized properties
closes #4643
2021-12-13 12:11:49 +00:00
22bb1ce8e0 4.0.3 is next 2021-12-12 23:27:54 +00:00
178dcb71a9 Release 4.0.2 2021-12-12 23:27:50 +00:00
0a58fd5472 GeneratorManager: fixed addGenerator() being case-sensitive when overwrite=true
this was caused by 083a1e1ff6.

This was discovered by a new PHPStan rule I'm working on, which disallows overwriting the values of parameter variables. During the refactor of this function to correct the error, another error appeared: Variable might not be defined.

This is yet another excellent example of why mutability is bad.
2021-12-12 21:58:07 +00:00
e06eefeab0 build/generate-known-translation-apis: fixed incorrect positional parameter order
closes #4639
2021-12-11 21:28:52 +00:00
ede07c4314 Mark KnownTranslationKeys and KnownTranslationFactory as @internal 2021-12-11 21:24:18 +00:00
cba00bf1e2 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2021-12-10 23:24:38 +00:00
e81bee3866 ConsoleReaderThread: disable opcache for console reader subprocess 2021-12-10 23:24:18 +00:00
e6b85988b2 Bump fgrosse/phpasn1 from 2.3.0 to 2.3.1 (#4636)
Bumps [fgrosse/phpasn1](https://github.com/fgrosse/PHPASN1) from 2.3.0 to 2.3.1.
- [Release notes](https://github.com/fgrosse/PHPASN1/releases)
- [Changelog](https://github.com/fgrosse/PHPASN1/blob/master/CHANGELOG.md)
- [Commits](https://github.com/fgrosse/PHPASN1/compare/v2.3.0...v2.3.1)

---
updated-dependencies:
- dependency-name: fgrosse/phpasn1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-10 22:37:28 +00:00
b50591303b README: make shield show download count for 4.0.1, not 3.26.3 2021-12-10 20:23:48 +00:00
448f26cefc SimpleCommandMap: do not strip backslashes from unquoted command arguments 2021-12-10 18:27:49 +00:00
fa48100da5 PluginDescription: ensure base type of decoded document is actually an array
fixes #4628
2021-12-10 18:08:50 +00:00
bcf8a3424c Merge branch 'legacy/pm3' into stable 2021-12-10 18:02:06 +00:00
69d5bfa0d4 3.26.4 is next 2021-12-10 17:55:11 +00:00
549fb923bf Release 3.26.3 2021-12-10 17:55:07 +00:00
6d5c463bdd PlayerExperienceChangeEvent: added range checks to setNewProgress()
WE FINALLY FUCKING FOUND IT

This took several years to identify because PHP's exception stack traces don't show the actual values of parameters, but rather the values of the variables they were assigned to.

This means that if the parameter variable is mutated, the exception trace will show the value of the variable inside the function, not the value that was actually passed.
2021-12-10 17:29:57 +00:00
911ad344c9 Human: do not mutate parameter variables in setXpAndProgress()
this caused a mystery that took 3 entire years to debug.
2021-12-10 17:27:28 +00:00
3b77462935 WritableBookBase: fixed crash when finding pages containing corrupted UTF-8 characters
maybe we should treat this as corrupted? but for now, it's consistent with how we deal with signs.
2021-12-10 16:39:13 +00:00
6b40ed7bf8 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2021-12-10 16:32:32 +00:00
1ed9302f5a ItemEntity: clone items given to the constructor directly
this fixes some bizarre mutability issues that occurred when using World->dropItem() with the same object multiple times.
2021-12-10 16:31:56 +00:00
b3dab0beef readme: added total downloads & latest downloads badges
[ci skip]
2021-12-10 00:40:29 +00:00
6ddaed97fa 4.0.2 is next 2021-12-09 00:48:45 +00:00
036b90d247 Release 4.0.1 2021-12-09 00:48:42 +00:00
d909cd8a91 Merge branch 'legacy/pm3' into stable 2021-12-09 00:33:05 +00:00
06eaf9f273 3.26.3 is next 2021-12-09 00:27:03 +00:00
1e56ed2ea3 Release 3.26.2 2021-12-09 00:26:59 +00:00
dccb8a3595 Merge branch 'legacy/pm3' into stable 2021-12-09 00:00:11 +00:00
0ace807756 Merge commit 'b081394125f90c14d6894b24e2edb32f3284b3a0' into stable 2021-12-08 23:59:51 +00:00
40895a86e5 draft-release: stick a banner on the release notes to declare obsolescence 2021-12-08 23:55:43 +00:00
b081394125 Do not restrict the allowed update channels client-side
we really should have an endpoint on the server that deals with this.
2021-12-08 21:57:16 +00:00
f48cf68cac updater: log a message when an update was found, but it's an older version 2021-12-08 21:55:44 +00:00
264cff70ec Release new PM3 builds onto pm3 channel 2021-12-08 21:55:12 +00:00
3aabfa4ab0 bootstrap: display value of PHPRC when PHP binary is borked
PHPRC overrides the search path for php.ini, which might break the php.ini locating.
2021-12-08 20:48:44 +00:00
0793e7e094 PluginLoadabilityChecker: fixed logic of extension compatibility check
if the extension doesn't specify any version, we can't do any constraint other than *.
2021-12-08 20:08:53 +00:00
3d9e19546f EntityShootBowEvent: fixed incorrect field type 2021-12-07 23:35:45 +00:00
e0eeb87ea0 World: simplify tile position checking code 2021-12-07 16:45:20 +00:00
78ffad5ffc World: add checks for tile position outside of world bounds, closes #4622 2021-12-07 16:41:52 +00:00
5a351d3b17 StringToItemParser: fixed not recognizing slime or slime_block 2021-12-06 23:51:30 +00:00
0c012ca5d9 Replace usages of ItemFactory in tests with VanillaItems 2021-12-06 23:45:36 +00:00
0530cb72df StringToItemParser: fixed some bogus aliases inherited from Item::fromString() 2021-12-06 23:44:41 +00:00
ee060f3e02 Update PHPUnit dependency junk 2021-12-06 16:42:40 +00:00
e7deffa9af Update in-house dependency versions 2021-12-06 16:41:43 +00:00
6e4b73c183 FallingBlock: fixed crash when block is unable to be determined 2021-12-06 16:40:47 +00:00
62f150586f Bump pocketmine/locale-data from 2.0.20 to 2.0.22 (#4621)
Bumps [pocketmine/locale-data](https://github.com/pmmp/Language) from 2.0.20 to 2.0.22.
- [Release notes](https://github.com/pmmp/Language/releases)
- [Commits](https://github.com/pmmp/Language/compare/2.0.20...2.0.22)

---
updated-dependencies:
- dependency-name: pocketmine/locale-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-06 15:01:12 +00:00
8ed9551ac9 Bump pocketmine/binaryutils from 0.2.2 to 0.2.3 (#4620)
Bumps [pocketmine/binaryutils](https://github.com/pmmp/BinaryUtils) from 0.2.2 to 0.2.3.
- [Release notes](https://github.com/pmmp/BinaryUtils/releases)
- [Commits](https://github.com/pmmp/BinaryUtils/compare/0.2.2...0.2.3)

---
updated-dependencies:
- dependency-name: pocketmine/binaryutils
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-06 14:59:42 +00:00
4d2d0f1d35 changelog: mention removal of Player->getLowerCaseName()
closes #4618
2021-12-06 00:58:21 +00:00
cac9db9bcc changelog: fixed mistake in CreativeInventory documentation, closes #4616 2021-12-05 15:01:45 +00:00
300d194185 CS again 2021-12-05 01:09:03 +00:00
13340a21d3 fix CS 2021-12-05 01:01:16 +00:00
27f599793a tools: added old-but-gold server-ping.php 2021-12-05 01:00:24 +00:00
527e975fa9 shut 2021-12-05 00:45:23 +00:00
8e37f86480 Avoid file_put_contents() when overwriting files
this fixes many cases of corruption during disk-full situations - file_put_contents() would write an empty file, destroying the original data.
fixes #3152
2021-12-05 00:26:48 +00:00
8e8cee45b8 Config: use JSON_THROW_ON_ERROR for encoding 2021-12-04 21:44:12 +00:00
1a046c6cd5 LevelDB: fixed server crash when corrupted / invalid blockstate NBT is encountered 2021-12-04 18:17:17 +00:00
e61aaaccca LevelDB: removed hack for problem fixed by 1f9400f901 2021-12-04 16:20:57 +00:00
1b86355c40 Server: Suppress "Minecraft network interface running" messages if RakLibInterface registration is cancelled (#4603) 2021-12-02 20:29:01 +00:00
1669d33f7e Updated DevTools submodule to pmmp/DevTools@39510af5bc 2021-12-02 00:58:15 +00:00
2da65c5a6e 4.0.1 is next 2021-12-01 22:33:58 +00:00
468faa464b Release 4.0.0 2021-12-01 22:33:52 +00:00
59de045ecb PM4 LET'S GOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
Merge branch 'master' into stable
2021-12-01 22:19:37 +00:00
bd8308cc6f changelog: mention pocketmine subdirectory removal 2021-12-01 22:15:38 +00:00
edc3bae172 Merge branch 'stable' 2021-12-01 22:11:44 +00:00
06e7030817 Prepare changelog for 4.0.0 2021-12-01 22:09:34 +00:00
cb0af44ccb start.sh: improve errors when PHP isn't found 2021-11-30 23:51:35 +00:00
d535f02096 Make nicer errors for PHP binary not being found 2021-11-30 23:45:25 +00:00
7665f4f443 start.sh: remove 7 2021-11-30 23:43:17 +00:00
20d6b69813 3.26.2 is next 2021-11-30 22:27:42 +00:00
6b7d0307af Release 3.26.1 2021-11-30 22:27:42 +00:00
baeac2eb07 Fixed tiles not being sent with chunks 2021-11-30 22:19:28 +00:00
2850ea1e89 4.0.0-BETA16 is next 2021-11-30 19:27:05 +00:00
d560cf17fc Release 4.0.0-BETA15 2021-11-30 19:27:04 +00:00
3f6efd0018 Merge branch 'stable' 2021-11-30 19:20:40 +00:00
aea124af74 Fix inconsistent class name 2021-11-30 19:17:26 +00:00
8620e67d88 Protocol changes for 1.18.0 2021-11-30 19:16:38 +00:00
d5f81fe261 3.26.1 is next 2021-11-30 18:53:36 +00:00
0aeac3af7d Release 3.26.0 2021-11-30 18:53:36 +00:00
9931c1d50a Protocol changes for 1.18.0 2021-11-30 18:46:29 +00:00
d21a3d8750 4.0.0-BETA15 is next 2021-11-30 01:26:07 +00:00
6d62b06ce6 Release 4.0.0-BETA14 2021-11-30 01:26:07 +00:00
8be92d16fe Merge branch 'stable' 2021-11-30 01:19:54 +00:00
8079ae341a Updated build/php submodule to pmmp/php-build-scripts@bd329dba08 2021-11-30 01:19:14 +00:00
ba295dc7dc Always use LF in .neon files 2021-11-30 01:16:28 +00:00
38325c8573 Updated translations 2021-11-30 01:14:21 +00:00
f239b077b9 Fixed PHPStan complaints 2021-11-30 00:36:38 +00:00
6f8f460a6c Partially revert "ConsoleReaderChildProcess: Commit suicide in more cases"
This reverts commit cbe0f44c4f.

This achieves the same result as the reverted commit wrt. process in the
same manner (writing a keepalive into the socket and checking if it
failed to send). However, it does _not_ allow the process to die on
reaching pipe EOF, since this can cause many spams of subprocesses when
stdin is actually not a tty (e.g. in a Docker container).
2021-11-30 00:27:52 +00:00
882df94bcb ConsoleReaderThread: fixed zombie process leak 2021-11-29 23:45:10 +00:00
4a8ca603a1 Log a message when forceShutdown() is called for anything other than a graceful shutdown 2021-11-28 18:53:34 +00:00
52f0c4f3ed Removed dodgy test using invalid block metadata 2021-11-27 22:51:14 +00:00
e2815eed60 BlockFactory: remap a bunch more invalid states 2021-11-27 20:07:58 +00:00
932a88764c composer commands suck 2021-11-27 04:07:25 +00:00
9540193766 Fixed everything lighting on fire 2021-11-27 03:54:30 +00:00
cc23e0b7a1 Updated DevTools submodule to pmmp/DevTools@6af57741e6 2021-11-27 03:52:32 +00:00
1f9400f901 World: automatically remap invalid blockstates on chunk load
this fixes a wide range of blocks with invalid blockstates becoming update! blocks on the client.

The most common occurrence of this was air with nonzero metadata left behind by world editors which set blockIDs but not block metadata. This caused large ghost structures of update! blocks to appear from nowhere.

The performance impact of this is very minimal (20 microseconds per chunk load in timings, compared to average 660 microseconds to load tiles).
2021-11-27 01:12:30 +00:00
e5149756a8 WorldTimings: fixed merge error introduced by 3bf87378ef 2021-11-27 00:06:09 +00:00
bc18969a09 Merge branch 'stable' 2021-11-26 23:45:09 +00:00
c19174a174 3.25.7 is next 2021-11-26 23:37:47 +00:00
f95142f6b6 Release 3.25.6 2021-11-26 23:37:46 +00:00
7ace24caab Fixed borked build number
this was a problem before the recent clean-up; the only reason it just decided to show now is because 2000+25 is valid PHP code, so PHP saved our asses.
2021-11-26 23:36:19 +00:00
32f619ac49 3.25.6 is next 2021-11-26 23:20:48 +00:00
1bb6ac4fb6 Release 3.25.5 2021-11-26 23:20:40 +00:00
533d3aae8b Merge branch 'stable' 2021-11-26 22:41:18 +00:00
52a891ba73 shut 2021-11-26 22:32:25 +00:00
71b813d4f9 Define pocketmine\BUILD_NUMBER from phar metadata
this way we don't have to patch the code (no idea why we were doing that anyway).
2021-11-26 22:27:58 +00:00
f2540a72ad Backport improved make-release.php from PM4 2021-11-26 22:10:46 +00:00
03f13495b7 Merge branch 'stable' 2021-11-26 21:59:55 +00:00
7e0f6c02a1 Updated build/php submodule to pmmp/php-build-scripts@a59722c676 2021-11-26 21:59:39 +00:00
1bc7869f6e Added remapping for almost 4000 invalid blockstates
when a block has sole ownership of an ID, the state bitmask can be ignored and we can just claim the whole metadata range for that single block.
This fixes a large number of issues with unknown blocks on older worlds where world editors did not remove the metadata, although update blocks will currently still appear on initial chunk send due to lack of AOT conversion (TODO).
2021-11-26 01:58:52 +00:00
5556861000 ItemFactory: move SweetBerries registration to the correct place 2021-11-26 00:46:35 +00:00
7dd5d0b593 4.0.0-BETA14 is next 2021-11-25 00:40:43 +00:00
9338d42742 Release 4.0.0-BETA13 2021-11-25 00:40:40 +00:00
9346ecdc39 Merge branch 'stable' 2021-11-25 00:01:48 +00:00
c023c02b6c MemoryManager: Removed obsolete workaround for $GLOBALS not being defined on threads
this was long since fixed, and everyone has since been forced to upgrade to pthreads 4.0.0, which definitely has the fix.
2021-11-24 23:57:55 +00:00
bb7683158f Remove dead ignoreErrors patterns 2021-11-24 23:52:51 +00:00
fad96b77ce stfu 2021-11-24 23:49:56 +00:00
40575a6dcf Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-24 23:43:03 +00:00
40f8f042da Merge branch 'stable' 2021-11-24 23:42:53 +00:00
0fe6038c41 Merge branch 'stable' 2021-11-24 23:41:40 +00:00
adff561483 phpstan: go nuclear on OPcache
when using dynamic reflection (which is the default), any time static reflection comes into play, bad shit starts to happen because of FileReadTrapStreamWrapper.
I attempted to fix these issues (phpstan/phpstan-src#801) and failed miserably.
So, to save the hassle, it's time to just remove OPcache from the picture (which, unfortunately, also means that PHPStan will not benefit from JIT).
2021-11-24 23:40:54 +00:00
ad56392d95 Skull: fixed calculation of collision boxes (#4591) 2021-11-24 21:42:51 +00:00
472ffb28ff ScriptPluginLoader: use parseDocComment() instead of reinventing the wheel 2021-11-24 17:22:49 +00:00
726c5652f7 ScriptPluginLoader: fixed reading @tags from non-docblock lines preceding the first docblock 2021-11-24 17:07:34 +00:00
b784a04e08 Utils: fixed parseDocComment() ignoring tags containing hyphens 2021-11-24 16:38:37 +00:00
5c7125f190 Improved error handling for loading broken entity / tile data 2021-11-23 17:41:26 +00:00
eb0cf52d81 Remove useless code (#4590) 2021-11-23 17:09:33 +00:00
d8f0fd0a7e McRegion: skip chunks with TerrainGenerated=false
legacy PM used to save even ungenerated chunks, and omitted some tags when doing so which we expect to always be present.
2021-11-23 01:49:48 +00:00
fb0eebc0dc RegionWorldProvider: Show a more specific message on missing required ByteArrayTags 2021-11-23 01:39:35 +00:00
020cd7b966 CrashDump: fixed encodedData being uninitialized before getEncodedData() is called 2021-11-22 22:31:07 +00:00
c37c261c0f Separate crashdump file generation from crashdump data collection
this allows CrashDump to be used just to generate data, which will come in useful for non-crash error reporting in the future (e.g. packet decoding errors).
2021-11-22 22:19:40 +00:00
2bb97d8904 Be quiet CS 2021-11-22 15:40:47 +00:00
d3878b2d57 fixed spam 2021-11-22 15:37:33 +00:00
cbe0f44c4f ConsoleReaderChildProcess: Commit suicide in more cases
this makes it slightly less annoying to get rid of as an orphan process, though it still won't immediately die.
2021-11-22 14:58:45 +00:00
37622e02b8 Updated translations 2021-11-21 21:11:39 +00:00
ed8b4950a3 Updated BedrockProtocol 2021-11-21 21:10:58 +00:00
fc7d297f60 Added missing fields of StructureSettings 2021-11-21 20:51:35 +00:00
7b4ef293bd NetworkBinaryStream: fixed incorrect field types for StructureSettings 2021-11-21 20:49:00 +00:00
c72d66f370 Merge branch 'stable' 2021-11-20 18:28:55 +00:00
3683884b9c Updated build/php submodule to pmmp/php-build-scripts@7a2ab5b922 2021-11-20 18:27:43 +00:00
37e8b1ee8c Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-20 18:25:45 +00:00
046dafc34f Merge branch 'stable' 2021-11-20 18:25:30 +00:00
db135788b9 Updated transient dependencies 2021-11-20 18:19:27 +00:00
b34e6f53eb Changed visibility of Projectile->move to Protected. (#4585) 2021-11-19 23:21:10 +00:00
b4b954cc5f build/generate-registry-annotations: accommodate code with CRLF 2021-11-19 21:38:43 +00:00
7210db25b0 Bump phpstan/phpstan from 1.1.2 to 1.2.0 (#4583)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.1.2 to 1.2.0.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/master/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.1.2...1.2.0)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-19 14:42:01 +00:00
4599913034 Separate crashdump rendering from crashdump data collection
this allows this code to be reused for reproducing crashdumps based on the original data.
2021-11-18 00:58:20 +00:00
c48aa274e7 Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-15 22:52:47 +00:00
269231c228 Ban foreach(arrayWithStringKeys as k => v)
this is not as good as phpstan/phpstan-src#769 (e.g. array_key_first()/array_key_last() aren't covered by this, nor is array_rand()) but it does eliminate the most infuriating cases where this usually crops up.
2021-11-15 22:52:05 +00:00
4cad552909 Allow input of relative coordinates to setworldspawn command (#4575) 2021-11-14 20:07:37 +00:00
f2d5455c5e changelog: mention that armor right-click equipping is now supported
[ci skip]
closes #4570
2021-11-14 16:42:35 +00:00
65247b7248 changelog: add notes about ender inventory
closes #4569
2021-11-14 16:41:57 +00:00
2f408708f0 Explosion: fixed blocks with tiles not using said tiles for drop info
closes #4571
2021-11-14 16:27:47 +00:00
3dd03075cb StringToItemParser: added some quality-of-life aliases 2021-11-14 15:52:50 +00:00
82b5bca83e Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-14 15:52:05 +00:00
639867a640 Added missing aliases for wooden items 2021-11-14 15:51:41 +00:00
d4a382d568 Fix position of setworldspawn command (#4574)
* The world spawn position is no longer rounded

* Remove round() since the position is always int
2021-11-14 15:40:20 +00:00
399824c31c Add correct drop for Podzol (#4573) 2021-11-14 14:15:36 +00:00
ada469bc45 README: do not show beta releases on badge
[ci skip]
2021-11-12 01:35:39 +00:00
dc8243f88b Bump phpstan/phpstan from 1.1.1 to 1.1.2 (#4564)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/1.1.1...1.1.2)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-12 00:24:23 +00:00
7668171c56 Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-12 00:17:07 +00:00
e4754ab029 PluginBase: Improved error messages for commands containing illegal characters 2021-11-12 00:16:53 +00:00
3276047497 Updated composer dependencies 2021-11-12 00:13:22 +00:00
49a8eff11e BUILDING: submodules are no longer required
submodules are useful (e.g. devtools, build/php) but they are not required to build a server phar.
2021-11-11 14:29:56 +00:00
73592349cd 4.0.0-BETA13 is next 2021-11-09 16:50:46 +00:00
635a9143de Release 4.0.0-BETA12 2021-11-09 16:50:42 +00:00
c3ec9c0948 Effect default duration is once again NOT hardcoded, like PM3
I have no fucking idea why I hardcoded this to begin with. Not one of my better ideas ...
2021-11-09 01:52:47 +00:00
09a2e006a8 CS AGAIN 2021-11-09 00:20:06 +00:00
fed59d3ebe added missing file 2021-11-09 00:11:39 +00:00
c7beb0a702 Clean up inventory auto close mess from PM3
on PM3 there was no concept of 'current window', we had no idea which window the player was actually looking at.
2021-11-08 23:51:25 +00:00
5be429a8c4 Ensure inventories get evacuated on server-side window close 2021-11-08 23:48:05 +00:00
ab002ca06d Improved handling of temporary inventory windows
evacuation behaviour is now consistent regardless of who is doing it
2021-11-08 23:36:58 +00:00
6efb1db107 Fixed inventories not working after dying with inventory open
closes #4185
closes #4177
2021-11-08 23:04:00 +00:00
6fdcfb01c8 Seal up main inventory open/close logic inside InventoryManager where it belongs 2021-11-08 22:58:06 +00:00
1beec348f9 3.25.5 is next 2021-11-08 22:33:09 +00:00
7306a2d939 Release 3.25.4 2021-11-08 22:33:08 +00:00
4bf338f783 Player: fixed removeWindow() causing all other inventories to be unopenable 2021-11-08 22:29:14 +00:00
255ff63fda 3.25.4 is next 2021-11-08 20:35:15 +00:00
d72f6a3ac6 Release 3.25.3 2021-11-08 20:35:14 +00:00
93a1e84ad9 TypeConverter: further simplification 2021-11-08 20:27:53 +00:00
c33f97ae41 TypeConverter: clean up absurdly overcomplicated bullshit in createInventoryAction() 2021-11-08 20:18:19 +00:00
cc4bb91fcb Implemented IPv6 support (#4554) 2021-11-08 20:03:28 +00:00
eb9012401b Merge branch 'stable' 2021-11-08 19:53:56 +00:00
3b34268ed6 Human: try to trap this stupid float cast bug in the wild 2021-11-08 19:48:39 +00:00
4c07078586 Merge branch 'stable' 2021-11-08 19:01:08 +00:00
19a3efe893 ....... 2021-11-08 18:57:14 +00:00
a1ecdc27e5 Removed Vanilla*::fromString()
these were misbegotten and should never have existed.
If someone really needs these for some reason, they can use getAll()[name].
2021-11-08 18:52:14 +00:00
f93b5be789 Added new dynamic StringToEffectParser 2021-11-08 18:49:28 +00:00
1fb60b5b3a CS fix again 2021-11-08 18:45:05 +00:00
08420c2556 Added new dynamic StringToEnchantmentParser
this should be used instead of VanillaEnchantments::fromString(), because it allows registering custom aliases.
2021-11-08 18:44:15 +00:00
18f5fb66bb Abstract the base functionality of StringToItemParser 2021-11-08 18:37:05 +00:00
a6f6b60bed fix CS again 2021-11-08 18:02:24 +00:00
c6c992a1f0 Preparations for negative Y support 2021-11-08 17:28:22 +00:00
df39a1ca07 TeleportCommand: do not hardcode world bounds 2021-11-08 17:22:01 +00:00
be6d1843de Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-08 17:17:20 +00:00
2b0b9bd8ed Update composer dependencies 2021-11-08 17:16:57 +00:00
76dad46e13 Bump phpstan/phpstan from 1.0.2 to 1.1.1 (#4560)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.0.2 to 1.1.1.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/1.0.2...1.1.1)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 13:36:06 +00:00
eb3530b6e6 Use pmmp/setup-php-action to compile PHP 2021-11-07 23:13:56 +00:00
4131bcef08 Changed "Level" string to "World" in Position::__toString() method. (#4559) 2021-11-07 21:11:55 +00:00
b84f7c18ec Install ext/crypto from PECL 2021-11-07 19:18:09 +00:00
45edb94607 Crafting tables now work the same way as anvils and enchanting tables
Removing almost all special-case logic for crafting tables.
2021-11-07 16:20:07 +00:00
6b316dc29a PluginManager: Make declaration of duplicate permissions a load error 2021-11-06 17:05:37 +00:00
d9d37f7fa6 ResourcePacksPacketHandler: fixed a mistake from c773e43eda
fixes #4557

Note: you may need to clear your local pack cache in order to get it working again.
2021-11-06 16:45:14 +00:00
4cb6c7dc1e PluginManager: fixed plugins being able to alter groups of other plugins' permissions
this could happen if a plugin declared a permission already declared by another plugin, and then declared a different default for it (e.g. true instead of op).
2021-11-06 16:32:19 +00:00
b392651354 pocketmine.yml: always refer to worlds as worlds in config comments, not levels 2021-11-06 02:22:14 +00:00
f81c55ce6c 4.0.0-BETA12 is next 2021-11-06 01:17:03 +00:00
002feacf8e Release 4.0.0-BETA11 2021-11-06 01:16:58 +00:00
b8523f7a18 Player: fix the fix which just degraded performance
if a chunk was requested for generation, count++ and count(activeRequests)++, which means that we would only get to submit half as many generation requests as we're allowed to.
Calculate the limit at the start and remember it instead.
2021-11-06 01:00:35 +00:00
640e88009b Player: fixed a mistake in generation rate limit
we don't want to allow sending further chunks when we haven't generated near ones, because we won't be able to see them anyway, and we might end up not needing them.
This now fully matches the results of PM3.
2021-11-06 00:57:37 +00:00
6cd272c9e1 Update transient composer dependencies 2021-11-06 00:55:13 +00:00
566c57bcd3 we no longer need submodules for these jobs 2021-11-06 00:35:39 +00:00
3c754b079c Move resources/locale to Composer dependency
all remaining submodules are now non-essential to running a server.
They are also versioned and updates can be done automatically using 'composer update'.

Finally, we can also put an end to the issue of translations being rendered incorrectly or being missing due to outdated submodules.
2021-11-06 00:32:58 +00:00
dbf9a33160 ChunkSelector: Improve algorithm to send chunks in proper circles, instead of squares
this ensures that the edge of loaded terain is always the same distance
away in any direction. This also means that when flying parallel to X or
Z axes, you now have about 12% more chunks directly in front of you,
instead of to your left and right, which gives the impression that
chunks are loading faster (they aren't, they are just being ordered in a
more sensible way).
2021-11-06 00:15:04 +00:00
07b1cff306 Bonemeal is no longer consumed when cancelling plant growth events (#4551) 2021-11-05 16:15:55 +00:00
0989c77037 Player: check that horizontal distance travelled is > 0 before adding exhaustion, or triggering a new chunk order
closes #4548
2021-11-05 15:46:55 +00:00
5107d0df4e Player: cap maximum number of active generation requests
this fixes the horrible spotty chunk loading seen in https://twitter.com/dktapps/status/1456397007946461190?s=20.
In practice, this made chunks invisible on teleport for several tens of seconds after teleporting. Having a larger chunks-per-tick with large render distance compounded to worsen the problem.
It wasn't really noticeable on small render distances, but very obvious on large ones with fast chunk sending and slow generation.

This also fixes #4187 (at least to the extent that it works on PM3, anyway).
2021-11-05 15:09:42 +00:00
579ef63663 EntityDataHelper: accept FloatTag for vector3 as well as Double
MCPE uses Float for entity positions.
2021-11-04 20:46:34 +00:00
8abc952c74 simulate-chunk-selector: do not reallocate colours on every frame 2021-11-04 19:38:40 +00:00
4c3a5fdd73 Clean PHPStan baselines from 1.0.2 2021-11-04 19:28:52 +00:00
54f287feb6 Merge branch 'stable' 2021-11-04 19:27:41 +00:00
84f8b3eb2d Move CrashDump to pocketmine\crash namespace 2021-11-04 19:23:45 +00:00
15fca84f3b remove some PHPStan error patterns 2021-11-04 19:22:49 +00:00
c60144210f Regenerate PHPStan bugs baseline 2021-11-04 19:18:29 +00:00
8ac999cbd4 Use object models for crashdump generation 2021-11-04 16:55:04 +00:00
4f8501ff34 Added a tool to visualise behaviour of ChunkSelector
I actually intended to write a tool for debugging generation, but it turns out this, as an intermediary step, is also useful and a whole bunch of fun to play with.
2021-11-04 14:14:55 +00:00
2405e45b35 Player: mark as not using item when held item slot is changed
closes #4538
2021-11-03 21:26:20 +00:00
e0b07ff308 Human: do not add more XP if totalXp limit was already reached
this matches the vanilla behaviour. For some reason it doesn't consider levels (so you can have a level higher or lower than this without actually having that amount of XP), but this matches Java behaviour as of 1.10.

fixes #4543
2021-11-03 20:45:55 +00:00
729f831b8f PHPStan 1.0.2 2021-11-03 20:26:32 +00:00
0356716e8e PopulationTask: do not expose internal fields as public
this code dates back to pthreads v2, when visibility on Threaded object fields meant different things (wtf, krakjoe??)
2021-11-03 15:23:43 +00:00
5c81b04813 PopulationTask: use typed properties 2021-11-03 15:22:49 +00:00
1ebb206762 World: fixed yet another edge case in drainPopulationRequestQueue() leading to assertion failure
really had to go fucking nuclear on it :(
2021-11-03 14:58:58 +00:00
29e2d92098 Bump phpstan/phpstan from 1.0.0 to 1.0.1 (#4541)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/1.0.0...1.0.1)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-03 11:27:42 +00:00
ef82a2cd79 Fixed PlayerEmoteEvent::setEmoteId() being useless 2021-11-02 23:10:26 +00:00
87031627bf Do not call PlayerEmoteEvent if rate limit was reached 2021-11-02 23:09:43 +00:00
f066199971 Implement emote support (#4523) 2021-11-02 23:04:55 +00:00
a0e9eec652 4.0.0-BETA11 is next 2021-11-02 19:18:20 +00:00
fa6a432d58 Release 4.0.0-BETA10 2021-11-02 19:18:19 +00:00
102277c636 draft-release: fixed BedrockData JSON minification 2021-11-02 19:16:36 +00:00
f50f26d52e 4.0.0-BETA10 is next 2021-11-02 19:14:15 +00:00
4ca7c29cde Release 4.0.0-BETA9 2021-11-02 19:14:12 +00:00
9f64bc8180 Merge branch 'stable' 2021-11-02 17:30:36 +00:00
f75a05d7fa 3.25.3 is next 2021-11-02 17:21:49 +00:00
3dae873731 Release 3.25.2 2021-11-02 17:21:44 +00:00
aa0dc60c32 Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-11-02 17:10:19 +00:00
d184838ba0 Move Promise classes to their own namespace 2021-11-02 17:10:07 +00:00
ead8ccf08d CocoaBlock: call BlockGrowEvent when growing for any reason (#4536) 2021-11-02 17:05:07 +00:00
38f97bed52 World: fixed PopulationTask failed assumption that generator is always registered
if the worker selected previously had a generator registered, but has since been shutdown, the workerStartHook that cleans up generatorRegisteredWorkers won't yet have been called.
This results in the worker being started by the submission of PopulationTask, and the generator doesn't get preemptively registered.
2021-11-02 16:58:14 +00:00
275f145418 BedrockData is now a Composer dependency
this should put a stop to people nagging me about incorrect blocks (we have a check to make sure composer dependencies are up to date).
2021-11-02 16:45:45 +00:00
6b07f7a5ec pmmp/BedrockProtocol@5.0.0+bedrock-1.17.40 2021-11-02 16:22:56 +00:00
e131c2cefa Drop pocketmine/spl 2021-11-02 16:08:29 +00:00
e34364412b Replace InvalidStateException usages with InvalidArgument or LogicException 2021-11-02 16:05:54 +00:00
4eef458d29 Config: throw AssumptionFailedError if config type is invalid or DETECT during save()
this should never happen ... it was already checked in load()
2021-11-02 16:03:43 +00:00
8b3565b75d PopulationTask no longer depends on a World object
this means it's now possible to test generation offline without too much hassle.
World::generateChunkCallback() has been removed from public API.
2021-11-02 15:40:51 +00:00
facfd7c04a sanity check 2021-11-02 15:26:54 +00:00
65ef9f786a Use standard chunkHash() to index population chunks 2021-11-02 15:25:03 +00:00
4dc13ab3da ConsoleReaderThread: strip control characters
this fixes a bug I encountered when accidentally pressing ctrl+a+d (which inserts a chr(1) character), because it made the server unable to find the command - but still reported an error containing what looked like a valid command (character isn't printable).
2021-11-02 15:11:23 +00:00
ede4157814 Check to see if the player can start using the Releasable item. (#4532) 2021-11-02 14:36:16 +00:00
34ea199fb0 World: fixed additional edge case - population promise rejected before task completion
if this happened, the index would stay set in activeChunkPopulationTasks, eventually causing the generation queue to jam up completely and non-forced generation to come to a standstill.
2021-11-02 14:30:23 +00:00
1775699f05 World: make sure that chunks locked by PopulationTask always get unlocked, no matter what
fixes #4527
2021-11-02 14:20:42 +00:00
32a857b8b4 fix CS 2021-11-02 14:09:16 +00:00
7e4be29fc4 Gracefully force-shutdown on failure to start RakLib
this now won't generate a crashdump.
2021-11-02 13:51:01 +00:00
c17587d436 World: use new Vector3() instead of Block->getPosition()
When profiling this, I noticed that we spend a stupidly large amount of time creating useless Position objects in the case of update=true, because Vector3->sides() calls Position->getSide(), which calls Position::fromObject(parent::getSide()). This is stupid because the update logic doesn't require Positions anywhere (as evidenced by this change needing no other alterations.

A rough profile shows that this improves setBlock() performance by about 25% in the update=true case, which is a pretty big margin.
As an added bonus, it gets rid of some unrealized cyclic dependencies in World->changedBlocks.
2021-11-02 03:00:00 +00:00
0f6b7e48cb Updated BedrockProtocol:
it's weirdly satisfying that LevelChunkPacket::create() with the extra parameter turns out to be exactly the same length as the old way.
2021-11-02 01:37:56 +00:00
f2912fcdd8 Updated pocketmine/log and pocketmine/log-pthreads (BC breaks included)
AttachableLogger deals with Closures now instead of LoggerAttachment objects
ThreadedLoggerAttachment no longer implements LoggerAttachment
2021-11-01 22:22:22 +00:00
f6cb4f9597 Updated BedrockProtocol 2021-11-01 21:32:02 +00:00
54442f7e4b Merge branch 'stable' 2021-11-01 21:18:35 +00:00
5257755dc5 shut 2021-11-01 21:15:58 +00:00
8c16ecaa5b Merge branch 'stable' 2021-11-01 21:08:32 +00:00
3214da8642 pthreads 4.0.0 2021-11-01 21:01:59 +00:00
f827a555d5 Merge branch 'stable' 2021-11-01 18:13:24 +00:00
61145baded Merge commit 27ae959e8 2021-11-01 17:42:31 +00:00
4d54d6c552 Merge commit f8f39687e2 2021-11-01 17:41:47 +00:00
485bc2c565 Merge commit 94737934de 2021-11-01 17:41:10 +00:00
07b4f844a9 Merge commit debb469de1 2021-11-01 17:40:40 +00:00
19e5775f6b Merge commit 73dc0598e4 2021-11-01 17:40:06 +00:00
804fb3f603 Merge commit 141fbde660 2021-11-01 17:39:40 +00:00
6175b03433 Merge commit 69952ae2af 2021-11-01 17:36:34 +00:00
a78248a19c Merge commit '71f2a34616961d6328f06fd911b6d4450a61643e' 2021-11-01 17:33:02 +00:00
414ccb9f10 Merge commit 'd17cd658030e66009c0bb32941387c7444d3ffe0' 2021-11-01 17:31:05 +00:00
794142fe49 Merge l7/l8/l9 baselines into actual-problems
it doesn't serve any practical purpose to keep these separated, particularly since it's getting so difficult to figure out which errors are coming from which levels (since we always use 9, it doesn't really make any difference).
2021-11-01 17:27:31 +00:00
ff27c5f7db PHPStan 1.0.0 2021-11-01 17:24:20 +00:00
4d4362801f AvailableCommandsPacket: remove dead code 2021-11-01 17:01:26 +00:00
0babe0a1ab LevelDB: remove unused private method 2021-11-01 16:55:05 +00:00
d696ebcda3 Level: do not use static:: to access levelIdCounter
the field is private.
2021-11-01 16:52:27 +00:00
c3768b997a Updated to pmmp/BedrockProtocol@146498c279 2021-11-01 16:13:33 +00:00
f6480017ce Update PHPUnit dependency junk 2021-11-01 16:12:37 +00:00
9f5c16bc46 Projectile: use closure instead of do/while for reading id/data of block
not ideal, but whatever I guess... this at least provides scope isolation
2021-11-01 15:56:28 +00:00
8865bb73ba BanEntry: remove useless do/while 2021-11-01 15:52:55 +00:00
2dee1dbc28 Remove ridiculous code in ResourcePackManager 2021-11-01 15:41:21 +00:00
0f0b6f0efa Utils: eliminate usages of backtick operator 2021-11-01 15:25:56 +00:00
d5f13d8be2 Timezone: make PHPStan 1.0 happy 2021-11-01 15:24:16 +00:00
27ae959e89 Terminal: backport shell_exec() code from PM4 to make PHPStan 1.0 happy 2021-11-01 15:23:36 +00:00
f8f39687e2 Achievement: declare proper type for $list static property 2021-11-01 15:22:33 +00:00
94737934de PlayerDeathEvent: fixed LSP violation reported by PHPStan 1.0 2021-11-01 14:17:54 +00:00
debb469de1 Updated PHPUnit dependency junk 2021-11-01 13:54:04 +00:00
616eb0050d World: remove premature optimisation of setBlockAt() introduced by ece28e5d7b
closes #4531
it turns out that letting the light updates themselves handle this is faster than trying to get in the way.
2021-11-01 02:34:44 +00:00
9d30bc8b95 World: fixed assertion failure when requesting, cancelling, and then re-requesting chunks via requestChunkPopulation()
if the request/cancel/re-request happens all in the time before the queue gets drained, chunk hashes may appear multiple times in the queue. We don't want to process them twice if this happens (although it's mostly harmless anyway).
2021-11-01 02:17:11 +00:00
46b7d35cd3 Player: return from callback if used chunk status is not REQUESTED_GENERATION()
this can happen especially on large render distances when flying fast and changing direction - we decide we don't want the chunk, then, after changing direction and re-ordering chunks, we decide we do want it again, and end up registering a second callback. In this case, we need to ensure that only one of the callbacks gets executed (it doesn't matter which one).
2021-11-01 01:45:49 +00:00
c781efcf90 World: avoid calling the same logic twice in requestChunkPopulation()
orderChunkPopulation() checks the preconditions too. Have them both call an internal function that doesn't.
2021-11-01 00:36:57 +00:00
e4a54f5b6a World: deduplicate code in request/orderChunkPopulation 2021-11-01 00:33:18 +00:00
afb54f1ae4 World: flip orderChunkPopulation() condition around
this makes it more obvious that the code is similar to requestChunkPopulation() barring one distinct difference.
2021-11-01 00:25:30 +00:00
8f803df511 World: check population locks _after_ checking if the chunk is already populated, not before
this led to another case where a population request would be queued up for an already-populated chunk for no reason.
2021-10-31 23:54:27 +00:00
f4a3c40b5c World: use better variable names in orderChunkPopulation() 2021-10-31 23:48:00 +00:00
9dec82cdbc World: fixed requestChunkPopulation() queuing requests for chunks which are already populated
this led to chunk sending getting bogged down if there were more than population-queue-size chunks waiting to be generated.
2021-10-31 23:40:34 +00:00
74031d2fbe World: remove the fulfilled promise from the population request map
fixes crash when unregistering chunk loaders
2021-10-31 23:05:12 +00:00
bd60e41268 Revert "Revert "Player: do not re-request the same ungenerated chunks multiple times""
This reverts commit 3265d3f6b4.
2021-10-31 22:57:56 +00:00
96cfdc79b8 World: fixed original promise not getting fulfilled if the chunk became populated=true after a promise was already made (but not fulfilled) to populate it
this could happen if a plugin calls setPopulated(true) on a chunk after a request for its population landed in the queue, but before it actually got processed. In that case, the promise would never get fulfilled.
2021-10-31 22:54:37 +00:00
2fa0a914ff World::orderChunkPopulation() may return a pre-resolved promise
this does not indicate a failure; it indicates that the chunk has already been successfully populated.
In this case, we shouldn't be putting the task back on the queue.

This is skirting around the real bug, which is that requestChunkPopulation() doesn't check if the target chunk is already populated before it creates a new promise that it will be.
2021-10-31 22:51:37 +00:00
08636d079d Promise: expose isResolved() 2021-10-31 22:48:52 +00:00
3265d3f6b4 Revert "Player: do not re-request the same ungenerated chunks multiple times"
This reverts commit 866020dfdb.

For some fucking reason this broke resending chunks in some cases (and
sending chunks at all in others). I don't have time to debug this right
now, so it's going to have to remain broken, infuriatingly enough.
2021-10-31 21:25:21 +00:00
0f78a2b5ef Advisory chunk locking for chunk population (#4513)
this allows chunks locked for population to be modified. If the PopulationTask detects that the chunk was modified during the onCompletion(), the result of the population will be discarded and rescheduled, so that it includes user modifications.
2021-10-31 21:11:20 +00:00
f1a791ef75 Improved Promise API - separate resolver and consumer APIs
this makes creating a promise slightly more cumbersome, but I'm more concerned about people who might try to call 'new Promise' directly.
2021-10-31 19:49:57 +00:00
866020dfdb Player: do not re-request the same ungenerated chunks multiple times
this doesn't affect chunk resends, since they'll be kicked back to NEEDED, which is detected by orderChunks().
2021-10-31 18:52:37 +00:00
c580bb2434 InGamePacketHandler: mark player as not using item in more cases
fixes #4355
2021-10-31 14:41:31 +00:00
4fe3f69702 World: eliminate final remaining 'no loaders attached' debug message on player creation 2021-10-31 14:33:27 +00:00
018006541e changelog: mention block-picking changes
[ci skip]
2021-10-31 14:12:12 +00:00
fbb91d123d World::unregisterChunkListenerFromAll(): go through unregisterChunkListener()
this ensures that everything gets cleaned up properly (e.g. player chunk listeners).
2021-10-31 14:03:40 +00:00
3dc75644d9 World: avoid duplicated logger code in initChunk() 2021-10-31 14:02:25 +00:00
1cabe4baf3 World: do not crash on duplicate tiles loaded from disk
closes #4049
2021-10-31 13:58:32 +00:00
73dc0598e4 CrashDump: remove derp space 2021-10-30 23:22:37 +01:00
faad2365e2 World: Register a temporary chunk loader on chunks used by PopulationTask
fixes #3839
2021-10-30 22:17:06 +01:00
4f816d03a7 SurvivalBlockBreakHandler: remove useless code 2021-10-30 21:35:58 +01:00
1d22761d27 Remove useless newline 2021-10-30 21:25:47 +01:00
5b8ce7e3e2 Cake: fixed desync on cancellation of eating
closes #3591
we don't support eating in creative right now, but the cake shouldn't appear to be eaten when it's not.
2021-10-30 21:02:24 +01:00
08f3c18de9 Arrow: do not add pickups to creative players' inventories
closes #2932
2021-10-30 17:16:46 +01:00
141fbde660 Player: fixed getting re-banned on rejoin after unban from hardcore death
closes #2175
2021-10-30 16:58:03 +01:00
465a509858 World: remove spammy debug message 2021-10-30 16:13:01 +01:00
69952ae2af Human: limit lifetime total XP range to INT32_MAX
closes #4484
2021-10-30 16:05:10 +01:00
71f2a34616 Entity: spawnTo() must silently swallow errors
Player->showPlayer() assumes that spawnTo() will take care of all the checks necessary to ensure we don't actually spawn a player to someone it shouldn't be able to see. In PM3, there's nothing we can do about that.
This could be a problem if anything decides to override spawnTo() to do additional stuff and assumes that the function will always succeed; however, there's not much reason to do that (plugins sending packets should override sendSpawnPacket() instead).
2021-10-30 15:38:27 +01:00
63dfcc60c3 4.0.0-BETA9 is next 2021-10-29 22:35:23 +01:00
428bd5ae91 Release 4.0.0-BETA8 2021-10-29 22:35:15 +01:00
d17cd65803 3.25.2 is next 2021-10-29 22:23:28 +01:00
a8d5e8c5f6 Release 3.25.1 2021-10-29 22:23:22 +01:00
19f448d074 pocketmine/math 0.4.0 2021-10-29 21:56:56 +01:00
1c18c731ef bootstrap: check for zlib raw support in leveldb 2021-10-29 19:15:12 +01:00
8a2ecfe1d4 Merge branch 'stable' 2021-10-29 19:04:01 +01:00
089e62b44e Entity::spawnTo(): verify that the target player belongs to the same world as the entity
this should never be hit in the PM case, but it's an InvalidArgument rather than AssumptionFailedError because plugins can and do call this with bad things.
2021-10-29 18:54:00 +01:00
32a34d2494 Location: change order of constructor parameters
to be consistent with Position::__construct() and Location::fromObject() (although Location::fromObject() has no choice, thanks to the anti-feature known as late static binding ...)
2021-10-29 15:43:09 +01:00
ee9f5e0044 Location: make __construct() parameters mandatory
I did consider allowing yaw/pitch to remain optional, but considering the implicit immutability of Location, it really doesn't make any sense to create a Location with default yaw/pitch - just create a Position in that case instead.
2021-10-29 15:40:58 +01:00
88b7389080 InventoryManager: reduce code duplication 2021-10-29 15:37:52 +01:00
f1cc168d26 phpstan: exclude a couple of files from analysis temporarily
close #4472
2021-10-29 00:23:13 +01:00
fb5543a2ad Updated BedrockProtocol dependency 2021-10-29 00:16:11 +01:00
a4eda9a8f5 World: call nearby entities' onNearbyBlockChange() in setChunk()
fixes #2779 in all known cases.
2021-10-28 23:59:32 +01:00
eb75df6f8e World: Intelligently perform automatic transfer or deletion of tiles in setChunk(), depending on the context
tiles may be deleted in the following circumstances:
1) the target block in the new chunk doesn't expect a tile
2) the target block in the new chunk expects a different type of tile (responsibility of the plugin developer to create the new tile)
3) there's already a tile in the target chunk which conflicts with the old one

In all other cases, the tile will be transferred.

This resolves a large number of unintentional bugs caused by world editors replacing chunks without setting the deleteTilesAndEntities parameter to false (even the core itself does it).

closes #4520
2021-10-28 23:48:17 +01:00
c66790b6a6 World: never delete entities in setChunk()
entities exist completely independently from chunks now, so there is no need to interact with them whatsoever.
As I wrote in #4520, there's no sense in deleting entities here, since a chunk replacement is essentially just a mass block update.

On that theme, it might be a good idea to call Entity->onNearbyBlockChange() for all entities in the target and adjacent chunks when replacing a chunk, to ensure that they get the proper movement updates.
2021-10-28 23:42:28 +01:00
d78801b9d5 World: fixed tiles and entities getting deleted when adjacent chunks are modified during population 2021-10-28 22:24:47 +01:00
d410db4302 Chunk: rename DIRTY_FLAG_TERRAIN to DIRTY_FLAG_BLOCKS
we use the word 'terrain' elsewhere to refer to the combination of blocks and biomes, so using TERRAIN here is misleading.
2021-10-28 22:11:07 +01:00
a62ce64fdd Revert "Chunk: added modification counter"
This reverts commit a5418a019d.

The more I assessed this, the more I realized that this implementation
doesn't actually offer any value. Since modcounters don't persist after
chunk unload + reload, they can't be reliably used to detect changes in
chunks without additional event subscriptions.
For the purpose I actually intended to use them for (population task
cancellation) there's a) another solution, and b) modcounts are
unreliable for that too, because of the aforementioned potential for
chunks to get unloaded and reloaded.
For the case of detecting dirty chunks within PopulationTask itself,
they are also unnecessary, since the dirty flags are sufficient within
there, since FastChunkSerializer doesn't copy dirty flags.

In conclusion, this was a misbegotten addition with little real value,
but does impact performance in hot paths.
2021-10-28 21:02:04 +01:00
5db3915aad Make MemoryManager aware of ChunkCache 2021-10-28 20:28:00 +01:00
eb40b741ae StandardPacketBroadcaster now splits broadcasts by session-specific PacketSerializerContext
in the normal case, all sessions will share the same PacketSerializerContext and Compressor, so this code will be the same as before
However, for the multi-protocol hackers out there, this should reduce the maintenance burden (@Driesboy) since now only the PacketSerializerContext needs to be maintained. I recommend a separate PacketSerializerContext for each protocol (perhaps put the protocol version in the serializer context too, if you need it for some reason).
2021-10-28 20:15:37 +01:00
b3720b3f17 4.0.0-BETA8 is next 2021-10-28 17:28:53 +01:00
7effa03ba4 Release 4.0.0-BETA7 2021-10-28 17:28:48 +01:00
d0474ccd92 make-release: note which channel the build will be released into 2021-10-28 16:29:20 +01:00
2b0768f720 make-release: fixed retention of +dev on release versions 2021-10-28 16:26:56 +01:00
dba148cfaa build/make-release: make arg parsing use getopt 2021-10-28 16:25:18 +01:00
48f77abe7e Leave channel ID in VersionInfo
so that I don't have to type it out every time I make a new release. Most of the time it's going to be posted to the same channel as before anyway.
2021-10-28 16:03:43 +01:00
bb05af103d PluginManager: fixed crash when using a plugin-loader plugin (read: devtools)
closes #4518
2021-10-28 15:55:05 +01:00
0ef5c67b9b Use static constructor for MovePlayerPacket
this marks the last of the packets created using the old way.
2021-10-27 21:10:16 +01:00
6d89265510 Player: reduce code duplication
back when this was just hardcoded >> 4 everywhere, nobody thought anything of it, but now it uses constants, it's easy to cross-reference and see where the duplicates are.
2021-10-26 23:02:50 +01:00
a7d8a598e1 World: reduce code duplication for chunk coordinate calculation 2021-10-26 22:58:17 +01:00
51fbff204b World: make PhpStorm understand return type of getAdjacentChunks() 2021-10-26 20:32:09 +01:00
1873457840 PopulationTask: stop using dynamic properties 2021-10-26 20:21:58 +01:00
fca70efbb1 World: move chunk population related methods to be in the same overall place 2021-10-26 16:44:08 +01:00
8f88393184 World: Specialize generateChunkCallback() for PopulationTask
this allows us to also set the adjacent chunks before calling ChunkPopulateEvent, to give a more accurate picture of what changed.
2021-10-26 15:28:00 +01:00
b9d9b69bbe ConsoleReaderThread: trim the string before returning it
it will have a newline at the end that was added by the subprocess when posting it to the main process.
2021-10-26 01:07:14 +01:00
1d99cd329a CS again 2021-10-26 00:50:43 +01:00
bd8cba1a7f Added unit tests for Utils::testValidInstance() 2021-10-26 00:49:41 +01:00
24d4daec90 Utils::testValidInstance() now accepts interfaces for the baseName 2021-10-26 00:32:32 +01:00
4178c81209 Utils: fixed testValidInstance() not accepting the same valid class for both className and baseName
this caused problems in PlayerCreationEvent because plugins set the base class and then set the player class to the same thing.
2021-10-26 00:31:30 +01:00
94f4ef5862 PopulationTask: Throw AssumptionFailedError if center chunk is null for some reason 2021-10-25 21:07:03 +01:00
2e2515354c PopulationTask: fixed undefined method call
fuck you PhpStorm! fuck you PhpStorm! fuck you PhpStorm!
2021-10-25 20:57:43 +01:00
359d0835f3 CS 2021-10-25 20:54:39 +01:00
d4cbde6f10 PopulationTask: use modification counters to detect changed chunks
instead of using terrain dirty flags, which aren't suitable for this purpose
2021-10-25 20:53:50 +01:00
a5418a019d Chunk: added modification counter
this is independent from the terrain dirty flags (which are specifically used to track state of chunks needing to be saved).
2021-10-25 20:53:11 +01:00
baba25953f Chunk: make all parameters of __construct() mandatory and non-nullable
having the constructor fill in defaults for these invariably causes bugs.
2021-10-25 20:22:50 +01:00
d53347454b Chunk: use HeightArray::fill() 2021-10-25 20:17:30 +01:00
401e8d117b Flat: use a less dumb way to build biome array 2021-10-25 20:15:33 +01:00
9835d75f65 Chunk: removed heighArray parameter from constructor
we don't pass this anywhere, and really it should be dynamically initialized anyway, just like light.
2021-10-25 20:13:50 +01:00
b8519d1af4 World: fixed every chunk having terrain saved at least once, even if unmodified
setPopulated() sets dirty flags on the chunk, causing the autosave sweep
to think they've been changed when they haven't. We now pass
terrainPopulated to the constructor to avoid this ambiguity recurring in
the future.
2021-10-25 19:53:47 +01:00
f6e53f826b Fixed Anvil/McRegion chunks getting autosaved on first time, even when unchanged
setGenerated/setPopulated and friends set hasChanged = true, which causes the world to autosave them the first time around, even though they weren't modified.
2021-10-25 19:52:44 +01:00
42ede30e77 ... 2021-10-23 23:57:28 +01:00
04aedc6494 Updated BedrockProtocol 2021-10-23 23:54:49 +01:00
701a71a4ee Sound::encode() position is no longer nullable
making this nullable was based on the invalid assumption that global sounds have no position, but it turns out they _do_ still use the position to make the sound come from the correct direction.
2021-10-23 02:01:26 +01:00
e50072dc27 Clean PHPStan baselines 2021-10-23 01:55:10 +01:00
c77829f4ad Migrate packet creation to use ::create() methods in all but one case
MovePlayerPacket doesn't yet have a ::create() due to a complication with fields that aren't always present.
2021-10-23 01:46:01 +01:00
c773e43eda Updated BedrockProtocol to pmmp/BedrockProtocol@97fa88e9ef 2021-10-23 01:16:45 +01:00
c262c2e726 Updated composer dependencies 2021-10-23 01:14:54 +01:00
a4b65d6a3f PlayerCreationEvent: verify that the class actually exists and is instantiable
this ensures that crashdumps blame the plugin instead of the core on bad classes, such as in this case: https://crash.pmmp.io/view/5331225
2021-10-21 20:37:45 +01:00
986b4e0651 Enforce single-line PhpDoc for properties where possible 2021-10-21 20:32:37 +01:00
2971bf30a5 actions: combine code verify into one step
this way the diff takes one less click to get to.
2021-10-21 00:30:19 +01:00
4f2bcb61d6 Fixed crashdump generation when crashing before PluginManager was created 2021-10-20 23:35:04 +01:00
e2275cc8ec PluginManager: Prevent infinite recursion in loadPlugins()
if a plugin calls loadPlugins(server->getPluginPath()) during its onLoad(), and it itself is in that plugin path, an infinite recursion will occur.
2021-10-20 23:10:18 +01:00
620874d902 PluginManager: Extract checkPluginLoadability() to a PluginLoadabilityChecker unit
this can be more easily unit-tested.
2021-10-20 22:31:56 +01:00
44508a138f Moved plugin extension requirement checks to PluginManager::checkPluginLoadability()
these don't really belong in PluginDescription.
2021-10-20 22:13:30 +01:00
aa408c9a97 Fixed 9646128d01 2021-10-20 21:54:57 +01:00
6d78a0b435 CS 2021-10-20 21:52:42 +01:00
76b4b23d98 PluginManager: remove loadPlugin()
loadPlugins() is now the preferred option, since it does all the proper checks.
In addition, the server now acknowledges that loading a single plugin may cause multiple plugins to be loaded, so returning only a single Plugin is not representative of what's actually happening.
2021-10-20 21:52:19 +01:00
03fcd844eb PluginManager::loadPlugins() now accepts files as well as directories
loadPlugins() is now a superior option to loadPlugin(), since it enforces dependency checks and also supports automatic loading of plugins when new loaders are installed.
2021-10-20 21:36:14 +01:00
fecc13f362 Merge branch 'master' of github.com:pmmp/PocketMine-MP 2021-10-20 21:23:14 +01:00
9646128d01 Updated resources/locale submodule to pmmp/Language@09c709f242 2021-10-20 21:22:56 +01:00
a788954551 Fixed dependency handling across plugin loaders (#3971) 2021-10-20 20:22:00 +01:00
dc07ac33d3 protocol: fixed missing field of CraftRecipeAuto 2021-10-20 19:47:32 +01:00
ec3986827c Update BedrockProtocol to 3.0.1, widen constraint to allow newer patch versions 2021-10-20 16:20:10 +01:00
09c840b66a Update transient composer dependencies 2021-10-20 16:19:20 +01:00
80b402e529 ItemTranslator: throw the proper exceptions when failing to map network IDs 2021-10-20 14:01:39 +01:00
a3f8546ac4 4.0.0-BETA7 is next 2021-10-19 19:13:43 +01:00
46920818b5 Release 4.0.0-BETA6 2021-10-19 19:13:43 +01:00
69cb575789 Merge branch 'stable' 2021-10-19 19:05:25 +01:00
fee6478cbe Updated BedrockData and BedrockProtocol for 1.17.40 support 2021-10-19 19:00:29 +01:00
9c5cec77b1 3.25.1 is next 2021-10-19 18:27:30 +01:00
f48b703533 Release 3.25.0 2021-10-19 18:27:26 +01:00
70636f6eb4 Protocol changes for 1.17.40 2021-10-19 18:00:34 +01:00
c70b80c273 ItemEntity: implement partial itemstack pickups in the dumbest way possible
Given the various limitations and flexibilities posed by EntityItemPickupEvent, I settled on this as the simplest way to deal with the problem.

- EntityItemPickupEvent may have its destination inventory changed, so we can't cache the result of getAddableItemQuantity() to use after the event.
- The item itself may have changed, so even if we thought we could add some items before the change, we might not be able to afterwards.

Considering the above facts, it's better to just give the whole itemstack to EntityItemPickupEvent, and let plugins use getAddableItemQuantity() on their own to decide if their chosen inventory can accommodate the item or not.
If it can't, then we'll just drop it on the ground.
This also fixes a potential issue where plugins changing the item to a custom one might end up with their items and the actual items both just vanishing if the target inventory was full.
closes #4499
2021-10-17 22:37:49 +01:00
a794d24c81 Added a tool to generate a Markdown document of all core permissions 2021-10-17 17:02:18 +01:00
8db5732b44 Drop respect/validation
it's not worth this turning into compatibility baggage just so that we can parse plugin_list.yml, especially when we have new ways to handle data parsing coming in the pipeline.
For something as small as plugin_list.yml, it's easier (and in this case better too) to just validate it manually (respect/validation was anyway too strict considering it's YAML we're dealing with).
2021-10-15 17:15:46 +01:00
48f809d3fa Removed another dead PHPStan error pattern
this was actually a PHPStan bug fixed in 0.12.99.
2021-10-15 17:01:09 +01:00
0348236860 fucking CS again 2021-10-14 15:56:50 +01:00
8c07748100 RakLibInterface: print packet exception info as a block using Utils::printableExceptionInfo() 2021-10-14 15:55:08 +01:00
06e7338ff9 Move exception printing utilities from MainLogger to Utils
where they can be useful to other stuff apart from just the logger
2021-10-14 15:54:20 +01:00
bdbfa70558 Server: break some isolated stuff out of Server::__construct() 2021-10-14 15:44:18 +01:00
7a4af7a0bc SignalHandler: fix CS
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
2021-10-14 15:14:27 +01:00
34b1392598 Cross-platform signal handler 2021-10-14 15:03:11 +01:00
321345fcc8 Sapling: simplify condition 2021-10-13 23:00:38 +01:00
0ac9f4fe61 BlockFactory: move SweetBerryBush to its proper place 2021-10-13 22:26:51 +01:00
2db53775e0 Sort item_from_string_bc_map using SORT_NATURAL 2021-10-13 21:01:59 +01:00
8523f0fb0b CS fix 2021-10-13 20:31:24 +01:00
b570324288 LegacyStringToItemParser: rely exclusively on item_from_string_bc_map.json, do not interpret integers given as strings
fixes #4507
2021-10-13 20:29:18 +01:00
6284cd14c7 LegacyStringToItemParser: added getMappings() 2021-10-13 20:19:44 +01:00
ce8af4e3bc 4.0.0-BETA6 is next 2021-10-13 00:02:01 +01:00
299 changed files with 10010 additions and 4944 deletions

1
.gitattributes vendored
View File

@ -4,6 +4,7 @@
*.sh text eol=lf
*.txt text eol=lf
*.properties text eol=lf
*.neon text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

View File

@ -35,17 +35,18 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
- name: Patch VersionInfo
- name: Calculate build number
id: build-number
run: |
BUILD_NUMBER=2000+$GITHUB_RUN_NUMBER #to stay above jenkins
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
echo "Build number: $BUILD_NUMBER"
sed -i "s/const BUILD_NUMBER = 0/const BUILD_NUMBER = ${BUILD_NUMBER}/" src/VersionInfo.php
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
- name: Minify BedrockData JSON files
run: php resources/vanilla/.minify_json.php
run: php vendor/pocketmine/bedrock-data/.minify_json.php
- name: Build PocketMine-MP.phar
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }}
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }} --build ${{ steps.build-number.outputs.BUILD_NUMBER }}
- name: Get PocketMine-MP release version
id: get-pm-version
@ -56,7 +57,7 @@ jobs:
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
- name: Generate build info
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} > build_info.json
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} > build_info.json
- name: Upload release artifacts
uses: actions/upload-artifact@v2

View File

@ -16,17 +16,11 @@ jobs:
php: [8.0.11]
steps:
- uses: actions/checkout@v2 #needed for build.sh
- name: Check for PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Build and prepare PHP cache
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Compile PHP
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: ./tests/gh-actions/build.sh "${{ matrix.php }}"
php-version: ${{ matrix.php }}
install-path: "./bin"
phpstan:
name: PHPStan analysis
@ -42,23 +36,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -91,26 +73,12 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
submodules: true
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -146,23 +114,11 @@ jobs:
with:
submodules: true
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -195,26 +151,12 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
submodules: true
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -238,11 +180,10 @@ jobs:
- name: Regenerate KnownTranslation APIs
run: php build/generate-known-translation-apis.php
- name: Run git diff
run: git diff
- name: Fail job if changes were made
run: git diff --quiet
- name: Verify code is unchanged
run: |
git diff
git diff --quiet
codestyle:
name: Code Style checks
@ -254,10 +195,10 @@ jobs:
- uses: actions/checkout@v2
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.9.0
uses: shivammathur/setup-php@2.15.0
with:
php-version: 8.0
tools: php-cs-fixer
tools: php-cs-fixer:3.2
- name: Run PHP-CS-Fixer
run: php-cs-fixer fix --dry-run --diff

6
.gitmodules vendored
View File

@ -1,12 +1,6 @@
[submodule "resources/locale"]
path = resources/locale
url = https://github.com/pmmp/Language.git
[submodule "tests/plugins/DevTools"]
path = tests/plugins/DevTools
url = https://github.com/pmmp/DevTools.git
[submodule "build/php"]
path = build/php
url = https://github.com/pmmp/php-build-scripts.git
[submodule "resources/vanilla"]
path = resources/vanilla
url = https://github.com/pmmp/BedrockData.git

View File

@ -18,6 +18,9 @@ return (new PhpCsFixer\Config)
'array_syntax' => [
'syntax' => 'short'
],
'binary_operator_spaces' => [
'default' => 'single_space'
],
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => [
@ -61,10 +64,19 @@ return (new PhpCsFixer\Config)
],
'sort_algorithm' => 'alpha'
],
'phpdoc_line_span' => [
'property' => 'single',
'method' => null,
'const' => null
],
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'return_type_declaration' => [
'space_before' => 'one'
],
'single_import_per_statement' => true,
'strict_param' => true,
'unary_operator_spaces' => true,
])
->setFinder($finder)
->setIndent("\t")

View File

@ -14,13 +14,12 @@ Because PocketMine-MP requires several non-standard PHP extensions and configura
If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`.
## Setting up environment
1. `git clone --recursive https://github.com/pmmp/PocketMine-MP.git`
1. `git clone https://github.com/pmmp/PocketMine-MP.git`
2. `composer install`
## Checking out a different branch to build
1. `git checkout <branch to checkout>`
2. `git submodule update --init`
3. Re-run `composer install` to synchronize dependencies.
2. Re-run `composer install` to synchronize dependencies.
## Optimizing for release builds
1. Add the flags `--no-dev --classmap-authoritative` to your `composer install` command. This will reduce build size and improve autoloading speed.

View File

@ -5,9 +5,12 @@
<p align="center">
<img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" />
<a href="https://github.com/pmmp/PocketMine-MP/releases"><img src="https://img.shields.io/github/v/tag/pmmp/PocketMine-MP?label=release&logo=github" alt="GitHub tag (latest semver)" /></a>
<img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver">
<a href="https://hub.docker.com/r/pmmp/pocketmine-mp"><img src="https://img.shields.io/docker/v/pmmp/pocketmine-mp?logo=docker&label=image" alt="Docker image version (latest semver)" /></a>
<a href="https://discord.gg/bmSAZBG"><img src="https://img.shields.io/discord/373199722573201408?label=discord&color=7289DA&logo=discord" alt="Discord" /></a>
<br>
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/total?label=downloads%40total">
<img alt="GitHub release (latest by SemVer)" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/latest/total?sort=semver">
</p>
## Getting started

View File

@ -23,15 +23,15 @@ declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
if(count($argv) !== 4){
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)>");
if(count($argv) !== 5){
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number>");
exit(1);
}
echo json_encode([
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
"base_version" => \pocketmine\VersionInfo::BASE_VERSION,
"build" => \pocketmine\VersionInfo::BUILD_NUMBER,
"build" => (int) $argv[4],
"is_dev" => \pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD,
"channel" => \pocketmine\VersionInfo::BUILD_CHANNEL,
"git_commit" => $argv[1],

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\build\generate_known_translation_apis;
use pocketmine\lang\Translatable;
use pocketmine\utils\Utils;
use Webmozart\PathUtil\Path;
use function array_map;
use function count;
@ -40,6 +41,7 @@ use function preg_match_all;
use function str_replace;
use function strtoupper;
use const INI_SCANNER_RAW;
use const SORT_NUMERIC;
use const SORT_STRING;
use const STDERR;
@ -94,13 +96,15 @@ function generate_known_translation_keys(array $languageDefinitions) : void{
/**
* This class contains constants for all the translations known to PocketMine-MP as per the used version of pmmp/Language.
* This class is generated automatically, do NOT modify it by hand.
*
* @internal
*/
final class KnownTranslationKeys{
HEADER;
ksort($languageDefinitions, SORT_STRING);
foreach($languageDefinitions as $k => $_){
foreach(Utils::stringifyKeys($languageDefinitions) as $k => $_){
echo "\tpublic const ";
echo constantify($k);
echo " = \"" . $k . "\";\n";
@ -126,6 +130,8 @@ function generate_known_translation_factory(array $languageDefinitions) : void{
* This class contains factory methods for all the translations known to PocketMine-MP as per the used version of
* pmmp/Language.
* This class is generated automatically, do NOT modify it by hand.
*
* @internal
*/
final class KnownTranslationFactory{
@ -135,17 +141,22 @@ HEADER;
$parameterRegex = '/{%(.+?)}/';
$translationContainerClass = (new \ReflectionClass(Translatable::class))->getShortName();
foreach($languageDefinitions as $key => $value){
foreach(Utils::stringifyKeys($languageDefinitions) as $key => $value){
$parameters = [];
$allParametersPositional = true;
if(preg_match_all($parameterRegex, $value, $matches) > 0){
foreach($matches[1] as $parameterName){
if(is_numeric($parameterName)){
$parameters[$parameterName] = "param$parameterName";
}else{
$parameters[$parameterName] = $parameterName;
$allParametersPositional = false;
}
}
}
if($allParametersPositional){
ksort($parameters, SORT_NUMERIC);
}
echo "\tpublic static function " .
functionify($key) .
"(" . implode(", ", array_map(fn(string $paramName) => "$translationContainerClass|string \$$paramName", $parameters)) . ") : $translationContainerClass{\n";
@ -172,7 +183,7 @@ HEADER;
echo "Done generating KnownTranslationFactory.\n";
}
$lang = parse_ini_file(Path::join(\pocketmine\RESOURCE_PATH, "locale", "eng.ini"), false, INI_SCANNER_RAW);
$lang = parse_ini_file(Path::join(\pocketmine\LOCALE_DATA_PATH, "eng.ini"), false, INI_SCANNER_RAW);
if($lang === false){
fwrite(STDERR, "Missing language files!\n");
exit(1);

View File

@ -91,7 +91,7 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
throw new \RuntimeException("Failed to get contents of $file");
}
if(preg_match("/^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match('/^((final|abstract)\s+)?class /m', $contents) !== 1){
if(preg_match("/(*ANYCRLF)^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match('/(*ANYCRLF)^((final|abstract)\s+)?class /m', $contents) !== 1){
continue;
}
$shortClassName = basename($file, ".php");
@ -101,7 +101,7 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
}
$reflect = new \ReflectionClass($className);
$docComment = $reflect->getDocComment();
if($docComment === false || preg_match("/^\s*\*\s*@generate-registry-docblock$/m", $docComment) !== 1){
if($docComment === false || preg_match("/(*ANYCRLF)^\s*\*\s*@generate-registry-docblock$/m", $docComment) !== 1){
continue;
}
echo "Found registry in $file\n";

View File

@ -23,20 +23,29 @@ declare(strict_types=1);
namespace pocketmine\build\make_release;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
use pocketmine\VersionInfo;
use function count;
use function array_keys;
use function array_map;
use function dirname;
use function fgets;
use function file_get_contents;
use function file_put_contents;
use function fwrite;
use function getopt;
use function is_string;
use function max;
use function preg_replace;
use function sleep;
use function sprintf;
use function str_pad;
use function strlen;
use function system;
use const STDERR;
use const STDIN;
use const STDOUT;
use const STR_PAD_LEFT;
require_once dirname(__DIR__) . '/vendor/autoload.php';
@ -60,22 +69,38 @@ function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev
file_put_contents($versionInfoPath, $versionInfo);
}
/**
* @param string[] $argv
* @phpstan-param list<string> $argv
*/
function main(array $argv) : void{
if(count($argv) < 2){
fwrite(STDERR, "Arguments: <channel> [release version] [next version]\n");
exit(1);
const ACCEPTED_OPTS = [
"current" => "Version to insert and tag",
"next" => "Version to put in the file after tagging",
"channel" => "Release channel to post this build into"
];
function main() : void{
$filteredOpts = [];
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help"])) as $optName => $optValue){
if($optName === "help"){
fwrite(STDOUT, "Options:\n");
$maxLength = max(array_map(fn(string $str) => strlen($str), array_keys(ACCEPTED_OPTS)));
foreach(ACCEPTED_OPTS as $acceptedName => $description){
fwrite(STDOUT, str_pad("--$acceptedName", $maxLength + 4, " ", STR_PAD_LEFT) . ": $description\n");
}
exit(0);
}
if(!is_string($optValue)){
fwrite(STDERR, "--$optName expects exactly 1 value\n");
exit(1);
}
$filteredOpts[$optName] = $optValue;
}
if(isset($argv[2])){
$currentVer = new VersionString($argv[2]);
if(isset($filteredOpts["current"])){
$currentVer = new VersionString($filteredOpts["current"]);
}else{
$currentVer = VersionInfo::VERSION();
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
}
if(isset($argv[3])){
$nextVer = new VersionString($argv[3]);
if(isset($filteredOpts["next"])){
$nextVer = new VersionString($filteredOpts["next"]);
}else{
$nextVer = new VersionString(sprintf(
"%u.%u.%u",
@ -84,8 +109,10 @@ function main(array $argv) : void{
$currentVer->getPatch() + 1
));
}
$channel = $filteredOpts["channel"] ?? VersionInfo::BUILD_CHANNEL;
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
echo "$currentVer will be published on release channel \"$channel\".\n";
echo "please add appropriate notes to the changelog and press enter...";
fgets(STDIN);
system('git add "' . dirname(__DIR__) . '/changelogs"');
@ -95,10 +122,10 @@ function main(array $argv) : void{
exit(1);
}
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $argv[1]);
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
system('git tag ' . $currentVer->getBaseVersion());
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, "");
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
system('git add "' . $versionInfoPath . '"');
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
echo "pushing changes in 5 seconds\n";
@ -106,4 +133,4 @@ function main(array $argv) : void{
system('git push origin HEAD ' . $currentVer->getBaseVersion());
}
main($argv);
main();

View File

@ -134,13 +134,18 @@ function main() : void{
exit(1);
}
$opts = getopt("", ["out:", "git:"]);
$opts = getopt("", ["out:", "git:", "build:"]);
if(isset($opts["git"])){
$gitHash = $opts["git"];
}else{
$gitHash = Git::getRepositoryStatePretty(dirname(__DIR__));
echo "Git hash detected as $gitHash" . PHP_EOL;
}
if(isset($opts["build"])){
$build = (int) $opts["build"];
}else{
$build = 0;
}
foreach(buildPhar(
$opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar",
dirname(__DIR__) . DIRECTORY_SEPARATOR,
@ -150,7 +155,8 @@ function main() : void{
'vendor'
],
[
'git' => $gitHash
'git' => $gitHash,
'build' => $build
],
<<<'STUB'
<?php

38
changelogs/3.25.md Normal file
View File

@ -0,0 +1,38 @@
**For Minecraft: Bedrock Edition 1.17.40**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 3.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.
# 3.25.0
- Added support for Minecraft: Bedrock Edition 1.17.40.
- Removed compatibility with earlier versions.
# 3.25.1
- Fixed autosave bug that caused unmodified chunks to be saved at least once (during the first autosave after they were loaded).
- `Entity->spawnTo()` now has an additional sanity check for matching worlds (might expose a few new errors in plugins).
- Fixed a missing field in `CraftRecipeAuto` item stack request type.
# 3.25.2
- Now analysed using level 9 on PHPStan 1.0.0.
- `ext-pthreads` v4.0.0 or newer is now required.
- Fixed crash in `Player->showPlayer()` when the target is not in the same world.
- `Human->setLifetimeTotalXp()` now limits the maximum value to 2^31.
- Fixed players, who died in hardcore mode and were unbanned, getting re-banned on next server join.
# 3.25.3
- Fixed crash when players try to pickup XP while already having max XP.
- Added a sanity check to `Human->setCurrentTotalXp()` to try and catch an elusive bug that's been appearing in the wild - please get in touch if you know how to reproduce it!
# 3.25.4
- Fixed a long-standing issue with `Player->removeWindow()` breaking inventory UIs on the client.
# 3.25.5
- Protocol: Fixed incorrect encoding in `StructureSettings`
- Fixed reading tags from non-docblock comments in script plugins.
- Build number is now defined in phar metadata instead of being patched into the source code directly.
# 3.25.6
- Fixed borked build number in release build of 3.25.5.

32
changelogs/3.26.md Normal file
View File

@ -0,0 +1,32 @@
**For Minecraft: Bedrock Edition 1.18.0**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 3.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.
# 3.26.0
- Added support for Minecraft: Bedrock Edition 1.18.0.
- Removed compatibility with earlier versions.
# 3.26.1
- Fixed a bug in chunk sending that caused double chests to not be paired, signs to be blank, and various other issues.
# 3.26.2
- Improved error messages shown by `start.cmd`, `start.sh` and `start.ps1` when the PHP binary was not found.
- The value of PHPRC is now shown when erroring out due to unsatisfied PHP requirements.
- Removed restriction on the range of valid channels for `auto-updater.channel` in `pocketmine.yml`.
# 3.26.3
- `PlayerExperienceChangeEvent->setNewProgress()` now performs range checks. This fixes the root of a very old and confusing crash bug which took several years to identify the cause of.
- Note that the defective plugin(s) which caused this problem will still cause a server crash, but the plugin responsible will now get blamed correctly.
# 3.26.4
- Fixed skins appearing black when using RTX resource packs.
- Fixed chunks containing furnaces in old worlds (pre-2017) being discarded as corrupted.
- This was caused by a strict corruption check detecting bad data created by a bug in PocketMine-MP that was fixed in 2017.
# 3.26.5
- Fixed several denial-of-service attack vectors related to writable book text length and encoding.
- Fixed several denial-of-service attack vectors related to skin data field lengths.

1767
changelogs/4.0-beta.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
"require": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.0",
"ext-chunkutils2": "^0.3.1",
"ext-crypto": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
@ -22,7 +22,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pthreads": "~3.2.0",
"ext-pthreads": "^4.0",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
@ -34,28 +34,28 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-protocol": "2.0.0+bedrock1.17.30",
"pocketmine/bedrock-data": "~1.5.0+bedrock-1.18.0",
"pocketmine/bedrock-protocol": "~7.1.0+bedrock-1.18.0",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "dev-master",
"pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0",
"pocketmine/errorhandler": "^0.3.0",
"pocketmine/log": "^0.3.0",
"pocketmine/log-pthreads": "^0.2.0",
"pocketmine/math": "^0.3.0",
"pocketmine/nbt": "^0.3.0",
"pocketmine/locale-data": "~2.3.0",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0",
"pocketmine/nbt": "^0.3.2",
"pocketmine/raklib": "^0.14.2",
"pocketmine/raklib-ipc": "^0.1.0",
"pocketmine/snooze": "^0.3.0",
"pocketmine/spl": "dev-master",
"ramsey/uuid": "^4.1",
"respect/validation": "^2.0",
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-phpunit": "^0.12.6",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpstan/phpstan": "1.2.0",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.2"
},
"autoload": {
@ -79,7 +79,7 @@
"sort-packages": true
},
"scripts": {
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/DevTools/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
"make-server": [
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
"@php -dphar.readonly=0 build/server-phar.php"

617
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,9 @@
includes:
- tests/phpstan/configs/actual-problems.neon
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
- tests/phpstan/configs/gc-hacks.neon
- tests/phpstan/configs/impossible-generics.neon
- tests/phpstan/configs/l7-baseline.neon
- tests/phpstan/configs/l8-baseline.neon
- tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon
- tests/phpstan/configs/pthreads-bugs.neon
- tests/phpstan/configs/runtime-type-checks.neon
- tests/phpstan/configs/spl-fixed-array-sucks.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
@ -16,11 +12,11 @@ includes:
rules:
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
parameters:
level: 8
checkExplicitMixed: true
level: 9
checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false
bootstrapFiles:

View File

@ -1,4 +1,691 @@
{
"-2": -2,
"-3": -3,
"-4": -4,
"-5": -5,
"-6": -6,
"-7": -7,
"-8": -8,
"-9": -9,
"-10": -10,
"-11": -11,
"-12": -12,
"-13": -13,
"-14": -14,
"-15": -15,
"-16": -16,
"-17": -17,
"-18": -18,
"-19": -19,
"-20": -20,
"-21": -21,
"-22": -22,
"-23": -23,
"-24": -24,
"-25": -25,
"-26": -26,
"-27": -27,
"-28": -28,
"-29": -29,
"-30": -30,
"-31": -31,
"-32": -32,
"-33": -33,
"-34": -34,
"-35": -35,
"-36": -36,
"-37": -37,
"-38": -38,
"-39": -39,
"-40": -40,
"-41": -41,
"-42": -42,
"-43": -43,
"-44": -44,
"-45": -45,
"-46": -46,
"-47": -47,
"-48": -48,
"-49": -49,
"-50": -50,
"-51": -51,
"-52": -52,
"-53": -53,
"-54": -54,
"-55": -55,
"-56": -56,
"-57": -57,
"-58": -58,
"-59": -59,
"-60": -60,
"-61": -61,
"-62": -62,
"-63": -63,
"-64": -64,
"-65": -65,
"-66": -66,
"-67": -67,
"-68": -68,
"-69": -69,
"-70": -70,
"-71": -71,
"-72": -72,
"-73": -73,
"-74": -74,
"-75": -75,
"-76": -76,
"-77": -77,
"-78": -78,
"-79": -79,
"-80": -80,
"-81": -81,
"-82": -82,
"-83": -83,
"-84": -84,
"-85": -85,
"-86": -86,
"-87": -87,
"-88": -88,
"-89": -89,
"-90": -90,
"-91": -91,
"-92": -92,
"-93": -93,
"-94": -94,
"-95": -95,
"-96": -96,
"-97": -97,
"-98": -98,
"-99": -99,
"-100": -100,
"-101": -101,
"-102": -102,
"-103": -103,
"-104": -104,
"-105": -105,
"-106": -106,
"-107": -107,
"-108": -108,
"-109": -109,
"-110": -110,
"-111": -111,
"-112": -112,
"-113": -113,
"-114": -114,
"-115": -115,
"-116": -116,
"-117": -117,
"-118": -118,
"-119": -119,
"-120": -120,
"-121": -121,
"-122": -122,
"-123": -123,
"-124": -124,
"-125": -125,
"-126": -126,
"-127": -127,
"-128": -128,
"-129": -129,
"-130": -130,
"-131": -131,
"-132": -132,
"-133": -133,
"-134": -134,
"-135": -135,
"-136": -136,
"-137": -137,
"-138": -138,
"-139": -139,
"-140": -140,
"-141": -141,
"-142": -142,
"-143": -143,
"-144": -144,
"-145": -145,
"-146": -146,
"-147": -147,
"-148": -148,
"-149": -149,
"-150": -150,
"-151": -151,
"-152": -152,
"-153": -153,
"-154": -154,
"-155": -155,
"-156": -156,
"-157": -157,
"-159": -159,
"-160": -160,
"-161": -161,
"-162": -162,
"-163": -163,
"-164": -164,
"-165": -165,
"-166": -166,
"-167": -167,
"-168": -168,
"-169": -169,
"-170": -170,
"-171": -171,
"-172": -172,
"-173": -173,
"-174": -174,
"-175": -175,
"-176": -176,
"-177": -177,
"-178": -178,
"-179": -179,
"-180": -180,
"-181": -181,
"-182": -182,
"-183": -183,
"-184": -184,
"-185": -185,
"-186": -186,
"-187": -187,
"-188": -188,
"-189": -189,
"-190": -190,
"-191": -191,
"-192": -192,
"-193": -193,
"-194": -194,
"-195": -195,
"-196": -196,
"-197": -197,
"-198": -198,
"-199": -199,
"-200": -200,
"-201": -201,
"-202": -202,
"-203": -203,
"-204": -204,
"-206": -206,
"-207": -207,
"-208": -208,
"-209": -209,
"-210": -210,
"-211": -211,
"-213": -213,
"-214": -214,
"0": 0,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"10": 10,
"11": 11,
"12": 12,
"13": 13,
"14": 14,
"15": 15,
"16": 16,
"17": 17,
"18": 18,
"19": 19,
"20": 20,
"21": 21,
"22": 22,
"23": 23,
"24": 24,
"25": 25,
"26": 26,
"27": 27,
"28": 28,
"29": 29,
"30": 30,
"31": 31,
"32": 32,
"33": 33,
"34": 34,
"35": 35,
"36": 36,
"37": 37,
"38": 38,
"39": 39,
"40": 40,
"41": 41,
"42": 42,
"43": 43,
"44": 44,
"45": 45,
"46": 46,
"47": 47,
"48": 48,
"49": 49,
"50": 50,
"51": 51,
"52": 52,
"53": 53,
"54": 54,
"55": 55,
"56": 56,
"57": 57,
"58": 58,
"59": 59,
"60": 60,
"61": 61,
"62": 62,
"63": 63,
"64": 64,
"65": 65,
"66": 66,
"67": 67,
"68": 68,
"69": 69,
"70": 70,
"71": 71,
"72": 72,
"73": 73,
"74": 74,
"75": 75,
"76": 76,
"77": 77,
"78": 78,
"79": 79,
"80": 80,
"81": 81,
"82": 82,
"83": 83,
"84": 84,
"85": 85,
"86": 86,
"87": 87,
"88": 88,
"89": 89,
"90": 90,
"91": 91,
"92": 92,
"93": 93,
"94": 94,
"95": 95,
"96": 96,
"97": 97,
"98": 98,
"99": 99,
"100": 100,
"101": 101,
"102": 102,
"103": 103,
"104": 104,
"105": 105,
"106": 106,
"107": 107,
"108": 108,
"109": 109,
"110": 110,
"111": 111,
"112": 112,
"113": 113,
"114": 114,
"115": 115,
"116": 116,
"117": 117,
"118": 118,
"119": 119,
"120": 120,
"121": 121,
"122": 122,
"123": 123,
"124": 124,
"125": 125,
"126": 126,
"127": 127,
"128": 128,
"129": 129,
"130": 130,
"131": 131,
"132": 132,
"133": 133,
"134": 134,
"135": 135,
"136": 136,
"137": 137,
"138": 138,
"139": 139,
"140": 140,
"141": 141,
"142": 142,
"143": 143,
"144": 144,
"145": 145,
"146": 146,
"147": 147,
"148": 148,
"149": 149,
"150": 150,
"151": 151,
"152": 152,
"153": 153,
"154": 154,
"155": 155,
"156": 156,
"157": 157,
"158": 158,
"159": 159,
"160": 160,
"161": 161,
"162": 162,
"163": 163,
"164": 164,
"165": 165,
"166": 166,
"167": 167,
"168": 168,
"169": 169,
"170": 170,
"171": 171,
"172": 172,
"173": 173,
"174": 174,
"175": 175,
"176": 176,
"177": 177,
"178": 178,
"179": 179,
"180": 180,
"181": 181,
"182": 182,
"183": 183,
"184": 184,
"185": 185,
"186": 186,
"187": 187,
"188": 188,
"189": 189,
"190": 190,
"191": 191,
"192": 192,
"193": 193,
"194": 194,
"195": 195,
"196": 196,
"197": 197,
"198": 198,
"199": 199,
"200": 200,
"201": 201,
"202": 202,
"203": 203,
"204": 204,
"205": 205,
"206": 206,
"207": 207,
"208": 208,
"209": 209,
"213": 213,
"214": 214,
"215": 215,
"216": 216,
"218": 218,
"219": 219,
"220": 220,
"221": 221,
"222": 222,
"223": 223,
"224": 224,
"225": 225,
"226": 226,
"227": 227,
"228": 228,
"229": 229,
"231": 231,
"232": 232,
"233": 233,
"234": 234,
"235": 235,
"236": 236,
"237": 237,
"238": 238,
"239": 239,
"240": 240,
"241": 241,
"243": 243,
"244": 244,
"245": 245,
"246": 246,
"247": 247,
"248": 248,
"249": 249,
"250": 250,
"251": 251,
"252": 252,
"253": 253,
"254": 254,
"255": 255,
"256": 256,
"257": 257,
"258": 258,
"259": 259,
"260": 260,
"261": 261,
"262": 262,
"263": 263,
"264": 264,
"265": 265,
"266": 266,
"267": 267,
"268": 268,
"269": 269,
"270": 270,
"271": 271,
"272": 272,
"273": 273,
"274": 274,
"275": 275,
"276": 276,
"277": 277,
"278": 278,
"279": 279,
"280": 280,
"281": 281,
"282": 282,
"283": 283,
"284": 284,
"285": 285,
"286": 286,
"287": 287,
"288": 288,
"289": 289,
"290": 290,
"291": 291,
"292": 292,
"293": 293,
"294": 294,
"295": 295,
"296": 296,
"297": 297,
"298": 298,
"299": 299,
"300": 300,
"301": 301,
"302": 302,
"303": 303,
"304": 304,
"305": 305,
"306": 306,
"307": 307,
"308": 308,
"309": 309,
"310": 310,
"311": 311,
"312": 312,
"313": 313,
"314": 314,
"315": 315,
"316": 316,
"317": 317,
"318": 318,
"319": 319,
"320": 320,
"321": 321,
"322": 322,
"323": 323,
"324": 324,
"325": 325,
"328": 328,
"329": 329,
"330": 330,
"331": 331,
"332": 332,
"333": 333,
"334": 334,
"335": 335,
"336": 336,
"337": 337,
"338": 338,
"339": 339,
"340": 340,
"341": 341,
"342": 342,
"344": 344,
"345": 345,
"346": 346,
"347": 347,
"348": 348,
"349": 349,
"350": 350,
"351": 351,
"352": 352,
"353": 353,
"354": 354,
"355": 355,
"356": 356,
"357": 357,
"358": 358,
"359": 359,
"360": 360,
"361": 361,
"362": 362,
"363": 363,
"364": 364,
"365": 365,
"366": 366,
"367": 367,
"368": 368,
"369": 369,
"370": 370,
"371": 371,
"372": 372,
"373": 373,
"374": 374,
"375": 375,
"376": 376,
"377": 377,
"378": 378,
"379": 379,
"380": 380,
"381": 381,
"382": 382,
"383": 383,
"384": 384,
"385": 385,
"386": 386,
"387": 387,
"388": 388,
"389": 389,
"390": 390,
"391": 391,
"392": 392,
"393": 393,
"394": 394,
"395": 395,
"396": 396,
"397": 397,
"398": 398,
"399": 399,
"400": 400,
"401": 401,
"402": 402,
"403": 403,
"404": 404,
"405": 405,
"406": 406,
"407": 407,
"408": 408,
"409": 409,
"410": 410,
"411": 411,
"412": 412,
"413": 413,
"414": 414,
"415": 415,
"416": 416,
"417": 417,
"418": 418,
"419": 419,
"420": 420,
"421": 421,
"422": 422,
"423": 423,
"424": 424,
"425": 425,
"426": 426,
"427": 427,
"428": 428,
"429": 429,
"430": 430,
"431": 431,
"432": 432,
"433": 433,
"434": 434,
"437": 437,
"438": 438,
"441": 441,
"442": 442,
"443": 443,
"444": 444,
"445": 445,
"446": 446,
"447": 447,
"448": 448,
"449": 449,
"450": 450,
"451": 451,
"452": 452,
"453": 453,
"455": 455,
"457": 457,
"458": 458,
"459": 459,
"460": 460,
"461": 461,
"462": 462,
"463": 463,
"464": 464,
"465": 465,
"466": 466,
"467": 467,
"468": 468,
"469": 469,
"470": 470,
"471": 471,
"472": 472,
"473": 473,
"474": 474,
"475": 475,
"476": 476,
"477": 477,
"499": 499,
"500": 500,
"501": 501,
"502": 502,
"503": 503,
"504": 504,
"505": 505,
"506": 506,
"507": 507,
"508": 508,
"509": 509,
"510": 510,
"511": 511,
"513": 513,
"acacia_button": -140,
"acacia_door": 430,
"acacia_door_block": 196,
@ -226,27 +913,16 @@
"egg": 344,
"element_0": 36,
"element_1": -12,
"element_2": -13,
"element_3": -14,
"element_4": -15,
"element_5": -16,
"element_6": -17,
"element_7": -18,
"element_8": -19,
"element_9": -20,
"element_10": -21,
"element_100": -111,
"element_101": -112,
"element_102": -113,
"element_103": -114,
"element_104": -115,
"element_105": -116,
"element_106": -117,
"element_107": -118,
"element_108": -119,
"element_109": -120,
"element_11": -22,
"element_110": -121,
"element_111": -122,
"element_112": -123,
"element_113": -124,
"element_114": -125,
"element_115": -126,
"element_116": -127,
"element_117": -128,
"element_118": -129,
"element_12": -23,
"element_13": -24,
"element_14": -25,
@ -255,7 +931,6 @@
"element_17": -28,
"element_18": -29,
"element_19": -30,
"element_2": -13,
"element_20": -31,
"element_21": -32,
"element_22": -33,
@ -266,7 +941,6 @@
"element_27": -38,
"element_28": -39,
"element_29": -40,
"element_3": -14,
"element_30": -41,
"element_31": -42,
"element_32": -43,
@ -277,7 +951,6 @@
"element_37": -48,
"element_38": -49,
"element_39": -50,
"element_4": -15,
"element_40": -51,
"element_41": -52,
"element_42": -53,
@ -288,7 +961,6 @@
"element_47": -58,
"element_48": -59,
"element_49": -60,
"element_5": -16,
"element_50": -61,
"element_51": -62,
"element_52": -63,
@ -299,7 +971,6 @@
"element_57": -68,
"element_58": -69,
"element_59": -70,
"element_6": -17,
"element_60": -71,
"element_61": -72,
"element_62": -73,
@ -310,7 +981,6 @@
"element_67": -78,
"element_68": -79,
"element_69": -80,
"element_7": -18,
"element_70": -81,
"element_71": -82,
"element_72": -83,
@ -321,7 +991,6 @@
"element_77": -88,
"element_78": -89,
"element_79": -90,
"element_8": -19,
"element_80": -91,
"element_81": -92,
"element_82": -93,
@ -332,7 +1001,6 @@
"element_87": -98,
"element_88": -99,
"element_89": -100,
"element_9": -20,
"element_90": -101,
"element_91": -102,
"element_92": -103,
@ -343,6 +1011,25 @@
"element_97": -108,
"element_98": -109,
"element_99": -110,
"element_100": -111,
"element_101": -112,
"element_102": -113,
"element_103": -114,
"element_104": -115,
"element_105": -116,
"element_106": -117,
"element_107": -118,
"element_108": -119,
"element_109": -120,
"element_110": -121,
"element_111": -122,
"element_112": -123,
"element_113": -124,
"element_114": -125,
"element_115": -126,
"element_116": -127,
"element_117": -128,
"element_118": -129,
"elytra": 444,
"emerald": 388,
"emerald_block": 133,
@ -532,8 +1219,8 @@
"leather_pants": 300,
"leather_tunic": 299,
"leave": 18,
"leaves": 18,
"leave2": 161,
"leaves": 18,
"leaves2": 161,
"lectern": -194,
"lever": 69,

Submodule resources/locale deleted from 17fc6a1050

View File

@ -108,7 +108,7 @@ player:
verify-xuid: true
level-settings:
#The default format that levels will use when created
#The default format that worlds will use when created
default-format: leveldb
chunk-sending:
@ -176,7 +176,7 @@ aliases:
#savestop: [save-all, stop]
worlds:
#These settings will override the generator set in server.properties and allows loading multiple levels
#These settings will override the generator set in server.properties and allows loading multiple worlds
#Example:
#world:
# seed: 404

View File

@ -35,4 +35,6 @@ define('pocketmine\_CORE_CONSTANTS_INCLUDED', true);
define('pocketmine\PATH', dirname(__DIR__) . '/');
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
define('pocketmine\BEDROCK_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-data/');
define('pocketmine\LOCALE_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/locale-data/');
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine;
use pocketmine\event\server\LowMemoryEvent;
use pocketmine\network\mcpe\cache\ChunkCache;
use pocketmine\scheduler\DumpWorkerMemoryTask;
use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings;
@ -187,6 +188,7 @@ class MemoryManager{
foreach($this->server->getWorldManager()->getWorlds() as $world){
$world->clearCache(true);
}
ChunkCache::pruneCaches();
}
if($this->lowMemChunkGC){
@ -323,6 +325,9 @@ class MemoryManager{
if(!$property->isPublic()){
$property->setAccessible(true);
}
if(!$property->isInitialized()){
continue;
}
$staticCount++;
$staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
@ -350,35 +355,33 @@ class MemoryManager{
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$logger->info("Wrote $staticCount static properties");
if(isset($GLOBALS)){ //This might be null if we're on a different thread
$globalVariables = [];
$globalCount = 0;
$globalVariables = [];
$globalCount = 0;
$ignoredGlobals = [
'GLOBALS' => true,
'_SERVER' => true,
'_REQUEST' => true,
'_POST' => true,
'_GET' => true,
'_FILES' => true,
'_ENV' => true,
'_COOKIE' => true,
'_SESSION' => true
];
$ignoredGlobals = [
'GLOBALS' => true,
'_SERVER' => true,
'_REQUEST' => true,
'_POST' => true,
'_GET' => true,
'_FILES' => true,
'_ENV' => true,
'_COOKIE' => true,
'_SESSION' => true
];
foreach($GLOBALS as $varName => $value){
if(isset($ignoredGlobals[$varName])){
continue;
}
$globalCount++;
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
foreach(Utils::stringifyKeys($GLOBALS) as $varName => $value){
if(isset($ignoredGlobals[$varName])){
continue;
}
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$logger->info("Wrote $globalCount global variables");
$globalCount++;
$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));
$logger->info("Wrote $globalCount global variables");
foreach(get_defined_functions()["user"] as $function){
$reflect = new \ReflectionFunction($function);
@ -448,6 +451,9 @@ class MemoryManager{
if(!$property->isPublic()){
$property->setAccessible(true);
}
if(!$property->isInitialized($object)){
continue;
}
$info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}

View File

@ -34,6 +34,7 @@ namespace pocketmine {
use pocketmine\utils\Timezone;
use pocketmine\wizard\SetupWizard;
use Webmozart\PathUtil\Path;
use function defined;
use function extension_loaded;
use function phpversion;
use function preg_match;
@ -116,8 +117,8 @@ namespace pocketmine {
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "3.2.0") < 0){
$messages[] = "pthreads >= 3.2.0 is required, while you have $pthreads_version.";
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") > 0){
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
}
}
@ -126,6 +127,9 @@ namespace pocketmine {
if(version_compare($leveldb_version, "0.2.1") < 0){
$messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
}
if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
$messages[] = "Given version of php-leveldb doesn't support ZLIB_RAW compression (use https://github.com/pmmp/php-leveldb)";
}
}
$chunkutils2_version = phpversion("chunkutils2");
@ -142,6 +146,10 @@ namespace pocketmine {
$messages[] = "The native PocketMine extension is no longer supported.";
}
if(!defined('AF_INET6')){
$messages[] = "IPv6 support is required, but your PHP binary was built without IPv6 support.";
}
return $messages;
}
@ -205,6 +213,8 @@ JIT_WARNING
}
critical_error("PHP binary used: " . $binary);
critical_error("Loaded php.ini: " . (($file = php_ini_loaded_file()) !== false ? $file : "none"));
$phprc = getenv("PHPRC");
critical_error("Value of PHPRC environment variable: " . ($phprc === false ? "" : $phprc));
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
echo PHP_EOL;
exit(1);

View File

@ -34,6 +34,8 @@ use pocketmine\console\ConsoleCommandSender;
use pocketmine\console\ConsoleReaderThread;
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;
@ -64,6 +66,7 @@ use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\raklib\RakLibInterface;
use pocketmine\network\Network;
use pocketmine\network\NetworkInterfaceStartException;
use pocketmine\network\query\DedicatedQueryNetworkInterface;
use pocketmine\network\query\QueryHandler;
use pocketmine\network\query\QueryInfo;
@ -81,6 +84,8 @@ use pocketmine\plugin\PluginGraylist;
use pocketmine\plugin\PluginManager;
use pocketmine\plugin\PluginOwned;
use pocketmine\plugin\ScriptPluginLoader;
use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
use pocketmine\resourcepacks\ResourcePackManager;
use pocketmine\scheduler\AsyncPool;
use pocketmine\snooze\SleeperHandler;
@ -97,7 +102,7 @@ use pocketmine\utils\MainLogger;
use pocketmine\utils\NotCloneable;
use pocketmine\utils\NotSerializable;
use pocketmine\utils\Process;
use pocketmine\utils\Promise;
use pocketmine\utils\SignalHandler;
use pocketmine\utils\Terminal;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
@ -117,13 +122,18 @@ use function base64_encode;
use function cli_set_process_title;
use function copy;
use function count;
use function date;
use function fclose;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function filemtime;
use function fopen;
use function get_class;
use function ini_set;
use function is_array;
use function is_dir;
use function is_resource;
use function is_string;
use function json_decode;
use function max;
@ -250,6 +260,8 @@ class Server{
/** @var Player[] */
private array $playerList = [];
private SignalHandler $signalHandler;
/**
* @var CommandSender[][]
* @phpstan-var array<string, array<int, CommandSender>>
@ -315,6 +327,10 @@ class Server{
return $this->configGroup->getConfigInt("server-port", 19132);
}
public function getPortV6() : int{
return $this->configGroup->getConfigInt("server-portv6", 19133);
}
public function getViewDistance() : int{
return max(2, $this->configGroup->getConfigInt("view-distance", 8));
}
@ -331,6 +347,11 @@ class Server{
return $str !== "" ? $str : "0.0.0.0";
}
public function getIpV6() : string{
$str = $this->configGroup->getConfigString("server-ipv6");
return $str !== "" ? $str : "::";
}
public function getServerUniqueId() : UuidInterface{
return $this->serverID;
}
@ -516,9 +537,13 @@ 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");
}
try{
file_put_contents($this->getPlayerDataPath($name), zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP));
}catch(\ErrorException $e){
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
}catch(\RuntimeException | \ErrorException $e){
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
$this->logger->logException($e);
}
@ -545,11 +570,11 @@ class Server{
$playerPos = null;
$spawn = $world->getSpawnLocation();
}
$playerPromise = new Promise();
$playerPromiseResolver = new PromiseResolver();
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
function() use ($playerPromise, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
function() use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
if(!$session->isConnected()){
$playerPromise->reject();
$playerPromiseResolver->reject();
return;
}
@ -569,16 +594,16 @@ class Server{
if(!$player->hasPlayedBefore()){
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
}
$playerPromise->resolve($player);
$playerPromiseResolver->resolve($player);
},
static function() use ($playerPromise, $session) : void{
static function() use ($playerPromiseResolver, $session) : void{
if($session->isConnected()){
$session->disconnect("Spawn terrain generation failed");
}
$playerPromise->reject();
$playerPromiseResolver->reject();
}
);
return $playerPromise;
return $playerPromiseResolver->getPromise();
}
/**
@ -669,7 +694,13 @@ class Server{
}
public function removeOp(string $name) : void{
$this->operators->remove(strtolower($name));
$lowercaseName = strtolower($name);
foreach($this->operators->getAll() as $operatorName => $_){
$operatorName = (string) $operatorName;
if($lowercaseName === strtolower($operatorName)){
$this->operators->remove($operatorName);
}
}
if(($player = $this->getPlayerExact($name)) !== null){
$player->unsetBasePermission(DefaultPermissions::ROOT_OPERATOR);
@ -734,7 +765,7 @@ class Server{
public function __construct(\DynamicClassLoader $autoloader, \AttachableThreadedLogger $logger, string $dataPath, string $pluginPath){
if(self::$instance !== null){
throw new \InvalidStateException("Only one server instance can exist at once");
throw new \LogicException("Only one server instance can exist at once");
}
self::$instance = $this;
$this->startTime = microtime(true);
@ -743,6 +774,11 @@ class Server{
$this->autoloader = $autoloader;
$this->logger = $logger;
$this->signalHandler = new SignalHandler(function() : void{
$this->logger->info("Received signal interrupt, stopping the server");
$this->shutdown();
});
try{
foreach([
$dataPath,
@ -773,6 +809,8 @@ class Server{
new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES, [
"motd" => VersionInfo::NAME . " Server",
"server-port" => 19132,
"server-portv6" => 19133,
"enable-ipv6" => true,
"white-list" => false,
"max-players" => 20,
"gamemode" => 0,
@ -922,7 +960,7 @@ class Server{
$this->commandMap = new SimpleCommandMap($this);
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\RESOURCE_PATH, "vanilla", "recipes.json"));
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes.json"));
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
@ -965,115 +1003,14 @@ class Server{
$this->pluginManager->loadPlugins($this->pluginPath);
$this->enablePlugins(PluginEnableOrder::STARTUP());
$getGenerator = function(string $generatorName, string $generatorOptions, string $worldName) : ?string{
$generatorEntry = GeneratorManager::getInstance()->getGenerator($generatorName);
if($generatorEntry === null){
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
$worldName,
KnownTranslationFactory::pocketmine_level_unknownGenerator($generatorName)
)));
return null;
}
try{
$generatorEntry->validateGeneratorOptions($generatorOptions);
}catch(InvalidGeneratorOptionsException $e){
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
$worldName,
KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions($generatorOptions, $generatorName, $e->getMessage())
)));
return null;
}
return $generatorEntry->getGeneratorClass();
};
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
if($options === null){
$options = [];
}elseif(!is_array($options)){
continue;
}
if(!$this->worldManager->loadWorld($name, true) && !$this->worldManager->isWorldGenerated($name)){
$creationOptions = WorldCreationOptions::create();
//TODO: error checking
$generatorName = $options["generator"] ?? "default";
$generatorOptions = isset($options["preset"]) && is_string($options["preset"]) ? $options["preset"] : "";
$generatorClass = $getGenerator($generatorName, $generatorOptions, $name);
if($generatorClass === null){
continue;
}
$creationOptions->setGeneratorClass($generatorClass);
$creationOptions->setGeneratorOptions($generatorOptions);
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
}
if(isset($options["seed"])){
$convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? ""));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
}
$this->worldManager->generateWorld($name, $creationOptions);
}
if(!$this->startupPrepareWorlds()){
return;
}
if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString("level-name", "world");
if(trim($default) == ""){
$this->getLogger()->warning("level-name cannot be null, using default");
$default = "world";
$this->configGroup->setConfigString("level-name", "world");
}
if(!$this->worldManager->loadWorld($default, true) && !$this->worldManager->isWorldGenerated($default)){
$generatorName = $this->configGroup->getConfigString("level-type");
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
if($generatorClass !== null){
$creationOptions = WorldCreationOptions::create()
->setGeneratorClass($generatorClass)
->setGeneratorOptions($generatorOptions);
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
$this->worldManager->generateWorld($default, $creationOptions);
}
}
$world = $this->worldManager->getWorldByName($default);
if($world === null){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
$this->forceShutdown();
return;
}
$this->worldManager->setDefaultWorld($world);
}
$this->enablePlugins(PluginEnableOrder::POSTWORLD());
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
if(!$this->network->registerInterface(new RakLibInterface($this)) && $useQuery){
//RakLib would normally handle the transport for Query packets
//if it's not registered we need to make sure Query still works
$this->network->registerInterface(new DedicatedQueryNetworkInterface($this->getIp(), $this->getPort(), new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
}
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($this->getIp(), (string) $this->getPort())));
if($useQuery){
$this->network->registerRawPacketHandler(new QueryHandler($this));
}
foreach($this->getIPBans()->getEntries() as $entry){
$this->network->blockAddress($entry->getName(), -1);
}
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
if(!$this->startupPrepareNetworkInterfaces()){
$this->forceShutdown();
return;
}
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
@ -1111,6 +1048,153 @@ class Server{
}
}
private function startupPrepareWorlds() : bool{
$getGenerator = function(string $generatorName, string $generatorOptions, string $worldName) : ?string{
$generatorEntry = GeneratorManager::getInstance()->getGenerator($generatorName);
if($generatorEntry === null){
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
$worldName,
KnownTranslationFactory::pocketmine_level_unknownGenerator($generatorName)
)));
return null;
}
try{
$generatorEntry->validateGeneratorOptions($generatorOptions);
}catch(InvalidGeneratorOptionsException $e){
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
$worldName,
KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions($generatorOptions, $generatorName, $e->getMessage())
)));
return null;
}
return $generatorEntry->getGeneratorClass();
};
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
if($options === null){
$options = [];
}elseif(!is_array($options)){
continue;
}
if(!$this->worldManager->loadWorld($name, true) && !$this->worldManager->isWorldGenerated($name)){
$creationOptions = WorldCreationOptions::create();
//TODO: error checking
$generatorName = $options["generator"] ?? "default";
$generatorOptions = isset($options["preset"]) && is_string($options["preset"]) ? $options["preset"] : "";
$generatorClass = $getGenerator($generatorName, $generatorOptions, $name);
if($generatorClass === null){
continue;
}
$creationOptions->setGeneratorClass($generatorClass);
$creationOptions->setGeneratorOptions($generatorOptions);
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
}
if(isset($options["seed"])){
$convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? ""));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
}
$this->worldManager->generateWorld($name, $creationOptions);
}
}
if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString("level-name", "world");
if(trim($default) == ""){
$this->getLogger()->warning("level-name cannot be null, using default");
$default = "world";
$this->configGroup->setConfigString("level-name", "world");
}
if(!$this->worldManager->loadWorld($default, true) && !$this->worldManager->isWorldGenerated($default)){
$generatorName = $this->configGroup->getConfigString("level-type");
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
if($generatorClass !== null){
$creationOptions = WorldCreationOptions::create()
->setGeneratorClass($generatorClass)
->setGeneratorOptions($generatorOptions);
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
if($convertedSeed !== null){
$creationOptions->setSeed($convertedSeed);
}
$this->worldManager->generateWorld($default, $creationOptions);
}
}
$world = $this->worldManager->getWorldByName($default);
if($world === null){
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
$this->forceShutdown();
return false;
}
$this->worldManager->setDefaultWorld($world);
}
return true;
}
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
$prettyIp = $ipV6 ? "[$ip]" : $ip;
try{
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6));
}catch(NetworkInterfaceStartException $e){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
$ip,
(string) $port,
$e->getMessage()
)));
return false;
}
if($rakLibRegistered){
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
}
if($useQuery){
if(!$rakLibRegistered){
//RakLib would normally handle the transport for Query packets
//if it's not registered we need to make sure Query still works
$this->network->registerInterface(new DedicatedQueryNetworkInterface($ip, $port, $ipV6, new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
}
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_query_running($prettyIp, (string) $port)));
}
return true;
}
private function startupPrepareNetworkInterfaces() : bool{
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
if(
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery) ||
(
$this->configGroup->getConfigBool("enable-ipv6", true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery)
)
){
return false;
}
if($useQuery){
$this->network->registerRawPacketHandler(new QueryHandler($this));
}
foreach($this->getIPBans()->getEntries() as $entry){
$this->network->blockAddress($entry->getName(), -1);
}
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
}
return true;
}
/**
* Subscribes to a particular message broadcast channel.
* The channel ID can be any arbitrary string.
@ -1135,7 +1219,7 @@ class Server{
* Unsubscribes from all broadcast channels.
*/
public function unsubscribeFromAllBroadcastChannels(CommandSender $subscriber) : void{
foreach($this->broadcastSubscribers as $channelId => $recipients){
foreach(Utils::stringifyKeys($this->broadcastSubscribers) as $channelId => $recipients){
$this->unsubscribeFromBroadcastChannel($channelId, $subscriber);
}
}
@ -1326,7 +1410,10 @@ class Server{
* Shuts the server down correctly
*/
public function shutdown() : void{
$this->isRunning = false;
if($this->isRunning){
$this->isRunning = false;
$this->signalHandler->unregister();
}
}
public function forceShutdown() : void{
@ -1338,6 +1425,9 @@ class Server{
echo "\x1b]0;\x07";
}
if($this->isRunning){
$this->logger->emergency("Forcing server shutdown");
}
try{
if(!$this->isRunning()){
$this->sendUsage(SendUsageTask::TYPE_CLOSE);
@ -1436,6 +1526,25 @@ class Server{
$this->crashDump();
}
private function writeCrashDumpFile(CrashDump $dump) : string{
$crashFolder = Path::join($this->getDataPath(), "crashdumps");
if(!is_dir($crashFolder)){
mkdir($crashFolder);
}
$crashDumpPath = Path::join($crashFolder, date("D_M_j-H.i.s-T_Y", (int) $dump->getData()->time) . ".log");
$fp = @fopen($crashDumpPath, "wb");
if(!is_resource($fp)){
throw new \RuntimeException("Unable to open new file to generate crashdump");
}
$writer = new CrashDumpRenderer($fp, $dump->getData());
$writer->renderHumanReadable();
$dump->encodeData($writer);
fclose($fp);
return $crashDumpPath;
}
public function crashDump() : void{
while(@ob_end_flush()){}
if(!$this->isRunning){
@ -1450,9 +1559,11 @@ class Server{
ini_set("memory_limit", '-1'); //Fix error dump not dumped on memory problems
try{
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_create()));
$dump = new CrashDump($this);
$dump = new CrashDump($this, $this->pluginManager ?? null);
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($dump->getPath())));
$crashDumpPath = $this->writeCrashDumpFile($dump);
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath)));
if($this->configGroup->getPropertyBool("auto-report.enabled", true)){
$report = true;
@ -1464,8 +1575,8 @@ class Server{
}
@touch($stamp); //update file timestamp
$plugin = $dump->getData()["plugin"];
if(is_string($plugin)){
$plugin = $dump->getData()->plugin;
if($plugin !== ""){
$p = $this->pluginManager->getPlugin($plugin);
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
@ -1473,7 +1584,7 @@ class Server{
}
}
if($dump->getData()["error"]["type"] === \ParseError::class){
if($dump->getData()->error["type"] === \ParseError::class){
$report = false;
}

View File

@ -25,14 +25,15 @@ namespace pocketmine;
use pocketmine\utils\Git;
use pocketmine\utils\VersionString;
use function is_array;
use function is_int;
use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.0.0-BETA5";
public const BASE_VERSION = "4.0.5";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_NUMBER = 0;
public const BUILD_CHANNEL = "beta";
public const BUILD_CHANNEL = "stable";
private function __construct(){
//NOOP
@ -61,12 +62,29 @@ final class VersionInfo{
return self::$gitHash;
}
private static ?int $buildNumber = null;
public static function BUILD_NUMBER() : int{
if(self::$buildNumber === null){
self::$buildNumber = 0;
if(\Phar::running(true) !== ""){
$phar = new \Phar(\Phar::running(false));
$meta = $phar->getMetadata();
if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){
self::$buildNumber = $meta["build"];
}
}
}
return self::$buildNumber;
}
/** @var VersionString|null */
private static $fullVersion = null;
public static function VERSION() : VersionString{
if(self::$fullVersion === null){
self::$fullVersion = new VersionString(self::BASE_VERSION, self::IS_DEVELOPMENT_BUILD, self::BUILD_NUMBER);
self::$fullVersion = new VersionString(self::BASE_VERSION, self::IS_DEVELOPMENT_BUILD, self::BUILD_NUMBER());
}
return self::$fullVersion;
}

View File

@ -37,8 +37,6 @@ use function assert;
use function strlen;
abstract class BaseSign extends Transparent{
//TODO: conditionally useless properties, find a way to fix
protected SignText $text;
protected ?int $editorEntityRuntimeId = null;

View File

@ -145,7 +145,7 @@ class Bed extends Transparent{
$isNight = ($time >= World::TIME_NIGHT and $time < World::TIME_SUNRISE);
if(!$isNight){
$player->sendMessage(KnownTranslationFactory::tile_bed_tooFar()->prefix(TextFormat::GRAY));
$player->sendMessage(KnownTranslationFactory::tile_bed_noSleep()->prefix(TextFormat::GRAY));
return true;
}

View File

@ -493,7 +493,7 @@ class Block{
return $this->position->getWorld()->getBlock($this->position->getSide($side, $step));
}
throw new \InvalidStateException("Block does not have a valid world");
throw new \LogicException("Block does not have a valid world");
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\Tile;
use pocketmine\utils\Utils;
class BlockIdentifier{
@ -40,6 +41,10 @@ class BlockIdentifier{
$this->blockId = $blockId;
$this->variant = $variant;
$this->itemId = $itemId;
if($tileClass !== null){
Utils::testValidInstance($tileClass, Tile::class);
}
$this->tileClass = $tileClass;
}

View File

@ -94,8 +94,7 @@ class Cake extends Transparent implements FoodSource{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){
$player->consumeObject($this);
return true;
return $player->consumeObject($this);
}
return false;

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\TreeType;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
@ -94,10 +95,7 @@ class CocoaBlock extends Transparent{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->age < 2 and $item instanceof Fertilizer){
$this->age++;
$this->position->getWorld()->setBlock($this->position, $this);
if($item instanceof Fertilizer && $this->grow()){
$item->pop();
return true;
@ -117,12 +115,25 @@ class CocoaBlock extends Transparent{
}
public function onRandomTick() : void{
if($this->age < 2 and mt_rand(1, 5) === 1){
$this->age++;
$this->position->getWorld()->setBlock($this->position, $this);
if(mt_rand(1, 5) === 1){
$this->grow();
}
}
private function grow() : bool{
if($this->age < 2){
$block = clone $this;
$block->age++;
$ev = new BlockGrowEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
return true;
}
}
return false;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
VanillaItems::COCOA_BEANS()->setCount($this->age === 2 ? mt_rand(2, 3) : 1)

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\crafting\CraftingGrid;
use pocketmine\block\inventory\CraftingTableInventory;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
@ -32,7 +32,7 @@ class CraftingTable extends Opaque{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player instanceof Player){
$player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG));
$player->setCurrentWindow(new CraftingTableInventory($this->position));
}
return true;

View File

@ -80,10 +80,9 @@ abstract class Crops extends Flowable{
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
$item->pop();
}
$item->pop();
return true;
}

View File

@ -23,6 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
class Podzol extends Opaque{
public function getDropsForCompatibleTool(Item $item) : array{
return [
VanillaBlocks::DIRT()->asItem()
];
}
}

View File

@ -76,9 +76,7 @@ class Sapling extends Flowable{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($item instanceof Fertilizer){
$this->grow($player);
if($item instanceof Fertilizer && $this->grow($player)){
$item->pop();
return true;
@ -108,21 +106,20 @@ class Sapling extends Flowable{
}
}
private function grow(?Player $player) : void{
private function grow(?Player $player) : bool{
$random = new Random(mt_rand());
$tree = TreeFactory::get($random, $this->treeType);
$transaction = $tree?->getBlockTransaction($this->position->getWorld(), $this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), $random);
if($transaction === null){
return;
return false;
}
$ev = new StructureGrowEvent($this, $transaction, $player);
$ev->call();
if($ev->isCancelled()){
return;
if(!$ev->isCancelled()){
return $transaction->apply();
}
$transaction->apply();
return false;
}
public function getFuelTime() : int{

View File

@ -124,8 +124,14 @@ class Skull extends Flowable{
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
//TODO: different bounds depending on attached face
return [AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5)];
$collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5);
return match($this->facing){
Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)],
Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)],
Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)],
Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)],
default => [$collisionBox]
};
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{

View File

@ -48,7 +48,8 @@ class Sugarcane extends Flowable{
return 0b1111;
}
private function grow() : void{
private function grow() : bool{
$grew = false;
for($y = 1; $y < 3; ++$y){
if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){
break;
@ -61,12 +62,14 @@ class Sugarcane extends Flowable{
break;
}
$this->position->getWorld()->setBlock($b->position, $ev->getNewState());
$grew = true;
}else{
break;
}
}
$this->age = 0;
$this->position->getWorld()->setBlock($this->position, $this);
return $grew;
}
public function getAge() : int{ return $this->age; }
@ -82,12 +85,10 @@ class Sugarcane extends Flowable{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($item instanceof Fertilizer){
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
$this->grow();
if(!$this->getSide(Facing::DOWN)->isSameType($this) && $this->grow()){
$item->pop();
}
$item->pop();
return true;
}

View File

@ -99,9 +99,9 @@ class SweetBerryBush extends Flowable{
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
$item->pop();
}
$item->pop();
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
$this->position->getWorld()->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
$this->position->getWorld()->dropItem($this->position, $this->asItem()->setCount($dropAmount));

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\utils\CloningRegistryTrait;
use function assert;
/**
* This doc-block is generated automatically, do not modify it manually.
@ -581,12 +580,6 @@ final class VanillaBlocks{
self::_registryRegister($name, $block);
}
public static function fromString(string $name) : Block{
$result = self::_registryFromString($name);
assert($result instanceof Block);
return $result;
}
/**
* @return Block[]
*/

View File

@ -24,10 +24,10 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\player\Player;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
class AnvilInventory extends SimpleInventory implements BlockInventory{
class AnvilInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public const SLOT_INPUT = 0;
@ -37,13 +37,4 @@ class AnvilInventory extends SimpleInventory implements BlockInventory{
$this->holder = $holder;
parent::__construct(2);
}
public function onClose(Player $who) : void{
parent::onClose($who);
foreach($this->getContents() as $item){
$who->dropItem($item);
}
$this->clearAll();
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\world\Position;
use pocketmine\world\sound\ChestCloseSound;
use pocketmine\world\sound\ChestOpenSound;
@ -50,6 +51,6 @@ class ChestInventory extends SimpleInventory implements BlockInventory{
$holder = $this->getHolder();
//event ID is always 1 for a chest
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
}
}

View File

@ -0,0 +1,37 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\crafting\CraftingGrid;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
final class CraftingTableInventory extends CraftingGrid implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(CraftingGrid::SIZE_BIG);
}
}

View File

@ -24,10 +24,10 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\player\Player;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
class EnchantInventory extends SimpleInventory implements BlockInventory{
class EnchantInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public const SLOT_INPUT = 0;
@ -37,13 +37,4 @@ class EnchantInventory extends SimpleInventory implements BlockInventory{
$this->holder = $holder;
parent::__construct(2);
}
public function onClose(Player $who) : void{
parent::onClose($who);
foreach($this->getContents() as $item){
$who->dropItem($item);
}
$this->clearAll();
}
}

View File

@ -28,6 +28,7 @@ use pocketmine\inventory\DelegateInventory;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\PlayerEnderInventory;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\player\Player;
use pocketmine\world\Position;
use pocketmine\world\sound\EnderChestCloseSound;
@ -74,7 +75,7 @@ class EnderChestInventory extends DelegateInventory implements BlockInventory{
$holder = $this->getHolder();
//event ID is always 1 for a chest
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
}
public function onClose(Player $who) : void{

View File

@ -24,10 +24,10 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\player\Player;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
final class LoomInventory extends SimpleInventory implements BlockInventory{
final class LoomInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public const SLOT_BANNER = 0;
@ -38,13 +38,4 @@ final class LoomInventory extends SimpleInventory implements BlockInventory{
$this->holder = $holder;
parent::__construct($size);
}
public function onClose(Player $who) : void{
parent::onClose($who);
foreach($this->getContents() as $item){
$who->dropItem($item);
}
$this->clearAll();
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\BlockLegacyIds;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\world\Position;
use pocketmine\world\sound\ShulkerBoxCloseSound;
use pocketmine\world\sound\ShulkerBoxOpenSound;
@ -59,6 +60,6 @@ class ShulkerBoxInventory extends SimpleInventory implements BlockInventory{
$holder = $this->getHolder();
//event ID is always 1 for a chest
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\math\Facing;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
final class Bell extends Spawnable{
@ -81,6 +82,6 @@ final class Bell extends Spawnable{
$nbt->setByte(self::TAG_RINGING, 1);
$nbt->setInt(self::TAG_DIRECTION, BlockDataSerializer::writeLegacyHorizontalFacing($bellHitFace));
$nbt->setInt(self::TAG_TICKS, 0);
return BlockActorDataPacket::create($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), new CacheableNbt($nbt));
return BlockActorDataPacket::create(BlockPosition::fromVector3($this->position), new CacheableNbt($nbt));
}
}

View File

@ -48,11 +48,14 @@ trait ContainerTrait{
$inventory = $this->getRealInventory();
$listeners = $inventory->getListeners()->toArray();
$inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
$inventory->clearAll();
$newContents = [];
/** @var CompoundTag $itemNBT */
foreach($inventoryTag as $itemNBT){
$inventory->setItem($itemNBT->getByte("Slot"), Item::nbtDeserialize($itemNBT));
$newContents[$itemNBT->getByte("Slot")] = Item::nbtDeserialize($itemNBT);
}
$inventory->setContents($newContents);
$inventory->getListeners()->add(...$listeners);
}

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\math\Vector3;
use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
@ -112,21 +113,25 @@ final class TileFactory{
/**
* @internal
* @throws NbtDataException
* @throws SavedDataLoadingException
*/
public function createFromData(World $world, CompoundTag $nbt) : ?Tile{
$type = $nbt->getString(Tile::TAG_ID, "");
if(!isset($this->knownTiles[$type])){
return null;
try{
$type = $nbt->getString(Tile::TAG_ID, "");
if(!isset($this->knownTiles[$type])){
return null;
}
$class = $this->knownTiles[$type];
assert(is_a($class, Tile::class, true));
/**
* @var Tile $tile
* @see Tile::__construct()
*/
$tile = new $class($world, new Vector3($nbt->getInt(Tile::TAG_X), $nbt->getInt(Tile::TAG_Y), $nbt->getInt(Tile::TAG_Z)));
$tile->readSaveData($nbt);
}catch(NbtException $e){
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
}
$class = $this->knownTiles[$type];
assert(is_a($class, Tile::class, true));
/**
* @var Tile $tile
* @see Tile::__construct()
*/
$tile = new $class($world, new Vector3($nbt->getInt(Tile::TAG_X), $nbt->getInt(Tile::TAG_Y), $nbt->getInt(Tile::TAG_Z)));
$tile->readSaveData($nbt);
return $tile;
}

View File

@ -25,7 +25,7 @@ namespace pocketmine\block\utils;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Translatable;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
use pocketmine\utils\EnumTrait;
/**
@ -54,18 +54,18 @@ final class RecordType{
protected static function setup() : void{
self::registerAll(
new RecordType("disk_13", "C418 - 13", LevelSoundEventPacket::SOUND_RECORD_13, KnownTranslationFactory::item_record_13_desc()),
new RecordType("disk_cat", "C418 - cat", LevelSoundEventPacket::SOUND_RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()),
new RecordType("disk_blocks", "C418 - blocks", LevelSoundEventPacket::SOUND_RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()),
new RecordType("disk_chirp", "C418 - chirp", LevelSoundEventPacket::SOUND_RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()),
new RecordType("disk_far", "C418 - far", LevelSoundEventPacket::SOUND_RECORD_FAR, KnownTranslationFactory::item_record_far_desc()),
new RecordType("disk_mall", "C418 - mall", LevelSoundEventPacket::SOUND_RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()),
new RecordType("disk_mellohi", "C418 - mellohi", LevelSoundEventPacket::SOUND_RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()),
new RecordType("disk_stal", "C418 - stal", LevelSoundEventPacket::SOUND_RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()),
new RecordType("disk_strad", "C418 - strad", LevelSoundEventPacket::SOUND_RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()),
new RecordType("disk_ward", "C418 - ward", LevelSoundEventPacket::SOUND_RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()),
new RecordType("disk_11", "C418 - 11", LevelSoundEventPacket::SOUND_RECORD_11, KnownTranslationFactory::item_record_11_desc()),
new RecordType("disk_wait", "C418 - wait", LevelSoundEventPacket::SOUND_RECORD_WAIT, KnownTranslationFactory::item_record_wait_desc())
new RecordType("disk_13", "C418 - 13", LevelSoundEvent::RECORD_13, KnownTranslationFactory::item_record_13_desc()),
new RecordType("disk_cat", "C418 - cat", LevelSoundEvent::RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()),
new RecordType("disk_blocks", "C418 - blocks", LevelSoundEvent::RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()),
new RecordType("disk_chirp", "C418 - chirp", LevelSoundEvent::RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()),
new RecordType("disk_far", "C418 - far", LevelSoundEvent::RECORD_FAR, KnownTranslationFactory::item_record_far_desc()),
new RecordType("disk_mall", "C418 - mall", LevelSoundEvent::RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()),
new RecordType("disk_mellohi", "C418 - mellohi", LevelSoundEvent::RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()),
new RecordType("disk_stal", "C418 - stal", LevelSoundEvent::RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()),
new RecordType("disk_strad", "C418 - strad", LevelSoundEvent::RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()),
new RecordType("disk_ward", "C418 - ward", LevelSoundEvent::RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()),
new RecordType("disk_11", "C418 - 11", LevelSoundEvent::RECORD_11, KnownTranslationFactory::item_record_11_desc()),
new RecordType("disk_wait", "C418 - wait", LevelSoundEvent::RECORD_WAIT, KnownTranslationFactory::item_record_wait_desc())
//TODO: Lena Raine - Pigstep
);
}

View File

@ -206,7 +206,7 @@ class SimpleCommandMap implements CommandMap{
foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){
$args[$k] = stripslashes($matches[$i][$k]);
$args[$k] = $i === 1 ? stripslashes($matches[$i][$k]) : $matches[$i][$k];
break;
}
}

View File

@ -32,7 +32,7 @@ use pocketmine\player\Player;
use function array_shift;
use function count;
use function implode;
use function preg_match;
use function inet_pton;
class BanIpCommand extends VanillaCommand{
@ -57,7 +57,7 @@ class BanIpCommand extends VanillaCommand{
$value = array_shift($args);
$reason = implode(" ", $args);
if(preg_match("/^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$/", $value)){
if(inet_pton($value) !== false){
$this->processIPBan($value, $sender, $reason);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_banip_success($value));

View File

@ -26,7 +26,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;
use pocketmine\entity\effect\StringToEffectParser;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\Limits;
@ -69,9 +69,8 @@ class EffectCommand extends VanillaCommand{
return true;
}
try{
$effect = VanillaEffects::fromString($args[1]);
}catch(\InvalidArgumentException $e){
$effect = StringToEffectParser::getInstance()->parse($args[1]);
if($effect === null){
$sender->sendMessage(KnownTranslationFactory::commands_effect_notFound($args[1])->prefix(TextFormat::RED));
return true;
}

View File

@ -26,7 +26,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\enchantment\StringToEnchantmentParser;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\TextFormat;
@ -66,9 +66,8 @@ class EnchantCommand extends VanillaCommand{
return true;
}
try{
$enchantment = VanillaEnchantments::fromString($args[1]);
}catch(\InvalidArgumentException $e){
$enchantment = StringToEnchantmentParser::getInstance()->parse($args[1]);
if($enchantment === null){
$sender->sendMessage(KnownTranslationFactory::commands_enchant_notFound($args[1]));
return true;
}

View File

@ -59,7 +59,7 @@ class KickCommand extends VanillaCommand{
$reason = trim(implode(" ", $args));
if(($player = $sender->getServer()->getPlayerByPrefix($name)) instanceof Player){
$player->kick("Kicked by admin." . ($reason !== "" ? "Reason: " . $reason : ""));
$player->kick("Kicked by admin." . ($reason !== "" ? " Reason: " . $reason : ""));
if($reason !== ""){
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kick_success_reason($player->getName(), $reason));
}else{

View File

@ -29,7 +29,7 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use function count;
use function preg_match;
use function inet_pton;
class PardonIpCommand extends VanillaCommand{
@ -52,7 +52,7 @@ class PardonIpCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
if(preg_match("/^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$/", $args[0])){
if(inet_pton($args[0]) !== false){
$sender->getServer()->getIPBans()->remove($args[0]);
$sender->getServer()->getNetwork()->unblockAddress($args[0]);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_unbanip_success($args[0]));

View File

@ -31,8 +31,8 @@ use pocketmine\math\Vector3;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use pocketmine\world\World;
use function count;
use function round;
class SetWorldSpawnCommand extends VanillaCommand{
@ -54,22 +54,32 @@ class SetWorldSpawnCommand extends VanillaCommand{
if($sender instanceof Player){
$location = $sender->getPosition();
$world = $location->getWorld();
$pos = $location->asVector3()->round();
$pos = $location->asVector3()->floor();
}else{
$sender->sendMessage(TextFormat::RED . "You can only perform this command as a player");
return true;
}
}elseif(count($args) === 3){
$world = $sender->getServer()->getWorldManager()->getDefaultWorld();
$pos = new Vector3($this->getInteger($sender, $args[0]), $this->getInteger($sender, $args[1]), $this->getInteger($sender, $args[2]));
if($sender instanceof Player){
$base = $sender->getPosition();
$world = $base->getWorld();
}else{
$base = new Vector3(0.0, 0.0, 0.0);
$world = $sender->getServer()->getWorldManager()->getDefaultWorld();
}
$pos = (new Vector3(
$this->getRelativeDouble($base->x, $sender, $args[0]),
$this->getRelativeDouble($base->y, $sender, $args[1], World::Y_MIN, World::Y_MAX),
$this->getRelativeDouble($base->z, $sender, $args[2]),
))->floor();
}else{
throw new InvalidCommandSyntaxException();
}
$world->setSpawnLocation($pos);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_setworldspawn_success((string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_setworldspawn_success((string) $pos->x, (string) $pos->y, (string) $pos->z));
return true;
}

View File

@ -32,6 +32,7 @@ use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use pocketmine\world\World;
use function array_shift;
use function count;
use function round;
@ -111,9 +112,9 @@ class TeleportCommand extends VanillaCommand{
}
$x = $this->getRelativeDouble($base->x, $sender, $targetArgs[0]);
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], 0, 256);
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], World::Y_MIN, World::Y_MAX);
$z = $this->getRelativeDouble($base->z, $sender, $targetArgs[2]);
$targetLocation = new Location($x, $y, $z, $yaw, $pitch, $base->getWorld());
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
$subject->teleport($targetLocation);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_tp_success_coordinates(

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\console;
use pocketmine\utils\AssumptionFailedError;
use function fclose;
use function fgets;
use function fopen;
@ -44,7 +45,9 @@ final class ConsoleReader{
fclose($this->stdin);
}
$this->stdin = fopen("php://stdin", "r");
$stdin = fopen("php://stdin", "r");
if($stdin === false) throw new AssumptionFailedError("Opening stdin should never fail");
$this->stdin = $stdin;
}
/**
@ -60,11 +63,10 @@ final class ConsoleReader{
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
return null;
}elseif($count === false){ //stream error
$this->initStdin();
return null;
}
if(($raw = fgets($this->stdin)) === false){ //broken pipe or EOF
$this->initStdin();
usleep(200000); //prevent CPU waste if it's end of pipe
return null; //loop back round
}

View File

@ -46,7 +46,10 @@ if($socket === false){
$consoleReader = new ConsoleReader();
while(!feof($socket)){
$line = $consoleReader->readLine();
if($line !== null){
fwrite($socket, $line . "\n");
if(@fwrite($socket, ($line ?? "") . "\n") === false){
//Always send even if there's no line, to check if the parent is alive
//If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return
//false even though the connection is actually broken. However, fwrite() will fail.
break;
}
}

View File

@ -31,6 +31,7 @@ use function base64_encode;
use function fgets;
use function fopen;
use function preg_replace;
use function proc_close;
use function proc_open;
use function proc_terminate;
use function sprintf;
@ -39,6 +40,7 @@ use function stream_socket_accept;
use function stream_socket_get_name;
use function stream_socket_server;
use function stream_socket_shutdown;
use function trim;
use const PHP_BINARY;
use const STREAM_SHUT_RDWR;
@ -85,7 +87,7 @@ final class ConsoleReaderThread extends Thread{
//Windows sucks, and likes to corrupt UTF-8 file paths when they travel to the subprocess, so we base64 encode
//the path to avoid the problem. This is an abysmally shitty hack, but here we are :(
$sub = proc_open(
[PHP_BINARY, '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address],
[PHP_BINARY, '-dopcache.enable_cli=0', '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address],
[
2 => fopen("php://stderr", "w"),
],
@ -113,7 +115,12 @@ final class ConsoleReaderThread extends Thread{
break;
}
$buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $command);
$command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
$command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
if($command === ""){
continue;
}
$buffer[] = $command;
if($notifier !== null){
$notifier->wakeupSleeper();
}
@ -124,6 +131,7 @@ final class ConsoleReaderThread extends Thread{
//gets stuck in a blocking fgets() read because stream_select() is a hunk of junk (hence the separate process in
//the first place).
proc_terminate($sub);
proc_close($sub);
stream_socket_shutdown($client, STREAM_SHUT_RDWR);
}

View File

@ -25,17 +25,14 @@ namespace pocketmine\crafting;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\player\Player;
use function max;
use function min;
use const PHP_INT_MAX;
class CraftingGrid extends SimpleInventory{
abstract class CraftingGrid extends SimpleInventory{
public const SIZE_SMALL = 2;
public const SIZE_BIG = 3;
/** @var Player */
protected $holder;
/** @var int */
private $gridWidth;
@ -48,8 +45,7 @@ class CraftingGrid extends SimpleInventory{
/** @var int|null */
private $yLen;
public function __construct(Player $holder, int $gridWidth){
$this->holder = $holder;
public function __construct(int $gridWidth){
$this->gridWidth = $gridWidth;
parent::__construct($this->getGridWidth() ** 2);
}
@ -63,13 +59,6 @@ class CraftingGrid extends SimpleInventory{
$this->seekRecipeBounds();
}
/**
* @return Player
*/
public function getHolder(){
return $this->holder;
}
private function seekRecipeBounds() : void{
$minX = PHP_INT_MAX;
$maxX = 0;
@ -111,7 +100,7 @@ class CraftingGrid extends SimpleInventory{
return $this->getItem(($y + $this->startY) * $this->gridWidth + ($x + $this->startX));
}
throw new \InvalidStateException("No ingredients found in grid");
throw new \LogicException("No ingredients found in grid");
}
/**

View File

@ -21,37 +21,31 @@
declare(strict_types=1);
namespace pocketmine;
namespace pocketmine\crash;
use Composer\InstalledVersions;
use pocketmine\errorhandler\ErrorTypeToStringMap;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\plugin\PluginBase;
use pocketmine\plugin\PluginManager;
use pocketmine\Server;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use pocketmine\VersionInfo;
use Webmozart\PathUtil\Path;
use function base64_encode;
use function date;
use function error_get_last;
use function fclose;
use function file;
use function file_exists;
use function file_get_contents;
use function fopen;
use function fwrite;
use function get_loaded_extensions;
use function implode;
use function is_dir;
use function is_resource;
use function json_encode;
use function json_last_error_msg;
use function ksort;
use function max;
use function mb_strtoupper;
use function microtime;
use function mkdir;
use function ob_end_clean;
use function ob_get_contents;
use function ob_start;
@ -67,9 +61,10 @@ use function zend_version;
use function zlib_encode;
use const FILE_IGNORE_NEW_LINES;
use const JSON_UNESCAPED_SLASHES;
use const PHP_EOL;
use const PHP_OS;
use const PHP_VERSION;
use const SORT_STRING;
use const ZLIB_ENCODING_DEFLATE;
class CrashDump{
@ -81,77 +76,34 @@ class CrashDump{
*/
private const FORMAT_VERSION = 4;
private const PLUGIN_INVOLVEMENT_NONE = "none";
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
private const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
public const PLUGIN_INVOLVEMENT_NONE = "none";
public const PLUGIN_INVOLVEMENT_DIRECT = "direct";
public const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
/** @var Server */
private $server;
/** @var resource */
private $fp;
/** @var float */
private $time;
/**
* @var mixed[]
* @phpstan-var array<string, mixed>
*/
private $data = [];
private CrashDumpData $data;
/** @var string */
private $encodedData = "";
/** @var string */
private $path;
private $encodedData;
public function __construct(Server $server){
$this->time = microtime(true);
private ?PluginManager $pluginManager;
public function __construct(Server $server, ?PluginManager $pluginManager){
$now = microtime(true);
$this->server = $server;
$this->pluginManager = $pluginManager;
$this->data = new CrashDumpData();
$this->data->format_version = self::FORMAT_VERSION;
$this->data->time = $now;
$this->data->uptime = $now - $this->server->getStartTime();
$crashPath = Path::join($this->server->getDataPath(), "crashdumps");
if(!is_dir($crashPath)){
mkdir($crashPath);
}
$this->path = Path::join($crashPath, date("D_M_j-H.i.s-T_Y", (int) $this->time) . ".log");
$fp = @fopen($this->path, "wb");
if(!is_resource($fp)){
throw new \RuntimeException("Could not create Crash Dump");
}
$this->fp = $fp;
$this->data["format_version"] = self::FORMAT_VERSION;
$this->data["time"] = $this->time;
$this->data["uptime"] = $this->time - $this->server->getStartTime();
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->time));
$this->addLine();
$this->baseCrash();
$this->generalData();
$this->pluginsData();
$this->extraData();
$this->encodeData();
fclose($this->fp);
}
public function getPath() : string{
return $this->path;
}
public function getEncodedData() : string{
return $this->encodedData;
}
/**
* @return mixed[]
* @phpstan-return array<string, mixed>
*/
public function getData() : array{
return $this->data;
}
private function encodeData() : void{
$this->addLine();
$this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
$this->addLine();
$this->addLine("===BEGIN CRASH DUMP===");
$json = json_encode($this->data, JSON_UNESCAPED_SLASHES);
if($json === false){
throw new \RuntimeException("Failed to encode crashdump JSON: " . json_last_error_msg());
@ -159,34 +111,45 @@ class CrashDump{
$zlibEncoded = zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9);
if($zlibEncoded === false) throw new AssumptionFailedError("ZLIB compression failed");
$this->encodedData = $zlibEncoded;
}
public function getEncodedData() : string{
return $this->encodedData;
}
public function getData() : CrashDumpData{
return $this->data;
}
public function encodeData(CrashDumpRenderer $renderer) : void{
$renderer->addLine();
$renderer->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
$renderer->addLine();
$renderer->addLine("===BEGIN CRASH DUMP===");
foreach(str_split(base64_encode($this->encodedData), 76) as $line){
$this->addLine($line);
$renderer->addLine($line);
}
$this->addLine("===END CRASH DUMP===");
$renderer->addLine("===END CRASH DUMP===");
}
private function pluginsData() : void{
if($this->server->getPluginManager() instanceof PluginManager){
$this->addLine();
$this->addLine("Loaded plugins:");
$this->data["plugins"] = [];
$plugins = $this->server->getPluginManager()->getPlugins();
if($this->pluginManager !== null){
$plugins = $this->pluginManager->getPlugins();
ksort($plugins, SORT_STRING);
foreach($plugins as $p){
$d = $p->getDescription();
$this->data["plugins"][$d->getName()] = [
"name" => $d->getName(),
"version" => $d->getVersion(),
"authors" => $d->getAuthors(),
"api" => $d->getCompatibleApis(),
"enabled" => $p->isEnabled(),
"depends" => $d->getDepend(),
"softDepends" => $d->getSoftDepend(),
"main" => $d->getMain(),
"load" => mb_strtoupper($d->getOrder()->name()),
"website" => $d->getWebsite()
];
$this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis()));
$this->data->plugins[$d->getName()] = new CrashDumpDataPluginEntry(
name: $d->getName(),
version: $d->getVersion(),
authors: $d->getAuthors(),
api: $d->getCompatibleApis(),
enabled: $p->isEnabled(),
depends: $d->getDepend(),
softDepends: $d->getSoftDepend(),
main: $d->getMain(),
load: mb_strtoupper($d->getOrder()->name()),
website: $d->getWebsite()
);
}
}
}
@ -195,32 +158,26 @@ class CrashDump{
global $argv;
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){
$this->data["parameters"] = (array) $argv;
$this->data->parameters = (array) $argv;
if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties);
}else{
$this->data["server.properties"] = $serverDotProperties;
$this->data->serverDotProperties = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties) ?? throw new AssumptionFailedError("Pattern is valid");
}
if(($pocketmineDotYml = @file_get_contents(Path::join($this->server->getDataPath(), "pocketmine.yml"))) !== false){
$this->data["pocketmine.yml"] = $pocketmineDotYml;
}else{
$this->data["pocketmine.yml"] = "";
$this->data->pocketmineDotYml = $pocketmineDotYml;
}
}else{
$this->data["pocketmine.yml"] = "";
$this->data["server.properties"] = "";
$this->data["parameters"] = [];
}
$extensions = [];
foreach(get_loaded_extensions() as $ext){
$extensions[$ext] = phpversion($ext);
$version = phpversion($ext);
if($version === false) throw new AssumptionFailedError();
$extensions[$ext] = $version;
}
$this->data["extensions"] = $extensions;
$this->data->extensions = $extensions;
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){
ob_start();
phpinfo();
$this->data["phpinfo"] = ob_get_contents();
$this->data->phpinfo = ob_get_contents(); // @phpstan-ignore-line
ob_end_clean();
}
}
@ -252,18 +209,14 @@ class CrashDump{
if(isset($lastError["trace"])){
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
}
$this->data["lastError"] = $lastError;
$this->data->lastError = $lastError;
}
$this->data["error"] = $error;
unset($this->data["error"]["fullFile"]);
unset($this->data["error"]["trace"]);
$this->addLine("Error: " . $error["message"]);
$this->addLine("File: " . $error["file"]);
$this->addLine("Line: " . $error["line"]);
$this->addLine("Type: " . $error["type"]);
$this->data->error = $error;
unset($this->data->error["fullFile"]);
unset($this->data->error["trace"]);
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_NONE;
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
foreach($error["trace"] as $frame){
if(!isset($frame["file"])){
@ -275,38 +228,25 @@ class CrashDump{
}
}
$this->addLine();
$this->addLine("Code:");
$this->data["code"] = [];
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) and file_exists($error["fullFile"])){
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
if($file !== false){
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
$this->data["code"][$l + 1] = $file[$l];
$this->data->code[$l + 1] = $file[$l];
}
}
}
$this->addLine();
$this->addLine("Backtrace:");
foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){
$this->addLine($line);
}
$this->addLine();
$this->data->trace = Utils::printableTrace($error["trace"]);
}
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
$frameCleanPath = Filesystem::cleanPath($filePath);
if(strpos($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX) !== 0){
$this->addLine();
if($crashFrame){
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT;
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
}else{
$this->addLine("A PLUGIN WAS INVOLVED IN THIS CRASH");
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_INDIRECT;
}
if(file_exists($filePath)){
@ -316,8 +256,7 @@ class CrashDump{
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
$filePath = Filesystem::cleanPath($file->getValue($plugin));
if(strpos($frameCleanPath, $filePath) === 0){
$this->data["plugin"] = $plugin->getName();
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
$this->data->plugin = $plugin->getName();
break;
}
}
@ -328,7 +267,6 @@ class CrashDump{
}
private function generalData() : void{
$version = VersionInfo::VERSION();
$composerLibraries = [];
foreach(InstalledVersions::getInstalledPackages() as $package){
$composerLibraries[$package] = sprintf(
@ -338,42 +276,19 @@ class CrashDump{
);
}
$this->data["general"] = [];
$this->data["general"]["name"] = $this->server->getName();
$this->data["general"]["base_version"] = VersionInfo::BASE_VERSION;
$this->data["general"]["build"] = VersionInfo::BUILD_NUMBER;
$this->data["general"]["is_dev"] = VersionInfo::IS_DEVELOPMENT_BUILD;
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
$this->data["general"]["git"] = VersionInfo::GIT_HASH();
$this->data["general"]["uname"] = php_uname("a");
$this->data["general"]["php"] = phpversion();
$this->data["general"]["zend"] = zend_version();
$this->data["general"]["php_os"] = PHP_OS;
$this->data["general"]["os"] = Utils::getOS();
$this->data["general"]["composer_libraries"] = $composerLibraries;
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
$this->addLine("Git commit: " . VersionInfo::GIT_HASH());
$this->addLine("uname -a: " . php_uname("a"));
$this->addLine("PHP Version: " . phpversion());
$this->addLine("Zend version: " . zend_version());
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
$this->addLine("Composer libraries: ");
foreach($composerLibraries as $library => $libraryVersion){
$this->addLine("- $library $libraryVersion");
}
}
/**
* @param string $line
*/
public function addLine($line = "") : void{
fwrite($this->fp, $line . PHP_EOL);
}
/**
* @param string $str
*/
public function add($str) : void{
fwrite($this->fp, $str);
$this->data->general = new CrashDumpDataGeneral(
name: $this->server->getName(),
base_version: VersionInfo::BASE_VERSION,
build: VersionInfo::BUILD_NUMBER(),
is_dev: VersionInfo::IS_DEVELOPMENT_BUILD,
protocol: ProtocolInfo::CURRENT_PROTOCOL,
git: VersionInfo::GIT_HASH(),
uname: php_uname("a"),
php: PHP_VERSION,
zend: zend_version(),
php_os: PHP_OS,
os: Utils::getOS(),
composer_libraries: $composerLibraries,
);
}
}

View File

@ -0,0 +1,84 @@
<?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\crash;
final class CrashDumpData implements \JsonSerializable{
public int $format_version;
public float $time;
public float $uptime;
/** @var mixed[] */
public array $lastError = [];
/** @var mixed[] */
public array $error;
public string $plugin_involvement;
public string $plugin = "";
/** @var string[] */
public array $code = [];
/** @var string[] */
public array $trace;
/**
* @var CrashDumpDataPluginEntry[]
* @phpstan-var array<string, CrashDumpDataPluginEntry>
*/
public array $plugins = [];
/** @var string[] */
public array $parameters = [];
public string $serverDotProperties = "";
public string $pocketmineDotYml = "";
/**
* @var string[]
* @phpstan-var array<string, string>
*/
public array $extensions = [];
public string $phpinfo = "";
public CrashDumpDataGeneral $general;
/**
* @return mixed[]
*/
public function jsonSerialize() : array{
$result = (array) $this;
unset($result["serverDotProperties"]);
unset($result["pocketmineDotYml"]);
$result["pocketmine.yml"] = $this->pocketmineDotYml;
$result["server.properties"] = $this->serverDotProperties;
return $result;
}
}

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\crash;
final class CrashDumpDataGeneral{
/**
* @param string[] $composer_libraries
* @phpstan-param array<string, string> $composer_libraries
*/
public function __construct(
public string $name,
public string $base_version,
public int $build,
public bool $is_dev,
public int $protocol,
public string $git,
public string $uname,
public string $php,
public string $zend,
public string $php_os,
public string $os,
public array $composer_libraries,
){}
}

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\crash;
final class CrashDumpDataPluginEntry{
/**
* @param string[] $authors
* @param string[] $api
* @param string[] $depends
* @param string[] $softDepends
*/
public function __construct(
public string $name,
public string $version,
public array $authors,
public array $api,
public bool $enabled,
public array $depends,
public array $softDepends,
public string $main,
public string $load,
public string $website,
){}
}

View File

@ -0,0 +1,103 @@
<?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\crash;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
use function count;
use function date;
use function fwrite;
use function implode;
use const PHP_EOL;
final class CrashDumpRenderer{
/**
* @param resource $fp
*/
public function __construct(private $fp, private CrashDumpData $data){
}
public function renderHumanReadable() : void{
$this->addLine($this->data->general->name . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->data->time));
$this->addLine();
$this->addLine("Error: " . $this->data->error["message"]);
$this->addLine("File: " . $this->data->error["file"]);
$this->addLine("Line: " . $this->data->error["line"]);
$this->addLine("Type: " . $this->data->error["type"]);
if($this->data->plugin_involvement !== CrashDump::PLUGIN_INVOLVEMENT_NONE){
$this->addLine();
$this->addLine(match($this->data->plugin_involvement){
CrashDump::PLUGIN_INVOLVEMENT_DIRECT => "THIS CRASH WAS CAUSED BY A PLUGIN",
CrashDump::PLUGIN_INVOLVEMENT_INDIRECT => "A PLUGIN WAS INVOLVED IN THIS CRASH",
default => "Unknown plugin involvement!"
});
}
if($this->data->plugin !== ""){
$this->addLine("BAD PLUGIN: " . $this->data->plugin);
}
$this->addLine();
$this->addLine("Code:");
foreach($this->data->code as $lineNumber => $line){
$this->addLine("[$lineNumber] $line");
}
$this->addLine();
$this->addLine("Backtrace:");
foreach($this->data->trace as $line){
$this->addLine($line);
}
$this->addLine();
$version = new VersionString($this->data->general->base_version, $this->data->general->is_dev, $this->data->general->build);
$this->addLine($this->data->general->name . " version: " . $version->getFullVersion(true) . " [Protocol " . $this->data->general->protocol . "]");
$this->addLine("Git commit: " . $this->data->general->git);
$this->addLine("uname -a: " . $this->data->general->uname);
$this->addLine("PHP Version: " . $this->data->general->php);
$this->addLine("Zend version: " . $this->data->general->zend);
$this->addLine("OS: " . $this->data->general->php_os . ", " . $this->data->general->os);
$this->addLine("Composer libraries: ");
foreach(Utils::stringifyKeys($this->data->general->composer_libraries) as $library => $libraryVersion){
$this->addLine("- $library $libraryVersion");
}
if(count($this->data->plugins) > 0){
$this->addLine();
$this->addLine("Loaded plugins:");
foreach($this->data->plugins as $p){
$this->addLine($p->name . " " . $p->version . " by " . implode(", ", $p->authors) . " for API(s) " . implode(", ", $p->api));
}
}
}
public function addLine(string $line = "") : void{
fwrite($this->fp, $line . PHP_EOL);
}
}

View File

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

View File

@ -0,0 +1,35 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\data\bedrock;
use pocketmine\utils\SingletonTrait;
use Webmozart\PathUtil\Path;
final class LegacyBiomeIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'biome_id_map.json'));
}
}

View File

@ -30,6 +30,6 @@ final class LegacyBlockIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'block_id_map.json'));
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'block_id_map.json'));
}
}

View File

@ -30,6 +30,6 @@ final class LegacyEntityIdToStringIdMap extends LegacyToStringBidirectionalIdMap
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'entity_id_map.json'));
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'entity_id_map.json'));
}
}

View File

@ -30,6 +30,6 @@ final class LegacyItemIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
use SingletonTrait;
public function __construct(){
parent::__construct(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'item_id_map.json'));
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'item_id_map.json'));
}
}

View File

@ -635,7 +635,7 @@ abstract class Entity{
$this->checkBlockIntersections();
if($this->location->y <= -16 and $this->isAlive()){
if($this->location->y <= World::Y_MIN - 16 and $this->isAlive()){
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10);
$this->attack($ev);
$hasUpdate = true;
@ -832,10 +832,10 @@ abstract class Entity{
$diffZ = $z - $floorZ;
if($world->getBlockAt($floorX, $floorY, $floorZ)->isSolid()){
$westNonSolid = !$world->getBlockAt($floorX - 1, $floorY, $floorZ)->isSolid();
$eastNonSolid = !$world->getBlockAt($floorX + 1, $floorY, $floorZ)->isSolid();
$downNonSolid = !$world->getBlockAt($floorX, $floorY - 1, $floorZ)->isSolid();
$upNonSolid = !$world->getBlockAt($floorX, $floorY + 1, $floorZ)->isSolid();
$westNonSolid = !$world->getBlockAt($floorX - 1, $floorY, $floorZ)->isSolid();
$eastNonSolid = !$world->getBlockAt($floorX + 1, $floorY, $floorZ)->isSolid();
$downNonSolid = !$world->getBlockAt($floorX, $floorY - 1, $floorZ)->isSolid();
$upNonSolid = !$world->getBlockAt($floorX, $floorY + 1, $floorZ)->isSolid();
$northNonSolid = !$world->getBlockAt($floorX, $floorY, $floorZ - 1)->isSolid();
$southNonSolid = !$world->getBlockAt($floorX, $floorY, $floorZ + 1)->isSolid();
@ -980,7 +980,7 @@ abstract class Entity{
final public function scheduleUpdate() : void{
if($this->closed){
throw new \InvalidStateException("Cannot schedule update on garbage entity " . get_class($this));
throw new \LogicException("Cannot schedule update on garbage entity " . get_class($this));
}
$this->getWorld()->updateEntities[$this->id] = $this;
}
@ -1185,9 +1185,9 @@ abstract class Entity{
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
$this->boundingBox->minY - $this->ySize,
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2,
$this->location->world,
$this->location->yaw,
$this->location->pitch,
$this->location->world
$this->location->pitch
);
$this->getWorld()->onEntityMoved($this);
@ -1418,20 +1418,21 @@ abstract class Entity{
* Called by spawnTo() to send whatever packets needed to spawn the entity to the client.
*/
protected function sendSpawnPacket(Player $player) : void{
$pk = new AddActorPacket();
$pk->entityRuntimeId = $this->getId();
$pk->type = static::getNetworkTypeId();
$pk->position = $this->location->asVector3();
$pk->motion = $this->getMotion();
$pk->yaw = $this->location->yaw;
$pk->headYaw = $this->location->yaw; //TODO
$pk->pitch = $this->location->pitch;
$pk->attributes = array_map(function(Attribute $attr) : NetworkAttribute{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
}, $this->attributeMap->getAll());
$pk->metadata = $this->getAllNetworkData();
$player->getNetworkSession()->sendDataPacket($pk);
$player->getNetworkSession()->sendDataPacket(AddActorPacket::create(
$this->getId(), //TODO: actor unique ID
$this->getId(),
static::getNetworkTypeId(),
$this->location->asVector3(),
$this->getMotion(),
$this->location->pitch,
$this->location->yaw,
$this->location->yaw, //TODO: head yaw
array_map(function(Attribute $attr) : NetworkAttribute{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
}, $this->attributeMap->getAll()),
$this->getAllNetworkData(),
[] //TODO: entity links
));
}
public function spawnTo(Player $player) : void{
@ -1439,7 +1440,7 @@ abstract class Entity{
//TODO: this will cause some visible lag during chunk resends; if the player uses a spawn egg in a chunk, the
//created entity won't be visible until after the resend arrives. However, this is better than possibly crashing
//the player by sending them entities too early.
if(!isset($this->hasSpawned[$id]) and $player->hasReceivedChunk($this->location->getFloorX() >> Chunk::COORD_BIT_SIZE, $this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
if(!isset($this->hasSpawned[$id]) and $player->getWorld() === $this->getWorld() and $player->hasReceivedChunk($this->location->getFloorX() >> Chunk::COORD_BIT_SIZE, $this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
$this->hasSpawned[$id] = $player;
$this->sendSpawnPacket($player);
@ -1588,8 +1589,8 @@ abstract class Entity{
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
$properties->setByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG, $this->alwaysShowNameTag ? 1 : 0);
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->size->getHeight());
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->size->getWidth());
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->size->getHeight() / $this->scale);
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->size->getWidth() / $this->scale);
$properties->setFloat(EntityMetadataProperties::SCALE, $this->scale);
$properties->setLong(EntityMetadataProperties::LEAD_HOLDER_EID, -1);
$properties->setLong(EntityMetadataProperties::OWNER_EID, $this->ownerId ?? -1);
@ -1598,7 +1599,7 @@ abstract class Entity{
$properties->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag);
$properties->setByte(EntityMetadataProperties::COLOR, 0);
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true);
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, $this->gravityEnabled);
$properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);
$properties->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $this->nameTagVisible);
$properties->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, true);

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\entity;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\math\Vector3;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
@ -38,34 +39,40 @@ final class EntityDataHelper{
//NOOP
}
/**
* @throws SavedDataLoadingException
*/
public static function parseLocation(CompoundTag $nbt, World $world) : Location{
$pos = self::parseVec3($nbt, "Pos", false);
$yawPitch = $nbt->getTag("Rotation");
if(!($yawPitch instanceof ListTag) or $yawPitch->getTagType() !== NBT::TAG_Float){
throw new \UnexpectedValueException("'Rotation' should be a List<Float>");
throw new SavedDataLoadingException("'Rotation' should be a List<Float>");
}
/** @var FloatTag[] $values */
$values = $yawPitch->getValue();
if(count($values) !== 2){
throw new \UnexpectedValueException("Expected exactly 2 entries for 'Rotation'");
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
}
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
}
/**
* @throws SavedDataLoadingException
*/
public static function parseVec3(CompoundTag $nbt, string $tagName, bool $optional) : Vector3{
$pos = $nbt->getTag($tagName);
if($pos === null and $optional){
return new Vector3(0, 0, 0);
}
if(!($pos instanceof ListTag) or $pos->getTagType() !== NBT::TAG_Double){
throw new \UnexpectedValueException("'$tagName' should be a List<Double>");
if(!($pos instanceof ListTag) or ($pos->getTagType() !== NBT::TAG_Double && $pos->getTagType() !== NBT::TAG_Float)){
throw new SavedDataLoadingException("'$tagName' should be a List<Double> or List<Float>");
}
/** @var DoubleTag[] $values */
/** @var DoubleTag[]|FloatTag[] $values */
$values = $pos->getValue();
if(count($values) !== 3){
throw new \UnexpectedValueException("Expected exactly 3 entries in '$tagName' tag");
throw new SavedDataLoadingException("Expected exactly 3 entries in '$tagName' tag");
}
return new Vector3($values[0]->getValue(), $values[1]->getValue(), $values[2]->getValue());
}

View File

@ -30,6 +30,7 @@ use pocketmine\block\BlockFactory;
use pocketmine\data\bedrock\EntityLegacyIds;
use pocketmine\data\bedrock\PotionTypeIdMap;
use pocketmine\data\bedrock\PotionTypeIds;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\object\ExperienceOrb;
use pocketmine\entity\object\FallingBlock;
use pocketmine\entity\object\ItemEntity;
@ -45,7 +46,7 @@ use pocketmine\entity\projectile\SplashPotion;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
@ -113,12 +114,12 @@ final class EntityFactory{
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
$itemTag = $nbt->getCompoundTag("Item");
if($itemTag === null){
throw new \UnexpectedValueException("Expected \"Item\" NBT tag not found");
throw new SavedDataLoadingException("Expected \"Item\" NBT tag not found");
}
$item = Item::nbtDeserialize($itemTag);
if($item->isNull()){
throw new \UnexpectedValueException("Item is invalid");
throw new SavedDataLoadingException("Item is invalid");
}
return new ItemEntity(EntityDataHelper::parseLocation($nbt, $world), $item, $nbt);
}, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM);
@ -126,7 +127,7 @@ final class EntityFactory{
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
$motive = PaintingMotive::getMotiveByName($nbt->getString("Motive"));
if($motive === null){
throw new \UnexpectedValueException("Unknown painting motive");
throw new SavedDataLoadingException("Unknown painting motive");
}
$blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ"));
if(($directionTag = $nbt->getTag("Direction")) instanceof ByteTag){
@ -134,7 +135,7 @@ final class EntityFactory{
}elseif(($facingTag = $nbt->getTag("Facing")) instanceof ByteTag){
$facing = Painting::DATA_TO_FACING[$facingTag->getValue()] ?? Facing::NORTH;
}else{
throw new \UnexpectedValueException("Missing facing info");
throw new SavedDataLoadingException("Missing facing info");
}
return new Painting(EntityDataHelper::parseLocation($nbt, $world), $blockIn, $facing, $motive, $nbt);
@ -151,7 +152,7 @@ final class EntityFactory{
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER));
if($potionType === null){
throw new \UnexpectedValueException("No such potion type");
throw new SavedDataLoadingException("No such potion type");
}
return new SplashPotion(EntityDataHelper::parseLocation($nbt, $world), null, $potionType, $nbt);
}, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION);
@ -222,25 +223,28 @@ final class EntityFactory{
/**
* Creates an entity from data stored on a chunk.
*
* @throws \RuntimeException
* @throws NbtDataException
* @throws SavedDataLoadingException
* @internal
*/
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
$saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$func = null;
if($saveId instanceof StringTag){
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
}elseif($saveId instanceof IntTag){ //legacy MCPE format
$func = $this->creationFuncs[$saveId->getValue() & 0xff] ?? null;
}
if($func === null){
return null;
}
/** @var Entity $entity */
$entity = $func($world, $nbt);
try{
$saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$func = null;
if($saveId instanceof StringTag){
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
}elseif($saveId instanceof IntTag){ //legacy MCPE format
$func = $this->creationFuncs[$saveId->getValue() & 0xff] ?? null;
}
if($func === null){
return null;
}
/** @var Entity $entity */
$entity = $func($world, $nbt);
return $entity;
return $entity;
}catch(NbtException $e){
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
}
}
public function injectSaveId(string $class, CompoundTag $saveData) : void{

View File

@ -27,6 +27,7 @@ use pocketmine\entity\utils\ExperienceUtils;
use pocketmine\event\player\PlayerExperienceChangeEvent;
use pocketmine\item\Durable;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\utils\Limits;
use pocketmine\world\sound\XpCollectSound;
use pocketmine\world\sound\XpLevelUpSound;
use function array_rand;
@ -141,7 +142,9 @@ class ExperienceManager{
public function setCurrentTotalXp(int $amount) : bool{
$newLevel = ExperienceUtils::getLevelFromXp($amount);
return $this->setXpAndProgress((int) $newLevel, $newLevel - ((int) $newLevel));
$xpLevel = (int) $newLevel;
$xpProgress = $newLevel - (int) $newLevel;
return $this->setXpAndProgress($xpLevel, $xpProgress);
}
/**
@ -151,6 +154,7 @@ class ExperienceManager{
* @param bool $playSound Whether to play level-up and XP gained sounds.
*/
public function addXp(int $amount, bool $playSound = true) : bool{
$amount = min($amount, Limits::INT32_MAX - $this->totalXp);
$oldLevel = $this->getXpLevel();
$oldTotal = $this->getCurrentTotalXp();
@ -223,8 +227,8 @@ class ExperienceManager{
* score when they die. (TODO: add this when MCPE supports it)
*/
public function setLifetimeTotalXp(int $amount) : void{
if($amount < 0){
throw new \InvalidArgumentException("XP must be greater than 0");
if($amount < 0 || $amount > Limits::INT32_MAX){
throw new \InvalidArgumentException("XP must be greater than 0 and less than " . Limits::INT32_MAX);
}
$this->totalXp = $amount;

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\entity;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\animation\TotemUseAnimation;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;
@ -47,8 +48,10 @@ use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
@ -102,12 +105,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
/**
* @throws InvalidSkinException
* @throws \UnexpectedValueException
* @throws SavedDataLoadingException
*/
public static function parseSkinNBT(CompoundTag $nbt) : Skin{
$skinTag = $nbt->getCompoundTag("Skin");
if($skinTag === null){
throw new \UnexpectedValueException("Missing skin data");
throw new SavedDataLoadingException("Missing skin data");
}
return new Skin( //this throws if the skin is invalid
$skinTag->getString("Name"),
@ -145,7 +148,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
*/
public function sendSkin(?array $targets = null) : void{
$this->server->broadcastPackets($targets ?? $this->hasSpawned, [
PlayerSkinPacket::create($this->getUniqueId(), SkinAdapterSingleton::get()->toSkinData($this->skin))
PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin))
]);
}
@ -214,6 +217,19 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->uuid = Uuid::uuid3(Uuid::NIL, ((string) $this->getId()) . $this->skin->getSkinData() . $this->getNameTag());
}
/**
* @param Item[] $items
* @phpstan-param array<int, Item> $items
*/
private static function populateInventoryFromListTag(Inventory $inventory, array $items) : void{
$listeners = $inventory->getListeners()->toArray();
$inventory->getListeners()->clear();
$inventory->setContents($items);
$inventory->getListeners()->add(...$listeners);
}
protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt);
@ -244,10 +260,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$inventoryTag = $nbt->getListTag("Inventory");
if($inventoryTag !== null){
$armorListeners = $this->armorInventory->getListeners()->toArray();
$this->armorInventory->getListeners()->clear();
$inventoryListeners = $this->inventory->getListeners()->toArray();
$this->inventory->getListeners()->clear();
$inventoryItems = [];
$armorInventoryItems = [];
/** @var CompoundTag $item */
foreach($inventoryTag as $i => $item){
@ -255,14 +269,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
if($slot >= 0 and $slot < 9){ //Hotbar
//Old hotbar saving stuff, ignore it
}elseif($slot >= 100 and $slot < 104){ //Armor
$this->armorInventory->setItem($slot - 100, Item::nbtDeserialize($item));
$armorInventoryItems[$slot - 100] = Item::nbtDeserialize($item);
}elseif($slot >= 9 and $slot < $this->inventory->getSize() + 9){
$this->inventory->setItem($slot - 9, Item::nbtDeserialize($item));
$inventoryItems[$slot - 9] = Item::nbtDeserialize($item);
}
}
$this->armorInventory->getListeners()->add(...$armorListeners);
$this->inventory->getListeners()->add(...$inventoryListeners);
self::populateInventoryFromListTag($this->inventory, $inventoryItems);
self::populateInventoryFromListTag($this->armorInventory, $armorInventoryItems);
}
$offHand = $nbt->getCompoundTag("OffHandItem");
if($offHand !== null){
@ -276,10 +290,13 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
if($enderChestInventoryTag !== null){
$enderChestInventoryItems = [];
/** @var CompoundTag $item */
foreach($enderChestInventoryTag as $i => $item){
$this->enderInventory->setItem($item->getByte("Slot"), Item::nbtDeserialize($item));
$enderChestInventoryItems[$item->getByte("Slot")] = Item::nbtDeserialize($item);
}
self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems);
}
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
@ -444,17 +461,24 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
}
$pk = new AddPlayerPacket();
$pk->uuid = $this->getUniqueId();
$pk->username = $this->getName();
$pk->entityRuntimeId = $this->getId();
$pk->position = $this->location->asVector3();
$pk->motion = $this->getMotion();
$pk->yaw = $this->location->yaw;
$pk->pitch = $this->location->pitch;
$pk->item = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand()));
$pk->metadata = $this->getAllNetworkData();
$player->getNetworkSession()->sendDataPacket($pk);
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
$this->getUniqueId(),
$this->getName(),
$this->getId(), //TODO: actor unique ID
$this->getId(),
"",
$this->location->asVector3(),
$this->getMotion(),
$this->location->pitch,
$this->location->yaw,
$this->location->yaw, //TODO: head yaw
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
$this->getAllNetworkData(),
AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->getId()), //TODO
[], //TODO: entity links
"", //device ID (we intentionally don't send this - secvuln)
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)
));
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);

View File

@ -34,7 +34,7 @@ class Location extends Position{
/** @var float */
public $pitch;
public function __construct(float $x, float $y, float $z, float $yaw = 0.0, float $pitch = 0.0, ?World $world = null){
public function __construct(float $x, float $y, float $z, ?World $world, float $yaw, float $pitch){
$this->yaw = $yaw;
$this->pitch = $pitch;
parent::__construct($x, $y, $z, $world);
@ -44,14 +44,14 @@ class Location extends Position{
* @return Location
*/
public static function fromObject(Vector3 $pos, ?World $world, float $yaw = 0.0, float $pitch = 0.0){
return new Location($pos->x, $pos->y, $pos->z, $yaw, $pitch, $world ?? (($pos instanceof Position) ? $pos->world : null));
return new Location($pos->x, $pos->y, $pos->z, $world ?? (($pos instanceof Position) ? $pos->world : null), $yaw, $pitch);
}
/**
* Return a Location instance
*/
public function asLocation() : Location{
return new Location($this->x, $this->y, $this->z, $this->yaw, $this->pitch, $this->world);
return new Location($this->x, $this->y, $this->z, $this->world, $this->yaw, $this->pitch);
}
public function getYaw() : float{

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\entity;
use Ahc\Json\Comment as CommentedJsonDecoder;
use pocketmine\utils\Limits;
use function implode;
use function in_array;
use function json_encode;
@ -48,7 +49,17 @@ final class Skin{
/** @var string */
private $geometryData;
private static function checkLength(string $string, string $name, int $maxLength) : void{
if(strlen($string) > $maxLength){
throw new InvalidSkinException("$name must be at most $maxLength bytes, but have " . strlen($string) . " bytes");
}
}
public function __construct(string $skinId, string $skinData, string $capeData = "", string $geometryName = "", string $geometryData = ""){
self::checkLength($skinId, "Skin ID", Limits::INT16_MAX);
self::checkLength($geometryName, "Geometry name", Limits::INT16_MAX);
self::checkLength($geometryData, "Geometry data", Limits::INT32_MAX);
if($skinId === ""){
throw new InvalidSkinException("Skin ID must not be empty");
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\Living;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class ArmSwingAnimation implements Animation{
@ -37,7 +38,7 @@ final class ArmSwingAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::ARM_SWING, 0)
ActorEventPacket::create($this->entity->getId(), ActorEvent::ARM_SWING, 0)
];
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\projectile\Arrow;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
class ArrowShakeAnimation implements Animation{
@ -40,7 +41,7 @@ class ArrowShakeAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->arrow->getId(), ActorEventPacket::ARROW_SHAKE, $this->durationInTicks)
ActorEventPacket::create($this->arrow->getId(), ActorEvent::ARROW_SHAKE, $this->durationInTicks)
];
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\entity\Human;
use pocketmine\item\Item;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class ConsumingItemAnimation implements Animation{
@ -45,7 +46,7 @@ final class ConsumingItemAnimation implements Animation{
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($this->item->getId(), $this->item->getMeta());
return [
//TODO: need to check the data values
ActorEventPacket::create($this->human->getId(), ActorEventPacket::EATING_ITEM, ($netId << 16) | $netData)
ActorEventPacket::create($this->human->getId(), ActorEvent::EATING_ITEM, ($netId << 16) | $netData)
];
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\Living;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class DeathAnimation implements Animation{
@ -37,7 +38,7 @@ final class DeathAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::DEATH_ANIMATION, 0)
ActorEventPacket::create($this->entity->getId(), ActorEvent::DEATH_ANIMATION, 0)
];
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\Living;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class HurtAnimation implements Animation{
@ -37,7 +38,7 @@ final class HurtAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::HURT_ANIMATION, 0)
ActorEventPacket::create($this->entity->getId(), ActorEvent::HURT_ANIMATION, 0)
];
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\Living;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class RespawnAnimation implements Animation{
@ -37,7 +38,7 @@ final class RespawnAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::RESPAWN, 0)
ActorEventPacket::create($this->entity->getId(), ActorEvent::RESPAWN, 0)
];
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\Squid;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class SquidInkCloudAnimation implements Animation{
@ -37,7 +38,7 @@ final class SquidInkCloudAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->squid->getId(), ActorEventPacket::SQUID_INK_CLOUD, 0)
ActorEventPacket::create($this->squid->getId(), ActorEvent::SQUID_INK_CLOUD, 0)
];
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
use pocketmine\entity\Human;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
final class TotemUseAnimation implements Animation{
@ -38,7 +39,7 @@ final class TotemUseAnimation implements Animation{
public function encode() : array{
return [
ActorEventPacket::create($this->human->getId(), ActorEventPacket::CONSUME_TOTEM, 0)
ActorEventPacket::create($this->human->getId(), ActorEvent::CONSUME_TOTEM, 0)
];
}
}

View File

@ -38,12 +38,14 @@ class Effect{
* @param Translatable|string $name Translation key used for effect name
* @param Color $color Color of bubbles given by this effect
* @param bool $bad Whether the effect is harmful
* @param int $defaultDuration
* @param bool $hasBubbles Whether the effect has potion bubbles. Some do not (e.g. Instant Damage has its own particles instead of bubbles)
*/
public function __construct(
protected Translatable|string $name,
protected Color $color,
protected bool $bad = false,
private int $defaultDuration = 600,
protected bool $hasBubbles = true
){}
@ -73,7 +75,7 @@ class Effect{
* Returns the default duration (in ticks) this effect will apply for if a duration is not specified.
*/
public function getDefaultDuration() : int{
return 600;
return $this->defaultDuration;
}
/**

View File

@ -23,10 +23,13 @@ declare(strict_types=1);
namespace pocketmine\entity\effect;
use pocketmine\color\Color;
use pocketmine\lang\Translatable;
abstract class InstantEffect extends Effect{
public function getDefaultDuration() : int{
return 1;
public function __construct(Translatable|string $name, Color $color, bool $bad = false, bool $hasBubbles = true){
parent::__construct($name, $color, $bad, 1, $hasBubbles);
}
public function canTick(EffectInstance $instance) : bool{

View File

@ -34,8 +34,8 @@ class PoisonEffect extends Effect{
/** @var bool */
private $fatal;
public function __construct(Translatable|string $name, Color $color, bool $isBad = false, bool $hasBubbles = true, bool $fatal = false){
parent::__construct($name, $color, $isBad, $hasBubbles);
public function __construct(Translatable|string $name, Color $color, bool $isBad = false, int $defaultDuration = 600, bool $hasBubbles = true, bool $fatal = false){
parent::__construct($name, $color, $isBad, $defaultDuration, $hasBubbles);
$this->fatal = $fatal;
}

View File

@ -0,0 +1,73 @@
<?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\entity\effect;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\StringToTParser;
/**
* Handles parsing effect types from strings. This is used to interpret names in the /effect command.
*
* @phpstan-extends StringToTParser<Effect>
*/
final class StringToEffectParser extends StringToTParser{
use SingletonTrait;
private static function make() : self{
$result = new self;
$result->register("absorption", fn() => VanillaEffects::ABSORPTION());
$result->register("blindness", fn() => VanillaEffects::BLINDNESS());
$result->register("conduit_power", fn() => VanillaEffects::CONDUIT_POWER());
$result->register("fatal_poison", fn() => VanillaEffects::FATAL_POISON());
$result->register("fire_resistance", fn() => VanillaEffects::FIRE_RESISTANCE());
$result->register("haste", fn() => VanillaEffects::HASTE());
$result->register("health_boost", fn() => VanillaEffects::HEALTH_BOOST());
$result->register("hunger", fn() => VanillaEffects::HUNGER());
$result->register("instant_damage", fn() => VanillaEffects::INSTANT_DAMAGE());
$result->register("instant_health", fn() => VanillaEffects::INSTANT_HEALTH());
$result->register("invisibility", fn() => VanillaEffects::INVISIBILITY());
$result->register("jump_boost", fn() => VanillaEffects::JUMP_BOOST());
$result->register("levitation", fn() => VanillaEffects::LEVITATION());
$result->register("mining_fatigue", fn() => VanillaEffects::MINING_FATIGUE());
$result->register("nausea", fn() => VanillaEffects::NAUSEA());
$result->register("night_vision", fn() => VanillaEffects::NIGHT_VISION());
$result->register("poison", fn() => VanillaEffects::POISON());
$result->register("regeneration", fn() => VanillaEffects::REGENERATION());
$result->register("resistance", fn() => VanillaEffects::RESISTANCE());
$result->register("saturation", fn() => VanillaEffects::SATURATION());
$result->register("slowness", fn() => VanillaEffects::SLOWNESS());
$result->register("speed", fn() => VanillaEffects::SPEED());
$result->register("strength", fn() => VanillaEffects::STRENGTH());
$result->register("water_breathing", fn() => VanillaEffects::WATER_BREATHING());
$result->register("weakness", fn() => VanillaEffects::WEAKNESS());
$result->register("wither", fn() => VanillaEffects::WITHER());
return $result;
}
public function parse(string $input) : ?Effect{
return parent::parse($input);
}
}

View File

@ -26,7 +26,6 @@ namespace pocketmine\entity\effect;
use pocketmine\color\Color;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\utils\RegistryTrait;
use function assert;
/**
* This doc-block is generated automatically, do not modify it manually.
@ -69,7 +68,7 @@ final class VanillaEffects{
//TODO: bad_omen
self::register("blindness", new Effect(KnownTranslationFactory::potion_blindness(), new Color(0x1f, 0x1f, 0x23), true));
self::register("conduit_power", new Effect(KnownTranslationFactory::potion_conduitPower(), new Color(0x1d, 0xc2, 0xd1)));
self::register("fatal_poison", new PoisonEffect(KnownTranslationFactory::potion_poison(), new Color(0x4e, 0x93, 0x31), true, true, true));
self::register("fatal_poison", new PoisonEffect(KnownTranslationFactory::potion_poison(), new Color(0x4e, 0x93, 0x31), true, 600, true, true));
self::register("fire_resistance", new Effect(KnownTranslationFactory::potion_fireResistance(), new Color(0xe4, 0x9a, 0x3a)));
self::register("haste", new Effect(KnownTranslationFactory::potion_digSpeed(), new Color(0xd9, 0xc0, 0x43)));
self::register("health_boost", new HealthBoostEffect(KnownTranslationFactory::potion_healthBoost(), new Color(0xf8, 0x7d, 0x23)));
@ -85,7 +84,7 @@ final class VanillaEffects{
self::register("poison", new PoisonEffect(KnownTranslationFactory::potion_poison(), new Color(0x4e, 0x93, 0x31), true));
self::register("regeneration", new RegenerationEffect(KnownTranslationFactory::potion_regeneration(), new Color(0xcd, 0x5c, 0xab)));
self::register("resistance", new Effect(KnownTranslationFactory::potion_resistance(), new Color(0x99, 0x45, 0x3a)));
self::register("saturation", new SaturationEffect(KnownTranslationFactory::potion_saturation(), new Color(0xf8, 0x24, 0x23), false));
self::register("saturation", new SaturationEffect(KnownTranslationFactory::potion_saturation(), new Color(0xf8, 0x24, 0x23)));
//TODO: slow_falling
self::register("slowness", new SlownessEffect(KnownTranslationFactory::potion_moveSlowdown(), new Color(0x5a, 0x6c, 0x81), true));
self::register("speed", new SpeedEffect(KnownTranslationFactory::potion_moveSpeed(), new Color(0x7c, 0xaf, 0xc6)));
@ -109,10 +108,4 @@ final class VanillaEffects{
$result = self::_registryGetAll();
return $result;
}
public static function fromString(string $name) : Effect{
$result = self::_registryFromString($name);
assert($result instanceof Effect);
return $result;
}
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\entity\object;
use pocketmine\block\Block;
use pocketmine\block\BlockFactory;
use pocketmine\block\utils\Fallable;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\Entity;
use pocketmine\entity\EntitySizeInfo;
use pocketmine\entity\Location;
@ -71,7 +72,7 @@ class FallingBlock extends Entity{
}
if($blockId === 0){
throw new \UnexpectedValueException("Missing block info from NBT");
throw new SavedDataLoadingException("Missing block info from NBT");
}
$damage = $nbt->getByte("Data", 0);

View File

@ -68,7 +68,7 @@ class ItemEntity extends Entity{
if($item->isNull()){
throw new \InvalidArgumentException("Item entity must have a non-air item with a count of at least 1");
}
$this->item = $item;
$this->item = clone $item;
parent::__construct($location, $nbt);
}
@ -206,14 +206,15 @@ class ItemEntity extends Entity{
}
protected function sendSpawnPacket(Player $player) : void{
$pk = new AddItemActorPacket();
$pk->entityRuntimeId = $this->getId();
$pk->position = $this->location->asVector3();
$pk->motion = $this->getMotion();
$pk->item = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getItem()));
$pk->metadata = $this->getAllNetworkData();
$player->getNetworkSession()->sendDataPacket($pk);
$player->getNetworkSession()->sendDataPacket(AddItemActorPacket::create(
$this->getId(), //TODO: entity unique ID
$this->getId(),
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getItem())),
$this->location->asVector3(),
$this->getMotion(),
$this->getAllNetworkData(),
false //TODO: I have no idea what this is needed for, but right now we don't support fishing anyway
));
}
public function getOffsetPosition(Vector3 $vector3) : Vector3{
@ -227,8 +228,8 @@ class ItemEntity extends Entity{
$item = $this->getItem();
$playerInventory = match(true){
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->canAddItem($item) => $player->getOffHandInventory(),
$player->getInventory()->canAddItem($item) => $player->getInventory(),
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->getAddableItemQuantity($item) > 0 => $player->getOffHandInventory(),
$player->getInventory()->getAddableItemQuantity($item) > 0 => $player->getInventory(),
default => null
};
@ -246,7 +247,12 @@ class ItemEntity extends Entity{
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
}
$ev->getInventory()?->addItem($ev->getItem());
$inventory = $ev->getInventory();
if($inventory !== null){
foreach($inventory->addItem($ev->getItem()) as $remains){
$this->getWorld()->dropItem($this->location, $remains, new Vector3(0, 0, 0));
}
}
$this->flagForDespawn();
}
}

View File

@ -149,17 +149,17 @@ class Painting extends Entity{
}
protected function sendSpawnPacket(Player $player) : void{
$pk = new AddPaintingPacket();
$pk->entityRuntimeId = $this->getId();
$pk->position = new Vector3(
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
($this->boundingBox->minY + $this->boundingBox->maxY) / 2,
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
);
$pk->direction = self::FACING_TO_DATA[$this->facing];
$pk->title = $this->motive->getName();
$player->getNetworkSession()->sendDataPacket($pk);
$player->getNetworkSession()->sendDataPacket(AddPaintingPacket::create(
$this->getId(), //TODO: entity unique ID
$this->getId(),
new Vector3(
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
($this->boundingBox->minY + $this->boundingBox->maxY) / 2,
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
),
self::FACING_TO_DATA[$this->facing],
$this->motive->getName()
));
}
/**

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