mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
Compare commits
401 Commits
1.7dev-100
...
3.0.10
Author | SHA1 | Date | |
---|---|---|---|
1ff6f8846e | |||
87f458f9bd | |||
5a7e575c3a | |||
d6d98183ea | |||
9ff5c65fb6 | |||
1532b0ef6d | |||
9ece971a2b | |||
5546c88f88 | |||
e8c7ae595d | |||
0d9f40873f | |||
a4aee98cba | |||
a97c7d3132 | |||
4a1ed21e52 | |||
1b053c7928 | |||
c684f99cc4 | |||
695793795e | |||
a4965842d6 | |||
1ef6f5d166 | |||
eccc249009 | |||
dbaf7287bc | |||
b3b240e25b | |||
2b30ef1671 | |||
124ebf69c5 | |||
4274640845 | |||
58b665985e | |||
0f5c48e342 | |||
08ad5db05b | |||
94e8623c75 | |||
921f7e8f6a | |||
710e1d014d | |||
7fc22d3227 | |||
7bfe487ee5 | |||
d8cf835f92 | |||
65e44364e5 | |||
ebbbc581ca | |||
8aa8280a63 | |||
6a637d9099 | |||
06b80a9536 | |||
b3ffce9729 | |||
ce9f18c6b4 | |||
9610c55b19 | |||
b01b477a2a | |||
2d454ae56f | |||
066c9d4fd4 | |||
23829952c3 | |||
7ee98ff139 | |||
f1cab91ac9 | |||
e0bc9c5e96 | |||
70caa00266 | |||
ee7c838040 | |||
34e9e93210 | |||
5dbb0d177e | |||
58f0ad3e3e | |||
0df3585c81 | |||
697723b551 | |||
5926d80525 | |||
a57ec1b1ba | |||
905259a4e1 | |||
ca6930006c | |||
33eeeb856e | |||
c43ce5c8fa | |||
57cfe9fd43 | |||
d8824e7ee1 | |||
3455d0f3b9 | |||
6b2250cbce | |||
8dae497610 | |||
cade15e2dd | |||
d3e54db146 | |||
0081e30a89 | |||
76174f1920 | |||
dd6b5902a6 | |||
87852f2fe1 | |||
056d24c67d | |||
484d34fe04 | |||
6c6630d845 | |||
a5a236084f | |||
641a5a5e23 | |||
ebacb8525f | |||
579ab5866b | |||
56b04fa0bb | |||
95787c2be9 | |||
7b7be9618c | |||
4a8232d591 | |||
40e5a1aacb | |||
dbda044229 | |||
d2a037de71 | |||
03510333dc | |||
064e9464bc | |||
2d3ce9e8b0 | |||
49f80830a7 | |||
80daaf09b2 | |||
30ad3a1705 | |||
4e7d1a7947 | |||
da6439e3f4 | |||
5f2d4c36c0 | |||
1f9bed275a | |||
77f3ca4d47 | |||
d88368ceb6 | |||
f77a829a52 | |||
f315aca4c3 | |||
9f7f62e9e5 | |||
cc97f76ec9 | |||
3b0aad38cf | |||
a9b7cd1699 | |||
37b65aac91 | |||
ad7787e13b | |||
7b0ce16b12 | |||
0ff6b7b572 | |||
763e20ba4e | |||
4b99285fd6 | |||
03a55d5e9d | |||
fe29b89fd1 | |||
b0780c4d1d | |||
c835c97aba | |||
78eae28a3e | |||
31c187f366 | |||
2e6afa54c2 | |||
e36a6dc8cc | |||
2e9e44ba05 | |||
c9ec6f0a63 | |||
60836ebec9 | |||
3def3cd502 | |||
b5da6b1591 | |||
8a9af7bf2f | |||
8cfd5604cf | |||
f51743765d | |||
0b9ce8a0d4 | |||
c3c360f589 | |||
5a55d434ab | |||
19d2d6b91c | |||
ff2e99ecdc | |||
07a156f5c4 | |||
13fe8ee96d | |||
eb0276d459 | |||
cfb10360ff | |||
05af87e1d4 | |||
b3ea9606c7 | |||
a080a9b75c | |||
137a05c418 | |||
245f5c6bef | |||
3be6665e3d | |||
8704d378d4 | |||
b9718f9e87 | |||
1d1e6966a2 | |||
610b7bd8b0 | |||
17607b8116 | |||
4c98d9d3ad | |||
7d5b3079bc | |||
88d83e0fca | |||
4b221c0601 | |||
e867427f71 | |||
c4c6c58615 | |||
89643ff9af | |||
9657d50aeb | |||
3725bea3e5 | |||
f3a84b332b | |||
f6481eab8f | |||
8e5aca70b4 | |||
85136b7b4a | |||
47742d74c8 | |||
ca54c8d78e | |||
601811f0f8 | |||
aeb551b317 | |||
37b445f210 | |||
d04991feb6 | |||
c327b3d2c4 | |||
af69418a55 | |||
8cd311bcb4 | |||
78ec3937bf | |||
4e3e807741 | |||
8c6161a4f2 | |||
c8a87b14d5 | |||
8fca7cc68d | |||
45f940681a | |||
e3c97d7d5e | |||
172abef2a7 | |||
709abb02e6 | |||
428ca29e4b | |||
f61ad20f6b | |||
3c9af5cd6d | |||
996935e9b2 | |||
3707a41b67 | |||
354b2dc5d1 | |||
0c70b83d81 | |||
083a1e1ff6 | |||
17b58357fb | |||
96a4dbb7d8 | |||
5eec683110 | |||
0bca3cd481 | |||
b54197904d | |||
fb484087a8 | |||
14914781fc | |||
fdd5b7b9c9 | |||
c83c0eb935 | |||
b331f8e1c9 | |||
ee787974f2 | |||
73e56c8a36 | |||
5f7c884255 | |||
2b5e6b790f | |||
1a21041d00 | |||
7b17a83227 | |||
edd150971e | |||
38f4afb17c | |||
9d16863b1a | |||
41a179e6e1 | |||
3a31c531af | |||
e081b7dffa | |||
0233ae1eb6 | |||
dce8ed9dd1 | |||
35eaf38ca1 | |||
1d71f0cf43 | |||
9644766df3 | |||
857f6dd5df | |||
0d177d5219 | |||
fe21f0e916 | |||
4c1d29cdf7 | |||
fa21cd96c5 | |||
a22e5616f6 | |||
b6317fa7ce | |||
b1cb63ebd6 | |||
7b7917939a | |||
6aaaaefd2f | |||
1bb0337420 | |||
b6b0bbde18 | |||
5d07f66d86 | |||
ec28612a12 | |||
6047810113 | |||
d535fe20a3 | |||
515e4aabc4 | |||
f27c6fcf70 | |||
7864a315f6 | |||
02b4eeeb9b | |||
6b4b4e4bb1 | |||
f2b8d6879f | |||
60212cef2f | |||
15270f8329 | |||
05ef13b23a | |||
1b4723d816 | |||
c493d0e6ac | |||
b3043f9552 | |||
18fdbc2834 | |||
a8c766be88 | |||
e20be3eeba | |||
51f43fb375 | |||
132746aa3d | |||
d03f36ebee | |||
7fce48d38c | |||
b7ca045c51 | |||
81957d133d | |||
299e4c8a85 | |||
0a50b8cb9b | |||
6d53350291 | |||
ad61d70eee | |||
353a1d69db | |||
1d8b77f16e | |||
e3d2fa10a5 | |||
ad15ab5b42 | |||
b003295d01 | |||
4f8f334436 | |||
71fdd59c4c | |||
0a9ed059d6 | |||
74c0863905 | |||
87ff1c0382 | |||
2eaba7c936 | |||
3ee6bfca2a | |||
63ab27550a | |||
d612988882 | |||
c9a0c381b1 | |||
982444949c | |||
8cf0fc63d8 | |||
c18ba38b74 | |||
3a1df1d99e | |||
8ccd13319c | |||
c513d355cb | |||
02b53785be | |||
9dd0ee7f05 | |||
595f1f58da | |||
509e8c5f6d | |||
263cd900a8 | |||
164ce76ff5 | |||
fbf760bafe | |||
2c1afe5f2c | |||
b109b457dc | |||
e12e2897bb | |||
8f41384923 | |||
acf29711c2 | |||
1c4dd4f280 | |||
faa88a55e4 | |||
c9ed517063 | |||
28b0f5f86a | |||
e87e2d4e52 | |||
86c27953ec | |||
5552704922 | |||
c7ac5dfd4b | |||
bd9b59f401 | |||
2f03f5f6d5 | |||
f4a26ddfd9 | |||
adb9390b53 | |||
6111ce7df1 | |||
1f73c08762 | |||
2900167ffa | |||
11cc9f19ad | |||
807af2e6fb | |||
f2511983cf | |||
bac649137b | |||
71224f51d5 | |||
6c3fc4af46 | |||
75d13be38e | |||
9bc860f7a8 | |||
66963fbf9a | |||
172c6420c1 | |||
e7fc9227bc | |||
13cd0cdcfd | |||
0bb5e88b5c | |||
389990e0a8 | |||
067aad9546 | |||
b1a7606e82 | |||
febba6e3a6 | |||
d8dc89e7c8 | |||
b75413e3c4 | |||
f08537a1e0 | |||
6643fa5f09 | |||
210e108574 | |||
813437e3ee | |||
24295ce02f | |||
29fd26627e | |||
22b91aaa24 | |||
f757ba1851 | |||
c285295037 | |||
8312ad709e | |||
63fc04b3dd | |||
34b8557094 | |||
edaef588ab | |||
889222e9c5 | |||
8239c67b1a | |||
ed65e91a3c | |||
619390c5b7 | |||
7e70569ba2 | |||
083ac8a770 | |||
b21572774a | |||
b8523cb304 | |||
6ceb9af749 | |||
bcd197d7bb | |||
3148f692c1 | |||
d8d22efc3b | |||
7b3653f75d | |||
9c5f7128a4 | |||
1e4a97f921 | |||
4d743ade45 | |||
78b5cc993b | |||
5e91c05424 | |||
e7c5d14af3 | |||
126a97b405 | |||
753ed3801d | |||
68ef4b210d | |||
c3822b795c | |||
be0e85dfae | |||
72690ea7f5 | |||
c9bd60123b | |||
05f4262e81 | |||
dd11bcaf11 | |||
b96adda14d | |||
5c66c615bf | |||
2ff2a2de08 | |||
78f8d54f89 | |||
2a0a2134d1 | |||
e70af362d0 | |||
24aab8365e | |||
0e5504ed3f | |||
83008440c0 | |||
b14dfa9f7e | |||
197102ca3d | |||
f497e43db3 | |||
38c3f00ef7 | |||
396056c636 | |||
dd1dfefd83 | |||
7565b786e7 | |||
ae0c1c185f | |||
723251e800 | |||
295016cbc1 | |||
d80c471ae1 | |||
b4068dfd2f | |||
3095eb544d | |||
0247dff909 | |||
5368120f13 | |||
2e7db552dc | |||
53c35aaa1d | |||
3293074cfc | |||
d8f4dde5f3 | |||
dfa6cd2b7e | |||
e03d2b23f7 | |||
96a2fd7482 | |||
88c56bcdc8 | |||
2731fc3d0f | |||
a02f178f80 | |||
96d26a77a1 | |||
554fe4d14d | |||
532269a484 | |||
1e2122d854 | |||
8d645b714f | |||
403e996d2f |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
players/*
|
||||
worlds/*
|
||||
plugin_data/*
|
||||
plugins/*
|
||||
bin*/*
|
||||
timings/*
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
||||
[submodule "tests/plugins/PocketMine-TesterPlugin"]
|
||||
path = tests/plugins/PocketMine-TesterPlugin
|
||||
url = https://github.com/pmmp/PocketMine-TesterPlugin.git
|
||||
[submodule "src/pocketmine/resources/vanilla"]
|
||||
path = src/pocketmine/resources/vanilla
|
||||
url = https://github.com/pmmp/BedrockData.git
|
||||
|
@ -6,9 +6,9 @@ php:
|
||||
before_script:
|
||||
# - pecl install channel://pecl.php.net/pthreads-3.1.6
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.0.2
|
||||
- git clone https://github.com/krakjoe/pthreads.git
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout d32079fb4a88e6e008104d36dbbf0c2dd7deb403
|
||||
- git checkout c8cfacda84f21032d6014b53e72bf345ac901dac
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
@ -18,7 +18,7 @@ before_script:
|
||||
- composer install
|
||||
|
||||
script:
|
||||
- ./tests/travis.sh
|
||||
- ./tests/travis.sh -t4
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
@ -42,7 +42,7 @@ We try to ensure that our project's codebase is as clean as possible and ensure
|
||||
- **Details should be provided of tests done.** Simply saying "Tested" or equivalent is not acceptable.
|
||||
|
||||
### Code contributions
|
||||
- **Avoid using GitHub Web Editor**. The web editor lacks most useful GIT features and **should only be used for very minor changes**. It is immediately clear if the web editor has been used, and if so the PR is more likely to be rejected. If you want to make serious contributions, **please learn how to use [GIT version control](https://git-scm.com/)**.
|
||||
- **Avoid committing changes directly on GitHub. This includes use of the web editor, and also uploading files.** The web editor lacks most useful GIT features and **should only be used for very minor changes**. It is immediately clear if the web editor has been used, and if so the PR is more likely to be rejected. If you want to make serious contributions, **please learn how to use [GIT version control](https://git-scm.com/)**.
|
||||
- **Do not copy-paste code**. There are potential license issues implicit with copy-pasting, and copy-paste usually indicates a lack of understanding of the actual code. Copy-pasted code is obvious a mile off and **any PR like this is likely to be closed**. If you want to use somebody else's code from a Git repository, **use [GIT's cherry-pick feature](https://git-scm.com/docs/git-cherry-pick)** to cherry-pick the commit. **Cherry-picking is the politer way to copy somebody's changes** and retains all the original accreditation, so there is no need for copy-pasted commits with descriptions like `Some code, thanks @exampleperson`.
|
||||
- **Make sure you can explain your changes**. If you can't provide a good explanation of changes, your PR may be rejected.
|
||||
- **Code should use the same style as in PocketMine-MP.** See [below](#code-syntax) for an example.
|
||||
|
14
README.md
14
README.md
@ -1,6 +1,6 @@
|
||||
# [](https://pmmp.io)
|
||||
|
||||
__A highly customisable, open source server software for Minecraft: Pocket Edition written in PHP__
|
||||
__A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP__
|
||||
|
||||
[](https://travis-ci.org/pmmp/PocketMine-MP)
|
||||
|
||||
@ -27,18 +27,6 @@ Yes you can! Contributions are welcomed provided that they comply with our [Cont
|
||||
|
||||
**Note: Please avoid development builds unless there is no other alternative for what you need.** Development builds are subject to changes at any time without notice, and it is likely that your server or plugins might break without warning.
|
||||
|
||||
## Third-party Libraries/Protocols Used
|
||||
* __[PHP Sockets](http://php.net/manual/en/book.sockets.php)__
|
||||
* __[PHP mbstring](http://php.net/manual/en/book.mbstring.php)__
|
||||
* __[PHP BCMath](http://php.net/manual/en/book.bc.php)__
|
||||
* __[PHP pthreads](http://pthreads.org/)__ by _[krakjoe](https://github.com/krakjoe)_: Threading for PHP - Share Nothing, Do Everything.
|
||||
* __[PHP YAML](https://code.google.com/p/php-yaml/)__ by _Bryan Davis_: The Yaml PHP Extension provides a wrapper to the LibYAML library.
|
||||
* __[LibYAML](http://pyyaml.org/wiki/LibYAML)__ by _Kirill Simonov_: A YAML 1.1 parser and emitter written in C.
|
||||
* __[cURL](http://curl.haxx.se/)__: cURL is a command line tool for transferring data with URL syntax
|
||||
* __[Zlib](http://www.zlib.net/)__: A Massively Spiffy Yet Delicately Unobtrusive Compression Library
|
||||
* __[Source RCON Protocol](https://developer.valvesoftware.com/wiki/Source_RCON_Protocol)__
|
||||
* __[UT3 Query Protocol](http://wiki.unrealadmin.org/UT3_query_protocol)__
|
||||
|
||||
## Licensing information
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -6,8 +6,11 @@
|
||||
"license": "LGPL-3.0",
|
||||
"require": {
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*",
|
||||
"ext-bcmath": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-date": "*",
|
||||
"ext-hash": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
@ -21,17 +24,23 @@
|
||||
"ext-yaml": ">=2.0.0",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"pocketmine/raklib": "0.11.0",
|
||||
"pocketmine/spl": "0.3.0",
|
||||
"pocketmine/binaryutils": "0.0.1",
|
||||
"pocketmine/nbt": "0.1.0",
|
||||
"pocketmine/math": "0.1.0"
|
||||
"pocketmine/raklib": "^0.12.0",
|
||||
"pocketmine/spl": "^0.3.0",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/nbt": "^0.2.0",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": ["src"]
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"pocketmine\\": "tests/phpunit/"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
@ -52,6 +61,10 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/pmmp/Math"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/pmmp/Snooze"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
123
composer.lock
generated
123
composer.lock
generated
@ -1,27 +1,28 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "401dbada37e501304f05b0f1fa818953",
|
||||
"content-hash": "2670b9e2a730ff758909be8b9e9d609a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "03e6851f814aba96487ec64181a6ae948edd9f7a"
|
||||
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/03e6851f814aba96487ec64181a6ae948edd9f7a",
|
||||
"reference": "03e6851f814aba96487ec64181a6ae948edd9f7a",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
|
||||
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
"php": ">=7.2",
|
||||
"php-64bit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -37,24 +38,25 @@
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/master",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2018-03-17T11:57:06+00:00"
|
||||
"time": "2018-04-16T09:05:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "1df74f0352309a9c1e6728fa416a3f0493d07b16"
|
||||
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/1df74f0352309a9c1e6728fa416a3f0493d07b16",
|
||||
"reference": "1df74f0352309a9c1e6728fa416a3f0493d07b16",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/95ae5600328ed2add44c0bc830a68d3660e9e0ef",
|
||||
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.0"
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -70,25 +72,26 @@
|
||||
"source": "https://github.com/pmmp/Math/tree/master",
|
||||
"issues": "https://github.com/pmmp/Math/issues"
|
||||
},
|
||||
"time": "2018-03-18T18:01:56+00:00"
|
||||
"time": "2018-06-09T09:26:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "d79f8615442887bb45cfacdb52e1e6eb47c38fd7"
|
||||
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/d79f8615442887bb45cfacdb52e1e6eb47c38fd7",
|
||||
"reference": "d79f8615442887bb45cfacdb52e1e6eb47c38fd7",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
|
||||
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.0",
|
||||
"pocketmine/binaryutils": "0.0.1"
|
||||
"php-64bit": "*",
|
||||
"pocketmine/binaryutils": "^0.1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -106,61 +109,98 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.1.0",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.0",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2018-04-13T18:43:03+00:00"
|
||||
"time": "2018-06-13T09:56:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "1da1b4c6cc6bd5337ce5e468d22bbb013ae02b43"
|
||||
"reference": "922da28efd828e2af6c19db1676ea9b6a267071c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/1da1b4c6cc6bd5337ce5e468d22bbb013ae02b43",
|
||||
"reference": "1da1b4c6cc6bd5337ce5e468d22bbb013ae02b43",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/922da28efd828e2af6c19db1676ea9b6a267071c",
|
||||
"reference": "922da28efd828e2af6c19db1676ea9b6a267071c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-pthreads": ">=3.1.7dev",
|
||||
"ext-sockets": "*",
|
||||
"php": ">=7.2.0RC3",
|
||||
"pocketmine/binaryutils": "0.0.1",
|
||||
"pocketmine/spl": "0.3.0"
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*",
|
||||
"php-ipv6": "*",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"pocketmine/spl": "^0.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"./"
|
||||
]
|
||||
"psr-4": {
|
||||
"raklib\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"GPL-3.0"
|
||||
],
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.11.0",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.0",
|
||||
"issues": "https://github.com/pmmp/RakLib/issues"
|
||||
},
|
||||
"time": "2018-04-13T19:05:24+00:00"
|
||||
"time": "2018-06-13T10:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "0.3.0",
|
||||
"name": "pocketmine/snooze",
|
||||
"version": "0.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "ee32424c100fd11ae7f7b8df7604623fd475f0ec"
|
||||
"url": "https://github.com/pmmp/Snooze.git",
|
||||
"reference": "3cc9d0164230889acb08e22cc126133809e9d346"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/ee32424c100fd11ae7f7b8df7604623fd475f0ec",
|
||||
"reference": "ee32424c100fd11ae7f7b8df7604623fd475f0ec",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/3cc9d0164230889acb08e22cc126133809e9d346",
|
||||
"reference": "3cc9d0164230889acb08e22cc126133809e9d346",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pthreads": ">=3.1.7dev",
|
||||
"php-64bit": ">=7.2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"pocketmine\\snooze\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"description": "Thread notification management library for code using the pthreads extension",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.1.0",
|
||||
"issues": "https://github.com/pmmp/Snooze/issues"
|
||||
},
|
||||
"time": "2018-06-13T09:36:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "0.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/ca3912099543ddc4b4b14f40e258d84ca547dfa5",
|
||||
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -179,7 +219,7 @@
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/SPL/tree/master"
|
||||
},
|
||||
"time": "2018-03-17T11:56:20+00:00"
|
||||
"time": "2018-06-09T17:30:36+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
@ -192,8 +232,11 @@
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*",
|
||||
"ext-bcmath": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-date": "*",
|
||||
"ext-hash": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
|
@ -38,7 +38,7 @@ PROJECT_NAME = PocketMine-MP
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "PM_VERSION - API PM_API"
|
||||
PROJECT_NUMBER = "PM_VERSION"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
@ -147,6 +147,4 @@ abstract class Achievement{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -64,6 +64,8 @@ class CrashDump{
|
||||
$this->extraData();
|
||||
|
||||
$this->encodeData();
|
||||
|
||||
fclose($this->fp);
|
||||
}
|
||||
|
||||
public function getPath() : string{
|
||||
@ -227,13 +229,13 @@ class CrashDump{
|
||||
}
|
||||
|
||||
private function generalData(){
|
||||
$version = new VersionString();
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
$this->data["general"] = [];
|
||||
$this->data["general"]["name"] = $this->server->getName();
|
||||
$this->data["general"]["version"] = $version->get(false);
|
||||
$this->data["general"]["version"] = $version->getFullVersion(false);
|
||||
$this->data["general"]["build"] = $version->getBuild();
|
||||
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
|
||||
$this->data["general"]["api"] = \pocketmine\API_VERSION;
|
||||
$this->data["general"]["api"] = \pocketmine\BASE_VERSION;
|
||||
$this->data["general"]["git"] = \pocketmine\GIT_COMMIT;
|
||||
$this->data["general"]["raklib"] = RakLib::VERSION;
|
||||
$this->data["general"]["uname"] = php_uname("a");
|
||||
@ -241,7 +243,7 @@ class CrashDump{
|
||||
$this->data["general"]["zend"] = zend_version();
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->addLine($this->server->getName() . " version: " . $version->get(false) . " #" . $version->getBuild() . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "; API " . API_VERSION . "]");
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . GIT_COMMIT);
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
$this->addLine("PHP Version: " . phpversion());
|
||||
@ -256,5 +258,4 @@ class CrashDump{
|
||||
public function add($str){
|
||||
fwrite($this->fp, $str);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\event\server\LowMemoryEvent;
|
||||
use pocketmine\scheduler\DumpWorkerMemoryTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class MemoryManager{
|
||||
@ -243,9 +242,9 @@ class MemoryManager{
|
||||
Timings::$garbageCollectorTimer->startTiming();
|
||||
|
||||
if($this->garbageCollectionAsync){
|
||||
$size = $this->server->getScheduler()->getAsyncTaskPoolSize();
|
||||
for($i = 0; $i < $size; ++$i){
|
||||
$this->server->getScheduler()->scheduleAsyncTaskToWorker(new GarbageCollectionTask(), $i);
|
||||
$pool = $this->server->getAsyncPool();
|
||||
foreach($pool->getRunningWorkers() as $i){
|
||||
$pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,13 +263,13 @@ class MemoryManager{
|
||||
* @param int $maxStringSize
|
||||
*/
|
||||
public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize){
|
||||
MainLogger::getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
|
||||
self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize);
|
||||
$this->server->getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
|
||||
self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $this->server->getLogger());
|
||||
|
||||
if($this->dumpWorkers){
|
||||
$scheduler = $this->server->getScheduler();
|
||||
for($i = 0, $size = $scheduler->getAsyncTaskPoolSize(); $i < $size; ++$i){
|
||||
$scheduler->scheduleAsyncTaskToWorker(new DumpWorkerMemoryTask($outputFolder, $maxNesting, $maxStringSize), $i);
|
||||
$pool = $this->server->getAsyncPool();
|
||||
foreach($pool->getRunningWorkers() as $i){
|
||||
$pool->submitTaskToWorker(new DumpWorkerMemoryTask($outputFolder, $maxNesting, $maxStringSize), $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -278,14 +277,15 @@ class MemoryManager{
|
||||
/**
|
||||
* Static memory dumper accessible from any thread.
|
||||
*
|
||||
* @param mixed $startingObject
|
||||
* @param string $outputFolder
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
* @param mixed $startingObject
|
||||
* @param string $outputFolder
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
* @param \Logger $logger
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize){
|
||||
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){
|
||||
$hardLimit = ini_get('memory_limit');
|
||||
ini_set('memory_limit', '-1');
|
||||
gc_disable();
|
||||
@ -329,7 +329,7 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
MainLogger::getLogger()->info("[Dump] Wrote $staticCount static properties");
|
||||
$logger->info("[Dump] Wrote $staticCount static properties");
|
||||
|
||||
if(isset($GLOBALS)){ //This might be null if we're on a different thread
|
||||
$globalVariables = [];
|
||||
@ -357,7 +357,7 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
file_put_contents($outputFolder . "/globalVariables.js", json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
MainLogger::getLogger()->info("[Dump] Wrote $globalCount global variables");
|
||||
$logger->info("[Dump] Wrote $globalCount global variables");
|
||||
}
|
||||
|
||||
self::continueDump($startingObject, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
@ -394,15 +394,22 @@ class MemoryManager{
|
||||
$info["implements"] = implode(", ", $reflection->getInterfaceNames());
|
||||
}
|
||||
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if($property->isStatic()){
|
||||
continue;
|
||||
}
|
||||
for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if($property->isStatic()){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
$name = $property->getName();
|
||||
if($reflection !== $original and !$property->isPublic()){
|
||||
$name = $reflection->getName() . ":" . $name;
|
||||
}
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
|
||||
self::continueDump($property->getValue($object), $info["properties"][$name], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
self::continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
fwrite($obData, "$hash@$className: " . json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
|
||||
@ -411,7 +418,7 @@ class MemoryManager{
|
||||
|
||||
}while($continue);
|
||||
|
||||
MainLogger::getLogger()->info("[Dump] Wrote " . count($objects) . " objects");
|
||||
$logger->info("[Dump] Wrote " . count($objects) . " objects");
|
||||
|
||||
fclose($obData);
|
||||
|
||||
@ -421,7 +428,7 @@ class MemoryManager{
|
||||
arsort($instanceCounts, SORT_NUMERIC);
|
||||
file_put_contents($outputFolder . "/instanceCounts.js", json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
|
||||
MainLogger::getLogger()->info("[Dump] Finished!");
|
||||
$logger->info("[Dump] Finished!");
|
||||
|
||||
ini_set('memory_limit', $hardLimit);
|
||||
gc_enable();
|
||||
|
@ -134,6 +134,4 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
public function removeMetadata(string $metadataKey, Plugin $owningPlugin){
|
||||
$this->server->getPlayerMetadata()->removeMetadata($this, $metadataKey, $owningPlugin);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ use pocketmine\inventory\transaction\CraftingTransaction;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Consumable;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\WritableBook;
|
||||
use pocketmine\item\WrittenBook;
|
||||
@ -86,7 +87,6 @@ use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Location;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\level\WeakPosition;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\metadata\MetadataValue;
|
||||
@ -298,7 +298,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/** @var Vector3|null */
|
||||
protected $sleeping = null;
|
||||
/** @var WeakPosition|null */
|
||||
/** @var Position|null */
|
||||
private $spawnPosition = null;
|
||||
|
||||
//TODO: Abilities
|
||||
@ -401,7 +401,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
*
|
||||
* @return UUID|null
|
||||
*/
|
||||
public function getUniqueId(){
|
||||
public function getUniqueId() : ?UUID{
|
||||
return parent::getUniqueId();
|
||||
}
|
||||
|
||||
@ -459,7 +459,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/**
|
||||
* @param Player $player
|
||||
*/
|
||||
public function spawnTo(Player $player){
|
||||
public function spawnTo(Player $player) : void{
|
||||
if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevel() === $this->level and $player->canSee($this) and !$this->isSpectator()){
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
@ -535,10 +535,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function canBeCollidedWith() : bool{
|
||||
return !$this->isSpectator();
|
||||
return !$this->isSpectator() and parent::canBeCollidedWith();
|
||||
}
|
||||
|
||||
public function resetFallDistance(){
|
||||
public function resetFallDistance() : void{
|
||||
parent::resetFallDistance();
|
||||
if($this->inAirTicks !== 0){
|
||||
$this->startAirTicks = 5;
|
||||
@ -666,8 +666,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function sendCommandData(){
|
||||
//TODO: this needs fixing
|
||||
|
||||
$pk = new AvailableCommandsPacket();
|
||||
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
|
||||
if(isset($pk->commandData[$command->getName()]) or $command->getName() === "help"){
|
||||
@ -688,7 +686,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$aliases = $command->getAliases();
|
||||
if(!empty($aliases)){
|
||||
if(!\in_array($data->commandName, $aliases, true)){
|
||||
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;
|
||||
}
|
||||
@ -812,7 +810,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
*
|
||||
* If null is given, will additionally send the skin to the player itself as well as its viewers.
|
||||
*/
|
||||
public function sendSkin(array $targets = null) : void{
|
||||
public function sendSkin(?array $targets = null) : void{
|
||||
parent::sendSkin($targets ?? $this->server->getOnlinePlayers());
|
||||
}
|
||||
|
||||
@ -923,12 +921,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
protected function switchLevel(Level $targetLevel) : bool{
|
||||
$oldLevel = $this->level;
|
||||
if(parent::switchLevel($targetLevel)){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
$this->unloadChunk($X, $Z, $oldLevel);
|
||||
if($oldLevel !== null){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
$this->unloadChunk($X, $Z, $oldLevel);
|
||||
}
|
||||
}
|
||||
|
||||
$this->usedChunks = [];
|
||||
$this->loadQueue = [];
|
||||
$this->level->sendTime($this);
|
||||
$this->level->sendDifficulty($this);
|
||||
|
||||
@ -1061,9 +1062,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
protected function orderChunks(){
|
||||
protected function orderChunks() : void{
|
||||
if(!$this->isConnected() or $this->viewDistance === -1){
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
Timings::$playerChunkOrderTimer->startTiming();
|
||||
@ -1148,8 +1149,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->loadQueue = $newOrder;
|
||||
|
||||
Timings::$playerChunkOrderTimer->stopTiming();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1169,7 +1168,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return bool
|
||||
*/
|
||||
public function hasValidSpawnPosition() : bool{
|
||||
return $this->spawnPosition instanceof WeakPosition and $this->spawnPosition->isValid();
|
||||
return $this->spawnPosition !== null and $this->spawnPosition->isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1184,7 +1183,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}else{
|
||||
$level = $pos->getLevel();
|
||||
}
|
||||
$this->spawnPosition = new WeakPosition($pos->x, $pos->y, $pos->z, $level);
|
||||
$this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level);
|
||||
$pk = new SetSpawnPositionPacket();
|
||||
$pk->x = $this->spawnPosition->getFloorX();
|
||||
$pk->y = $this->spawnPosition->getFloorY();
|
||||
@ -1487,23 +1486,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz){
|
||||
if(!$this->onGround or $movY != 0){
|
||||
$bb = clone $this->boundingBox;
|
||||
$bb->minY = $this->y - 0.2;
|
||||
$bb->maxY = $this->y + 0.2;
|
||||
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
|
||||
$bb = clone $this->boundingBox;
|
||||
$bb->minY = $this->y - 0.2;
|
||||
$bb->maxY = $this->y + 0.2;
|
||||
|
||||
$this->onGround = count($this->level->getCollisionBlocks($bb, true)) > 0;
|
||||
}
|
||||
$this->isCollided = $this->onGround;
|
||||
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
|
||||
}
|
||||
|
||||
public function canBeMovedByCurrents() : bool{
|
||||
return false; //currently has no server-side movement
|
||||
}
|
||||
|
||||
protected function checkNearEntities(int $tickDiff){
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){
|
||||
protected function checkNearEntities(){
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(1, 0.5, 1), $this) as $entity){
|
||||
$entity->scheduleUpdate();
|
||||
|
||||
if(!$entity->isAlive() or $entity->isFlaggedForDespawn()){
|
||||
@ -1637,17 +1633,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->newPosition = null;
|
||||
}
|
||||
|
||||
public function jump(){
|
||||
public function jump() : void{
|
||||
$this->server->getPluginManager()->callEvent(new PlayerJumpEvent($this));
|
||||
parent::jump();
|
||||
}
|
||||
|
||||
public function setMotion(Vector3 $mot){
|
||||
if(parent::setMotion($mot)){
|
||||
public function setMotion(Vector3 $motion) : bool{
|
||||
if(parent::setMotion($motion)){
|
||||
$this->broadcastMotion();
|
||||
|
||||
if($this->motionY > 0){
|
||||
$this->startAirTicks = (-log($this->gravity / ($this->gravity + $this->drag * $this->motionY)) / $this->drag) * 2 + 5;
|
||||
if($this->motion->y > 0){
|
||||
$this->startAirTicks = (-log($this->gravity / ($this->gravity + $this->drag * $this->motion->y)) / $this->drag) * 2 + 5;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1655,11 +1651,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function updateMovement(bool $teleport = false){
|
||||
protected function updateMovement(bool $teleport = false) : void{
|
||||
|
||||
}
|
||||
|
||||
protected function tryChangeMovement(){
|
||||
protected function tryChangeMovement() : void{
|
||||
|
||||
}
|
||||
|
||||
@ -1702,14 +1698,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
if($this->spawned){
|
||||
$this->processMovement($tickDiff);
|
||||
$this->motionX = $this->motionY = $this->motionZ = 0; //TODO: HACK! (Fixes player knockback being messed up)
|
||||
$this->motion->x = $this->motion->y = $this->motion->z = 0; //TODO: HACK! (Fixes player knockback being messed up)
|
||||
|
||||
Timings::$timerEntityBaseTick->startTiming();
|
||||
$this->entityBaseTick($tickDiff);
|
||||
Timings::$timerEntityBaseTick->stopTiming();
|
||||
|
||||
if(!$this->isSpectator() and $this->isAlive()){
|
||||
$this->checkNearEntities($tickDiff);
|
||||
Timings::$playerCheckNearEntitiesTimer->startTiming();
|
||||
$this->checkNearEntities();
|
||||
Timings::$playerCheckNearEntitiesTimer->stopTiming();
|
||||
|
||||
if($this->speed !== null){
|
||||
if($this->onGround){
|
||||
@ -1744,7 +1742,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function doFoodTick(int $tickDiff = 1){
|
||||
protected function doFoodTick(int $tickDiff = 1) : void{
|
||||
if($this->isSurvival()){
|
||||
parent::doFoodTick($tickDiff);
|
||||
}
|
||||
@ -1827,11 +1825,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return ($targetDot - $eyeDot) >= -$maxDiff;
|
||||
}
|
||||
|
||||
protected function initHumanData(){
|
||||
protected function initHumanData() : void{
|
||||
$this->setNameTag($this->username);
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
$this->addDefaultWindows();
|
||||
}
|
||||
@ -1914,7 +1912,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
if(!$packet->skipVerification){
|
||||
$this->server->getScheduler()->scheduleAsyncTask(new VerifyLoginTask($this, $packet));
|
||||
$this->server->getAsyncPool()->submitTask(new VerifyLoginTask($this, $packet));
|
||||
}else{
|
||||
$this->onVerifyCompleted($packet, null, true);
|
||||
}
|
||||
@ -1946,7 +1944,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$xuid = "";
|
||||
}
|
||||
|
||||
if($xuid === ""){
|
||||
if($xuid === "" or !is_string($xuid)){
|
||||
if($signedByMojang){
|
||||
$this->server->getLogger()->error($this->getName() . " should have an XUID, but none found");
|
||||
}
|
||||
@ -1955,7 +1953,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->server->getLogger()->debug($this->getName() . " is NOT logged into to Xbox Live");
|
||||
$this->server->getLogger()->debug($this->getName() . " is NOT logged into Xbox Live");
|
||||
}else{
|
||||
$this->server->getLogger()->debug($this->getName() . " is logged into Xbox Live");
|
||||
$this->xuid = $xuid;
|
||||
@ -1994,7 +1992,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(($level = $this->server->getLevelByName($this->namedtag->getString("Level", "", true))) === null){
|
||||
$this->setLevel($this->server->getDefaultLevel());
|
||||
$this->namedtag->setString("Level", $this->level->getFolderName());
|
||||
$spawnLocation = $this->level->getSpawnLocation();
|
||||
$spawnLocation = $this->level->getSafeSpawn();
|
||||
$this->namedtag->setTag(new ListTag("Pos", [
|
||||
new DoubleTag("", $spawnLocation->x),
|
||||
new DoubleTag("", $spawnLocation->y),
|
||||
@ -2084,9 +2082,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
if(!$this->hasValidSpawnPosition()){
|
||||
if(($level = $this->server->getLevelByName($this->namedtag->getString("SpawnLevel", ""))) instanceof Level){
|
||||
$this->spawnPosition = new WeakPosition($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
|
||||
$this->spawnPosition = new Position($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
|
||||
}else{
|
||||
$this->spawnPosition = WeakPosition::fromObject($this->level->getSafeSpawn());
|
||||
$this->spawnPosition = $this->level->getSafeSpawn();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2145,8 +2143,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->sendPotionEffects($this);
|
||||
$this->sendData($this);
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
$this->armorInventory->sendContents($this);
|
||||
$this->sendAllInventories();
|
||||
$this->inventory->sendCreativeContents();
|
||||
$this->inventory->sendHeldItem($this);
|
||||
$this->dataPacket($this->server->getCraftingManager()->getCraftingDataPacket());
|
||||
@ -2168,7 +2165,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$message = TextFormat::clean($message, $this->removeFormat);
|
||||
foreach(explode("\n", $message) as $messagePart){
|
||||
@ -2243,7 +2240,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
switch($packet->event){
|
||||
case EntityEventPacket::EATING_ITEM:
|
||||
@ -2285,7 +2282,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if($action !== null){
|
||||
$actions[] = $action;
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
}catch(\Exception $e){
|
||||
$this->server->getLogger()->debug("Unhandled inventory action from " . $this->getName() . ": " . $e->getMessage());
|
||||
$this->sendAllInventories();
|
||||
return false;
|
||||
@ -2394,7 +2391,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
@ -2506,20 +2503,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isInsideOfWater()){
|
||||
$ev->setDamage($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
|
||||
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||
}
|
||||
|
||||
$target->attack($ev);
|
||||
|
||||
if($ev->isCancelled()){
|
||||
if($heldItem->isTool() and $this->isSurvival()){
|
||||
if($heldItem instanceof Durable and $this->isSurvival()){
|
||||
$this->inventory->sendContents($this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if($ev->getDamage(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
|
||||
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
|
||||
$pk = new AnimatePacket();
|
||||
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
|
||||
$pk->entityRuntimeId = $target->getId();
|
||||
@ -2529,8 +2526,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
if($this->isSurvival()){
|
||||
if($heldItem->useOn($target)){
|
||||
if($this->isAlive()){
|
||||
//reactive damage like thorns might cause us to be killed by attacking another mob, which
|
||||
//would mean we'd already have dropped the inventory by the time we reached here
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival()){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
|
||||
@ -2633,7 +2632,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$target = $this->level->getEntity($packet->target);
|
||||
if($target === null){
|
||||
@ -2747,40 +2746,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->jump();
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_START_SPRINT:
|
||||
$ev = new PlayerToggleSprintEvent($this, true);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
if($ev->isCancelled()){
|
||||
$this->sendData($this);
|
||||
}else{
|
||||
$this->setSprinting(true);
|
||||
}
|
||||
$this->toggleSprint(true);
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_STOP_SPRINT:
|
||||
$ev = new PlayerToggleSprintEvent($this, false);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
if($ev->isCancelled()){
|
||||
$this->sendData($this);
|
||||
}else{
|
||||
$this->setSprinting(false);
|
||||
}
|
||||
$this->toggleSprint(false);
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_START_SNEAK:
|
||||
$ev = new PlayerToggleSneakEvent($this, true);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
if($ev->isCancelled()){
|
||||
$this->sendData($this);
|
||||
}else{
|
||||
$this->setSneaking(true);
|
||||
}
|
||||
$this->toggleSneak(true);
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_STOP_SNEAK:
|
||||
$ev = new PlayerToggleSneakEvent($this, false);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
if($ev->isCancelled()){
|
||||
$this->sendData($this);
|
||||
}else{
|
||||
$this->setSneaking(false);
|
||||
}
|
||||
$this->toggleSneak(false);
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_START_GLIDE:
|
||||
case PlayerActionPacket::ACTION_STOP_GLIDE:
|
||||
@ -2805,6 +2780,26 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function toggleSprint(bool $sprint) : void{
|
||||
$ev = new PlayerToggleSprintEvent($this, $sprint);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
if($ev->isCancelled()){
|
||||
$this->sendData($this);
|
||||
}else{
|
||||
$this->setSprinting($sprint);
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleSneak(bool $sneak) : void{
|
||||
$ev = new PlayerToggleSneakEvent($this, $sneak);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
if($ev->isCancelled()){
|
||||
$this->sendData($this);
|
||||
}else{
|
||||
$this->setSneaking($sneak);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleAnimate(AnimatePacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
@ -2851,7 +2846,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
if(isset($this->windowIndex[$packet->windowId])){
|
||||
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
|
||||
@ -2901,7 +2896,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$pos = new Vector3($packet->x, $packet->y, $packet->z);
|
||||
if($pos->distanceSquared($this) > 10000 or $this->level->checkSpawnProtection($this, $pos)){
|
||||
@ -3343,7 +3338,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @param string $reason Reason showed in console
|
||||
* @param bool $notify
|
||||
*/
|
||||
final public function close($message = "", string $reason = "generic reason", bool $notify = true){
|
||||
final public function close($message = "", string $reason = "generic reason", bool $notify = true) : void{
|
||||
if($this->isConnected() and !$this->closed){
|
||||
|
||||
try{
|
||||
@ -3494,7 +3489,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
public function kill(){
|
||||
public function kill() : void{
|
||||
if(!$this->spawned){
|
||||
return;
|
||||
}
|
||||
@ -3504,7 +3499,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->sendRespawnPacket($this->getSpawn());
|
||||
}
|
||||
|
||||
protected function onDeath(){
|
||||
protected function onDeath() : void{
|
||||
$message = "death.attack.generic";
|
||||
|
||||
$params = [
|
||||
@ -3619,7 +3614,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
//Crafting grid must always be evacuated even if keep-inventory is true. This dumps the contents into the
|
||||
//main inventory and drops the rest on the ground.
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params)));
|
||||
|
||||
@ -3680,8 +3675,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->sendData($this->getViewers());
|
||||
|
||||
$this->sendSettings();
|
||||
$this->inventory->sendContents($this);
|
||||
$this->armorInventory->sendContents($this);
|
||||
$this->sendAllInventories();
|
||||
|
||||
$this->spawnToAll();
|
||||
$this->scheduleUpdate();
|
||||
@ -3693,7 +3687,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_DAMAGE);
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
if(!$this->isAlive()){
|
||||
return;
|
||||
}
|
||||
@ -3710,11 +3704,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
parent::attack($source);
|
||||
}
|
||||
|
||||
protected function doHitAnimation() : void{
|
||||
parent::doHitAnimation();
|
||||
if($this->spawned){
|
||||
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION, null, [$this]);
|
||||
public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{
|
||||
if($this->spawned and $players === null){
|
||||
$players = $this->getViewers();
|
||||
$players[] = $this;
|
||||
}
|
||||
parent::broadcastEntityEvent($eventId, $eventData, $players);
|
||||
}
|
||||
|
||||
public function getOffsetPosition(Vector3 $vector3) : Vector3{
|
||||
@ -3802,15 +3797,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->craftingGrid = $grid;
|
||||
}
|
||||
|
||||
public function resetCraftingGridType() : void{
|
||||
$contents = $this->craftingGrid->getContents();
|
||||
if(count($contents) > 0){
|
||||
$drops = $this->inventory->addItem(...$contents);
|
||||
foreach($drops as $drop){
|
||||
$this->dropItem($drop);
|
||||
}
|
||||
public function doCloseInventory() : void{
|
||||
/** @var Inventory[] $inventories */
|
||||
$inventories = [$this->craftingGrid, $this->cursorInventory];
|
||||
foreach($inventories as $inventory){
|
||||
$contents = $inventory->getContents();
|
||||
if(count($contents) > 0){
|
||||
$drops = $this->inventory->addItem(...$contents);
|
||||
foreach($drops as $drop){
|
||||
$this->dropItem($drop);
|
||||
}
|
||||
|
||||
$this->craftingGrid->clearAll();
|
||||
$inventory->clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){
|
||||
|
@ -24,49 +24,6 @@ declare(strict_types=1);
|
||||
namespace {
|
||||
const INT32_MIN = -0x80000000;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
|
||||
function safe_var_dump(){
|
||||
static $cnt = 0;
|
||||
foreach(func_get_args() as $var){
|
||||
switch(true){
|
||||
case is_array($var):
|
||||
echo str_repeat(" ", $cnt) . "array(" . count($var) . ") {" . PHP_EOL;
|
||||
foreach($var as $key => $value){
|
||||
echo str_repeat(" ", $cnt + 1) . "[" . (is_int($key) ? $key : '"' . $key . '"') . "]=>" . PHP_EOL;
|
||||
++$cnt;
|
||||
safe_var_dump($value);
|
||||
--$cnt;
|
||||
}
|
||||
echo str_repeat(" ", $cnt) . "}" . PHP_EOL;
|
||||
break;
|
||||
case is_int($var):
|
||||
echo str_repeat(" ", $cnt) . "int(" . $var . ")" . PHP_EOL;
|
||||
break;
|
||||
case is_float($var):
|
||||
echo str_repeat(" ", $cnt) . "float(" . $var . ")" . PHP_EOL;
|
||||
break;
|
||||
case is_bool($var):
|
||||
echo str_repeat(" ", $cnt) . "bool(" . ($var === true ? "true" : "false") . ")" . PHP_EOL;
|
||||
break;
|
||||
case is_string($var):
|
||||
echo str_repeat(" ", $cnt) . "string(" . strlen($var) . ") \"$var\"" . PHP_EOL;
|
||||
break;
|
||||
case is_resource($var):
|
||||
echo str_repeat(" ", $cnt) . "resource() of type (" . get_resource_type($var) . ")" . PHP_EOL;
|
||||
break;
|
||||
case is_object($var):
|
||||
echo str_repeat(" ", $cnt) . "object(" . get_class($var) . ")" . PHP_EOL;
|
||||
break;
|
||||
case is_null($var):
|
||||
echo str_repeat(" ", $cnt) . "NULL" . PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dummy(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace pocketmine {
|
||||
@ -76,13 +33,13 @@ namespace pocketmine {
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\Timezone;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
use raklib\RakLib;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const VERSION = "1.7dev";
|
||||
const API_VERSION = "3.0.0-ALPHA12";
|
||||
const CODENAME = "[REDACTED]";
|
||||
const BASE_VERSION = "3.0.10";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
||||
const MIN_PHP_VERSION = "7.2.0";
|
||||
|
||||
@ -98,13 +55,14 @@ namespace pocketmine {
|
||||
|
||||
if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
|
||||
critical_error(\pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . ".");
|
||||
critical_error("Please use the installer provided on the homepage, or update to a newer PHP version.");
|
||||
critical_error("Please refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(PHP_INT_SIZE < 8){
|
||||
critical_error("Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported.");
|
||||
critical_error("Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.");
|
||||
critical_error("Please refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -120,12 +78,18 @@ namespace pocketmine {
|
||||
$extensions = [
|
||||
"bcmath" => "BC Math",
|
||||
"curl" => "cURL",
|
||||
"ctype" => "ctype",
|
||||
"date" => "Date",
|
||||
"hash" => "Hash",
|
||||
"json" => "JSON",
|
||||
"mbstring" => "Multibyte String",
|
||||
"openssl" => "OpenSSL",
|
||||
"pcre" => "PCRE",
|
||||
"phar" => "Phar",
|
||||
"pthreads" => "pthreads",
|
||||
"reflection" => "Reflection",
|
||||
"sockets" => "Sockets",
|
||||
"spl" => "SPL",
|
||||
"yaml" => "YAML",
|
||||
"zip" => "Zip",
|
||||
"zlib" => "Zlib"
|
||||
@ -143,8 +107,8 @@ namespace pocketmine {
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
if(version_compare($pthreads_version, "3.1.7-dev") < 0){
|
||||
critical_error("pthreads >= 3.1.7-dev is required, while you have $pthreads_version.");
|
||||
if(version_compare($pthreads_version, "3.1.7dev") < 0){
|
||||
critical_error("pthreads >= 3.1.7dev is required, while you have $pthreads_version.");
|
||||
++$errors;
|
||||
}
|
||||
}
|
||||
@ -163,22 +127,12 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
if($errors > 0){
|
||||
critical_error("Please use the installer provided on the homepage, or recompile PHP again.");
|
||||
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
error_reporting(-1);
|
||||
|
||||
function error_handler($severity, $message, $file, $line){
|
||||
if(error_reporting() & $severity){
|
||||
throw new \ErrorException($message, 0, $severity, $file, $line);
|
||||
}else{ //stfu operator
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
set_error_handler('\pocketmine\error_handler');
|
||||
|
||||
if(\Phar::running(true) !== ""){
|
||||
define('pocketmine\PATH', \Phar::running(true) . "/");
|
||||
}else{
|
||||
@ -187,27 +141,15 @@ namespace pocketmine {
|
||||
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
|
||||
|
||||
function composer_error_die($message){
|
||||
critical_error($message);
|
||||
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}else{
|
||||
critical_error("Composer autoloader not found.");
|
||||
critical_error("Please install/update Composer dependencies or use provided builds.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}else{
|
||||
composer_error_die("Composer autoloader not found.");
|
||||
}
|
||||
|
||||
if(!class_exists(RakLib::class)){
|
||||
composer_error_die("Unable to find the RakLib library.");
|
||||
}
|
||||
if(version_compare(RakLib::VERSION, "0.11.0") < 0){ //TODO: remove this check (it's managed by Composer now)
|
||||
composer_error_die("RakLib version 0.11.0 is required, while you have version " . RakLib::VERSION . ".");
|
||||
}
|
||||
if(!class_exists(\BaseClassLoader::class)){
|
||||
composer_error_die("Unable to find the PocketMine-SPL library.");
|
||||
}
|
||||
set_error_handler([Utils::class, 'errorExceptionHandler']);
|
||||
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
@ -227,14 +169,10 @@ namespace pocketmine {
|
||||
|
||||
define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
|
||||
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-profiler"]);
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard"]);
|
||||
|
||||
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);
|
||||
|
||||
Terminal::init();
|
||||
|
||||
define('pocketmine\ANSI', Terminal::hasFormattingCodes());
|
||||
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);
|
||||
@ -251,15 +189,6 @@ namespace pocketmine {
|
||||
}
|
||||
unset($tzError);
|
||||
|
||||
if(isset($opts["enable-profiler"])){
|
||||
if(function_exists("profiler_enable")){
|
||||
\profiler_enable();
|
||||
$logger->notice("Execution is being profiled");
|
||||
}else{
|
||||
$logger->notice("No profiler found. Please install https://github.com/krakjoe/profiler");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -268,10 +197,13 @@ namespace pocketmine {
|
||||
$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){
|
||||
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";
|
||||
|
@ -47,10 +47,11 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\lang\BaseLang;
|
||||
use pocketmine\lang\TextContainer;
|
||||
use pocketmine\level\biome\Biome;
|
||||
use pocketmine\level\format\io\LevelProvider;
|
||||
use pocketmine\level\format\io\LevelProviderManager;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\GeneratorManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\LevelException;
|
||||
use pocketmine\metadata\EntityMetadataStore;
|
||||
@ -87,9 +88,11 @@ use pocketmine\plugin\PluginLoadOrder;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\plugin\ScriptPluginLoader;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\scheduler\FileWriteTask;
|
||||
use pocketmine\scheduler\SendUsageTask;
|
||||
use pocketmine\scheduler\ServerScheduler;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\tile\Tile;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
@ -101,7 +104,6 @@ use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\UUID;
|
||||
use pocketmine\utils\VersionString;
|
||||
|
||||
/**
|
||||
* The class that manages everything
|
||||
@ -116,6 +118,9 @@ class Server{
|
||||
/** @var \Threaded */
|
||||
private static $sleeper = null;
|
||||
|
||||
/** @var SleeperHandler */
|
||||
private $tickSleeper;
|
||||
|
||||
/** @var BanList */
|
||||
private $banByName = null;
|
||||
|
||||
@ -131,18 +136,20 @@ class Server{
|
||||
/** @var bool */
|
||||
private $isRunning = true;
|
||||
|
||||
/** @var bool */
|
||||
private $hasStopped = false;
|
||||
|
||||
/** @var PluginManager */
|
||||
private $pluginManager = null;
|
||||
|
||||
/** @var float */
|
||||
private $profilingTickRate = 20;
|
||||
|
||||
/** @var AutoUpdater */
|
||||
private $updater = null;
|
||||
|
||||
/** @var ServerScheduler */
|
||||
private $scheduler = null;
|
||||
/** @var AsyncPool */
|
||||
private $asyncPool;
|
||||
|
||||
/**
|
||||
* Counts the ticks since the server start
|
||||
@ -150,17 +157,24 @@ class Server{
|
||||
* @var int
|
||||
*/
|
||||
private $tickCounter = 0;
|
||||
/** @var int */
|
||||
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];
|
||||
/** @var float[] */
|
||||
private $useAverage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
/** @var float */
|
||||
private $currentTPS = 20;
|
||||
/** @var float */
|
||||
private $currentUse = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $doTitleTick = true;
|
||||
|
||||
/** @var int */
|
||||
private $sendUsageTicker = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $dispatchSignals = false;
|
||||
|
||||
/** @var \AttachableThreadedLogger */
|
||||
@ -207,29 +221,41 @@ class Server{
|
||||
|
||||
/** @var Network */
|
||||
private $network;
|
||||
|
||||
/** @var bool */
|
||||
private $networkCompressionAsync = true;
|
||||
/** @var int */
|
||||
public $networkCompressionLevel = 7;
|
||||
|
||||
/** @var bool */
|
||||
private $autoTickRate = true;
|
||||
/** @var int */
|
||||
private $autoTickRateLimit = 20;
|
||||
/** @var bool */
|
||||
private $alwaysTickPlayers = false;
|
||||
/** @var int */
|
||||
private $baseTickRate = 1;
|
||||
|
||||
/** @var int */
|
||||
private $autoSaveTicker = 0;
|
||||
/** @var int */
|
||||
private $autoSaveTicks = 6000;
|
||||
|
||||
/** @var BaseLang */
|
||||
private $baseLang;
|
||||
|
||||
/** @var bool */
|
||||
private $forceLanguage = false;
|
||||
|
||||
/** @var UUID */
|
||||
private $serverID;
|
||||
|
||||
/** @var \ClassLoader */
|
||||
private $autoloader;
|
||||
/** @var string */
|
||||
private $dataPath;
|
||||
/** @var string */
|
||||
private $pluginPath;
|
||||
|
||||
/** @var string[] */
|
||||
private $uniquePlayers = [];
|
||||
|
||||
/** @var QueryHandler */
|
||||
@ -240,7 +266,7 @@ class Server{
|
||||
|
||||
/** @var Config */
|
||||
private $properties;
|
||||
|
||||
/** @var mixed[] */
|
||||
private $propertyCache = [];
|
||||
|
||||
/** @var Config */
|
||||
@ -282,13 +308,6 @@ class Server{
|
||||
return \pocketmine\VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCodename() : string{
|
||||
return \pocketmine\CODENAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@ -300,7 +319,7 @@ class Server{
|
||||
* @return string
|
||||
*/
|
||||
public function getApiVersion() : string{
|
||||
return \pocketmine\API_VERSION;
|
||||
return \pocketmine\BASE_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -385,7 +404,8 @@ class Server{
|
||||
* @return string
|
||||
*/
|
||||
public function getIp() : string{
|
||||
return $this->getConfigString("server-ip", "0.0.0.0");
|
||||
$str = $this->getConfigString("server-ip");
|
||||
return $str !== "" ? $str : "0.0.0.0";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -568,7 +588,7 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MainLogger
|
||||
* @return \AttachableThreadedLogger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->logger;
|
||||
@ -623,11 +643,8 @@ class Server{
|
||||
return $this->resourceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServerScheduler
|
||||
*/
|
||||
public function getScheduler(){
|
||||
return $this->scheduler;
|
||||
public function getAsyncPool() : AsyncPool{
|
||||
return $this->asyncPool;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -797,7 +814,7 @@ class Server{
|
||||
$nbt = new BigEndianNBTStream();
|
||||
try{
|
||||
if($async){
|
||||
$this->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData())));
|
||||
$this->asyncPool->submitTask(new FileWriteTask($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData())));
|
||||
}else{
|
||||
file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData()));
|
||||
}
|
||||
@ -1000,16 +1017,16 @@ class Server{
|
||||
|
||||
$path = $this->getDataPath() . "worlds/" . $name . "/";
|
||||
|
||||
$provider = LevelProviderManager::getProvider($path);
|
||||
$providerClass = LevelProviderManager::getProvider($path);
|
||||
|
||||
if($provider === null){
|
||||
if($providerClass === null){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, "Cannot identify format of world"]));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
$level = new Level($this, $name, $path, $provider);
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
}catch(\Throwable $e){
|
||||
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, $e->getMessage()]));
|
||||
@ -1019,8 +1036,6 @@ class Server{
|
||||
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
$level->initLevel();
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelLoadEvent($level));
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
@ -1050,23 +1065,24 @@ class Server{
|
||||
}
|
||||
|
||||
if(!($generator !== null and class_exists($generator, true) and is_subclass_of($generator, Generator::class))){
|
||||
$generator = Generator::getGenerator($this->getLevelType());
|
||||
$generator = GeneratorManager::getGenerator($this->getLevelType());
|
||||
}
|
||||
|
||||
if(($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "pmanvil"))) === null){
|
||||
$provider = LevelProviderManager::getProviderByName($providerName = "pmanvil");
|
||||
if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){
|
||||
$providerClass = LevelProviderManager::getProviderByName("pmanvil");
|
||||
if($providerClass === null){
|
||||
throw new \InvalidStateException("Default level provider has not been registered");
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
$path = $this->getDataPath() . "worlds/" . $name . "/";
|
||||
/** @var LevelProvider $provider */
|
||||
$provider::generate($path, $name, $seed, $generator, $options);
|
||||
/** @var LevelProvider $providerClass */
|
||||
$providerClass::generate($path, $name, $seed, $generator, $options);
|
||||
|
||||
$level = new Level($this, $name, $path, (string) $provider);
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
$level->initLevel();
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.generationError", [$name, $e->getMessage()]));
|
||||
@ -1393,17 +1409,18 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ClassLoader $autoloader
|
||||
* @param \ThreadedLogger $logger
|
||||
* @param string $dataPath
|
||||
* @param string $pluginPath
|
||||
* @param \ClassLoader $autoloader
|
||||
* @param \AttachableThreadedLogger $logger
|
||||
* @param string $dataPath
|
||||
* @param string $pluginPath
|
||||
*/
|
||||
public function __construct(\ClassLoader $autoloader, \ThreadedLogger $logger, string $dataPath, string $pluginPath){
|
||||
public function __construct(\ClassLoader $autoloader, \AttachableThreadedLogger $logger, string $dataPath, string $pluginPath){
|
||||
if(self::$instance !== null){
|
||||
throw new \InvalidStateException("Only one server instance can exist at once");
|
||||
}
|
||||
self::$instance = $this;
|
||||
self::$sleeper = new \Threaded;
|
||||
$this->tickSleeper = new SleeperHandler();
|
||||
$this->autoloader = $autoloader;
|
||||
$this->logger = $logger;
|
||||
|
||||
@ -1423,14 +1440,10 @@ class Server{
|
||||
$this->dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
|
||||
$this->pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
|
||||
|
||||
$this->console = new CommandReader();
|
||||
|
||||
$version = new VersionString($this->getPocketMineVersion());
|
||||
|
||||
$this->logger->info("Loading pocketmine.yml...");
|
||||
if(!file_exists($this->dataPath . "pocketmine.yml")){
|
||||
$content = file_get_contents(\pocketmine\RESOURCE_PATH . "pocketmine.yml");
|
||||
if($version->isDev()){
|
||||
if(\pocketmine\IS_DEVELOPMENT_BUILD){
|
||||
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
|
||||
}
|
||||
@file_put_contents($this->dataPath . "pocketmine.yml", $content);
|
||||
@ -1439,6 +1452,19 @@ class Server{
|
||||
|
||||
define('pocketmine\DEBUG', (int) $this->getProperty("debug.level", 1));
|
||||
|
||||
$this->forceLanguage = (bool) $this->getProperty("settings.force-language", false);
|
||||
$this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE));
|
||||
$this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()]));
|
||||
|
||||
if(\pocketmine\IS_DEVELOPMENT_BUILD and !((bool) $this->getProperty("settings.enable-dev-builds", false))){
|
||||
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error1", [\pocketmine\NAME]));
|
||||
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error2"));
|
||||
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error3"));
|
||||
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error4", ["settings.enable-dev-builds"]));
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if(((int) ini_get('zend.assertions')) > 0 and ((bool) $this->getProperty("debug.assertions.warn-if-enabled", true)) !== false){
|
||||
$this->logger->warning("Debugging assertions are enabled, this may impact on performance. To disable them, set `zend.assertions = -1` in php.ini.");
|
||||
}
|
||||
@ -1477,26 +1503,22 @@ class Server{
|
||||
"xbox-auth" => true
|
||||
]);
|
||||
|
||||
$this->forceLanguage = (bool) $this->getProperty("settings.force-language", false);
|
||||
$this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE));
|
||||
$this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()]));
|
||||
|
||||
$this->memoryManager = new MemoryManager($this);
|
||||
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.start", [TextFormat::AQUA . $this->getVersion() . TextFormat::RESET]));
|
||||
|
||||
if(($poolSize = $this->getProperty("settings.async-workers", "auto")) === "auto"){
|
||||
$poolSize = ServerScheduler::$WORKERS;
|
||||
$poolSize = 2;
|
||||
$processors = Utils::getCoreCount() - 2;
|
||||
|
||||
if($processors > 0){
|
||||
$poolSize = max(1, $processors);
|
||||
}
|
||||
}else{
|
||||
$poolSize = (int) $poolSize;
|
||||
$poolSize = max(1, (int) $poolSize);
|
||||
}
|
||||
|
||||
ServerScheduler::$WORKERS = $poolSize;
|
||||
$this->asyncPool = new AsyncPool($this, $poolSize, (int) max(-1, (int) $this->getProperty("memory.async-worker-hard-limit", 256)), $this->autoloader, $this->logger);
|
||||
|
||||
if($this->getProperty("network.batch-threshold", 256) >= 0){
|
||||
Network::$BATCH_THRESHOLD = (int) $this->getProperty("network.batch-threshold", 256);
|
||||
@ -1509,16 +1531,21 @@ class Server{
|
||||
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 7");
|
||||
$this->networkCompressionLevel = 7;
|
||||
}
|
||||
$this->networkCompressionAsync = $this->getProperty("network.async-compression", true);
|
||||
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
|
||||
|
||||
$this->autoTickRate = (bool) $this->getProperty("level-settings.auto-tick-rate", true);
|
||||
$this->autoTickRateLimit = (int) $this->getProperty("level-settings.auto-tick-rate-limit", 20);
|
||||
$this->alwaysTickPlayers = (int) $this->getProperty("level-settings.always-tick-players", false);
|
||||
$this->alwaysTickPlayers = (bool) $this->getProperty("level-settings.always-tick-players", false);
|
||||
$this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1);
|
||||
|
||||
$this->doTitleTick = (bool) $this->getProperty("console.title-tick", true);
|
||||
$this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes();
|
||||
|
||||
$this->scheduler = new ServerScheduler();
|
||||
$consoleNotifier = new SleeperNotifier();
|
||||
$this->console = new CommandReader($consoleNotifier);
|
||||
$this->tickSleeper->addNotifier($consoleNotifier, function() : void{
|
||||
$this->checkConsole();
|
||||
});
|
||||
$this->console->start(PTHREADS_INHERIT_NONE);
|
||||
|
||||
if($this->getConfigBool("enable-rcon", false)){
|
||||
try{
|
||||
@ -1526,11 +1553,10 @@ class Server{
|
||||
$this,
|
||||
$this->getConfigString("rcon.password", ""),
|
||||
$this->getConfigInt("rcon.port", $this->getPort()),
|
||||
($ip = $this->getIp()) != "" ? $ip : "0.0.0.0",
|
||||
$this->getConfigInt("rcon.threads", 1),
|
||||
$this->getConfigInt("rcon.clients-per-thread", 50)
|
||||
$this->getIp(),
|
||||
$this->getConfigInt("rcon.max-clients", 50)
|
||||
);
|
||||
}catch(\Throwable $e){
|
||||
}catch(\Exception $e){
|
||||
$this->getLogger()->critical("RCON can't be started: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
@ -1572,7 +1598,7 @@ class Server{
|
||||
@cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion());
|
||||
}
|
||||
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp() === "" ? "*" : $this->getIp(), $this->getPort()]));
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp(), $this->getPort()]));
|
||||
define("BOOTUP_RANDOM", random_bytes(16));
|
||||
$this->serverID = Utils::getMachineUniqueId($this->getIp() . $this->getPort());
|
||||
|
||||
@ -1585,9 +1611,7 @@ class Server{
|
||||
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.info", [
|
||||
$this->getName(),
|
||||
($version->isDev() ? TextFormat::YELLOW : "") . $version->get(true) . TextFormat::RESET,
|
||||
$this->getCodename(),
|
||||
$this->getApiVersion()
|
||||
(\pocketmine\IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET
|
||||
]));
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.license", [$this->getName()]));
|
||||
|
||||
@ -1601,6 +1625,7 @@ class Server{
|
||||
Entity::init();
|
||||
Tile::init();
|
||||
BlockFactory::init();
|
||||
BlockFactory::registerStaticRuntimeIdMappings();
|
||||
Enchantment::init();
|
||||
ItemFactory::init();
|
||||
Item::initCreativeItems();
|
||||
@ -1608,13 +1633,13 @@ class Server{
|
||||
|
||||
$this->craftingManager = new CraftingManager();
|
||||
|
||||
$this->resourceManager = new ResourcePackManager($this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR);
|
||||
$this->resourceManager = new ResourcePackManager($this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR, $this->logger);
|
||||
|
||||
$this->pluginManager = new PluginManager($this, $this->commandMap);
|
||||
$this->pluginManager = new PluginManager($this, $this->commandMap, ((bool) $this->getProperty("plugins.legacy-data-dir", true)) ? null : $this->getDataPath() . "plugin_data" . DIRECTORY_SEPARATOR);
|
||||
$this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender);
|
||||
$this->profilingTickRate = (float) $this->getProperty("settings.profile-report-trigger", 20);
|
||||
$this->pluginManager->registerInterface(PharPluginLoader::class);
|
||||
$this->pluginManager->registerInterface(ScriptPluginLoader::class);
|
||||
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
|
||||
$this->pluginManager->registerInterface(new ScriptPluginLoader());
|
||||
|
||||
register_shutdown_function([$this, "crashDump"]);
|
||||
|
||||
@ -1633,31 +1658,24 @@ class Server{
|
||||
$this->logger->debug($this->getLanguage()->translateString("pocketmine.debug.enable"));
|
||||
}
|
||||
|
||||
Generator::registerDefaultGenerators();
|
||||
GeneratorManager::registerDefaultGenerators();
|
||||
|
||||
foreach((array) $this->getProperty("worlds", []) as $name => $options){
|
||||
if(!is_array($options)){
|
||||
continue;
|
||||
}
|
||||
if(!$this->loadLevel($name)){
|
||||
$seed = $options["seed"] ?? time();
|
||||
if(is_string($seed) and !is_numeric($seed)){
|
||||
$seed = Utils::javaStringHash($seed);
|
||||
}elseif(!is_int($seed)){
|
||||
$seed = (int) $seed;
|
||||
}
|
||||
|
||||
if(isset($options["generator"])){
|
||||
$generatorOptions = explode(":", $options["generator"]);
|
||||
$generator = Generator::getGenerator(array_shift($generatorOptions));
|
||||
$generator = GeneratorManager::getGenerator(array_shift($generatorOptions));
|
||||
if(count($options) > 0){
|
||||
$options["preset"] = implode(":", $generatorOptions);
|
||||
}
|
||||
}else{
|
||||
$generator = Generator::getGenerator("default");
|
||||
$generator = GeneratorManager::getGenerator("default");
|
||||
}
|
||||
|
||||
$this->generateLevel($name, $seed, $generator, $options);
|
||||
$this->generateLevel($name, Generator::convertSeed((string) ($options["seed"] ?? "")), $generator, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1669,13 +1687,7 @@ class Server{
|
||||
$this->setConfigString("level-name", "world");
|
||||
}
|
||||
if(!$this->loadLevel($default)){
|
||||
$seed = getopt("", ["level-seed::"])["level-seed"] ?? $this->properties->get("level-seed", time());
|
||||
if(!is_numeric($seed) or bccomp($seed, "9223372036854775807") > 0){
|
||||
$seed = Utils::javaStringHash($seed);
|
||||
}elseif(PHP_INT_SIZE === 8){
|
||||
$seed = (int) $seed;
|
||||
}
|
||||
$this->generateLevel($default, $seed === 0 ? time() : $seed);
|
||||
$this->generateLevel($default, Generator::convertSeed($this->getConfigString("level-seed")));
|
||||
}
|
||||
|
||||
$this->setDefaultLevel($this->getLevelByName($default));
|
||||
@ -1775,11 +1787,11 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
* @param string $subtitle
|
||||
* @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.
|
||||
* @param string $title
|
||||
* @param string $subtitle
|
||||
* @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.
|
||||
* @param Player[]|null $recipients
|
||||
*
|
||||
* @return int
|
||||
@ -1871,7 +1883,7 @@ class Server{
|
||||
|
||||
if(!$forceSync and !$immediate and $this->networkCompressionAsync){
|
||||
$task = new CompressBatchedTask($pk, $targets);
|
||||
$this->getScheduler()->scheduleAsyncTask($task);
|
||||
$this->asyncPool->submitTask($task);
|
||||
}else{
|
||||
$this->broadcastPacketsCallback($pk, $targets, $immediate);
|
||||
}
|
||||
@ -1925,7 +1937,7 @@ class Server{
|
||||
|
||||
public function checkConsole(){
|
||||
Timings::$serverCommandTimer->startTiming();
|
||||
if(($line = $this->console->getLine()) !== null){
|
||||
while(($line = $this->console->getLine()) !== null){
|
||||
$this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line));
|
||||
if(!$ev->isCancelled()){
|
||||
$this->dispatchCommand($ev->getSender(), $ev->getCommand());
|
||||
@ -1981,8 +1993,8 @@ class Server{
|
||||
$this->getNetwork()->blockAddress($entry->getName(), -1);
|
||||
}
|
||||
|
||||
$this->pluginManager->registerInterface(PharPluginLoader::class);
|
||||
$this->pluginManager->registerInterface(ScriptPluginLoader::class);
|
||||
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
|
||||
$this->pluginManager->registerInterface(new ScriptPluginLoader());
|
||||
$this->pluginManager->loadPlugins($this->pluginPath);
|
||||
$this->enablePlugins(PluginLoadOrder::STARTUP);
|
||||
$this->enablePlugins(PluginLoadOrder::POSTWORLD);
|
||||
@ -1990,7 +2002,7 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the server correctly
|
||||
* Shuts the server down correctly
|
||||
*/
|
||||
public function shutdown(){
|
||||
$this->isRunning = false;
|
||||
@ -2035,20 +2047,21 @@ class Server{
|
||||
$this->getLogger()->debug("Removing event handlers");
|
||||
HandlerList::unregisterAll();
|
||||
|
||||
if($this->scheduler instanceof ServerScheduler){
|
||||
$this->getLogger()->debug("Stopping all tasks");
|
||||
$this->scheduler->cancelAllTasks();
|
||||
$this->scheduler->mainThreadHeartbeat(PHP_INT_MAX);
|
||||
if($this->asyncPool instanceof AsyncPool){
|
||||
$this->getLogger()->debug("Shutting down async task worker pool");
|
||||
$this->asyncPool->shutdown();
|
||||
}
|
||||
|
||||
if($this->properties->hasChanged()){
|
||||
if($this->properties !== null and $this->properties->hasChanged()){
|
||||
$this->getLogger()->debug("Saving properties");
|
||||
$this->properties->save();
|
||||
}
|
||||
|
||||
$this->getLogger()->debug("Closing console");
|
||||
$this->console->shutdown();
|
||||
$this->console->notify();
|
||||
if($this->console instanceof CommandReader){
|
||||
$this->getLogger()->debug("Closing console");
|
||||
$this->console->shutdown();
|
||||
$this->console->notify();
|
||||
}
|
||||
|
||||
if($this->network instanceof Network){
|
||||
$this->getLogger()->debug("Stopping network interfaces");
|
||||
@ -2098,7 +2111,7 @@ class Server{
|
||||
$this->logger->info("[UPnP] Trying to port forward...");
|
||||
try{
|
||||
UPnP::PortForward($this->getPort());
|
||||
}catch(\Throwable $e){
|
||||
}catch(\Exception $e){
|
||||
$this->logger->alert("UPnP portforward failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
@ -2131,10 +2144,6 @@ class Server{
|
||||
* @param array|null $trace
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null){
|
||||
if($e === null){
|
||||
return;
|
||||
}
|
||||
|
||||
global $lastError;
|
||||
|
||||
if($trace === null){
|
||||
@ -2152,7 +2161,7 @@ class Server{
|
||||
$this->logger->logException($e, $trace);
|
||||
|
||||
$lastError = [
|
||||
"type" => \get_class($e),
|
||||
"type" => get_class($e),
|
||||
"message" => $errstr,
|
||||
"fullFile" => $e->getFile(),
|
||||
"file" => $errfile,
|
||||
@ -2192,7 +2201,7 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
if($dump->getData()["error"]["type"] === "E_PARSE" or $dump->getData()["error"]["type"] === "E_COMPILE_ERROR"){
|
||||
if($dump->getData()["error"]["type"] === \ParseError::class){
|
||||
$report = false;
|
||||
}
|
||||
|
||||
@ -2224,9 +2233,6 @@ class Server{
|
||||
}catch(\Throwable $e){}
|
||||
}
|
||||
|
||||
//$this->checkMemory();
|
||||
//$dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n";
|
||||
|
||||
$this->forceShutdown();
|
||||
$this->isRunning = false;
|
||||
@Utils::kill(getmypid());
|
||||
@ -2237,18 +2243,18 @@ class Server{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getTickSleeper() : SleeperHandler{
|
||||
return $this->tickSleeper;
|
||||
}
|
||||
|
||||
private function tickProcessor(){
|
||||
$this->nextTick = microtime(true);
|
||||
|
||||
while($this->isRunning){
|
||||
$this->tick();
|
||||
$next = $this->nextTick - 0.0001;
|
||||
if($next > microtime(true)){
|
||||
try{
|
||||
@time_sleep_until($next);
|
||||
}catch(\Throwable $e){
|
||||
//Sometimes $next is less than the current time. High load?
|
||||
}
|
||||
}
|
||||
|
||||
//sleeps are self-correcting - if we undersleep 1ms on this tick, we'll sleep an extra ms on the next tick
|
||||
$this->tickSleeper->sleepUntil($this->nextTick);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2330,7 +2336,7 @@ class Server{
|
||||
$p->dataPacket($pk);
|
||||
}
|
||||
|
||||
private function checkTickUpdates($currentTick, $tickTime){
|
||||
private function checkTickUpdates(int $currentTick, float $tickTime) : void{
|
||||
foreach($this->players as $p){
|
||||
if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){
|
||||
$p->close("", "Login timeout");
|
||||
@ -2399,7 +2405,7 @@ class Server{
|
||||
|
||||
public function sendUsage($type = SendUsageTask::TYPE_STATUS){
|
||||
if((bool) $this->getProperty("anonymous-statistics.enabled", true)){
|
||||
$this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers));
|
||||
$this->asyncPool->submitTask(new SendUsageTask($this, $type, $this->uniquePlayers));
|
||||
}
|
||||
$this->uniquePlayers = [];
|
||||
}
|
||||
@ -2464,6 +2470,8 @@ class Server{
|
||||
try{
|
||||
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
|
||||
$this->queryHandler->handle($interface, $address, $port, $payload);
|
||||
}else{
|
||||
$this->logger->debug("Unhandled raw packet from $address $port: " . bin2hex($payload));
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
@ -2479,31 +2487,28 @@ class Server{
|
||||
/**
|
||||
* Tries to execute a server tick
|
||||
*/
|
||||
private function tick() : bool{
|
||||
private function tick() : void{
|
||||
$tickTime = microtime(true);
|
||||
if(($tickTime - $this->nextTick) < -0.025){ //Allow half a tick of diff
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
Timings::$serverTickTimer->startTiming();
|
||||
|
||||
++$this->tickCounter;
|
||||
|
||||
$this->checkConsole();
|
||||
|
||||
Timings::$connectionTimer->startTiming();
|
||||
$this->network->processInterfaces();
|
||||
|
||||
if($this->rcon !== null){
|
||||
$this->rcon->check();
|
||||
}
|
||||
|
||||
Timings::$connectionTimer->stopTiming();
|
||||
|
||||
Timings::$schedulerTimer->startTiming();
|
||||
$this->scheduler->mainThreadHeartbeat($this->tickCounter);
|
||||
$this->pluginManager->tickSchedulers($this->tickCounter);
|
||||
Timings::$schedulerTimer->stopTiming();
|
||||
|
||||
Timings::$schedulerAsyncTimer->startTiming();
|
||||
$this->asyncPool->collectTasks();
|
||||
Timings::$schedulerAsyncTimer->stopTiming();
|
||||
|
||||
$this->checkTickUpdates($this->tickCounter, $tickTime);
|
||||
|
||||
foreach($this->players as $player){
|
||||
@ -2511,7 +2516,7 @@ class Server{
|
||||
}
|
||||
|
||||
if(($this->tickCounter % 20) === 0){
|
||||
if($this->doTitleTick and Terminal::hasFormattingCodes()){
|
||||
if($this->doTitleTick){
|
||||
$this->titleTick();
|
||||
}
|
||||
$this->currentTPS = 20;
|
||||
@ -2576,8 +2581,6 @@ class Server{
|
||||
}else{
|
||||
$this->nextTick += 0.05;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,14 +67,10 @@ abstract class Thread extends \Thread{
|
||||
public function start(?int $options = \PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
return parent::start($options);
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,12 +79,9 @@ abstract class Thread extends \Thread{
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
||||
$this->notify();
|
||||
|
||||
if(!$this->isJoined()){
|
||||
if(!$this->isTerminated()){
|
||||
$this->join();
|
||||
}
|
||||
$this->notify();
|
||||
$this->join();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
|
@ -67,14 +67,10 @@ abstract class Worker extends \Worker{
|
||||
public function start(?int $options = \PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
return parent::start($options);
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,16 +79,10 @@ abstract class Worker extends \Worker{
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
||||
$this->notify();
|
||||
|
||||
if($this->isRunning()){
|
||||
$this->shutdown();
|
||||
while($this->unstack() !== null);
|
||||
$this->notify();
|
||||
$this->unstack();
|
||||
}elseif(!$this->isJoined()){
|
||||
if(!$this->isTerminated()){
|
||||
$this->join();
|
||||
}
|
||||
$this->shutdown();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
|
@ -81,5 +81,4 @@ class Air extends Transparent{
|
||||
public function getBlastResistance() : float{
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\inventory\AnvilInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -55,13 +54,17 @@ class Anvil extends Fallable{
|
||||
return 6000;
|
||||
}
|
||||
|
||||
public function getVariantBitmask() : int{
|
||||
return 0x0c;
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
static $names = [
|
||||
self::TYPE_NORMAL => "Anvil",
|
||||
self::TYPE_SLIGHTLY_DAMAGED => "Slightly Damaged Anvil",
|
||||
self::TYPE_VERY_DAMAGED => "Very Damaged Anvil"
|
||||
];
|
||||
return $names[$this->meta & 0x0c] ?? "Anvil";
|
||||
return $names[$this->getVariant()] ?? "Anvil";
|
||||
}
|
||||
|
||||
public function getToolType() : int{
|
||||
@ -106,13 +109,7 @@ class Anvil extends Fallable{
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
$direction = ($player !== null ? $player->getDirection() : 0) & 0x03;
|
||||
$this->meta = ($this->meta & 0x0c) | $direction;
|
||||
$this->meta = $this->getVariant() | $direction;
|
||||
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
ItemFactory::get($this->getItemId(), $this->getDamage() >> 2)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class Bed extends Transparent{
|
||||
|
||||
$this->getLevel()->setBlock($this, $this, false, false);
|
||||
|
||||
if(($other = $this->getOtherHalf()) !== null and !$other->isOccupied()){
|
||||
if(($other = $this->getOtherHalf()) !== null and $other->isOccupied() !== $occupied){
|
||||
$other->setOccupied($occupied);
|
||||
}
|
||||
}
|
||||
|
@ -48,5 +48,4 @@ class Bedrock extends Solid{
|
||||
public function isBreakable(Item $item) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -471,6 +471,30 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how much XP will be dropped by breaking this block with the given item.
|
||||
*
|
||||
* @param Item $item
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getXpDropForTool(Item $item) : int{
|
||||
if($item->hasEnchantment(Enchantment::SILK_TOUCH) or !$this->isCompatibleWithTool($item)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->getXpDropAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how much XP this block will drop when broken with an appropriate tool.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getXpDropAmount() : int{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Silk Touch enchanted tools will cause this block to drop as itself. Since most blocks drop
|
||||
* themselves anyway, this is implicitly true.
|
||||
|
@ -25,14 +25,11 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\utils\MainLogger;
|
||||
|
||||
/**
|
||||
* Manages block registration and instance creation
|
||||
*/
|
||||
class BlockFactory{
|
||||
/** @var \SplFixedArray<Block> */
|
||||
private static $list = null;
|
||||
/** @var \SplFixedArray<Block> */
|
||||
private static $fullList = null;
|
||||
|
||||
@ -65,7 +62,6 @@ class BlockFactory{
|
||||
* this if you need to reset the block factory back to its original defaults for whatever reason.
|
||||
*/
|
||||
public static function init() : void{
|
||||
self::$list = new \SplFixedArray(256);
|
||||
self::$fullList = new \SplFixedArray(4096);
|
||||
|
||||
self::$light = new \SplFixedArray(256);
|
||||
@ -327,17 +323,11 @@ class BlockFactory{
|
||||
|
||||
//TODO: RESERVED6
|
||||
|
||||
foreach(self::$list as $id => $block){
|
||||
if($block === null){
|
||||
for($id = 0, $size = self::$fullList->getSize() >> 4; $id < $size; ++$id){
|
||||
if(self::$fullList[$id << 4] === null){
|
||||
self::registerBlock(new UnknownBlock($id));
|
||||
}
|
||||
}
|
||||
|
||||
/** @var mixed[] $runtimeIdMap */
|
||||
$runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
|
||||
foreach($runtimeIdMap as $obj){
|
||||
self::registerMapping($obj["runtimeID"], $obj["id"], $obj["data"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -360,8 +350,6 @@ class BlockFactory{
|
||||
throw new \RuntimeException("Trying to overwrite an already registered block");
|
||||
}
|
||||
|
||||
self::$list[$id] = clone $block;
|
||||
|
||||
for($meta = 0; $meta < 16; ++$meta){
|
||||
$variant = clone $block;
|
||||
$variant->setDamage($meta);
|
||||
@ -372,7 +360,7 @@ class BlockFactory{
|
||||
self::$transparent[$id] = $block->isTransparent();
|
||||
self::$hardness[$id] = $block->getHardness();
|
||||
self::$light[$id] = $block->getLightLevel();
|
||||
self::$lightFilter[$id] = $block->getLightFilter() + 1; //opacity plus 1 standard light filter
|
||||
self::$lightFilter[$id] = min(15, $block->getLightFilter() + 1); //opacity plus 1 standard light filter
|
||||
self::$diffusesSkyLight[$id] = $block->diffusesSkyLight();
|
||||
self::$blastResistance[$id] = $block->getBlastResistance();
|
||||
}
|
||||
@ -426,10 +414,18 @@ class BlockFactory{
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRegistered(int $id) : bool{
|
||||
$b = self::$list[$id];
|
||||
$b = self::$fullList[$id << 4];
|
||||
return $b !== null and !($b instanceof UnknownBlock);
|
||||
}
|
||||
|
||||
public static function registerStaticRuntimeIdMappings() : void{
|
||||
/** @var mixed[] $runtimeIdMap */
|
||||
$runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
|
||||
foreach($runtimeIdMap as $obj){
|
||||
self::registerMapping($obj["runtimeID"], $obj["id"], $obj["data"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@ -439,19 +435,12 @@ class BlockFactory{
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
if($id === Block::AIR){
|
||||
//TODO: HACK! (weird air blocks with non-zero damage values shouldn't turn into update! blocks)
|
||||
$meta = 0;
|
||||
}
|
||||
|
||||
$index = ($id << 4) | $meta;
|
||||
if(!isset(self::$staticRuntimeIdMap[$index])){
|
||||
self::registerMapping($rtId = ++self::$lastRuntimeId, $id, $meta);
|
||||
MainLogger::getLogger()->error("ID $id meta $meta does not have a corresponding block static runtime ID, added a new unknown runtime ID ($rtId)");
|
||||
return $rtId;
|
||||
}
|
||||
|
||||
return self::$staticRuntimeIdMap[$index];
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
* if still not found, return update! block
|
||||
*/
|
||||
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,5 +52,4 @@ class BrickStairs extends Stair{
|
||||
public function getName() : string{
|
||||
return "Brick Stairs";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\TieredTool;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Furnace as TileFurnace;
|
||||
use pocketmine\tile\Tile;
|
||||
@ -83,7 +82,7 @@ class BurningFurnace extends Solid{
|
||||
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this));
|
||||
}
|
||||
|
||||
if($furnace->namedtag->hasTag("Lock", StringTag::class) and $furnace->namedtag->getString("Lock") !== $item->getCustomName()){
|
||||
if(!$furnace->canOpenWith($item->getCustomName())){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Chest as TileChest;
|
||||
use pocketmine\tile\Tile;
|
||||
@ -115,7 +114,7 @@ class Chest extends Transparent{
|
||||
if(
|
||||
!$this->getSide(Vector3::SIDE_UP)->isTransparent() or
|
||||
($chest->isPaired() and !$chest->getPair()->getBlock()->getSide(Vector3::SIDE_UP)->isTransparent()) or
|
||||
($chest->namedtag->hasTag("Lock", StringTag::class) and $chest->namedtag->getString("Lock") !== $item->getCustomName())
|
||||
!$chest->canOpenWith($item->getCustomName())
|
||||
){
|
||||
return true;
|
||||
}
|
||||
|
@ -57,4 +57,7 @@ class CoalOre extends Solid{
|
||||
];
|
||||
}
|
||||
|
||||
public function getXpDropForTool(Item $item) : int{
|
||||
return mt_rand(0, 2);
|
||||
}
|
||||
}
|
||||
|
@ -48,5 +48,4 @@ class CobblestoneStairs extends Stair{
|
||||
public function getName() : string{
|
||||
return "Cobblestone Stairs";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -90,5 +90,4 @@ class CobblestoneWall extends Transparent{
|
||||
public function canConnect(Block $block){
|
||||
return $block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class ConcretePowder extends Fallable{
|
||||
private function checkAdjacentWater() : ?Block{
|
||||
for($i = 1; $i < 6; ++$i){ //Do not check underneath
|
||||
if($this->getSide($i) instanceof Water){
|
||||
return Block::get(Block::CONCRETE, $this->meta);
|
||||
return BlockFactory::get(Block::CONCRETE, $this->meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,4 +56,8 @@ class DiamondOre extends Solid{
|
||||
ItemFactory::get(Item::DIAMOND)
|
||||
];
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(3, 7);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Hoe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Player;
|
||||
|
||||
@ -50,8 +51,8 @@ class Dirt extends Solid{
|
||||
}
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
if($item->isHoe()){
|
||||
$item->useOn($this);
|
||||
if($item instanceof Hoe){
|
||||
$item->applyDamage(1);
|
||||
if($this->meta === 1){
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::DIRT), true);
|
||||
}else{
|
||||
|
@ -35,7 +35,7 @@ abstract class DoubleSlab extends Solid{
|
||||
abstract public function getSlabId() : int;
|
||||
|
||||
public function getName() : string{
|
||||
return "Double " . BlockFactory::get($this->getSlabId(), $this->getVariant())->getName() . " Slab";
|
||||
return "Double " . BlockFactory::get($this->getSlabId(), $this->getVariant())->getName();
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
|
@ -30,5 +30,4 @@ class DoubleStoneSlab2 extends DoubleStoneSlab{
|
||||
public function getSlabId() : int{
|
||||
return self::STONE_SLAB2;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -53,7 +53,11 @@ class EmeraldOre extends Solid{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
ItemFactory::get(Item::EMERALD)
|
||||
ItemFactory::get(Item::EMERALD)
|
||||
];
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(3, 7);
|
||||
}
|
||||
}
|
||||
|
@ -106,5 +106,4 @@ class EnderChest extends Chest{
|
||||
public function getFuelTime() : int{
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -105,5 +105,4 @@ abstract class Fence extends Transparent{
|
||||
public function canConnect(Block $block){
|
||||
return $block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,8 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Hoe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\Shovel;
|
||||
use pocketmine\level\generator\object\TallGrass as TallGrassObject;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
@ -98,13 +100,13 @@ class Grass extends Solid{
|
||||
TallGrassObject::growGrass($this->getLevel(), $this, new Random(mt_rand()), 8, 2);
|
||||
|
||||
return true;
|
||||
}elseif($item->isHoe()){
|
||||
$item->useOn($this);
|
||||
}elseif($item instanceof Hoe){
|
||||
$item->applyDamage(1);
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND));
|
||||
|
||||
return true;
|
||||
}elseif($item->isShovel() and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){
|
||||
$item->useOn($this);
|
||||
}elseif($item instanceof Shovel and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){
|
||||
$item->applyDamage(1);
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GRASS_PATH));
|
||||
|
||||
return true;
|
||||
|
@ -55,5 +55,4 @@ class Gravel extends Fallable{
|
||||
|
||||
return parent::getDropsForCompatibleTool($item);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,4 +57,7 @@ class LapisOre extends Solid{
|
||||
];
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(2, 5);
|
||||
}
|
||||
}
|
||||
|
@ -121,5 +121,4 @@ class Lava extends Liquid{
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,8 +67,7 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
|
||||
protected function findLog(Block $pos, array $visited, $distance, &$check, $fromSide = null) : bool{
|
||||
++$check;
|
||||
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;
|
||||
@ -83,45 +82,45 @@ class Leaves extends Transparent{
|
||||
}
|
||||
if($fromSide === null){
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
if($this->findLog($pos->getSide($side), $visited, $distance + 1, $check, $side)){
|
||||
if($this->findLog($pos->getSide($side), $visited, $distance + 1, $side)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}else{ //No more loops
|
||||
switch($fromSide){
|
||||
case 2:
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $check, $fromSide)){
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $check, $fromSide)){
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $check, $fromSide)){
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $check, $fromSide)){
|
||||
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $check, $fromSide)){
|
||||
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $fromSide)){
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@ -147,11 +146,10 @@ class Leaves extends Transparent{
|
||||
if(($this->meta & 0b00001100) === 0x08){
|
||||
$this->meta &= 0x03;
|
||||
$visited = [];
|
||||
$check = 0;
|
||||
|
||||
$this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new LeavesDecayEvent($this));
|
||||
|
||||
if($ev->isCancelled() or $this->findLog($this, $visited, 0, $check)){
|
||||
if($ev->isCancelled() or $this->findLog($this, $visited, 0)){
|
||||
$this->getLevel()->setBlock($this, $this, false, false);
|
||||
}else{
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
|
@ -57,4 +57,8 @@ class MonsterSpawner extends Transparent{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(15, 43);
|
||||
}
|
||||
}
|
||||
|
@ -48,5 +48,4 @@ class NetherBrickStairs extends Stair{
|
||||
public function getToolHarvestLevel() : int{
|
||||
return TieredTool::TIER_WOODEN;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,4 +57,7 @@ class NetherQuartzOre extends Solid{
|
||||
];
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(2, 5);
|
||||
}
|
||||
}
|
||||
|
@ -61,5 +61,4 @@ class NetherReactor extends Solid{
|
||||
ItemFactory::get(Item::DIAMOND, 0, 3)
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,5 +46,4 @@ class PackedIce extends Solid{
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_PICKAXE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,5 +48,4 @@ class QuartzStairs extends Stair{
|
||||
public function getName() : string{
|
||||
return "Quartz Stairs";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,5 +50,4 @@ class RedMushroomBlock extends Solid{
|
||||
Item::get(Item::RED_MUSHROOM, 0, mt_rand(0, 2))
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,5 +30,4 @@ class RedSandstoneStairs extends SandstoneStairs{
|
||||
public function getName() : string{
|
||||
return "Red " . parent::getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,4 +70,8 @@ class RedstoneOre extends Solid{
|
||||
ItemFactory::get(Item::REDSTONE_DUST, 0, mt_rand(4, 5))
|
||||
];
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(1, 5);
|
||||
}
|
||||
}
|
||||
|
@ -46,5 +46,4 @@ class Sand extends Fallable{
|
||||
|
||||
return "Sand";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,5 +48,4 @@ class SandstoneStairs extends Stair{
|
||||
public function getName() : string{
|
||||
return "Sandstone Stairs";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ class Sapling extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(mt_rand(1, 7) === 1){
|
||||
if($this->level->getFullLightAt($this->x, $this->y, $this->z) >= 8 and mt_rand(1, 7) === 1){
|
||||
if(($this->meta & 0x08) === 0x08){
|
||||
Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
|
||||
}else{
|
||||
|
@ -51,5 +51,4 @@ class SeaLantern extends Transparent{
|
||||
ItemFactory::get(Item::PRISMARINE_CRYSTALS, 0, 3)
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ class SignPost extends Transparent{
|
||||
if($face !== Vector3::SIDE_DOWN){
|
||||
|
||||
if($face === Vector3::SIDE_UP){
|
||||
$this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f;
|
||||
$this->meta = $player !== null ? (floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f) : 0;
|
||||
$this->getLevel()->setBlock($blockReplace, $this, true);
|
||||
}else{
|
||||
$this->meta = $face;
|
||||
|
@ -56,5 +56,4 @@ class Snow extends Solid{
|
||||
ItemFactory::get(Item::SNOWBALL, 0, 4)
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,5 +56,4 @@ class SoulSand extends Solid{
|
||||
$this->z + 1
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,5 +39,4 @@ class Sponge extends Solid{
|
||||
public function getName() : string{
|
||||
return "Sponge";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,5 +32,4 @@ class StainedGlass extends Glass{
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Glass";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,5 +32,4 @@ class StainedGlassPane extends GlassPane{
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Glass Pane";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -92,8 +92,8 @@ class StandingBanner extends Transparent{
|
||||
$tile = $this->level->getTile($this);
|
||||
|
||||
$drop = ItemFactory::get(Item::BANNER, ($tile instanceof TileBanner ? $tile->getBaseColor() : 0));
|
||||
if($tile instanceof TileBanner and ($patterns = $tile->namedtag->getListTag(TileBanner::TAG_PATTERNS)) !== null and !$patterns->empty()){
|
||||
$drop->setNamedTagEntry($patterns);
|
||||
if($tile instanceof TileBanner and !($patterns = $tile->getPatterns())->empty()){
|
||||
$drop->setNamedTagEntry(clone $patterns);
|
||||
}
|
||||
|
||||
return [$drop];
|
||||
|
@ -30,5 +30,4 @@ class StillLava extends Lava{
|
||||
public function getName() : string{
|
||||
return "Still Lava";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -76,5 +76,4 @@ class Stone extends Solid{
|
||||
|
||||
return parent::getDropsForCompatibleTool($item);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,5 +48,4 @@ class StoneBrickStairs extends Stair{
|
||||
public function getName() : string{
|
||||
return "Stone Brick Stairs";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\FlintSteel;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
@ -46,8 +47,8 @@ class TNT extends Solid{
|
||||
}
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
if($item->getId() === Item::FLINT_STEEL){
|
||||
$item->useOn($this);
|
||||
if($item instanceof FlintSteel){
|
||||
$item->applyDamage(1);
|
||||
$this->ignite();
|
||||
return true;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ abstract class Command{
|
||||
if($this->permissionMessage === null){
|
||||
$target->sendMessage($target->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
}elseif($this->permissionMessage !== ""){
|
||||
$target->sendMessage(str_replace("<permission>", $this->getPermission(), $this->permissionMessage));
|
||||
$target->sendMessage(str_replace("<permission>", $this->permission, $this->permissionMessage));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -139,11 +139,11 @@ abstract class Command{
|
||||
* @return bool
|
||||
*/
|
||||
public function testPermissionSilent(CommandSender $target) : bool{
|
||||
if(($perm = $this->getPermission()) === null or $perm === ""){
|
||||
if($this->permission === null or $this->permission === ""){
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach(explode(";", $perm) as $permission){
|
||||
foreach(explode(";", $this->permission) as $permission){
|
||||
if($target->hasPermission($permission)){
|
||||
return true;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\Thread;
|
||||
|
||||
class CommandReader extends Thread{
|
||||
@ -31,20 +32,26 @@ class CommandReader extends Thread{
|
||||
public const TYPE_STREAM = 1;
|
||||
public const TYPE_PIPED = 2;
|
||||
|
||||
/** @var resource */
|
||||
private static $stdin;
|
||||
|
||||
/** @var \Threaded */
|
||||
protected $buffer;
|
||||
private $shutdown = false;
|
||||
private $type = self::TYPE_STREAM;
|
||||
|
||||
public function __construct(){
|
||||
/** @var SleeperNotifier|null */
|
||||
private $notifier;
|
||||
|
||||
public function __construct(?SleeperNotifier $notifier = null){
|
||||
$this->buffer = new \Threaded;
|
||||
$this->notifier = $notifier;
|
||||
|
||||
$opts = getopt("", ["disable-readline"]);
|
||||
|
||||
if(extension_loaded("readline") and !isset($opts["disable-readline"]) and !$this->isPipe(STDIN)){
|
||||
$this->type = self::TYPE_READLINE;
|
||||
}
|
||||
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
@ -71,14 +78,12 @@ class CommandReader extends Thread{
|
||||
}
|
||||
|
||||
private function initStdin(){
|
||||
global $stdin;
|
||||
|
||||
if(is_resource($stdin)){
|
||||
fclose($stdin);
|
||||
if(is_resource(self::$stdin)){
|
||||
fclose(self::$stdin);
|
||||
}
|
||||
|
||||
$stdin = fopen("php://stdin", "r");
|
||||
if($this->isPipe($stdin)){
|
||||
self::$stdin = fopen("php://stdin", "r");
|
||||
if($this->isPipe(self::$stdin)){
|
||||
$this->type = self::TYPE_PIPED;
|
||||
}else{
|
||||
$this->type = self::TYPE_STREAM;
|
||||
@ -92,7 +97,7 @@ class CommandReader extends Thread{
|
||||
* @return bool
|
||||
*/
|
||||
private function isPipe($stream) : bool{
|
||||
return is_resource($stream) and ((function_exists("posix_isatty") and !posix_isatty($stream)) or ((fstat($stream)["mode"] & 0170000) === 0010000));
|
||||
return is_resource($stream) and (!stream_isatty($stream) or ((fstat($stream)["mode"] & 0170000) === 0010000));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,9 +114,7 @@ class CommandReader extends Thread{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
global $stdin;
|
||||
|
||||
if(!is_resource($stdin)){
|
||||
if(!is_resource(self::$stdin)){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
@ -119,7 +122,7 @@ class CommandReader extends Thread{
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case self::TYPE_STREAM:
|
||||
//stream_select doesn't work on piped streams for some reason
|
||||
$r = [$stdin];
|
||||
$r = [self::$stdin];
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
@ -127,7 +130,7 @@ class CommandReader extends Thread{
|
||||
}
|
||||
|
||||
case self::TYPE_PIPED:
|
||||
if(($raw = fgets($stdin)) === false){ //broken pipe or EOF
|
||||
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
|
||||
$this->initStdin();
|
||||
$this->synchronized(function(){
|
||||
$this->wait(200000);
|
||||
@ -142,6 +145,9 @@ class CommandReader extends Thread{
|
||||
|
||||
if($line !== ""){
|
||||
$this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
||||
if($this->notifier !== null){
|
||||
$this->notifier->wakeupSleeper();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -161,6 +167,8 @@ class CommandReader extends Thread{
|
||||
}
|
||||
|
||||
public function run(){
|
||||
$this->registerClassLoader();
|
||||
|
||||
if($this->type !== self::TYPE_READLINE){
|
||||
$this->initStdin();
|
||||
}
|
||||
@ -168,8 +176,7 @@ class CommandReader extends Thread{
|
||||
while(!$this->shutdown and $this->readLine());
|
||||
|
||||
if($this->type !== self::TYPE_READLINE){
|
||||
global $stdin;
|
||||
fclose($stdin);
|
||||
fclose(self::$stdin);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -145,5 +145,4 @@ class ConsoleCommandSender implements CommandSender{
|
||||
}
|
||||
$this->lineHeight = $height;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -47,13 +47,12 @@ class FormattedCommandAlias extends Command{
|
||||
foreach($this->formatStrings as $formatString){
|
||||
try{
|
||||
$commands[] = $this->buildCommand($formatString, $args);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$sender->sendMessage(TextFormat::RED . $e->getMessage());
|
||||
return false;
|
||||
}catch(\Throwable $e){
|
||||
if($e instanceof \InvalidArgumentException){
|
||||
$sender->sendMessage(TextFormat::RED . $e->getMessage());
|
||||
}else{
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception"));
|
||||
$sender->getServer()->getLogger()->logException($e);
|
||||
}
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception"));
|
||||
$sender->getServer()->getLogger()->logException($e);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -155,5 +154,4 @@ class FormattedCommandAlias extends Command{
|
||||
private static function inRange(int $i, int $j, int $k) : bool{
|
||||
return $i >= $j and $i <= $k;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class PluginCommand extends Command implements PluginIdentifiableCommand{
|
||||
* @param string $name
|
||||
* @param Plugin $owner
|
||||
*/
|
||||
public function __construct($name, Plugin $owner){
|
||||
public function __construct(string $name, Plugin $owner){
|
||||
parent::__construct($name);
|
||||
$this->owningPlugin = $owner;
|
||||
$this->executor = $owner;
|
||||
|
@ -47,6 +47,4 @@ class RemoteConsoleCommandSender extends ConsoleCommandSender{
|
||||
public function getName() : string{
|
||||
return "Rcon";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -345,6 +345,4 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class EnchantCommand extends VanillaCommand{
|
||||
|
||||
$item = $player->getInventory()->getItemInHand();
|
||||
|
||||
if($item->getId() <= 0){
|
||||
if($item->isNull()){
|
||||
$sender->sendMessage(new TranslationContainer("commands.enchant.noItem"));
|
||||
return true;
|
||||
}
|
||||
|
@ -99,5 +99,4 @@ class HelpCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -62,15 +62,7 @@ class KillCommand extends VanillaCommand{
|
||||
$player = $sender->getServer()->getPlayer($args[0]);
|
||||
|
||||
if($player instanceof Player){
|
||||
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
|
||||
if($ev->isCancelled()){
|
||||
return true;
|
||||
}
|
||||
|
||||
$player->setLastDamageCause($ev);
|
||||
$player->setHealth(0);
|
||||
|
||||
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kill.successful", [$player->getName()]));
|
||||
}else{
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound"));
|
||||
@ -86,14 +78,7 @@ class KillCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
|
||||
if($ev->isCancelled()){
|
||||
return true;
|
||||
}
|
||||
|
||||
$sender->setLastDamageCause($ev);
|
||||
$sender->setHealth(0);
|
||||
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
$sender->sendMessage(new TranslationContainer("commands.kill.successful", [$sender->getName()]));
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
|
@ -50,9 +50,7 @@ class TellCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$name = strtolower(array_shift($args));
|
||||
|
||||
$player = $sender->getServer()->getPlayer($name);
|
||||
$player = $sender->getServer()->getPlayer(array_shift($args));
|
||||
|
||||
if($player === $sender){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.message.sameTarget"));
|
||||
|
@ -98,23 +98,35 @@ class TimingsCommand extends VanillaCommand{
|
||||
if($paste){
|
||||
fseek($fileTimings, 0);
|
||||
$data = [
|
||||
"syntax" => "text",
|
||||
"poster" => $sender->getServer()->getName(),
|
||||
"content" => stream_get_contents($fileTimings)
|
||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||
"data" => $content = stream_get_contents($fileTimings)
|
||||
];
|
||||
fclose($fileTimings);
|
||||
|
||||
$sender->getServer()->getScheduler()->scheduleAsyncTask(new class([
|
||||
["page" => "http://paste.ubuntu.com", "extraOpts" => [
|
||||
CURLOPT_HTTPHEADER => ["User-Agent: " . $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion()],
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $data,
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]]
|
||||
], $sender) extends BulkCurlTask{
|
||||
$host = $sender->getServer()->getProperty("timings.host", "timings.pmmp.io");
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new class($sender, $host, $agent, $data) extends BulkCurlTask{
|
||||
/** @var string */
|
||||
private $host;
|
||||
|
||||
public function __construct(CommandSender $sender, string $host, string $agent, array $data){
|
||||
parent::__construct([
|
||||
["page" => "https://$host?upload=true", "extraOpts" => [
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"User-Agent: $agent",
|
||||
"Content-Type: application/x-www-form-urlencoded"
|
||||
],
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]]
|
||||
], $sender);
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$sender = $this->fetchLocal($server);
|
||||
$sender = $this->fetchLocal();
|
||||
if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
|
||||
return;
|
||||
}
|
||||
@ -123,23 +135,14 @@ class TimingsCommand extends VanillaCommand{
|
||||
$server->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
list(, $headers) = $result;
|
||||
foreach($headers as $headerGroup){
|
||||
if(isset($headerGroup["location"]) and preg_match('#^http://paste\\.ubuntu\\.com/([A-Za-z0-9+\/=]+)/#', trim($headerGroup["location"]), $match)){
|
||||
$pasteId = $match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isset($pasteId)){
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsUpload", ["http://paste.ubuntu.com/" . $pasteId . "/"]));
|
||||
if(isset($result[0]) && is_array($response = json_decode($result[0], true)) && isset($response["id"])){
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsRead",
|
||||
["http://" . $sender->getServer()->getProperty("timings.host", "timings.pmmp.io") . "/?url=" . urlencode($pasteId)]));
|
||||
["https://" . $this->host . "/?id=" . $response["id"]]));
|
||||
}else{
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.pasteError"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}else{
|
||||
fclose($fileTimings);
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings]));
|
||||
|
@ -50,8 +50,6 @@ class VersionCommand extends VanillaCommand{
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended", [
|
||||
$sender->getServer()->getName(),
|
||||
$sender->getServer()->getPocketMineVersion(),
|
||||
$sender->getServer()->getCodename(),
|
||||
$sender->getServer()->getApiVersion(),
|
||||
$sender->getServer()->getVersion(),
|
||||
ProtocolInfo::CURRENT_PROTOCOL
|
||||
]));
|
||||
|
@ -51,7 +51,7 @@ class Attribute{
|
||||
/** @var Attribute[] */
|
||||
protected static $attributes = [];
|
||||
|
||||
public static function init(){
|
||||
public static function init() : void{
|
||||
self::addAttribute(self::ABSORPTION, "minecraft:absorption", 0.00, 340282346638528859811704183484516925440.00, 0.00);
|
||||
self::addAttribute(self::SATURATION, "minecraft:player.saturation", 0.00, 20.00, 20.00);
|
||||
self::addAttribute(self::EXHAUSTION, "minecraft:player.exhaustion", 0.00, 5.00, 0.0);
|
||||
@ -92,7 +92,7 @@ class Attribute{
|
||||
*
|
||||
* @return Attribute|null
|
||||
*/
|
||||
public static function getAttribute(int $id){
|
||||
public static function getAttribute(int $id) : ?Attribute{
|
||||
return isset(self::$attributes[$id]) ? clone self::$attributes[$id] : null;
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ class Attribute{
|
||||
*
|
||||
* @return Attribute|null
|
||||
*/
|
||||
public static function getAttributeByName(string $name){
|
||||
public static function getAttributeByName(string $name) : ?Attribute{
|
||||
foreach(self::$attributes as $a){
|
||||
if($a->getName() === $name){
|
||||
return clone $a;
|
||||
@ -170,7 +170,7 @@ class Attribute{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetToDefault(){
|
||||
public function resetToDefault() : void{
|
||||
$this->setValue($this->getDefaultValue());
|
||||
}
|
||||
|
||||
@ -219,7 +219,7 @@ class Attribute{
|
||||
return $this->shouldSend and $this->desynchronized;
|
||||
}
|
||||
|
||||
public function markSynchronized(bool $synced = true){
|
||||
public function markSynchronized(bool $synced = true) : void{
|
||||
$this->desynchronized = !$synced;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class AttributeMap implements \ArrayAccess{
|
||||
/** @var Attribute[] */
|
||||
private $attributes = [];
|
||||
|
||||
public function addAttribute(Attribute $attribute){
|
||||
public function addAttribute(Attribute $attribute) : void{
|
||||
$this->attributes[$attribute->getId()] = $attribute;
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ class AttributeMap implements \ArrayAccess{
|
||||
*
|
||||
* @return Attribute|null
|
||||
*/
|
||||
public function getAttribute(int $id){
|
||||
public function getAttribute(int $id) : ?Attribute{
|
||||
return $this->attributes[$id] ?? null;
|
||||
}
|
||||
|
||||
@ -56,19 +56,28 @@ class AttributeMap implements \ArrayAccess{
|
||||
});
|
||||
}
|
||||
|
||||
public function offsetExists($offset){
|
||||
public function offsetExists($offset) : bool{
|
||||
return isset($this->attributes[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset){
|
||||
/**
|
||||
* @param int $offset
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function offsetGet($offset) : float{
|
||||
return $this->attributes[$offset]->getValue();
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value){
|
||||
/**
|
||||
* @param int $offset
|
||||
* @param float $value
|
||||
*/
|
||||
public function offsetSet($offset, $value) : void{
|
||||
$this->attributes[$offset]->setValue($value);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset){
|
||||
public function offsetUnset($offset) : void{
|
||||
throw new \RuntimeException("Could not unset an attribute from an attribute map");
|
||||
}
|
||||
}
|
||||
|
@ -111,8 +111,8 @@ class Effect{
|
||||
*/
|
||||
public static function getEffectByName(string $name) : ?Effect{
|
||||
$const = self::class . "::" . strtoupper($name);
|
||||
if(\defined($const)){
|
||||
return self::getEffect(\constant($const));
|
||||
if(defined($const)){
|
||||
return self::getEffect(constant($const));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -53,7 +53,6 @@ use pocketmine\level\Level;
|
||||
use pocketmine\level\Location;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Math;
|
||||
use pocketmine\math\Vector2;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\metadata\Metadatable;
|
||||
@ -170,7 +169,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_LIMITED_LIFE = 77;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
|
||||
/* 80 (byte) something to do with nametag visibility? */
|
||||
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
|
||||
@ -388,23 +387,17 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/** @var float|null */
|
||||
public $lastZ = null;
|
||||
|
||||
/** @var float */
|
||||
public $motionX = 0.0;
|
||||
/** @var float */
|
||||
public $motionY = 0.0;
|
||||
/** @var float */
|
||||
public $motionZ = 0.0;
|
||||
/** @var Vector3 */
|
||||
public $temporalVector;
|
||||
/** @var float */
|
||||
public $lastMotionX;
|
||||
/** @var float */
|
||||
public $lastMotionY;
|
||||
/** @var float */
|
||||
public $lastMotionZ;
|
||||
protected $motion;
|
||||
/** @var Vector3 */
|
||||
protected $lastMotion;
|
||||
/** @var bool */
|
||||
protected $forceMovementUpdate = false;
|
||||
|
||||
/** @var Vector3 */
|
||||
public $temporalVector;
|
||||
|
||||
|
||||
/** @var float */
|
||||
public $lastYaw;
|
||||
/** @var float */
|
||||
@ -560,9 +553,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->setGenericFlag(self::DATA_FLAG_AFFECTED_BY_GRAVITY, true);
|
||||
$this->setGenericFlag(self::DATA_FLAG_HAS_COLLISION, true);
|
||||
|
||||
$this->initEntity();
|
||||
$this->propertyManager->clearDirtyProperties(); //Prevents resending properties that were set during construction
|
||||
|
||||
$this->chunk->addEntity($this);
|
||||
$this->level->addEntity($this);
|
||||
$this->initEntity();
|
||||
|
||||
$this->lastUpdate = $this->server->getTick();
|
||||
$this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
|
||||
|
||||
@ -573,7 +569,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNameTag(){
|
||||
public function getNameTag() : string{
|
||||
return $this->propertyManager->getString(self::DATA_NAMETAG);
|
||||
}
|
||||
|
||||
@ -595,22 +591,22 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setNameTag($name){
|
||||
public function setNameTag(string $name) : void{
|
||||
$this->propertyManager->setString(self::DATA_NAMETAG, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setNameTagVisible(bool $value = true){
|
||||
public function setNameTagVisible(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_CAN_SHOW_NAMETAG, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setNameTagAlwaysVisible(bool $value = true){
|
||||
$this->setGenericFlag(self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value);
|
||||
public function setNameTagAlwaysVisible(bool $value = true) : void{
|
||||
$this->propertyManager->setByte(self::DATA_ALWAYS_SHOW_NAMETAG, $value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -623,7 +619,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @param float $value
|
||||
*/
|
||||
public function setScale(float $value){
|
||||
public function setScale(float $value) : void{
|
||||
$multiplier = $value / $this->getScale();
|
||||
|
||||
$this->width *= $multiplier;
|
||||
@ -635,7 +631,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->propertyManager->setFloat(self::DATA_SCALE, $value);
|
||||
}
|
||||
|
||||
public function getBoundingBox(){
|
||||
public function getBoundingBox() : AxisAlignedBB{
|
||||
return $this->boundingBox;
|
||||
}
|
||||
|
||||
@ -656,7 +652,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_SNEAKING);
|
||||
}
|
||||
|
||||
public function setSneaking(bool $value = true){
|
||||
public function setSneaking(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_SNEAKING, $value);
|
||||
}
|
||||
|
||||
@ -664,7 +660,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_SPRINTING);
|
||||
}
|
||||
|
||||
public function setSprinting(bool $value = true){
|
||||
public function setSprinting(bool $value = true) : void{
|
||||
if($value !== $this->isSprinting()){
|
||||
$this->setGenericFlag(self::DATA_FLAG_SPRINTING, $value);
|
||||
$attr = $this->attributeMap->getAttribute(Attribute::MOVEMENT_SPEED);
|
||||
@ -676,7 +672,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_IMMOBILE);
|
||||
}
|
||||
|
||||
public function setImmobile(bool $value = true){
|
||||
public function setImmobile(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_IMMOBILE, $value);
|
||||
}
|
||||
|
||||
@ -698,9 +694,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
/**
|
||||
* Sets whether the entity is able to climb climbable blocks.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setCanClimb(bool $value = true){
|
||||
public function setCanClimb(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_CAN_CLIMB, $value);
|
||||
}
|
||||
|
||||
@ -718,7 +715,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setCanClimbWalls(bool $value = true){
|
||||
public function setCanClimbWalls(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_WALLCLIMBING, $value);
|
||||
}
|
||||
|
||||
@ -726,7 +723,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* Returns the entity ID of the owning entity, or null if the entity doesn't have an owner.
|
||||
* @return int|null
|
||||
*/
|
||||
public function getOwningEntityId(){
|
||||
public function getOwningEntityId() : ?int{
|
||||
return $this->propertyManager->getLong(self::DATA_OWNER_EID);
|
||||
}
|
||||
|
||||
@ -734,7 +731,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* Returns the owning entity, or null if the entity was not found.
|
||||
* @return Entity|null
|
||||
*/
|
||||
public function getOwningEntity(){
|
||||
public function getOwningEntity() : ?Entity{
|
||||
$eid = $this->getOwningEntityId();
|
||||
if($eid !== null){
|
||||
return $this->server->findEntity($eid, $this->level);
|
||||
@ -750,7 +747,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @throws \InvalidArgumentException if the supplied entity is not valid
|
||||
*/
|
||||
public function setOwningEntity(Entity $owner = null){
|
||||
public function setOwningEntity(?Entity $owner) : void{
|
||||
if($owner === null){
|
||||
$this->propertyManager->removeProperty(self::DATA_OWNER_EID);
|
||||
}elseif($owner->closed){
|
||||
@ -764,7 +761,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* Returns the entity ID of the entity's target, or null if it doesn't have a target.
|
||||
* @return int|null
|
||||
*/
|
||||
public function getTargetEntityId(){
|
||||
public function getTargetEntityId() : ?int{
|
||||
return $this->propertyManager->getLong(self::DATA_TARGET_EID);
|
||||
}
|
||||
|
||||
@ -774,7 +771,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @return Entity|null
|
||||
*/
|
||||
public function getTargetEntity(){
|
||||
public function getTargetEntity() : ?Entity{
|
||||
$eid = $this->getTargetEntityId();
|
||||
if($eid !== null){
|
||||
return $this->server->findEntity($eid, $this->level);
|
||||
@ -790,7 +787,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @throws \InvalidArgumentException if the target entity is not valid
|
||||
*/
|
||||
public function setTargetEntity(Entity $target = null){
|
||||
public function setTargetEntity(?Entity $target) : void{
|
||||
if($target === null){
|
||||
$this->propertyManager->removeProperty(self::DATA_TARGET_EID);
|
||||
}elseif($target->closed){
|
||||
@ -823,7 +820,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveId(){
|
||||
public function getSaveId() : string{
|
||||
if(!isset(self::$saveNames[static::class])){
|
||||
throw new \InvalidStateException("Entity is not registered");
|
||||
}
|
||||
@ -831,7 +828,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return current(self::$saveNames[static::class]);
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
if(!($this instanceof Player)){
|
||||
$this->namedtag->setString("id", $this->getSaveId(), true);
|
||||
|
||||
@ -850,9 +847,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
]));
|
||||
|
||||
$this->namedtag->setTag(new ListTag("Motion", [
|
||||
new DoubleTag("", $this->motionX),
|
||||
new DoubleTag("", $this->motionY),
|
||||
new DoubleTag("", $this->motionZ)
|
||||
new DoubleTag("", $this->motion->x),
|
||||
new DoubleTag("", $this->motion->y),
|
||||
new DoubleTag("", $this->motion->z)
|
||||
]));
|
||||
|
||||
$this->namedtag->setTag(new ListTag("Rotation", [
|
||||
@ -867,7 +864,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->namedtag->setByte("Invulnerable", $this->invulnerable ? 1 : 0);
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
assert($this->namedtag instanceof CompoundTag);
|
||||
|
||||
if($this->namedtag->hasTag("CustomName", StringTag::class)){
|
||||
@ -881,18 +878,16 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->setNameTagVisible($this->namedtag->getByte("CustomNameVisible", 1) !== 0);
|
||||
}
|
||||
}
|
||||
|
||||
$this->scheduleUpdate();
|
||||
}
|
||||
|
||||
protected function addAttributes(){
|
||||
protected function addAttributes() : void{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EntityDamageEvent $source
|
||||
*/
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
@ -906,7 +901,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @param EntityRegainHealthEvent $source
|
||||
*/
|
||||
public function heal(EntityRegainHealthEvent $source){
|
||||
public function heal(EntityRegainHealthEvent $source) : void{
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
@ -915,7 +910,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->setHealth($this->getHealth() + $source->getAmount());
|
||||
}
|
||||
|
||||
public function kill(){
|
||||
public function kill() : void{
|
||||
$this->health = 0;
|
||||
$this->scheduleUpdate();
|
||||
}
|
||||
@ -946,7 +941,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @param float $amount
|
||||
*/
|
||||
public function setHealth(float $amount){
|
||||
public function setHealth(float $amount) : void{
|
||||
if($amount == $this->health){
|
||||
return;
|
||||
}
|
||||
@ -973,25 +968,25 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @param int $amount
|
||||
*/
|
||||
public function setMaxHealth(int $amount){
|
||||
public function setMaxHealth(int $amount) : void{
|
||||
$this->maxHealth = $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EntityDamageEvent $type
|
||||
*/
|
||||
public function setLastDamageCause(EntityDamageEvent $type){
|
||||
public function setLastDamageCause(EntityDamageEvent $type) : void{
|
||||
$this->lastDamageCause = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityDamageEvent|null
|
||||
*/
|
||||
public function getLastDamageCause(){
|
||||
public function getLastDamageCause() : ?EntityDamageEvent{
|
||||
return $this->lastDamageCause;
|
||||
}
|
||||
|
||||
public function getAttributeMap(){
|
||||
public function getAttributeMap() : AttributeMap{
|
||||
return $this->attributeMap;
|
||||
}
|
||||
|
||||
@ -1041,7 +1036,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return $this->fireTicks > 0;
|
||||
}
|
||||
|
||||
public function setOnFire(int $seconds){
|
||||
public function setOnFire(int $seconds) : void{
|
||||
$ticks = $seconds * 20;
|
||||
if($ticks > $this->fireTicks){
|
||||
$this->fireTicks = $ticks;
|
||||
@ -1064,7 +1059,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->fireTicks = $fireTicks;
|
||||
}
|
||||
|
||||
public function extinguish(){
|
||||
public function extinguish() : void{
|
||||
$this->fireTicks = 0;
|
||||
$this->setGenericFlag(self::DATA_FLAG_ONFIRE, false);
|
||||
}
|
||||
@ -1096,7 +1091,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* Called to deal damage to entities when they are on fire.
|
||||
*/
|
||||
protected function dealFireDamage(){
|
||||
protected function dealFireDamage() : void{
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1);
|
||||
$this->attack($ev);
|
||||
}
|
||||
@ -1106,14 +1101,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
public function canBeCollidedWith() : bool{
|
||||
return true;
|
||||
return $this->isAlive();
|
||||
}
|
||||
|
||||
protected function updateMovement(bool $teleport = false){
|
||||
protected function updateMovement(bool $teleport = false) : void{
|
||||
$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->motionX - $this->lastMotionX) ** 2 + ($this->motionY - $this->lastMotionY) ** 2 + ($this->motionZ - $this->lastMotionZ) ** 2;
|
||||
$diffMotion = $this->motion->subtract($this->lastMotion)->lengthSquared();
|
||||
|
||||
if($teleport or $diffPosition > 0.0001 or $diffRotation > 1.0){
|
||||
$this->lastX = $this->x;
|
||||
@ -1126,10 +1121,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->broadcastMovement($teleport);
|
||||
}
|
||||
|
||||
if($diffMotion > 0.0025 or ($diffMotion > 0.0001 and $this->getMotion()->lengthSquared() <= 0.0001)){ //0.05 ** 2
|
||||
$this->lastMotionX = $this->motionX;
|
||||
$this->lastMotionY = $this->motionY;
|
||||
$this->lastMotionZ = $this->motionZ;
|
||||
if($diffMotion > 0.0025 or ($diffMotion > 0.0001 and $this->motion->lengthSquared() <= 0.0001)){ //0.05 ** 2
|
||||
$this->lastMotion = clone $this->motion;
|
||||
|
||||
$this->broadcastMotion();
|
||||
}
|
||||
@ -1139,7 +1132,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return new Vector3($vector3->x, $vector3->y + $this->baseOffset, $vector3->z);
|
||||
}
|
||||
|
||||
protected function broadcastMovement(bool $teleport = false){
|
||||
protected function broadcastMovement(bool $teleport = false) : void{
|
||||
if($this->chunk !== null){
|
||||
$pk = new MoveEntityPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
@ -1153,7 +1146,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
protected function broadcastMotion(){
|
||||
protected function broadcastMotion() : void{
|
||||
if($this->chunk !== null){
|
||||
$pk = new SetEntityMotionPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
@ -1167,29 +1160,29 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function applyGravity(){
|
||||
$this->motionY -= $this->gravity;
|
||||
protected function applyGravity() : void{
|
||||
$this->motion->y -= $this->gravity;
|
||||
}
|
||||
|
||||
protected function tryChangeMovement(){
|
||||
protected function tryChangeMovement() : void{
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
if($this->applyDragBeforeGravity()){
|
||||
$this->motionY *= $friction;
|
||||
$this->motion->y *= $friction;
|
||||
}
|
||||
|
||||
$this->applyGravity();
|
||||
|
||||
if(!$this->applyDragBeforeGravity()){
|
||||
$this->motionY *= $friction;
|
||||
$this->motion->y *= $friction;
|
||||
}
|
||||
|
||||
if($this->onGround){
|
||||
$friction *= $this->level->getBlockAt(Math::floorFloat($this->x), Math::floorFloat($this->y) - 1, Math::floorFloat($this->z))->getFrictionFactor();
|
||||
$friction *= $this->level->getBlockAt((int) floor($this->x), (int) floor($this->y - 1), (int) floor($this->z))->getFrictionFactor();
|
||||
}
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionZ *= $friction;
|
||||
$this->motion->x *= $friction;
|
||||
$this->motion->z *= $friction;
|
||||
}
|
||||
|
||||
protected function checkObstruction(float $x, float $y, float $z) : bool{
|
||||
@ -1197,9 +1190,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return false;
|
||||
}
|
||||
|
||||
$floorX = Math::floorFloat($x);
|
||||
$floorY = Math::floorFloat($y);
|
||||
$floorZ = Math::floorFloat($z);
|
||||
$floorX = (int) floor($x);
|
||||
$floorY = (int) floor($y);
|
||||
$floorZ = (int) floor($z);
|
||||
|
||||
$diffX = $x - $floorX;
|
||||
$diffY = $y - $floorY;
|
||||
@ -1248,37 +1241,37 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$force = lcg_value() * 0.2 + 0.1;
|
||||
|
||||
if($direction === Vector3::SIDE_WEST){
|
||||
$this->motionX = -$force;
|
||||
$this->motion->x = -$force;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($direction === Vector3::SIDE_EAST){
|
||||
$this->motionX = $force;
|
||||
$this->motion->x = $force;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($direction === Vector3::SIDE_DOWN){
|
||||
$this->motionY = -$force;
|
||||
$this->motion->y = -$force;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($direction === Vector3::SIDE_UP){
|
||||
$this->motionY = $force;
|
||||
$this->motion->y = $force;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($direction === Vector3::SIDE_NORTH){
|
||||
$this->motionZ = -$force;
|
||||
$this->motion->z = -$force;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($direction === Vector3::SIDE_SOUTH){
|
||||
$this->motionZ = $force;
|
||||
$this->motion->z = $force;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1290,7 +1283,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getDirection(){
|
||||
public function getDirection() : ?int{
|
||||
$rotation = ($this->yaw - 90) % 360;
|
||||
if($rotation < 0){
|
||||
$rotation += 360.0;
|
||||
@ -1340,11 +1333,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
$this->lastUpdate = $currentTick;
|
||||
|
||||
if($this->needsDespawn){
|
||||
$this->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->isAlive()){
|
||||
if($this->onDeathUpdate($tickDiff)){
|
||||
$this->flagForDespawn();
|
||||
@ -1358,16 +1346,19 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
if($this->hasMovementUpdate()){
|
||||
$this->tryChangeMovement();
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
if(abs($this->motionX) <= self::MOTION_THRESHOLD){
|
||||
$this->motionX = 0;
|
||||
if(abs($this->motion->x) <= self::MOTION_THRESHOLD){
|
||||
$this->motion->x = 0;
|
||||
}
|
||||
if(abs($this->motionY) <= self::MOTION_THRESHOLD){
|
||||
$this->motionY = 0;
|
||||
if(abs($this->motion->y) <= self::MOTION_THRESHOLD){
|
||||
$this->motion->y = 0;
|
||||
}
|
||||
if(abs($this->motionZ) <= self::MOTION_THRESHOLD){
|
||||
$this->motionZ = 0;
|
||||
if(abs($this->motion->z) <= self::MOTION_THRESHOLD){
|
||||
$this->motion->z = 0;
|
||||
}
|
||||
|
||||
if($this->motion->x != 0 or $this->motion->y != 0 or $this->motion->z != 0){
|
||||
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
|
||||
}
|
||||
|
||||
$this->forceMovementUpdate = false;
|
||||
@ -1388,7 +1379,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
//return !($this instanceof Player);
|
||||
}
|
||||
|
||||
final public function scheduleUpdate(){
|
||||
final public function scheduleUpdate() : void{
|
||||
if($this->closed){
|
||||
throw new \InvalidStateException("Cannot schedule update on garbage entity " . get_class($this));
|
||||
}
|
||||
$this->level->updateEntities[$this->id] = $this;
|
||||
}
|
||||
|
||||
@ -1403,7 +1397,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
final public function setForceMovementUpdate(bool $value = true){
|
||||
final public function setForceMovementUpdate(bool $value = true) : void{
|
||||
$this->forceMovementUpdate = $value;
|
||||
|
||||
$this->blocksAround = null;
|
||||
@ -1416,9 +1410,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public function hasMovementUpdate() : bool{
|
||||
return (
|
||||
$this->forceMovementUpdate or
|
||||
$this->motionX != 0 or
|
||||
$this->motionY != 0 or
|
||||
$this->motionZ != 0 or
|
||||
$this->motion->x != 0 or
|
||||
$this->motion->y != 0 or
|
||||
$this->motion->z != 0 or
|
||||
!$this->onGround
|
||||
);
|
||||
}
|
||||
@ -1427,7 +1421,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function resetFallDistance(){
|
||||
public function resetFallDistance() : void{
|
||||
$this->fallDistance = 0.0;
|
||||
}
|
||||
|
||||
@ -1435,7 +1429,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param float $distanceThisTick
|
||||
* @param bool $onGround
|
||||
*/
|
||||
protected function updateFallState(float $distanceThisTick, bool $onGround){
|
||||
protected function updateFallState(float $distanceThisTick, bool $onGround) : void{
|
||||
if($onGround){
|
||||
if($this->fallDistance > 0){
|
||||
$this->fall($this->fallDistance);
|
||||
@ -1451,11 +1445,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @param float $fallDistance
|
||||
*/
|
||||
public function fall(float $fallDistance){
|
||||
|
||||
}
|
||||
|
||||
public function handleLavaMovement(){ //TODO
|
||||
public function fall(float $fallDistance) : void{
|
||||
|
||||
}
|
||||
|
||||
@ -1463,16 +1453,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return $this->eyeHeight;
|
||||
}
|
||||
|
||||
public function moveFlying(){ //TODO
|
||||
public function onCollideWithPlayer(Player $player) : void{
|
||||
|
||||
}
|
||||
|
||||
public function onCollideWithPlayer(Player $player){
|
||||
|
||||
}
|
||||
|
||||
public function isInsideOfWater() : bool{
|
||||
$block = $this->level->getBlockAt(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z));
|
||||
public function isUnderwater() : bool{
|
||||
$block = $this->level->getBlockAt((int) floor($this->x), (int) floor($y = ($this->y + $this->getEyeHeight())), (int) floor($this->z));
|
||||
|
||||
if($block instanceof Water){
|
||||
$f = ($block->y + 1) - ($block->getFluidHeightPercent() - 0.1111111);
|
||||
@ -1483,7 +1469,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
public function isInsideOfSolid() : bool{
|
||||
$block = $this->level->getBlockAt(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z));
|
||||
$block = $this->level->getBlockAt((int) floor($this->x), (int) floor($y = ($this->y + $this->getEyeHeight())), (int) floor($this->z));
|
||||
|
||||
return $block->isSolid() and !$block->isTransparent() and $block->collidesWithBB($this->getBoundingBox());
|
||||
}
|
||||
@ -1497,7 +1483,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
Timings::$entityMoveTimer->startTiming();
|
||||
|
||||
$newBB = $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz);
|
||||
$newBB = $this->boundingBox->offsetCopy($dx, $dy, $dz);
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $newBB, false);
|
||||
|
||||
@ -1585,7 +1571,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
assert(abs($dx) <= 20 and abs($dy) <= 20 and abs($dz) <= 20, "Movement distance is excessive: dx=$dx, dy=$dy, dz=$dz");
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false);
|
||||
$list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->offsetCopy($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false);
|
||||
|
||||
foreach($list as $bb){
|
||||
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
|
||||
@ -1661,15 +1647,15 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->updateFallState($dy, $this->onGround);
|
||||
|
||||
if($movX != $dx){
|
||||
$this->motionX = 0;
|
||||
$this->motion->x = 0;
|
||||
}
|
||||
|
||||
if($movY != $dy){
|
||||
$this->motionY = 0;
|
||||
$this->motion->y = 0;
|
||||
}
|
||||
|
||||
if($movZ != $dz){
|
||||
$this->motionZ = 0;
|
||||
$this->motion->z = 0;
|
||||
}
|
||||
|
||||
//TODO: vehicle collision events (first we need to spawn them!)
|
||||
@ -1677,7 +1663,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
Timings::$entityMoveTimer->stopTiming();
|
||||
}
|
||||
|
||||
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz){
|
||||
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
|
||||
$this->isCollidedVertically = $movY != $dy;
|
||||
$this->isCollidedHorizontally = ($movX != $dx or $movZ != $dz);
|
||||
$this->isCollided = ($this->isCollidedHorizontally or $this->isCollidedVertically);
|
||||
@ -1691,12 +1677,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
if($this->blocksAround === null){
|
||||
$inset = 0.001; //Offset against floating-point errors
|
||||
|
||||
$minX = Math::floorFloat($this->boundingBox->minX + $inset);
|
||||
$minY = Math::floorFloat($this->boundingBox->minY + $inset);
|
||||
$minZ = Math::floorFloat($this->boundingBox->minZ + $inset);
|
||||
$maxX = Math::floorFloat($this->boundingBox->maxX - $inset);
|
||||
$maxY = Math::floorFloat($this->boundingBox->maxY - $inset);
|
||||
$maxZ = Math::floorFloat($this->boundingBox->maxZ - $inset);
|
||||
$minX = (int) floor($this->boundingBox->minX + $inset);
|
||||
$minY = (int) floor($this->boundingBox->minY + $inset);
|
||||
$minZ = (int) floor($this->boundingBox->minZ + $inset);
|
||||
$maxX = (int) floor($this->boundingBox->maxX - $inset);
|
||||
$maxY = (int) floor($this->boundingBox->maxY - $inset);
|
||||
$maxZ = (int) floor($this->boundingBox->maxZ - $inset);
|
||||
|
||||
$this->blocksAround = [];
|
||||
|
||||
@ -1724,7 +1710,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkBlockCollision(){
|
||||
protected function checkBlockCollision() : void{
|
||||
$vector = $this->temporalVector->setComponents(0, 0, 0);
|
||||
|
||||
foreach($this->getBlocksAround() as $block){
|
||||
@ -1735,9 +1721,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
if($vector->lengthSquared() > 0){
|
||||
$vector = $vector->normalize();
|
||||
$d = 0.014;
|
||||
$this->motionX += $vector->x * $d;
|
||||
$this->motionY += $vector->y * $d;
|
||||
$this->motionZ += $vector->z * $d;
|
||||
$this->motion->x += $vector->x * $d;
|
||||
$this->motion->y += $vector->y * $d;
|
||||
$this->motion->z += $vector->z * $d;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1773,7 +1759,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setRotation(float $yaw, float $pitch){
|
||||
public function setRotation(float $yaw, float $pitch) : void{
|
||||
$this->yaw = $yaw;
|
||||
$this->pitch = $pitch;
|
||||
$this->scheduleUpdate();
|
||||
@ -1789,7 +1775,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function checkChunks(){
|
||||
protected function checkChunks() : void{
|
||||
$chunkX = $this->getFloorX() >> 4;
|
||||
$chunkZ = $this->getFloorZ() >> 4;
|
||||
if($this->chunk === null or ($this->chunk->getX() !== $chunkX or $this->chunk->getZ() !== $chunkZ)){
|
||||
@ -1820,17 +1806,17 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
protected function resetLastMovements(){
|
||||
protected function resetLastMovements() : void{
|
||||
list($this->lastX, $this->lastY, $this->lastZ) = [$this->x, $this->y, $this->z];
|
||||
list($this->lastYaw, $this->lastPitch) = [$this->yaw, $this->pitch];
|
||||
list($this->lastMotionX, $this->lastMotionY, $this->lastMotionZ) = [$this->motionX, $this->motionY, $this->motionZ];
|
||||
$this->lastMotion = clone $this->motion;
|
||||
}
|
||||
|
||||
public function getMotion() : Vector3{
|
||||
return new Vector3($this->motionX, $this->motionY, $this->motionZ);
|
||||
return clone $this->motion;
|
||||
}
|
||||
|
||||
public function setMotion(Vector3 $motion){
|
||||
public function setMotion(Vector3 $motion) : bool{
|
||||
if(!$this->justCreated){
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion));
|
||||
if($ev->isCancelled()){
|
||||
@ -1838,9 +1824,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
$this->motionX = $motion->x;
|
||||
$this->motionY = $motion->y;
|
||||
$this->motionZ = $motion->z;
|
||||
$this->motion = clone $motion;
|
||||
|
||||
if(!$this->justCreated){
|
||||
$this->updateMovement();
|
||||
@ -1860,7 +1844,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function teleport(Vector3 $pos, float $yaw = null, float $pitch = null) : bool{
|
||||
public function teleport(Vector3 $pos, ?float $yaw = null, ?float $pitch = null) : bool{
|
||||
if($pos instanceof Location){
|
||||
$yaw = $yaw ?? $pos->yaw;
|
||||
$pitch = $pitch ?? $pos->pitch;
|
||||
@ -1945,7 +1929,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/**
|
||||
* @param Player $player
|
||||
*/
|
||||
public function spawnTo(Player $player){
|
||||
public function spawnTo(Player $player) : void{
|
||||
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;
|
||||
|
||||
@ -1953,7 +1937,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
public function spawnToAll(){
|
||||
public function spawnToAll() : void{
|
||||
if($this->chunk === null or $this->closed){
|
||||
return;
|
||||
}
|
||||
@ -1964,7 +1948,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
public function respawnToAll(){
|
||||
public function respawnToAll() : void{
|
||||
foreach($this->hasSpawned as $key => $player){
|
||||
unset($this->hasSpawned[$key]);
|
||||
$this->spawnTo($player);
|
||||
@ -1975,7 +1959,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param Player $player
|
||||
* @param bool $send
|
||||
*/
|
||||
public function despawnFrom(Player $player, bool $send = true){
|
||||
public function despawnFrom(Player $player, bool $send = true) : void{
|
||||
if(isset($this->hasSpawned[$player->getLoaderId()])){
|
||||
if($send){
|
||||
$pk = new RemoveEntityPacket();
|
||||
@ -1986,7 +1970,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
public function despawnFromAll(){
|
||||
public function despawnFromAll() : void{
|
||||
foreach($this->hasSpawned as $player){
|
||||
$this->despawnFrom($player);
|
||||
}
|
||||
@ -1997,6 +1981,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*/
|
||||
public function flagForDespawn() : void{
|
||||
$this->needsDespawn = true;
|
||||
$this->scheduleUpdate();
|
||||
}
|
||||
|
||||
public function isFlaggedForDespawn() : bool{
|
||||
@ -2016,7 +2001,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* WARNING: Entities are unusable after this has been executed!
|
||||
*/
|
||||
public function close(){
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
$this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this));
|
||||
$this->closed = true;
|
||||
@ -2045,7 +2030,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param bool $value
|
||||
* @param int $propertyType
|
||||
*/
|
||||
public function setDataFlag(int $propertyId, int $flagId, bool $value = true, int $propertyType = self::DATA_TYPE_LONG){
|
||||
public function setDataFlag(int $propertyId, int $flagId, bool $value = true, int $propertyType = self::DATA_TYPE_LONG) : void{
|
||||
if($this->getDataFlag($propertyId, $flagId) !== $value){
|
||||
$flags = (int) $this->propertyManager->getPropertyValue($propertyId, $propertyType);
|
||||
$flags ^= 1 << $flagId;
|
||||
@ -2079,7 +2064,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param int $flagId
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setGenericFlag(int $flagId, bool $value = true){
|
||||
public function setGenericFlag(int $flagId, bool $value = true) : void{
|
||||
$this->setDataFlag(self::DATA_FLAGS, $flagId, $value, self::DATA_TYPE_LONG);
|
||||
}
|
||||
|
||||
@ -2087,7 +2072,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param Player[]|Player $player
|
||||
* @param array $data Properly formatted entity data, defaults to everything
|
||||
*/
|
||||
public function sendData($player, array $data = null){
|
||||
public function sendData($player, ?array $data = null) : void{
|
||||
if(!is_array($player)){
|
||||
$player = [$player];
|
||||
}
|
||||
@ -2140,5 +2125,4 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public function __toString(){
|
||||
return (new \ReflectionClass($this))->getShortName() . "(" . $this->getId() . ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ interface EntityIds{
|
||||
public const POLAR_BEAR = 28;
|
||||
public const LLAMA = 29;
|
||||
public const PARROT = 30;
|
||||
|
||||
public const DOLPHIN = 31;
|
||||
public const ZOMBIE = 32;
|
||||
public const CREEPER = 33;
|
||||
public const SKELETON = 34;
|
||||
@ -86,6 +86,7 @@ interface EntityIds{
|
||||
public const EYE_OF_ENDER_SIGNAL = 70;
|
||||
public const ENDER_CRYSTAL = 71;
|
||||
public const FIREWORKS_ROCKET = 72;
|
||||
public const TRIDENT = 73;
|
||||
|
||||
public const SHULKER_BULLET = 76;
|
||||
public const FISHING_HOOK = 77;
|
||||
@ -116,4 +117,11 @@ interface EntityIds{
|
||||
public const EVOCATION_FANG = 103;
|
||||
public const EVOCATION_ILLAGER = 104;
|
||||
public const VEX = 105;
|
||||
public const ICE_BOMB = 106;
|
||||
public const BALLOON = 107;
|
||||
public const PUFFERFISH = 108;
|
||||
public const SALMON = 109;
|
||||
public const DROWNED = 110;
|
||||
public const TROPICAL_FISH = 111;
|
||||
public const FISH = 112;
|
||||
}
|
||||
|
@ -30,11 +30,14 @@ use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\event\player\PlayerExhaustEvent;
|
||||
use pocketmine\event\player\PlayerExperienceChangeEvent;
|
||||
use pocketmine\inventory\EnderChestInventory;
|
||||
use pocketmine\inventory\EntityInventoryEventProcessor;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\inventory\PlayerInventory;
|
||||
use pocketmine\item\Consumable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\item\FoodSource;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\Totem;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\ByteArrayTag;
|
||||
@ -43,6 +46,7 @@ use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
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\PlayerSkinPacket;
|
||||
@ -111,7 +115,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
/**
|
||||
* @return UUID|null
|
||||
*/
|
||||
public function getUniqueId(){
|
||||
public function getUniqueId() : ?UUID{
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
@ -151,14 +155,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
*
|
||||
* @param Player[]|null $targets
|
||||
*/
|
||||
public function sendSkin(array $targets = null) : void{
|
||||
public function sendSkin(?array $targets = null) : void{
|
||||
$pk = new PlayerSkinPacket();
|
||||
$pk->uuid = $this->getUniqueId();
|
||||
$pk->skin = $this->skin;
|
||||
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
|
||||
}
|
||||
|
||||
public function jump(){
|
||||
public function jump() : void{
|
||||
parent::jump();
|
||||
if($this->isSprinting()){
|
||||
$this->exhaust(0.8, PlayerExhaustEvent::CAUSE_SPRINT_JUMPING);
|
||||
@ -179,7 +183,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setFood(float $new){
|
||||
public function setFood(float $new) : void{
|
||||
$attr = $this->attributeMap->getAttribute(Attribute::HUNGER);
|
||||
$old = $attr->getValue();
|
||||
$attr->setValue($new);
|
||||
@ -202,7 +206,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return $this->attributeMap->getAttribute(Attribute::HUNGER)->getMaxValue();
|
||||
}
|
||||
|
||||
public function addFood(float $amount){
|
||||
public function addFood(float $amount) : void{
|
||||
$attr = $this->attributeMap->getAttribute(Attribute::HUNGER);
|
||||
$amount += $attr->getValue();
|
||||
$amount = max(min($amount, $attr->getMaxValue()), $attr->getMinValue());
|
||||
@ -230,11 +234,11 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setSaturation(float $saturation){
|
||||
public function setSaturation(float $saturation) : void{
|
||||
$this->attributeMap->getAttribute(Attribute::SATURATION)->setValue($saturation);
|
||||
}
|
||||
|
||||
public function addSaturation(float $amount){
|
||||
public function addSaturation(float $amount) : void{
|
||||
$attr = $this->attributeMap->getAttribute(Attribute::SATURATION);
|
||||
$attr->setValue($attr->getValue() + $amount, true);
|
||||
}
|
||||
@ -249,7 +253,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
*
|
||||
* @param float $exhaustion
|
||||
*/
|
||||
public function setExhaustion(float $exhaustion){
|
||||
public function setExhaustion(float $exhaustion) : void{
|
||||
$this->attributeMap->getAttribute(Attribute::EXHAUSTION)->setValue($exhaustion);
|
||||
}
|
||||
|
||||
@ -529,14 +533,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function getEnderChestInventory(){
|
||||
public function getEnderChestInventory() : EnderChestInventory{
|
||||
return $this->enderChestInventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
|
||||
*/
|
||||
protected function initHumanData(){
|
||||
protected function initHumanData() : void{
|
||||
if($this->namedtag->hasTag("NameTag", StringTag::class)){
|
||||
$this->setNameTag($this->namedtag->getString("NameTag"));
|
||||
}
|
||||
@ -555,18 +559,21 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$this->uuid = UUID::fromData((string) $this->getId(), $this->skin->getSkinData(), $this->getNameTag());
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->setPlayerFlag(self::DATA_PLAYER_FLAG_SLEEP, false);
|
||||
$this->propertyManager->setBlockPos(self::DATA_PLAYER_BED_POSITION, null);
|
||||
|
||||
$this->inventory = new PlayerInventory($this);
|
||||
$this->enderChestInventory = new EnderChestInventory($this);
|
||||
$this->enderChestInventory = new EnderChestInventory();
|
||||
$this->initHumanData();
|
||||
|
||||
$inventoryTag = $this->namedtag->getListTag("Inventory");
|
||||
if($inventoryTag !== null){
|
||||
$armorListener = $this->armorInventory->getEventProcessor();
|
||||
$this->armorInventory->setEventProcessor(null);
|
||||
|
||||
/** @var CompoundTag $item */
|
||||
foreach($inventoryTag as $i => $item){
|
||||
$slot = $item->getByte("Slot");
|
||||
@ -578,6 +585,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$this->inventory->setItem($slot - 9, Item::nbtDeserialize($item));
|
||||
}
|
||||
}
|
||||
|
||||
$this->armorInventory->setEventProcessor($armorListener);
|
||||
}
|
||||
|
||||
$enderChestInventoryTag = $this->namedtag->getListTag("EnderChestInventory");
|
||||
@ -590,6 +599,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
$this->inventory->setHeldItemIndex($this->namedtag->getInt("SelectedInventorySlot", 0), false);
|
||||
|
||||
$this->inventory->setEventProcessor(new EntityInventoryEventProcessor($this));
|
||||
|
||||
$this->setFood((float) $this->namedtag->getInt("foodLevel", (int) $this->getFood(), true));
|
||||
$this->setExhaustion($this->namedtag->getFloat("foodExhaustionLevel", $this->getExhaustion(), true));
|
||||
@ -607,7 +617,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
protected function addAttributes(){
|
||||
protected function addAttributes() : void{
|
||||
parent::addAttributes();
|
||||
|
||||
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::SATURATION));
|
||||
@ -629,7 +639,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
public function doFoodTick(int $tickDiff = 1){
|
||||
protected function doFoodTick(int $tickDiff = 1) : void{
|
||||
if($this->isAlive()){
|
||||
$food = $this->getFood();
|
||||
$health = $this->getHealth();
|
||||
@ -641,8 +651,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
|
||||
if($difficulty === Level::DIFFICULTY_PEACEFUL and $this->foodTickTimer % 10 === 0){
|
||||
if($food < 20){
|
||||
if($food < $this->getMaxFood()){
|
||||
$this->addFood(1.0);
|
||||
$food = $this->getFood();
|
||||
}
|
||||
if($this->foodTickTimer % 20 === 0 and $health < $this->getMaxHealth()){
|
||||
$this->heal(new EntityRegainHealthEvent($this, 1, EntityRegainHealthEvent::CAUSE_SATURATION));
|
||||
@ -656,16 +667,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$this->exhaust(3.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN);
|
||||
}
|
||||
}elseif($food <= 0){
|
||||
if(($difficulty === 1 and $health > 10) or ($difficulty === 2 and $health > 1) or $difficulty === 3){
|
||||
if(($difficulty === Level::DIFFICULTY_EASY and $health > 10) or ($difficulty === Level::DIFFICULTY_NORMAL and $health > 1) or $difficulty === Level::DIFFICULTY_HARD){
|
||||
$this->attack(new EntityDamageEvent($this, EntityDamageEvent::CAUSE_STARVATION, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($food <= 6){
|
||||
if($this->isSprinting()){
|
||||
$this->setSprinting(false);
|
||||
}
|
||||
$this->setSprinting(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -674,14 +683,49 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return $this->getNameTag();
|
||||
}
|
||||
|
||||
public function getDrops() : array{
|
||||
return array_merge(
|
||||
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
|
||||
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : []
|
||||
);
|
||||
public function applyDamageModifiers(EntityDamageEvent $source) : void{
|
||||
parent::applyDamageModifiers($source);
|
||||
|
||||
$type = $source->getCause();
|
||||
if($type !== EntityDamageEvent::CAUSE_SUICIDE and $type !== EntityDamageEvent::CAUSE_VOID
|
||||
and $this->inventory->getItemInHand() instanceof Totem){ //TODO: check offhand as well (when it's implemented)
|
||||
|
||||
$compensation = $this->getHealth() - $source->getFinalDamage() - 1;
|
||||
if($compensation < 0){
|
||||
$source->setModifier($compensation, EntityDamageEvent::MODIFIER_TOTEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
|
||||
parent::applyPostDamageEffects($source);
|
||||
$totemModifier = $source->getModifier(EntityDamageEvent::MODIFIER_TOTEM);
|
||||
if($totemModifier < 0){ //Totem prevented death
|
||||
$this->removeAllEffects();
|
||||
|
||||
$this->addEffect(new EffectInstance(Effect::getEffect(Effect::REGENERATION), 40 * 20, 1));
|
||||
$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->level->broadcastLevelEvent($this->add(0, $this->eyeHeight, 0), LevelEventPacket::EVENT_SOUND_TOTEM);
|
||||
|
||||
$hand = $this->inventory->getItemInHand();
|
||||
if($hand instanceof Totem){
|
||||
$hand->pop(); //Plugins could alter max stack size
|
||||
$this->inventory->setItemInHand($hand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDrops() : array{
|
||||
return array_filter(array_merge(
|
||||
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
|
||||
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : []
|
||||
), function(Item $item) : bool{ return !$item->hasEnchantment(Enchantment::VANISHING); });
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setInt("foodLevel", (int) $this->getFood(), true);
|
||||
@ -743,7 +787,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player){
|
||||
public function spawnTo(Player $player) : void{
|
||||
if($player !== $this){
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
@ -776,7 +820,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
public function close(){
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
if($this->inventory !== null){
|
||||
$this->inventory->removeAllViewers(true);
|
||||
@ -806,7 +850,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
* @param int $flagId
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setPlayerFlag(int $flagId, bool $value = true){
|
||||
public function setPlayerFlag(int $flagId, bool $value = true) : void{
|
||||
$this->setDataFlag(self::DATA_PLAYER_FLAGS, $flagId, $value, self::DATA_TYPE_BYTE);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\event\entity\EntityDeathEvent;
|
||||
use pocketmine\event\entity\EntityEffectAddEvent;
|
||||
use pocketmine\event\entity\EntityEffectRemoveEvent;
|
||||
use pocketmine\inventory\ArmorInventory;
|
||||
use pocketmine\inventory\ArmorInventoryEventProcessor;
|
||||
use pocketmine\item\Armor;
|
||||
use pocketmine\item\Consumable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
@ -72,10 +73,12 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
abstract public function getName() : string;
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->armorInventory = new ArmorInventory($this);
|
||||
//TODO: load/save armor inventory contents
|
||||
$this->armorInventory->setEventProcessor(new ArmorInventoryEventProcessor($this));
|
||||
|
||||
$health = $this->getMaxHealth();
|
||||
|
||||
@ -112,7 +115,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
}
|
||||
|
||||
protected function addAttributes(){
|
||||
protected function addAttributes() : void{
|
||||
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::HEALTH));
|
||||
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::FOLLOW_RANGE));
|
||||
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::KNOCKBACK_RESISTANCE));
|
||||
@ -121,7 +124,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::ABSORPTION));
|
||||
}
|
||||
|
||||
public function setHealth(float $amount){
|
||||
public function setHealth(float $amount) : void{
|
||||
$wasAlive = $this->isAlive();
|
||||
parent::setHealth($amount);
|
||||
$this->attributeMap->getAttribute(Attribute::HEALTH)->setValue(ceil($this->getHealth()), true);
|
||||
@ -134,7 +137,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
return (int) $this->attributeMap->getAttribute(Attribute::HEALTH)->getMaxValue();
|
||||
}
|
||||
|
||||
public function setMaxHealth(int $amount){
|
||||
public function setMaxHealth(int $amount) : void{
|
||||
$this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount);
|
||||
}
|
||||
|
||||
@ -142,11 +145,11 @@ abstract class Living extends Entity implements Damageable{
|
||||
return $this->attributeMap->getAttribute(Attribute::ABSORPTION)->getValue();
|
||||
}
|
||||
|
||||
public function setAbsorption(float $absorption){
|
||||
public function setAbsorption(float $absorption) : void{
|
||||
$this->attributeMap->getAttribute(Attribute::ABSORPTION)->setValue($absorption);
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setFloat("Health", $this->getHealth(), true);
|
||||
|
||||
@ -186,7 +189,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
/**
|
||||
* Removes all effects from the mob.
|
||||
*/
|
||||
public function removeAllEffects(){
|
||||
public function removeAllEffects() : void{
|
||||
foreach($this->effects as $effect){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
@ -197,11 +200,15 @@ abstract class Living extends Entity implements Damageable{
|
||||
*
|
||||
* @param int $effectId
|
||||
*/
|
||||
public function removeEffect(int $effectId){
|
||||
public function removeEffect(int $effectId) : void{
|
||||
if(isset($this->effects[$effectId])){
|
||||
$effect = $this->effects[$effectId];
|
||||
$hasExpired = $effect->hasExpired();
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityEffectRemoveEvent($this, $effect));
|
||||
if($ev->isCancelled()){
|
||||
if($hasExpired and !$ev->getEffect()->hasExpired()){ //altered duration of an expired effect to make it not get removed
|
||||
$this->sendEffectAdd($ev->getEffect(), true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -221,7 +228,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
*
|
||||
* @return EffectInstance|null
|
||||
*/
|
||||
public function getEffect(int $effectId){
|
||||
public function getEffect(int $effectId) : ?EffectInstance{
|
||||
return $this->effects[$effectId] ?? null;
|
||||
}
|
||||
|
||||
@ -292,7 +299,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
/**
|
||||
* Recalculates the mob's potion bubbles colour based on the active effects.
|
||||
*/
|
||||
protected function recalculateEffectColor(){
|
||||
protected function recalculateEffectColor() : void{
|
||||
/** @var Color[] $colors */
|
||||
$colors = [];
|
||||
$ambient = true;
|
||||
@ -323,7 +330,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* Sends the mob's potion effects to the specified player.
|
||||
* @param Player $player
|
||||
*/
|
||||
public function sendPotionEffects(Player $player){
|
||||
public function sendPotionEffects(Player $player) : void{
|
||||
foreach($this->effects as $effect){
|
||||
$pk = new MobEffectPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
@ -374,13 +381,13 @@ abstract class Living extends Entity implements Damageable{
|
||||
/**
|
||||
* Called when the entity jumps from the ground. This method adds upwards velocity to the entity.
|
||||
*/
|
||||
public function jump(){
|
||||
public function jump() : void{
|
||||
if($this->onGround){
|
||||
$this->motionY = $this->getJumpVelocity(); //Y motion should already be 0 if we're jumping from the ground.
|
||||
$this->motion->y = $this->getJumpVelocity(); //Y motion should already be 0 if we're jumping from the ground.
|
||||
}
|
||||
}
|
||||
|
||||
public function fall(float $fallDistance){
|
||||
public function fall(float $fallDistance) : void{
|
||||
$damage = ceil($fallDistance - 3 - ($this->hasEffect(Effect::JUMP) ? $this->getEffect(Effect::JUMP)->getEffectLevel() : 0));
|
||||
if($damage > 0){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
|
||||
@ -427,7 +434,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
return $this->armorInventory;
|
||||
}
|
||||
|
||||
public function setOnFire(int $seconds){
|
||||
public function setOnFire(int $seconds) : void{
|
||||
parent::setOnFire($seconds - (int) min($seconds, $seconds * $this->getHighestArmorEnchantmentLevel(Enchantment::FIRE_PROTECTION) * 0.15));
|
||||
}
|
||||
|
||||
@ -440,12 +447,12 @@ abstract class Living extends Entity implements Damageable{
|
||||
public function applyDamageModifiers(EntityDamageEvent $source) : void{
|
||||
if($source->canBeReducedByArmor()){
|
||||
//MCPE uses the same system as PC did pre-1.9
|
||||
$source->setDamage(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
|
||||
$source->setModifier(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
|
||||
}
|
||||
|
||||
$cause = $source->getCause();
|
||||
if($this->hasEffect(Effect::DAMAGE_RESISTANCE) and $cause !== EntityDamageEvent::CAUSE_VOID and $cause !== EntityDamageEvent::CAUSE_SUICIDE){
|
||||
$source->setDamage(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
|
||||
$source->setModifier(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
|
||||
}
|
||||
|
||||
$totalEpf = 0;
|
||||
@ -454,20 +461,21 @@ abstract class Living extends Entity implements Damageable{
|
||||
$totalEpf += $item->getEnchantmentProtectionFactor($source);
|
||||
}
|
||||
}
|
||||
$source->setDamage(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
|
||||
$source->setModifier(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
|
||||
|
||||
$source->setDamage(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
|
||||
$source->setModifier(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after EntityDamageEvent execution to apply post-hurt effects, such as reducing absorption or modifying
|
||||
* armour durability.
|
||||
* This will not be called by damage sources causing death.
|
||||
*
|
||||
* @param EntityDamageEvent $source
|
||||
*/
|
||||
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
|
||||
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getDamage(EntityDamageEvent::MODIFIER_ABSORPTION)));
|
||||
$this->damageArmor($source->getDamage(EntityDamageEvent::MODIFIER_BASE));
|
||||
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
|
||||
$this->damageArmor($source->getBaseDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,10 +500,10 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->armorInventory->setContents($armor);
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
if($this->attackTime > 0 or $this->noDamageTicks > 0){
|
||||
$lastCause = $this->getLastDamageCause();
|
||||
if($lastCause !== null and $lastCause->getDamage() >= $source->getDamage()){
|
||||
if($lastCause !== null and $lastCause->getBaseDamage() >= $source->getBaseDamage()){
|
||||
$source->setCancelled();
|
||||
}
|
||||
}
|
||||
@ -527,6 +535,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attackTime = 10; //0.5 seconds cooldown
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
if($source instanceof EntityDamageByChildEntityEvent){
|
||||
@ -540,55 +550,52 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
$deltaX = $this->x - $e->x;
|
||||
$deltaZ = $this->z - $e->z;
|
||||
$this->knockBack($e, $source->getDamage(), $deltaX, $deltaZ, $source->getKnockBack());
|
||||
$this->knockBack($e, $source->getBaseDamage(), $deltaX, $deltaZ, $source->getKnockBack());
|
||||
}
|
||||
}
|
||||
|
||||
$this->applyPostDamageEffects($source);
|
||||
|
||||
if($this->isAlive()){
|
||||
$this->applyPostDamageEffects($source);
|
||||
$this->doHitAnimation();
|
||||
}else{
|
||||
$this->startDeathAnimation();
|
||||
}
|
||||
|
||||
$this->attackTime = 10; //0.5 seconds cooldown
|
||||
}
|
||||
|
||||
protected function doHitAnimation() : void{
|
||||
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION);
|
||||
}
|
||||
|
||||
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4){
|
||||
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4) : void{
|
||||
$f = sqrt($x * $x + $z * $z);
|
||||
if($f <= 0){
|
||||
return;
|
||||
}
|
||||
if(mt_rand() / mt_getrandmax() > $this->getAttributeMap()->getAttribute(Attribute::KNOCKBACK_RESISTANCE)->getValue()){
|
||||
$f = 1 / $f;
|
||||
|
||||
$f = 1 / $f;
|
||||
$motion = clone $this->motion;
|
||||
|
||||
$motion = new Vector3($this->motionX, $this->motionY, $this->motionZ);
|
||||
$motion->x /= 2;
|
||||
$motion->y /= 2;
|
||||
$motion->z /= 2;
|
||||
$motion->x += $x * $f * $base;
|
||||
$motion->y += $base;
|
||||
$motion->z += $z * $f * $base;
|
||||
|
||||
$motion->x /= 2;
|
||||
$motion->y /= 2;
|
||||
$motion->z /= 2;
|
||||
$motion->x += $x * $f * $base;
|
||||
$motion->y += $base;
|
||||
$motion->z += $z * $f * $base;
|
||||
if($motion->y > $base){
|
||||
$motion->y = $base;
|
||||
}
|
||||
|
||||
if($motion->y > $base){
|
||||
$motion->y = $base;
|
||||
$this->setMotion($motion);
|
||||
}
|
||||
|
||||
$this->setMotion($motion);
|
||||
}
|
||||
|
||||
public function kill(){
|
||||
public function kill() : void{
|
||||
parent::kill();
|
||||
$this->onDeath();
|
||||
$this->startDeathAnimation();
|
||||
}
|
||||
|
||||
protected function onDeath(){
|
||||
protected function onDeath() : void{
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops()));
|
||||
foreach($ev->getDrops() as $item){
|
||||
$this->getLevel()->dropItem($this, $item);
|
||||
@ -649,7 +656,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function doEffectsTick(int $tickDiff = 1){
|
||||
protected function doEffectsTick(int $tickDiff = 1) : void{
|
||||
foreach($this->effects as $instance){
|
||||
$type = $instance->getType();
|
||||
if($type->canTick($instance)){
|
||||
@ -666,7 +673,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* Ticks the entity's air supply when it cannot breathe.
|
||||
* @param int $tickDiff
|
||||
*/
|
||||
protected function doAirSupplyTick(int $tickDiff){
|
||||
protected function doAirSupplyTick(int $tickDiff) : void{
|
||||
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or
|
||||
lcg_value() <= (1 / ($respirationLevel + 1))
|
||||
){
|
||||
@ -686,7 +693,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* @return bool
|
||||
*/
|
||||
public function canBreathe() : bool{
|
||||
return $this->hasEffect(Effect::WATER_BREATHING) or !$this->isInsideOfWater();
|
||||
return $this->hasEffect(Effect::WATER_BREATHING) or !$this->isUnderwater();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -703,7 +710,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setBreathing(bool $value = true){
|
||||
public function setBreathing(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_BREATHING, $value);
|
||||
}
|
||||
|
||||
@ -721,7 +728,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* Sets the number of air ticks left in the entity's air supply.
|
||||
* @param int $ticks
|
||||
*/
|
||||
public function setAirSupplyTicks(int $ticks){
|
||||
public function setAirSupplyTicks(int $ticks) : void{
|
||||
$this->propertyManager->setShort(self::DATA_AIR, $ticks);
|
||||
}
|
||||
|
||||
@ -737,7 +744,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* Sets the maximum amount of air ticks the air supply can hold.
|
||||
* @param int $ticks
|
||||
*/
|
||||
public function setMaxAirSupplyTicks(int $ticks){
|
||||
public function setMaxAirSupplyTicks(int $ticks) : void{
|
||||
$this->propertyManager->setShort(self::DATA_MAX_AIR, $ticks);
|
||||
}
|
||||
|
||||
@ -745,7 +752,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* Called when the entity's air supply ticks reaches -20 or lower. The entity will usually take damage at this point
|
||||
* and then the supply is reset to 0, so this method will be called roughly every second.
|
||||
*/
|
||||
public function onAirExpired(){
|
||||
public function onAirExpired() : void{
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2);
|
||||
$this->attack($ev);
|
||||
}
|
||||
@ -815,13 +822,10 @@ abstract class Living extends Entity implements Damageable{
|
||||
*
|
||||
* @return Block|null
|
||||
*/
|
||||
public function getTargetBlock(int $maxDistance, array $transparent = []){
|
||||
try{
|
||||
$block = $this->getLineOfSight($maxDistance, 1, $transparent)[0];
|
||||
if($block instanceof Block){
|
||||
return $block;
|
||||
}
|
||||
}catch(\ArrayOutOfBoundsException $e){
|
||||
public function getTargetBlock(int $maxDistance, array $transparent = []) : ?Block{
|
||||
$line = $this->getLineOfSight($maxDistance, 1, $transparent);
|
||||
if(!empty($line)){
|
||||
return array_shift($line);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -852,7 +856,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->armorInventory->sendContents($player);
|
||||
}
|
||||
|
||||
public function close(){
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
if($this->armorInventory !== null){
|
||||
$this->armorInventory->removeAllViewers(true);
|
||||
|
@ -99,5 +99,4 @@ class Skin{
|
||||
$this->geometryData = (string) json_encode(json_decode($this->geometryData));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class Squid extends WaterAnimal{
|
||||
|
||||
private $switchDirectionTicker = 0;
|
||||
|
||||
public function initEntity(){
|
||||
public function initEntity() : void{
|
||||
$this->setMaxHealth(10);
|
||||
parent::initEntity();
|
||||
}
|
||||
@ -51,7 +51,7 @@ class Squid extends WaterAnimal{
|
||||
return "Squid";
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
parent::attack($source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
@ -93,30 +93,28 @@ class Squid extends WaterAnimal{
|
||||
$this->swimDirection->y = -0.5;
|
||||
}
|
||||
|
||||
$inWater = $this->isInsideOfWater();
|
||||
$inWater = $this->isUnderwater();
|
||||
if(!$inWater){
|
||||
$this->swimDirection = null;
|
||||
}elseif($this->swimDirection !== null){
|
||||
if($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()){
|
||||
$this->motionX = $this->swimDirection->x * $this->swimSpeed;
|
||||
$this->motionY = $this->swimDirection->y * $this->swimSpeed;
|
||||
$this->motionZ = $this->swimDirection->z * $this->swimSpeed;
|
||||
if($this->motion->lengthSquared() <= $this->swimDirection->lengthSquared()){
|
||||
$this->motion = $this->swimDirection->multiply($this->swimSpeed);
|
||||
}
|
||||
}else{
|
||||
$this->swimDirection = $this->generateRandomDirection();
|
||||
$this->swimSpeed = mt_rand(50, 100) / 2000;
|
||||
}
|
||||
|
||||
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
|
||||
$this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI);
|
||||
$this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI);
|
||||
$f = sqrt(($this->motion->x ** 2) + ($this->motion->z ** 2));
|
||||
$this->yaw = (-atan2($this->motion->x, $this->motion->z) * 180 / M_PI);
|
||||
$this->pitch = (-atan2($f, $this->motion->y) * 180 / M_PI);
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function applyGravity(){
|
||||
if(!$this->isInsideOfWater()){
|
||||
protected function applyGravity() : void{
|
||||
if(!$this->isUnderwater()){
|
||||
parent::applyGravity();
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class Villager extends Creature implements NPC, Ageable{
|
||||
return "Villager";
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
/** @var int $profession */
|
||||
@ -52,7 +52,7 @@ class Villager extends Creature implements NPC, Ageable{
|
||||
$this->setProfession($profession);
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setInt("Profession", $this->getProfession());
|
||||
}
|
||||
@ -62,7 +62,7 @@ class Villager extends Creature implements NPC, Ageable{
|
||||
*
|
||||
* @param int $profession
|
||||
*/
|
||||
public function setProfession(int $profession){
|
||||
public function setProfession(int $profession) : void{
|
||||
$this->propertyManager->setInt(self::DATA_VARIANT, $profession);
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,10 @@ abstract class WaterAnimal extends Creature implements Ageable{
|
||||
}
|
||||
|
||||
public function canBreathe() : bool{
|
||||
return $this->isInsideOfWater();
|
||||
return $this->isUnderwater();
|
||||
}
|
||||
|
||||
public function onAirExpired(){
|
||||
public function onAirExpired() : void{
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2);
|
||||
$this->attack($ev);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class ExperienceOrb extends Entity{
|
||||
*/
|
||||
protected $targetPlayerRuntimeId = null;
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->age = $this->namedtag->getShort("Age", 0);
|
||||
@ -115,7 +115,7 @@ class ExperienceOrb extends Entity{
|
||||
$this->setXpValue($value);
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setShort("Age", $this->age);
|
||||
@ -165,7 +165,7 @@ class ExperienceOrb extends Entity{
|
||||
}
|
||||
|
||||
$currentTarget = $this->getTargetPlayer();
|
||||
if($currentTarget !== null and $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2){
|
||||
if($currentTarget !== null and (!$currentTarget->isAlive() or $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2)){
|
||||
$currentTarget = null;
|
||||
}
|
||||
|
||||
@ -192,9 +192,9 @@ class ExperienceOrb extends Entity{
|
||||
$oneMinusDistance = (1 - $distance) ** 2;
|
||||
|
||||
if($oneMinusDistance > 0){
|
||||
$this->motionX += $vector->x / $distance * $oneMinusDistance * 0.2;
|
||||
$this->motionY += $vector->y / $distance * $oneMinusDistance * 0.2;
|
||||
$this->motionZ += $vector->z / $distance * $oneMinusDistance * 0.2;
|
||||
$this->motion->x += $vector->x / $distance * $oneMinusDistance * 0.2;
|
||||
$this->motion->y += $vector->y / $distance * $oneMinusDistance * 0.2;
|
||||
$this->motion->z += $vector->z / $distance * $oneMinusDistance * 0.2;
|
||||
}
|
||||
|
||||
if($currentTarget->canPickupXp() and $this->boundingBox->intersectsWith($currentTarget->getBoundingBox())){
|
||||
@ -210,7 +210,7 @@ class ExperienceOrb extends Entity{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function tryChangeMovement(){
|
||||
protected function tryChangeMovement() : void{
|
||||
$this->checkObstruction($this->x, $this->y, $this->z);
|
||||
parent::tryChangeMovement();
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class FallingBlock extends Entity{
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$blockId = 0;
|
||||
@ -64,8 +64,7 @@ class FallingBlock extends Entity{
|
||||
}
|
||||
|
||||
if($blockId === 0){
|
||||
$this->close();
|
||||
return;
|
||||
throw new \UnexpectedValueException("Invalid " . get_class($this) . " entity: block ID is 0 or missing");
|
||||
}
|
||||
|
||||
$damage = $this->namedtag->getByte("Data", 0);
|
||||
@ -83,7 +82,7 @@ class FallingBlock extends Entity{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
|
||||
parent::attack($source);
|
||||
}
|
||||
@ -126,15 +125,15 @@ class FallingBlock extends Entity{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
public function getBlock(){
|
||||
public function getBlock() : int{
|
||||
return $this->block->getId();
|
||||
}
|
||||
|
||||
public function getDamage(){
|
||||
public function getDamage() : int{
|
||||
return $this->block->getDamage();
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
$this->namedtag->setInt("TileID", $this->block->getId(), true);
|
||||
$this->namedtag->setByte("Data", $this->block->getDamage());
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\entity\object;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\ItemDespawnEvent;
|
||||
use pocketmine\event\entity\ItemSpawnEvent;
|
||||
use pocketmine\event\inventory\InventoryPickupItemEvent;
|
||||
@ -54,7 +53,7 @@ class ItemEntity extends Entity{
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->setMaxHealth(5);
|
||||
@ -67,8 +66,7 @@ class ItemEntity extends Entity{
|
||||
|
||||
$itemTag = $this->namedtag->getCompoundTag("Item");
|
||||
if($itemTag === null){
|
||||
$this->close();
|
||||
return;
|
||||
throw new \UnexpectedValueException("Invalid " . get_class($this) . " entity: expected \"Item\" NBT tag not found");
|
||||
}
|
||||
|
||||
$this->item = Item::nbtDeserialize($itemTag);
|
||||
@ -77,18 +75,6 @@ class ItemEntity extends Entity{
|
||||
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
if(
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_VOID or
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK or
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_LAVA or
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_ENTITY_EXPLOSION or
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_BLOCK_EXPLOSION
|
||||
){
|
||||
parent::attack($source);
|
||||
}
|
||||
}
|
||||
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
if($this->closed){
|
||||
return false;
|
||||
@ -119,7 +105,7 @@ class ItemEntity extends Entity{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function tryChangeMovement(){
|
||||
protected function tryChangeMovement() : void{
|
||||
$this->checkObstruction($this->x, $this->y, $this->z);
|
||||
parent::tryChangeMovement();
|
||||
}
|
||||
@ -128,7 +114,7 @@ class ItemEntity extends Entity{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setTag($this->item->nbtSerialize(-1, "Item"));
|
||||
$this->namedtag->setShort("Health", (int) $this->getHealth());
|
||||
@ -167,7 +153,7 @@ class ItemEntity extends Entity{
|
||||
/**
|
||||
* @param int $delay
|
||||
*/
|
||||
public function setPickupDelay(int $delay){
|
||||
public function setPickupDelay(int $delay) : void{
|
||||
$this->pickupDelay = $delay;
|
||||
}
|
||||
|
||||
@ -181,7 +167,7 @@ class ItemEntity extends Entity{
|
||||
/**
|
||||
* @param string $owner
|
||||
*/
|
||||
public function setOwner(string $owner){
|
||||
public function setOwner(string $owner) : void{
|
||||
$this->owner = $owner;
|
||||
}
|
||||
|
||||
@ -195,7 +181,7 @@ class ItemEntity extends Entity{
|
||||
/**
|
||||
* @param string $thrower
|
||||
*/
|
||||
public function setThrower(string $thrower){
|
||||
public function setThrower(string $thrower) : void{
|
||||
$this->thrower = $thrower;
|
||||
}
|
||||
|
||||
@ -210,7 +196,7 @@ class ItemEntity extends Entity{
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
public function onCollideWithPlayer(Player $player){
|
||||
public function onCollideWithPlayer(Player $player) : void{
|
||||
if($this->getPickupDelay() > 0){
|
||||
return;
|
||||
}
|
||||
|
@ -70,13 +70,13 @@ class Painting extends Entity{
|
||||
parent::__construct($level, $nbt);
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
$this->setMaxHealth(1);
|
||||
$this->setHealth(1);
|
||||
parent::initEntity();
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setInt("TileX", (int) $this->blockIn->x);
|
||||
$this->namedtag->setInt("TileY", (int) $this->blockIn->y);
|
||||
@ -86,7 +86,7 @@ class Painting extends Entity{
|
||||
$this->namedtag->setByte("Direction", (int) $this->direction); //Save both for full compatibility
|
||||
}
|
||||
|
||||
public function kill(){
|
||||
public function kill() : void{
|
||||
parent::kill();
|
||||
|
||||
$drops = true;
|
||||
@ -138,7 +138,7 @@ class Painting extends Entity{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function updateMovement(bool $teleport = false){
|
||||
protected function updateMovement(bool $teleport = false) : void{
|
||||
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ class Painting extends Entity{
|
||||
return PaintingMotive::getMotiveByName($this->motive);
|
||||
}
|
||||
|
||||
public function getDirection() : int{
|
||||
public function getDirection() : ?int{
|
||||
return $this->direction;
|
||||
}
|
||||
|
||||
|
@ -47,13 +47,13 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
public $canCollide = false;
|
||||
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
|
||||
parent::attack($source);
|
||||
}
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
if($this->namedtag->hasTag("Fuse", ShortTag::class)){
|
||||
@ -73,7 +73,7 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setShort("Fuse", $this->fuse, true); //older versions incorrectly saved this as a byte
|
||||
}
|
||||
@ -101,7 +101,7 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
return $hasUpdate or $this->fuse >= 0;
|
||||
}
|
||||
|
||||
public function explode(){
|
||||
public function explode() : void{
|
||||
$this->server->getPluginManager()->callEvent($ev = new ExplosionPrimeEvent($this, 4));
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
|
@ -48,7 +48,7 @@ class Arrow extends Projectile{
|
||||
|
||||
protected $damage = 2;
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt, Entity $shootingEntity = null, bool $critical = false){
|
||||
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){
|
||||
parent::__construct($level, $nbt, $shootingEntity);
|
||||
$this->setCritical($critical);
|
||||
}
|
||||
@ -57,7 +57,7 @@ class Arrow extends Projectile{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_CRITICAL);
|
||||
}
|
||||
|
||||
public function setCritical(bool $value = true){
|
||||
public function setCritical(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_CRITICAL, $value);
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ class Arrow extends Projectile{
|
||||
$this->broadcastEntityEvent(EntityEventPacket::ARROW_SHAKE, 7); //7 ticks
|
||||
}
|
||||
|
||||
public function onCollideWithPlayer(Player $player){
|
||||
public function onCollideWithPlayer(Player $player) : void{
|
||||
if($this->blockHit === null){
|
||||
return;
|
||||
}
|
||||
|
@ -55,20 +55,20 @@ abstract class Projectile extends Entity{
|
||||
/** @var int|null */
|
||||
protected $blockHitData;
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt, Entity $shootingEntity = null){
|
||||
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null){
|
||||
parent::__construct($level, $nbt);
|
||||
if($shootingEntity !== null){
|
||||
$this->setOwningEntity($shootingEntity);
|
||||
}
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source){
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
|
||||
parent::attack($source);
|
||||
}
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->setMaxHealth(1);
|
||||
@ -117,10 +117,10 @@ abstract class Projectile extends Entity{
|
||||
* @return int
|
||||
*/
|
||||
public function getResultDamage() : int{
|
||||
return (int) ceil(sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2) * $this->damage);
|
||||
return (int) ceil($this->motion->length() * $this->damage);
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setShort("Age", $this->age);
|
||||
@ -159,7 +159,7 @@ abstract class Projectile extends Entity{
|
||||
Timings::$entityMoveTimer->startTiming();
|
||||
|
||||
$start = $this->asVector3();
|
||||
$end = $start->add($this->motionX, $this->motionY, $this->motionZ);
|
||||
$end = $start->add($this->motion);
|
||||
|
||||
$blockHit = null;
|
||||
$entityHit = null;
|
||||
@ -185,7 +185,7 @@ abstract class Projectile extends Entity{
|
||||
continue;
|
||||
}
|
||||
|
||||
$entityBB = $entity->boundingBox->grow(0.3, 0.3, 0.3);
|
||||
$entityBB = $entity->boundingBox->expandedCopy(0.3, 0.3, 0.3);
|
||||
$entityHitResult = $entityBB->calculateIntercept($start, $end);
|
||||
|
||||
if($entityHitResult === null){
|
||||
@ -215,7 +215,7 @@ abstract class Projectile extends Entity{
|
||||
}elseif($blockHit !== null){
|
||||
$ev = new ProjectileHitBlockEvent($this, $hitResult, $blockHit);
|
||||
}else{
|
||||
\assert(false, "unknown hit type");
|
||||
assert(false, "unknown hit type");
|
||||
}
|
||||
|
||||
if($ev !== null){
|
||||
@ -230,15 +230,15 @@ abstract class Projectile extends Entity{
|
||||
}
|
||||
|
||||
$this->isCollided = $this->onGround = true;
|
||||
$this->motionX = $this->motionY = $this->motionZ = 0;
|
||||
$this->motion->x = $this->motion->y = $this->motion->z = 0;
|
||||
}else{
|
||||
$this->isCollided = $this->onGround = false;
|
||||
$this->blockHit = $this->blockHitId = $this->blockHitData = null;
|
||||
|
||||
//recompute angles...
|
||||
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
|
||||
$this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI);
|
||||
$this->pitch = (atan2($this->motionY, $f) * 180 / M_PI);
|
||||
$f = sqrt(($this->motion->x ** 2) + ($this->motion->z ** 2));
|
||||
$this->yaw = (atan2($this->motion->x, $this->motion->z) * 180 / M_PI);
|
||||
$this->pitch = (atan2($this->motion->y, $f) * 180 / M_PI);
|
||||
}
|
||||
|
||||
$this->checkChunks();
|
||||
|
@ -42,13 +42,13 @@ class SplashPotion extends Throwable{
|
||||
protected $gravity = 0.05;
|
||||
protected $drag = 0.01;
|
||||
|
||||
protected function initEntity(){
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->setPotionId($this->namedtag->getShort("PotionId", 0));
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setShort("PotionId", $this->getPotionId());
|
||||
}
|
||||
@ -81,7 +81,7 @@ class SplashPotion extends Throwable{
|
||||
|
||||
if($hasEffects){
|
||||
if(!$this->willLinger()){
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->grow(4.125, 2.125, 4.125), $this) as $entity){
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(4.125, 2.125, 4.125), $this) as $entity){
|
||||
if($entity instanceof Living){
|
||||
$distanceSquared = $entity->distanceSquared($this);
|
||||
if($distanceSquared > 16){ //4 blocks
|
||||
|
@ -59,7 +59,7 @@ abstract class Event{
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function setCancelled(bool $value = true){
|
||||
public function setCancelled(bool $value = true) : void{
|
||||
if(!($this instanceof Cancellable)){
|
||||
throw new \BadMethodCallException("Event is not Cancellable");
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ abstract class EventPriority{
|
||||
public static function fromString(string $name) : int{
|
||||
$name = strtoupper($name);
|
||||
$const = self::class . "::" . $name;
|
||||
if($name !== "ALL" and \defined($const)){
|
||||
return \constant($const);
|
||||
if($name !== "ALL" and defined($const)){
|
||||
return constant($const);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Unable to resolve priority \"$name\"");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user