mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-12 12:25:09 +00:00
Compare commits
135 Commits
Author | SHA1 | Date | |
---|---|---|---|
6399dacba7 | |||
4d13c48e5c | |||
9159e8f002 | |||
f5aa461945 | |||
16817ff301 | |||
18863b1098 | |||
7b1ae2a822 | |||
8ecf5e02b9 | |||
39c607cbd5 | |||
d867ffc60d | |||
c57eb26fd5 | |||
c35d91a104 | |||
9fc260fb1a | |||
73d0f799c2 | |||
ecb2e6e3af | |||
7b75b6928d | |||
753a8a6937 | |||
bc76b1cafe | |||
00e415fc79 | |||
dbbe1f2d5c | |||
ce27c03774 | |||
c4a8781b5c | |||
dbab8b5733 | |||
2b08bbc7b1 | |||
17037f5e9c | |||
fee3c17148 | |||
25e6cb74b3 | |||
8d2e59222e | |||
cd778661c2 | |||
c2afc05e7c | |||
9be95bf263 | |||
4b65e1cbe1 | |||
5caae37768 | |||
92e1811b06 | |||
293c2710d0 | |||
8a7017fd6b | |||
15f8886958 | |||
3226a9dc6a | |||
1a1e3ff63b | |||
ea413d0882 | |||
0890b5fc99 | |||
163ed225f2 | |||
a4a6d3e094 | |||
ecbf3e9722 | |||
47a959dace | |||
3968f85c82 | |||
6d109bfc6f | |||
42d8357821 | |||
f2ac63d235 | |||
3e4366b30d | |||
314ce1d012 | |||
8d2e3894ef | |||
025cb73bf5 | |||
2a6ffb5aa9 | |||
932418b951 | |||
95812252d6 | |||
cac3c356a5 | |||
c5d3e9be76 | |||
714f4dc023 | |||
a86bcd5110 | |||
7ffc477d76 | |||
4cb0b319c0 | |||
5edff79f5f | |||
0c91d568b4 | |||
35fabc7765 | |||
b5a98a993f | |||
0840ba8067 | |||
274cf58ccf | |||
1bce5d0bc2 | |||
0d5d5e21a8 | |||
a145e18c1e | |||
d1ca779c1a | |||
abbb8bbf55 | |||
86c7870427 | |||
48080b7f90 | |||
b216fb8910 | |||
d3171d6a8e | |||
c063a4da29 | |||
cc79dfa64c | |||
d6b9950901 | |||
1815fe5b46 | |||
3e993250d8 | |||
1163ac1d7a | |||
9a51ba697a | |||
32ad9d0c1a | |||
3d840e969d | |||
d1b70bd400 | |||
29f002b32c | |||
da17ade575 | |||
f0c36f3413 | |||
77d8f133f1 | |||
43ebb23085 | |||
e198c8fa8b | |||
cc3285c8fe | |||
305c63ba4d | |||
acaa0e33b0 | |||
f63857deed | |||
eda3d9b5e4 | |||
c3872619cd | |||
39cc590829 | |||
f347345bb3 | |||
bb05cfb36c | |||
7d5c3c9b46 | |||
cff2d37add | |||
447477c5fb | |||
abdbb2bf0e | |||
783c13926f | |||
45329ddf67 | |||
20af789963 | |||
93cb9390e0 | |||
0aed7f86f5 | |||
bf44bd016d | |||
04e4a36653 | |||
7439e1971d | |||
b961b4e003 | |||
d9eac8fc0a | |||
aeeee5eb53 | |||
13994055d9 | |||
247875e3d5 | |||
ee60a7bc36 | |||
348c2a599b | |||
562b47a1e5 | |||
4e060bc13f | |||
2807f14fcd | |||
f0539f4898 | |||
63d7e7b811 | |||
4da06078ed | |||
8a6381c3fa | |||
d0d61597c7 | |||
7a2a4e2aa3 | |||
e41a2c0792 | |||
11a6e04a28 | |||
70b1ac856d | |||
d724374d1a | |||
a19143cae7 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ server.properties
|
||||
memory_dumps/*
|
||||
resource_packs/
|
||||
server.lock
|
||||
/phpstan.neon
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
|
@ -9,7 +9,7 @@ before_script:
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.0.4
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout 6ca019c58b4fa09ee2ff490f2444e34bef0773d0
|
||||
- git checkout 1b7da492b944146fa9680f6399bd9c6c6c6095e0
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
|
10
README.md
10
README.md
@ -5,25 +5,27 @@
|
||||
|
||||
[](https://travis-ci.org/pmmp/PocketMine-MP)
|
||||
|
||||
### Getting started
|
||||
## Getting started
|
||||
- [Documentation](http://pmmp.readthedocs.org/)
|
||||
- [Installation instructions](https://pmmp.readthedocs.io/en/rtfd/installation.html)
|
||||
- [Docker image](https://hub.docker.com/r/pmmp/pocketmine-mp)
|
||||
- [Plugin repository](https://poggit.pmmp.io/plugins)
|
||||
|
||||
### Discussion
|
||||
## Discussion/Help
|
||||
- [Forums](https://forums.pmmp.io/)
|
||||
- [Community Discord](https://discord.gg/bmSAZBG)
|
||||
- [StackOverflow](https://stackoverflow.com/tags/pocketmine)
|
||||
|
||||
### For developers
|
||||
## For developers
|
||||
* [Latest API documentation](https://jenkins.pmmp.io/job/PocketMine-MP-doc/doxygen/) - Doxygen documentation generated from development
|
||||
* [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - Development tools plugin for creating plugins
|
||||
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
* [Contributing Guidelines](CONTRIBUTING.md)
|
||||
|
||||
### Donate
|
||||
## Donate
|
||||
- Bitcoin Cash (BCH): `qq3r46hn6ljnhnqnfwxt5pg3g447eq9jhvw5ddfear`
|
||||
- Bitcoin (BTC): `171u8K9e4FtU6j3e5sqNoxKUgEw9qWQdRV`
|
||||
- Stellar Lumens (XLM): `GAAC5WZ33HCTE3BFJFZJXONMEIBNHFLBXM2HJVAZHXXPYA3HP5XPPS7T`
|
||||
- [Patreon](https://www.patreon.com/pocketminemp)
|
||||
|
||||
## Licensing information
|
||||
|
@ -76,6 +76,6 @@ system('git tag ' . $currentVer->getBaseVersion());
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true);
|
||||
system('git add "' . $versionInfoPath . '"');
|
||||
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
|
||||
echo "pushing changes in 10 seconds\n";
|
||||
sleep(10);
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
sleep(5);
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
|
Submodule build/php updated: ffc465f4f8...8308571448
@ -69,3 +69,61 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- `Entity->despawnFromAll()`
|
||||
- Fixed plugin `softdepend` not influencing load order when a soft-depended plugin had an unresolved soft dependency of its own.
|
||||
- Fixed endless falling of sand on top of fences.
|
||||
|
||||
# 3.9.5
|
||||
- Fixed some issues with multiple consecutive commas inside quotes in form responses.
|
||||
- Fixed server crash when the manifest json does not contain a json object in a resource pack.
|
||||
- Ender pearls no longer collide with blocks that do not have any collision boxes.
|
||||
|
||||
# 3.9.6
|
||||
- Updated Composer dependencies to their latest versions.
|
||||
- Prevent clients repeating the resource pack sequence. This fixes error spam with bugged 1.12 clients.
|
||||
- `Internet::simpleCurl()` now includes the PocketMine-MP version in the user-agent string.
|
||||
- Spawn protection is now disabled by default in the setup wizard.
|
||||
- Default difficulty is now NORMAL(2) instead of EASY(1).
|
||||
- Fixed crashing on corrupted world manifest and unsupported world formats.
|
||||
- Fixed `/transferserver` being usable without appropriate permissions.
|
||||
- `RegionLoader->removeChunk()` now writes the region header as appropriate.
|
||||
- Fixed performance issue when loading large regions (bug in header validation).
|
||||
- Fixed skin geometry being removed when the JSON contained comments.
|
||||
- Added new constants to `EventPacket`.
|
||||
- Added encode/decode for `StructureTemplateDataExportRequestPacket` and `StructureTemplateDataExportResponsePacket`.
|
||||
- Fixed broken type asserts in `LevelChunkPacket::withCache()` and `ClientCacheMissResponsePacket::create()`.
|
||||
- `types\CommandParameter` field `byte1` has been renamed to `flags`.
|
||||
- Cleaned up public interface of `AvailableCommandsPacket`, removing fields which exposed details of the encoding scheme.
|
||||
- Improved documentation for the following API methods:
|
||||
- `pocketmine\item\Item`:
|
||||
- `addCreativeItem()`
|
||||
- `removeCreativeItem()`
|
||||
- `clearCreativeItems()`
|
||||
- `pocketmine\level\Explosion`:
|
||||
- `explodeA()`
|
||||
- `explodeB()`
|
||||
- Fixed various cosmetic documentation inconsistencies in the core and dependencies.
|
||||
|
||||
# 3.9.7
|
||||
- Fixed a crash that could occur during timezone detection.
|
||||
- Squid no longer spin around constantly in enclosed spaces. Their performance impact is reduced.
|
||||
- Cleaned up the bootstrap file.
|
||||
|
||||
# 3.9.8
|
||||
- Added [PHPStan](https://github.com/phpstan/phpstan) configuration. PHPStan is now used on CI for automated QA, which should improve stability and quality going forward.
|
||||
- The following constants are now autoloaded when loading the Composer autoloader:
|
||||
- `pocketmine\NAME`
|
||||
- `pocketmine\BASE_VERSION`
|
||||
- `pocketmine\IS_DEVELOPMENT_BUILD`
|
||||
- `pocketmine\BUILD_NUMBER`
|
||||
- `INT32_MIN`
|
||||
- `INT32_MAX`
|
||||
- `INT32_MASK`
|
||||
- Fixed memory leaks and crashes caused by plugin use of `Player->showPlayer()` and `Entity->spawnTo()`.
|
||||
- Fixed crashes that could occur when tile classes were overridden with classes incompatible with the originals.
|
||||
- Fixed improper handling of non-Compound root NBT tags on network itemstack decoding.
|
||||
- Fixed paintings dropping multiple items when destroyed by block updates.
|
||||
- Fixed `var_dump()` not showing private and protected properties of `DataPacket` subclasses.
|
||||
- Fixed overloads with zero arguments being missing when decoding `AvailableCommandsPacket`.
|
||||
- `CraftingDataPacket` now retains the `cleanRecipes` field when decoding.
|
||||
- Fixed `Block->getMetadata()` returning null (non-iterable).
|
||||
- `PlayerChatEvent` documentation has been updated to specify that `CommandSender` recipients are accepted. This behaviour was already present in previous versions, but incorrectly documented.
|
||||
- Fixed various issues with PHPDoc comments reported by PHPStan.
|
||||
- Fixed various minor code nits reported by PHPStan.
|
||||
|
@ -36,7 +36,11 @@
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": ["src"]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src/pocketmine/GlobalConstants.php",
|
||||
"src/pocketmine/VersionInfo.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
52
composer.lock
generated
52
composer.lock
generated
@ -92,16 +92,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "8b3b1160679398387cb896fd5d06018413437dfa"
|
||||
"reference": "435f2ee265bce75ef1aa9563f9b60ff36d705e80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/8b3b1160679398387cb896fd5d06018413437dfa",
|
||||
"reference": "8b3b1160679398387cb896fd5d06018413437dfa",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/435f2ee265bce75ef1aa9563f9b60ff36d705e80",
|
||||
"reference": "435f2ee265bce75ef1aa9563f9b60ff36d705e80",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -119,23 +119,23 @@
|
||||
],
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.9",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.10",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2019-07-22T13:15:53+00:00"
|
||||
"time": "2019-10-21T14:40:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c"
|
||||
"reference": "68be8a79fd0169043ef514797c304517cb8a6071"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c",
|
||||
"reference": "b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/68be8a79fd0169043ef514797c304517cb8a6071",
|
||||
"reference": "68be8a79fd0169043ef514797c304517cb8a6071",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -153,23 +153,23 @@
|
||||
],
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.2",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.3",
|
||||
"issues": "https://github.com/pmmp/Math/issues"
|
||||
},
|
||||
"time": "2019-01-04T15:42:36+00:00"
|
||||
"time": "2019-10-21T14:35:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.10",
|
||||
"version": "0.2.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "2db27aebe7dc89772aaa8df53361eef801f60063"
|
||||
"reference": "b5777265329753b74dd40bb105eedabeefb98724"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/2db27aebe7dc89772aaa8df53361eef801f60063",
|
||||
"reference": "2db27aebe7dc89772aaa8df53361eef801f60063",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/b5777265329753b74dd40bb105eedabeefb98724",
|
||||
"reference": "b5777265329753b74dd40bb105eedabeefb98724",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -194,10 +194,10 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.10",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2019-07-22T15:22:23+00:00"
|
||||
"time": "2019-12-01T08:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
@ -276,23 +276,20 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49"
|
||||
"reference": "94d4df142fe837ba836e9348dd00209e4bdcc307"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/94d4df142fe837ba836e9348dd00209e4bdcc307",
|
||||
"reference": "94d4df142fe837ba836e9348dd00209e4bdcc307",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"exclude-from-classmap": [
|
||||
"stubs"
|
||||
],
|
||||
"classmap": [
|
||||
"./"
|
||||
]
|
||||
@ -302,9 +299,10 @@
|
||||
],
|
||||
"description": "Standard library files required by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/SPL/tree/master"
|
||||
"source": "https://github.com/pmmp/SPL/tree/0.3.3",
|
||||
"issues": "https://github.com/pmmp/SPL/issues"
|
||||
},
|
||||
"time": "2018-08-12T15:17:39+00:00"
|
||||
"time": "2019-10-28T11:41:20+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
124
phpstan.neon.dist
Normal file
124
phpstan.neon.dist
Normal file
@ -0,0 +1,124 @@
|
||||
includes:
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/optional-com-dotnet.neon
|
||||
- tests/phpstan/configs/optional-leveldb.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
- tests/phpstan/configs/pthreads-bugs.neon
|
||||
|
||||
parameters:
|
||||
level: 3
|
||||
autoload_files:
|
||||
- tests/phpstan/bootstrap.php
|
||||
- src/pocketmine/PocketMine.php
|
||||
paths:
|
||||
- src
|
||||
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Cannot instantiate interface pocketmine\\\\level\\\\format\\\\io\\\\LevelProvider\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/Server.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method pocketmine\\\\command\\\\CommandSender\\:\\:teleport\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/command/defaults/TeleportCommand.php
|
||||
# comment: "not actually possible, but high cost to fix warning"
|
||||
|
||||
-
|
||||
message: "#^Array \\(array\\<string\\>\\) does not accept pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 2
|
||||
path: src/pocketmine/entity/Entity.php
|
||||
|
||||
-
|
||||
message: "#^Invalid array key type pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/entity/Entity.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\EntityDeathEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\Living but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/EntityDeathEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\EntityShootBowEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\Living but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/EntityShootBowEvent.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\event\\\\entity\\\\EntityShootBowEvent\\:\\:\\$projectile \\(pocketmine\\\\entity\\\\projectile\\\\Projectile\\) does not accept pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/EntityShootBowEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ItemDespawnEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\object\\\\ItemEntity but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ItemDespawnEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ItemSpawnEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\object\\\\ItemEntity but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ItemSpawnEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ProjectileHitEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\projectile\\\\Projectile but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ProjectileHitEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ProjectileLaunchEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\projectile\\\\Projectile but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ProjectileLaunchEvent.php
|
||||
|
||||
-
|
||||
message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\hell\\\\Nether has an unused parameter \\$options\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/level/generator/hell/Nether.php
|
||||
|
||||
-
|
||||
message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\normal\\\\Normal has an unused parameter \\$options\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/level/generator/normal/Normal.php
|
||||
|
||||
-
|
||||
message: "#^Used constant pocketmine\\\\RESOURCE_PATH not found\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/network/mcpe/protocol/StartGamePacket.php
|
||||
|
||||
-
|
||||
message: "#^Constructor of class pocketmine\\\\scheduler\\\\TaskScheduler has an unused parameter \\$logger\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/scheduler/TaskScheduler.php
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\COMPOSER_AUTOLOADER_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\DATA not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\GIT_COMMIT not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\PLUGIN_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\RESOURCE_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\START_TIME not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\VERSION not found\\.$#"
|
||||
path: src
|
||||
|
@ -333,7 +333,7 @@ class CrashDump{
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . GIT_COMMIT);
|
||||
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
$this->addLine("PHP Version: " . phpversion());
|
||||
$this->addLine("Zend version: " . zend_version());
|
||||
|
30
src/pocketmine/GlobalConstants.php
Normal file
30
src/pocketmine/GlobalConstants.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// composer autoload doesn't use require_once and also pthreads can inherit things
|
||||
if(defined('pocketmine\_GLOBAL_CONSTANTS_INCLUDED')){
|
||||
return;
|
||||
}
|
||||
define('pocketmine\_GLOBAL_CONSTANTS_INCLUDED', true);
|
||||
|
||||
const INT32_MIN = -0x80000000;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
const INT32_MASK = 0xffffffff;
|
@ -261,6 +261,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var bool */
|
||||
public $loggedIn = false;
|
||||
|
||||
/** @var bool */
|
||||
private $resourcePacksDone = false;
|
||||
|
||||
/** @var bool */
|
||||
public $spawned = false;
|
||||
|
||||
@ -1613,7 +1616,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$diff = $this->distanceSquared($newPos) / $tickDiff ** 2;
|
||||
|
||||
if($this->isSurvival() and !$revert and $diff > 0.0625){
|
||||
if($this->isSurvival() and $diff > 0.0625){
|
||||
$ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ));
|
||||
$ev->setCancelled($this->allowMovementCheats);
|
||||
|
||||
@ -2056,6 +2059,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{
|
||||
if($this->resourcePacksDone){
|
||||
return false;
|
||||
}
|
||||
switch($packet->status){
|
||||
case ResourcePackClientResponsePacket::STATUS_REFUSED:
|
||||
//TODO: add lang strings for this
|
||||
@ -2097,6 +2103,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
$this->resourcePacksDone = true;
|
||||
$this->completeLoginSequence();
|
||||
break;
|
||||
default:
|
||||
@ -2821,7 +2828,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->stopSleep();
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_RESPAWN:
|
||||
if(!$this->spawned or $this->isAlive() or !$this->isOnline()){
|
||||
if($this->isAlive()){
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3049,6 +3056,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||
if($this->resourcePacksDone){
|
||||
return false;
|
||||
}
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pack = $manager->getPackById($packet->packId);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
|
@ -21,11 +21,6 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {
|
||||
const INT32_MIN = -0x80000000;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
}
|
||||
|
||||
namespace pocketmine {
|
||||
|
||||
use pocketmine\utils\MainLogger;
|
||||
@ -57,18 +52,18 @@ namespace pocketmine {
|
||||
if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
|
||||
//If PHP version isn't high enough, anything below might break, so don't bother checking it.
|
||||
return [
|
||||
\pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . "."
|
||||
"PHP >= " . MIN_PHP_VERSION . " is required, but you have PHP " . PHP_VERSION . "."
|
||||
];
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
|
||||
if(PHP_INT_SIZE < 8){
|
||||
$messages[] = "Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
|
||||
$messages[] = "32-bit systems/PHP are no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
|
||||
}
|
||||
|
||||
if(php_sapi_name() !== "cli"){
|
||||
$messages[] = "You must run " . \pocketmine\NAME . " using the CLI.";
|
||||
$messages[] = "Only PHP CLI is supported.";
|
||||
}
|
||||
|
||||
$extensions = [
|
||||
@ -121,6 +116,29 @@ namespace pocketmine {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
function emit_performance_warnings(\Logger $logger){
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
|
||||
}
|
||||
if(!extension_loaded("pocketmine_chunkutils")){
|
||||
$logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance.");
|
||||
}
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
$logger->warning("Debugging assertions are enabled. This may degrade performance. To disable them, set `zend.assertions = -1` in php.ini.");
|
||||
}
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged installation detected. This will degrade autoloading speed and make startup times longer.");
|
||||
}
|
||||
}
|
||||
|
||||
function set_ini_entries(){
|
||||
ini_set("allow_url_fopen", '1');
|
||||
ini_set("display_errors", '1');
|
||||
ini_set("display_startup_errors", '1');
|
||||
ini_set("default_charset", "utf-8");
|
||||
ini_set('assert.exception', '1');
|
||||
}
|
||||
|
||||
function server(){
|
||||
if(!empty($messages = check_platform_dependencies())){
|
||||
echo PHP_EOL;
|
||||
@ -136,6 +154,7 @@ namespace pocketmine {
|
||||
unset($messages);
|
||||
|
||||
error_reporting(-1);
|
||||
set_ini_entries();
|
||||
|
||||
if(\Phar::running(true) !== ""){
|
||||
define('pocketmine\PATH', \Phar::running(true) . "/");
|
||||
@ -161,20 +180,27 @@ namespace pocketmine {
|
||||
|
||||
set_error_handler([Utils::class, 'errorExceptionHandler']);
|
||||
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader->register(false);
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
define('pocketmine\VERSION', $version->getFullVersion(true));
|
||||
|
||||
set_time_limit(0); //Who set it to 30 seconds?!?!
|
||||
$gitHash = str_repeat("00", 20);
|
||||
|
||||
ini_set("allow_url_fopen", '1');
|
||||
ini_set("display_errors", '1');
|
||||
ini_set("display_startup_errors", '1');
|
||||
ini_set("default_charset", "utf-8");
|
||||
if(\Phar::running(true) === ""){
|
||||
if(Utils::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){
|
||||
$gitHash = trim($out);
|
||||
if(Utils::execute("git diff --quiet") === 1 or Utils::execute("git diff --cached --quiet") === 1){ //Locally-modified
|
||||
$gitHash .= "-dirty";
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
}
|
||||
}
|
||||
|
||||
ini_set("memory_limit", '-1');
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
|
||||
define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
|
||||
|
||||
@ -187,8 +213,7 @@ namespace pocketmine {
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
}
|
||||
|
||||
define('pocketmine\LOCK_FILE_PATH', \pocketmine\DATA . 'server.lock');
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\LOCK_FILE_PATH, "a+b"));
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\DATA . 'server.lock', "a+b"));
|
||||
if(!flock(\pocketmine\LOCK_FILE, LOCK_EX | LOCK_NB)){
|
||||
//wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the
|
||||
//other server wrote its PID and released exclusive lock before we get our lock
|
||||
@ -222,42 +247,7 @@ namespace pocketmine {
|
||||
}
|
||||
unset($tzError);
|
||||
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning(PHP_EOL . PHP_EOL . PHP_EOL . "\tYou are running " . \pocketmine\NAME . " with xdebug enabled. This has a major impact on performance." . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
if(!extension_loaded("pocketmine_chunkutils")){
|
||||
$logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance.");
|
||||
}
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged " . \pocketmine\NAME . " installation detected. Consider using a phar in production for better performance.");
|
||||
}
|
||||
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
define('pocketmine\VERSION', $version->getFullVersion(true));
|
||||
|
||||
$gitHash = str_repeat("00", 20);
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
if(Utils::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){
|
||||
$gitHash = trim($out);
|
||||
if(Utils::execute("git diff --quiet") === 1 or Utils::execute("git diff --cached --quiet") === 1){ //Locally-modified
|
||||
$gitHash .= "-dirty";
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
}
|
||||
}
|
||||
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
|
||||
|
||||
@define("INT32_MASK", is_int(0xffffffff) ? 0xffffffff : -1);
|
||||
@ini_set("opcache.mmap_base", bin2hex(random_bytes(8))); //Fix OPCache address errors
|
||||
emit_performance_warnings($logger);
|
||||
|
||||
$exitCode = 0;
|
||||
do{
|
||||
@ -272,6 +262,13 @@ namespace pocketmine {
|
||||
//TODO: move this to a Server field
|
||||
define('pocketmine\START_TIME', microtime(true));
|
||||
ThreadManager::init();
|
||||
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader->register(false);
|
||||
|
||||
new Server($autoloader, $logger, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
|
||||
|
||||
$logger->info("Stopping other threads");
|
||||
@ -281,9 +278,7 @@ namespace pocketmine {
|
||||
usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
echo "Some threads could not be stopped, performing a force-kill" . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
$logger->debug("Some threads could not be stopped, performing a force-kill");
|
||||
Utils::kill(getmypid());
|
||||
}
|
||||
}while(false);
|
||||
@ -296,5 +291,7 @@ namespace pocketmine {
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
\pocketmine\server();
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
\pocketmine\server();
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ class Server{
|
||||
* @var int
|
||||
*/
|
||||
private $tickCounter = 0;
|
||||
/** @var int */
|
||||
/** @var float */
|
||||
private $nextTick = 0;
|
||||
/** @var float[] */
|
||||
private $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
|
||||
@ -593,7 +593,7 @@ class Server{
|
||||
* @return int
|
||||
*/
|
||||
public function getDifficulty() : int{
|
||||
return $this->getConfigInt("difficulty", 1);
|
||||
return $this->getConfigInt("difficulty", Level::DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1101,11 +1101,17 @@ class Server{
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @var LevelProvider $provider
|
||||
* @see LevelProvider::__construct()
|
||||
*/
|
||||
$provider = new $providerClass($path);
|
||||
|
||||
try{
|
||||
/**
|
||||
* @var LevelProvider $provider
|
||||
* @see LevelProvider::__construct()
|
||||
*/
|
||||
$provider = new $providerClass($path);
|
||||
}catch(LevelException $e){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, $e->getMessage()]));
|
||||
return false;
|
||||
}
|
||||
try{
|
||||
GeneratorManager::getGenerator($provider->getGenerator(), true);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
@ -1438,7 +1444,7 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return string[][]
|
||||
*/
|
||||
public function getCommandAliases() : array{
|
||||
$section = $this->getProperty("aliases");
|
||||
@ -1529,7 +1535,7 @@ class Server{
|
||||
"force-gamemode" => false,
|
||||
"hardcore" => false,
|
||||
"pvp" => true,
|
||||
"difficulty" => 1,
|
||||
"difficulty" => Level::DIFFICULTY_NORMAL,
|
||||
"generator-settings" => "",
|
||||
"level-name" => "world",
|
||||
"level-seed" => "",
|
||||
@ -1559,12 +1565,6 @@ class Server{
|
||||
return;
|
||||
}
|
||||
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
$this->logger->warning("Debugging assertions are enabled, this may impact on performance. To disable them, set `zend.assertions = -1` in php.ini.");
|
||||
}
|
||||
|
||||
ini_set('assert.exception', '1');
|
||||
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->setLogDebug(\pocketmine\DEBUG > 1);
|
||||
}
|
||||
@ -1789,7 +1789,7 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param TextContainer|string $message
|
||||
* @param Player[] $recipients
|
||||
* @param CommandSender[] $recipients
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@ -1798,7 +1798,6 @@ class Server{
|
||||
return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS);
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendMessage($message);
|
||||
}
|
||||
@ -1823,7 +1822,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendTip($tip);
|
||||
}
|
||||
@ -1849,7 +1847,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendPopup($popup);
|
||||
}
|
||||
@ -1879,7 +1876,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->addTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
|
||||
}
|
||||
@ -2200,6 +2196,7 @@ class Server{
|
||||
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.defaultGameMode", [self::getGamemodeString($this->getGamemode())]));
|
||||
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.donate", [TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET]));
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.startFinished", [round(microtime(true) - \pocketmine\START_TIME, 3)]));
|
||||
|
||||
$this->tickProcessor();
|
||||
|
@ -21,7 +21,15 @@
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
// composer autoload doesn't use require_once and also pthreads can inherit things
|
||||
// TODO: drop this file and use a final class with constants
|
||||
if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
return;
|
||||
}
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.9.4";
|
||||
const BASE_VERSION = "3.9.8";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
@ -69,7 +69,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
/** @var int|null */
|
||||
protected $itemId;
|
||||
|
||||
/** @var AxisAlignedBB */
|
||||
/** @var AxisAlignedBB|null */
|
||||
protected $boundingBox = null;
|
||||
|
||||
|
||||
@ -763,7 +763,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
return $this->level->getBlockMetadata()->getMetadata($this, $metadataKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
public function hasMetadata(string $metadataKey) : bool{
|
||||
|
@ -80,6 +80,9 @@ class BurningFurnace extends Solid{
|
||||
$furnace = $this->getLevel()->getTile($this);
|
||||
if(!($furnace instanceof TileFurnace)){
|
||||
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this));
|
||||
if(!($furnace instanceof TileFurnace)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$furnace->canOpenWith($item->getCustomName())){
|
||||
|
@ -109,6 +109,9 @@ class Chest extends Transparent{
|
||||
$chest = $t;
|
||||
}else{
|
||||
$chest = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this));
|
||||
if(!($chest instanceof TileChest)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
|
@ -47,6 +47,9 @@ class ItemFrame extends Flowable{
|
||||
$tile = $this->level->getTile($this);
|
||||
if(!($tile instanceof TileItemFrame)){
|
||||
$tile = Tile::createTile(Tile::ITEM_FRAME, $this->getLevel(), TileItemFrame::createNBT($this));
|
||||
if(!($tile instanceof TileItemFrame)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if($tile->hasItem()){
|
||||
|
@ -55,7 +55,7 @@ abstract class Command{
|
||||
*/
|
||||
private $activeAliases = [];
|
||||
|
||||
/** @var CommandMap */
|
||||
/** @var CommandMap|null */
|
||||
private $commandMap = null;
|
||||
|
||||
/** @var string */
|
||||
|
@ -141,6 +141,7 @@ class CommandReader extends Thread{
|
||||
case self::TYPE_STREAM:
|
||||
//stream_select doesn't work on piped streams for some reason
|
||||
$r = [self::$stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
|
@ -60,7 +60,6 @@ class GamemodeCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $sender;
|
||||
if(isset($args[1])){
|
||||
$target = $sender->getServer()->getPlayer($args[1]);
|
||||
if($target === null){
|
||||
@ -68,7 +67,9 @@ class GamemodeCommand extends VanillaCommand{
|
||||
|
||||
return true;
|
||||
}
|
||||
}elseif(!($sender instanceof Player)){
|
||||
}elseif($sender instanceof Player){
|
||||
$target = $sender;
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,10 @@ class TransferServerCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(count($args) < 1){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}elseif(!($sender instanceof Player)){
|
||||
|
@ -318,7 +318,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
/** @var string[] */
|
||||
private static $knownEntities = [];
|
||||
/** @var string[][] */
|
||||
private static $saveNames = [];
|
||||
@ -460,8 +460,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/** @var EntityDamageEvent|null */
|
||||
protected $lastDamageCause = null;
|
||||
|
||||
/** @var Block[] */
|
||||
protected $blocksAround = [];
|
||||
/** @var Block[]|null */
|
||||
protected $blocksAround = null;
|
||||
|
||||
/** @var float|null */
|
||||
public $lastX = null;
|
||||
@ -2048,7 +2048,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param Player $player
|
||||
*/
|
||||
public function spawnTo(Player $player) : void{
|
||||
if(!isset($this->hasSpawned[$player->getLoaderId()])){
|
||||
if(!isset($this->hasSpawned[$player->getLoaderId()]) and $this->chunk !== null and isset($player->usedChunks[Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())])){
|
||||
$this->hasSpawned[$player->getLoaderId()] = $player;
|
||||
|
||||
$this->sendSpawnPacket($player);
|
||||
|
@ -587,6 +587,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return (int) min(100, 7 * $this->getXpLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PlayerInventory
|
||||
*/
|
||||
public function getInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use Ahc\Json\Comment as CommentedJsonDecoder;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function strlen;
|
||||
|
||||
@ -129,7 +129,7 @@ class Skin{
|
||||
*/
|
||||
public function debloatGeometryData() : void{
|
||||
if($this->geometryData !== ""){
|
||||
$this->geometryData = (string) json_encode(json_decode($this->geometryData));
|
||||
$this->geometryData = (string) json_encode((new CommentedJsonDecoder())->decode($this->geometryData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class Squid extends WaterAnimal{
|
||||
public $width = 0.95;
|
||||
public $height = 0.95;
|
||||
|
||||
/** @var Vector3 */
|
||||
/** @var Vector3|null */
|
||||
public $swimDirection = null;
|
||||
public $swimSpeed = 0.1;
|
||||
|
||||
@ -82,7 +82,7 @@ class Squid extends WaterAnimal{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(++$this->switchDirectionTicker === 100 or $this->isCollided){
|
||||
if(++$this->switchDirectionTicker === 100){
|
||||
$this->switchDirectionTicker = 0;
|
||||
if(mt_rand(0, 100) < 50){
|
||||
$this->swimDirection = null;
|
||||
|
@ -90,6 +90,9 @@ class Painting extends Entity{
|
||||
}
|
||||
|
||||
public function kill() : void{
|
||||
if(!$this->isAlive()){
|
||||
return;
|
||||
}
|
||||
parent::kill();
|
||||
|
||||
$drops = true;
|
||||
|
@ -23,36 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity\projectile;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\ProjectileHitEvent;
|
||||
use pocketmine\level\sound\EndermanTeleportSound;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
|
||||
class EnderPearl extends Throwable{
|
||||
public const NETWORK_ID = self::ENDER_PEARL;
|
||||
|
||||
protected function calculateInterceptWithBlock(Block $block, Vector3 $start, Vector3 $end) : ?RayTraceResult{
|
||||
if($block->getId() !== Block::AIR and empty($block->getCollisionBoxes())){
|
||||
//TODO: remove this once block collision boxes are fixed properly
|
||||
$bb = new AxisAlignedBB(
|
||||
$block->x,
|
||||
$block->y,
|
||||
$block->z,
|
||||
$block->x + 1,
|
||||
$block->y + 1,
|
||||
$block->z + 1
|
||||
);
|
||||
|
||||
return $bb->calculateIntercept($start, $end);
|
||||
}
|
||||
|
||||
return parent::calculateInterceptWithBlock($block, $start, $end);
|
||||
}
|
||||
|
||||
protected function onHit(ProjectileHitEvent $event) : void{
|
||||
$owner = $this->getOwningEntity();
|
||||
if($owner !== null){
|
||||
|
@ -56,7 +56,6 @@ abstract class Event{
|
||||
throw new \BadMethodCallException(get_class($this) . " is not Cancellable");
|
||||
}
|
||||
|
||||
/** @var Event $this */
|
||||
return $this->isCancelled;
|
||||
}
|
||||
|
||||
@ -70,7 +69,6 @@ abstract class Event{
|
||||
throw new \BadMethodCallException(get_class($this) . " is not Cancellable");
|
||||
}
|
||||
|
||||
/** @var Event $this */
|
||||
$this->isCancelled = $value;
|
||||
}
|
||||
|
||||
@ -78,8 +76,6 @@ abstract class Event{
|
||||
* Calls event handlers registered for this event.
|
||||
*
|
||||
* @throws \RuntimeException if event call recursion reaches the max depth limit
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function call() : void{
|
||||
if(self::$eventCallDepth >= self::MAX_EVENT_CALL_DEPTH){
|
||||
|
@ -23,10 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Called when a player chats something
|
||||
@ -39,15 +41,15 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
protected $format;
|
||||
|
||||
/**
|
||||
* @var Player[]
|
||||
* @var CommandSender[]
|
||||
*/
|
||||
protected $recipients = [];
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
* @param string $message
|
||||
* @param string $format
|
||||
* @param Player[] $recipients
|
||||
* @param Player $player
|
||||
* @param string $message
|
||||
* @param string $format
|
||||
* @param CommandSender[] $recipients
|
||||
*/
|
||||
public function __construct(Player $player, string $message, string $format = "chat.type.text", array $recipients = null){
|
||||
$this->player = $player;
|
||||
@ -56,7 +58,11 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
$this->format = $format;
|
||||
|
||||
if($recipients === null){
|
||||
$this->recipients = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS);
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
if($permissible instanceof CommandSender){
|
||||
$this->recipients[spl_object_id($permissible)] = $permissible;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->recipients = $recipients;
|
||||
}
|
||||
@ -100,14 +106,14 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player[]
|
||||
* @return CommandSender[]
|
||||
*/
|
||||
public function getRecipients() : array{
|
||||
return $this->recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[] $recipients
|
||||
* @param CommandSender[] $recipients
|
||||
*/
|
||||
public function setRecipients(array $recipients) : void{
|
||||
$this->recipients = $recipients;
|
||||
|
@ -39,15 +39,15 @@ class PlayerCreationEvent extends Event{
|
||||
/** @var int */
|
||||
private $port;
|
||||
|
||||
/** @var Player::class */
|
||||
/** @var string */
|
||||
private $baseClass;
|
||||
/** @var Player::class */
|
||||
/** @var string */
|
||||
private $playerClass;
|
||||
|
||||
/**
|
||||
* @param SourceInterface $interface
|
||||
* @param Player::class $baseClass
|
||||
* @param Player::class $playerClass
|
||||
* @param string $baseClass
|
||||
* @param string $playerClass
|
||||
* @param string $address
|
||||
* @param int $port
|
||||
*/
|
||||
@ -91,14 +91,14 @@ class PlayerCreationEvent extends Event{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player::class
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseClass(){
|
||||
return $this->baseClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player::class $class
|
||||
* @param string $class
|
||||
*/
|
||||
public function setBaseClass($class){
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
@ -109,14 +109,14 @@ class PlayerCreationEvent extends Event{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player::class
|
||||
* @return string
|
||||
*/
|
||||
public function getPlayerClass(){
|
||||
return $this->playerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player::class $class
|
||||
* @param string $class
|
||||
*/
|
||||
public function setPlayerClass($class){
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
|
@ -47,7 +47,7 @@ abstract class BaseInventory implements Inventory{
|
||||
/** @var string */
|
||||
protected $title;
|
||||
/** @var \SplFixedArray|Item[] */
|
||||
protected $slots = [];
|
||||
protected $slots;
|
||||
/** @var Player[] */
|
||||
protected $viewers = [];
|
||||
/** @var InventoryEventProcessor */
|
||||
|
@ -43,7 +43,7 @@ class CraftingManager{
|
||||
/** @var FurnaceRecipe[] */
|
||||
protected $furnaceRecipes = [];
|
||||
|
||||
/** @var BatchPacket */
|
||||
/** @var BatchPacket|null */
|
||||
private $craftingDataCache;
|
||||
|
||||
public function __construct(){
|
||||
|
@ -65,6 +65,7 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
* @param int $iterations
|
||||
*
|
||||
* @return int
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
protected function matchRecipeItems(array $txItems, array $recipeItems, bool $wildcards, int $iterations = 0) : int{
|
||||
if(empty($recipeItems)){
|
||||
|
@ -137,6 +137,10 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all previously added items from the creative menu.
|
||||
* Note: Players who are already online when this is called will not see this change.
|
||||
*/
|
||||
public static function clearCreativeItems(){
|
||||
Item::$creative = [];
|
||||
}
|
||||
@ -145,10 +149,22 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
return Item::$creative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the creative menu.
|
||||
* Note: Players who are already online when this is called will not see this change.
|
||||
*
|
||||
* @param Item $item
|
||||
*/
|
||||
public static function addCreativeItem(Item $item){
|
||||
Item::$creative[] = clone $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the creative menu.
|
||||
* Note: Players who are already online when this is called will not see this change.
|
||||
*
|
||||
* @param Item $item
|
||||
*/
|
||||
public static function removeCreativeItem(Item $item){
|
||||
$index = self::getCreativeItemIndex($item);
|
||||
if($index !== -1){
|
||||
|
@ -48,7 +48,7 @@ class Shears extends Tool{
|
||||
}
|
||||
|
||||
public function onDestroyBlock(Block $block) : bool{
|
||||
if($block->getHardness() === 0 or $block->isCompatibleWithTool($this)){
|
||||
if($block->getHardness() === 0.0 or $block->isCompatibleWithTool($this)){
|
||||
return $this->applyDamage(1);
|
||||
}
|
||||
return false;
|
||||
|
@ -186,16 +186,18 @@ class WritableBook extends Item{
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
public function getPages() : array{
|
||||
$pages = $this->getNamedTag()->getListTag(self::TAG_PAGES);
|
||||
if($pages === null){
|
||||
return [];
|
||||
}
|
||||
/** @var CompoundTag[] $pages */
|
||||
$pages = $this->getPagesTag()->getValue();
|
||||
|
||||
return $pages->getValue();
|
||||
return $pages;
|
||||
}
|
||||
|
||||
protected function getPagesTag() : ListTag{
|
||||
return $this->getNamedTag()->getListTag(self::TAG_PAGES) ?? new ListTag(self::TAG_PAGES, [], NBT::TAG_Compound);
|
||||
$pagesTag = $this->getNamedTag()->getListTag(self::TAG_PAGES);
|
||||
if($pagesTag !== null and $pagesTag->getTagType() === NBT::TAG_Compound){
|
||||
return $pagesTag;
|
||||
}
|
||||
return new ListTag(self::TAG_PAGES, [], NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +94,7 @@ class Enchantment{
|
||||
public const SLOT_ELYTRA = 0x4000;
|
||||
public const SLOT_TRIDENT = 0x8000;
|
||||
|
||||
/** @var Enchantment[] */
|
||||
/** @var \SplFixedArray|Enchantment[] */
|
||||
protected static $enchantments;
|
||||
|
||||
public static function init() : void{
|
||||
|
Submodule src/pocketmine/lang/locale updated: 73ed1ab3e1...85343cfb7f
@ -89,6 +89,9 @@ class Explosion{
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates which blocks will be destroyed by this explosion. If explodeB() is called without calling this, no blocks
|
||||
* will be destroyed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function explodeA() : bool{
|
||||
@ -148,6 +151,12 @@ class Explosion{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the explosion's effects on the world. This includes destroying blocks (if any), harming and knocking back entities,
|
||||
* and creating sounds and particles.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function explodeB() : bool{
|
||||
$send = [];
|
||||
$updateBlocks = [];
|
||||
|
@ -265,7 +265,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelTimings */
|
||||
public $timings;
|
||||
|
||||
/** @var int */
|
||||
/** @var float */
|
||||
public $tickRateTime = 0;
|
||||
/**
|
||||
* @deprecated
|
||||
|
@ -29,7 +29,7 @@ use function assert;
|
||||
|
||||
class Position extends Vector3{
|
||||
|
||||
/** @var Level */
|
||||
/** @var Level|null */
|
||||
public $level = null;
|
||||
|
||||
/**
|
||||
|
@ -694,50 +694,48 @@ class Chunk{
|
||||
public function initChunk(Level $level){
|
||||
if(!$this->isInit){
|
||||
$changed = false;
|
||||
if($this->NBTentities !== null){
|
||||
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
try{
|
||||
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
|
||||
if(!($entity instanceof Entity)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}catch(\Throwable $t){
|
||||
$level->getServer()->getLogger()->logException($t);
|
||||
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
try{
|
||||
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
|
||||
if(!($entity instanceof Entity)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}catch(\Throwable $t){
|
||||
$level->getServer()->getLogger()->logException($t);
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$level->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag(Tile::TAG_ID, StringTag::class)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Tile::createTile($nbt->getString(Tile::TAG_ID), $level, $nbt) === null){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
|
||||
$this->NBTentities = null;
|
||||
$this->NBTtiles = null;
|
||||
}
|
||||
$this->NBTentities = [];
|
||||
$level->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag(Tile::TAG_ID, StringTag::class)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Tile::createTile($nbt->getString(Tile::TAG_ID), $level, $nbt) === null){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->NBTtiles = [];
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
|
||||
$this->hasChanged = $changed;
|
||||
|
||||
|
@ -129,7 +129,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
$version = $this->levelData->getInt("StorageVersion", INT32_MAX, true);
|
||||
if($version > self::CURRENT_STORAGE_VERSION){
|
||||
throw new LevelException("Specified LevelDB world format version ($version) is not supported by " . \pocketmine\NAME);
|
||||
throw new LevelException("Specified LevelDB world format version ($version) is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,10 @@ use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function array_fill;
|
||||
use function ceil;
|
||||
use function chr;
|
||||
use function fclose;
|
||||
use function fgetc;
|
||||
use function feof;
|
||||
use function file_exists;
|
||||
use function filesize;
|
||||
use function fopen;
|
||||
@ -40,6 +39,7 @@ use function fseek;
|
||||
use function ftruncate;
|
||||
use function fwrite;
|
||||
use function is_resource;
|
||||
use function max;
|
||||
use function ord;
|
||||
use function pack;
|
||||
use function str_pad;
|
||||
@ -60,6 +60,8 @@ class RegionLoader{
|
||||
public const MAX_SECTOR_LENGTH = 255 << 12; //255 sectors (~0.996 MiB)
|
||||
public const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps
|
||||
|
||||
private const FIRST_SECTOR = 2; //location table occupies 0 and 1
|
||||
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
|
||||
/** @var int */
|
||||
@ -71,8 +73,8 @@ class RegionLoader{
|
||||
/** @var resource */
|
||||
protected $filePointer;
|
||||
/** @var int */
|
||||
protected $lastSector;
|
||||
/** @var int[][] [offset in sectors, chunk size in sectors, timestamp] */
|
||||
protected $nextSector = self::FIRST_SECTOR;
|
||||
/** @var RegionLocationTableEntry[] */
|
||||
protected $locationTable = [];
|
||||
/** @var int */
|
||||
public $lastUsed = 0;
|
||||
@ -83,6 +85,9 @@ class RegionLoader{
|
||||
$this->filePath = $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
public function open(){
|
||||
$exists = file_exists($this->filePath);
|
||||
if(!$exists){
|
||||
@ -111,7 +116,7 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
protected function isChunkGenerated(int $index) : bool{
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
return !$this->locationTable[$index]->isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,23 +136,25 @@ class RegionLoader{
|
||||
return null;
|
||||
}
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fseek($this->filePointer, $this->locationTable[$index]->getFirstSector() << 12);
|
||||
|
||||
$prefix = fread($this->filePointer, 4);
|
||||
if($prefix === false or strlen($prefix) !== 4){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (unexpected end of file reading length prefix)");
|
||||
}
|
||||
$length = Binary::readInt($prefix);
|
||||
|
||||
if($length <= 0 or $length > self::MAX_SECTOR_LENGTH){ //Not yet generated / corrupted
|
||||
if($length >= self::MAX_SECTOR_LENGTH){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (sector count $length larger than max " . self::MAX_SECTOR_LENGTH . ")");
|
||||
}
|
||||
if($length <= 0){ //TODO: if we reached here, the locationTable probably needs updating
|
||||
return null;
|
||||
}
|
||||
if($length > self::MAX_SECTOR_LENGTH){ //corrupted
|
||||
throw new CorruptedChunkException("Length for chunk x=$x,z=$z ($length) is larger than maximum " . self::MAX_SECTOR_LENGTH);
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index][1] << 12) . " sectors, got $length sectors)");
|
||||
$this->locationTable[$index][1] = $length >> 12;
|
||||
if($length > ($this->locationTable[$index]->getSectorCount() << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index]->getSectorCount() << 12) . " sectors, got $length sectors)");
|
||||
$old = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($old->getFirstSector(), $length >> 12, time());
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
@ -190,26 +197,22 @@ class RegionLoader{
|
||||
if($length + 4 > self::MAX_SECTOR_LENGTH){
|
||||
throw new ChunkException("Chunk is too big! " . ($length + 4) . " > " . self::MAX_SECTOR_LENGTH);
|
||||
}
|
||||
$sectors = (int) ceil(($length + 4) / 4096);
|
||||
|
||||
$newSize = (int) ceil(($length + 4) / 4096);
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$indexChanged = false;
|
||||
if($this->locationTable[$index][1] < $sectors){
|
||||
$this->locationTable[$index][0] = $this->lastSector + 1;
|
||||
$this->lastSector += $sectors; //The GC will clean this shift "later"
|
||||
$indexChanged = true;
|
||||
}elseif($this->locationTable[$index][1] != $sectors){
|
||||
$indexChanged = true;
|
||||
$offset = $this->locationTable[$index]->getFirstSector();
|
||||
|
||||
if($this->locationTable[$index]->getSectorCount() < $newSize){
|
||||
$offset = $this->nextSector;
|
||||
}
|
||||
|
||||
$this->locationTable[$index][1] = $sectors;
|
||||
$this->locationTable[$index][2] = time();
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($offset, $newSize, time());
|
||||
$this->bumpNextFreeSector($this->locationTable[$index]);
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
fseek($this->filePointer, $offset << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $newSize << 12, "\x00", STR_PAD_RIGHT));
|
||||
|
||||
if($indexChanged){
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,8 +223,8 @@ class RegionLoader{
|
||||
*/
|
||||
public function removeChunk(int $x, int $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry(0, 0, 0);
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,9 +266,11 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
protected function loadLocationTable(){
|
||||
fseek($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
|
||||
$headerRaw = fread($this->filePointer, self::REGION_HEADER_LENGTH);
|
||||
if(($len = strlen($headerRaw)) !== self::REGION_HEADER_LENGTH){
|
||||
@ -273,43 +278,64 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
$data = unpack("N*", $headerRaw);
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = $data[$i + 1];
|
||||
$offset = $index >> 8;
|
||||
if($offset !== 0){
|
||||
self::getChunkCoords($i, $x, $z);
|
||||
$fileOffset = $offset << 12;
|
||||
$timestamp = $data[$i + 1025];
|
||||
|
||||
fseek($this->filePointer, $fileOffset);
|
||||
if(fgetc($this->filePointer) === false){ //Try and read from the location
|
||||
throw new CorruptedRegionException("Region file location offset x=$x,z=$z points to invalid file location $fileOffset");
|
||||
}elseif(isset($usedOffsets[$offset])){
|
||||
self::getChunkCoords($usedOffsets[$offset], $existingX, $existingZ);
|
||||
throw new CorruptedRegionException("Found two chunk offsets (chunk1: x=$existingX,z=$existingZ, chunk2: x=$x,z=$z) pointing to the file location $fileOffset");
|
||||
}else{
|
||||
$usedOffsets[$offset] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
$this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]];
|
||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||
if($offset === 0){
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0);
|
||||
}else{
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry($offset, $index & 0xff, $timestamp);
|
||||
$this->bumpNextFreeSector($this->locationTable[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkLocationTableValidity();
|
||||
|
||||
fseek($this->filePointer, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
private function checkLocationTableValidity() : void{
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$entry = $this->locationTable[$i];
|
||||
if($entry->isNull()){
|
||||
continue;
|
||||
}
|
||||
|
||||
self::getChunkCoords($i, $x, $z);
|
||||
$offset = $entry->getFirstSector();
|
||||
$fileOffset = $offset << 12;
|
||||
|
||||
//TODO: more validity checks
|
||||
|
||||
fseek($this->filePointer, $fileOffset);
|
||||
if(feof($this->filePointer)){
|
||||
throw new CorruptedRegionException("Region file location offset x=$x,z=$z points to invalid file location $fileOffset");
|
||||
}
|
||||
if(isset($usedOffsets[$offset])){
|
||||
self::getChunkCoords($usedOffsets[$offset], $existingX, $existingZ);
|
||||
throw new CorruptedRegionException("Found two chunk offsets (chunk1: x=$existingX,z=$existingZ, chunk2: x=$x,z=$z) pointing to the file location $fileOffset");
|
||||
}
|
||||
$usedOffsets[$offset] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
private function writeLocationTable(){
|
||||
$write = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$write[] = (($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
|
||||
$write[] = (($this->locationTable[$i]->getFirstSector() << 8) | $this->locationTable[$i]->getSectorCount());
|
||||
}
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$write[] = $this->locationTable[$i][2];
|
||||
$write[] = $this->locationTable[$i]->getTimestamp();
|
||||
}
|
||||
fseek($this->filePointer, 0);
|
||||
fwrite($this->filePointer, pack("N*", ...$write), 4096 * 2);
|
||||
@ -317,16 +343,21 @@ class RegionLoader{
|
||||
|
||||
protected function writeLocationIndex($index){
|
||||
fseek($this->filePointer, $index << 2);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index]->getFirstSector() << 8) | $this->locationTable[$index]->getSectorCount()), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index]->getTimestamp()), 4);
|
||||
}
|
||||
|
||||
protected function createBlank(){
|
||||
fseek($this->filePointer, 0);
|
||||
ftruncate($this->filePointer, 8192); // this fills the file with the null byte
|
||||
$this->lastSector = 1;
|
||||
$this->locationTable = array_fill(0, 1024, [0, 0, 0]);
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private function bumpNextFreeSector(RegionLocationTableEntry $entry) : void{
|
||||
$this->nextSector = max($this->nextSector, $entry->getLastSector()) + 1;
|
||||
}
|
||||
|
||||
public function getX() : int{
|
||||
|
@ -0,0 +1,98 @@
|
||||
<?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\level\format\io\region;
|
||||
|
||||
use function range;
|
||||
|
||||
class RegionLocationTableEntry{
|
||||
|
||||
/** @var int */
|
||||
private $firstSector;
|
||||
/** @var int */
|
||||
private $sectorCount;
|
||||
/** @var int */
|
||||
private $timestamp;
|
||||
|
||||
/**
|
||||
* @param int $firstSector
|
||||
* @param int $sectorCount
|
||||
* @param int $timestamp
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(int $firstSector, int $sectorCount, int $timestamp){
|
||||
if($firstSector < 0){
|
||||
throw new \InvalidArgumentException("Start sector must be positive, got $firstSector");
|
||||
}
|
||||
$this->firstSector = $firstSector;
|
||||
if($sectorCount < 0 or $sectorCount > 255){
|
||||
throw new \InvalidArgumentException("Sector count must be in range 0...255, got $sectorCount");
|
||||
}
|
||||
$this->sectorCount = $sectorCount;
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getFirstSector() : int{
|
||||
return $this->firstSector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastSector() : int{
|
||||
return $this->firstSector + $this->sectorCount - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of sector offsets reserved by this chunk.
|
||||
* @return int[]
|
||||
*/
|
||||
public function getUsedSectors() : array{
|
||||
return range($this->getFirstSector(), $this->getLastSector());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSectorCount() : int{
|
||||
return $this->sectorCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimestamp() : int{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNull() : bool{
|
||||
return $this->firstSector === 0 or $this->sectorCount === 0;
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ abstract class LightUpdate{
|
||||
/** @var ChunkManager */
|
||||
protected $level;
|
||||
|
||||
/** @var int[] blockhash => new light level */
|
||||
/** @var int[][] blockhash => [x, y, z, new light level] */
|
||||
protected $updateNodes = [];
|
||||
|
||||
/** @var \SplQueue */
|
||||
|
@ -29,7 +29,7 @@ namespace pocketmine\metadata;
|
||||
use pocketmine\plugin\Plugin;
|
||||
|
||||
abstract class MetadataStore{
|
||||
/** @var \SplObjectStorage[] */
|
||||
/** @var \SplObjectStorage[]|MetadataValue[][] */
|
||||
private $metadataMap;
|
||||
|
||||
/**
|
||||
@ -100,7 +100,7 @@ abstract class MetadataStore{
|
||||
* @param Plugin $owningPlugin
|
||||
*/
|
||||
public function invalidateAll(Plugin $owningPlugin){
|
||||
/** @var MetadataValue[] $values */
|
||||
/** @var \SplObjectStorage|MetadataValue[] $values */
|
||||
foreach($this->metadataMap as $values){
|
||||
if(isset($values[$owningPlugin])){
|
||||
$values[$owningPlugin]->invalidate();
|
||||
|
@ -37,6 +37,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\UUID;
|
||||
use function count;
|
||||
@ -92,7 +93,11 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
if($c !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT count $c");
|
||||
}
|
||||
$nbt = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
$decodedNBT = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
if(!($decodedNBT instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Unexpected root tag type for itemstack");
|
||||
}
|
||||
$nbt = $decodedNBT;
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
@ -377,7 +382,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an EntityUniqueID
|
||||
* Writes an EntityRuntimeID
|
||||
*
|
||||
* @param int $eid
|
||||
*/
|
||||
@ -592,4 +597,40 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putVarLong($data->varlong1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getStructureSettings() : StructureSettings{
|
||||
$result = new StructureSettings();
|
||||
|
||||
$result->paletteName = $this->getString();
|
||||
|
||||
$result->ignoreEntities = $this->getBool();
|
||||
$result->ignoreBlocks = $this->getBool();
|
||||
|
||||
$this->getBlockPosition($result->structureSizeX, $result->structureSizeY, $result->structureSizeZ);
|
||||
$this->getBlockPosition($result->structureOffsetX, $result->structureOffsetY, $result->structureOffsetZ);
|
||||
|
||||
$result->lastTouchedByPlayerID = $this->getEntityUniqueId();
|
||||
$result->rotation = $this->getByte();
|
||||
$result->mirror = $this->getByte();
|
||||
$result->integrityValue = $this->getFloat();
|
||||
$result->integritySeed = $this->getInt();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function putStructureSettings(StructureSettings $structureSettings) : void{
|
||||
$this->putString($structureSettings->paletteName);
|
||||
|
||||
$this->putBool($structureSettings->ignoreEntities);
|
||||
$this->putBool($structureSettings->ignoreBlocks);
|
||||
|
||||
$this->putBlockPosition($structureSettings->structureSizeX, $structureSettings->structureSizeY, $structureSettings->structureSizeZ);
|
||||
$this->putBlockPosition($structureSettings->structureOffsetX, $structureSettings->structureOffsetY, $structureSettings->structureOffsetZ);
|
||||
|
||||
$this->putEntityUniqueId($structureSettings->lastTouchedByPlayerID);
|
||||
$this->putByte($structureSettings->rotation);
|
||||
$this->putByte($structureSettings->mirror);
|
||||
$this->putFloat($structureSettings->integrityValue);
|
||||
$this->putInt($structureSettings->integritySeed);
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,6 @@ use function implode;
|
||||
use function json_decode;
|
||||
use function json_last_error_msg;
|
||||
use function preg_match;
|
||||
use function preg_split;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function trim;
|
||||
@ -270,16 +269,31 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
*/
|
||||
private static function stupid_json_decode(string $json, bool $assoc = false){
|
||||
if(preg_match('/^\[(.+)\]$/s', $json, $matches) > 0){
|
||||
$parts = preg_split('/(?:"(?:\\"|[^"])*"|)\K(,)/', $matches[1]); //Splits on commas not inside quotes, ignoring escaped quotes
|
||||
foreach($parts as $k => $part){
|
||||
$part = trim($part);
|
||||
if($part === ""){
|
||||
$part = "\"\"";
|
||||
$raw = $matches[1];
|
||||
$lastComma = -1;
|
||||
$newParts = [];
|
||||
$quoteType = null;
|
||||
for($i = 0, $len = strlen($raw); $i <= $len; ++$i){
|
||||
if($i === $len or ($raw[$i] === "," and $quoteType === null)){
|
||||
$part = substr($raw, $lastComma + 1, $i - ($lastComma + 1));
|
||||
if(trim($part) === ""){ //regular parts will have quotes or something else that makes them non-empty
|
||||
$part = '""';
|
||||
}
|
||||
$newParts[] = $part;
|
||||
$lastComma = $i;
|
||||
}elseif($raw[$i] === '"'){
|
||||
if($quoteType === null){
|
||||
$quoteType = $raw[$i];
|
||||
}elseif($raw[$i] === $quoteType){
|
||||
for($backslashes = 0; $backslashes < $i && $raw[$i - $backslashes - 1] === "\\"; ++$backslashes){}
|
||||
if(($backslashes % 2) === 0){ //unescaped quote
|
||||
$quoteType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$parts[$k] = $part;
|
||||
}
|
||||
|
||||
$fixed = "[" . implode(",", $parts) . "]";
|
||||
$fixed = "[" . implode(",", $newParts) . "]";
|
||||
if(($ret = json_decode($fixed, $assoc)) === null){
|
||||
throw new \InvalidArgumentException("Failed to fix JSON: " . json_last_error_msg() . "(original: $json, modified: $fixed)");
|
||||
}
|
||||
|
@ -29,11 +29,7 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandData;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandParameter;
|
||||
use function array_flip;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_search;
|
||||
use function array_values;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
use function count;
|
||||
use function dechex;
|
||||
|
||||
@ -83,30 +79,6 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
*/
|
||||
public const ARG_FLAG_POSTFIX = 0x1000000;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* A list of every single enum value for every single command in the packet, including alias names.
|
||||
*/
|
||||
public $enumValues = [];
|
||||
/** @var int */
|
||||
private $enumValuesCount = 0;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* A list of argument postfixes. Used for the /xp command's <int>L.
|
||||
*/
|
||||
public $postfixes = [];
|
||||
|
||||
/**
|
||||
* @var CommandEnum[]
|
||||
* List of command enums, from command aliases to argument enums.
|
||||
*/
|
||||
public $enums = [];
|
||||
/**
|
||||
* @var int[] string => int map of enum name to index
|
||||
*/
|
||||
private $enumMap = [];
|
||||
|
||||
/**
|
||||
* @var CommandData[]
|
||||
* List of command data, including name, description, alias indexes and parameters.
|
||||
@ -121,20 +93,26 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
public $softEnums = [];
|
||||
|
||||
protected function decodePayload(){
|
||||
for($i = 0, $this->enumValuesCount = $this->getUnsignedVarInt(); $i < $this->enumValuesCount; ++$i){
|
||||
$this->enumValues[] = $this->getString();
|
||||
/** @var string[] $enumValues */
|
||||
$enumValues = [];
|
||||
for($i = 0, $enumValuesCount = $this->getUnsignedVarInt(); $i < $enumValuesCount; ++$i){
|
||||
$enumValues[] = $this->getString();
|
||||
}
|
||||
|
||||
/** @var string[] $postfixes */
|
||||
$postfixes = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$postfixes[] = $this->getString();
|
||||
}
|
||||
|
||||
/** @var CommandEnum[] $enums */
|
||||
$enums = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$enums[] = $this->getEnum($enumValues);
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->postfixes[] = $this->getString();
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->enums[] = $this->getEnum();
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->commandData[] = $this->getCommandData();
|
||||
$this->commandData[] = $this->getCommandData($enums, $postfixes);
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
@ -142,17 +120,26 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEnum() : CommandEnum{
|
||||
/**
|
||||
* @param string[] $enumValueList
|
||||
*
|
||||
* @return CommandEnum
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
protected function getEnum(array $enumValueList) : CommandEnum{
|
||||
$retval = new CommandEnum();
|
||||
$retval->enumName = $this->getString();
|
||||
|
||||
$listSize = count($enumValueList);
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$index = $this->getEnumValueIndex();
|
||||
if(!isset($this->enumValues[$index])){
|
||||
$index = $this->getEnumValueIndex($listSize);
|
||||
if(!isset($enumValueList[$index])){
|
||||
throw new \UnexpectedValueException("Invalid enum value index $index");
|
||||
}
|
||||
//Get the enum value from the initial pile of mess
|
||||
$retval->enumValues[] = $this->enumValues[$index];
|
||||
$retval->enumValues[] = $enumValueList[$index];
|
||||
}
|
||||
|
||||
return $retval;
|
||||
@ -170,17 +157,21 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
return $retval;
|
||||
}
|
||||
|
||||
protected function putEnum(CommandEnum $enum){
|
||||
/**
|
||||
* @param CommandEnum $enum
|
||||
* @param int[] $enumValueMap string enum name -> int index
|
||||
*/
|
||||
protected function putEnum(CommandEnum $enum, array $enumValueMap) : void{
|
||||
$this->putString($enum->enumName);
|
||||
|
||||
$this->putUnsignedVarInt(count($enum->enumValues));
|
||||
$listSize = count($enumValueMap);
|
||||
foreach($enum->enumValues as $value){
|
||||
//Dumb bruteforce search. I hate this packet.
|
||||
$index = array_search($value, $this->enumValues, true);
|
||||
if($index === false){
|
||||
$index = $enumValueMap[$value] ?? -1;
|
||||
if($index === -1){
|
||||
throw new \InvalidStateException("Enum value '$value' not found");
|
||||
}
|
||||
$this->putEnumValueIndex($index);
|
||||
$this->putEnumValueIndex($index, $listSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,51 +184,66 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEnumValueIndex() : int{
|
||||
if($this->enumValuesCount < 256){
|
||||
/**
|
||||
* @param int $valueCount
|
||||
*
|
||||
* @return int
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
protected function getEnumValueIndex(int $valueCount) : int{
|
||||
if($valueCount < 256){
|
||||
return $this->getByte();
|
||||
}elseif($this->enumValuesCount < 65536){
|
||||
}elseif($valueCount < 65536){
|
||||
return $this->getLShort();
|
||||
}else{
|
||||
return $this->getLInt();
|
||||
}
|
||||
}
|
||||
|
||||
protected function putEnumValueIndex(int $index){
|
||||
if($this->enumValuesCount < 256){
|
||||
protected function putEnumValueIndex(int $index, int $valueCount) : void{
|
||||
if($valueCount < 256){
|
||||
$this->putByte($index);
|
||||
}elseif($this->enumValuesCount < 65536){
|
||||
}elseif($valueCount < 65536){
|
||||
$this->putLShort($index);
|
||||
}else{
|
||||
$this->putLInt($index);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCommandData() : CommandData{
|
||||
/**
|
||||
* @param CommandEnum[] $enums
|
||||
* @param string[] $postfixes
|
||||
*
|
||||
* @return CommandData
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
protected function getCommandData(array $enums, array $postfixes) : CommandData{
|
||||
$retval = new CommandData();
|
||||
$retval->commandName = $this->getString();
|
||||
$retval->commandDescription = $this->getString();
|
||||
$retval->flags = $this->getByte();
|
||||
$retval->permission = $this->getByte();
|
||||
$retval->aliases = $this->enums[$this->getLInt()] ?? null;
|
||||
$retval->aliases = $enums[$this->getLInt()] ?? null;
|
||||
|
||||
for($overloadIndex = 0, $overloadCount = $this->getUnsignedVarInt(); $overloadIndex < $overloadCount; ++$overloadIndex){
|
||||
$retval->overloads[$overloadIndex] = [];
|
||||
for($paramIndex = 0, $paramCount = $this->getUnsignedVarInt(); $paramIndex < $paramCount; ++$paramIndex){
|
||||
$parameter = new CommandParameter();
|
||||
$parameter->paramName = $this->getString();
|
||||
$parameter->paramType = $this->getLInt();
|
||||
$parameter->isOptional = $this->getBool();
|
||||
$parameter->byte1 = $this->getByte();
|
||||
$parameter->flags = $this->getByte();
|
||||
|
||||
if($parameter->paramType & self::ARG_FLAG_ENUM){
|
||||
$index = ($parameter->paramType & 0xffff);
|
||||
$parameter->enum = $this->enums[$index] ?? null;
|
||||
$parameter->enum = $enums[$index] ?? null;
|
||||
if($parameter->enum === null){
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: expected enum at $index, but got none");
|
||||
}
|
||||
}elseif($parameter->paramType & self::ARG_FLAG_POSTFIX){
|
||||
$index = ($parameter->paramType & 0xffff);
|
||||
$parameter->postfix = $this->postfixes[$index] ?? null;
|
||||
$parameter->postfix = $postfixes[$index] ?? null;
|
||||
if($parameter->postfix === null){
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: expected postfix at $index, but got none");
|
||||
}
|
||||
@ -252,14 +258,19 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
return $retval;
|
||||
}
|
||||
|
||||
protected function putCommandData(CommandData $data){
|
||||
/**
|
||||
* @param CommandData $data
|
||||
* @param int[] $enumIndexes string enum name -> int index
|
||||
* @param int[] $postfixIndexes
|
||||
*/
|
||||
protected function putCommandData(CommandData $data, array $enumIndexes, array $postfixIndexes) : void{
|
||||
$this->putString($data->commandName);
|
||||
$this->putString($data->commandDescription);
|
||||
$this->putByte($data->flags);
|
||||
$this->putByte($data->permission);
|
||||
|
||||
if($data->aliases !== null){
|
||||
$this->putLInt($this->enumMap[$data->aliases->enumName] ?? -1);
|
||||
$this->putLInt($enumIndexes[$data->aliases->enumName] ?? -1);
|
||||
}else{
|
||||
$this->putLInt(-1);
|
||||
}
|
||||
@ -272,10 +283,10 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
$this->putString($parameter->paramName);
|
||||
|
||||
if($parameter->enum !== null){
|
||||
$type = self::ARG_FLAG_ENUM | self::ARG_FLAG_VALID | ($this->enumMap[$parameter->enum->enumName] ?? -1);
|
||||
$type = self::ARG_FLAG_ENUM | self::ARG_FLAG_VALID | ($enumIndexes[$parameter->enum->enumName] ?? -1);
|
||||
}elseif($parameter->postfix !== null){
|
||||
$key = array_search($parameter->postfix, $this->postfixes, true);
|
||||
if($key === false){
|
||||
$key = $postfixIndexes[$parameter->postfix] ?? -1;
|
||||
if($key === -1){
|
||||
throw new \InvalidStateException("Postfix '$parameter->postfix' not in postfixes array");
|
||||
}
|
||||
$type = self::ARG_FLAG_POSTFIX | $key;
|
||||
@ -285,12 +296,12 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
|
||||
$this->putLInt($type);
|
||||
$this->putBool($parameter->isOptional);
|
||||
$this->putByte($parameter->byte1);
|
||||
$this->putByte($parameter->flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function argTypeToString(int $argtype) : string{
|
||||
private function argTypeToString(int $argtype, array $postfixes) : string{
|
||||
if($argtype & self::ARG_FLAG_VALID){
|
||||
if($argtype & self::ARG_FLAG_ENUM){
|
||||
return "stringenum (" . ($argtype & 0xffff) . ")";
|
||||
@ -319,7 +330,7 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
return "command";
|
||||
}
|
||||
}elseif($argtype & self::ARG_FLAG_POSTFIX){
|
||||
$postfix = $this->postfixes[$argtype & 0xffff];
|
||||
$postfix = $postfixes[$argtype & 0xffff];
|
||||
|
||||
return "int (postfix $postfix)";
|
||||
}else{
|
||||
@ -330,60 +341,62 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$enumValuesMap = [];
|
||||
$postfixesMap = [];
|
||||
$enumMap = [];
|
||||
/** @var int[] $enumValueIndexes */
|
||||
$enumValueIndexes = [];
|
||||
/** @var int[] $postfixIndexes */
|
||||
$postfixIndexes = [];
|
||||
/** @var int[] $enumIndexes */
|
||||
$enumIndexes = [];
|
||||
/** @var CommandEnum[] $enums */
|
||||
$enums = [];
|
||||
foreach($this->commandData as $commandData){
|
||||
if($commandData->aliases !== null){
|
||||
$enumMap[$commandData->aliases->enumName] = $commandData->aliases;
|
||||
if(!isset($enumIndexes[$commandData->aliases->enumName])){
|
||||
$enums[$enumIndexes[$commandData->aliases->enumName] = count($enumIndexes)] = $commandData->aliases;
|
||||
}
|
||||
|
||||
foreach($commandData->aliases->enumValues as $str){
|
||||
$enumValuesMap[$str] = true;
|
||||
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); //latest index
|
||||
}
|
||||
}
|
||||
|
||||
/** @var CommandParameter[] $overload */
|
||||
foreach($commandData->overloads as $overload){
|
||||
/**
|
||||
* @var CommandParameter[] $overload
|
||||
* @var CommandParameter $parameter
|
||||
*/
|
||||
/** @var CommandParameter $parameter */
|
||||
foreach($overload as $parameter){
|
||||
if($parameter->enum !== null){
|
||||
$enumMap[$parameter->enum->enumName] = $parameter->enum;
|
||||
if(!isset($enumIndexes[$parameter->enum->enumName])){
|
||||
$enums[$enumIndexes[$parameter->enum->enumName] = count($enumIndexes)] = $parameter->enum;
|
||||
}
|
||||
foreach($parameter->enum->enumValues as $str){
|
||||
$enumValuesMap[$str] = true;
|
||||
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
if($parameter->postfix !== null){
|
||||
$postfixesMap[$parameter->postfix] = true;
|
||||
$postfixIndexes[$parameter->postfix] = $postfixIndexes[$parameter->postfix] ?? count($postfixIndexes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->enumValues = array_map('\strval', array_keys($enumValuesMap)); //stupid PHP key casting D:
|
||||
$this->putUnsignedVarInt($this->enumValuesCount = count($this->enumValues));
|
||||
foreach($this->enumValues as $enumValue){
|
||||
$this->putString($enumValue);
|
||||
$this->putUnsignedVarInt(count($enumValueIndexes));
|
||||
foreach($enumValueIndexes as $enumValue => $index){
|
||||
$this->putString((string) $enumValue); //stupid PHP key casting D:
|
||||
}
|
||||
|
||||
$this->postfixes = array_map('\strval', array_keys($postfixesMap));
|
||||
$this->putUnsignedVarInt(count($this->postfixes));
|
||||
foreach($this->postfixes as $postfix){
|
||||
$this->putString($postfix);
|
||||
$this->putUnsignedVarInt(count($postfixIndexes));
|
||||
foreach($postfixIndexes as $postfix => $index){
|
||||
$this->putString((string) $postfix); //stupid PHP key casting D:
|
||||
}
|
||||
|
||||
$this->enums = array_values($enumMap);
|
||||
$this->enumMap = array_flip(array_keys($enumMap));
|
||||
$this->putUnsignedVarInt(count($this->enums));
|
||||
foreach($this->enums as $enum){
|
||||
$this->putEnum($enum);
|
||||
$this->putUnsignedVarInt(count($enums));
|
||||
foreach($enums as $enum){
|
||||
$this->putEnum($enum, $enumValueIndexes);
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt(count($this->commandData));
|
||||
foreach($this->commandData as $data){
|
||||
$this->putCommandData($data);
|
||||
$this->putCommandData($data, $enumIndexes, $postfixIndexes);
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt(count($this->softEnums));
|
||||
|
@ -39,9 +39,9 @@ class BossEventPacket extends DataPacket{
|
||||
public const TYPE_HIDE = 2;
|
||||
/* C2S: Unregisters a player from a boss fight. */
|
||||
public const TYPE_UNREGISTER_PLAYER = 3;
|
||||
/* S2C: Appears not to be implemented. Currently bar percentage only appears to change in response to the target entity's health. */
|
||||
/* S2C: Sets the bar percentage. */
|
||||
public const TYPE_HEALTH_PERCENT = 4;
|
||||
/* S2C: Also appears to not be implemented. Title client-side sticks as the target entity's nametag, or their entity type name if not set. */
|
||||
/* S2C: Sets title of the bar. */
|
||||
public const TYPE_TITLE = 5;
|
||||
/* S2C: Not sure on this. Includes color and overlay fields, plus an unknown short. TODO: check this */
|
||||
public const TYPE_UNKNOWN_6 = 6;
|
||||
|
@ -42,7 +42,7 @@ class ClientCacheMissResponsePacket extends DataPacket/* implements ClientboundP
|
||||
*/
|
||||
public static function create(array $blobs) : self{
|
||||
//type check
|
||||
(static function(ChunkCacheBlob ...$blobs){})($blobs);
|
||||
(static function(ChunkCacheBlob ...$blobs){})(...$blobs);
|
||||
|
||||
$result = new self;
|
||||
$result->blobs = $blobs;
|
||||
|
@ -140,7 +140,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
}
|
||||
$this->decodedEntries[] = $entry;
|
||||
}
|
||||
$this->getBool(); //cleanRecipes
|
||||
$this->cleanRecipes = $this->getBool();
|
||||
}
|
||||
|
||||
private static function writeEntry($entry, NetworkBinaryStream $stream, int $pos){
|
||||
|
@ -135,7 +135,7 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
abstract public function handle(NetworkSession $session) : bool;
|
||||
|
||||
public function clean(){
|
||||
$this->buffer = null;
|
||||
$this->buffer = "";
|
||||
$this->isEncoded = false;
|
||||
$this->offset = 0;
|
||||
return $this;
|
||||
@ -143,7 +143,7 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
|
||||
public function __debugInfo(){
|
||||
$data = [];
|
||||
foreach($this as $k => $v){
|
||||
foreach((array) $this as $k => $v){
|
||||
if($k === "buffer" and is_string($v)){
|
||||
$data[$k] = bin2hex($v);
|
||||
}elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){
|
||||
|
@ -43,6 +43,11 @@ class EventPacket extends DataPacket{
|
||||
public const TYPE_PATTERN_REMOVED = 10; //???
|
||||
public const TYPE_COMMANED_EXECUTED = 11;
|
||||
public const TYPE_FISH_BUCKETED = 12;
|
||||
public const TYPE_MOB_BORN = 13;
|
||||
public const TYPE_PET_DIED = 14;
|
||||
public const TYPE_CAULDRON_BLOCK_USED = 15;
|
||||
public const TYPE_COMPOSTER_BLOCK_USED = 16;
|
||||
public const TYPE_BELL_BLOCK_USED = 17;
|
||||
|
||||
/** @var int */
|
||||
public $playerRuntimeId;
|
||||
|
@ -57,7 +57,7 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
}
|
||||
|
||||
public static function withCache(int $chunkX, int $chunkZ, int $subChunkCount, array $usedBlobHashes, string $extraPayload) : self{
|
||||
(static function(int ...$hashes){})($usedBlobHashes);
|
||||
(static function(int ...$hashes){})(...$usedBlobHashes);
|
||||
$result = new self;
|
||||
$result->chunkX = $chunkX;
|
||||
$result->chunkZ = $chunkZ;
|
||||
|
@ -26,16 +26,39 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
|
||||
class StructureTemplateDataExportRequestPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET;
|
||||
|
||||
public const TYPE_ALWAYS_LOAD = 1;
|
||||
public const TYPE_CREATE_AND_LOAD = 2;
|
||||
|
||||
/** @var string */
|
||||
public $structureTemplateName;
|
||||
/** @var int */
|
||||
public $structureBlockX;
|
||||
/** @var int */
|
||||
public $structureBlockY;
|
||||
/** @var int */
|
||||
public $structureBlockZ;
|
||||
/** @var StructureSettings */
|
||||
public $structureSettings;
|
||||
/** @var int */
|
||||
public $structureTemplateResponseType;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
//TODO
|
||||
$this->structureTemplateName = $this->getString();
|
||||
$this->getBlockPosition($this->structureBlockX, $this->structureBlockY, $this->structureBlockZ);
|
||||
$this->structureSettings = $this->getStructureSettings();
|
||||
$this->structureTemplateResponseType = $this->getByte();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
//TODO
|
||||
$this->putString($this->structureTemplateName);
|
||||
$this->putBlockPosition($this->structureBlockX, $this->structureBlockY, $this->structureBlockZ);
|
||||
$this->putStructureSettings($this->structureSettings);
|
||||
$this->putByte($this->structureTemplateResponseType);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
|
@ -30,12 +30,24 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
class StructureTemplateDataExportResponsePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET;
|
||||
|
||||
/** @var string */
|
||||
public $structureTemplateName;
|
||||
/** @var string|null */
|
||||
public $namedtag;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
//TODO
|
||||
$this->structureTemplateName = $this->getString();
|
||||
if($this->getBool()){
|
||||
$this->namedtag = $this->getRemaining();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
//TODO
|
||||
$this->putString($this->structureTemplateName);
|
||||
$this->putBool($this->namedtag !== null);
|
||||
if($this->namedtag !== null){
|
||||
$this->put($this->namedtag);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
|
@ -31,7 +31,7 @@ class CommandParameter{
|
||||
/** @var bool */
|
||||
public $isOptional;
|
||||
/** @var int */
|
||||
public $byte1 = 0; //unknown, always zero except for in /gamerule command
|
||||
public $flags = 0; //shows enum name if 1, always zero except for in /gamerule command
|
||||
/** @var CommandEnum|null */
|
||||
public $enum;
|
||||
/** @var string|null */
|
||||
|
@ -40,8 +40,8 @@ final class RuntimeBlockMapping{
|
||||
private static $legacyToRuntimeMap = [];
|
||||
/** @var int[] */
|
||||
private static $runtimeToLegacyMap = [];
|
||||
/** @var mixed[] */
|
||||
private static $bedrockKnownStates;
|
||||
/** @var mixed[]|null */
|
||||
private static $bedrockKnownStates = null;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
@ -77,6 +77,12 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
}
|
||||
|
||||
private static function lazyInit() : void{
|
||||
if(self::$bedrockKnownStates === null){
|
||||
self::init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomizes the order of the runtimeID table to prevent plugins relying on them.
|
||||
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
|
||||
@ -101,6 +107,7 @@ final class RuntimeBlockMapping{
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
self::lazyInit();
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
@ -115,6 +122,7 @@ final class RuntimeBlockMapping{
|
||||
* @return int[] [id, meta]
|
||||
*/
|
||||
public static function fromStaticRuntimeId(int $runtimeId) : array{
|
||||
self::lazyInit();
|
||||
$v = self::$runtimeToLegacyMap[$runtimeId];
|
||||
return [$v >> 4, $v & 0xf];
|
||||
}
|
||||
@ -128,7 +136,7 @@ final class RuntimeBlockMapping{
|
||||
* @return array
|
||||
*/
|
||||
public static function getBedrockKnownStates() : array{
|
||||
self::lazyInit();
|
||||
return self::$bedrockKnownStates;
|
||||
}
|
||||
}
|
||||
RuntimeBlockMapping::init();
|
||||
|
@ -0,0 +1,55 @@
|
||||
<?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\network\mcpe\protocol\types;
|
||||
|
||||
class StructureSettings{
|
||||
/** @var string */
|
||||
public $paletteName;
|
||||
/** @var bool */
|
||||
public $ignoreEntities;
|
||||
/** @var bool */
|
||||
public $ignoreBlocks;
|
||||
/** @var int */
|
||||
public $structureSizeX;
|
||||
/** @var int */
|
||||
public $structureSizeY;
|
||||
/** @var int */
|
||||
public $structureSizeZ;
|
||||
/** @var int */
|
||||
public $structureOffsetX;
|
||||
/** @var int */
|
||||
public $structureOffsetY;
|
||||
/** @var int */
|
||||
public $structureOffsetZ;
|
||||
/** @var int */
|
||||
public $lastTouchedByPlayerID;
|
||||
/** @var int */
|
||||
public $rotation;
|
||||
/** @var int */
|
||||
public $mirror;
|
||||
/** @var float */
|
||||
public $integrityValue;
|
||||
/** @var int */
|
||||
public $integritySeed;
|
||||
}
|
@ -50,7 +50,7 @@ abstract class UPnP{
|
||||
$com = new \COM("HNetCfg.NATUPnP");
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
|
||||
if($com === false or !is_object($com->StaticPortMappingCollection)){
|
||||
if(!is_object($com->StaticPortMappingCollection)){
|
||||
throw new \RuntimeException("Failed to portforward using UPnP. Ensure that network discovery is enabled in Control Panel.");
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ abstract class UPnP{
|
||||
/** @noinspection PhpUndefinedClassInspection */
|
||||
$com = new \COM("HNetCfg.NATUPnP");
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
if($com === false or !is_object($com->StaticPortMappingCollection)){
|
||||
if(!is_object($com->StaticPortMappingCollection)){
|
||||
return false;
|
||||
}
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
|
@ -112,12 +112,7 @@ class Permission{
|
||||
$desc = null;
|
||||
$children = [];
|
||||
if(isset($data["default"])){
|
||||
$value = Permission::getByName($data["default"]);
|
||||
if($value !== null){
|
||||
$default = $value;
|
||||
}else{
|
||||
throw new \InvalidStateException("'default' key contained unknown value");
|
||||
}
|
||||
$default = Permission::getByName($data["default"]);
|
||||
}
|
||||
|
||||
if(isset($data["children"])){
|
||||
|
@ -733,7 +733,7 @@ class PluginManager{
|
||||
$eventClass = $parameters[0]->getClass();
|
||||
}catch(\ReflectionException $e){ //class doesn't exist
|
||||
if(isset($tags["softDepend"]) && !isset($this->plugins[$tags["softDepend"]])){
|
||||
$this->server->getLogger()->debug("Not registering @softDepend listener " . Utils::getNiceClosureName($handlerClosure) . "(" . $parameters[0]->getType()->getName() . ") because plugin \"" . $tags["softDepend"] . "\" not found");
|
||||
$this->server->getLogger()->debug("Not registering @softDepend listener " . Utils::getNiceClosureName($handlerClosure) . "() because plugin \"" . $tags["softDepend"] . "\" not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ interface ResourcePack{
|
||||
* @param int $length Maximum length of data to return.
|
||||
*
|
||||
* @return string byte-array
|
||||
* @throws \InvalidArgumentException if the chunk does not exist
|
||||
*/
|
||||
public function getPackChunk(int $start, int $length) : string;
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ class ZippedResourcePack implements ResourcePack{
|
||||
|
||||
/**
|
||||
* @param string $zipPath Path to the resource pack zip
|
||||
* @throws ResourcePackException
|
||||
*/
|
||||
public function __construct(string $zipPath){
|
||||
$this->path = $zipPath;
|
||||
@ -104,7 +105,9 @@ class ZippedResourcePack implements ResourcePack{
|
||||
}catch(\RuntimeException $e){
|
||||
throw new ResourcePackException("Failed to parse manifest.json: " . $e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if(!($manifest instanceof \stdClass)){
|
||||
throw new ResourcePackException("manifest.json should contain a JSON object, not " . gettype($manifest));
|
||||
}
|
||||
if(!self::verifyManifest($manifest)){
|
||||
throw new ResourcePackException("manifest.json is missing required fields");
|
||||
}
|
||||
@ -148,7 +151,7 @@ class ZippedResourcePack implements ResourcePack{
|
||||
public function getPackChunk(int $start, int $length) : string{
|
||||
fseek($this->fileResource, $start);
|
||||
if(feof($this->fileResource)){
|
||||
throw new \RuntimeException("Requested a resource pack chunk with invalid start offset");
|
||||
throw new \InvalidArgumentException("Requested a resource pack chunk with invalid start offset");
|
||||
}
|
||||
return fread($this->fileResource, $length);
|
||||
}
|
||||
|
@ -263,6 +263,7 @@ class AsyncPool{
|
||||
while(($task = $worker->unstack()) !== null){
|
||||
//cancelRun() is not strictly necessary here, but it might be used to inform plugins of the task state
|
||||
//(i.e. it never executed).
|
||||
assert($task instanceof AsyncTask);
|
||||
$task->cancelRun();
|
||||
$this->removeTask($task, true);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ use pocketmine\utils\Utils;
|
||||
|
||||
abstract class Task{
|
||||
|
||||
/** @var TaskHandler */
|
||||
/** @var TaskHandler|null */
|
||||
private $taskHandler = null;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
|
||||
/** @var ChestInventory */
|
||||
protected $inventory;
|
||||
/** @var DoubleChestInventory */
|
||||
/** @var DoubleChestInventory|null */
|
||||
protected $doubleInventory = null;
|
||||
|
||||
/** @var int|null */
|
||||
|
@ -210,7 +210,7 @@ class Internet{
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT_MS => (int) ($timeout * 1000),
|
||||
CURLOPT_TIMEOUT_MS => (int) ($timeout * 1000),
|
||||
CURLOPT_HTTPHEADER => array_merge(["User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 " . \pocketmine\NAME], $extraHeaders),
|
||||
CURLOPT_HTTPHEADER => array_merge(["User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 " . \pocketmine\NAME . "/" . \pocketmine\VERSION], $extraHeaders),
|
||||
CURLOPT_HEADER => true
|
||||
]);
|
||||
try{
|
||||
|
@ -61,10 +61,10 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
/** @var \Threaded */
|
||||
protected $logStream;
|
||||
/** @var bool */
|
||||
protected $shutdown;
|
||||
protected $shutdown = false;
|
||||
/** @var bool */
|
||||
protected $logDebug;
|
||||
/** @var MainLogger */
|
||||
/** @var MainLogger|null */
|
||||
public static $logger = null;
|
||||
/** @var bool */
|
||||
private $syncFlush = false;
|
||||
@ -344,7 +344,6 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
}
|
||||
|
||||
public function run(){
|
||||
$this->shutdown = false;
|
||||
$logResource = fopen($this->logFile, "ab");
|
||||
if(!is_resource($logResource)){
|
||||
throw new \RuntimeException("Couldn't open log file");
|
||||
|
@ -28,6 +28,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\wizard;
|
||||
|
||||
use pocketmine\lang\BaseLang;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Config;
|
||||
use pocketmine\utils\Internet;
|
||||
use pocketmine\utils\InternetException;
|
||||
@ -45,7 +46,7 @@ class SetupWizard{
|
||||
public const DEFAULT_NAME = \pocketmine\NAME . " Server";
|
||||
public const DEFAULT_PORT = 19132;
|
||||
public const DEFAULT_PLAYERS = 20;
|
||||
public const DEFAULT_GAMEMODE = 0;
|
||||
public const DEFAULT_GAMEMODE = Player::SURVIVAL;
|
||||
|
||||
/** @var BaseLang */
|
||||
private $lang;
|
||||
@ -162,7 +163,7 @@ LICENSE;
|
||||
|
||||
$this->message($this->lang->get("spawn_protection_info"));
|
||||
|
||||
if(strtolower($this->getInput($this->lang->get("spawn_protection"), "y", "Y/n")) === "n"){
|
||||
if(strtolower($this->getInput($this->lang->get("spawn_protection"), "n", "y/N")) === "n"){
|
||||
$config->set("spawn-protection", -1);
|
||||
}else{
|
||||
$config->set("spawn-protection", 16);
|
||||
|
24
tests/phpstan/bootstrap.php
Normal file
24
tests/phpstan/bootstrap.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?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);
|
||||
|
||||
define('pocketmine\_PHPSTAN_ANALYSIS', true);
|
80
tests/phpstan/configs/gc-hacks.neon
Normal file
80
tests/phpstan/configs/gc-hacks.neon
Normal file
@ -0,0 +1,80 @@
|
||||
#cyclic refs in lots of objects prevent GC, causing increased memory pressure
|
||||
#the proper fix is to not have cycles, but in the meantime we have these hacks
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Property pocketmine\\\\Player\\:\\:\\$sessionAdapter \\(pocketmine\\\\network\\\\mcpe\\\\PlayerNetworkSessionAdapter\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/Player.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\Player\\:\\:\\$cursorInventory \\(pocketmine\\\\inventory\\\\PlayerCursorInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/Player.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\Player\\:\\:\\$craftingGrid \\(pocketmine\\\\inventory\\\\CraftingGrid\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/Player.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\Player\\:\\:\\$perm \\(pocketmine\\\\permission\\\\PermissibleBase\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/Player.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\entity\\\\Entity\\:\\:\\$namedtag \\(pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Entity.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\entity\\\\Human\\:\\:\\$inventory \\(pocketmine\\\\inventory\\\\PlayerInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Human.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\entity\\\\Human\\:\\:\\$enderChestInventory \\(pocketmine\\\\inventory\\\\EnderChestInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Human.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\entity\\\\Living\\:\\:\\$armorInventory \\(pocketmine\\\\inventory\\\\ArmorInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Living.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\inventory\\\\DoubleChestInventory\\:\\:\\$left \\(pocketmine\\\\inventory\\\\ChestInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/inventory/DoubleChestInventory.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\inventory\\\\DoubleChestInventory\\:\\:\\$right \\(pocketmine\\\\inventory\\\\ChestInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/inventory/DoubleChestInventory.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\level\\\\Level\\:\\:\\$provider \\(pocketmine\\\\level\\\\format\\\\io\\\\LevelProvider\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/Level.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\level\\\\Level\\:\\:\\$blockMetadata \\(pocketmine\\\\metadata\\\\BlockMetadataStore\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/Level.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\level\\\\Level\\:\\:\\$temporalPosition \\(pocketmine\\\\level\\\\Position\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/Level.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\tile\\\\Chest\\:\\:\\$inventory \\(pocketmine\\\\inventory\\\\ChestInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\tile\\\\Furnace\\:\\:\\$inventory \\(pocketmine\\\\inventory\\\\FurnaceInventory\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/tile/Furnace.php
|
||||
|
12
tests/phpstan/configs/optional-com-dotnet.neon
Normal file
12
tests/phpstan/configs/optional-com-dotnet.neon
Normal file
@ -0,0 +1,12 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Instantiated class COM not found\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/network/upnp/UPnP.php
|
||||
|
||||
-
|
||||
message: "#^Access to property \\$StaticPortMappingCollection on an unknown class COM\\.$#"
|
||||
count: 4
|
||||
path: ../../../src/pocketmine/network/upnp/UPnP.php
|
||||
|
30
tests/phpstan/configs/optional-leveldb.neon
Normal file
30
tests/phpstan/configs/optional-leveldb.neon
Normal file
@ -0,0 +1,30 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Used constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Instantiated class LevelDB not found\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Return typehint of method pocketmine\\\\level\\\\format\\\\io\\\\leveldb\\\\LevelDB\\:\\:createDB\\(\\) has invalid type LevelDB\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Return typehint of method pocketmine\\\\level\\\\format\\\\io\\\\leveldb\\\\LevelDB\\:\\:getDatabase\\(\\) has invalid type LevelDB\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\level\\\\format\\\\io\\\\leveldb\\\\LevelDB\\:\\:\\$db has unknown class LevelDB as its type\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Call to method (get|put|delete|close)\\(\\) on an unknown class LevelDB\\.$#"
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
33
tests/phpstan/configs/phpstan-bugs.neon
Normal file
33
tests/phpstan/configs/phpstan-bugs.neon
Normal file
@ -0,0 +1,33 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^PHPDoc tag @param has invalid value \\(.+\\)\\: Unexpected token \"&\", expected variable at offset \\d+$#"
|
||||
path: ../../../src
|
||||
|
||||
-
|
||||
message: "#^Default value of the parameter \\#\\d+ \\$[A-Za-z\\d_]+ \\(\\-?\\d+\\) of method .+\\(\\) is incompatible with type float\\.$#"
|
||||
path: ../../../src
|
||||
|
||||
-
|
||||
message: "#^Cannot access an offset on Threaded\\.$#"
|
||||
path: ../../../src
|
||||
|
||||
-
|
||||
message: "#^Cannot assign new offset to Threaded\\.$#"
|
||||
path: ../../../src
|
||||
|
||||
-
|
||||
message: "#^Offset string does not exist on array\\(\\)\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/pocketmine/MemoryManager.php
|
||||
|
||||
-
|
||||
message: "#^Offset \\(int\\|string\\) does not exist on array\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/MemoryManager.php
|
||||
|
||||
-
|
||||
message: "#^Array \\(array\\) does not accept key int\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
|
6
tests/phpstan/configs/pthreads-bugs.neon
Normal file
6
tests/phpstan/configs/pthreads-bugs.neon
Normal file
@ -0,0 +1,6 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Variable \\$GLOBALS in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
path: ../../../src/pocketmine/MemoryManager.php
|
||||
|
@ -26,6 +26,12 @@ namespace pocketmine\network\mcpe;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class StupidJsonDecodeTest extends TestCase{
|
||||
/** @var \Closure */
|
||||
private $stupidJsonDecodeFunc;
|
||||
|
||||
public function setUp() : void{
|
||||
$this->stupidJsonDecodeFunc = (new \ReflectionMethod(PlayerNetworkSessionAdapter::class, 'stupid_json_decode'))->getClosure();
|
||||
}
|
||||
|
||||
public function stupidJsonDecodeProvider() : array{
|
||||
return [
|
||||
@ -34,7 +40,10 @@ class StupidJsonDecodeTest extends TestCase{
|
||||
["false", false],
|
||||
["NULL", null],
|
||||
['["\",,\"word","a\",,\"word2",]', ['",,"word', 'a",,"word2', '']],
|
||||
['["\",,\"word","a\",,\"word2",""]', ['",,"word', 'a",,"word2', '']]
|
||||
['["\",,\"word","a\",,\"word2",""]', ['",,"word', 'a",,"word2', '']],
|
||||
['["Hello,, PocketMine"]', ['Hello,, PocketMine']],
|
||||
['[,]', ['', '']],
|
||||
['[]', []]
|
||||
];
|
||||
}
|
||||
|
||||
@ -47,10 +56,7 @@ class StupidJsonDecodeTest extends TestCase{
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function testStupidJsonDecode(string $brokenJson, $expect){
|
||||
$func = new \ReflectionMethod(PlayerNetworkSessionAdapter::class, 'stupid_json_decode');
|
||||
$func->setAccessible(true);
|
||||
|
||||
$decoded = $func->invoke(null, $brokenJson, true);
|
||||
$decoded = ($this->stupidJsonDecodeFunc)($brokenJson, true);
|
||||
self::assertEquals($expect, $decoded);
|
||||
}
|
||||
}
|
||||
|
Submodule tests/plugins/PocketMine-DevTools updated: c0f0f9383d...73c96dfdba
@ -21,9 +21,11 @@ if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#Run PHPUnit tests
|
||||
#7.5.12 introduces changes that set the build on fire because we don't ship libxml - TODO FIX
|
||||
curl https://phar.phpunit.de/phpunit-7.5.11.phar --silent --location -o phpunit.phar
|
||||
[ ! -f phpstan.phar ] && echo "Downloading PHPStan..." && curl -sSLO https://github.com/phpstan/phpstan/releases/download/0.12.0/phpstan.phar
|
||||
"$PHP_BINARY" phpstan.phar analyze --no-progress --memory-limit=2G || exit 1
|
||||
echo "PHPStan scan succeeded"
|
||||
|
||||
[ ! -f phpunit.phar ] && echo "Downloading PHPUnit..." && curl https://phar.phpunit.de/phpunit-7.phar --silent --location -o phpunit.phar
|
||||
"$PHP_BINARY" phpunit.phar --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit || exit 1
|
||||
|
||||
#Run-the-server tests
|
||||
|
Reference in New Issue
Block a user