Compare commits

...

110 Commits

Author SHA1 Message Date
6c21c23444 Release 3.15.3 2020-11-08 14:48:31 +00:00
55e0d9c520 Properly time chunk loading and chunk sending on timings reports, closes #3895 2020-11-08 14:30:12 +00:00
37ee3f2775 Player: fixed orderChunks performance issue on newly-generated maps
every time a chunk passed through Level->generateChunkCallback(), it fired onChunkChanged() for chunk listeners, which in turn caused players to rerun chunk orders even though the target chunk had not been sent yet anyway.
2020-11-05 16:17:39 +00:00
bfdcc12e81 phpstan 0.12.54 2020-11-05 14:49:00 +00:00
b2299e08e0 phpstan 0.12.53 2020-11-03 15:00:56 +00:00
d7741050c5 Updated composer dependencies 2020-11-03 14:54:50 +00:00
6cff08cd65 Chunk: fixed hasChanged being set on fastDeserialize() chunks (caused by 2bb497b716)
this caused some performance issues and silent bugs with the generator, notably that the generator would always think all chunks had been changed, causing them to be re-set back into the world 9 times.
2020-11-01 15:50:21 +00:00
fec42f16ba Level: properly define type of generator field 2020-11-01 14:36:05 +00:00
deb0cee8a0 BaseInventory::canAddItem(): consider item max stack size (#3881)
this fixes addItem() failing when canAddItem() reported that an item can be added.
2020-11-01 13:49:13 +00:00
c0dafe7872 Explosion: remove dead code
this was needed for the old ExplodePacket, which was removed a few versions back.
2020-10-29 13:32:56 +00:00
340881d590 remove superfluous newline 2020-10-26 15:59:57 +00:00
e2e960e43d tests: add missing function imports 2020-10-26 15:59:42 +00:00
500fd2d842 tests: strip useless phpdoc 2020-10-26 15:59:17 +00:00
0b550b346b imports cleanup 2020-10-26 15:43:25 +00:00
1424114cf2 Clean phpstan baselines
some of these are dead, others are FPs fixed by newer PHPStan versions.
2020-10-24 17:22:49 +01:00
a8980a0f67 phpstan 0.12.51 2020-10-24 17:10:31 +01:00
69aa7c5ac1 Support for Composer v2 (#3880) 2020-10-24 16:42:38 +01:00
11b74868ee CraftingTransaction: remove impossible condition
this is never hit thanks to the logic flow above - recipeItems is never empty.
2020-10-24 11:22:02 +01:00
9a53de0903 Utils: explode() never returns an empty array 2020-10-24 11:19:37 +01:00
0f8101d4a6 McRegion: Ignore files which don't have a valid file extension
previously a file with a 4-letter name ending in 'mca' in the region folder of a PMAnvil world would cause the world format to be unrecognized. This happens because strrpos() returns false when the substring isn't found, which gets coerced to 0 when used in addition.
2020-10-24 11:15:07 +01:00
55ecac4c80 Fixed always-true condition in world loading
this has a couple of side effects which need to be explored.
- first of all, this bug prevented generateLevel() from filling in the preset from server.properties. With this fix, worlds which don't have any extra generator settings will start to be generated using server.properties settings, which is almost certainly not expected behaviour.
- preset can now be specified separately from generator in pocketmine.yml, which is nicer for users.
2020-10-24 11:10:35 +01:00
2a1d1e90a2 php-cs-fixer nits 2020-10-21 16:44:57 +01:00
4444a79468 Bump phpunit/phpunit from 9.4.1 to 9.4.2 (#3875) 2020-10-20 09:06:46 +00:00
4cbeee3ab8 Bump phpstan/phpstan from 0.12.49 to 0.12.50 (#3874) 2020-10-17 13:44:43 +00:00
a251960c1c AsyncPool: expose workerUsage to the API
this allows plugins (and maybe later on the core) to detect async worker overload and warn the user about potential performance issues.
I planned to implement such detection in the core directly, but it turned out to be a bit more complex than I anticipated. At the least, this API might be useful to someone else.
2020-10-16 21:20:49 +01:00
52f734799e Human: do not modify totalXp unless setting XP succeeds 2020-10-16 20:43:03 +01:00
42171f6e06 Human: beware negative values in addXp() 2020-10-16 20:42:13 +01:00
1fe4fdc67c PluginDescription: fixed some very old refactoring errors 2020-10-15 13:55:40 +01:00
af4f30d1c8 Bump phpstan/phpstan from 0.12.48 to 0.12.49 (#3873) 2020-10-13 16:31:25 +00:00
3e2926441d PluginDescription: make sure that extensions constraints are actually strings 2020-10-13 17:21:10 +01:00
0b33762be0 PluginDescription: fixed type of extensions (reported by phpstan 0.12.49) 2020-10-13 17:21:10 +01:00
e6f89213dc Entity: properly account for upwards motion when calculating fall distance (#3867)
Previously, upwards movement wouldn't be considered, but downwards would, so if an entity bobbed up and down in the air for a while (e.g. while being comboed in PvP), the downwards distance would accumulate and deal a large amount of fall damage on touchdown.
This commit changes fall distance measurement to correctly account for upwards movement.

A better way of measuring fall distance would simply be to record the highest Y coordinate reached while in the air, and then measure the distance between that and the point of contact when landing. This would also remove the need to constantly update the fallDistance field. However, this would involve a BC break and will therefore have to wait until PM4.
2020-10-13 14:16:09 +01:00
f8d249b240 Isolate and always show IP details on install (#3870)
* Isolate and always show IP details on install

* camelCase
2020-10-13 14:11:28 +01:00
b39afa20d1 Bump phpunit/phpunit from 9.4.0 to 9.4.1 (#3872) 2020-10-13 13:09:56 +00:00
7027a9b972 camelCase 2020-10-12 08:25:34 -04:00
b02f3f4090 Isolate and always show IP details on install 2020-10-11 18:57:30 -04:00
8564912149 phpstan: define LEVELDB_ZLIB_RAW_COMPRESSION if it doesn't exist (for phpstan)
this improves the analysis quality by informing phpstan of the type of whatever should be there.
2020-10-11 23:01:21 +01:00
873535f719 Timezone: explicitly check result of getURL()
phpstan-strict-rules should report this, but it doesn't ...
2020-10-09 17:23:22 +01:00
78f4fcf6ab StatusCommand: removed "Maximum memory (system)"
it's not clear what this was actually supposed to represent, but it actually reports VmSize, which is already reported by "Total virtual memory". This line confuses users and is misleading.
2020-10-08 22:58:37 +01:00
90b749c260 net: reduce default compression level to 6
On larger packets, this worsens compression ratio by 1-2%, but reduces CPU load due to compression by 15-20%. Higher levels than 6 are far more expensive for diminishing returns.
Level 5 produces a further 25-30% CPU reduction, but increases bandwidth usage by 20-25%, so 6 is the sweet spot.
2020-10-08 16:51:10 +01:00
d5398b2781 3.15.3 is next 2020-10-06 13:33:42 +01:00
d7a66ad755 Release 3.15.2 2020-10-06 13:33:42 +01:00
b3f88e7b73 Updated language submodule to pmmp/Language@c85a7b79f3 2020-10-06 13:28:51 +01:00
55adc1ef63 Updated build/php submodule to pmmp/php-build-scripts@e45cfc1ece 2020-10-06 13:09:05 +01:00
868d236ddc Updated composer dependencies 2020-10-06 12:39:19 +01:00
59e9c84806 Merge branch 'stable' of https://github.com/pmmp/pocketmine-mp into stable 2020-10-06 12:35:51 +01:00
a110317d1b Bump phpstan/phpstan from 0.12.47 to 0.12.48 (#3851) 2020-10-05 13:23:19 +00:00
b169d89291 Added some documentation to FurnaceBurnEvent 2020-10-04 21:40:52 +01:00
74bef7f423 Bump phpunit/phpunit from 9.3.11 to 9.4.0 (#3850) 2020-10-02 11:02:17 +00:00
8db7867881 travis: notify about build results 2020-10-01 22:05:12 +01:00
d8f8afe531 Bump phpstan/phpstan from 0.12.46 to 0.12.47 (#3844) 2020-09-30 10:51:33 +00:00
7dabf305f8 Bump phpstan/phpstan from 0.12.44 to 0.12.46 (#3841) 2020-09-29 07:01:51 +00:00
ed0053d0ee Updated build/php submodule to pmmp/php-build-scripts@732ceba847 2020-09-26 13:20:45 +01:00
28255e35d1 Updated composer dependencies 2020-09-26 13:18:38 +01:00
0fc9170bbf Bump phpstan/phpstan from 0.12.43 to 0.12.44 (#3838) 2020-09-25 13:20:29 +00:00
7e2efae024 Bump phpstan/phpstan from 0.12.42 to 0.12.43 (#3831) 2020-09-24 11:23:32 +00:00
e9fa07b550 Bump phpunit/phpunit from 9.3.10 to 9.3.11 (#3835) 2020-09-24 09:57:58 +00:00
8ac32824a2 Bump phpunit/phpunit from 9.3.9 to 9.3.10 (#3828) 2020-09-15 17:01:03 +00:00
1322defead Bump phpunit/phpunit from 9.3.8 to 9.3.9 (#3825) 2020-09-12 12:32:33 +00:00
d084b7a34b RegionGarbageMap: add an extra overlap check
this shouldn't ever be triggered, but we want to know if it does.
2020-09-12 01:38:13 +01:00
c2d0605b1e Entity: avoid implicit float truncation in getDirection()
this didn't cause any bugs because of the way the function is written, but it might have in other circumstances.
2020-09-11 21:01:22 +01:00
0ff0b33047 StatusCommand: avoid modulo operator on float
this was detected by a custom PHPStan extension.
2020-09-11 20:56:18 +01:00
114df07622 RegionLoader: specify type of unpack() return
PHPStan has no idea what is going on in this code because unpack() returns mixed[].
Possibly it might be a good idea to implement a dynamic return type extension for this.
2020-09-11 20:48:37 +01:00
4a88db7f43 travis: update pthreads to pmmp/pthreads@b81ab29df5 2020-09-11 14:30:22 +01:00
d3ea29d527 Release memory to OS on garbage collection
ZMM often holds onto big chunks of memory after they aren't used anymore, which is fine in a webserver, but it's not OK for PM.
2020-09-09 01:40:18 +01:00
d2f1a3cf5b SubChunk: workaround opcache preloading constant issue
non-class constants aren't stored during preloading phase and for some reason they aren't pre-resolved in opcode arrays. However, they are resolved by value when referenced by class constants, and class constants stick, so we can use those instead.
2020-09-04 17:53:22 +01:00
f9c2ed6200 composer: do not install packages with min version higher than 7.3.0
running composer update on 7.4 will generate a lock file using the newest dependencies which work for the current PHP version, which usually isn't desirable for a project like this where developers might be using newer PHP versions than users.
2020-09-04 01:00:26 +01:00
e47a711494 Bump phpstan/phpstan from 0.12.40 to 0.12.42 (#3812) 2020-09-03 14:13:14 +00:00
2ea7a9e216 3.15.2 is next 2020-09-03 15:09:52 +01:00
9f60484212 Release 3.15.1 2020-09-03 15:09:52 +01:00
3031d89ec5 Potato: drop 1-5 potatoes per harvest, not 1-4 2020-09-03 14:54:58 +01:00
883e135bc0 Potato: drop poisonous potatoes when harvested, fixes #2830 2020-09-03 14:54:04 +01:00
4448f603a6 Updated build/php submodule to pmmp/php-build-scripts@3a3e3495c3 2020-09-03 14:53:23 +01:00
9365525efa ANTI CHEAT DOESN'T TRIP ON STAIRS ANYMORE 🎉
This commit fixes the 5+ years-old bug with the movement anti-cheat that everyone has complained about: sprinting on stairs causes rubberbanding.
This commit addresses this problem at long last, along with a handful of precursor commits that were necessary to fix this problem:
- dac76f0e0f
- 89fe8f7f10
- 2d77b1e364

Additionally, these changes now allow the anti-cheat to be accurate to at least 0.001 of a block, perhaps even better. I didn't commit a change to the threshold here, but it was instrumental to pinning down the exact nature of these bugs.

This closes #1475, at long last.
2020-09-03 14:27:11 +01:00
17bee5e349 Fixed crash when using strings for flatworld layers
I don't know why this didn't show up sooner.
2020-09-03 14:15:37 +01:00
c6e0753c3e clean up phpstan baselines 2020-09-02 15:04:37 +01:00
2ae7ba275b Updated Composer dependencies 2020-09-02 12:15:29 +01:00
6aa0a82341 Bump phpstan/phpstan-strict-rules from 0.12.4 to 0.12.5 (#3807) 2020-09-01 10:00:39 +00:00
0af08a7375 Call BlockGrowEvent when sugarcane grows (#3780) 2020-08-31 16:03:38 +01:00
81c1613e5d StupidJsonDecodeTest: added a callable prototype
for some reason this causes a new error to be reported which previously didn't show. I have no idea why.
2020-08-31 13:45:46 +01:00
9cf8f608d8 Provide a default for health (#3806) 2020-08-30 21:50:11 +01:00
dd4f26a9cf Switch "Auto: Spam" trap to direct links (#3550) 2020-08-30 18:03:38 +01:00
f976545f56 Delete security-dos-vulnerability.md 2020-08-29 23:41:37 +01:00
9929fb0abd Create SECURITY.md 2020-08-29 23:40:47 +01:00
37e453b875 Updated composer dependencies 2020-08-28 23:34:32 +01:00
b7578fef9c Fixup TesterPlugin to PHPStan standards 2020-08-28 21:17:21 +01:00
09eb904f6b fixed explicit-mixed errors exposed by upgrading pocketmine/nbt 2020-08-28 15:47:41 +01:00
b47d6bbc22 Bump phpstan/phpstan from 0.12.39 to 0.12.40 (#3800) 2020-08-27 11:45:20 +00:00
aa26ddf8b1 Bump phpunit/phpunit from 9.3.7 to 9.3.8 (#3799) 2020-08-27 11:44:54 +00:00
119c72980f Bump phpstan/phpstan from 0.12.37 to 0.12.39 (#3794) 2020-08-25 20:38:22 +00:00
eba888449d ExperienceUtils: handle an unhandled error condition explicitly
this would previously throw a TypeError on some negative numbers and crash. This should still crash, but this makes it explicit.
2020-08-25 21:28:29 +01:00
dac76f0e0f Player: reset ySize when syncing movement 2020-08-23 18:31:25 +01:00
89fe8f7f10 Entity: shift BB back down after trying to auto-step
this fixes bugs where the entity would jump in the air when walking on blocks like carpet. It also fixes a lot of the issues with stepping in the anti-cheat, allowing to reduce the error tolerance on movement processing.
2020-08-23 17:54:48 +01:00
2d77b1e364 Entity: fixed recalculateCollisionBox not taking ySize into account
this was causing the movement anti-cheat to shit itself after the first movement because it used setPosition() on the player if the position wasn't exactly perfect (which obviously it never is perfect, because of fp rounding errors).
2020-08-23 17:51:36 +01:00
e59a4296f8 LevelDB: fixed crash on corrupted level.dat 2020-08-22 19:36:22 +01:00
6856761946 Updated composer dependencies to fix some 7.4 issues 2020-08-21 22:09:26 +01:00
4fe3401182 travis: moved PHP-specific configuration to its own YAML file
this allows it to be imported by other repos using the same config (for example plugins needing PHPStan analysis) without them needing to copy paste big blocks of shit every time something little changes.
2020-08-21 17:42:47 +01:00
e80ad22702 Updated build/php submodule 2020-08-21 00:34:10 +01:00
c22ab37372 Player: pre-cancel PlayerInteractEvent when left-clicking a block in spectator mode
fixes #3778
2020-08-20 23:43:52 +01:00
1f9d672cfc Updated DevTools submodule to release 1.14.1 2020-08-20 16:49:49 +01:00
974cbae725 travis: added PHP 7.4 2020-08-20 16:21:58 +01:00
b53f88027e Explosion: fixed blocks getting updated too early
this bug became painfully obvious when testing bamboo. Detonating TNT near tall bamboo canes would result in bamboo canes not deleting themselves in some circumstances, because the non-destroyed parts of the cane would be updated before their supporting block was deleted, and subsequently would not be re-updated afterwards.
I think this bug should also reproduce with cacti, but I have not tested.
2020-08-16 00:09:17 +01:00
9a0f723dff Updated composer dependencies 2020-08-15 20:19:37 +01:00
ab2003a85d Merge branch 'stable' of https://github.com/pmmp/pocketmine-mp into stable 2020-08-15 20:17:04 +01:00
4befd9095a Updated build/php submodule to pmmp/php-build-scripts@f93a6f0e31 2020-08-15 20:16:29 +01:00
06623d788a Bump phpunit/phpunit from 9.3.5 to 9.3.7 (#3771) 2020-08-15 17:46:26 +00:00
730ee74a65 Use objects for internal structures created in TextFormat::toJSON() (#3747) 2020-08-15 18:30:26 +01:00
700e0afee0 Updated build/php submodule to pmmp/php-build-scripts@cfc425ad63 2020-08-11 21:39:15 +01:00
4b9712fdee fixed changelog typo 2020-08-11 21:37:15 +01:00
dbd015b866 3.15.1 is next 2020-08-11 21:26:12 +01:00
69 changed files with 953 additions and 648 deletions

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Help & support on Discord
url: https://discord.gg/bmSAZBG
about: We don't accept support requests on the issue tracker. Please try asking on Discord instead.
- name: Help & support on forums
url: https://forums.pmmp.io
about: We don't accept support requests on the issue tracker. Please try asking on forums instead.
- name: Documentation
url: https://pmmp.rtfd.io
about: PocketMine-MP documentation

View File

@ -1,14 +0,0 @@
---
name: Help & support
about: We don't accept support requests here. Try the links on the README.
title: ''
labels: Support request
assignees: ''
---
We don't accept support requests on the issue tracker. Please try the following links instead:
Documentation: http://pmmp.rtfd.io
Forums: https://forums.pmmp.io
Discord: https://discord.gg/bmSAZBG

View File

@ -1,12 +0,0 @@
---
name: Security/DoS vulnerability
about: 'Bug or exploit that can be used to attack servers (hint: don''t report it
on a public issue tracker)'
title: ''
labels: 'Auto: Spam'
assignees: ''
---
Please DO NOT report security vulnerabilities here.
Instead, send an email to team@pmmp.io or contact a developer directly, IN PRIVATE.

View File

@ -1,20 +1,5 @@
language: php
php:
- 7.3
before_script:
- phpenv config-rm xdebug.ini
- echo | pecl install channel://pecl.php.net/yaml-2.1.0
- git clone https://github.com/pmmp/pthreads.git
- cd pthreads
- git checkout 45579e1e622acd80f9c880f3a025ba3b98b8ebef
- phpize
- ./configure
- make
- make install
- cd ..
- echo "extension=pthreads.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
import:
source: ./tests/travis/setup-php.yml
script:
- composer install --prefer-dist
@ -29,4 +14,8 @@ cache:
- $HOME/.composer/cache/vcs
notifications:
email: false
email:
recipients:
- team@pmmp.io
on_success: change
on_failure: always

33
SECURITY.md Normal file
View File

@ -0,0 +1,33 @@
# Security Policy
## Supported Versions
The following release lines are currently receiving active security updates and bug fixes:
| Version | Supported |
| -------- | ------------------ |
| 3.15.x | :white_check_mark: |
| < 3.15.0 | :x: |
## Reporting a Vulnerability
**DO NOT report vulnerabilities on the GitHub issue tracker.**
GitHub is public and anyone can see the issues you post on the issue tracker, including people who would exploit vulnerabilities for their own gain.
**WARNING: You may put live servers at risk by reporting a vulnerability on the GitHub issue tracker.**
**Contact us** by sending an email to [**team@pmmp.io**](mailto:team@pmmp.io?subject=Security%20Vulnerability%20in%20PocketMine-MP). Include the following information:
- Version of PocketMine-MP
- Detailed description of the vulnerability (e.g. how to exploit it, what the effects are)
Please note that we can't guarantee a reply to every email.
## FAQ
### Do you offer a bug bounty?
No.
### How soon can I expect a fix for a vulnerability I've reported?
This depends on the nature of the problem. We can't provide any general ETA (nor would it be wise to provide one).
In general, it depends on when developers have time to look into the problem, how complex the problem is to fix, and how many users it impacts.
When a fix for a severe vulnerability is pushed, a patch release for the target version will usually be released within 24 hours so that users can update.

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\build\make_release;
use pocketmine\utils\VersionString;
use function defined;
use function dirname;
use function fgets;
use function file_get_contents;
@ -37,7 +38,6 @@ use const STDIN;
require_once dirname(__DIR__) . '/vendor/autoload.php';
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev) : void{
$versionInfo = file_get_contents($versionInfoPath);
$versionInfo = preg_replace(

View File

@ -6,7 +6,7 @@ Plugin developers should **only** update their required API to this version if y
**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.14.0
# 3.15.0
- Added support for Minecraft: Bedrock Edition 1.16.20.
- Removed compatibility with 1.16.0.
@ -15,3 +15,20 @@ Plugin developers should **only** update their required API to this version if y
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
- New blocks, items & mobs aren't implemented
- Nether doesn't exist
# 3.15.1
- Fixed various PHP 7.4 compatibility issues in Composer dependencies (primarily callback-validator).
- Fixed LevelDB worlds with corrupted `level.dat` crashing the server instead of failing gracefully.
- Fixed error spam when using strings for layers in flatworld presets (`e.g. bedrock,3xdirt,grass`).
- Fixed blocks not getting updated properly on explosions.
- Fixed `BlockGrowEvent` not being called when sugarcane grows.
- Potato crops now drop poisonous potatoes when harvested.
- Fixed the wrong number of potatoes being dropped when harvesting potato crops.
- Players no longer get pullbacks when sprinting on slabs, stairs and various other blocks when `player.anti-cheat.allow-movement-cheats` is set to `false`. (This bug has been around for over 5 years, so many of you will be used to its existence.)
- Fixed entity collision box calculation not taking clip distance into account.
- Entities now step up the correct height of the target block, instead of jumping into the air 0.6 blocks and falling back down.
# 3.15.2
- Fixed issues with preloading `SubChunk`.
- `/gc` and automatic garbage collection will now release unused heap blocks back to the OS. Previously, the PHP process might hold onto these blocks indefinitely even when not used, causing elevated real memory usage.
- Added some documentation to `FurnaceBurnEvent`.

View File

@ -33,12 +33,12 @@
"pocketmine/classloader": "^0.1.0",
"pocketmine/log": "^0.2.0",
"pocketmine/log-pthreads": "^0.1.0",
"pocketmine/callback-validator": "^1.0.1",
"pocketmine/callback-validator": "^1.0.2",
"adhocore/json-comment": "^0.1.0",
"ocramius/package-versions": "^1.5"
"composer-runtime-api": "^2.0"
},
"require-dev": {
"phpstan/phpstan": "0.12.37",
"phpstan/phpstan": "0.12.54",
"phpstan/phpstan-phpunit": "^0.12.6",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpunit/phpunit": "^9.2"
@ -57,5 +57,10 @@
"psr-4": {
"pocketmine\\": "tests/phpunit/"
}
},
"config": {
"platform": {
"php": "7.3.0"
}
}
}

814
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@ includes:
- tests/phpstan/configs/actual-problems.neon
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
- tests/phpstan/configs/com-dotnet-magic.neon
- tests/phpstan/configs/custom-leveldb.neon
- tests/phpstan/configs/gc-hacks.neon
- tests/phpstan/configs/l7-baseline.neon
- tests/phpstan/configs/l8-baseline.neon
@ -20,6 +19,8 @@ parameters:
checkExplicitMixed: true
bootstrapFiles:
- tests/phpstan/bootstrap.php
scanDirectories:
- tests/plugins/TesterPlugin
scanFiles:
- src/pocketmine/PocketMine.php
- build/make-release.php
@ -29,6 +30,7 @@ parameters:
- build/make-release.php
- build/server-phar.php
- tests/phpunit
- tests/plugins/TesterPlugin
dynamicConstantNames:
- pocketmine\IS_DEVELOPMENT_BUILD
- pocketmine\DEBUG

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine;
use PackageVersions\Versions;
use Composer\InstalledVersions;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\plugin\PluginBase;
use pocketmine\plugin\PluginLoadOrder;
@ -54,6 +54,7 @@ use function php_uname;
use function phpinfo;
use function phpversion;
use function preg_replace;
use function sprintf;
use function str_split;
use function strpos;
use function substr;
@ -338,6 +339,15 @@ class CrashDump{
private function generalData() : void{
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
$composerLibraries = [];
foreach(InstalledVersions::getInstalledPackages() as $package){
$composerLibraries[$package] = sprintf(
"%s@%s",
InstalledVersions::getPrettyVersion($package) ?? "unknown",
InstalledVersions::getReference($package) ?? "unknown"
);
}
$this->data["general"] = [];
$this->data["general"]["name"] = $this->server->getName();
$this->data["general"]["base_version"] = \pocketmine\BASE_VERSION;
@ -350,7 +360,7 @@ class CrashDump{
$this->data["general"]["zend"] = zend_version();
$this->data["general"]["php_os"] = PHP_OS;
$this->data["general"]["os"] = Utils::getOS();
$this->data["general"]["composer_libraries"] = Versions::VERSIONS;
$this->data["general"]["composer_libraries"] = $composerLibraries;
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
$this->addLine("uname -a: " . php_uname("a"));
@ -358,7 +368,7 @@ class CrashDump{
$this->addLine("Zend version: " . zend_version());
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
$this->addLine("Composer libraries: ");
foreach(Versions::VERSIONS as $library => $libraryVersion){
foreach($composerLibraries as $library => $libraryVersion){
$this->addLine("- $library $libraryVersion");
}
}

View File

@ -40,6 +40,7 @@ use function fwrite;
use function gc_collect_cycles;
use function gc_disable;
use function gc_enable;
use function gc_mem_caches;
use function get_class;
use function get_declared_classes;
use function implode;
@ -273,6 +274,7 @@ class MemoryManager{
}
$cycles = gc_collect_cycles();
gc_mem_caches();
Timings::$garbageCollectorTimer->stopTiming();

View File

@ -1601,10 +1601,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$dy = $newPos->y - $this->y;
$dz = $newPos->z - $this->z;
//the client likes to clip into blocks like stairs, but we do full server-side prediction of that without
//help from the client's position changes, so we deduct the expected clip height from the moved distance.
$expectedClipDistance = $this->ySize * (1 - self::STEP_CLIP_MULTIPLIER);
$dy -= $expectedClipDistance;
$this->move($dx, $dy, $dz);
$diff = $this->distanceSquared($newPos);
//TODO: Explore lowering this threshold now that stairs work properly.
if($this->isSurvival() and $diff > 0.0625){
$ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ));
$ev->setCancelled($this->allowMovementCheats);
@ -1614,7 +1619,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$ev->isCancelled()){
$revert = true;
$this->server->getLogger()->debug($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()]));
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos);
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos . ", expected clip distance: $expectedClipDistance");
}
}
@ -2881,7 +2886,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$target = $this->level->getBlock($pos);
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $packet->face, PlayerInteractEvent::LEFT_CLICK_BLOCK);
if($this->level->checkSpawnProtection($this, $target)){
if($this->isSpectator() || $this->level->checkSpawnProtection($this, $target)){
$ev->setCancelled();
}
@ -3903,10 +3908,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($targets !== null){
if(in_array($this, $targets, true)){
$this->forceMoveSync = $pos->asVector3();
$this->ySize = 0;
}
$this->server->broadcastPacket($targets, $pk);
}else{
$this->forceMoveSync = $pos->asVector3();
$this->ySize = 0;
$this->dataPacket($pk);
}
}
@ -4115,7 +4122,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function onChunkChanged(Chunk $chunk){
if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
$hasSent = $this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())] ?? false;
if($hasSent){
$this->usedChunks[$hash] = false;
$this->nextChunkOrderRun = 0;
}

View File

@ -1400,10 +1400,10 @@ class Server{
Network::$BATCH_THRESHOLD = -1;
}
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 7);
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 6);
if($this->networkCompressionLevel < 1 or $this->networkCompressionLevel > 9){
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 7");
$this->networkCompressionLevel = 7;
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 6");
$this->networkCompressionLevel = 6;
}
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
@ -1543,7 +1543,7 @@ class Server{
if(isset($options["generator"])){
$generatorOptions = explode(":", $options["generator"]);
$generator = GeneratorManager::getGenerator(array_shift($generatorOptions));
if(count($options) > 0){
if(count($generatorOptions) > 0){
$options["preset"] = implode(":", $generatorOptions);
}
}else{

View File

@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.15.0";
const BASE_VERSION = "3.15.3";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;

View File

@ -40,9 +40,13 @@ class Potato extends Crops{
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
ItemFactory::get(Item::POTATO, 0, $this->getDamage() >= 0x07 ? mt_rand(1, 4) : 1)
$result = [
ItemFactory::get(Item::POTATO, 0, $this->getDamage() >= 0x07 ? mt_rand(1, 5) : 1)
];
if($this->getDamage() >= 7 && mt_rand(0, 49) === 0){
$result[] = ItemFactory::get(Item::POISONOUS_POTATO);
}
return $result;
}
public function getPickedItem() : Item{

View File

@ -87,7 +87,12 @@ class Sugarcane extends Flowable{
for($y = 1; $y < 3; ++$y){
$b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z);
if($b->getId() === self::AIR){
$this->getLevelNonNull()->setBlock($b, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK));
$ev->call();
if($ev->isCancelled()){
break;
}
$this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true);
break;
}
}

View File

@ -54,9 +54,9 @@ class StatusCommand extends VanillaCommand{
$server = $sender->getServer();
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
$time = microtime(true) - \pocketmine\START_TIME;
$time = (int) (microtime(true) - \pocketmine\START_TIME);
$seconds = floor($time % 60);
$seconds = $time % 60;
$minutes = null;
$hours = null;
$days = null;
@ -100,7 +100,6 @@ class StatusCommand extends VanillaCommand{
$sender->sendMessage(TextFormat::GOLD . "Total memory: " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2), 2) . " MB.");
$sender->sendMessage(TextFormat::GOLD . "Total virtual memory: " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2), 2) . " MB.");
$sender->sendMessage(TextFormat::GOLD . "Heap memory: " . TextFormat::RED . number_format(round(($rUsage[0] / 1024) / 1024, 2), 2) . " MB.");
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (system): " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2), 2) . " MB.");
if($server->getProperty("memory.global-limit") > 0){
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (manager): " . TextFormat::RED . number_format(round($server->getProperty("memory.global-limit"), 2), 2) . " MB.");

View File

@ -80,6 +80,7 @@ use function count;
use function current;
use function deg2rad;
use function floor;
use function fmod;
use function get_class;
use function in_array;
use function is_a;
@ -94,6 +95,7 @@ use const M_PI_2;
abstract class Entity extends Location implements Metadatable, EntityIds{
public const MOTION_THRESHOLD = 0.00001;
protected const STEP_CLIP_MULTIPLIER = 0.4;
public const NETWORK_ID = -1;
@ -707,10 +709,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->boundingBox->setBounds(
$this->x - $halfWidth,
$this->y,
$this->y + $this->ySize,
$this->z - $halfWidth,
$this->x + $halfWidth,
$this->y + $this->height,
$this->y + $this->height + $this->ySize,
$this->z + $halfWidth
);
}
@ -1311,7 +1313,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
public function getDirection() : ?int{
$rotation = ($this->yaw - 90) % 360;
$rotation = fmod($this->yaw - 90, 360);
if($rotation < 0){
$rotation += 360.0;
}
@ -1449,8 +1451,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->fall($this->fallDistance);
$this->resetFallDistance();
}
}elseif($distanceThisTick < 0){
}elseif($distanceThisTick < $this->fallDistance){
//we've fallen some distance (distanceThisTick is negative)
//or we ascended back towards where fall distance was measured from initially (distanceThisTick is positive but less than existing fallDistance)
$this->fallDistance -= $distanceThisTick;
}else{
//we ascended past the apex where fall distance was originally being measured from
//reset it so it will be measured starting from the new, higher position
$this->fallDistance = 0;
}
}
@ -1538,7 +1546,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($this->keepMovement){
$this->boundingBox->offset($dx, $dy, $dz);
}else{
$this->ySize *= 0.4;
$this->ySize *= self::STEP_CLIP_MULTIPLIER;
/*
if($this->isColliding){ //With cobweb?
@ -1605,7 +1613,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->boundingBox->offset(0, 0, $dz);
if($this->stepHeight > 0 and $fallingFlag and $this->ySize < 0.05 and ($movX != $dx or $movZ != $dz)){
if($this->stepHeight > 0 and $fallingFlag and ($movX != $dx or $movZ != $dz)){
$cx = $dx;
$cy = $dy;
$cz = $dz;
@ -1637,13 +1645,20 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->boundingBox->offset(0, 0, $dz);
$reverseDY = -$dy;
foreach($list as $bb){
$reverseDY = $bb->calculateYOffset($this->boundingBox, $reverseDY);
}
$dy += $reverseDY;
$this->boundingBox->offset(0, $reverseDY, 0);
if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){
$dx = $cx;
$dy = $cy;
$dz = $cz;
$this->boundingBox->setBB($axisalignedbb1);
}else{
$this->ySize += 0.5; //FIXME: this should be the height of the block it walked up, not fixed 0.5
$this->ySize += $dy;
}
}
}

View File

@ -400,12 +400,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* @param bool $playSound Whether to play level-up and XP gained sounds.
*/
public function addXp(int $amount, bool $playSound = true) : bool{
$this->totalXp += $amount;
$oldLevel = $this->getXpLevel();
$oldTotal = $this->getCurrentTotalXp();
if($this->setCurrentTotalXp($oldTotal + $amount)){
if($amount > 0){
$this->totalXp += $amount;
}
if($playSound){
$newLevel = $this->getXpLevel();
if((int) ($newLevel / 5) > (int) ($oldLevel / 5)){

View File

@ -45,6 +45,7 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
@ -97,17 +98,15 @@ abstract class Living extends Entity implements Damageable{
//TODO: load/save armor inventory contents
$this->armorInventory->setEventProcessor(new ArmorInventoryEventProcessor($this));
$health = $this->getMaxHealth();
if($this->namedtag->hasTag("HealF", FloatTag::class)){
$health = $this->namedtag->getFloat("HealF");
$this->namedtag->removeTag("HealF");
}elseif($this->namedtag->hasTag("Health")){
$healthTag = $this->namedtag->getTag("Health");
$health = (float) $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
if(!($healthTag instanceof FloatTag)){
$this->namedtag->removeTag("Health");
}
}elseif($this->namedtag->hasTag("Health", ShortTag::class)){
//Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
$health = $this->namedtag->getShort("Health");
$this->namedtag->removeTag("Health");
}else{
$health = $this->namedtag->getFloat("Health", $this->getMaxHealth());
}
$this->setHealth($health);

View File

@ -24,6 +24,8 @@ declare(strict_types=1);
namespace pocketmine\entity\utils;
use pocketmine\math\Math;
use pocketmine\utils\AssumptionFailedError;
use function count;
use function max;
abstract class ExperienceUtils{
@ -59,6 +61,9 @@ abstract class ExperienceUtils{
* This returns a floating-point number, the decimal part being the progress through the resulting level.
*/
public static function getLevelFromXp(int $xp) : float{
if($xp < 0){
throw new \InvalidArgumentException("XP must be at least 0");
}
if($xp <= self::getXpToReachLevel(16)){
$a = 1;
$b = 6;
@ -74,6 +79,9 @@ abstract class ExperienceUtils{
}
$x = Math::solveQuadratic($a, $b, $c - $xp);
if(count($x) === 0){
throw new AssumptionFailedError("Expected at least 1 solution");
}
return max($x); //we're only interested in the positive solution
}

View File

@ -28,6 +28,9 @@ use pocketmine\event\Cancellable;
use pocketmine\item\Item;
use pocketmine\tile\Furnace;
/**
* Called when a furnace is about to consume a new fuel item.
*/
class FurnaceBurnEvent extends BlockEvent implements Cancellable{
/** @var Furnace */
private $furnace;
@ -53,18 +56,31 @@ class FurnaceBurnEvent extends BlockEvent implements Cancellable{
return $this->fuel;
}
/**
* Returns the number of ticks that the furnace will be powered for.
*/
public function getBurnTime() : int{
return $this->burnTime;
}
/**
* Sets the number of ticks that the given fuel will power the furnace for.
*/
public function setBurnTime(int $burnTime) : void{
$this->burnTime = $burnTime;
}
/**
* Returns whether the fuel item will be consumed.
*/
public function isBurning() : bool{
return $this->burning;
}
/**
* Sets whether the fuel will be consumed. If false, the furnace will smelt as if it consumed fuel, but no fuel
* will be deducted.
*/
public function setBurning(bool $burning) : void{
$this->burning = $burning;
}

View File

@ -252,11 +252,11 @@ abstract class BaseInventory implements Inventory{
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
$slot = $this->getItem($i);
if($item->equals($slot)){
if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){
if(($diff = min($slot->getMaxStackSize(), $item->getMaxStackSize()) - $slot->getCount()) > 0){
$count -= $diff;
}
}elseif($slot->isNull()){
$count -= $this->getMaxStackSize();
$count -= min($this->getMaxStackSize(), $item->getMaxStackSize());
}
if($count <= 0){

View File

@ -108,9 +108,6 @@ class CraftingTransaction extends InventoryTransaction{
}
}
if($iterations < 1){
throw new TransactionValidationException("Tried to craft zero times");
}
if(count($txItems) > 0){
//all items should be destroyed in this process
throw new TransactionValidationException("Expected 0 ingredients left over, have " . count($txItems));

View File

@ -42,6 +42,7 @@ use pocketmine\nbt\tag\NamedTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Binary;
use function array_map;
use function base64_decode;
@ -475,7 +476,12 @@ class Item implements ItemIds, \JsonSerializable{
public function getLore() : array{
$display = $this->getNamedTagEntry(self::TAG_DISPLAY);
if($display instanceof CompoundTag and ($lore = $display->getListTag(self::TAG_DISPLAY_LORE)) !== null){
return $lore->getAllValues();
return array_map(function(NamedTag $line) : string{
if(!($line instanceof StringTag)){
throw new AssumptionFailedError("Nobody bothered to handle this error case and we can't fix it until PM4, oops ... #blameshoghi");
}
return $line->getValue();
}, $lore->getValue());
}
return [];

View File

@ -154,7 +154,6 @@ class Explosion{
* and creating sounds and particles.
*/
public function explodeB() : bool{
$send = [];
$updateBlocks = [];
$source = (new Vector3($this->source->x, $this->source->y, $this->source->z))->floor();
@ -232,7 +231,9 @@ class Explosion{
$t->close();
}
}
foreach($this->affectedBlocks as $block){
$pos = new Vector3($block->x, $block->y, $block->z);
for($side = 0; $side <= 5; $side++){
@ -252,7 +253,6 @@ class Explosion{
$updateBlocks[$index] = true;
}
}
$send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z);
}
$this->level->addParticle(new HugeExplodeSeedParticle($source));

View File

@ -53,7 +53,6 @@ use pocketmine\level\format\io\ChunkRequestTask;
use pocketmine\level\format\io\exception\CorruptedChunkException;
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
use pocketmine\level\format\io\LevelProvider;
use pocketmine\level\generator\Generator;
use pocketmine\level\generator\GeneratorManager;
use pocketmine\level\generator\GeneratorRegisterTask;
use pocketmine\level\generator\GeneratorUnregisterTask;
@ -289,7 +288,10 @@ class Level implements ChunkManager, Metadatable{
/** @var bool */
private $doingTick = false;
/** @var string|Generator */
/**
* @var string
* @phpstan-var class-string<\pocketmine\level\generator\Generator>
*/
private $generator;
/** @var bool */

View File

@ -80,15 +80,14 @@ class LevelTimings{
$this->entityTick = new TimingsHandler("** " . $name . "entityTick");
$this->tileEntityTick = new TimingsHandler("** " . $name . "tileEntityTick");
$this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend");
$this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare");
Timings::init(); //make sure the timers we want are available
$this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend", Timings::$playerChunkSendTimer);
$this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare", Timings::$playerChunkSendTimer);
$this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad");
$this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad", Timings::$worldLoadTimer);
$this->syncChunkLoadDataTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Data");
$this->syncChunkLoadEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Entities");
$this->syncChunkLoadTileEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - TileEntities");
Timings::init(); //make sure the timer we want is available
$this->syncChunkSaveTimer = new TimingsHandler("** " . $name . "syncChunkSave", Timings::$worldSaveTimer);
$this->doTick = new TimingsHandler($name . "doTick");

View File

@ -30,6 +30,7 @@ use pocketmine\block\BlockFactory;
use pocketmine\entity\Entity;
use pocketmine\level\Level;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\tile\Spawnable;
@ -691,13 +692,14 @@ class Chunk{
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
foreach($this->NBTentities as $nbt){
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
$idTag = $nbt->getTag("id");
if(!($idTag instanceof IntTag) && !($idTag instanceof StringTag)){ //allow mixed types (because of leveldb)
$changed = true;
continue;
}
try{
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
$entity = Entity::createEntity($idTag->getValue(), $level, $nbt);
if(!($entity instanceof Entity)){
$changed = true;
continue;
@ -927,6 +929,7 @@ class Chunk{
$chunk->setGenerated($terrainGenerated);
$chunk->setPopulated($terrainPopulated);
$chunk->setLightPopulated($lightPopulated);
$chunk->setChanged(false);
return $chunk;
}

View File

@ -38,6 +38,8 @@ if(!defined(__NAMESPACE__ . '\ZERO_NIBBLE_ARRAY')){
}
class SubChunk implements SubChunkInterface{
private const ZERO_NIBBLE_ARRAY = ZERO_NIBBLE_ARRAY;
/** @var string */
protected $ids;
/** @var string */
@ -68,7 +70,7 @@ class SubChunk implements SubChunkInterface{
substr_count($this->ids, "\x00") === 4096 and
(!$checkLight or (
substr_count($this->skyLight, "\xff") === 2048 and
$this->blockLight === ZERO_NIBBLE_ARRAY
$this->blockLight === self::ZERO_NIBBLE_ARRAY
))
);
}
@ -230,14 +232,14 @@ class SubChunk implements SubChunkInterface{
* reference to the const instead of duplicating the whole string. The string will only be duplicated when
* modified, which is perfect for this purpose.
*/
if($this->data === ZERO_NIBBLE_ARRAY){
$this->data = ZERO_NIBBLE_ARRAY;
if($this->data === self::ZERO_NIBBLE_ARRAY){
$this->data = self::ZERO_NIBBLE_ARRAY;
}
if($this->skyLight === ZERO_NIBBLE_ARRAY){
$this->skyLight = ZERO_NIBBLE_ARRAY;
if($this->skyLight === self::ZERO_NIBBLE_ARRAY){
$this->skyLight = self::ZERO_NIBBLE_ARRAY;
}
if($this->blockLight === ZERO_NIBBLE_ARRAY){
$this->blockLight = ZERO_NIBBLE_ARRAY;
if($this->blockLight === self::ZERO_NIBBLE_ARRAY){
$this->blockLight = self::ZERO_NIBBLE_ARRAY;
}
}
}

View File

@ -131,7 +131,11 @@ class LevelDB extends BaseLevelProvider{
throw new LevelException("Truncated level.dat");
}
$nbt = new LittleEndianNBTStream();
$levelData = $nbt->read(substr($rawLevelData, 8));
try{
$levelData = $nbt->read(substr($rawLevelData, 8));
}catch(\UnexpectedValueException $e){
throw new LevelException("Invalid level.dat (" . $e->getMessage() . ")", 0, $e);
}
if($levelData instanceof CompoundTag){
$this->levelData = $levelData;
}else{

View File

@ -245,11 +245,13 @@ class McRegion extends BaseLevelProvider{
if($isValid){
$files = array_filter(scandir($path . "/region/", SCANDIR_SORT_NONE), function(string $file) : bool{
return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file
$extPos = strrpos($file, ".");
return $extPos !== false && substr($file, $extPos + 1, 2) === "mc"; //region file
});
foreach($files as $f){
if(substr($f, strrpos($f, ".") + 1) !== static::REGION_FILE_EXTENSION){
$extPos = strrpos($f, ".");
if($extPos !== false && substr($f, $extPos + 1) !== static::REGION_FILE_EXTENSION){
$isValid = false;
break;
}

View File

@ -69,11 +69,14 @@ final class RegionGarbageMap{
/** @var RegionLocationTableEntry|null $prevEntry */
$prevEntry = null;
foreach($usedMap as $firstSector => $entry){
$expectedStart = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
$actualStart = $entry->getFirstSector();
if($expectedStart < $actualStart){
$prevEndPlusOne = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
$currentStart = $entry->getFirstSector();
if($prevEndPlusOne < $currentStart){
//found a gap in the table
$garbageMap[$expectedStart] = new RegionLocationTableEntry($expectedStart, $actualStart - $expectedStart, 0);
$garbageMap[$prevEndPlusOne] = new RegionLocationTableEntry($prevEndPlusOne, $currentStart - $prevEndPlusOne, 0);
}elseif($prevEndPlusOne > $currentStart){
//current entry starts inside the previous. This would be a bug since RegionLoader should prevent this
throw new AssumptionFailedError("Overlapping entries detected");
}
$prevEntry = $entry;
}

View File

@ -303,6 +303,7 @@ class RegionLoader{
throw new CorruptedRegionException("Corrupted region header (unexpected end of file)");
}
/** @var int[] $data */
$data = unpack("N*", $headerRaw);
for($i = 0; $i < 1024; ++$i){

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\level\generator;
use pocketmine\block\BlockFactory;
use pocketmine\item\ItemFactory;
use pocketmine\level\biome\Biome;
use pocketmine\level\Level;
use pocketmine\level\SimpleChunkManager;
@ -34,7 +35,10 @@ use function unserialize;
class GeneratorRegisterTask extends AsyncTask{
/** @var string */
/**
* @var string
* @phpstan-var class-string<Generator>
*/
public $generatorClass;
/** @var string */
public $settings;
@ -47,6 +51,7 @@ class GeneratorRegisterTask extends AsyncTask{
/**
* @param mixed[] $generatorSettings
* @phpstan-param class-string<Generator> $generatorClass
* @phpstan-param array<string, mixed> $generatorSettings
*/
public function __construct(Level $level, string $generatorClass, array $generatorSettings = []){
@ -59,6 +64,7 @@ class GeneratorRegisterTask extends AsyncTask{
public function onRun(){
BlockFactory::init();
ItemFactory::init();
Biome::init();
$manager = new SimpleChunkManager($this->seed, $this->worldHeight);
$this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager);

View File

@ -58,7 +58,7 @@ class PluginDescription{
private $compatibleOperatingSystems = [];
/**
* @var string[][]
* @phpstan-var array<string, list<mixed>>
* @phpstan-var array<string, list<string>>
*/
private $extensions = [];
/** @var string[] */
@ -104,13 +104,13 @@ class PluginDescription{
$this->name = $plugin["name"];
if(preg_match('/^[A-Za-z0-9 _.-]+$/', $this->name) === 0){
throw new PluginException("Invalid PluginDescription name");
throw new PluginException("Invalid Plugin name");
}
$this->name = str_replace(" ", "_", $this->name);
$this->version = (string) $plugin["version"];
$this->main = $plugin["main"];
if(stripos($this->main, "pocketmine\\") === 0){
throw new PluginException("Invalid PluginDescription main, cannot start within the PocketMine namespace");
throw new PluginException("Invalid Plugin main, cannot start within the PocketMine namespace");
}
$this->api = array_map("\strval", (array) ($plugin["api"] ?? []));
@ -132,7 +132,7 @@ class PluginDescription{
$k = $v;
$v = "*";
}
$this->extensions[$k] = is_array($v) ? $v : [$v];
$this->extensions[$k] = array_map('strval', is_array($v) ? $v : [$v]);
}
}
@ -149,7 +149,7 @@ class PluginDescription{
if(isset($plugin["load"])){
$order = mb_strtoupper($plugin["load"]);
if(!defined(PluginLoadOrder::class . "::" . $order)){
throw new PluginException("Invalid PluginDescription load");
throw new PluginException("Invalid Plugin load");
}else{
$this->order = constant(PluginLoadOrder::class . "::" . $order);
}

View File

@ -84,7 +84,7 @@ network:
#Set to 0 to compress everything, -1 to disable.
batch-threshold: 256
#Compression level used when sending batched packets. Higher = more CPU, less bandwidth usage
compression-level: 7
compression-level: 6
#Use AsyncTasks for compression. Adds half/one tick delay, less CPU load on main thread
async-compression: false
#Experimental, only for Windows. Tries to use UPnP to automatically port forward

View File

@ -329,6 +329,16 @@ class AsyncPool{
$this->collectWorkers();
}
/**
* Returns an array of worker ID => task queue size
*
* @return int[]
* @phpstan-return array<int, int>
*/
public function getTaskQueueSizes() : array{
return $this->workerUsage;
}
public function shutdownUnusedWorkers() : int{
$ret = 0;
$time = time();

View File

@ -25,11 +25,13 @@ namespace pocketmine\scheduler;
use function gc_collect_cycles;
use function gc_enable;
use function gc_mem_caches;
class GarbageCollectionTask extends AsyncTask{
public function onRun(){
gc_enable();
gc_collect_cycles();
gc_mem_caches();
}
}

View File

@ -59,6 +59,8 @@ abstract class Timings{
/** @var TimingsHandler */
public static $serverCommandTimer;
/** @var TimingsHandler */
public static $worldLoadTimer;
/** @var TimingsHandler */
public static $worldSaveTimer;
/** @var TimingsHandler */
public static $populationTimer;
@ -126,6 +128,7 @@ abstract class Timings{
self::$connectionTimer = new TimingsHandler("Connection Handler");
self::$schedulerTimer = new TimingsHandler("Scheduler");
self::$serverCommandTimer = new TimingsHandler("Server Command");
self::$worldLoadTimer = new TimingsHandler("World Load");
self::$worldSaveTimer = new TimingsHandler("World Save");
self::$populationTimer = new TimingsHandler("World Population");
self::$generationCallbackTimer = new TimingsHandler("World Generation Callback");

View File

@ -141,8 +141,8 @@ abstract class TextFormat{
if(!is_array($string)){
$string = self::tokenize($string);
}
$newString = [];
$pointer =& $newString;
$newString = new TextFormatJsonObject();
$pointer = $newString;
$color = "white";
$bold = false;
$italic = false;
@ -152,165 +152,164 @@ abstract class TextFormat{
$index = 0;
foreach($string as $token){
if(isset($pointer["text"])){
if(!isset($newString["extra"])){
$newString["extra"] = [];
if($pointer->text !== null){
if($newString->extra === null){
$newString->extra = [];
}
$newString["extra"][$index] = [];
$pointer =& $newString["extra"][$index];
$newString->extra[$index] = $pointer = new TextFormatJsonObject();
if($color !== "white"){
$pointer["color"] = $color;
$pointer->color = $color;
}
if($bold){
$pointer["bold"] = true;
$pointer->bold = true;
}
if($italic){
$pointer["italic"] = true;
$pointer->italic = true;
}
if($underlined){
$pointer["underlined"] = true;
$pointer->underlined = true;
}
if($strikethrough){
$pointer["strikethrough"] = true;
$pointer->strikethrough = true;
}
if($obfuscated){
$pointer["obfuscated"] = true;
$pointer->obfuscated = true;
}
++$index;
}
switch($token){
case TextFormat::BOLD:
if(!$bold){
$pointer["bold"] = true;
$pointer->bold = true;
$bold = true;
}
break;
case TextFormat::OBFUSCATED:
if(!$obfuscated){
$pointer["obfuscated"] = true;
$pointer->obfuscated = true;
$obfuscated = true;
}
break;
case TextFormat::ITALIC:
if(!$italic){
$pointer["italic"] = true;
$pointer->italic = true;
$italic = true;
}
break;
case TextFormat::UNDERLINE:
if(!$underlined){
$pointer["underlined"] = true;
$pointer->underlined = true;
$underlined = true;
}
break;
case TextFormat::STRIKETHROUGH:
if(!$strikethrough){
$pointer["strikethrough"] = true;
$pointer->strikethrough = true;
$strikethrough = true;
}
break;
case TextFormat::RESET:
if($color !== "white"){
$pointer["color"] = "white";
$pointer->color = "white";
$color = "white";
}
if($bold){
$pointer["bold"] = false;
$pointer->bold = false;
$bold = false;
}
if($italic){
$pointer["italic"] = false;
$pointer->italic = false;
$italic = false;
}
if($underlined){
$pointer["underlined"] = false;
$pointer->underlined = false;
$underlined = false;
}
if($strikethrough){
$pointer["strikethrough"] = false;
$pointer->strikethrough = false;
$strikethrough = false;
}
if($obfuscated){
$pointer["obfuscated"] = false;
$pointer->obfuscated = false;
$obfuscated = false;
}
break;
//Colors
case TextFormat::BLACK:
$pointer["color"] = "black";
$pointer->color = "black";
$color = "black";
break;
case TextFormat::DARK_BLUE:
$pointer["color"] = "dark_blue";
$pointer->color = "dark_blue";
$color = "dark_blue";
break;
case TextFormat::DARK_GREEN:
$pointer["color"] = "dark_green";
$pointer->color = "dark_green";
$color = "dark_green";
break;
case TextFormat::DARK_AQUA:
$pointer["color"] = "dark_aqua";
$pointer->color = "dark_aqua";
$color = "dark_aqua";
break;
case TextFormat::DARK_RED:
$pointer["color"] = "dark_red";
$pointer->color = "dark_red";
$color = "dark_red";
break;
case TextFormat::DARK_PURPLE:
$pointer["color"] = "dark_purple";
$pointer->color = "dark_purple";
$color = "dark_purple";
break;
case TextFormat::GOLD:
$pointer["color"] = "gold";
$pointer->color = "gold";
$color = "gold";
break;
case TextFormat::GRAY:
$pointer["color"] = "gray";
$pointer->color = "gray";
$color = "gray";
break;
case TextFormat::DARK_GRAY:
$pointer["color"] = "dark_gray";
$pointer->color = "dark_gray";
$color = "dark_gray";
break;
case TextFormat::BLUE:
$pointer["color"] = "blue";
$pointer->color = "blue";
$color = "blue";
break;
case TextFormat::GREEN:
$pointer["color"] = "green";
$pointer->color = "green";
$color = "green";
break;
case TextFormat::AQUA:
$pointer["color"] = "aqua";
$pointer->color = "aqua";
$color = "aqua";
break;
case TextFormat::RED:
$pointer["color"] = "red";
$pointer->color = "red";
$color = "red";
break;
case TextFormat::LIGHT_PURPLE:
$pointer["color"] = "light_purple";
$pointer->color = "light_purple";
$color = "light_purple";
break;
case TextFormat::YELLOW:
$pointer["color"] = "yellow";
$pointer->color = "yellow";
$color = "yellow";
break;
case TextFormat::WHITE:
$pointer["color"] = "white";
$pointer->color = "white";
$color = "white";
break;
default:
$pointer["text"] = $token;
$pointer->text = $token;
break;
}
}
if(isset($newString["extra"])){
foreach($newString["extra"] as $k => $d){
if(!isset($d["text"])){
unset($newString["extra"][$k]);
if($newString->extra !== null){
foreach($newString->extra as $k => $d){
if($d->text === null){
unset($newString->extra[$k]);
}
}
}

View File

@ -0,0 +1,60 @@
<?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\utils;
/**
* @internal
* @see TextFormat::toJSON()
*/
final class TextFormatJsonObject implements \JsonSerializable{
/** @var string|null */
public $text = null;
/** @var string|null */
public $color = null;
/** @var bool|null */
public $bold = null;
/** @var bool|null */
public $italic = null;
/** @var bool|null */
public $underlined = null;
/** @var bool|null */
public $strikethrough = null;
/** @var bool|null */
public $obfuscated = null;
/**
* @var TextFormatJsonObject[]|null
* @phpstan-var array<int, TextFormatJsonObject>|null
*/
public $extra = null;
public function jsonSerialize(){
$result = (array) $this;
foreach($result as $k => $v){
if($v === null){
unset($result[$k]);
}
}
return $result;
}
}

View File

@ -88,7 +88,7 @@ abstract class Timezone{
break;
}
if($response = Internet::getURL("http://ip-api.com/json") //If system timezone detection fails or timezone is an invalid value.
if(($response = Internet::getURL("http://ip-api.com/json")) !== false //If system timezone detection fails or timezone is an invalid value.
and $ip_geolocation_data = json_decode($response, true)
and $ip_geolocation_data['status'] !== 'fail'
and date_default_timezone_set($ip_geolocation_data['timezone'])

View File

@ -527,7 +527,7 @@ class Utils{
$ret = explode("\n", $contents);
ob_end_clean();
if(count($ret) >= 1 and preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
if(preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
return ((int) $m[1]) - ($includeCurrent ? 3 : 4); //$value + zval call + extra call
}
return -1;

View File

@ -92,6 +92,7 @@ class SetupWizard{
$config->save();
if(strtolower($this->getInput($this->lang->get("skip_installer"), "n", "y/N")) === "y"){
$this->printIpDetails();
return true;
}
@ -101,6 +102,7 @@ class SetupWizard{
$this->generateUserFiles();
$this->networkFunctions();
$this->printIpDetails();
$this->endWizard();
@ -218,7 +220,9 @@ LICENSE;
}
$config->save();
}
private function printIpDetails() : void{
$this->message($this->lang->get("ip_get"));
$externalIP = Internet::getIP();

View File

@ -23,6 +23,11 @@ declare(strict_types=1);
define('pocketmine\_PHPSTAN_ANALYSIS', true);
if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
//leveldb might not be loaded
define('LEVELDB_ZLIB_RAW_COMPRESSION', 4);
}
//TODO: these need to be defined properly or removed
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 2) . '/vendor/autoload.php');
define('pocketmine\DATA', '');

View File

@ -530,21 +530,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/plugin/PluginDescription.php
-
message: "#^Method pocketmine\\\\plugin\\\\PluginDescription\\:\\:getRequiredExtensions\\(\\) should return array\\<string, array\\<int, string\\>\\> but returns array\\<string, array\\<int, mixed\\>\\>\\.$#"
count: 1
path: ../../../src/pocketmine/plugin/PluginDescription.php
-
message: "#^Parameter \\#1 \\$str of function substr expects string, mixed given\\.$#"
count: 2
path: ../../../src/pocketmine/plugin/PluginDescription.php
-
message: "#^Part \\$constr \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 2
path: ../../../src/pocketmine/plugin/PluginDescription.php
-
message: "#^Parameter \\#1 \\$description of method pocketmine\\\\command\\\\Command\\:\\:setDescription\\(\\) expects string, mixed given\\.$#"
count: 1
@ -560,11 +545,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/plugin/PluginManager.php
-
message: "#^Part \\$openResult \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: ../../../src/pocketmine/resourcepacks/ZippedResourcePack.php
-
message: "#^Parameter \\#2 \\$code of class pocketmine\\\\resourcepacks\\\\ResourcePackException constructor expects int, mixed given\\.$#"
count: 1

View File

@ -1,12 +0,0 @@
parameters:
ignoreErrors:
#TODO: use custom stubs
-
message: "#^Used constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#"
count: 1
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
-
message: "#^Constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#"
count: 1
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php

View File

@ -355,21 +355,11 @@ parameters:
count: 1
path: ../../../src/pocketmine/command/CommandReader.php
-
message: "#^Parameter \\#2 \\$params of class pocketmine\\\\lang\\\\TranslationContainer constructor expects array\\<float\\|int\\|string\\>, array\\<int, string\\|null\\> given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanCommand.php
-
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
-
message: "#^Parameter \\#2 \\$params of class pocketmine\\\\lang\\\\TranslationContainer constructor expects array\\<float\\|int\\|string\\>, array\\<int, string\\|null\\> given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
-
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
count: 1
@ -575,11 +565,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/level/Explosion.php
-
message: "#^Parameter \\#2 \\$generatorClass of class pocketmine\\\\level\\\\generator\\\\GeneratorRegisterTask constructor expects string, pocketmine\\\\level\\\\generator\\\\Generator\\|string given\\.$#"
count: 1
path: ../../../src/pocketmine/level/Level.php
-
message: "#^Cannot access offset 'priority' on array\\('priority' \\=\\> int, 'data' \\=\\> pocketmine\\\\math\\\\Vector3\\)\\|int\\|pocketmine\\\\math\\\\Vector3\\.$#"
count: 1
@ -751,12 +736,7 @@ parameters:
path: ../../../src/pocketmine/level/format/io/region/McRegion.php
-
message: "#^Only numeric types are allowed in \\+, int\\|false given on the left side\\.$#"
count: 2
path: ../../../src/pocketmine/level/format/io/region/McRegion.php
-
message: "#^Only numeric types are allowed in %, int\\|false given on the left side\\.$#"
message: "#^Only numeric types are allowed in %%, int\\|false given on the left side\\.$#"
count: 1
path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php

View File

@ -240,11 +240,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/Server.php
-
message: "#^Parameter \\#1 \\$name of static method pocketmine\\\\level\\\\generator\\\\GeneratorManager\\:\\:getGenerator\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/Server.php
-
message: "#^Parameter \\#1 \\$uuid of method pocketmine\\\\Server\\:\\:updatePlayerListData\\(\\) expects pocketmine\\\\utils\\\\UUID, pocketmine\\\\utils\\\\UUID\\|null given\\.$#"
count: 1
@ -535,46 +530,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/command/SimpleCommandMap.php
-
message: "#^Parameter \\#1 \\$target of method pocketmine\\\\permission\\\\BanList\\:\\:addBan\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanCommand.php
-
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayerExact\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanCommand.php
-
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
-
message: "#^Parameter \\#1 \\$ip of method pocketmine\\\\command\\\\defaults\\\\BanIpCommand\\:\\:processIPBan\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
-
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
-
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getOfflinePlayer\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/DeopCommand.php
-
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/KickCommand.php
-
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getOfflinePlayer\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/OpCommand.php
-
message: "#^Cannot call method addParticle\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#"
count: 1
@ -590,11 +545,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/command/defaults/SetWorldSpawnCommand.php
-
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/command/defaults/TellCommand.php
-
message: "#^Cannot call method getTime\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#"
count: 1
@ -775,11 +725,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/entity/Human.php
-
message: "#^Cannot call method getValue\\(\\) on pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#"
count: 1
path: ../../../src/pocketmine/entity/Living.php
-
message: "#^Parameter \\#1 \\$attribute of method pocketmine\\\\entity\\\\AttributeMap\\:\\:addAttribute\\(\\) expects pocketmine\\\\entity\\\\Attribute, pocketmine\\\\entity\\\\Attribute\\|null given\\.$#"
count: 6
@ -1225,11 +1170,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/level/format/Chunk.php
-
message: "#^Cannot call method getValue\\(\\) on pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#"
count: 1
path: ../../../src/pocketmine/level/format/Chunk.php
-
message: "#^Method pocketmine\\\\level\\\\format\\\\Chunk\\:\\:getSubChunk\\(\\) should return pocketmine\\\\level\\\\format\\\\SubChunkInterface but returns pocketmine\\\\level\\\\format\\\\SubChunkInterface\\|null\\.$#"
count: 1
@ -1550,11 +1490,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/network/rcon/RCONInstance.php
-
message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/permission/BanEntry.php
-
message: "#^Method pocketmine\\\\permission\\\\DefaultPermissions\\:\\:registerPermission\\(\\) should return pocketmine\\\\permission\\\\Permission but returns pocketmine\\\\permission\\\\Permission\\|null\\.$#"
count: 1
@ -1645,16 +1580,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/tile/Spawnable.php
-
message: "#^Cannot clone non\\-object variable \\$customBlockDataTag of type pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#"
count: 1
path: ../../../src/pocketmine/tile/Tile.php
-
message: "#^Parameter \\#1 \\$tag of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setTag\\(\\) expects pocketmine\\\\nbt\\\\tag\\\\NamedTag, pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/tile/Tile.php
-
message: "#^Cannot call method getBlockAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#"
count: 1
@ -1695,11 +1620,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/utils/Config.php
-
message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#"
count: 1
path: ../../../src/pocketmine/utils/Config.php
-
message: "#^Method pocketmine\\\\utils\\\\MainLogger\\:\\:getLogger\\(\\) should return pocketmine\\\\utils\\\\MainLogger but returns pocketmine\\\\utils\\\\MainLogger\\|null\\.$#"
count: 1
@ -1711,7 +1631,7 @@ parameters:
path: ../../../src/pocketmine/utils/Utils.php
-
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\StupidJsonDecodeTest\\:\\:\\$stupidJsonDecodeFunc \\(Closure\\) does not accept Closure\\|null\\.$#"
message: "#^Cannot call method cancel\\(\\) on pocketmine\\\\scheduler\\\\TaskHandler\\|null\\.$#"
count: 1
path: ../../phpunit/network/mcpe/StupidJsonDecodeTest.php
path: ../../plugins/TesterPlugin/src/pmmp/TesterPlugin/CheckTestCompletionTask.php

View File

@ -5,3 +5,8 @@ parameters:
count: 1
path: ../../phpunit/network/mcpe/StupidJsonDecodeTest.php
-
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\StupidJsonDecodeTest\\:\\:\\$stupidJsonDecodeFunc \\(Closure\\(string, bool\\)\\: mixed\\) does not accept Closure\\|null\\.$#"
count: 1
path: ../../phpunit/network/mcpe/StupidJsonDecodeTest.php

View File

@ -60,11 +60,6 @@ parameters:
count: 2
path: ../../../src/pocketmine/level/Level.php
-
message: "#^Call to function assert\\(\\) with bool will always evaluate to true\\.$#"
count: 1
path: ../../../src/pocketmine/level/Level.php
-
message: "#^Call to function is_subclass_of\\(\\) with class\\-string\\<pocketmine\\\\level\\\\generator\\\\Generator\\> and 'pocketmine\\\\\\\\level\\\\\\\\generator\\\\\\\\Generator' will always evaluate to true\\.$#"
count: 1

View File

@ -110,8 +110,6 @@ class BlockTest extends TestCase{
/**
* @dataProvider blockGetProvider
* @param int $id
* @param int $meta
*/
public function testBlockGet(int $id, int $meta) : void{
$block = BlockFactory::get($id, $meta);

View File

@ -79,9 +79,6 @@ class ItemTest extends TestCase{
/**
* @dataProvider itemFromStringProvider
* @param string $string
* @param int $id
* @param int $meta
*/
public function testFromStringSingle(string $string, int $id, int $meta) : void{
$item = ItemFactory::fromStringSingle($string);

View File

@ -82,8 +82,6 @@ class RegionLoaderTest extends TestCase{
/**
* @dataProvider outOfBoundsCoordsProvider
* @param int $x
* @param int $z
*
* @throws ChunkException
* @throws \InvalidArgumentException
@ -109,8 +107,6 @@ class RegionLoaderTest extends TestCase{
/**
* @dataProvider outOfBoundsCoordsProvider
* @param int $x
* @param int $z
*
* @throws \InvalidArgumentException
* @throws \pocketmine\level\format\io\exception\CorruptedChunkException

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\level\format\io\region;
use PHPUnit\Framework\TestCase;
use function sprintf;
class RegionLocationTableEntryTest extends TestCase{

View File

@ -26,7 +26,10 @@ namespace pocketmine\network\mcpe;
use PHPUnit\Framework\TestCase;
class StupidJsonDecodeTest extends TestCase{
/** @var \Closure */
/**
* @var \Closure
* @phpstan-var \Closure(string $json, bool $assoc=) : mixed
*/
private $stupidJsonDecodeFunc;
public function setUp() : void{
@ -54,7 +57,6 @@ class StupidJsonDecodeTest extends TestCase{
/**
* @dataProvider stupidJsonDecodeProvider
*
* @param string $brokenJson
* @param mixed $expect
*
* @throws \ReflectionException

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\utils;
use PHPUnit\Framework\TestCase;
use function yaml_parse;
class ConfigTest extends TestCase{
@ -61,7 +62,6 @@ class ConfigTest extends TestCase{
/**
* @dataProvider fixYamlIndexesProvider
*
* @param string $test
* @param mixed[] $expected
*/
public function testFixYamlIndexes(string $test, array $expected) : void{

View File

@ -51,7 +51,6 @@ class UtilsTest extends TestCase{
}
/**
* @param string $docComment
* @dataProvider parseDocCommentNewlineProvider
*/
public function testParseDocCommentNewlines(string $docComment) : void{

View File

@ -26,6 +26,7 @@ namespace pmmp\TesterPlugin;
use pocketmine\event\Listener;
use pocketmine\event\server\CommandEvent;
use pocketmine\plugin\PluginBase;
use function array_shift;
class Main extends PluginBase implements Listener{
@ -38,7 +39,7 @@ class Main extends PluginBase implements Listener{
/** @var int */
protected $currentTestNumber = 0;
public function onEnable(){
public function onEnable() : void{
$this->getServer()->getPluginManager()->registerEvents($this, $this);
$this->getScheduler()->scheduleRepeatingTask(new CheckTestCompletionTask($this), 10);
@ -49,7 +50,7 @@ class Main extends PluginBase implements Listener{
];
}
public function onServerCommand(CommandEvent $event){
public function onServerCommand(CommandEvent $event) : void{
//The CI will send this command as a failsafe to prevent the build from hanging if the tester plugin failed to
//run. However, if the plugin loaded successfully we don't want to allow this to stop the server as there may
//be asynchronous tests running. Instead we cancel this and stop the server of our own accord once all tests
@ -59,10 +60,7 @@ class Main extends PluginBase implements Listener{
}
}
/**
* @return Test|null
*/
public function getCurrentTest(){
public function getCurrentTest() : ?Test{
return $this->currentTest;
}
@ -77,7 +75,7 @@ class Main extends PluginBase implements Listener{
return false;
}
public function onTestCompleted(Test $test){
public function onTestCompleted(Test $test) : void{
$message = "Finished test #" . $this->currentTestNumber . " (" . $test->getName() . "): ";
switch($test->getResult()){
case Test::RESULT_OK:
@ -103,7 +101,7 @@ class Main extends PluginBase implements Listener{
$this->currentTest = null;
}
public function onAllTestsCompleted(){
public function onAllTestsCompleted() : void{
$this->getLogger()->notice("All tests finished, stopping the server");
$this->getServer()->shutdown();
}

View File

@ -23,15 +23,21 @@ declare(strict_types=1);
namespace pmmp\TesterPlugin;
use function time;
abstract class Test{
const RESULT_WAITING = -1;
const RESULT_OK = 0;
const RESULT_FAILED = 1;
const RESULT_ERROR = 2;
/** @var Main */
private $plugin;
/** @var int */
private $result = Test::RESULT_WAITING;
/** @var int */
private $startTime;
/** @var int */
private $timeout = 60; //seconds
public function __construct(Main $plugin){
@ -42,7 +48,7 @@ abstract class Test{
return $this->plugin;
}
final public function start(){
final public function start() : void{
$this->startTime = time();
try{
$this->run();
@ -55,11 +61,11 @@ abstract class Test{
}
}
public function tick(){
public function tick() : void{
}
abstract public function run();
abstract public function run() : void;
public function isFinished() : bool{
return $this->result !== Test::RESULT_WAITING;
@ -69,7 +75,7 @@ abstract class Test{
return !$this->isFinished() and time() - $this->timeout > $this->startTime;
}
protected function setTimeout(int $timeout){
protected function setTimeout(int $timeout) : void{
$this->timeout = $timeout;
}
@ -77,7 +83,7 @@ abstract class Test{
return $this->result;
}
public function setResult(int $result){
public function setResult(int $result) : void{
$this->result = $result;
}

View File

@ -26,11 +26,16 @@ namespace pmmp\TesterPlugin\tests;
use pmmp\TesterPlugin\Test;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\MainLogger;
use function ob_end_flush;
use function ob_get_contents;
use function ob_start;
use function strpos;
class AsyncTaskMainLoggerTest extends Test{
public function run(){
public function run() : void{
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
/** @var bool */
@ -43,7 +48,9 @@ class AsyncTaskMainLoggerTest extends Test{
public function onRun(){
ob_start();
MainLogger::getLogger()->info("Testing");
if(strpos(ob_get_contents(), "Testing") !== false){
$contents = ob_get_contents();
if($contents === false) throw new AssumptionFailedError("ob_get_contents() should not return false here");
if(strpos($contents, "Testing") !== false){
$this->success = true;
}
ob_end_flush();
@ -65,5 +72,4 @@ class AsyncTaskMainLoggerTest extends Test{
return "Verifies that the MainLogger is accessible by MainLogger::getLogger() in an AsyncTask";
}
}

View File

@ -25,14 +25,15 @@ namespace pmmp\TesterPlugin\tests;
use pmmp\TesterPlugin\Test;
use pocketmine\scheduler\AsyncTask;
use function usleep;
class AsyncTaskMemoryLeakTest extends Test{
public function run(){
public function run() : void{
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new TestAsyncTask());
}
public function tick(){
public function tick() : void{
if(TestAsyncTask::$destroyed === true){
$this->setResult(Test::RESULT_OK);
}
@ -48,6 +49,7 @@ class AsyncTaskMemoryLeakTest extends Test{
}
class TestAsyncTask extends AsyncTask{
/** @var bool */
public static $destroyed = false;
public function onRun(){

View File

@ -41,6 +41,7 @@ class AsyncTaskPublishProgressRaceTest extends Test{
//this test is racy, but it should fail often enough to be a pest if something is broken
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
/** @var bool */
private static $success = false;
public function __construct(AsyncTaskPublishProgressRaceTest $t){

View File

@ -0,0 +1,18 @@
language: php
php:
- 7.3
- 7.4
before_script:
- phpenv config-rm xdebug.ini
- echo | pecl install channel://pecl.php.net/yaml-2.1.0
- git clone https://github.com/pmmp/pthreads.git
- cd pthreads
- git checkout b81ab29df58fa0fb239a9d5ca1c2380a0d087feb
- phpize
- ./configure
- make
- make install
- cd ..
- echo "extension=pthreads.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- composer self-update --2