mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 10:53:05 +00:00
Compare commits
457 Commits
Author | SHA1 | Date | |
---|---|---|---|
13f28d8454 | |||
5a97c378fc | |||
e5d62ec901 | |||
cfd975009e | |||
73257ffde7 | |||
8252bea699 | |||
ea935a1af5 | |||
e8a5fa8a37 | |||
db734675d8 | |||
6ede56015d | |||
5334099fbf | |||
82e9072223 | |||
2c11742f9e | |||
bd4a63b668 | |||
cd36af46bf | |||
aa7d55e21d | |||
31e8efa6d1 | |||
facca13139 | |||
fffeeddca6 | |||
e6ba3ce8a6 | |||
11cae2f0c0 | |||
f5a18df835 | |||
1cc7027f92 | |||
8776b71d63 | |||
0b9d0f3cdc | |||
e419d76367 | |||
36cde9f352 | |||
05c602a044 | |||
0db7e57a15 | |||
205e47c0c4 | |||
e328d00cca | |||
ccbcc14600 | |||
e544bc0d4b | |||
fd27227cc7 | |||
b42966f61b | |||
ca86ec2ec2 | |||
97b6183404 | |||
0587d03c03 | |||
c7f1b605f2 | |||
f069df65a8 | |||
1e624e7bb9 | |||
f16a530849 | |||
7137b8a8a4 | |||
ab57914322 | |||
260869c0d5 | |||
9135877da1 | |||
07cb603231 | |||
f59094e6d6 | |||
d8acae5495 | |||
239fe909be | |||
c22576a266 | |||
bac788fd00 | |||
f6d96c5827 | |||
b1458db47b | |||
2d2c9379cd | |||
3400771770 | |||
e12ecaf629 | |||
1303cbfe02 | |||
ad4a211cba | |||
d1e56c4611 | |||
d9bbab54f4 | |||
07cf4eb7a9 | |||
50a7663369 | |||
651ef500a3 | |||
30f2e75278 | |||
2cfc25b4f8 | |||
5bded9cff8 | |||
5816ff85ba | |||
8f7d8347ee | |||
3614d9a78d | |||
818d0e19ab | |||
17720041a3 | |||
c329ff7d4f | |||
8794292788 | |||
0a39e580e9 | |||
c4580dd56d | |||
fab81d28bc | |||
039478223e | |||
64b5db4bf2 | |||
c85f4256c7 | |||
1192b8bdf1 | |||
6dcd2a4ece | |||
3fff0a0656 | |||
e1e1bfa5e3 | |||
1eedac87b2 | |||
cda3e6f4dc | |||
e6a58e2690 | |||
27350c4673 | |||
0d5704b156 | |||
f355044626 | |||
4794ba236a | |||
6490a49c70 | |||
5cd7e11b29 | |||
08e3b8ffdc | |||
9232f4509c | |||
cef77907c6 | |||
06ec8b8397 | |||
ee08286eca | |||
a83211f96a | |||
0b3c4ee496 | |||
54de518634 | |||
a908197907 | |||
3e23a568ca | |||
dadc5c1b87 | |||
a37d740111 | |||
2de0ec02ba | |||
d83820477f | |||
8726604899 | |||
9cbe378e8c | |||
494660102e | |||
216138a37e | |||
b08c38f8f9 | |||
911b6feaf9 | |||
2cb6990698 | |||
f7d66613df | |||
95c32d26df | |||
9e1f6a2486 | |||
76994f15ac | |||
cf73d74bd0 | |||
37a8d95464 | |||
9a4b72add5 | |||
919534d978 | |||
cb598155a4 | |||
00888fdc55 | |||
77795ae3bc | |||
f39fc7e525 | |||
77f7595e0e | |||
e8d3a25028 | |||
1370930ea9 | |||
70c3008b7b | |||
46930b98b7 | |||
92be8c8ec0 | |||
62069bc7af | |||
26230c1f9b | |||
a9fafbc5eb | |||
b8778cb791 | |||
73c5fe5cf9 | |||
b3cfa5a3a0 | |||
6127a02a8b | |||
dbca36e5e2 | |||
1171bae691 | |||
494f8e8251 | |||
3c9af56e06 | |||
40a2211a5a | |||
0196aa21d7 | |||
833f3e574b | |||
a386ff8ce7 | |||
e6c3b0fc0d | |||
9568364277 | |||
73d4ff6b52 | |||
7e98aa1497 | |||
f00c69c513 | |||
50a4c42f3f | |||
6b61efcfc8 | |||
1a99938e4b | |||
a4d68fb32b | |||
5682cc8d53 | |||
4eb59c0372 | |||
3486db3f1b | |||
733d530ed0 | |||
c10ce84035 | |||
82d9e481d2 | |||
d27c7f7141 | |||
394c999710 | |||
6399dacba7 | |||
b6bbf655d7 | |||
3d2c018442 | |||
da8c54cf8b | |||
8e984a1bc3 | |||
124e60301a | |||
4d13c48e5c | |||
9159e8f002 | |||
f5aa461945 | |||
16817ff301 | |||
18863b1098 | |||
e3cffca34b | |||
d20d9fb689 | |||
7b1ae2a822 | |||
8ecf5e02b9 | |||
39c607cbd5 | |||
d867ffc60d | |||
c57eb26fd5 | |||
c35d91a104 | |||
9fc260fb1a | |||
73d0f799c2 | |||
ecb2e6e3af | |||
7b75b6928d | |||
753a8a6937 | |||
bc76b1cafe | |||
00e415fc79 | |||
dbbe1f2d5c | |||
740f0a2314 | |||
7fdfe947b0 | |||
b7c4379700 | |||
20b7418916 | |||
85521f5e7a | |||
f37ea6a203 | |||
abf8081ebc | |||
8594cb3e74 | |||
d155de35ed | |||
e37c8e3a5d | |||
e38c0c0fe1 | |||
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 | |||
d8188b807a | |||
8e68655fc7 | |||
6d109bfc6f | |||
b7a5a53c9d | |||
76bd0f452c | |||
363556e9b6 | |||
6f08853b29 | |||
42d8357821 | |||
f2ac63d235 | |||
5d17405b92 | |||
3dd53ad998 | |||
a303c4b294 | |||
fa56290bb4 | |||
01d6cbe9c3 | |||
f682c16740 | |||
74c09dc202 | |||
0917b67573 | |||
5cb0eafcb2 | |||
221e6db47d | |||
8d06018d81 | |||
600e16d9f6 | |||
4340349db7 | |||
6105198313 | |||
c96ba13c23 | |||
c8d0cb315b | |||
be9c413a9e | |||
3e4366b30d | |||
7f3460190b | |||
10d44292e1 | |||
70f81334ae | |||
ead572fab9 | |||
ef8e286277 | |||
ba5a5981a0 | |||
c428596009 | |||
a81d8dd6d5 | |||
5bcbef90ea | |||
1c67f094e3 | |||
99d350914e | |||
49a9e0a880 | |||
7b152def7d | |||
3eb78fb0ff | |||
38c759c86e | |||
314ce1d012 | |||
7fcd40df15 | |||
ba39327b28 | |||
8d2e3894ef | |||
8ee0fbccc5 | |||
fe4354959b | |||
19377c86a4 | |||
fb23aade34 | |||
57e9fe78a3 | |||
6b97281c58 | |||
da67a085fc | |||
e2fc7cdf88 | |||
635bb08fb9 | |||
308d9ce3a8 | |||
5a76c38363 | |||
54530da6c1 | |||
dd55a0bccd | |||
f03bd982b5 | |||
025cb73bf5 | |||
2a6ffb5aa9 | |||
d53b84386f | |||
a7ed933b37 | |||
29cc9283f8 | |||
e60962c31f | |||
f6b5301e17 | |||
73b923e3a1 | |||
d2e4eb40b3 | |||
6a31628e78 | |||
bfb1ad1327 | |||
dfa603c335 | |||
eef979db4c | |||
6166c90bfd | |||
35d1d0080a | |||
3bacb1a9cb | |||
898af49e97 | |||
3061eb4157 | |||
b8d1d8f212 | |||
8c6189775b | |||
932418b951 | |||
95812252d6 | |||
7dec912d15 | |||
dbd36d66ae | |||
40b4166a6e | |||
51d18ffb89 | |||
af3c7b7c76 | |||
3511ac010d | |||
cac3c356a5 | |||
07f19dd4a1 | |||
17a17c31f3 | |||
75742b487f | |||
4e9a2b6d8c | |||
4ea907ae1a | |||
8b912c1363 | |||
080209c469 | |||
5b11ddee35 | |||
3b7ded0ba3 | |||
c5d3e9be76 | |||
714f4dc023 | |||
a86bcd5110 | |||
7ffc477d76 | |||
4cb0b319c0 | |||
5ebe9859e9 | |||
cd2b60a860 | |||
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 | |||
1be6783c34 | |||
092edc9d43 | |||
2ba8eac27f | |||
25ff90b2c6 | |||
b912ae78bc | |||
677d43028a | |||
7bfb55ec9a | |||
2f61d42518 | |||
dbb669b156 | |||
4d0e8741fe | |||
53dc6e2050 | |||
807b860cfe | |||
d756500928 | |||
7ef27a1a21 | |||
6b4d8b91c6 | |||
8f5eb7ef37 | |||
0ea9a08963 | |||
18a1bfe4dd | |||
2d3562c687 | |||
cb40484a2e | |||
e75a08a5a3 | |||
95dfff727e | |||
d55889d85f | |||
99f65f19ac | |||
581eeee01d | |||
17341d7406 | |||
04c0cd142d | |||
7a747d6f93 | |||
e93d034a4e | |||
5a08a10448 | |||
622f93df45 | |||
b788982d60 | |||
26faf4a952 | |||
670bf2b9d1 | |||
f5491346ce | |||
73d3f9f7f7 | |||
d874be99a6 | |||
1767cbe80d | |||
64fbf5025a | |||
aaa01bb6f8 | |||
50d71809e1 | |||
6839712394 | |||
9b5ae7ec75 | |||
94eb64c2be | |||
3fae57508b | |||
a883c35fd0 | |||
740398282f | |||
9d14bc54d6 | |||
bd69c66d03 | |||
c58a1bf9b7 | |||
59c310b914 |
2
.github/ISSUE_TEMPLATE/help---support.md
vendored
2
.github/ISSUE_TEMPLATE/help---support.md
vendored
@ -11,4 +11,4 @@ We don't accept support requests on the issue tracker. Please try the following
|
||||
|
||||
Documentation: http://pmmp.rtfd.io
|
||||
Forums: https://forums.pmmp.io
|
||||
Discord: https://discord.gg/bge7dYQ
|
||||
Discord: https://discord.gg/bmSAZBG
|
||||
|
4
.github/support.yml
vendored
4
.github/support.yml
vendored
@ -5,10 +5,10 @@ supportLabel: "Support request"
|
||||
# Comment to post on issues marked as support requests. Add a link
|
||||
# to a support page, or set to `false` to disable
|
||||
supportComment: >
|
||||
Thanks, but this issue tracker not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
|
||||
Thanks, but this issue tracker is not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
|
||||
|
||||
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bge7dYQ) | [Forums](https://forums.pmmp.io)
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
|
||||
|
||||
# Whether to close issues marked as support requests
|
||||
close: true
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ server.properties
|
||||
memory_dumps/*
|
||||
resource_packs/
|
||||
server.lock
|
||||
/phpstan.neon
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
||||
[submodule "src/pocketmine/resources/vanilla"]
|
||||
path = src/pocketmine/resources/vanilla
|
||||
url = https://github.com/pmmp/BedrockData.git
|
||||
[submodule "build/php"]
|
||||
path = build/php
|
||||
url = https://github.com/pmmp/php-build-scripts.git
|
||||
|
@ -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
|
||||
|
12
README.md
12
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/bge7dYQ)
|
||||
- [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
|
||||
|
@ -36,41 +36,45 @@ use function system;
|
||||
use const pocketmine\BASE_VERSION;
|
||||
use const STDIN;
|
||||
|
||||
require_once dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(isset($argv[1])){
|
||||
$nextVer = new VersionString($argv[1]);
|
||||
$currentVer = new VersionString($argv[1]);
|
||||
}else{
|
||||
$currentVer = new VersionString(BASE_VERSION);
|
||||
$nextVer = new VersionString(sprintf(
|
||||
"%u.%u.%u",
|
||||
$currentVer->getMajor(),
|
||||
$currentVer->getMinor(),
|
||||
$currentVer->getPatch() + 1
|
||||
));
|
||||
}
|
||||
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
|
||||
$versionInfo = file_get_contents($versionInfoPath);
|
||||
|
||||
file_put_contents($versionInfoPath, preg_replace(
|
||||
'/^const IS_DEVELOPMENT_BUILD = true;$/m',
|
||||
'const IS_DEVELOPMENT_BUILD = false;',
|
||||
$versionInfo
|
||||
$nextVer = new VersionString(sprintf(
|
||||
"%u.%u.%u",
|
||||
$currentVer->getMajor(),
|
||||
$currentVer->getMinor(),
|
||||
$currentVer->getPatch() + 1
|
||||
));
|
||||
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev) : void{
|
||||
$versionInfo = file_get_contents($versionInfoPath);
|
||||
$versionInfo = preg_replace(
|
||||
$pattern = '/^const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
||||
'const BASE_VERSION = "' . $newVersion . '";',
|
||||
$versionInfo
|
||||
);
|
||||
$versionInfo = preg_replace(
|
||||
'/^const IS_DEVELOPMENT_BUILD = (?:true|false);$/m',
|
||||
'const IS_DEVELOPMENT_BUILD = ' . ($isDev ? 'true' : 'false'). ';',
|
||||
$versionInfo
|
||||
);
|
||||
file_put_contents($versionInfoPath, $versionInfo);
|
||||
}
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false);
|
||||
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
system('git add "' . dirname(__DIR__) . '/changelogs"');
|
||||
system('git commit -m "Release ' . BASE_VERSION . '" --include "' . $versionInfoPath . '"');
|
||||
system('git tag ' . BASE_VERSION);
|
||||
file_put_contents($versionInfoPath, $mod = preg_replace(
|
||||
$pattern = '/^const BASE_VERSION = "' . preg_quote(BASE_VERSION, '/') . '";$/m',
|
||||
'const BASE_VERSION = "' . $nextVer->getBaseVersion() . '";',
|
||||
$versionInfo
|
||||
));
|
||||
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
|
||||
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);
|
||||
system('git push origin HEAD ' . BASE_VERSION);
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
sleep(5);
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
|
1
build/php
Submodule
1
build/php
Submodule
Submodule build/php added at 8308571448
147
build/server-phar.php
Normal file
147
build/server-phar.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?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\build\server_phar;
|
||||
|
||||
use pocketmine\utils\Git;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* @param string[] $strings
|
||||
* @param string|null $delim
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
function preg_quote_array(array $strings, string $delim = null) : array{
|
||||
return array_map(function(string $str) use ($delim) : string{ return preg_quote($str, $delim); }, $strings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pharPath
|
||||
* @param string $basePath
|
||||
* @param string[] $includedPaths
|
||||
* @param array $metadata
|
||||
* @param string $stub
|
||||
* @param int $signatureAlgo
|
||||
* @param int|null $compression
|
||||
*
|
||||
* @return \Generator|string[]
|
||||
*/
|
||||
function buildPhar(string $pharPath, string $basePath, array $includedPaths, array $metadata, string $stub, int $signatureAlgo = \Phar::SHA1, ?int $compression = null){
|
||||
$basePath = rtrim(str_replace("/", DIRECTORY_SEPARATOR, $basePath), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
$includedPaths = array_map(function($path){
|
||||
return rtrim(str_replace("/", DIRECTORY_SEPARATOR, $path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
}, $includedPaths);
|
||||
yield "Creating output file $pharPath";
|
||||
if(file_exists($pharPath)){
|
||||
yield "Phar file already exists, overwriting...";
|
||||
try{
|
||||
\Phar::unlinkArchive($pharPath);
|
||||
}catch(\PharException $e){
|
||||
//unlinkArchive() doesn't like dodgy phars
|
||||
unlink($pharPath);
|
||||
}
|
||||
}
|
||||
|
||||
yield "Adding files...";
|
||||
|
||||
$start = microtime(true);
|
||||
$phar = new \Phar($pharPath);
|
||||
$phar->setMetadata($metadata);
|
||||
$phar->setStub($stub);
|
||||
$phar->setSignatureAlgorithm($signatureAlgo);
|
||||
$phar->startBuffering();
|
||||
|
||||
//If paths contain any of these, they will be excluded
|
||||
$excludedSubstrings = preg_quote_array([
|
||||
realpath($pharPath), //don't add the phar to itself
|
||||
], '/');
|
||||
|
||||
$folderPatterns = preg_quote_array([
|
||||
DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR,
|
||||
DIRECTORY_SEPARATOR . '.' //"Hidden" files, git dirs etc
|
||||
], '/');
|
||||
|
||||
//Only exclude these within the basedir, otherwise the project won't get built if it itself is in a directory that matches these patterns
|
||||
$basePattern = preg_quote(rtrim($basePath, DIRECTORY_SEPARATOR), '/');
|
||||
foreach($folderPatterns as $p){
|
||||
$excludedSubstrings[] = $basePattern . '.*' . $p;
|
||||
}
|
||||
|
||||
$regex = sprintf('/^(?!.*(%s))^%s(%s).*/i',
|
||||
implode('|', $excludedSubstrings), //String may not contain any of these substrings
|
||||
preg_quote($basePath, '/'), //String must start with this path...
|
||||
implode('|', preg_quote_array($includedPaths, '/')) //... and must be followed by one of these relative paths, if any were specified. If none, this will produce a null capturing group which will allow anything.
|
||||
);
|
||||
|
||||
$directory = new \RecursiveDirectoryIterator($basePath, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::CURRENT_AS_PATHNAME); //can't use fileinfo because of symlinks
|
||||
$iterator = new \RecursiveIteratorIterator($directory);
|
||||
$regexIterator = new \RegexIterator($iterator, $regex);
|
||||
|
||||
$count = count($phar->buildFromIterator($regexIterator, $basePath));
|
||||
yield "Added $count files";
|
||||
|
||||
if($compression !== null){
|
||||
yield "Checking for compressible files...";
|
||||
foreach($phar as $file => $finfo){
|
||||
/** @var \PharFileInfo $finfo */
|
||||
if($finfo->getSize() > (1024 * 512)){
|
||||
yield "Compressing " . $finfo->getFilename();
|
||||
$finfo->compress($compression);
|
||||
}
|
||||
}
|
||||
}
|
||||
$phar->stopBuffering();
|
||||
|
||||
yield "Done in " . round(microtime(true) - $start, 3) . "s";
|
||||
}
|
||||
|
||||
function main() : void{
|
||||
if(ini_get("phar.readonly") == 1){
|
||||
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$opts = getopt("", ["out:"]);
|
||||
$gitHash = Git::getRepositoryStatePretty(dirname(__DIR__));
|
||||
echo "Git hash detected as $gitHash" . PHP_EOL;
|
||||
foreach(buildPhar(
|
||||
$opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar",
|
||||
dirname(__DIR__) . DIRECTORY_SEPARATOR,
|
||||
[
|
||||
'src',
|
||||
'vendor'
|
||||
],
|
||||
[
|
||||
'git' => $gitHash
|
||||
],
|
||||
'<?php require("phar://" . __FILE__ . "/src/pocketmine/PocketMine.php"); __HALT_COMPILER();'
|
||||
) as $line){
|
||||
echo $line . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
main();
|
||||
}
|
20
changelogs/3.10.md
Normal file
20
changelogs/3.10.md
Normal file
@ -0,0 +1,20 @@
|
||||
**For Minecraft: Bedrock Edition 1.13.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.10.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.13.0
|
||||
- Removed compatibility with 1.12.0
|
||||
|
||||
## Note about skins
|
||||
PocketMine-MP **does not support skins made in the Charactor Creator** (known as Persona skins), due to technical changes which would require premature backwards compatibility breaks. The dev team has decided not to support Persona yet.
|
||||
These skins will be **replaced with a random solid-colour skin. This is not a bug.**
|
||||
Skins chosen from the Classic tab (classic skins) will continue to work as normal.
|
||||
|
||||
# 3.10.1
|
||||
- Fixed custom plugin-created skins being invisible when no geometry name was specified.
|
||||
- Updated RakLib to 0.12.6 to fix security bugs.
|
55
changelogs/3.11.md
Normal file
55
changelogs/3.11.md
Normal file
@ -0,0 +1,55 @@
|
||||
**For Minecraft: Bedrock Edition 1.14.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.11.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.14.0
|
||||
- Removed compatibility with 1.13.0
|
||||
|
||||
# 3.11.1
|
||||
- Fixed blocks with incorrect properties when placed or interacted with.
|
||||
|
||||
# 3.11.2
|
||||
## Core
|
||||
- PHPStan 0.12.3 with level 5 is now used for automated static analysis.
|
||||
- Fixed a possible crash when plugins override the `EnderChest` tile class with something incompatible.
|
||||
- Fixed disconnected players being considered as never played.
|
||||
- Fixed enchantments with IDs outside the range 0-255 in item NBT crashing the server.
|
||||
- Fixed particles rendering incorrectly.
|
||||
- Timings handlers are no longer able to underflow; they now throw exceptions when attempting to be stopped more times than they were started.
|
||||
- Fixed explosion rays getting stuck in empty subchunks (possible incorrect behaviour in large caves).
|
||||
- Fixed bad tile/entity NBT data being propagated from world providers in some cases.
|
||||
- Fixed a possible crash when detecting timezone on CentOS.
|
||||
- Fixed many cases of incorrectly documented types in the API found by PHPStan.
|
||||
- Generation tasks no longer assume that generator instances stored in TLS are always valid, fixing a possible crash.
|
||||
|
||||
## Protocol
|
||||
- Fixed skin animation image corruption in LoginPacket handling caused by incorrect data handling.
|
||||
- Fixed skin animation extra data not being decoded from LoginPacket.
|
||||
- `SkinImage` now throws `InvalidArgumentException` if it receives an unexpected amount of bytes for the given image heigh/width.
|
||||
- Fixed broken code in `PlayerAuthInputPacket::create()`.
|
||||
- Removed some dead constants from `NetworkInventoryAction`.
|
||||
|
||||
# 3.11.3
|
||||
- Fixed some PHPStan false-positives in release builds.
|
||||
- Git hash is now correctly detected for source builds when the working directory is not the repository root.
|
||||
- Added a specialized build script `build/server-phar.php` for creating server phars.
|
||||
- Fixed timings crashing the server.
|
||||
- Timings chains now work correctly.
|
||||
- Fixed some minor timing errors in chained timings.
|
||||
- Forcing resource packs no longer causes removal of client-sided resource packs. If this behaviour is desired, use a vanilla resource pack at the bottom of your resource stack (as was necessary for non-forced packs).
|
||||
- Added documentation to the API to clarify that effect durations are in ticks.
|
||||
|
||||
# 3.11.4
|
||||
- Fixed performance issue in leaf decay.
|
||||
- Fixed entity position desync when entities stop moving, but still have velocity on the client.
|
||||
- Fixed a crash when encountering truncated `level.dat` files in LevelDB worlds.
|
||||
- Core code is now analyzed using PHPStan level 6.
|
||||
- The core constants `pocketmine\PATH` and `pocketmine\RESOURCE_PATH` are now unconditionally available when including the Composer autoloader.
|
||||
- Populate type information in lots of places where it was previously missing; this will improve the quality of static analysis for plugins.
|
||||
- `MainLogger::logException()` now logs previous exceptions recursively.
|
||||
- `MainLogger::logException()` now always logs exceptions as `critical`.
|
129
changelogs/3.9.md
Normal file
129
changelogs/3.9.md
Normal file
@ -0,0 +1,129 @@
|
||||
**For Minecraft: Bedrock Edition 1.12.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.9.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.12.0
|
||||
- Removed compatibility with 1.11.0
|
||||
|
||||
## Protocol
|
||||
- The following classes in the `\pocketmine\network\mcpe\protocol` namespace have been renamed:
|
||||
- `AddEntityPacket` -> `AddActorPacket`
|
||||
- `AddItemEntityPacket` -> `AddItemActorPacket`
|
||||
- `AvailableEntityIdentifiersPacket` -> `AvailableActorIdentifiersPacket`
|
||||
- `BlockEntityDataPacket` -> `BlockActorDataPacket`
|
||||
- `EntityEventPacket` -> `ActorEventPacket`
|
||||
- `EntityFallPacket` -> `ActorFallPacket`
|
||||
- `EntityPickRequestPacket` -> `ActorPickRequestPacket`
|
||||
- `MoveEntityAbsolutePacket` -> `MoveActorAbsolutePacket`
|
||||
- `MoveEntityDeltaPacket` -> `MoveActorDeltaPacket`
|
||||
- `RemoveEntityPacket` -> `RemoveActorPacket`
|
||||
- `SetEntityDataPacket` -> `SetActorDataPacket`
|
||||
- `SetEntityLinkPacket` -> `SetActorLinkPacket`
|
||||
- `SetEntityMotionPacket` -> `SetActorMotionPacket`
|
||||
- `TakeItemEntityPacket` -> `TakeItemActorPacket`
|
||||
- The following classes in the `\pocketmine\network\mcpe\protocol` namespace have been removed:
|
||||
- `FullChunkDataPacket`
|
||||
- The following classes in the `\pocketmine\network\mcpe\protocol` namespace have been added:
|
||||
- `AddEntityPacket` (not to be confused with the old one)
|
||||
- `ClientCacheBlobStatusPacket`
|
||||
- `ClientCacheMissResponsePacket`
|
||||
- `ClientCacheStatusPacket`
|
||||
- `LevelChunkPacket`
|
||||
- `RemoveEntityPacket` (not to be confused with the old one)
|
||||
- `StructureTemplateDataExportRequestPacket`
|
||||
- `StructureTemplateDataExportResponsePacket`
|
||||
|
||||
# 3.9.1
|
||||
- Fixed resource packs not working on 1.12 clients.
|
||||
- Fixed some particles displaying incorrectly (some still don't render at all).
|
||||
- Fixed `Entity->setFireTicks()` with a value of `0` setting the on-fire flag.
|
||||
- Silenced a debug message which appeared every time a player right-clicked a block.
|
||||
- Updated constants for `LevelSoundEventPacket`.
|
||||
|
||||
# 3.9.2
|
||||
- Logger warnings for illegal player movements have been lowered to debug.
|
||||
- TNT explosions now start from the center instead of the base. This fixes unexpected results when TNT is lit on top of obsidian.
|
||||
- Fixed the `loadbefore` directive in `plugin.yml` sometimes being ignored.
|
||||
- Fixed `Item->setCustomName()` with an empty string leaving behind an empty tag.
|
||||
- Fixed incorrect positioning of bucket empty sound.
|
||||
- Fixed some incorrect tag parsing in `/give` involving quoted numbers.
|
||||
|
||||
# 3.9.3
|
||||
- Fixed a memory leak on async task removal in error conditions.
|
||||
- Fixed scheduled block updates (for example liquid) triggering chunk reloading. This could cause a significant performance issue in some conditions.
|
||||
- Fixed some minor cosmetic issues in documentation.
|
||||
|
||||
# 3.9.4
|
||||
- Fixed a memory leak when scheduled updates were pending on a chunk being unloaded.
|
||||
- Fixed plugin detection in crashdumps. Previously `src/pocketmine` anywhere in the path would cause the error to be considered a core crash, regardless of the preceding path.
|
||||
- Fixed entity metadata types for 1.12. The SLOT type was removed and a COMPOUND_TAG type added. This change involves changes to internal API which may break plugins. **See the warning at the top of this changelog about API versioning.**
|
||||
- Fixed random and base populator amounts of trees and tallgrass never being initialized. This bug had no obvious effect, but may have become a problem in future PHP versions.
|
||||
- The following internal methods have been marked as `@deprecated` and documentation warnings added:
|
||||
- `Entity->getBlocksAround()`
|
||||
- `Entity->despawnFrom()`
|
||||
- `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.
|
@ -17,17 +17,17 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": ">=3.1.7dev",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-spl": "*",
|
||||
"ext-yaml": ">=2.0.0",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"pocketmine/raklib": "^0.12.0",
|
||||
"pocketmine/spl": "^0.3.0",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/nbt": "^0.2.6",
|
||||
"pocketmine/raklib": "^0.12.5",
|
||||
"pocketmine/spl": "^0.3.5",
|
||||
"pocketmine/binaryutils": "^0.1.9",
|
||||
"pocketmine/nbt": "^0.2.10",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"daverandom/callback-validator": "dev-master",
|
||||
@ -36,7 +36,12 @@
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": ["src"]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src/pocketmine/CoreConstants.php",
|
||||
"src/pocketmine/GlobalConstants.php",
|
||||
"src/pocketmine/VersionInfo.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
75
composer.lock
generated
75
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2f5313e4ebd7b62c785cf683b27464b4",
|
||||
"content-hash": "f693f278b3bb9c1d266079fa17644dcb",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -92,16 +92,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "33f511715d22418c03368b49b45a6c25d6b33806"
|
||||
"reference": "435f2ee265bce75ef1aa9563f9b60ff36d705e80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/33f511715d22418c03368b49b45a6c25d6b33806",
|
||||
"reference": "33f511715d22418c03368b49b45a6c25d6b33806",
|
||||
"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.8",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.10",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2019-01-16T17:31:44+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,30 +153,30 @@
|
||||
],
|
||||
"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.7",
|
||||
"version": "0.2.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a"
|
||||
"reference": "b5777265329753b74dd40bb105eedabeefb98724"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a",
|
||||
"reference": "2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/b5777265329753b74dd40bb105eedabeefb98724",
|
||||
"reference": "b5777265329753b74dd40bb105eedabeefb98724",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-zlib": "*",
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*",
|
||||
"pocketmine/binaryutils": "^0.1.0"
|
||||
"pocketmine/binaryutils": "^0.1.9"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -194,33 +194,33 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.7",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2019-03-29T19:39:42+00:00"
|
||||
"time": "2019-12-01T08:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.12.4",
|
||||
"version": "0.12.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "fc1ccc8e61b9033e5372436b2e28a7a95388373f"
|
||||
"reference": "18450e01185e6064790bda563ac672e7141c6992"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/fc1ccc8e61b9033e5372436b2e28a7a95388373f",
|
||||
"reference": "fc1ccc8e61b9033e5372436b2e28a7a95388373f",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/18450e01185e6064790bda563ac672e7141c6992",
|
||||
"reference": "18450e01185e6064790bda563ac672e7141c6992",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-pthreads": ">=3.1.7dev",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-sockets": "*",
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*",
|
||||
"php-ipv6": "*",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/binaryutils": "^0.1.9",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"pocketmine/spl": "^0.3.0"
|
||||
},
|
||||
@ -235,10 +235,10 @@
|
||||
],
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.4",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.6",
|
||||
"issues": "https://github.com/pmmp/RakLib/issues"
|
||||
},
|
||||
"time": "2019-05-02T14:53:51+00:00"
|
||||
"time": "2019-12-07T13:43:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
@ -276,23 +276,20 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49"
|
||||
"reference": "88052c67d3df2cc2dc2d99ebeae3d7ede3fc64ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/88052c67d3df2cc2dc2d99ebeae3d7ede3fc64ab",
|
||||
"reference": "88052c67d3df2cc2dc2d99ebeae3d7ede3fc64ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"exclude-from-classmap": [
|
||||
"stubs"
|
||||
],
|
||||
"classmap": [
|
||||
"./"
|
||||
]
|
||||
@ -302,16 +299,16 @@
|
||||
],
|
||||
"description": "Standard library files required by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/SPL/tree/0.3.2"
|
||||
"source": "https://github.com/pmmp/SPL/tree/0.3",
|
||||
"issues": "https://github.com/pmmp/SPL/issues"
|
||||
},
|
||||
"time": "2018-08-12T15:17:39+00:00"
|
||||
"time": "2020-01-14T16:23:26+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"ext-pthreads": 20,
|
||||
"daverandom/callback-validator": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
@ -329,7 +326,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": ">=3.1.7dev",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-spl": "*",
|
||||
|
@ -530,7 +530,7 @@ HIDE_FRIEND_COMPOUNDS = NO
|
||||
# blocks will be appended to the function's detailed documentation block.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
HIDE_IN_BODY_DOCS = YES
|
||||
|
||||
# The INTERNAL_DOCS tag determines if documentation that is typed after a
|
||||
# \internal command is included. If the tag is set to NO then the documentation
|
||||
@ -600,7 +600,7 @@ SORT_MEMBER_DOCS = YES
|
||||
# this will also influence the order of the classes in the class list.
|
||||
# The default value is: NO.
|
||||
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_BRIEF_DOCS = YES
|
||||
|
||||
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
|
||||
# (brief and detailed) documentation of class members so that constructors and
|
||||
@ -612,7 +612,7 @@ SORT_BRIEF_DOCS = NO
|
||||
# detailed member documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_MEMBERS_CTORS_1ST = YES
|
||||
|
||||
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
|
||||
# of group names into alphabetical order. If set to NO the group names will
|
||||
@ -1565,7 +1565,7 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest
|
||||
|
||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||
# extension names that should be enabled during MathJax rendering. For example
|
||||
|
118
phpstan.neon.dist
Normal file
118
phpstan.neon.dist
Normal file
@ -0,0 +1,118 @@
|
||||
includes:
|
||||
- tests/phpstan/configs/com-dotnet-magic.neon
|
||||
- tests/phpstan/configs/custom-leveldb.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
- tests/phpstan/configs/pthreads-bugs.neon
|
||||
- tests/phpstan/configs/runtime-type-checks.neon
|
||||
|
||||
parameters:
|
||||
level: 6
|
||||
autoload_files:
|
||||
- tests/phpstan/bootstrap.php
|
||||
- src/pocketmine/PocketMine.php
|
||||
- build/server-phar.php
|
||||
paths:
|
||||
- src
|
||||
- build/server-phar.php
|
||||
dynamicConstantNames:
|
||||
- pocketmine\IS_DEVELOPMENT_BUILD
|
||||
- pocketmine\DEBUG
|
||||
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
|
||||
checkMissingIterableValueType: false #TODO: pthreads Threaded base for too many things, fix this later
|
||||
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: "#^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\\\\PLUGIN_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\START_TIME not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\VERSION not found\\.$#"
|
||||
path: src
|
||||
|
@ -25,12 +25,16 @@ namespace pocketmine;
|
||||
|
||||
abstract class Collectable extends \Threaded{
|
||||
|
||||
/** @var bool */
|
||||
private $isGarbage = false;
|
||||
|
||||
public function isGarbage() : bool{
|
||||
return $this->isGarbage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setGarbage(){
|
||||
$this->isGarbage = true;
|
||||
}
|
||||
|
33
src/pocketmine/CoreConstants.php
Normal file
33
src/pocketmine/CoreConstants.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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;
|
||||
|
||||
// composer autoload doesn't use require_once and also pthreads can inherit things
|
||||
if(defined('pocketmine\_CORE_CONSTANTS_INCLUDED')){
|
||||
return;
|
||||
}
|
||||
define('pocketmine\_CORE_CONSTANTS_INCLUDED', true);
|
||||
|
||||
define('pocketmine\PATH', dirname(__DIR__, 2) . '/');
|
||||
define('pocketmine\RESOURCE_PATH', __DIR__ . '/resources/');
|
@ -96,8 +96,11 @@ class CrashDump{
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var resource */
|
||||
private $fp;
|
||||
/** @var int */
|
||||
private $time;
|
||||
/** @var mixed[] */
|
||||
private $data = [];
|
||||
/** @var string */
|
||||
private $encodedData = "";
|
||||
@ -134,6 +137,9 @@ class CrashDump{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEncodedData(){
|
||||
return $this->encodedData;
|
||||
}
|
||||
@ -142,7 +148,7 @@ class CrashDump{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
private function encodeData(){
|
||||
private function encodeData() : void{
|
||||
$this->addLine();
|
||||
$this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
|
||||
$this->addLine();
|
||||
@ -158,7 +164,7 @@ class CrashDump{
|
||||
$this->addLine("===END CRASH DUMP===");
|
||||
}
|
||||
|
||||
private function pluginsData(){
|
||||
private function pluginsData() : void{
|
||||
if($this->server->getPluginManager() instanceof PluginManager){
|
||||
$this->addLine();
|
||||
$this->addLine("Loaded plugins:");
|
||||
@ -182,7 +188,7 @@ class CrashDump{
|
||||
}
|
||||
}
|
||||
|
||||
private function extraData(){
|
||||
private function extraData() : void{
|
||||
global $argv;
|
||||
|
||||
if($this->server->getProperty("auto-report.send-settings", true) !== false){
|
||||
@ -209,7 +215,7 @@ class CrashDump{
|
||||
}
|
||||
}
|
||||
|
||||
private function baseCrash(){
|
||||
private function baseCrash() : void{
|
||||
global $lastExceptionError, $lastError;
|
||||
|
||||
if(isset($lastExceptionError)){
|
||||
@ -291,7 +297,7 @@ class CrashDump{
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
$frameCleanPath = Utils::cleanPath($filePath); //this will be empty in phar stub
|
||||
if($frameCleanPath !== "" and strpos($frameCleanPath, "src/pocketmine/") === false and strpos($frameCleanPath, "vendor/pocketmine/") === false and file_exists($filePath)){
|
||||
if(strpos($frameCleanPath, "plugins") === 0 and file_exists($filePath)){
|
||||
$this->addLine();
|
||||
if($crashFrame){
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
@ -317,7 +323,7 @@ class CrashDump{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function generalData(){
|
||||
private function generalData() : void{
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
$this->data["general"] = [];
|
||||
$this->data["general"]["name"] = $this->server->getName();
|
||||
@ -333,17 +339,27 @@ 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());
|
||||
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $line
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addLine($line = ""){
|
||||
fwrite($this->fp, $line . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($str){
|
||||
fwrite($this->fp, $str);
|
||||
}
|
||||
|
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;
|
@ -44,6 +44,8 @@ interface IPlayer extends ServerOperator{
|
||||
|
||||
/**
|
||||
* @param bool $banned
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setBanned(bool $banned);
|
||||
|
||||
@ -54,6 +56,8 @@ interface IPlayer extends ServerOperator{
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setWhitelisted(bool $value);
|
||||
|
||||
|
@ -115,7 +115,7 @@ class MemoryManager{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
private function init(){
|
||||
private function init() : void{
|
||||
$this->memoryLimit = ((int) $this->server->getProperty("memory.main-limit", 0)) * 1024 * 1024;
|
||||
|
||||
$defaultMemory = 1024;
|
||||
@ -201,6 +201,8 @@ class MemoryManager{
|
||||
* @param int $limit
|
||||
* @param bool $global
|
||||
* @param int $triggerCount
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function trigger(int $memory, int $limit, bool $global = false, int $triggerCount = 0){
|
||||
$this->server->getLogger()->debug(sprintf("[Memory Manager] %sLow memory triggered, limit %gMB, using %gMB",
|
||||
@ -230,6 +232,8 @@ class MemoryManager{
|
||||
|
||||
/**
|
||||
* Called every tick to update the memory manager state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check(){
|
||||
Timings::$memoryManagerTimer->startTiming();
|
||||
@ -297,6 +301,8 @@ class MemoryManager{
|
||||
* @param string $outputFolder
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize){
|
||||
$this->server->getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
|
||||
@ -319,6 +325,7 @@ class MemoryManager{
|
||||
* @param int $maxStringSize
|
||||
* @param \Logger $logger
|
||||
*
|
||||
* @return void
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){
|
||||
@ -472,14 +479,14 @@ class MemoryManager{
|
||||
|
||||
/**
|
||||
* @param mixed $from
|
||||
* @param mixed &$data
|
||||
* @param object[] &$objects
|
||||
* @param int[] &$refCounts
|
||||
* @param mixed $data reference parameter
|
||||
* @param object[] $objects reference parameter
|
||||
* @param int[] $refCounts reference parameter
|
||||
* @param int $recursion
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
*/
|
||||
private static function continueDump($from, &$data, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize){
|
||||
private static function continueDump($from, &$data, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : void{
|
||||
if($maxNesting <= 0){
|
||||
$data = "(error) NESTING LIMIT REACHED";
|
||||
return;
|
||||
|
@ -57,6 +57,9 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Server
|
||||
*/
|
||||
public function getServer(){
|
||||
return $this->server;
|
||||
}
|
||||
|
@ -99,20 +99,20 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableEntityIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\BookEditPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
@ -146,6 +146,10 @@ use pocketmine\network\mcpe\protocol\types\CommandParameter;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinImage;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\network\mcpe\VerifyLoginTask;
|
||||
@ -261,6 +265,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var bool */
|
||||
public $loggedIn = false;
|
||||
|
||||
/** @var bool */
|
||||
private $resourcePacksDone = false;
|
||||
|
||||
/** @var bool */
|
||||
public $spawned = false;
|
||||
|
||||
@ -275,6 +282,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var string */
|
||||
protected $xuid = "";
|
||||
|
||||
/** @var int */
|
||||
protected $windowCnt = 2;
|
||||
/** @var int[] */
|
||||
protected $windows = [];
|
||||
@ -457,17 +465,22 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function getFirstPlayed(){
|
||||
return $this->namedtag instanceof CompoundTag ? $this->namedtag->getLong("firstPlayed", 0, true) : null;
|
||||
return $this->namedtag->getLong("firstPlayed", 0, true);
|
||||
}
|
||||
|
||||
public function getLastPlayed(){
|
||||
return $this->namedtag instanceof CompoundTag ? $this->namedtag->getLong("lastPlayed", 0, true) : null;
|
||||
return $this->namedtag->getLong("lastPlayed", 0, true);
|
||||
}
|
||||
|
||||
public function hasPlayedBefore() : bool{
|
||||
return $this->playedBefore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAllowFlight(bool $value){
|
||||
$this->allowFlight = $value;
|
||||
$this->sendSettings();
|
||||
@ -477,6 +490,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $this->allowFlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFlying(bool $value){
|
||||
if($this->flying !== $value){
|
||||
$this->flying = $value;
|
||||
@ -489,6 +507,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $this->flying;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAutoJump(bool $value){
|
||||
$this->autoJump = $value;
|
||||
$this->sendSettings();
|
||||
@ -502,6 +525,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $this->allowMovementCheats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAllowMovementCheats(bool $value = true){
|
||||
$this->allowMovementCheats = $value;
|
||||
}
|
||||
@ -531,6 +559,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param bool $remove
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRemoveFormat(bool $remove = true){
|
||||
$this->removeFormat = $remove;
|
||||
@ -558,6 +588,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function hidePlayer(Player $player){
|
||||
if($player === $this){
|
||||
@ -569,6 +601,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showPlayer(Player $player){
|
||||
if($player === $this){
|
||||
@ -597,6 +631,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $this->viewDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $distance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setViewDistance(int $distance){
|
||||
$this->viewDistance = $this->server->getAllowedViewDistance($distance);
|
||||
|
||||
@ -627,6 +666,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOp(bool $value){
|
||||
if($value === $this->isOp()){
|
||||
@ -678,6 +719,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param PermissionAttachment $attachment
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAttachment(PermissionAttachment $attachment){
|
||||
$this->perm->removeAttachment($attachment);
|
||||
@ -713,6 +756,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $this->perm->getEffectivePermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function sendCommandData(){
|
||||
$pk = new AvailableCommandsPacket();
|
||||
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
|
||||
@ -734,7 +780,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$data->overloads[0][0] = $parameter;
|
||||
|
||||
$aliases = $command->getAliases();
|
||||
if(!empty($aliases)){
|
||||
if(count($aliases) > 0){
|
||||
if(!in_array($data->commandName, $aliases, true)){
|
||||
//work around a client bug which makes the original name not show when aliases are used
|
||||
$aliases[] = $data->commandName;
|
||||
@ -810,6 +856,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDisplayName(string $name){
|
||||
$this->displayName = $name;
|
||||
@ -895,6 +943,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @internal Plugins should not use this method.
|
||||
*
|
||||
* @param int $pingMS
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updatePing(int $pingMS){
|
||||
$this->lastPingMeasure = $pingMS;
|
||||
@ -919,6 +969,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_ACTION) and $this->startAction > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUsingItem(bool $value){
|
||||
$this->startAction = $value ? $this->server->getTick() : -1;
|
||||
$this->setGenericFlag(self::DATA_FLAG_ACTION, $value);
|
||||
@ -988,6 +1043,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param Level|null $level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function unloadChunk(int $x, int $z, Level $level = null){
|
||||
$level = $level ?? $this->level;
|
||||
$index = Level::chunkHash($x, $z);
|
||||
@ -1004,6 +1066,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
unset($this->loadQueue[$index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param BatchPacket $payload
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendChunk(int $x, int $z, BatchPacket $payload){
|
||||
if(!$this->isConnected()){
|
||||
return;
|
||||
@ -1026,6 +1095,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function sendNextChunk(){
|
||||
if(!$this->isConnected()){
|
||||
return;
|
||||
@ -1060,6 +1132,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
Timings::$playerChunkSendTimer->stopTiming();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function doFirstSpawn(){
|
||||
if($this->spawned){
|
||||
return; //avoid player spawning twice (this can only happen on 3.x with a custom malicious client)
|
||||
@ -1109,9 +1184,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
protected function sendRespawnPacket(Vector3 $pos){
|
||||
/**
|
||||
* @param Vector3 $pos
|
||||
* @param int $respawnState
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function sendRespawnPacket(Vector3 $pos, int $respawnState = RespawnPacket::SEARCHING_FOR_SPAWN){
|
||||
$pk = new RespawnPacket();
|
||||
$pk->position = $pos->add(0, $this->baseOffset, 0);
|
||||
$pk->respawnState = $respawnState;
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
@ -1199,7 +1282,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$this->loadQueue = $newOrder;
|
||||
if(!empty($this->loadQueue) or !empty($unloadChunks)){
|
||||
if(count($this->loadQueue) > 0 or count($unloadChunks) > 0){
|
||||
$pk = new NetworkChunkPublisherUpdatePacket();
|
||||
$pk->x = $this->getFloorX();
|
||||
$pk->y = $this->getFloorY();
|
||||
@ -1236,6 +1319,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Position object
|
||||
*
|
||||
* @param Vector3|Position $pos
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSpawn(Vector3 $pos){
|
||||
if(!($pos instanceof Position)){
|
||||
@ -1295,6 +1380,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function stopSleep(){
|
||||
if($this->sleeping instanceof Vector3){
|
||||
$b = $this->level->getBlock($this->sleeping);
|
||||
@ -1358,6 +1446,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* @param string $achievementId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAchievement(string $achievementId){
|
||||
if($this->hasAchievement($achievementId)){
|
||||
@ -1446,6 +1536,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/**
|
||||
* @internal
|
||||
* Sends the player's gamemode to the client.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendGamemode(){
|
||||
$pk = new SetPlayerGameTypePacket();
|
||||
@ -1455,6 +1547,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* Sends all the option flags
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendSettings(){
|
||||
$pk = new AdventureSettingsPacket();
|
||||
@ -1560,6 +1654,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return false; //currently has no server-side movement
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function checkNearEntities(){
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(1, 0.5, 1), $this) as $entity){
|
||||
$entity->scheduleUpdate();
|
||||
@ -1572,6 +1669,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $tickDiff
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processMovement(int $tickDiff){
|
||||
if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->isSleeping()){
|
||||
return;
|
||||
@ -1596,7 +1698,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* If you must tamper with this code, be aware that this can cause very nasty results. Do not waste our time
|
||||
* asking for help if you suffer the consequences of messing with this.
|
||||
*/
|
||||
$this->server->getLogger()->warning($this->getName() . " moved too fast, reverting movement");
|
||||
$this->server->getLogger()->debug($this->getName() . " moved too fast, reverting movement");
|
||||
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition);
|
||||
$revert = true;
|
||||
}elseif(!$this->level->isInLoadedTerrain($newPos) or !$this->level->isChunkGenerated($newPos->getFloorX() >> 4, $newPos->getFloorZ() >> 4)){
|
||||
@ -1613,7 +1715,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);
|
||||
|
||||
@ -1621,7 +1723,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$revert = true;
|
||||
$this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()]));
|
||||
$this->server->getLogger()->debug($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()]));
|
||||
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition);
|
||||
}
|
||||
}
|
||||
@ -1714,6 +1816,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $sendAll
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendAttributes(bool $sendAll = false){
|
||||
$entries = $sendAll ? $this->attributeMap->getAll() : $this->attributeMap->needSend();
|
||||
if(count($entries) > 0){
|
||||
@ -1819,6 +1926,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function checkNetwork(){
|
||||
if(!$this->isOnline()){
|
||||
return;
|
||||
@ -1910,14 +2020,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->uuid = UUID::fromString($packet->clientUUID);
|
||||
$this->rawUUID = $this->uuid->toBinary();
|
||||
|
||||
$skin = new Skin(
|
||||
$animations = [];
|
||||
foreach($packet->clientData["AnimatedImageData"] as $animation){
|
||||
$animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], base64_decode($animation["Image"], true)), $animation["Type"], $animation["Frames"]);
|
||||
}
|
||||
|
||||
$skinData = new SkinData(
|
||||
$packet->clientData["SkinId"],
|
||||
base64_decode($packet->clientData["SkinData"] ?? ""),
|
||||
base64_decode($packet->clientData["CapeData"] ?? ""),
|
||||
$packet->clientData["SkinGeometryName"] ?? "",
|
||||
base64_decode($packet->clientData["SkinGeometry"] ?? "")
|
||||
base64_decode($packet->clientData["SkinResourcePatch"] ?? ""),
|
||||
new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"])),
|
||||
$animations,
|
||||
new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")),
|
||||
base64_decode($packet->clientData["SkinGeometryData"] ?? ""),
|
||||
base64_decode($packet->clientData["SkinAnimationData"] ?? ""),
|
||||
$packet->clientData["PremiumSkin"] ?? false,
|
||||
$packet->clientData["PersonaSkin"] ?? false,
|
||||
$packet->clientData["CapeOnClassicSkin"] ?? false,
|
||||
$packet->clientData["CapeId"] ?? ""
|
||||
);
|
||||
|
||||
$skin = SkinAdapterSingleton::get()->fromSkinData($skinData);
|
||||
|
||||
if(!$skin->isValid()){
|
||||
$this->close("", "disconnectionScreen.invalidSkin");
|
||||
|
||||
@ -1954,6 +2077,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param bool $immediate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendPlayStatus(int $status, bool $immediate = false){
|
||||
$pk = new PlayStatusPacket();
|
||||
$pk->status = $status;
|
||||
@ -1997,6 +2126,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->processLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function processLogin(){
|
||||
foreach($this->server->getLoggedInPlayers() as $p){
|
||||
if($p !== $this and ($p->iusername === $this->iusername or $this->getUniqueId()->equals($p->getUniqueId()))){
|
||||
@ -2056,6 +2188,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
|
||||
@ -2093,10 +2228,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk = new ResourcePackStackPacket();
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pk->resourcePackStack = $manager->getResourceStack();
|
||||
$pk->mustAccept = $manager->resourcePacksRequired();
|
||||
//we don't force here, because it doesn't have user-facing effects
|
||||
//but it does have an annoying side-effect when true: it makes
|
||||
//the client remove its own non-server-supplied resource packs.
|
||||
$pk->mustAccept = false;
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
$this->resourcePacksDone = true;
|
||||
$this->completeLoginSequence();
|
||||
break;
|
||||
default:
|
||||
@ -2106,6 +2245,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function completeLoginSequence(){
|
||||
/** @var float[] $pos */
|
||||
$pos = $this->namedtag->getListTag("Pos")->getAllValues();
|
||||
@ -2149,7 +2291,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->spawnZ = $spawnPosition->getFloorZ();
|
||||
$pk->hasAchievementsDisabled = true;
|
||||
$pk->time = $this->level->getTime();
|
||||
$pk->eduMode = false;
|
||||
$pk->eduEditionOffer = 0;
|
||||
$pk->rainLevel = 0; //TODO: implement these properly
|
||||
$pk->lightningLevel = 0;
|
||||
$pk->commandsEnabled = true;
|
||||
@ -2157,7 +2299,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableEntityIdentifiersPacket());
|
||||
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
|
||||
$this->sendDataPacket(new BiomeDefinitionListPacket());
|
||||
|
||||
$this->level->sendTime($this);
|
||||
@ -2279,14 +2421,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleEntityEvent(EntityEventPacket $packet) : bool{
|
||||
public function handleEntityEvent(ActorEventPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->doCloseInventory();
|
||||
|
||||
switch($packet->event){
|
||||
case EntityEventPacket::EATING_ITEM:
|
||||
case ActorEventPacket::EATING_ITEM:
|
||||
if($packet->data === 0){
|
||||
return false;
|
||||
}
|
||||
@ -2484,6 +2626,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
|
||||
if($this->isUsingItem()){
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
if($slot instanceof Consumable){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
$this->resetItemCooldown($slot);
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
}
|
||||
$directionVector = $this->getDirectionVector();
|
||||
|
||||
if($this->isCreative()){
|
||||
@ -2637,33 +2800,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME:
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
|
||||
if($slot instanceof Consumable){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->resetItemCooldown($slot);
|
||||
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2821,7 +2957,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;
|
||||
}
|
||||
|
||||
@ -2855,6 +2991,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case PlayerActionPacket::ACTION_STOP_SWIMMING:
|
||||
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //ignored (for now)
|
||||
break;
|
||||
default:
|
||||
$this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName());
|
||||
return false;
|
||||
@ -2904,6 +3042,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRespawn(RespawnPacket $packet) : bool{
|
||||
if(!$this->isAlive() && $packet->respawnState === RespawnPacket::CLIENT_READY_TO_SPAWN){
|
||||
$this->sendRespawnPacket($this, RespawnPacket::READY_TO_SPAWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an item on the ground in front of the player. Returns if the item drop was successful.
|
||||
*
|
||||
@ -2981,7 +3128,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $handled;
|
||||
}
|
||||
|
||||
public function handleBlockEntityData(BlockEntityDataPacket $packet) : bool{
|
||||
public function handleBlockEntityData(BlockActorDataPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
@ -3047,6 +3194,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)){
|
||||
@ -3118,6 +3268,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Called when a packet is received from the client. This method will call DataPacketReceiveEvent.
|
||||
*
|
||||
* @param DataPacket $packet
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleDataPacket(DataPacket $packet){
|
||||
if($this->sessionAdapter !== null){
|
||||
@ -3275,6 +3427,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
|
||||
* @param int $stay Duration in ticks to stay on screen for
|
||||
* @param int $fadeOut Duration in ticks for fade-out.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1){
|
||||
$this->setTitleDuration($fadeIn, $stay, $fadeOut);
|
||||
@ -3288,6 +3442,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Sets the subtitle message, without sending a title.
|
||||
*
|
||||
* @param string $subtitle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addSubTitle(string $subtitle){
|
||||
$this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE);
|
||||
@ -3297,6 +3453,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Adds small text to the user's screen.
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addActionBarMessage(string $message){
|
||||
$this->sendTitleText($message, SetTitlePacket::TYPE_SET_ACTIONBAR_MESSAGE);
|
||||
@ -3304,6 +3462,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* Removes the title from the client's screen.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeTitles(){
|
||||
$pk = new SetTitlePacket();
|
||||
@ -3313,6 +3473,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/**
|
||||
* Resets the title duration settings to defaults and removes any existing titles.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetTitles(){
|
||||
$pk = new SetTitlePacket();
|
||||
@ -3326,6 +3488,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @param int $fadeIn Title fade-in time in ticks.
|
||||
* @param int $stay Title stay time in ticks.
|
||||
* @param int $fadeOut Title fade-out time in ticks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTitleDuration(int $fadeIn, int $stay, int $fadeOut){
|
||||
if($fadeIn >= 0 and $stay >= 0 and $fadeOut >= 0){
|
||||
@ -3343,6 +3507,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
*
|
||||
* @param string $title
|
||||
* @param int $type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function sendTitleText(string $title, int $type){
|
||||
$pk = new SetTitlePacket();
|
||||
@ -3355,6 +3521,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Sends a direct chat message to a player
|
||||
*
|
||||
* @param TextContainer|string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendMessage($message){
|
||||
if($message instanceof TextContainer){
|
||||
@ -3374,6 +3542,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param string[] $parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendTranslation(string $message, array $parameters = []){
|
||||
$pk = new TextPacket();
|
||||
@ -3382,7 +3552,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->needsTranslation = true;
|
||||
$pk->message = $this->server->getLanguage()->translateString($message, $parameters, "pocketmine.");
|
||||
foreach($parameters as $i => $p){
|
||||
$parameters[$i] = $this->server->getLanguage()->translateString($p, $parameters, "pocketmine.");
|
||||
$parameters[$i] = $this->server->getLanguage()->translateString($p, [], "pocketmine.");
|
||||
}
|
||||
$pk->parameters = $parameters;
|
||||
}else{
|
||||
@ -3399,6 +3569,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $subtitle @deprecated
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendPopup(string $message, string $subtitle = ""){
|
||||
$pk = new TextPacket();
|
||||
@ -3407,6 +3579,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendTip(string $message){
|
||||
$pk = new TextPacket();
|
||||
$pk->type = TextPacket::TYPE_TIP;
|
||||
@ -3417,6 +3594,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/**
|
||||
* @param string $sender
|
||||
* @param string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendWhisper(string $sender, string $message){
|
||||
$pk = new TextPacket();
|
||||
@ -3558,6 +3737,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function __debugInfo(){
|
||||
return [];
|
||||
}
|
||||
@ -3574,6 +3756,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Handles player data saving
|
||||
*
|
||||
* @throws \InvalidStateException if the player is closed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save(){
|
||||
if($this->closed){
|
||||
@ -3736,6 +3920,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Vector3 $pos
|
||||
* @param float|null $yaw
|
||||
* @param float|null $pitch
|
||||
* @param int $mode
|
||||
* @param Player[]|null $targets
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendPosition(Vector3 $pos, float $yaw = null, float $pitch = null, int $mode = MovePlayerPacket::MODE_NORMAL, array $targets = null){
|
||||
$yaw = $yaw ?? $this->yaw;
|
||||
$pitch = $pitch ?? $this->pitch;
|
||||
@ -3787,13 +3980,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function addDefaultWindows(){
|
||||
$this->addWindow($this->getInventory(), ContainerIds::INVENTORY, true);
|
||||
|
||||
$this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true);
|
||||
|
||||
$this->cursorInventory = new PlayerCursorInventory($this);
|
||||
$this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true);
|
||||
$this->addWindow($this->cursorInventory, ContainerIds::UI, true);
|
||||
|
||||
$this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL);
|
||||
|
||||
@ -3912,6 +4108,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @param Inventory $inventory
|
||||
* @param bool $force Forces removal of permanent windows such as normal inventory, cursor
|
||||
*
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException if trying to remove a fixed inventory window without the `force` parameter as true
|
||||
*/
|
||||
public function removeWindow(Inventory $inventory, bool $force = false){
|
||||
@ -3931,6 +4128,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* Removes all inventory windows from the player. By default this WILL NOT remove permanent windows.
|
||||
*
|
||||
* @param bool $removePermanentWindows Whether to remove permanent windows.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAllWindows(bool $removePermanentWindows = false){
|
||||
foreach($this->windowIndex as $id => $window){
|
||||
@ -3942,6 +4141,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function sendAllInventories(){
|
||||
foreach($this->windowIndex as $id => $inventory){
|
||||
$inventory->sendContents($this);
|
||||
|
@ -21,13 +21,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {
|
||||
const INT32_MIN = -0x80000000;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
}
|
||||
|
||||
namespace pocketmine {
|
||||
|
||||
use pocketmine\utils\Git;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\ServerKiller;
|
||||
use pocketmine\utils\Terminal;
|
||||
@ -40,6 +36,10 @@ namespace pocketmine {
|
||||
|
||||
const MIN_PHP_VERSION = "7.2.0";
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
function critical_error($message){
|
||||
echo "[ERROR] $message" . PHP_EOL;
|
||||
}
|
||||
@ -57,18 +57,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 = [
|
||||
@ -102,8 +102,8 @@ namespace pocketmine {
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
if(version_compare($pthreads_version, "3.1.7dev") < 0){
|
||||
$messages[] = "pthreads >= 3.1.7dev is required, while you have $pthreads_version.";
|
||||
if(version_compare($pthreads_version, "3.2.0") < 0){
|
||||
$messages[] = "pthreads >= 3.2.0 is required, while you have $pthreads_version.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,176 +121,179 @@ namespace pocketmine {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
if(!empty($messages = check_platform_dependencies())){
|
||||
echo PHP_EOL;
|
||||
$binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
|
||||
critical_error("Selected PHP binary ($binary) does not satisfy some requirements.");
|
||||
foreach($messages as $m){
|
||||
echo " - $m" . PHP_EOL;
|
||||
}
|
||||
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
echo PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
unset($messages);
|
||||
|
||||
error_reporting(-1);
|
||||
|
||||
if(\Phar::running(true) !== ""){
|
||||
define('pocketmine\PATH', \Phar::running(true) . "/");
|
||||
}else{
|
||||
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
$opts = getopt("", ["bootstrap:"]);
|
||||
if(isset($opts["bootstrap"])){
|
||||
$bootstrap = realpath($opts["bootstrap"]) ?: $opts["bootstrap"];
|
||||
}else{
|
||||
$bootstrap = \pocketmine\PATH . 'vendor/autoload.php';
|
||||
}
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
|
||||
|
||||
if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}else{
|
||||
critical_error("Composer autoloader not found at " . $bootstrap);
|
||||
critical_error("Please install/update Composer dependencies or use provided builds.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
set_error_handler([Utils::class, 'errorExceptionHandler']);
|
||||
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
/**
|
||||
* @param \Logger $logger
|
||||
* @return void
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader->register(false);
|
||||
|
||||
set_time_limit(0); //Who set it to 30 seconds?!?!
|
||||
|
||||
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("memory_limit", '-1');
|
||||
|
||||
define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
|
||||
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
|
||||
define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR);
|
||||
define('pocketmine\PLUGIN_PATH', isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
|
||||
|
||||
if(!file_exists(\pocketmine\DATA)){
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
define('pocketmine\LOCK_FILE_PATH', \pocketmine\DATA . 'server.lock');
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\LOCK_FILE_PATH, "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
|
||||
flock(\pocketmine\LOCK_FILE, LOCK_SH);
|
||||
$pid = stream_get_contents(\pocketmine\LOCK_FILE);
|
||||
critical_error("Another " . \pocketmine\NAME . " instance (PID $pid) is already using this folder (" . realpath(\pocketmine\DATA) . ").");
|
||||
critical_error("Please stop the other server first before running a new one.");
|
||||
exit(1);
|
||||
}
|
||||
ftruncate(\pocketmine\LOCK_FILE, 0);
|
||||
fwrite(\pocketmine\LOCK_FILE, (string) getmypid());
|
||||
fflush(\pocketmine\LOCK_FILE);
|
||||
flock(\pocketmine\LOCK_FILE, LOCK_SH); //prevent acquiring an exclusive lock from another process, but allow reading
|
||||
|
||||
//Logger has a dependency on timezone
|
||||
$tzError = Timezone::init();
|
||||
|
||||
if(isset($opts["enable-ansi"])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts["disable-ansi"])){
|
||||
Terminal::init(false);
|
||||
}else{
|
||||
Terminal::init();
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
$logger = new MainLogger(\pocketmine\DATA . "server.log");
|
||||
$logger->registerStatic();
|
||||
|
||||
foreach($tzError as $e){
|
||||
$logger->warning($e);
|
||||
}
|
||||
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";
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
function server(){
|
||||
if(count($messages = check_platform_dependencies()) > 0){
|
||||
echo PHP_EOL;
|
||||
$binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
|
||||
critical_error("Selected PHP binary ($binary) does not satisfy some requirements.");
|
||||
foreach($messages as $m){
|
||||
echo " - $m" . PHP_EOL;
|
||||
}
|
||||
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
echo PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
unset($messages);
|
||||
|
||||
error_reporting(-1);
|
||||
set_ini_entries();
|
||||
|
||||
$opts = getopt("", ["bootstrap:"]);
|
||||
if(isset($opts["bootstrap"])){
|
||||
$bootstrap = realpath($opts["bootstrap"]) ?: $opts["bootstrap"];
|
||||
}else{
|
||||
$bootstrap = dirname(__FILE__, 3) . '/vendor/autoload.php';
|
||||
}
|
||||
}
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
|
||||
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}else{
|
||||
critical_error("Composer autoloader not found at " . $bootstrap);
|
||||
critical_error("Please install/update Composer dependencies or use provided builds.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
set_error_handler([Utils::class, 'errorExceptionHandler']);
|
||||
|
||||
@define("INT32_MASK", is_int(0xffffffff) ? 0xffffffff : -1);
|
||||
@ini_set("opcache.mmap_base", bin2hex(random_bytes(8))); //Fix OPCache address errors
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
define('pocketmine\VERSION', $version->getFullVersion(true));
|
||||
|
||||
$exitCode = 0;
|
||||
do{
|
||||
if(!file_exists(\pocketmine\DATA . "server.properties") and !isset($opts["no-wizard"])){
|
||||
$installer = new SetupWizard();
|
||||
if(!$installer->run()){
|
||||
$exitCode = -1;
|
||||
break;
|
||||
$gitHash = str_repeat("00", 20);
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
$gitHash = Git::getRepositoryStatePretty(\pocketmine\PATH);
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move this to a Server field
|
||||
define('pocketmine\START_TIME', microtime(true));
|
||||
ThreadManager::init();
|
||||
new Server($autoloader, $logger, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
|
||||
$logger->info("Stopping other threads");
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
|
||||
$killer = new ServerKiller(8);
|
||||
$killer->start(PTHREADS_INHERIT_NONE);
|
||||
usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
|
||||
define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR);
|
||||
define('pocketmine\PLUGIN_PATH', isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
echo "Some threads could not be stopped, performing a force-kill" . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
Utils::kill(getmypid());
|
||||
if(!file_exists(\pocketmine\DATA)){
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
}
|
||||
}while(false);
|
||||
|
||||
$logger->shutdown();
|
||||
$logger->join();
|
||||
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
|
||||
flock(\pocketmine\LOCK_FILE, LOCK_SH);
|
||||
$pid = stream_get_contents(\pocketmine\LOCK_FILE);
|
||||
critical_error("Another " . \pocketmine\NAME . " instance (PID $pid) is already using this folder (" . realpath(\pocketmine\DATA) . ").");
|
||||
critical_error("Please stop the other server first before running a new one.");
|
||||
exit(1);
|
||||
}
|
||||
ftruncate(\pocketmine\LOCK_FILE, 0);
|
||||
fwrite(\pocketmine\LOCK_FILE, (string) getmypid());
|
||||
fflush(\pocketmine\LOCK_FILE);
|
||||
flock(\pocketmine\LOCK_FILE, LOCK_SH); //prevent acquiring an exclusive lock from another process, but allow reading
|
||||
|
||||
echo Terminal::$FORMAT_RESET . PHP_EOL;
|
||||
//Logger has a dependency on timezone
|
||||
$tzError = Timezone::init();
|
||||
|
||||
exit($exitCode);
|
||||
if(isset($opts["enable-ansi"])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts["disable-ansi"])){
|
||||
Terminal::init(false);
|
||||
}else{
|
||||
Terminal::init();
|
||||
}
|
||||
|
||||
$logger = new MainLogger(\pocketmine\DATA . "server.log");
|
||||
$logger->registerStatic();
|
||||
|
||||
foreach($tzError as $e){
|
||||
$logger->warning($e);
|
||||
}
|
||||
unset($tzError);
|
||||
|
||||
emit_performance_warnings($logger);
|
||||
|
||||
$exitCode = 0;
|
||||
do{
|
||||
if(!file_exists(\pocketmine\DATA . "server.properties") and !isset($opts["no-wizard"])){
|
||||
$installer = new SetupWizard();
|
||||
if(!$installer->run()){
|
||||
$exitCode = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//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");
|
||||
|
||||
$killer = new ServerKiller(8);
|
||||
$killer->start(PTHREADS_INHERIT_NONE);
|
||||
usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
$logger->debug("Some threads could not be stopped, performing a force-kill");
|
||||
Utils::kill(getmypid());
|
||||
}
|
||||
}while(false);
|
||||
|
||||
$logger->shutdown();
|
||||
$logger->join();
|
||||
|
||||
echo Terminal::$FORMAT_RESET . PHP_EOL;
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
\pocketmine\server();
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\RakLibInterface;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\query\QueryHandler;
|
||||
@ -113,6 +114,7 @@ use function asort;
|
||||
use function assert;
|
||||
use function base64_encode;
|
||||
use function class_exists;
|
||||
use function cli_set_process_title;
|
||||
use function count;
|
||||
use function define;
|
||||
use function explode;
|
||||
@ -127,7 +129,6 @@ use function getmypid;
|
||||
use function getopt;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
@ -227,7 +228,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];
|
||||
@ -482,6 +483,8 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAutoSave(bool $value){
|
||||
$this->autoSave = $value;
|
||||
@ -593,7 +596,7 @@ class Server{
|
||||
* @return int
|
||||
*/
|
||||
public function getDifficulty() : int{
|
||||
return $this->getConfigInt("difficulty", 1);
|
||||
return $this->getConfigInt("difficulty", Level::DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -873,6 +876,8 @@ class Server{
|
||||
/**
|
||||
* @param string $name
|
||||
* @param CompoundTag $nbtTag
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveOfflinePlayerData(string $name, CompoundTag $nbtTag){
|
||||
$ev = new PlayerDataSaveEvent($nbtTag, $name);
|
||||
@ -1101,11 +1106,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){
|
||||
@ -1205,9 +1216,9 @@ class Server{
|
||||
}
|
||||
$path = $this->getDataPath() . "worlds/" . $name . "/";
|
||||
if(!($this->getLevelByName($name) instanceof Level)){
|
||||
return is_dir($path) and !empty(array_filter(scandir($path, SCANDIR_SORT_NONE), function($v){
|
||||
return is_dir($path) and count(array_filter(scandir($path, SCANDIR_SORT_NONE), function($v){
|
||||
return $v !== ".." and $v !== ".";
|
||||
}));
|
||||
})) > 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1270,6 +1281,8 @@ class Server{
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param string $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setConfigString(string $variable, string $value){
|
||||
$this->properties->set($variable, $value);
|
||||
@ -1293,6 +1306,8 @@ class Server{
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param int $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setConfigInt(string $variable, int $value){
|
||||
$this->properties->set($variable, $value);
|
||||
@ -1329,6 +1344,8 @@ class Server{
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setConfigBool(string $variable, bool $value){
|
||||
$this->properties->set($variable, $value ? "1" : "0");
|
||||
@ -1363,6 +1380,8 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addOp(string $name){
|
||||
$this->operators->set(strtolower($name), true);
|
||||
@ -1375,6 +1394,8 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeOp(string $name){
|
||||
$this->operators->remove(strtolower($name));
|
||||
@ -1387,6 +1408,8 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addWhitelist(string $name){
|
||||
$this->whitelist->set(strtolower($name), true);
|
||||
@ -1395,6 +1418,8 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeWhitelist(string $name){
|
||||
$this->whitelist->remove(strtolower($name));
|
||||
@ -1433,12 +1458,15 @@ class Server{
|
||||
return $this->operators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reloadWhitelist(){
|
||||
$this->whitelist->reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return string[][]
|
||||
*/
|
||||
public function getCommandAliases() : array{
|
||||
$section = $this->getProperty("aliases");
|
||||
@ -1469,6 +1497,11 @@ class Server{
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $microseconds
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function microSleep(int $microseconds){
|
||||
Server::$sleeper->synchronized(function(int $ms){
|
||||
Server::$sleeper->wait($ms);
|
||||
@ -1529,7 +1562,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 +1592,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 +1816,7 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param TextContainer|string $message
|
||||
* @param Player[] $recipients
|
||||
* @param CommandSender[] $recipients
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@ -1798,7 +1825,6 @@ class Server{
|
||||
return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS);
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendMessage($message);
|
||||
}
|
||||
@ -1823,7 +1849,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendTip($tip);
|
||||
}
|
||||
@ -1849,7 +1874,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendPopup($popup);
|
||||
}
|
||||
@ -1879,7 +1903,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->addTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
|
||||
}
|
||||
@ -1916,6 +1939,8 @@ class Server{
|
||||
*
|
||||
* @param Player[] $players
|
||||
* @param DataPacket $packet
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function broadcastPacket(array $players, DataPacket $packet){
|
||||
$packet->encode();
|
||||
@ -1929,16 +1954,18 @@ class Server{
|
||||
* @param DataPacket[] $packets
|
||||
* @param bool $forceSync
|
||||
* @param bool $immediate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function batchPackets(array $players, array $packets, bool $forceSync = false, bool $immediate = false){
|
||||
if(empty($packets)){
|
||||
if(count($packets) === 0){
|
||||
throw new \InvalidArgumentException("Cannot send empty batch");
|
||||
}
|
||||
Timings::$playerNetworkTimer->startTiming();
|
||||
|
||||
$targets = array_filter($players, function(Player $player) : bool{ return $player->isConnected(); });
|
||||
|
||||
if(!empty($targets)){
|
||||
if(count($targets) > 0){
|
||||
$pk = new BatchPacket();
|
||||
|
||||
foreach($packets as $p){
|
||||
@ -1967,6 +1994,8 @@ class Server{
|
||||
* @param BatchPacket $pk
|
||||
* @param Player[] $players
|
||||
* @param bool $immediate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function broadcastPacketsCallback(BatchPacket $pk, array $players, bool $immediate = false){
|
||||
if(!$pk->isEncoded){
|
||||
@ -1981,6 +2010,8 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enablePlugins(int $type){
|
||||
foreach($this->pluginManager->getPlugins() as $plugin){
|
||||
@ -1997,11 +2028,16 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param Plugin $plugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enablePlugin(Plugin $plugin){
|
||||
$this->pluginManager->enablePlugin($plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function disablePlugins(){
|
||||
$this->pluginManager->disablePlugins();
|
||||
}
|
||||
@ -2036,6 +2072,9 @@ class Server{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reload(){
|
||||
$this->logger->info("Saving worlds...");
|
||||
|
||||
@ -2075,11 +2114,16 @@ class Server{
|
||||
|
||||
/**
|
||||
* Shuts the server down correctly
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function shutdown(){
|
||||
$this->isRunning = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function forceShutdown(){
|
||||
if($this->hasStopped){
|
||||
return;
|
||||
@ -2165,7 +2209,7 @@ class Server{
|
||||
/**
|
||||
* Starts the PocketMine-MP server and starts processing ticks and packets
|
||||
*/
|
||||
private function start(){
|
||||
private function start() : void{
|
||||
if($this->getConfigBool("enable-query", true)){
|
||||
$this->queryHandler = new QueryHandler();
|
||||
}
|
||||
@ -2200,12 +2244,18 @@ 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();
|
||||
$this->forceShutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $signo
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleSignal($signo){
|
||||
if($signo === SIGTERM or $signo === SIGINT or $signo === SIGHUP){
|
||||
$this->shutdown();
|
||||
@ -2215,6 +2265,8 @@ class Server{
|
||||
/**
|
||||
* @param \Throwable $e
|
||||
* @param array|null $trace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null){
|
||||
while(@ob_end_flush()){}
|
||||
@ -2248,6 +2300,9 @@ class Server{
|
||||
$this->crashDump();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function crashDump(){
|
||||
while(@ob_end_flush()){}
|
||||
if(!$this->isRunning){
|
||||
@ -2330,6 +2385,9 @@ class Server{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function __debugInfo(){
|
||||
return [];
|
||||
}
|
||||
@ -2338,7 +2396,7 @@ class Server{
|
||||
return $this->tickSleeper;
|
||||
}
|
||||
|
||||
private function tickProcessor(){
|
||||
private function tickProcessor() : void{
|
||||
$this->nextTick = microtime(true);
|
||||
|
||||
while($this->isRunning){
|
||||
@ -2349,6 +2407,11 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onPlayerLogin(Player $player){
|
||||
if($this->sendUsageTicker > 0){
|
||||
$this->uniquePlayers[$player->getRawUniqueId()] = $player->getRawUniqueId();
|
||||
@ -2357,27 +2420,49 @@ class Server{
|
||||
$this->loggedInPlayers[$player->getRawUniqueId()] = $player;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onPlayerLogout(Player $player){
|
||||
unset($this->loggedInPlayers[$player->getRawUniqueId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPlayer(Player $player){
|
||||
$this->players[spl_object_hash($player)] = $player;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removePlayer(Player $player){
|
||||
unset($this->players[spl_object_hash($player)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addOnlinePlayer(Player $player){
|
||||
$this->updatePlayerListData($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
|
||||
|
||||
$this->playerList[$player->getRawUniqueId()] = $player;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeOnlinePlayer(Player $player){
|
||||
if(isset($this->playerList[$player->getRawUniqueId()])){
|
||||
unset($this->playerList[$player->getRawUniqueId()]);
|
||||
@ -2393,12 +2478,14 @@ class Server{
|
||||
* @param Skin $skin
|
||||
* @param string $xboxUserId
|
||||
* @param Player[]|null $players
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updatePlayerListData(UUID $uuid, int $entityId, string $name, Skin $skin, string $xboxUserId = "", array $players = null){
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, $skin, $xboxUserId);
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, SkinAdapterSingleton::get()->toSkinData($skin), $xboxUserId);
|
||||
|
||||
$this->broadcastPacket($players ?? $this->playerList, $pk);
|
||||
}
|
||||
@ -2406,6 +2493,8 @@ class Server{
|
||||
/**
|
||||
* @param UUID $uuid
|
||||
* @param Player[]|null $players
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removePlayerListData(UUID $uuid, array $players = null){
|
||||
$pk = new PlayerListPacket();
|
||||
@ -2416,12 +2505,14 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param Player $p
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendFullPlayerListData(Player $p){
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
foreach($this->playerList as $player){
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($player->getSkin()), $player->getXuid());
|
||||
}
|
||||
|
||||
$p->dataPacket($pk);
|
||||
@ -2451,6 +2542,9 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function doAutoSave(){
|
||||
if($this->getAutoSave()){
|
||||
Timings::$worldSaveTimer->startTiming();
|
||||
@ -2469,6 +2563,11 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendUsage($type = SendUsageTask::TYPE_STATUS){
|
||||
if((bool) $this->getProperty("anonymous-statistics.enabled", true)){
|
||||
$this->asyncPool->submitTask(new SendUsageTask($this, $type, $this->uniquePlayers));
|
||||
@ -2505,7 +2604,7 @@ class Server{
|
||||
return $this->memoryManager;
|
||||
}
|
||||
|
||||
private function titleTick(){
|
||||
private function titleTick() : void{
|
||||
Timings::$titleTickTimer->startTiming();
|
||||
$d = Utils::getRealMemoryUsage();
|
||||
|
||||
@ -2530,6 +2629,8 @@ class Server{
|
||||
* @param int $port
|
||||
* @param string $payload
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* TODO: move this to Network
|
||||
*/
|
||||
public function handlePacket(AdvancedSourceInterface $interface, string $address, int $port, string $payload){
|
||||
|
@ -33,12 +33,21 @@ abstract class Thread extends \Thread{
|
||||
/** @var string|null */
|
||||
protected $composerAutoloaderPath;
|
||||
|
||||
/** @var bool */
|
||||
protected $isKilled = false;
|
||||
|
||||
/**
|
||||
* @return \ClassLoader|null
|
||||
*/
|
||||
public function getClassLoader(){
|
||||
return $this->classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ClassLoader|null $loader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassLoader(\ClassLoader $loader = null){
|
||||
$this->composerAutoloaderPath = \pocketmine\COMPOSER_AUTOLOADER_PATH;
|
||||
|
||||
@ -54,6 +63,8 @@ abstract class Thread extends \Thread{
|
||||
* WARNING: This method MUST be called from any descendent threads' run() method to make autoloading usable.
|
||||
* If you do not do this, you will not be able to use new classes that were not loaded when the thread was started
|
||||
* (unless you are using a custom autoloader).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerClassLoader(){
|
||||
if($this->composerAutoloaderPath !== null){
|
||||
@ -64,6 +75,11 @@ abstract class Thread extends \Thread{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $options TODO: pthreads bug
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function start(?int $options = \PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
@ -75,6 +91,8 @@ abstract class Thread extends \Thread{
|
||||
|
||||
/**
|
||||
* Stops the thread using the best way possible. Try to stop it yourself before calling this.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
@ -31,6 +31,9 @@ class ThreadManager extends \Volatile{
|
||||
/** @var ThreadManager */
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function init(){
|
||||
self::$instance = new ThreadManager();
|
||||
}
|
||||
@ -44,6 +47,8 @@ class ThreadManager extends \Volatile{
|
||||
|
||||
/**
|
||||
* @param Worker|Thread $thread
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($thread){
|
||||
if($thread instanceof Thread or $thread instanceof Worker){
|
||||
@ -53,6 +58,8 @@ class ThreadManager extends \Volatile{
|
||||
|
||||
/**
|
||||
* @param Worker|Thread $thread
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove($thread){
|
||||
if($thread instanceof Thread or $thread instanceof Worker){
|
||||
|
@ -19,9 +19,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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.8.7";
|
||||
const BASE_VERSION = "3.11.4";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
@ -33,12 +33,21 @@ abstract class Worker extends \Worker{
|
||||
/** @var string|null */
|
||||
protected $composerAutoloaderPath;
|
||||
|
||||
/** @var bool */
|
||||
protected $isKilled = false;
|
||||
|
||||
/**
|
||||
* @return \ClassLoader|null
|
||||
*/
|
||||
public function getClassLoader(){
|
||||
return $this->classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ClassLoader|null $loader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassLoader(\ClassLoader $loader = null){
|
||||
$this->composerAutoloaderPath = \pocketmine\COMPOSER_AUTOLOADER_PATH;
|
||||
|
||||
@ -54,6 +63,8 @@ abstract class Worker extends \Worker{
|
||||
* WARNING: This method MUST be called from any descendent threads' run() method to make autoloading usable.
|
||||
* If you do not do this, you will not be able to use new classes that were not loaded when the thread was started
|
||||
* (unless you are using a custom autoloader).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerClassLoader(){
|
||||
if($this->composerAutoloaderPath !== null){
|
||||
@ -64,6 +75,11 @@ abstract class Worker extends \Worker{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $options TODO: pthreads bug
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function start(?int $options = \PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
@ -75,6 +91,8 @@ abstract class Worker extends \Worker{
|
||||
|
||||
/**
|
||||
* Stops the thread using the best way possible. Try to stop it yourself before calling this.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
@ -76,6 +76,11 @@ class Bed extends Transparent{
|
||||
return ($this->meta & self::BITFLAG_OCCUPIED) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $occupied
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOccupied(bool $occupied = true){
|
||||
if($occupied){
|
||||
$this->meta |= self::BITFLAG_OCCUPIED;
|
||||
|
@ -40,6 +40,7 @@ use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
@ -69,7 +70,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
/** @var int|null */
|
||||
protected $itemId;
|
||||
|
||||
/** @var AxisAlignedBB */
|
||||
/** @var AxisAlignedBB|null */
|
||||
protected $boundingBox = null;
|
||||
|
||||
|
||||
@ -727,7 +728,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
*/
|
||||
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{
|
||||
$bbs = $this->getCollisionBoxes();
|
||||
if(empty($bbs)){
|
||||
if(count($bbs) === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -763,7 +764,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(
|
||||
|
@ -111,6 +111,11 @@ class CobblestoneWall extends Transparent{
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Block $block
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canConnect(Block $block){
|
||||
return $block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent());
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ abstract class Door extends Transparent{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getFullDamage(){
|
||||
private function getFullDamage() : int{
|
||||
$damage = $this->getDamage();
|
||||
$isUp = ($damage & 0x08) > 0;
|
||||
|
||||
|
@ -84,6 +84,9 @@ class EnderChest extends Chest{
|
||||
$enderChest = $t;
|
||||
}else{
|
||||
$enderChest = Tile::createTile(Tile::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this));
|
||||
if(!($enderChest instanceof TileEnderChest)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->getSide(Vector3::SIDE_UP)->isTransparent()){
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use function count;
|
||||
|
||||
abstract class Fence extends Transparent{
|
||||
|
||||
@ -85,7 +86,7 @@ abstract class Fence extends Transparent{
|
||||
);
|
||||
}
|
||||
|
||||
if(empty($bbs)){
|
||||
if(count($bbs) === 0){
|
||||
//centre post AABB (only needed if not connected on any axis - other BBs overlapping will do this if any connections are made)
|
||||
return [
|
||||
new AxisAlignedBB(
|
||||
@ -102,6 +103,11 @@ abstract class Fence extends Transparent{
|
||||
return $bbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Block $block
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canConnect(Block $block){
|
||||
return $block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent());
|
||||
}
|
||||
|
@ -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()){
|
||||
|
@ -39,6 +39,7 @@ class Leaves extends Transparent{
|
||||
public const DARK_OAK = 1;
|
||||
|
||||
protected $id = self::LEAVES;
|
||||
/** @var int */
|
||||
protected $woodType = self::WOOD;
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
@ -68,7 +69,7 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
|
||||
protected function findLog(Block $pos, array $visited, int $distance, ?int $fromSide = null) : bool{
|
||||
protected function findLog(Block $pos, array &$visited, int $distance, ?int $fromSide = null) : bool{
|
||||
$index = $pos->x . "." . $pos->y . "." . $pos->z;
|
||||
if(isset($visited[$index])){
|
||||
return false;
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\item\ItemFactory;
|
||||
class Leaves2 extends Leaves{
|
||||
|
||||
protected $id = self::LEAVES2;
|
||||
/** @var int */
|
||||
protected $woodType = self::WOOD2;
|
||||
|
||||
public function getName() : string{
|
||||
|
@ -38,6 +38,7 @@ use function min;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
|
||||
/** @var int */
|
||||
public $adjacentSources = 0;
|
||||
|
||||
/** @var Vector3|null */
|
||||
@ -90,6 +91,9 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
abstract public function getBucketEmptySound() : int;
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getFluidHeightPercent(){
|
||||
$d = $this->meta;
|
||||
if($d >= 8){
|
||||
@ -430,6 +434,9 @@ abstract class Liquid extends Transparent{
|
||||
return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function checkForHarden(){
|
||||
|
||||
}
|
||||
|
@ -74,6 +74,11 @@ class TNT extends Solid{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fuse
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ignite(int $fuse = 80){
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true);
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use function count;
|
||||
|
||||
abstract class Thin extends Transparent{
|
||||
|
||||
@ -77,7 +78,7 @@ abstract class Thin extends Transparent{
|
||||
);
|
||||
}
|
||||
|
||||
if(empty($bbs)){
|
||||
if(count($bbs) === 0){
|
||||
//centre post AABB (only needed if not connected on any axis - other BBs overlapping will do this if any connections are made)
|
||||
return [
|
||||
new AxisAlignedBB(
|
||||
|
@ -55,7 +55,7 @@ abstract class Command{
|
||||
*/
|
||||
private $activeAliases = [];
|
||||
|
||||
/** @var CommandMap */
|
||||
/** @var CommandMap|null */
|
||||
private $commandMap = null;
|
||||
|
||||
/** @var string */
|
||||
@ -114,6 +114,8 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string|null $permission
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPermission(string $permission = null){
|
||||
$this->permission = $permission;
|
||||
@ -259,6 +261,8 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAliases(array $aliases){
|
||||
$this->aliases = $aliases;
|
||||
@ -269,6 +273,8 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDescription(string $description){
|
||||
$this->description = $description;
|
||||
@ -276,6 +282,8 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string $permissionMessage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPermissionMessage(string $permissionMessage){
|
||||
$this->permissionMessage = $permissionMessage;
|
||||
@ -283,6 +291,8 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string $usage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUsage(string $usage){
|
||||
$this->usageMessage = $usage;
|
||||
@ -292,6 +302,8 @@ abstract class Command{
|
||||
* @param CommandSender $source
|
||||
* @param TextContainer|string $message
|
||||
* @param bool $sendToSource
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function broadcastCommandMessage(CommandSender $source, $message, bool $sendToSource = true){
|
||||
if($message instanceof TextContainer){
|
||||
|
@ -29,6 +29,8 @@ interface CommandMap{
|
||||
/**
|
||||
* @param string $fallbackPrefix
|
||||
* @param Command[] $commands
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerAll(string $fallbackPrefix, array $commands);
|
||||
|
||||
|
@ -54,7 +54,9 @@ class CommandReader extends Thread{
|
||||
|
||||
/** @var \Threaded */
|
||||
protected $buffer;
|
||||
/** @var bool */
|
||||
private $shutdown = false;
|
||||
/** @var int */
|
||||
private $type = self::TYPE_STREAM;
|
||||
|
||||
/** @var SleeperNotifier|null */
|
||||
@ -71,6 +73,9 @@ class CommandReader extends Thread{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function shutdown(){
|
||||
$this->shutdown = true;
|
||||
}
|
||||
@ -94,7 +99,7 @@ class CommandReader extends Thread{
|
||||
throw new \ThreadException($message);
|
||||
}
|
||||
|
||||
private function initStdin(){
|
||||
private function initStdin() : void{
|
||||
if(is_resource(self::$stdin)){
|
||||
fclose(self::$stdin);
|
||||
}
|
||||
@ -141,6 +146,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
|
||||
@ -184,6 +190,9 @@ class CommandReader extends Thread{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function run(){
|
||||
$this->registerClassLoader();
|
||||
|
||||
|
@ -31,6 +31,8 @@ interface CommandSender extends Permissible{
|
||||
|
||||
/**
|
||||
* @param TextContainer|string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendMessage($message);
|
||||
|
||||
@ -56,6 +58,8 @@ interface CommandSender extends Permissible{
|
||||
* Sets the line height used for command output pagination for this command sender. `null` will reset it to default.
|
||||
*
|
||||
* @param int|null $height
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setScreenLineHeight(int $height = null);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ use const PHP_INT_MAX;
|
||||
|
||||
class ConsoleCommandSender implements CommandSender{
|
||||
|
||||
/** @var PermissibleBase */
|
||||
private $perm;
|
||||
|
||||
/** @var int|null */
|
||||
@ -104,6 +105,8 @@ class ConsoleCommandSender implements CommandSender{
|
||||
|
||||
/**
|
||||
* @param TextContainer|string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendMessage($message){
|
||||
if($message instanceof TextContainer){
|
||||
@ -133,6 +136,8 @@ class ConsoleCommandSender implements CommandSender{
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOp(bool $value){
|
||||
|
||||
|
@ -32,6 +32,7 @@ use function strpos;
|
||||
use function substr;
|
||||
|
||||
class FormattedCommandAlias extends Command{
|
||||
/** @var string[] */
|
||||
private $formatStrings = [];
|
||||
|
||||
/**
|
||||
@ -74,14 +75,14 @@ class FormattedCommandAlias extends Command{
|
||||
$index = strpos($formatString, '$');
|
||||
while($index !== false){
|
||||
$start = $index;
|
||||
if($index > 0 and $formatString{$start - 1} === "\\"){
|
||||
if($index > 0 and $formatString[$start - 1] === "\\"){
|
||||
$formatString = substr($formatString, 0, $start - 1) . substr($formatString, $start);
|
||||
$index = strpos($formatString, '$', $index);
|
||||
continue;
|
||||
}
|
||||
|
||||
$required = false;
|
||||
if($formatString{$index + 1} == '$'){
|
||||
if($formatString[$index + 1] == '$'){
|
||||
$required = true;
|
||||
|
||||
++$index;
|
||||
@ -91,7 +92,7 @@ class FormattedCommandAlias extends Command{
|
||||
|
||||
$argStart = $index;
|
||||
|
||||
while($index < strlen($formatString) and self::inRange(ord($formatString{$index}) - 48, 0, 9)){
|
||||
while($index < strlen($formatString) and self::inRange(ord($formatString[$index]) - 48, 0, 9)){
|
||||
++$index;
|
||||
}
|
||||
|
||||
@ -109,7 +110,7 @@ class FormattedCommandAlias extends Command{
|
||||
|
||||
$rest = false;
|
||||
|
||||
if($index < strlen($formatString) and $formatString{$index} === "-"){
|
||||
if($index < strlen($formatString) and $formatString[$index] === "-"){
|
||||
$rest = true;
|
||||
++$index;
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ class PluginCommand extends Command implements PluginIdentifiableCommand{
|
||||
|
||||
/**
|
||||
* @param CommandExecutor $executor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setExecutor(CommandExecutor $executor){
|
||||
$this->executor = $executor;
|
||||
|
@ -41,6 +41,9 @@ class RemoteConsoleCommandSender extends ConsoleCommandSender{
|
||||
$this->messages .= trim($message, "\r\n") . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(){
|
||||
return $this->messages;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
$this->setDefaultCommands();
|
||||
}
|
||||
|
||||
private function setDefaultCommands(){
|
||||
private function setDefaultCommands() : void{
|
||||
$this->registerAll("pocketmine", [
|
||||
new BanCommand("ban"),
|
||||
new BanIpCommand("ban-ip"),
|
||||
@ -226,8 +226,8 @@ class SimpleCommandMap implements CommandMap{
|
||||
* This method is intended to provide capability for handling commands with spaces in their name.
|
||||
* The referenced parameters will be modified accordingly depending on the resulting matched command.
|
||||
*
|
||||
* @param string &$commandName
|
||||
* @param string[] &$args
|
||||
* @param string $commandName reference parameter
|
||||
* @param string[] $args reference parameter
|
||||
*
|
||||
* @return Command|null
|
||||
*/
|
||||
@ -328,12 +328,12 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($recursive)){
|
||||
if(count($recursive) > 0){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, implode(", ", $recursive)]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!empty($bad)){
|
||||
if(count($bad) > 0){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, implode(", ", $bad)]));
|
||||
continue;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class BanIpCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processIPBan(string $ip, CommandSender $sender, string $reason){
|
||||
private function processIPBan(string $ip, CommandSender $sender, string $reason) : void{
|
||||
$sender->getServer()->getIPBans()->addBan($ip, $reason, null, $sender->getName());
|
||||
|
||||
foreach($sender->getServer()->getOnlinePlayers() as $player){
|
||||
|
@ -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)){
|
||||
|
@ -65,7 +65,7 @@ abstract class VanillaCommand extends Command{
|
||||
* @return float
|
||||
*/
|
||||
protected function getRelativeDouble(float $original, CommandSender $sender, string $input, float $min = self::MIN_COORD, float $max = self::MAX_COORD) : float{
|
||||
if($input{0} === "~"){
|
||||
if($input[0] === "~"){
|
||||
$value = $this->getDouble($sender, substr($input, 1));
|
||||
|
||||
return $original + $value;
|
||||
|
@ -84,7 +84,7 @@ class VersionCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function describeToSender(Plugin $plugin, CommandSender $sender){
|
||||
private function describeToSender(Plugin $plugin, CommandSender $sender) : void{
|
||||
$desc = $plugin->getDescription();
|
||||
$sender->sendMessage(TextFormat::DARK_GREEN . $desc->getName() . TextFormat::WHITE . " version " . TextFormat::DARK_GREEN . $desc->getVersion());
|
||||
|
||||
|
@ -46,14 +46,22 @@ class Attribute{
|
||||
public const HORSE_JUMP_STRENGTH = 14;
|
||||
public const ZOMBIE_SPAWN_REINFORCEMENTS = 15;
|
||||
|
||||
/** @var int */
|
||||
private $id;
|
||||
/** @var float */
|
||||
protected $minValue;
|
||||
/** @var float */
|
||||
protected $maxValue;
|
||||
/** @var float */
|
||||
protected $defaultValue;
|
||||
/** @var float */
|
||||
protected $currentValue;
|
||||
/** @var string */
|
||||
protected $name;
|
||||
/** @var bool */
|
||||
protected $shouldSend;
|
||||
|
||||
/** @var bool */
|
||||
protected $desynchronized = true;
|
||||
|
||||
/** @var Attribute[] */
|
||||
@ -137,6 +145,11 @@ class Attribute{
|
||||
return $this->minValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $minValue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMinValue(float $minValue){
|
||||
if($minValue > ($max = $this->getMaxValue())){
|
||||
throw new \InvalidArgumentException("Minimum $minValue is greater than the maximum $max");
|
||||
@ -153,6 +166,11 @@ class Attribute{
|
||||
return $this->maxValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $maxValue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMaxValue(float $maxValue){
|
||||
if($maxValue < ($min = $this->getMinValue())){
|
||||
throw new \InvalidArgumentException("Maximum $maxValue is less than the minimum $min");
|
||||
@ -169,6 +187,11 @@ class Attribute{
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $defaultValue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaultValue(float $defaultValue){
|
||||
if($defaultValue > $this->getMaxValue() or $defaultValue < $this->getMinValue()){
|
||||
throw new \InvalidArgumentException("Default $defaultValue is outside the range " . $this->getMinValue() . " - " . $this->getMaxValue());
|
||||
|
@ -25,6 +25,9 @@ namespace pocketmine\entity;
|
||||
|
||||
use function array_filter;
|
||||
|
||||
/**
|
||||
* @phpstan-implements \ArrayAccess<int, float>
|
||||
*/
|
||||
class AttributeMap implements \ArrayAccess{
|
||||
/** @var Attribute[] */
|
||||
private $attributes = [];
|
||||
@ -58,6 +61,11 @@ class AttributeMap implements \ArrayAccess{
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset) : bool{
|
||||
return isset($this->attributes[$offset]);
|
||||
}
|
||||
@ -79,6 +87,9 @@ class AttributeMap implements \ArrayAccess{
|
||||
$this->attributes[$offset]->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*/
|
||||
public function offsetUnset($offset) : void{
|
||||
throw new \RuntimeException("Could not unset an attribute from an attribute map");
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use function assert;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
@ -32,8 +32,10 @@ use function is_string;
|
||||
|
||||
class DataPropertyManager{
|
||||
|
||||
/** @var mixed[][] */
|
||||
private $properties = [];
|
||||
|
||||
/** @var mixed[][] */
|
||||
private $dirtyProperties = [];
|
||||
|
||||
public function __construct(){
|
||||
@ -140,25 +142,14 @@ class DataPropertyManager{
|
||||
$this->setPropertyValue($key, Entity::DATA_TYPE_STRING, $value, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $key
|
||||
*
|
||||
* @return null|Item
|
||||
*/
|
||||
public function getItem(int $key) : ?Item{
|
||||
$value = $this->getPropertyValue($key, Entity::DATA_TYPE_SLOT);
|
||||
assert($value instanceof Item or $value === null);
|
||||
|
||||
public function getCompoundTag(int $key) : ?CompoundTag{
|
||||
$value = $this->getPropertyValue($key, Entity::DATA_TYPE_COMPOUND_TAG);
|
||||
assert($value instanceof CompoundTag or $value === null);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $key
|
||||
* @param Item $value
|
||||
* @param bool $force
|
||||
*/
|
||||
public function setItem(int $key, Item $value, bool $force = false) : void{
|
||||
$this->setPropertyValue($key, Entity::DATA_TYPE_SLOT, $value, $force);
|
||||
public function setCompoundTag(int $key, CompoundTag $value, bool $force = false) : void{
|
||||
$this->setPropertyValue($key, Entity::DATA_TYPE_COMPOUND_TAG, $value, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,7 +195,8 @@ class Effect{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default duration this effect will apply for if a duration is not specified.
|
||||
* Returns the default duration (in ticks) this effect will apply for if a duration is not specified.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDefaultDuration() : int{
|
||||
|
@ -75,6 +75,8 @@ class EffectInstance{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ticks remaining until the effect expires.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDuration() : int{
|
||||
@ -82,6 +84,8 @@ class EffectInstance{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ticks remaining until the effect expires.
|
||||
*
|
||||
* @param int $duration
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
|
@ -62,12 +62,12 @@ use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\AddEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetEntityMotionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetActorDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\Server;
|
||||
@ -102,7 +102,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_TYPE_INT = 2;
|
||||
public const DATA_TYPE_FLOAT = 3;
|
||||
public const DATA_TYPE_STRING = 4;
|
||||
public const DATA_TYPE_SLOT = 5;
|
||||
public const DATA_TYPE_COMPOUND_TAG = 5;
|
||||
public const DATA_TYPE_POS = 6;
|
||||
public const DATA_TYPE_LONG = 7;
|
||||
public const DATA_TYPE_VECTOR3F = 8;
|
||||
@ -154,70 +154,76 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_LEAD_HOLDER_EID = 37; //long
|
||||
public const DATA_SCALE = 38; //float
|
||||
public const DATA_HAS_NPC_COMPONENT = 39; //byte (???)
|
||||
public const DATA_SKIN_ID = 40; //string
|
||||
public const DATA_NPC_SKIN_ID = 41; //string
|
||||
public const DATA_URL_TAG = 42; //string
|
||||
public const DATA_MAX_AIR = 43; //short
|
||||
public const DATA_MARK_VARIANT = 44; //int
|
||||
public const DATA_CONTAINER_TYPE = 45; //byte (ContainerComponent)
|
||||
public const DATA_CONTAINER_BASE_SIZE = 46; //int (ContainerComponent)
|
||||
public const DATA_CONTAINER_EXTRA_SLOTS_PER_STRENGTH = 47; //int (used for llamas, inventory size is baseSize + thisProp * strength)
|
||||
public const DATA_BLOCK_TARGET = 48; //block coords (ender crystal)
|
||||
public const DATA_WITHER_INVULNERABLE_TICKS = 49; //int
|
||||
public const DATA_WITHER_TARGET_1 = 50; //long
|
||||
public const DATA_WITHER_TARGET_2 = 51; //long
|
||||
public const DATA_WITHER_TARGET_3 = 52; //long
|
||||
/* 53 (short) */
|
||||
public const DATA_BOUNDING_BOX_WIDTH = 54; //float
|
||||
public const DATA_BOUNDING_BOX_HEIGHT = 55; //float
|
||||
public const DATA_FUSE_LENGTH = 56; //int
|
||||
public const DATA_RIDER_SEAT_POSITION = 57; //vector3f
|
||||
public const DATA_RIDER_ROTATION_LOCKED = 58; //byte
|
||||
public const DATA_RIDER_MAX_ROTATION = 59; //float
|
||||
public const DATA_RIDER_MIN_ROTATION = 60; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS = 61; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_WAITING = 62; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 63; //int
|
||||
/* 64 (int) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_FACE = 65; //byte
|
||||
/* 66 (short) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_POS = 67; //block coords
|
||||
public const DATA_TRADING_PLAYER_EID = 68; //long
|
||||
public const DATA_NPC_SKIN_INDEX = 40; //string
|
||||
public const DATA_NPC_ACTIONS = 41; //string (maybe JSON blob?)
|
||||
public const DATA_MAX_AIR = 42; //short
|
||||
public const DATA_MARK_VARIANT = 43; //int
|
||||
public const DATA_CONTAINER_TYPE = 44; //byte (ContainerComponent)
|
||||
public const DATA_CONTAINER_BASE_SIZE = 45; //int (ContainerComponent)
|
||||
public const DATA_CONTAINER_EXTRA_SLOTS_PER_STRENGTH = 46; //int (used for llamas, inventory size is baseSize + thisProp * strength)
|
||||
public const DATA_BLOCK_TARGET = 47; //block coords (ender crystal)
|
||||
public const DATA_WITHER_INVULNERABLE_TICKS = 48; //int
|
||||
public const DATA_WITHER_TARGET_1 = 49; //long
|
||||
public const DATA_WITHER_TARGET_2 = 50; //long
|
||||
public const DATA_WITHER_TARGET_3 = 51; //long
|
||||
/* 52 (short) */
|
||||
public const DATA_BOUNDING_BOX_WIDTH = 53; //float
|
||||
public const DATA_BOUNDING_BOX_HEIGHT = 54; //float
|
||||
public const DATA_FUSE_LENGTH = 55; //int
|
||||
public const DATA_RIDER_SEAT_POSITION = 56; //vector3f
|
||||
public const DATA_RIDER_ROTATION_LOCKED = 57; //byte
|
||||
public const DATA_RIDER_MAX_ROTATION = 58; //float
|
||||
public const DATA_RIDER_MIN_ROTATION = 59; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS = 60; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_WAITING = 61; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 62; //int
|
||||
/* 63 (int) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_FACE = 64; //byte
|
||||
/* 65 (short) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_POS = 66; //block coords
|
||||
public const DATA_TRADING_PLAYER_EID = 67; //long
|
||||
|
||||
/* 70 (byte) command-block */
|
||||
public const DATA_COMMAND_BLOCK_COMMAND = 71; //string
|
||||
public const DATA_COMMAND_BLOCK_LAST_OUTPUT = 72; //string
|
||||
public const DATA_COMMAND_BLOCK_TRACK_OUTPUT = 73; //byte
|
||||
public const DATA_CONTROLLING_RIDER_SEAT_NUMBER = 74; //byte
|
||||
public const DATA_STRENGTH = 75; //int
|
||||
public const DATA_MAX_STRENGTH = 76; //int
|
||||
/* 77 (int) */
|
||||
public const DATA_LIMITED_LIFE = 78;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 79; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 80; //int
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 81; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 82; //byte
|
||||
/* 83 (unknown) */
|
||||
public const DATA_SCORE_TAG = 84; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 85; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 86; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 87; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 88; //long
|
||||
/* 89 (float) related to panda sitting
|
||||
* 90 (float) related to panda sitting */
|
||||
public const DATA_EAT_COUNTER = 91; //int (used by pandas)
|
||||
public const DATA_FLAGS2 = 92; //long (extended data flags)
|
||||
/* 93 (float) related to panda lying down
|
||||
* 94 (float) related to panda lying down */
|
||||
public const DATA_AREA_EFFECT_CLOUD_DURATION = 95; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_SPAWN_TIME = 96; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_PER_TICK = 97; //float, usually negative
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP = 98; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_PICKUP_COUNT = 99; //int
|
||||
public const DATA_INTERACTIVE_TAG = 100; //string (button text)
|
||||
public const DATA_TRADE_TIER = 101; //int
|
||||
public const DATA_MAX_TRADE_TIER = 102; //int
|
||||
public const DATA_TRADE_XP = 103; //int
|
||||
/* 69 (byte) command-block */
|
||||
public const DATA_COMMAND_BLOCK_COMMAND = 70; //string
|
||||
public const DATA_COMMAND_BLOCK_LAST_OUTPUT = 71; //string
|
||||
public const DATA_COMMAND_BLOCK_TRACK_OUTPUT = 72; //byte
|
||||
public const DATA_CONTROLLING_RIDER_SEAT_NUMBER = 73; //byte
|
||||
public const DATA_STRENGTH = 74; //int
|
||||
public const DATA_MAX_STRENGTH = 75; //int
|
||||
/* 76 (int) */
|
||||
public const DATA_LIMITED_LIFE = 77;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 80; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 81; //byte
|
||||
/* 82 (unknown) */
|
||||
public const DATA_SCORE_TAG = 83; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 84; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 85; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 86; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 87; //long
|
||||
/* 88 (float) related to panda sitting
|
||||
* 89 (float) related to panda sitting */
|
||||
public const DATA_EAT_COUNTER = 90; //int (used by pandas)
|
||||
public const DATA_FLAGS2 = 91; //long (extended data flags)
|
||||
/* 92 (float) related to panda lying down
|
||||
* 93 (float) related to panda lying down */
|
||||
public const DATA_AREA_EFFECT_CLOUD_DURATION = 94; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_SPAWN_TIME = 95; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_PER_TICK = 96; //float, usually negative
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP = 97; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_PICKUP_COUNT = 98; //int
|
||||
public const DATA_INTERACTIVE_TAG = 99; //string (button text)
|
||||
public const DATA_TRADE_TIER = 100; //int
|
||||
public const DATA_MAX_TRADE_TIER = 101; //int
|
||||
public const DATA_TRADE_XP = 102; //int
|
||||
public const DATA_SKIN_ID = 103; //int ???
|
||||
/* 104 (int) related to wither */
|
||||
public const DATA_COMMAND_BLOCK_TICK_DELAY = 105; //int
|
||||
public const DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 106; //byte
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_MIN = 107; //float
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_RANGE = 108; //float
|
||||
public const DATA_AMBIENT_SOUND_EVENT = 109; //string
|
||||
|
||||
public const DATA_FLAG_ONFIRE = 0;
|
||||
public const DATA_FLAG_SNEAKING = 1;
|
||||
@ -311,8 +317,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_PLAYER_FLAG_SLEEP = 1;
|
||||
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
|
||||
|
||||
/** @var int */
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
/** @var string[] */
|
||||
private static $knownEntities = [];
|
||||
/** @var string[][] */
|
||||
private static $saveNames = [];
|
||||
@ -454,15 +461,15 @@ 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;
|
||||
/** @var float|null */
|
||||
public $lastY = null;
|
||||
/** @var float|null */
|
||||
public $lastZ = null;
|
||||
/** @var float */
|
||||
public $lastX;
|
||||
/** @var float */
|
||||
public $lastY;
|
||||
/** @var float */
|
||||
public $lastZ;
|
||||
|
||||
/** @var Vector3 */
|
||||
protected $motion;
|
||||
@ -498,6 +505,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
/** @var float */
|
||||
private $health = 20.0;
|
||||
/** @var int */
|
||||
private $maxHealth = 20;
|
||||
|
||||
/** @var float */
|
||||
@ -1096,7 +1104,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->justCreated = false;
|
||||
|
||||
$changedProperties = $this->propertyManager->getDirty();
|
||||
if(!empty($changedProperties)){
|
||||
if(count($changedProperties) > 0){
|
||||
$this->sendData($this->hasSpawned, $changedProperties);
|
||||
$this->propertyManager->clearDirtyProperties();
|
||||
}
|
||||
@ -1137,7 +1145,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->setFireTicks($ticks);
|
||||
}
|
||||
|
||||
$this->setGenericFlag(self::DATA_FLAG_ONFIRE, true);
|
||||
$this->setGenericFlag(self::DATA_FLAG_ONFIRE, $this->isOnFire());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1204,15 +1212,19 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
protected function updateMovement(bool $teleport = false) : void{
|
||||
//TODO: hack for client-side AI interference: prevent client sided movement when motion is 0
|
||||
$this->setImmobile($this->motion->x == 0 and $this->motion->y == 0 and $this->motion->z == 0);
|
||||
|
||||
$diffPosition = ($this->x - $this->lastX) ** 2 + ($this->y - $this->lastY) ** 2 + ($this->z - $this->lastZ) ** 2;
|
||||
$diffRotation = ($this->yaw - $this->lastYaw) ** 2 + ($this->pitch - $this->lastPitch) ** 2;
|
||||
|
||||
$diffMotion = $this->motion->subtract($this->lastMotion)->lengthSquared();
|
||||
|
||||
if($teleport or $diffPosition > 0.0001 or $diffRotation > 1.0){
|
||||
$still = $this->motion->lengthSquared() == 0.0;
|
||||
$wasStill = $this->lastMotion->lengthSquared() == 0.0;
|
||||
if($wasStill !== $still){
|
||||
//TODO: hack for client-side AI interference: prevent client sided movement when motion is 0
|
||||
$this->setImmobile($still);
|
||||
}
|
||||
|
||||
if($teleport or $diffPosition > 0.0001 or $diffRotation > 1.0 or (!$wasStill and $still)){
|
||||
$this->lastX = $this->x;
|
||||
$this->lastY = $this->y;
|
||||
$this->lastZ = $this->z;
|
||||
@ -1223,7 +1235,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->broadcastMovement($teleport);
|
||||
}
|
||||
|
||||
if($diffMotion > 0.0025 or ($diffMotion > 0.0001 and $this->motion->lengthSquared() <= 0.0001)){ //0.05 ** 2
|
||||
if($diffMotion > 0.0025 or $wasStill !== $still){ //0.05 ** 2
|
||||
$this->lastMotion = clone $this->motion;
|
||||
|
||||
$this->broadcastMotion();
|
||||
@ -1235,7 +1247,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
protected function broadcastMovement(bool $teleport = false) : void{
|
||||
$pk = new MoveEntityAbsolutePacket();
|
||||
$pk = new MoveActorAbsolutePacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->position = $this->getOffsetPosition($this);
|
||||
|
||||
@ -1247,14 +1259,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$pk->zRot = $this->yaw;
|
||||
|
||||
if($teleport){
|
||||
$pk->flags |= MoveEntityAbsolutePacket::FLAG_TELEPORT;
|
||||
$pk->flags |= MoveActorAbsolutePacket::FLAG_TELEPORT;
|
||||
}
|
||||
|
||||
$this->level->broadcastPacketToViewers($this, $pk);
|
||||
}
|
||||
|
||||
protected function broadcastMotion() : void{
|
||||
$pk = new SetEntityMotionPacket();
|
||||
$pk = new SetActorMotionPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->motion = $this->getMotion();
|
||||
|
||||
@ -1776,6 +1788,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated WARNING: Despite what its name implies, this function DOES NOT return all the blocks around the entity.
|
||||
* Instead, it returns blocks which have reactions for an entity intersecting with them.
|
||||
*
|
||||
* @return Block[]
|
||||
*/
|
||||
public function getBlocksAround() : array{
|
||||
@ -2021,7 +2036,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param Player $player
|
||||
*/
|
||||
protected function sendSpawnPacket(Player $player) : void{
|
||||
$pk = new AddEntityPacket();
|
||||
$pk = new AddActorPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->type = static::NETWORK_ID;
|
||||
$pk->position = $this->asVector3();
|
||||
@ -2039,7 +2054,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);
|
||||
@ -2065,13 +2080,16 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated WARNING: This function DOES NOT permanently hide the entity from the player. As soon as the entity or
|
||||
* player moves, the player will once again be able to see the entity.
|
||||
*
|
||||
* @param Player $player
|
||||
* @param bool $send
|
||||
*/
|
||||
public function despawnFrom(Player $player, bool $send = true) : void{
|
||||
if(isset($this->hasSpawned[$player->getLoaderId()])){
|
||||
if($send){
|
||||
$pk = new RemoveEntityPacket();
|
||||
$pk = new RemoveActorPacket();
|
||||
$pk->entityUniqueId = $this->id;
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
@ -2079,6 +2097,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated WARNING: This function DOES NOT permanently hide the entity from viewers. As soon as the entity or
|
||||
* player moves, viewers will once again be able to see the entity.
|
||||
*/
|
||||
public function despawnFromAll() : void{
|
||||
foreach($this->hasSpawned as $player){
|
||||
$this->despawnFrom($player);
|
||||
@ -2193,7 +2215,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$player = [$player];
|
||||
}
|
||||
|
||||
$pk = new SetEntityDataPacket();
|
||||
$pk = new SetActorDataPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->metadata = $data ?? $this->propertyManager->getAll();
|
||||
|
||||
@ -2210,7 +2232,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{
|
||||
$pk = new EntityEventPacket();
|
||||
$pk = new ActorEventPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->event = $eventId;
|
||||
$pk->data = $eventData ?? 0;
|
||||
@ -2263,6 +2285,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
* @throws \ErrorException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
|
@ -26,5 +26,8 @@ namespace pocketmine\entity;
|
||||
|
||||
interface Explosive{
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function explode();
|
||||
}
|
||||
|
@ -46,13 +46,14 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\UUID;
|
||||
use function array_filter;
|
||||
@ -60,6 +61,7 @@ use function array_merge;
|
||||
use function array_rand;
|
||||
use function array_values;
|
||||
use function ceil;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function max;
|
||||
use function min;
|
||||
@ -79,6 +81,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
/** @var UUID */
|
||||
protected $uuid;
|
||||
/** @var string */
|
||||
protected $rawUUID;
|
||||
|
||||
public $width = 0.6;
|
||||
@ -88,10 +91,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
/** @var Skin */
|
||||
protected $skin;
|
||||
|
||||
/** @var int */
|
||||
protected $foodTickTimer = 0;
|
||||
|
||||
/** @var int */
|
||||
protected $totalXp = 0;
|
||||
/** @var int */
|
||||
protected $xpSeed;
|
||||
/** @var int */
|
||||
protected $xpCooldown = 0;
|
||||
|
||||
protected $baseOffset = 1.62;
|
||||
@ -182,7 +189,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
public function sendSkin(?array $targets = null) : void{
|
||||
$pk = new PlayerSkinPacket();
|
||||
$pk->uuid = $this->getUniqueId();
|
||||
$pk->skin = $this->skin;
|
||||
$pk->skin = SkinAdapterSingleton::get()->toSkinData($this->skin);
|
||||
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
|
||||
}
|
||||
|
||||
@ -553,7 +560,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($equipment)){
|
||||
if(count($equipment) > 0){
|
||||
$repairItem = $equipment[$k = array_rand($equipment)];
|
||||
if($repairItem->getDamage() > 0){
|
||||
$repairAmount = min($repairItem->getDamage(), $xpValue * 2);
|
||||
@ -587,6 +594,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return (int) min(100, 7 * $this->getXpLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PlayerInventory
|
||||
*/
|
||||
public function getInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
@ -754,7 +764,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$this->addEffect(new EffectInstance(Effect::getEffect(Effect::FIRE_RESISTANCE), 40 * 20, 1));
|
||||
$this->addEffect(new EffectInstance(Effect::getEffect(Effect::ABSORPTION), 5 * 20, 1));
|
||||
|
||||
$this->broadcastEntityEvent(EntityEventPacket::CONSUME_TOTEM);
|
||||
$this->broadcastEntityEvent(ActorEventPacket::CONSUME_TOTEM);
|
||||
$this->level->broadcastLevelEvent($this->add(0, $this->eyeHeight, 0), LevelEventPacket::EVENT_SOUND_TOTEM);
|
||||
|
||||
$hand = $this->inventory->getItemInHand();
|
||||
@ -847,7 +857,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)];
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))];
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\Player;
|
||||
@ -70,6 +70,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
protected $gravity = 0.08;
|
||||
protected $drag = 0.02;
|
||||
|
||||
/** @var int */
|
||||
protected $attackTime = 0;
|
||||
|
||||
/** @var int */
|
||||
@ -77,6 +78,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
/** @var int */
|
||||
protected $maxDeadTicks = 25;
|
||||
|
||||
/** @var float */
|
||||
protected $jumpVelocity = 0.42;
|
||||
|
||||
/** @var EffectInstance[] */
|
||||
@ -143,7 +145,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
parent::setHealth($amount);
|
||||
$this->attributeMap->getAttribute(Attribute::HEALTH)->setValue(ceil($this->getHealth()), true);
|
||||
if($this->isAlive() and !$wasAlive){
|
||||
$this->broadcastEntityEvent(EntityEventPacket::RESPAWN);
|
||||
$this->broadcastEntityEvent(ActorEventPacket::RESPAWN);
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +265,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* @return bool
|
||||
*/
|
||||
public function hasEffects() : bool{
|
||||
return !empty($this->effects);
|
||||
return count($this->effects) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,7 +334,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($colors)){
|
||||
if(count($colors) > 0){
|
||||
$this->propertyManager->setInt(Entity::DATA_POTION_COLOR, Color::mix(...$colors)->toARGB());
|
||||
$this->propertyManager->setByte(Entity::DATA_POTION_AMBIENT, $ambient ? 1 : 0);
|
||||
}else{
|
||||
@ -606,7 +608,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
protected function doHitAnimation() : void{
|
||||
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION);
|
||||
$this->broadcastEntityEvent(ActorEventPacket::HURT_ANIMATION);
|
||||
}
|
||||
|
||||
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4) : void{
|
||||
@ -664,7 +666,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
protected function startDeathAnimation() : void{
|
||||
$this->broadcastEntityEvent(EntityEventPacket::DEATH_ANIMATION);
|
||||
$this->broadcastEntityEvent(ActorEventPacket::DEATH_ANIMATION);
|
||||
}
|
||||
|
||||
protected function endDeathAnimation() : void{
|
||||
@ -713,7 +715,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($this->effects);
|
||||
return count($this->effects) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -893,7 +895,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
*/
|
||||
public function getTargetBlock(int $maxDistance, array $transparent = []) : ?Block{
|
||||
$line = $this->getLineOfSight($maxDistance, 1, $transparent);
|
||||
if(!empty($line)){
|
||||
if(count($line) > 0){
|
||||
return array_shift($line);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use function atan2;
|
||||
use function mt_rand;
|
||||
use function sqrt;
|
||||
@ -40,10 +40,12 @@ class Squid extends WaterAnimal{
|
||||
public $width = 0.95;
|
||||
public $height = 0.95;
|
||||
|
||||
/** @var Vector3 */
|
||||
/** @var Vector3|null */
|
||||
public $swimDirection = null;
|
||||
/** @var float */
|
||||
public $swimSpeed = 0.1;
|
||||
|
||||
/** @var int */
|
||||
private $switchDirectionTicker = 0;
|
||||
|
||||
public function initEntity() : void{
|
||||
@ -68,7 +70,7 @@ class Squid extends WaterAnimal{
|
||||
$this->swimDirection = (new Vector3($this->x - $e->x, $this->y - $e->y, $this->z - $e->z))->normalize();
|
||||
}
|
||||
|
||||
$this->broadcastEntityEvent(EntityEventPacket::SQUID_INK_CLOUD);
|
||||
$this->broadcastEntityEvent(ActorEventPacket::SQUID_INK_CLOUD);
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +84,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;
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\item\ItemFactory;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use function abs;
|
||||
use function get_class;
|
||||
|
||||
class FallingBlock extends Entity{
|
||||
@ -110,7 +111,7 @@ class FallingBlock extends Entity{
|
||||
$this->flagForDespawn();
|
||||
|
||||
$block = $this->level->getBlock($pos);
|
||||
if($block->getId() > 0 and $block->isTransparent() and !$block->canBeReplaced()){
|
||||
if(($block->isTransparent() and !$block->canBeReplaced()) or ($this->onGround and abs($this->y - $this->getFloorY()) > 0.001)){
|
||||
//FIXME: anvils are supposed to destroy torches
|
||||
$this->getLevel()->dropItem($this, ItemFactory::get($this->getBlock(), $this->getDamage()));
|
||||
}else{
|
||||
|
@ -28,8 +28,8 @@ use pocketmine\event\entity\ItemDespawnEvent;
|
||||
use pocketmine\event\entity\ItemSpawnEvent;
|
||||
use pocketmine\event\inventory\InventoryPickupItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\protocol\AddItemEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\Player;
|
||||
use function get_class;
|
||||
|
||||
@ -192,7 +192,7 @@ class ItemEntity extends Entity{
|
||||
}
|
||||
|
||||
protected function sendSpawnPacket(Player $player) : void{
|
||||
$pk = new AddItemEntityPacket();
|
||||
$pk = new AddItemActorPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = $this->asVector3();
|
||||
$pk->motion = $this->getMotion();
|
||||
@ -229,7 +229,7 @@ class ItemEntity extends Entity{
|
||||
break;
|
||||
}
|
||||
|
||||
$pk = new TakeItemEntityPacket();
|
||||
$pk = new TakeItemActorPacket();
|
||||
$pk->eid = $player->getId();
|
||||
$pk->target = $this->getId();
|
||||
$this->server->broadcastPacket($this->getViewers(), $pk);
|
||||
|
@ -90,6 +90,9 @@ class Painting extends Entity{
|
||||
}
|
||||
|
||||
public function kill() : void{
|
||||
if(!$this->isAlive()){
|
||||
return;
|
||||
}
|
||||
parent::kill();
|
||||
|
||||
$drops = true;
|
||||
@ -152,9 +155,11 @@ class Painting extends Entity{
|
||||
protected function sendSpawnPacket(Player $player) : void{
|
||||
$pk = new AddPaintingPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->x = $this->blockIn->x;
|
||||
$pk->y = $this->blockIn->y;
|
||||
$pk->z = $this->blockIn->z;
|
||||
$pk->position = new Vector3(
|
||||
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
|
||||
($this->boundingBox->minY + $this->boundingBox->maxY) / 2,
|
||||
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
|
||||
);
|
||||
$pk->direction = $this->direction;
|
||||
$pk->title = $this->motive;
|
||||
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\entity\Explosive;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\ExplosionPrimeEvent;
|
||||
use pocketmine\level\Explosion;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
|
||||
@ -42,6 +43,7 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
protected $gravity = 0.04;
|
||||
protected $drag = 0.02;
|
||||
|
||||
/** @var int */
|
||||
protected $fuse;
|
||||
|
||||
public $canCollide = false;
|
||||
@ -105,7 +107,7 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
$ev = new ExplosionPrimeEvent($this, 4);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$explosion = new Explosion($this, $ev->getForce(), $this);
|
||||
$explosion = new Explosion(Position::fromObject($this->add(0, $this->height / 2, 0), $this->level), $ev->getForce(), $this);
|
||||
if($ev->isBlockBreaking()){
|
||||
$explosion->explodeA();
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ use pocketmine\item\ItemFactory;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
use function sqrt;
|
||||
@ -143,7 +143,7 @@ class Arrow extends Projectile{
|
||||
|
||||
protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{
|
||||
parent::onHitBlock($blockHit, $hitResult);
|
||||
$this->broadcastEntityEvent(EntityEventPacket::ARROW_SHAKE, 7); //7 ticks
|
||||
$this->broadcastEntityEvent(ActorEventPacket::ARROW_SHAKE, 7); //7 ticks
|
||||
}
|
||||
|
||||
protected function onHitEntity(Entity $entityHit, RayTraceResult $hitResult) : void{
|
||||
@ -193,7 +193,7 @@ class Arrow extends Projectile{
|
||||
return;
|
||||
}
|
||||
|
||||
$pk = new TakeItemEntityPacket();
|
||||
$pk = new TakeItemActorPacket();
|
||||
$pk->eid = $player->getId();
|
||||
$pk->target = $this->getId();
|
||||
$this->server->broadcastPacket($this->getViewers(), $pk);
|
||||
|
@ -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){
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\item\Potion;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\utils\Color;
|
||||
use function count;
|
||||
use function round;
|
||||
use function sqrt;
|
||||
|
||||
@ -63,7 +64,7 @@ class SplashPotion extends Throwable{
|
||||
$effects = $this->getPotionEffects();
|
||||
$hasEffects = true;
|
||||
|
||||
if(empty($effects)){
|
||||
if(count($effects) === 0){
|
||||
$colors = [
|
||||
new Color(0x38, 0x5d, 0xc6) //Default colour for splash water bottle and similar with no effects.
|
||||
];
|
||||
|
@ -35,6 +35,8 @@ interface Cancellable{
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCancelled(bool $value = true);
|
||||
}
|
||||
|
@ -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){
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
|
||||
class EntityCombustEvent extends EntityEvent implements Cancellable{
|
||||
/** @var int */
|
||||
protected $duration;
|
||||
|
||||
/**
|
||||
|
@ -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,16 @@ class PlayerCreationEvent extends Event{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player::class
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseClass(){
|
||||
return $this->baseClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player::class $class
|
||||
* @param string $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setBaseClass($class){
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
@ -109,14 +111,16 @@ class PlayerCreationEvent extends Event{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player::class
|
||||
* @return string
|
||||
*/
|
||||
public function getPlayerClass(){
|
||||
return $this->playerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player::class $class
|
||||
* @param string $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPlayerClass($class){
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
|
@ -40,6 +40,7 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
|
||||
/** @var TextContainer|string */
|
||||
private $deathMessage;
|
||||
/** @var bool */
|
||||
private $keepInventory = false;
|
||||
|
||||
/**
|
||||
|
@ -46,8 +46,8 @@ abstract class BaseInventory implements Inventory{
|
||||
protected $name;
|
||||
/** @var string */
|
||||
protected $title;
|
||||
/** @var \SplFixedArray|Item[] */
|
||||
protected $slots = [];
|
||||
/** @var \SplFixedArray|(Item|null)[] */
|
||||
protected $slots;
|
||||
/** @var Player[] */
|
||||
protected $viewers = [];
|
||||
/** @var InventoryEventProcessor */
|
||||
@ -84,6 +84,8 @@ abstract class BaseInventory implements Inventory{
|
||||
* WARNING: If the size is smaller, any items past the new size will be lost.
|
||||
*
|
||||
* @param int $size
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSize(int $size){
|
||||
$this->slots->setSize($size);
|
||||
|
@ -43,7 +43,7 @@ class CraftingManager{
|
||||
/** @var FurnaceRecipe[] */
|
||||
protected $furnaceRecipes = [];
|
||||
|
||||
/** @var BatchPacket */
|
||||
/** @var BatchPacket|null */
|
||||
private $craftingDataCache;
|
||||
|
||||
public function __construct(){
|
||||
|
@ -142,6 +142,9 @@ class DoubleChestInventory extends ChestInventory implements InventoryHolder{
|
||||
return $this->right;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function invalidate(){
|
||||
$this->left = null;
|
||||
$this->right = null;
|
||||
|
@ -53,6 +53,8 @@ class EnderChestInventory extends ChestInventory{
|
||||
* Set the holder's position to that of a tile
|
||||
*
|
||||
* @param EnderChest $enderChest
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHolderPosition(EnderChest $enderChest){
|
||||
$this->holder->setComponents($enderChest->getFloorX(), $enderChest->getFloorY(), $enderChest->getFloorZ());
|
||||
|
@ -44,6 +44,8 @@ class FurnaceRecipe implements Recipe{
|
||||
|
||||
/**
|
||||
* @param Item $item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInput(Item $item){
|
||||
$this->ingredient = clone $item;
|
||||
|
@ -39,6 +39,7 @@ class MultiRecipe{
|
||||
public const TYPE_FIREWORKS = "00000000-0000-0000-0000-000000000002";
|
||||
public const TYPE_MAP_LOCKING_CARTOGRAPHY = "602234E4-CAC1-4353-8BB7-B1EBFF70024B";
|
||||
|
||||
/** @var UUID */
|
||||
private $uuid;
|
||||
|
||||
public function __construct(UUID $uuid){
|
||||
|
@ -66,19 +66,23 @@ class PlayerInventory extends BaseInventory{
|
||||
* @return bool if the equipment change was successful, false if not.
|
||||
*/
|
||||
public function equipItem(int $hotbarSlot) : bool{
|
||||
$holder = $this->getHolder();
|
||||
if(!$this->isHotbarSlot($hotbarSlot)){
|
||||
$this->sendContents($this->getHolder());
|
||||
if($holder instanceof Player){
|
||||
$this->sendContents($holder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$ev = new PlayerItemHeldEvent($this->getHolder(), $this->getItem($hotbarSlot), $hotbarSlot);
|
||||
$ev->call();
|
||||
if($holder instanceof Player){
|
||||
$ev = new PlayerItemHeldEvent($holder, $this->getItem($hotbarSlot), $hotbarSlot);
|
||||
$ev->call();
|
||||
|
||||
if($ev->isCancelled()){
|
||||
$this->sendHeldItem($this->getHolder());
|
||||
return false;
|
||||
if($ev->isCancelled()){
|
||||
$this->sendHeldItem($holder);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setHeldItemIndex($hotbarSlot, false);
|
||||
|
||||
return true;
|
||||
@ -93,7 +97,7 @@ class PlayerInventory extends BaseInventory{
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function throwIfNotHotbarSlot(int $slot){
|
||||
private function throwIfNotHotbarSlot(int $slot) : void{
|
||||
if(!$this->isHotbarSlot($slot)){
|
||||
throw new \InvalidArgumentException("$slot is not a valid hotbar slot index (expected 0 - " . ($this->getHotbarSize() - 1) . ")");
|
||||
}
|
||||
@ -128,6 +132,7 @@ class PlayerInventory extends BaseInventory{
|
||||
* @param bool $send Whether to send updates back to the inventory holder. This should usually be true for plugin calls.
|
||||
* It should only be false to prevent feedback loops of equipment packets between client and server.
|
||||
*
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException if the hotbar slot is out of range
|
||||
*/
|
||||
public function setHeldItemIndex(int $hotbarSlot, bool $send = true){
|
||||
@ -166,6 +171,8 @@ class PlayerInventory extends BaseInventory{
|
||||
* Sends the currently-held item to specified targets.
|
||||
*
|
||||
* @param Player|Player[] $target
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendHeldItem($target){
|
||||
$item = $this->getItemInHand();
|
||||
@ -197,6 +204,9 @@ class PlayerInventory extends BaseInventory{
|
||||
return 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function sendCreativeContents(){
|
||||
//TODO: this mess shouldn't be in here
|
||||
$holder = $this->getHolder();
|
||||
|
@ -79,8 +79,8 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
}
|
||||
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
if($row{$x} !== ' ' and !isset($ingredients[$row{$x}])){
|
||||
throw new \InvalidArgumentException("No item specified for symbol '" . $row{$x} . "'");
|
||||
if($row[$x] !== ' ' and !isset($ingredients[$row[$x]])){
|
||||
throw new \InvalidArgumentException("No item specified for symbol '" . $row[$x] . "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,6 @@ class ShapelessRecipe implements CraftingRecipe{
|
||||
return false; //failed to match the needed item to a given item
|
||||
}
|
||||
|
||||
return empty($input); //crafting grid should be empty apart from the given ingredient stacks
|
||||
return count($input) === 0; //crafting grid should be empty apart from the given ingredient stacks
|
||||
}
|
||||
}
|
||||
|
@ -65,16 +65,17 @@ 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)){
|
||||
if(count($recipeItems) === 0){
|
||||
throw new TransactionValidationException("No recipe items given");
|
||||
}
|
||||
if(empty($txItems)){
|
||||
if(count($txItems) === 0){
|
||||
throw new TransactionValidationException("No transaction items given");
|
||||
}
|
||||
|
||||
while(!empty($recipeItems)){
|
||||
while(count($recipeItems) > 0){
|
||||
/** @var Item $recipeItem */
|
||||
$recipeItem = array_pop($recipeItems);
|
||||
$needCount = $recipeItem->getCount();
|
||||
@ -113,7 +114,7 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
if($iterations < 1){
|
||||
throw new TransactionValidationException("Tried to craft zero times");
|
||||
}
|
||||
if(!empty($txItems)){
|
||||
if(count($txItems) > 0){
|
||||
//all items should be destroyed in this process
|
||||
throw new TransactionValidationException("Expected 0 ingredients left over, have " . count($txItems));
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ use function spl_object_hash;
|
||||
* @see InventoryAction
|
||||
*/
|
||||
class InventoryTransaction{
|
||||
/** @var bool */
|
||||
protected $hasExecuted = false;
|
||||
/** @var Player */
|
||||
protected $source;
|
||||
@ -237,13 +238,13 @@ class InventoryTransaction{
|
||||
* @return null|Item
|
||||
*/
|
||||
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
|
||||
assert(!empty($possibleActions));
|
||||
assert(count($possibleActions) > 0);
|
||||
|
||||
foreach($possibleActions as $i => $action){
|
||||
if($action->getSourceItem()->equalsExact($needOrigin)){
|
||||
$newList = $possibleActions;
|
||||
unset($newList[$i]);
|
||||
if(empty($newList)){
|
||||
if(count($newList) === 0){
|
||||
return $action->getTargetItem();
|
||||
}
|
||||
$result = $this->findResultItem($action->getTargetItem(), $newList);
|
||||
|
@ -37,6 +37,7 @@ class CreativeInventoryAction extends InventoryAction{
|
||||
*/
|
||||
public const TYPE_CREATE_ITEM = 1;
|
||||
|
||||
/** @var int */
|
||||
protected $actionType;
|
||||
|
||||
public function __construct(Item $sourceItem, Item $targetItem, int $actionType){
|
||||
|
@ -85,7 +85,7 @@ class Bucket extends Item implements Consumable{
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$player->getLevel()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true);
|
||||
$player->getLevel()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound());
|
||||
$player->getLevel()->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound());
|
||||
|
||||
if($player->isSurvival()){
|
||||
$player->getInventory()->setItemInHand($ev->getItem());
|
||||
|
@ -49,6 +49,8 @@ interface Consumable{
|
||||
* Called when this Consumable is consumed by mob, after standard resulting effects have been applied.
|
||||
*
|
||||
* @param Living $consumer
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onConsume(Living $consumer);
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ abstract class Durable extends Item{
|
||||
* Sets whether the item will take damage when used.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUnbreakable(bool $value = true){
|
||||
$this->setNamedTagEntry(new ByteTag("Unbreakable", $value ? 1 : 0));
|
||||
|
@ -123,6 +123,9 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/** @var Item[] */
|
||||
private static $creative = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function initCreativeItems(){
|
||||
self::clearCreativeItems();
|
||||
|
||||
@ -137,6 +140,12 @@ 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.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearCreativeItems(){
|
||||
Item::$creative = [];
|
||||
}
|
||||
@ -145,10 +154,26 @@ 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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function removeCreativeItem(Item $item){
|
||||
$index = self::getCreativeItemIndex($item);
|
||||
if($index !== -1){
|
||||
@ -217,7 +242,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
*
|
||||
* @param CompoundTag|string|null $tags
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setCompoundTag($tags) : Item{
|
||||
if($tags instanceof CompoundTag){
|
||||
@ -256,6 +281,9 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
return $this->getNamedTagEntry(self::TAG_BLOCK_ENTITY_TAG) instanceof CompoundTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function clearCustomBlockData(){
|
||||
$this->removeNamedTagEntry(self::TAG_BLOCK_ENTITY_TAG);
|
||||
return $this;
|
||||
@ -264,7 +292,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/**
|
||||
* @param CompoundTag $compound
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setCustomBlockData(CompoundTag $compound) : Item{
|
||||
$tags = clone $compound;
|
||||
@ -463,11 +491,11 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setCustomName(string $name) : Item{
|
||||
if($name === ""){
|
||||
$this->clearCustomName();
|
||||
return $this->clearCustomName();
|
||||
}
|
||||
|
||||
/** @var CompoundTag $display */
|
||||
@ -483,7 +511,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function clearCustomName() : Item{
|
||||
$display = $this->getNamedTagEntry(self::TAG_DISPLAY);
|
||||
@ -515,7 +543,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/**
|
||||
* @param string[] $lines
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setLore(array $lines) : Item{
|
||||
$display = $this->getNamedTagEntry(self::TAG_DISPLAY);
|
||||
@ -572,7 +600,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
*
|
||||
* @param CompoundTag $tag
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamedTag(CompoundTag $tag) : Item{
|
||||
if($tag->getCount() === 0){
|
||||
@ -587,7 +615,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
|
||||
/**
|
||||
* Removes the Item's NBT.
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function clearNamedTag() : Item{
|
||||
return $this->setCompoundTag("");
|
||||
@ -603,7 +631,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/**
|
||||
* @param int $count
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setCount(int $count) : Item{
|
||||
$this->count = $count;
|
||||
@ -616,7 +644,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
*
|
||||
* @param int $count
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException if trying to pop more items than are on the stack
|
||||
*/
|
||||
public function pop(int $count = 1) : Item{
|
||||
@ -684,7 +712,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/**
|
||||
* @param int $meta
|
||||
*
|
||||
* @return Item
|
||||
* @return $this
|
||||
*/
|
||||
public function setDamage(int $meta) : Item{
|
||||
$this->meta = $meta !== -1 ? $meta & 0x7FFF : -1;
|
||||
|
@ -46,6 +46,9 @@ class ItemFactory{
|
||||
/** @var \SplFixedArray */
|
||||
private static $list = null;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function init(){
|
||||
self::$list = new \SplFixedArray(65536);
|
||||
|
||||
@ -285,6 +288,7 @@ class ItemFactory{
|
||||
* @param Item $item
|
||||
* @param bool $override
|
||||
*
|
||||
* @return void
|
||||
* @throws \RuntimeException if something attempted to override an already-registered item without specifying the
|
||||
* $override parameter.
|
||||
*/
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\Player;
|
||||
use function array_rand;
|
||||
use function count;
|
||||
|
||||
class PaintingItem extends Item{
|
||||
public function __construct(int $meta = 0){
|
||||
@ -66,7 +67,7 @@ class PaintingItem extends Item{
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($motives)){ //No space available
|
||||
if(count($motives) === 0){ //No space available
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user