mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 11:16:57 +00:00
Compare commits
517 Commits
Author | SHA1 | Date | |
---|---|---|---|
d33acc4fd0 | |||
f7de6eb59f | |||
75a0627bf2 | |||
8752e363c9 | |||
9ed1b5ca7f | |||
1cbb31f1db | |||
1393b4c4e2 | |||
2921c86b3c | |||
9c3a929b65 | |||
9abaa42cd7 | |||
77b9feb3c0 | |||
e0e2e1775f | |||
d2d65ce6cc | |||
ff2e982f22 | |||
daf56e990b | |||
3f5e83a322 | |||
5ecc5ed7e0 | |||
cd80ae00d4 | |||
beb5d72299 | |||
0eef634aab | |||
0ea166a551 | |||
6417cff618 | |||
a71af952ba | |||
93dd05a03e | |||
98f903783c | |||
5d47ea4337 | |||
c242d6213a | |||
4ad1093fd7 | |||
fc0782df02 | |||
bfaa224f6b | |||
de88f0fce1 | |||
9b078854c4 | |||
42f8e061a5 | |||
1455c38dbe | |||
75df6973df | |||
4763360e9e | |||
0299191e64 | |||
2664a1b4d8 | |||
4249c00c3e | |||
517c4e5143 | |||
69c343bb9b | |||
70df1579a8 | |||
ea9f9aa250 | |||
34a899e28b | |||
b80868040e | |||
a7f1181335 | |||
bf8a8b386e | |||
4b518f2a58 | |||
60b1f0a6e9 | |||
5934399a0d | |||
b42132a7c3 | |||
c05697f506 | |||
d4fe1b8ece | |||
9b2653fb6f | |||
ed88684e71 | |||
cbb9c4f298 | |||
660d42e8d1 | |||
dbeceb02f9 | |||
fd77dd0066 | |||
87ce87112b | |||
1d71f5edb3 | |||
0f620157e8 | |||
2323601f98 | |||
d34b94302f | |||
ec4c61e113 | |||
231e491bb9 | |||
69cdc6f13a | |||
dfeb62491a | |||
178eedb536 | |||
4975da2aae | |||
5946ec8819 | |||
abf0dee426 | |||
30f5a8fac6 | |||
f704061618 | |||
23dc6e09d8 | |||
15b7fc978e | |||
bb396174ba | |||
dcef3cba21 | |||
5f8a9f8747 | |||
84e41e6967 | |||
5e0e0daf7d | |||
a95694ed06 | |||
762405d16a | |||
e3f46987f5 | |||
e4223bb7dc | |||
f091446ec7 | |||
b0f891081c | |||
acd7c9b336 | |||
75482124f2 | |||
288599cbe7 | |||
aa7206126a | |||
1a6db1c7ce | |||
f1c071ce7f | |||
e2f46a4358 | |||
36c0c350a7 | |||
4c08a05fae | |||
6295ef8a81 | |||
05dba61a69 | |||
b473ffdedc | |||
60dddcd12a | |||
c010ef45ed | |||
93c26a0b0c | |||
08ec021f78 | |||
545ec9c881 | |||
b0060caaf7 | |||
c90d1faa81 | |||
d5a1961e6b | |||
449dda83fb | |||
6bc79149c3 | |||
cdf7e28251 | |||
a02f422d85 | |||
f8bfbc107d | |||
554c029fbd | |||
e018311e73 | |||
de50f02076 | |||
71d02e5870 | |||
46d9475568 | |||
788b278fc3 | |||
2e4143f57e | |||
d312aef1ac | |||
200de3fe84 | |||
f560a6efea | |||
7ecd7fd13f | |||
5284ad0346 | |||
b893645a81 | |||
b19b3134ad | |||
7cf36f460b | |||
243f86b0a0 | |||
9156cbc269 | |||
a5f776af2f | |||
43fe6a1934 | |||
342a74ffcb | |||
3d2701e775 | |||
2183bf875c | |||
8cc2a4ce5d | |||
e26af3fa1b | |||
1634dd62e3 | |||
755db3dac8 | |||
3dabf90b0e | |||
f61e14e341 | |||
7b24fbc8db | |||
0543c17849 | |||
c4f3426bae | |||
046c39b02e | |||
87b471ce0f | |||
055ba6aa7c | |||
5c3eed40b3 | |||
3e5237b6e0 | |||
af1227f154 | |||
d9a867016c | |||
a50a863ab7 | |||
9caf62778c | |||
d257d36e55 | |||
1b03168b88 | |||
6b9fee05d6 | |||
44d8a5528e | |||
45a18ffe1e | |||
f0182c9996 | |||
265b61b3e6 | |||
2d88058710 | |||
ab48d85c35 | |||
cf43f479df | |||
c143834632 | |||
d9b7a28747 | |||
31ceafa111 | |||
a0eb6e23e5 | |||
694d7d4e20 | |||
2da2fdd6d4 | |||
0aa30295af | |||
c1c56f29bb | |||
9b820a0849 | |||
c6a4bc4bf7 | |||
3128449033 | |||
a60154e0b7 | |||
4cbbf2e91c | |||
b0624aff9f | |||
c38e2c5ccb | |||
02ef0bfbb4 | |||
a714612453 | |||
4835537886 | |||
4a6841a5a4 | |||
f61e099828 | |||
925da62afa | |||
09985c5763 | |||
447b9562bb | |||
d1ee9eb960 | |||
196cf8a68d | |||
cac21c2caf | |||
6dd2597934 | |||
5e68858ebf | |||
45c9caa38c | |||
b35759cc25 | |||
2a40c0d82c | |||
8ac1b18b17 | |||
4aef9919dc | |||
43426a4c5c | |||
3028832cd3 | |||
9f8a2dc61a | |||
d9ebe6f321 | |||
cb1eb1ee09 | |||
d563b9e31b | |||
7c44eea625 | |||
d749f19c73 | |||
41fd03f329 | |||
646c8970b8 | |||
58067b2ad1 | |||
0c9946621c | |||
f1cd6940f9 | |||
af5637e050 | |||
4221e274d6 | |||
a524b0e447 | |||
88a5e92c20 | |||
b876ae4ef8 | |||
1983964f9e | |||
c4c55a45c9 | |||
c5cd813b76 | |||
bc2dff3f51 | |||
839d5eab7b | |||
cd506bb443 | |||
4c8ffce86f | |||
78923177f9 | |||
df6bb2ea0e | |||
b7062e7bff | |||
ba68192206 | |||
6579930638 | |||
a0ab996b9f | |||
97980d4516 | |||
b261129788 | |||
d9220395d1 | |||
4f2f373a24 | |||
2858db430e | |||
de6d62aba2 | |||
32836cbfb8 | |||
8316e00927 | |||
6f694b0801 | |||
fd459cda54 | |||
a66dd4a7d9 | |||
3617eba4a3 | |||
e79cc98883 | |||
a3552875cb | |||
d259b2c9ee | |||
10fa74b417 | |||
ab5aec6c30 | |||
0e508876d2 | |||
50b89c30f8 | |||
17ceb27af4 | |||
adbd1c7bed | |||
495fdbd19f | |||
620784e4e7 | |||
cf20e626e2 | |||
d75c830a7e | |||
1dd6591ac1 | |||
6efef3bbc7 | |||
b1e0f82cbf | |||
c065cfbeda | |||
722924a779 | |||
60e1b29462 | |||
0171095036 | |||
5b511f6d06 | |||
426dee04a6 | |||
9d8898a4ed | |||
3bb22f9778 | |||
bb1944ca40 | |||
d1a20ecb4a | |||
16c636df83 | |||
f6a8ec83a1 | |||
28137efb53 | |||
e597067a92 | |||
06f00020cd | |||
7b0836d399 | |||
cea146e335 | |||
5eeaeb6c3e | |||
2712287995 | |||
8db1ccc1ae | |||
5d56030afa | |||
6be5e75263 | |||
d9c251b613 | |||
8085b81f5c | |||
6b44f99dfb | |||
33d3fff3c5 | |||
7c092b93b4 | |||
aa05650994 | |||
758d9b9784 | |||
24a6bf7365 | |||
9a5d51fd3d | |||
fa9ea6a7d7 | |||
6a7f39978b | |||
c52e1ea9f9 | |||
5e94d20d79 | |||
a0bb747d6d | |||
4bc0d850b1 | |||
ad9df6764d | |||
97583c8b04 | |||
107192c753 | |||
6309a242dc | |||
870f9abc20 | |||
0e2bbc44db | |||
e58d015f14 | |||
d9768abe47 | |||
e9b84ecc8b | |||
0d65f9c4b8 | |||
c83d12790e | |||
5863d4c066 | |||
22077c1fdd | |||
7d54d18732 | |||
bfbc845efa | |||
f33c19e77a | |||
2ff4228fb7 | |||
06c4f31db7 | |||
09dea035d4 | |||
a9fc67663c | |||
519659fd2b | |||
6c70e84fa2 | |||
7d0e631a75 | |||
5134c0cf5a | |||
65b751d080 | |||
27effff403 | |||
a940cc5b5e | |||
15e654131c | |||
6e6cda91ce | |||
0aa63d269a | |||
53a76c0d14 | |||
69500fe183 | |||
191f0038b8 | |||
99d6aa92cb | |||
90d01f5ed2 | |||
5af4dd20df | |||
c7d58db7eb | |||
a3b78236eb | |||
b70905b287 | |||
d8e27e6081 | |||
7a48c0b23d | |||
14a2ffa51b | |||
c447d51e3f | |||
557fd34754 | |||
32077d96b4 | |||
9f4722f537 | |||
cb04f287eb | |||
f649ef5195 | |||
b615cad22d | |||
b93e219231 | |||
a4a9309193 | |||
e621cde8f1 | |||
56ee957fda | |||
1193efd69e | |||
2738e38aee | |||
f466fd5568 | |||
72d447276b | |||
d5a5209334 | |||
3a85e6cab9 | |||
d0aff2ecbd | |||
bca493a682 | |||
ba12dfafd6 | |||
e09087de26 | |||
888dba704b | |||
511249c562 | |||
17f1bf5512 | |||
5179bb1d30 | |||
6bff840293 | |||
08897c6941 | |||
05d9bb45d0 | |||
dfe2aa9c67 | |||
4006be35d9 | |||
e5cda34548 | |||
032b20f659 | |||
fe6d546190 | |||
c7af1cf785 | |||
22fcfffa53 | |||
7dd53f2397 | |||
298259b473 | |||
c123f2d10b | |||
3e6f70ddf6 | |||
bea634a9b7 | |||
8daf3dc8b4 | |||
4cc7573a64 | |||
9d80802e53 | |||
ec1e257e21 | |||
d419d4308f | |||
9ca38ba868 | |||
424c50e1e9 | |||
566f3c6262 | |||
0d05dcec08 | |||
986077e03c | |||
ddcb2f002a | |||
c496480d2b | |||
6fce2b3349 | |||
64ed8adefc | |||
2eda8cfad3 | |||
91be5aba0c | |||
5df601c817 | |||
21e7b5ea43 | |||
8304675af7 | |||
1a47735d84 | |||
0cdf4d0c55 | |||
e6e28b74b5 | |||
ebffff0caa | |||
0dc4bd36e1 | |||
9d17c9a09d | |||
72f46b4631 | |||
3892f2f404 | |||
bfa415e108 | |||
b66095cb36 | |||
0336ae8229 | |||
4a1d67cb91 | |||
b4694092b7 | |||
4b3e17e681 | |||
d99ee515c6 | |||
5424644ca1 | |||
aa7c4bc64d | |||
df8e10cad9 | |||
d98a6e566c | |||
ade2be9eee | |||
39ed6a7cdf | |||
bec5aaa54b | |||
cf29ab1f17 | |||
c5c5a53a13 | |||
63a65680ac | |||
47cd6fe105 | |||
f582b5a3db | |||
7f0fa2ac3d | |||
f3b2bcfd13 | |||
c947909c2e | |||
09dadc72bc | |||
ca541032ae | |||
bcf9915082 | |||
8d6dc4e188 | |||
12d8d925c8 | |||
f3f229ef7c | |||
6614183c7f | |||
7ebf3c7bf4 | |||
334caaaa34 | |||
6fcaef068f | |||
c09ad9263b | |||
4cc2f037a9 | |||
99045fe21a | |||
bda271ca63 | |||
b3f2396ea5 | |||
ab0510cb37 | |||
9a423be1db | |||
08be51dc23 | |||
94352782d5 | |||
8fae79f85b | |||
9a2845640b | |||
580f71d496 | |||
24f11779f2 | |||
706c620d04 | |||
951870e6ec | |||
1405099768 | |||
1464487945 | |||
40c28f4d26 | |||
90bf94f8f7 | |||
522ef042a7 | |||
76ee6bc298 | |||
04f20c703c | |||
efe4b0cd3a | |||
527d8e9374 | |||
c1c70a8a98 | |||
b7f15b6574 | |||
6ab2fa84da | |||
b480c63060 | |||
f6b54f5116 | |||
89bfc380e3 | |||
40030e9800 | |||
ad1cf38c21 | |||
5d769147ca | |||
6f00a30ad7 | |||
b4bf6901e3 | |||
71c3c34976 | |||
16c253d7a9 | |||
7efe767f1f | |||
2e18fe710c | |||
878dd3b842 | |||
478a131aa5 | |||
53068caf3c | |||
fe7ad7a5b3 | |||
24f749a933 | |||
af80aefd45 | |||
1d5c741f28 | |||
3a373b880d | |||
1b7cd156aa | |||
7a164a8254 | |||
066c990301 | |||
287ff8d7bf | |||
1087212d75 | |||
0c350f2f57 | |||
bfcef2ab6b | |||
2994d0f3ae | |||
57cc0ebe75 | |||
7554d9a370 | |||
32574118ea | |||
5a3135659b | |||
b90d7d1839 | |||
670b940837 | |||
6cad7be3ef | |||
28a72a93b4 | |||
0f0d12bebc | |||
dfc11abf2d | |||
17eef9f902 | |||
b04319a4ab | |||
0afbf6c547 | |||
ec2cca04a7 | |||
272b76d24c | |||
8c672cb7c8 | |||
4d9368f205 | |||
97c267c70c | |||
85a3c0e7dc | |||
2f70a1eefb | |||
7ba6e92b6c | |||
47c862bc38 | |||
860c20109b | |||
1c0b49343c | |||
814a949580 | |||
b393f5f17e | |||
f1970492c1 | |||
4c9ca53b32 | |||
390db976e5 | |||
98ac534820 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,9 +7,6 @@
|
||||
[submodule "tests/plugins/PocketMine-DevTools"]
|
||||
path = tests/plugins/PocketMine-DevTools
|
||||
url = https://github.com/pmmp/PocketMine-DevTools.git
|
||||
[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
|
||||
|
@ -27,9 +27,11 @@
|
||||
"pocketmine/raklib": "^0.12.0",
|
||||
"pocketmine/spl": "^0.3.0",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/nbt": "^0.2.0",
|
||||
"pocketmine/nbt": "^0.2.1",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0"
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"daverandom/callback-validator": "dev-master",
|
||||
"adhocore/json-comment": "^0.0.7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
140
composer.lock
generated
140
composer.lock
generated
@ -4,20 +4,104 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2670b9e2a730ff758909be8b9e9d609a",
|
||||
"content-hash": "d3fb809caf4d5a5c99054f47f28ff271",
|
||||
"packages": [
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.0",
|
||||
"name": "adhocore/json-comment",
|
||||
"version": "v0.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595"
|
||||
"url": "https://github.com/adhocore/php-json-comment.git",
|
||||
"reference": "135356c7e7336ef59924f1d921c770045f937a76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
|
||||
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
|
||||
"url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/135356c7e7336ef59924f1d921c770045f937a76",
|
||||
"reference": "135356c7e7336ef59924f1d921c770045f937a76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ahc\\Json\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jitendra Adhikari",
|
||||
"email": "jiten.adhikary@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Lightweight JSON comment stripper library for PHP",
|
||||
"keywords": [
|
||||
"comment",
|
||||
"json",
|
||||
"strip-comment"
|
||||
],
|
||||
"time": "2018-08-01T12:27:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "daverandom/callback-validator",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DaveRandom/CallbackValidator.git",
|
||||
"reference": "d87a08cddbc6099816ed01e50ce25cdfc43b542f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/DaveRandom/CallbackValidator/zipball/d87a08cddbc6099816ed01e50ce25cdfc43b542f",
|
||||
"reference": "d87a08cddbc6099816ed01e50ce25cdfc43b542f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-reflection": "*",
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DaveRandom\\CallbackValidator\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Chris Wright",
|
||||
"email": "cw@daverandom.com"
|
||||
}
|
||||
],
|
||||
"description": "Tools for validating callback signatures",
|
||||
"time": "2017-04-03T15:22:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "925001c8eff97a36519b16bbd095964b0fc52772"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/925001c8eff97a36519b16bbd095964b0fc52772",
|
||||
"reference": "925001c8eff97a36519b16bbd095964b0fc52772",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -35,23 +119,23 @@
|
||||
],
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/master",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.3",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2018-04-16T09:05:08+00:00"
|
||||
"time": "2018-12-30T12:17:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef"
|
||||
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/95ae5600328ed2add44c0bc830a68d3660e9e0ef",
|
||||
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/ee299f5c9c444ca526c9c691b920f321458cf0b6",
|
||||
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -69,26 +153,27 @@
|
||||
],
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Math/tree/master",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.1",
|
||||
"issues": "https://github.com/pmmp/Math/issues"
|
||||
},
|
||||
"time": "2018-06-09T09:26:30+00:00"
|
||||
"time": "2018-08-15T15:43:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c"
|
||||
"reference": "291bf5cc2a94500eada1edbda51d15bed25a1e1c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
|
||||
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/291bf5cc2a94500eada1edbda51d15bed25a1e1c",
|
||||
"reference": "291bf5cc2a94500eada1edbda51d15bed25a1e1c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-zlib": "*",
|
||||
"php": ">=7.2.0",
|
||||
"php-64bit": "*",
|
||||
"pocketmine/binaryutils": "^0.1.0"
|
||||
@ -109,10 +194,10 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.0",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.3",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2018-06-13T09:56:00+00:00"
|
||||
"time": "2018-12-03T16:08:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
@ -191,16 +276,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5"
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/ca3912099543ddc4b4b14f40e258d84ca547dfa5",
|
||||
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -219,14 +304,15 @@
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/SPL/tree/master"
|
||||
},
|
||||
"time": "2018-06-09T17:30:36+00:00"
|
||||
"time": "2018-08-12T15:17:39+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"ext-pthreads": 20
|
||||
"ext-pthreads": 20,
|
||||
"daverandom/callback-validator": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
|
@ -33,6 +33,14 @@ use raklib\RakLib;
|
||||
|
||||
class CrashDump{
|
||||
|
||||
/**
|
||||
* Crashdump data format version, used by the crash archive to decide how to decode the crashdump
|
||||
* This should be incremented when backwards incompatible changes are introduced, such as fields being removed or
|
||||
* having their content changed, version format changing, etc.
|
||||
* It is not necessary to increase this when adding new fields.
|
||||
*/
|
||||
private const FORMAT_VERSION = 1;
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
private $fp;
|
||||
@ -54,6 +62,7 @@ class CrashDump{
|
||||
if(!is_resource($this->fp)){
|
||||
throw new \RuntimeException("Could not create Crash Dump");
|
||||
}
|
||||
$this->data["format_version"] = self::FORMAT_VERSION;
|
||||
$this->data["time"] = $this->time;
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time));
|
||||
$this->addLine();
|
||||
@ -150,7 +159,7 @@ class CrashDump{
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = (array) error_get_last();
|
||||
$error["trace"] = Utils::getTrace(4); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["trace"] = Utils::printableCurrentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$errorConversion = [
|
||||
E_ERROR => "E_ERROR",
|
||||
E_WARNING => "E_WARNING",
|
||||
@ -212,11 +221,11 @@ class CrashDump{
|
||||
$this->addLine("Code:");
|
||||
$this->data["code"] = [];
|
||||
|
||||
if($this->server->getProperty("auto-report.send-code", true) !== false){
|
||||
if($this->server->getProperty("auto-report.send-code", true) !== false and file_exists($error["fullFile"])){
|
||||
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
|
||||
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10; ++$l){
|
||||
$this->addLine("[" . ($l + 1) . "] " . @$file[$l]);
|
||||
$this->data["code"][$l + 1] = @$file[$l];
|
||||
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
|
||||
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
|
||||
$this->data["code"][$l + 1] = $file[$l];
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,10 +241,10 @@ class CrashDump{
|
||||
$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->getFullVersion(false);
|
||||
$this->data["general"]["build"] = $version->getBuild();
|
||||
$this->data["general"]["base_version"] = \pocketmine\BASE_VERSION;
|
||||
$this->data["general"]["build"] = \pocketmine\BUILD_NUMBER;
|
||||
$this->data["general"]["is_dev"] = \pocketmine\IS_DEVELOPMENT_BUILD;
|
||||
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
|
||||
$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");
|
||||
|
@ -185,7 +185,7 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
$ev = new LowMemoryEvent($memory, $limit, $global, $triggerCount);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
|
||||
$cycles = 0;
|
||||
if($this->garbageCollectionTrigger){
|
||||
@ -243,6 +243,9 @@ class MemoryManager{
|
||||
|
||||
if($this->garbageCollectionAsync){
|
||||
$pool = $this->server->getAsyncPool();
|
||||
if(($w = $pool->shutdownUnusedWorkers()) > 0){
|
||||
$this->server->getLogger()->debug("Shut down $w idle async pool workers");
|
||||
}
|
||||
foreach($pool->getRunningWorkers() as $i){
|
||||
$pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
|
||||
}
|
||||
@ -470,7 +473,7 @@ class MemoryManager{
|
||||
self::continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}elseif(is_string($from)){
|
||||
$data = "(string) len(". strlen($from) .") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||
}elseif(is_resource($from)){
|
||||
$data = "(resource) " . print_r($from, true);
|
||||
}else{
|
||||
|
@ -35,7 +35,7 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var CompoundTag|null */
|
||||
private $namedtag;
|
||||
private $namedtag = null;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
@ -44,10 +44,8 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
public function __construct(Server $server, string $name){
|
||||
$this->server = $server;
|
||||
$this->name = $name;
|
||||
if(file_exists($this->server->getDataPath() . "players/" . strtolower($this->name) . ".dat")){
|
||||
if($this->server->hasOfflinePlayerData($this->name)){
|
||||
$this->namedtag = $this->server->getOfflinePlayerData($this->name);
|
||||
}else{
|
||||
$this->namedtag = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,7 @@ namespace pocketmine {
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.1.4";
|
||||
const BASE_VERSION = "3.5.4";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
||||
@ -53,83 +53,89 @@ namespace pocketmine {
|
||||
* Enjoy it as much as I did writing it. I don't want to do it again.
|
||||
*/
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
/* Dependencies check */
|
||||
|
||||
$errors = 0;
|
||||
|
||||
if(php_sapi_name() !== "cli"){
|
||||
critical_error("You must run " . \pocketmine\NAME . " using the CLI.");
|
||||
++$errors;
|
||||
}
|
||||
|
||||
$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"
|
||||
];
|
||||
|
||||
foreach($extensions as $ext => $name){
|
||||
if(!extension_loaded($ext)){
|
||||
critical_error("Unable to find the $name ($ext) extension.");
|
||||
++$errors;
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
function check_platform_dependencies(){
|
||||
if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
|
||||
//If PHP version isn't high enough, anything below might break, so don't bother checking it.
|
||||
return [
|
||||
\pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . "."
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("pthreads")){
|
||||
$pthreads_version = phpversion("pthreads");
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
$messages = [];
|
||||
|
||||
if(PHP_INT_SIZE < 8){
|
||||
$messages[] = "Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
|
||||
}
|
||||
if(version_compare($pthreads_version, "3.1.7dev") < 0){
|
||||
critical_error("pthreads >= 3.1.7dev is required, while you have $pthreads_version.");
|
||||
++$errors;
|
||||
|
||||
if(php_sapi_name() !== "cli"){
|
||||
$messages[] = "You must run " . \pocketmine\NAME . " using the CLI.";
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("leveldb")){
|
||||
$leveldb_version = phpversion("leveldb");
|
||||
if(version_compare($leveldb_version, "0.2.1") < 0){
|
||||
critical_error("php-leveldb >= 0.2.1 is required, while you have $leveldb_version");
|
||||
++$errors;
|
||||
$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"
|
||||
];
|
||||
|
||||
foreach($extensions as $ext => $name){
|
||||
if(!extension_loaded($ext)){
|
||||
$messages[] = "Unable to find the $name ($ext) extension.";
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("pthreads")){
|
||||
$pthreads_version = phpversion("pthreads");
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
if(version_compare($pthreads_version, "3.1.7dev") < 0){
|
||||
$messages[] = "pthreads >= 3.1.7dev is required, while you have $pthreads_version.";
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("leveldb")){
|
||||
$leveldb_version = phpversion("leveldb");
|
||||
if(version_compare($leveldb_version, "0.2.1") < 0){
|
||||
$messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("pocketmine")){
|
||||
$messages[] = "The native PocketMine extension is no longer supported.";
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
if(extension_loaded("pocketmine")){
|
||||
critical_error("The native PocketMine extension is no longer supported.");
|
||||
++$errors;
|
||||
}
|
||||
|
||||
if($errors > 0){
|
||||
if(!empty($messages = check_platform_dependencies())){
|
||||
echo PHP_EOL;
|
||||
$binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
|
||||
critical_error("Selected PHP binary ($binary) does not satisfy some requirements.");
|
||||
foreach($messages as $m){
|
||||
echo " - $m" . PHP_EOL;
|
||||
}
|
||||
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
echo PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
unset($messages);
|
||||
|
||||
error_reporting(-1);
|
||||
|
||||
@ -139,12 +145,18 @@ namespace pocketmine {
|
||||
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
|
||||
$opts = getopt("", ["bootstrap:"]);
|
||||
if(isset($opts["bootstrap"])){
|
||||
$bootstrap = realpath($opts["bootstrap"]) ?: $opts["bootstrap"];
|
||||
}else{
|
||||
$bootstrap = \pocketmine\PATH . 'vendor/autoload.php';
|
||||
}
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
|
||||
|
||||
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}else{
|
||||
critical_error("Composer autoloader not found.");
|
||||
critical_error("Composer autoloader not found at " . $bootstrap);
|
||||
critical_error("Please install/update Composer dependencies or use provided builds.");
|
||||
exit(1);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ use pocketmine\event\HandlerList;
|
||||
use pocketmine\event\level\LevelInitEvent;
|
||||
use pocketmine\event\level\LevelLoadEvent;
|
||||
use pocketmine\event\player\PlayerDataSaveEvent;
|
||||
use pocketmine\event\server\CommandEvent;
|
||||
use pocketmine\event\server\QueryRegenerateEvent;
|
||||
use pocketmine\event\server\ServerCommandEvent;
|
||||
use pocketmine\inventory\CraftingManager;
|
||||
@ -82,6 +83,7 @@ use pocketmine\network\rcon\RCON;
|
||||
use pocketmine\network\upnp\UPnP;
|
||||
use pocketmine\permission\BanList;
|
||||
use pocketmine\permission\DefaultPermissions;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\plugin\PharPluginLoader;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\plugin\PluginLoadOrder;
|
||||
@ -89,7 +91,6 @@ 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\snooze\SleeperHandler;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
@ -99,6 +100,7 @@ use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\updater\AutoUpdater;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\Config;
|
||||
use pocketmine\utils\Internet;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
@ -195,9 +197,6 @@ class Server{
|
||||
/** @var ResourcePackManager */
|
||||
private $resourceManager;
|
||||
|
||||
/** @var ConsoleCommandSender */
|
||||
private $consoleSender;
|
||||
|
||||
/** @var int */
|
||||
private $maxPlayers;
|
||||
|
||||
@ -553,10 +552,11 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return bool
|
||||
*/
|
||||
public function getAllowFlight() : bool{
|
||||
return $this->getConfigBool("allow-flight", false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -731,6 +731,17 @@ class Server{
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the server has stored any saved data for this player.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOfflinePlayerData(string $name) : bool{
|
||||
return file_exists($this->getDataPath() . "players/$name.dat");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
@ -802,22 +813,17 @@ class Server{
|
||||
/**
|
||||
* @param string $name
|
||||
* @param CompoundTag $nbtTag
|
||||
* @param bool $async
|
||||
*/
|
||||
public function saveOfflinePlayerData(string $name, CompoundTag $nbtTag, bool $async = false){
|
||||
public function saveOfflinePlayerData(string $name, CompoundTag $nbtTag){
|
||||
$ev = new PlayerDataSaveEvent($nbtTag, $name);
|
||||
$ev->setCancelled(!$this->shouldSavePlayerData());
|
||||
|
||||
$this->pluginManager->callEvent($ev);
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$nbt = new BigEndianNBTStream();
|
||||
try{
|
||||
if($async){
|
||||
$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()));
|
||||
}
|
||||
file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData()));
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->critical($this->getLanguage()->translateString("pocketmine.data.saveError", [$name, $e->getMessage()]));
|
||||
$this->logger->logException($e);
|
||||
@ -988,6 +994,7 @@ class Server{
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @param Level $level
|
||||
*/
|
||||
public function removeLevel(Level $level) : void{
|
||||
@ -1025,18 +1032,12 @@ class Server{
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
}catch(\Throwable $e){
|
||||
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, $e->getMessage()]));
|
||||
$this->logger->logException($e);
|
||||
return false;
|
||||
}
|
||||
/** @see LevelProvider::__construct() */
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelLoadEvent($level));
|
||||
(new LevelLoadEvent($level))->call();
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
|
||||
@ -1075,24 +1076,19 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
$path = $this->getDataPath() . "worlds/" . $name . "/";
|
||||
/** @var LevelProvider $providerClass */
|
||||
$providerClass::generate($path, $name, $seed, $generator, $options);
|
||||
$path = $this->getDataPath() . "worlds/" . $name . "/";
|
||||
/** @var LevelProvider $providerClass */
|
||||
$providerClass::generate($path, $name, $seed, $generator, $options);
|
||||
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$this->levels[$level->getId()] = $level;
|
||||
/** @see LevelProvider::__construct() */
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.generationError", [$name, $e->getMessage()]));
|
||||
$this->logger->logException($e);
|
||||
return false;
|
||||
}
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelInitEvent($level));
|
||||
(new LevelInitEvent($level))->call();
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelLoadEvent($level));
|
||||
(new LevelLoadEvent($level))->call();
|
||||
|
||||
$this->getLogger()->notice($this->getLanguage()->translateString("pocketmine.level.backgroundGeneration", [$name]));
|
||||
|
||||
@ -1146,17 +1142,12 @@ class Server{
|
||||
* Useful for tracking entities across multiple worlds without needing strong references.
|
||||
*
|
||||
* @param int $entityId
|
||||
* @param Level|null $expectedLevel Level to look in first for the target
|
||||
* @param Level|null $expectedLevel @deprecated Level to look in first for the target
|
||||
*
|
||||
* @return Entity|null
|
||||
*/
|
||||
public function findEntity(int $entityId, Level $expectedLevel = null){
|
||||
$levels = $this->levels;
|
||||
if($expectedLevel !== null){
|
||||
array_unshift($levels, $expectedLevel);
|
||||
}
|
||||
|
||||
foreach($levels as $level){
|
||||
foreach($this->levels as $level){
|
||||
assert(!$level->isClosed());
|
||||
if(($entity = $level->getEntity($entityId)) instanceof Entity){
|
||||
return $entity;
|
||||
@ -1303,7 +1294,7 @@ class Server{
|
||||
if(($player = $this->getPlayerExact($name)) !== null){
|
||||
$player->recalculatePermissions();
|
||||
}
|
||||
$this->operators->save(true);
|
||||
$this->operators->save();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1323,7 +1314,7 @@ class Server{
|
||||
*/
|
||||
public function addWhitelist(string $name){
|
||||
$this->whitelist->set(strtolower($name), true);
|
||||
$this->whitelist->save(true);
|
||||
$this->whitelist->save();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1450,31 +1441,6 @@ class Server{
|
||||
}
|
||||
$this->config = new Config($this->dataPath . "pocketmine.yml", Config::YAML, []);
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
ini_set('assert.exception', '1');
|
||||
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->setLogDebug(\pocketmine\DEBUG > 1);
|
||||
}
|
||||
|
||||
$this->logger->info("Loading server properties...");
|
||||
$this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, [
|
||||
"motd" => \pocketmine\NAME . " Server",
|
||||
@ -1483,7 +1449,6 @@ class Server{
|
||||
"announce-player-achievements" => true,
|
||||
"spawn-protection" => 16,
|
||||
"max-players" => 20,
|
||||
"allow-flight" => false,
|
||||
"spawn-animals" => true,
|
||||
"spawn-mobs" => true,
|
||||
"gamemode" => 0,
|
||||
@ -1500,9 +1465,36 @@ class Server{
|
||||
"rcon.password" => substr(base64_encode(random_bytes(20)), 3, 10),
|
||||
"auto-save" => true,
|
||||
"view-distance" => 8,
|
||||
"xbox-auth" => true
|
||||
"xbox-auth" => true,
|
||||
"language" => "eng"
|
||||
]);
|
||||
|
||||
define('pocketmine\DEBUG', (int) $this->getProperty("debug.level", 1));
|
||||
|
||||
$this->forceLanguage = (bool) $this->getProperty("settings.force-language", false);
|
||||
$this->baseLang = new BaseLang($this->getConfigString("language", $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->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error5", ["https://github.com/pmmp/PocketMine-MP/releases"]));
|
||||
$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.");
|
||||
}
|
||||
|
||||
ini_set('assert.exception', '1');
|
||||
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->setLogDebug(\pocketmine\DEBUG > 1);
|
||||
}
|
||||
|
||||
$this->memoryManager = new MemoryManager($this);
|
||||
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.start", [TextFormat::AQUA . $this->getVersion() . TextFormat::RESET]));
|
||||
@ -1540,10 +1532,22 @@ class Server{
|
||||
|
||||
$this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes();
|
||||
|
||||
|
||||
$consoleSender = new ConsoleCommandSender();
|
||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender);
|
||||
|
||||
$consoleNotifier = new SleeperNotifier();
|
||||
$this->console = new CommandReader($consoleNotifier);
|
||||
$this->tickSleeper->addNotifier($consoleNotifier, function() : void{
|
||||
$this->checkConsole();
|
||||
$this->tickSleeper->addNotifier($consoleNotifier, function() use ($consoleSender) : void{
|
||||
Timings::$serverCommandTimer->startTiming();
|
||||
while(($line = $this->console->getLine()) !== null){
|
||||
$ev = new ServerCommandEvent($consoleSender, $line);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->dispatchCommand($ev->getSender(), $ev->getCommand());
|
||||
}
|
||||
}
|
||||
Timings::$serverCommandTimer->stopTiming();
|
||||
});
|
||||
$this->console->start(PTHREADS_INHERIT_NONE);
|
||||
|
||||
@ -1619,7 +1623,6 @@ class Server{
|
||||
Timings::init();
|
||||
TimingsHandler::setEnabled((bool) $this->getProperty("settings.enable-profiling", false));
|
||||
|
||||
$this->consoleSender = new ConsoleCommandSender();
|
||||
$this->commandMap = new SimpleCommandMap($this);
|
||||
|
||||
Entity::init();
|
||||
@ -1636,7 +1639,6 @@ class Server{
|
||||
$this->resourceManager = new ResourcePackManager($this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR, $this->logger);
|
||||
|
||||
$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(new PharPluginLoader($this->autoloader));
|
||||
$this->pluginManager->registerInterface(new ScriptPluginLoader());
|
||||
@ -1694,7 +1696,7 @@ class Server{
|
||||
}
|
||||
|
||||
if($this->properties->hasChanged()){
|
||||
$this->properties->save(true);
|
||||
$this->properties->save();
|
||||
}
|
||||
|
||||
if(!($this->getDefaultLevel() instanceof Level)){
|
||||
@ -1745,7 +1747,7 @@ class Server{
|
||||
if(!is_array($recipients)){
|
||||
/** @var Player[] $recipients */
|
||||
$recipients = [];
|
||||
foreach($this->pluginManager->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
if($permissible instanceof Player and $permissible->hasPermission(self::BROADCAST_CHANNEL_USERS)){
|
||||
$recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated
|
||||
}
|
||||
@ -1771,7 +1773,7 @@ class Server{
|
||||
/** @var Player[] $recipients */
|
||||
$recipients = [];
|
||||
|
||||
foreach($this->pluginManager->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
if($permissible instanceof Player and $permissible->hasPermission(self::BROADCAST_CHANNEL_USERS)){
|
||||
$recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated
|
||||
}
|
||||
@ -1801,7 +1803,7 @@ class Server{
|
||||
/** @var Player[] $recipients */
|
||||
$recipients = [];
|
||||
|
||||
foreach($this->pluginManager->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
if($permissible instanceof Player and $permissible->hasPermission(self::BROADCAST_CHANNEL_USERS)){
|
||||
$recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated
|
||||
}
|
||||
@ -1826,7 +1828,7 @@ class Server{
|
||||
/** @var CommandSender[] $recipients */
|
||||
$recipients = [];
|
||||
foreach(explode(";", $permissions) as $permission){
|
||||
foreach($this->pluginManager->getPermissionSubscriptions($permission) as $permissible){
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions($permission) as $permissible){
|
||||
if($permissible instanceof CommandSender and $permissible->hasPermission($permission)){
|
||||
$recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated
|
||||
}
|
||||
@ -1935,26 +1937,26 @@ class Server{
|
||||
$this->pluginManager->disablePlugins();
|
||||
}
|
||||
|
||||
public function checkConsole(){
|
||||
Timings::$serverCommandTimer->startTiming();
|
||||
while(($line = $this->console->getLine()) !== null){
|
||||
$this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line));
|
||||
if(!$ev->isCancelled()){
|
||||
$this->dispatchCommand($ev->getSender(), $ev->getCommand());
|
||||
}
|
||||
}
|
||||
Timings::$serverCommandTimer->stopTiming();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command from a CommandSender
|
||||
*
|
||||
* @param CommandSender $sender
|
||||
* @param string $commandLine
|
||||
* @param bool $internal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function dispatchCommand(CommandSender $sender, string $commandLine) : bool{
|
||||
public function dispatchCommand(CommandSender $sender, string $commandLine, bool $internal = false) : bool{
|
||||
if(!$internal){
|
||||
$ev = new CommandEvent($sender, $commandLine);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
|
||||
$commandLine = $ev->getCommand();
|
||||
}
|
||||
|
||||
if($this->commandMap->dispatch($sender, $commandLine)){
|
||||
return true;
|
||||
}
|
||||
@ -1974,6 +1976,7 @@ class Server{
|
||||
|
||||
$this->pluginManager->disablePlugins();
|
||||
$this->pluginManager->clearPlugins();
|
||||
PermissionManager::getInstance()->clearPermissions();
|
||||
$this->commandMap->clearCommands();
|
||||
|
||||
$this->logger->info("Reloading properties...");
|
||||
@ -2170,7 +2173,7 @@ class Server{
|
||||
"fullFile" => $e->getFile(),
|
||||
"file" => $errfile,
|
||||
"line" => $errline,
|
||||
"trace" => Utils::getTrace(0, $trace)
|
||||
"trace" => Utils::printableTrace($trace)
|
||||
];
|
||||
|
||||
global $lastExceptionError, $lastError;
|
||||
@ -2197,6 +2200,14 @@ class Server{
|
||||
|
||||
if($this->getProperty("auto-report.enabled", true) !== false){
|
||||
$report = true;
|
||||
|
||||
$stamp = $this->getDataPath() . "crashdumps/.last_crash";
|
||||
$crashInterval = 120; //2 minutes
|
||||
if(file_exists($stamp) and !($report = (filemtime($stamp) + $crashInterval < time()))){
|
||||
$this->logger->debug("Not sending crashdump due to last crash less than $crashInterval seconds ago");
|
||||
}
|
||||
@touch($stamp); //update file timestamp
|
||||
|
||||
$plugin = $dump->getData()["plugin"];
|
||||
if(is_string($plugin)){
|
||||
$p = $this->pluginManager->getPlugin($plugin);
|
||||
@ -2216,7 +2227,7 @@ class Server{
|
||||
|
||||
if($report){
|
||||
$url = ($this->getProperty("auto-report.use-https", true) ? "https" : "http") . "://" . $this->getProperty("auto-report.host", "crash.pmmp.io") . "/submit/api";
|
||||
$reply = Utils::postURL($url, [
|
||||
$reply = Internet::postURL($url, [
|
||||
"report" => "yes",
|
||||
"name" => $this->getName() . " " . $this->getPocketMineVersion(),
|
||||
"email" => "crash@pocketmine.net",
|
||||
@ -2239,6 +2250,12 @@ class Server{
|
||||
|
||||
$this->forceShutdown();
|
||||
$this->isRunning = false;
|
||||
|
||||
//Force minimum uptime to be >= 120 seconds, to reduce the impact of spammy crash loops
|
||||
$spacing = ((int) \pocketmine\START_TIME) - time() + 120;
|
||||
if($spacing > 0){
|
||||
sleep($spacing);
|
||||
}
|
||||
@Utils::kill(getmypid());
|
||||
exit(1);
|
||||
}
|
||||
@ -2311,7 +2328,7 @@ class Server{
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, "", 0, $skin, $xboxUserId);
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, $skin, $xboxUserId);
|
||||
|
||||
$this->broadcastPacket($players ?? $this->playerList, $pk);
|
||||
}
|
||||
@ -2334,7 +2351,7 @@ class Server{
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
foreach($this->playerList as $player){
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), "", 0, $player->getSkin(), $player->getXuid());
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
|
||||
}
|
||||
|
||||
$p->dataPacket($pk);
|
||||
@ -2350,41 +2367,37 @@ class Server{
|
||||
}
|
||||
|
||||
//Do level ticks
|
||||
foreach($this->getLevels() as $level){
|
||||
foreach($this->levels as $k => $level){
|
||||
if(!isset($this->levels[$k])){
|
||||
// Level unloaded during the tick of a level earlier in this loop, perhaps by plugin
|
||||
continue;
|
||||
}
|
||||
if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){
|
||||
continue;
|
||||
}
|
||||
try{
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$tickMs = (microtime(true) - $levelTime) * 1000;
|
||||
$level->tickRateTime = $tickMs;
|
||||
|
||||
if($this->autoTickRate){
|
||||
if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){
|
||||
$level->setTickRate($r = $level->getTickRate() - 1);
|
||||
if($r > $this->baseTickRate){
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
$this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks");
|
||||
}elseif($tickMs >= 50){
|
||||
if($level->getTickRate() === $this->baseTickRate){
|
||||
$level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50))));
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){
|
||||
$level->setTickRate($level->getTickRate() + 1);
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$tickMs = (microtime(true) - $levelTime) * 1000;
|
||||
$level->tickRateTime = $tickMs;
|
||||
|
||||
if($this->autoTickRate){
|
||||
if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){
|
||||
$level->setTickRate($r = $level->getTickRate() - 1);
|
||||
if($r > $this->baseTickRate){
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
$this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks");
|
||||
}elseif($tickMs >= 50){
|
||||
if($level->getTickRate() === $this->baseTickRate){
|
||||
$level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50))));
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){
|
||||
$level->setTickRate($level->getTickRate() + 1);
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
if(!$level->isClosed()){
|
||||
$this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickError", [$level->getName(), $e->getMessage()]));
|
||||
}else{
|
||||
$this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickUnloadError", [$level->getName()]));
|
||||
}
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2394,7 +2407,7 @@ class Server{
|
||||
Timings::$worldSaveTimer->startTiming();
|
||||
foreach($this->players as $index => $player){
|
||||
if($player->spawned){
|
||||
$player->save(true);
|
||||
$player->save();
|
||||
}elseif(!$player->isConnected()){
|
||||
$this->removePlayer($player);
|
||||
}
|
||||
@ -2478,9 +2491,7 @@ class Server{
|
||||
$this->logger->debug("Unhandled raw packet from $address $port: " . bin2hex($payload));
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
$this->logger->logException($e);
|
||||
|
||||
$this->getNetwork()->blockAddress($address, 600);
|
||||
}
|
||||
@ -2531,13 +2542,9 @@ class Server{
|
||||
}
|
||||
|
||||
if(($this->tickCounter & 0b111111111) === 0){
|
||||
try{
|
||||
$this->getPluginManager()->callEvent($this->queryRegenerateTask = new QueryRegenerateEvent($this, 5));
|
||||
if($this->queryHandler !== null){
|
||||
$this->queryHandler->regenerateInfo();
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
($this->queryRegenerateTask = new QueryRegenerateEvent($this, 5))->call();
|
||||
if($this->queryHandler !== null){
|
||||
$this->queryHandler->regenerateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
class ActivatorRail extends Rail{
|
||||
class ActivatorRail extends RedstoneRail{
|
||||
|
||||
protected $id = self::ACTIVATOR_RAIL;
|
||||
|
||||
|
266
src/pocketmine/block/BaseRail.php
Normal file
266
src/pocketmine/block/BaseRail.php
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
|
||||
abstract class BaseRail extends Flowable{
|
||||
|
||||
public const STRAIGHT_NORTH_SOUTH = 0;
|
||||
public const STRAIGHT_EAST_WEST = 1;
|
||||
public const ASCENDING_EAST = 2;
|
||||
public const ASCENDING_WEST = 3;
|
||||
public const ASCENDING_NORTH = 4;
|
||||
public const ASCENDING_SOUTH = 5;
|
||||
|
||||
private const ASCENDING_SIDES = [
|
||||
self::ASCENDING_NORTH => Vector3::SIDE_NORTH,
|
||||
self::ASCENDING_EAST => Vector3::SIDE_EAST,
|
||||
self::ASCENDING_SOUTH => Vector3::SIDE_SOUTH,
|
||||
self::ASCENDING_WEST => Vector3::SIDE_WEST
|
||||
];
|
||||
|
||||
protected const FLAG_ASCEND = 1 << 24; //used to indicate direction-up
|
||||
|
||||
protected const CONNECTIONS = [
|
||||
//straights
|
||||
self::STRAIGHT_NORTH_SOUTH => [
|
||||
Vector3::SIDE_NORTH,
|
||||
Vector3::SIDE_SOUTH
|
||||
],
|
||||
self::STRAIGHT_EAST_WEST => [
|
||||
Vector3::SIDE_EAST,
|
||||
Vector3::SIDE_WEST
|
||||
],
|
||||
|
||||
//ascending
|
||||
self::ASCENDING_EAST => [
|
||||
Vector3::SIDE_WEST,
|
||||
Vector3::SIDE_EAST | self::FLAG_ASCEND
|
||||
],
|
||||
self::ASCENDING_WEST => [
|
||||
Vector3::SIDE_EAST,
|
||||
Vector3::SIDE_WEST | self::FLAG_ASCEND
|
||||
],
|
||||
self::ASCENDING_NORTH => [
|
||||
Vector3::SIDE_SOUTH,
|
||||
Vector3::SIDE_NORTH | self::FLAG_ASCEND
|
||||
],
|
||||
self::ASCENDING_SOUTH => [
|
||||
Vector3::SIDE_NORTH,
|
||||
Vector3::SIDE_SOUTH | self::FLAG_ASCEND
|
||||
]
|
||||
];
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0.7;
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
if(!$blockReplace->getSide(Vector3::SIDE_DOWN)->isTransparent() and $this->getLevel()->setBlock($blockReplace, $this, true, true)){
|
||||
$this->tryReconnect();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function searchState(array $connections, array $lookup) : int{
|
||||
$meta = array_search($connections, $lookup, true);
|
||||
if($meta === false){
|
||||
$meta = array_search(array_reverse($connections), $lookup, true);
|
||||
}
|
||||
if($meta === false){
|
||||
throw new \InvalidArgumentException("No meta value matches connections " . implode(", ", array_map('dechex', $connections)));
|
||||
}
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a meta value for the rail with the given connections.
|
||||
*
|
||||
* @param array $connections
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \InvalidArgumentException if no state matches the given connections
|
||||
*/
|
||||
protected function getMetaForState(array $connections) : int{
|
||||
return self::searchState($connections, self::CONNECTIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection directions of this rail (depending on the current block state)
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
abstract protected function getConnectionsForState() : array;
|
||||
|
||||
/**
|
||||
* Returns all the directions this rail is already connected in.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function getConnectedDirections() : array{
|
||||
/** @var int[] $connections */
|
||||
$connections = [];
|
||||
|
||||
/** @var int $connection */
|
||||
foreach($this->getConnectionsForState() as $connection){
|
||||
$other = $this->getSide($connection & ~self::FLAG_ASCEND);
|
||||
$otherConnection = Vector3::getOppositeSide($connection & ~self::FLAG_ASCEND);
|
||||
|
||||
if(($connection & self::FLAG_ASCEND) !== 0){
|
||||
$other = $other->getSide(Vector3::SIDE_UP);
|
||||
|
||||
}elseif(!($other instanceof BaseRail)){ //check for rail sloping up to meet this one
|
||||
$other = $other->getSide(Vector3::SIDE_DOWN);
|
||||
$otherConnection |= self::FLAG_ASCEND;
|
||||
}
|
||||
|
||||
if(
|
||||
$other instanceof BaseRail and
|
||||
in_array($otherConnection, $other->getConnectionsForState(), true)
|
||||
){
|
||||
$connections[] = $connection;
|
||||
}
|
||||
}
|
||||
|
||||
return $connections;
|
||||
}
|
||||
|
||||
private function getPossibleConnectionDirections(array $constraints) : array{
|
||||
switch(count($constraints)){
|
||||
case 0:
|
||||
//No constraints, can connect in any direction
|
||||
$possible = [
|
||||
Vector3::SIDE_NORTH => true,
|
||||
Vector3::SIDE_SOUTH => true,
|
||||
Vector3::SIDE_WEST => true,
|
||||
Vector3::SIDE_EAST => true
|
||||
];
|
||||
foreach($possible as $p => $_){
|
||||
$possible[$p | self::FLAG_ASCEND] = true;
|
||||
}
|
||||
|
||||
return $possible;
|
||||
case 1:
|
||||
return $this->getPossibleConnectionDirectionsOneConstraint(array_shift($constraints));
|
||||
case 2:
|
||||
return [];
|
||||
default:
|
||||
throw new \InvalidArgumentException("Expected at most 2 constraints, got " . count($constraints));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{
|
||||
$opposite = Vector3::getOppositeSide($constraint & ~self::FLAG_ASCEND);
|
||||
|
||||
$possible = [$opposite => true];
|
||||
|
||||
if(($constraint & self::FLAG_ASCEND) === 0){
|
||||
//We can slope the other way if this connection isn't already a slope
|
||||
$possible[$opposite | self::FLAG_ASCEND] = true;
|
||||
}
|
||||
|
||||
return $possible;
|
||||
}
|
||||
|
||||
private function tryReconnect() : void{
|
||||
$thisConnections = $this->getConnectedDirections();
|
||||
$changed = false;
|
||||
|
||||
do{
|
||||
$possible = $this->getPossibleConnectionDirections($thisConnections);
|
||||
$continue = false;
|
||||
|
||||
foreach($possible as $thisSide => $_){
|
||||
$otherSide = Vector3::getOppositeSide($thisSide & ~self::FLAG_ASCEND);
|
||||
|
||||
$other = $this->getSide($thisSide & ~self::FLAG_ASCEND);
|
||||
|
||||
if(($thisSide & self::FLAG_ASCEND) !== 0){
|
||||
$other = $other->getSide(Vector3::SIDE_UP);
|
||||
|
||||
}elseif(!($other instanceof BaseRail)){ //check if other rails can slope up to meet this one
|
||||
$other = $other->getSide(Vector3::SIDE_DOWN);
|
||||
$otherSide |= self::FLAG_ASCEND;
|
||||
}
|
||||
|
||||
if(!($other instanceof BaseRail) or count($otherConnections = $other->getConnectedDirections()) >= 2){
|
||||
//we can only connect to a rail that has less than 2 connections
|
||||
continue;
|
||||
}
|
||||
|
||||
$otherPossible = $other->getPossibleConnectionDirections($otherConnections);
|
||||
|
||||
if(isset($otherPossible[$otherSide])){
|
||||
$otherConnections[] = $otherSide;
|
||||
$other->updateState($otherConnections);
|
||||
|
||||
$changed = true;
|
||||
$thisConnections[] = $thisSide;
|
||||
$continue = count($thisConnections) < 2;
|
||||
|
||||
break; //force recomputing possible directions, since this connection could invalidate others
|
||||
}
|
||||
}
|
||||
}while($continue);
|
||||
|
||||
if($changed){
|
||||
$this->updateState($thisConnections);
|
||||
}
|
||||
}
|
||||
|
||||
private function updateState(array $connections) : void{
|
||||
if(count($connections) === 1){
|
||||
$connections[] = Vector3::getOppositeSide($connections[0] & ~self::FLAG_ASCEND);
|
||||
}elseif(count($connections) !== 2){
|
||||
throw new \InvalidArgumentException("Expected exactly 2 connections, got " . count($connections));
|
||||
}
|
||||
|
||||
$this->meta = $this->getMetaForState($connections);
|
||||
$this->level->setBlock($this, $this, false, false); //avoid recursion
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent() or (
|
||||
isset(self::ASCENDING_SIDES[$this->meta & 0x07]) and
|
||||
$this->getSide(self::ASCENDING_SIDES[$this->meta & 0x07])->isTransparent()
|
||||
)){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getVariantBitmask() : int{
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -192,21 +192,25 @@ class Bed extends Transparent{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if($this->isHeadPart()){
|
||||
$tile = $this->getLevel()->getTile($this);
|
||||
if($tile instanceof TileBed){
|
||||
return [
|
||||
ItemFactory::get($this->getItemId(), $tile->getColor())
|
||||
];
|
||||
}else{
|
||||
return [
|
||||
ItemFactory::get($this->getItemId(), 14) //Red
|
||||
];
|
||||
}
|
||||
return [$this->getItem()];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return $this->getItem();
|
||||
}
|
||||
|
||||
private function getItem() : Item{
|
||||
$tile = $this->getLevel()->getTile($this);
|
||||
if($tile instanceof TileBed){
|
||||
return ItemFactory::get($this->getItemId(), $tile->getColor());
|
||||
}
|
||||
|
||||
return ItemFactory::get($this->getItemId(), 14); //Red
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
@ -109,6 +109,14 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
return $this->itemId ?? $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return int
|
||||
*/
|
||||
public function getRuntimeId() : int{
|
||||
return BlockFactory::toStaticRuntimeId($this->getId(), $this->getDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
|
@ -330,6 +330,10 @@ class BlockFactory{
|
||||
}
|
||||
}
|
||||
|
||||
public static function isInit() : bool{
|
||||
return self::$fullList !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a block type into the index. Plugins may use this method to register new block types or override
|
||||
* existing ones.
|
||||
@ -411,6 +415,7 @@ class BlockFactory{
|
||||
* Returns whether a specified block ID is already registered in the block factory.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRegistered(int $id) : bool{
|
||||
@ -421,8 +426,8 @@ class BlockFactory{
|
||||
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"]);
|
||||
foreach($runtimeIdMap as $k => $obj){
|
||||
self::registerMapping($k, $obj["id"], $obj["data"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\TieredTool;
|
||||
|
||||
class BrewingStand extends Transparent{
|
||||
|
||||
protected $id = self::BREWING_STAND_BLOCK;
|
||||
|
||||
protected $itemId = Item::BREWING_STAND;
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Cactus extends Transparent{
|
||||
|
||||
@ -77,7 +76,7 @@ class Cactus extends Transparent{
|
||||
}else{
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if(!$b->canBeFlowedInto()){
|
||||
if($b->isSolid()){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
break;
|
||||
}
|
||||
@ -95,7 +94,8 @@ class Cactus extends Transparent{
|
||||
for($y = 1; $y < 3; ++$y){
|
||||
$b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z);
|
||||
if($b->getId() === self::AIR){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS)));
|
||||
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
|
||||
}
|
||||
@ -117,7 +117,7 @@ class Cactus extends Transparent{
|
||||
$block1 = $this->getSide(Vector3::SIDE_SOUTH);
|
||||
$block2 = $this->getSide(Vector3::SIDE_WEST);
|
||||
$block3 = $this->getSide(Vector3::SIDE_EAST);
|
||||
if($block0->isTransparent() and $block1->isTransparent() and $block2->isTransparent() and $block3->isTransparent()){
|
||||
if(!$block0->isSolid() and !$block1->isSolid() and !$block2->isSolid() and !$block3->isSolid()){
|
||||
$this->getLevel()->setBlock($this, $this, true);
|
||||
|
||||
return true;
|
||||
|
@ -35,6 +35,8 @@ class Cake extends Transparent implements FoodSource{
|
||||
|
||||
protected $id = self::CAKE_BLOCK;
|
||||
|
||||
protected $itemId = Item::CAKE;
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
abstract class Crops extends Flowable{
|
||||
|
||||
@ -50,8 +49,8 @@ abstract class Crops extends Flowable{
|
||||
$block->meta = 7;
|
||||
}
|
||||
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block));
|
||||
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true);
|
||||
}
|
||||
@ -79,8 +78,8 @@ abstract class Crops extends Flowable{
|
||||
if($this->meta < 0x07){
|
||||
$block = clone $this;
|
||||
++$block->meta;
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block));
|
||||
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
class DetectorRail extends Rail{
|
||||
class DetectorRail extends RedstoneRail{
|
||||
|
||||
protected $id = self::DETECTOR_RAIL;
|
||||
|
||||
|
@ -47,4 +47,8 @@ abstract class DoubleSlab extends Solid{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return ItemFactory::get($this->getSlabId(), $this->getVariant());
|
||||
}
|
||||
}
|
||||
|
@ -111,4 +111,8 @@ class Farmland extends Transparent{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return ItemFactory::get(Item::DIRT);
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Fire extends Flowable{
|
||||
|
||||
@ -69,7 +68,7 @@ class Fire extends Flowable{
|
||||
if($entity instanceof Arrow){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$entity->setOnFire($ev->getDuration());
|
||||
}
|
||||
@ -153,7 +152,8 @@ class Fire extends Flowable{
|
||||
|
||||
private function burnBlock(Block $block, int $chanceBound) : void{
|
||||
if(mt_rand(0, $chanceBound) < $block->getFlammability()){
|
||||
$this->level->getServer()->getPluginManager()->callEvent($ev = new BlockBurnEvent($block, $this));
|
||||
$ev = new BlockBurnEvent($block, $this);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$block->onIncinerate();
|
||||
|
||||
|
@ -67,7 +67,8 @@ class Grass extends Solid{
|
||||
$lightAbove = $this->level->getFullLightAt($this->x, $this->y + 1, $this->z);
|
||||
if($lightAbove < 4 and BlockFactory::$lightFilter[$this->level->getBlockIdAt($this->x, $this->y + 1, $this->z)] >= 3){ //2 plus 1 standard filter amount
|
||||
//grass dies
|
||||
$this->level->getServer()->getPluginManager()->callEvent($ev = new BlockSpreadEvent($this, $this, BlockFactory::get(Block::DIRT)));
|
||||
$ev = new BlockSpreadEvent($this, $this, BlockFactory::get(Block::DIRT));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->level->setBlock($this, $ev->getNewState(), false, false);
|
||||
}
|
||||
@ -86,7 +87,8 @@ class Grass extends Solid{
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->level->getServer()->getPluginManager()->callEvent($ev = new BlockSpreadEvent($b = $this->level->getBlockAt($x, $y, $z), $this, BlockFactory::get(Block::GRASS)));
|
||||
$ev = new BlockSpreadEvent($b = $this->level->getBlockAt($x, $y, $z), $this, BlockFactory::get(Block::GRASS));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->level->setBlock($b, $ev->getNewState(), false, false);
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Lava extends Liquid{
|
||||
|
||||
@ -107,7 +106,7 @@ class Lava extends Liquid{
|
||||
$entity->attack($ev);
|
||||
|
||||
$ev = new EntityCombustByBlockEvent($this, $entity, 15);
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$entity->setOnFire($ev->getDuration());
|
||||
}
|
||||
|
@ -147,8 +147,8 @@ class Leaves extends Transparent{
|
||||
$this->meta &= 0x03;
|
||||
$visited = [];
|
||||
|
||||
$this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new LeavesDecayEvent($this));
|
||||
|
||||
$ev = new LeavesDecayEvent($this);
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or $this->findLog($this, $visited, 0)){
|
||||
$this->getLevel()->setBlock($this, $this, false, false);
|
||||
}else{
|
||||
|
@ -24,11 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\block\BlockFormEvent;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\sound\FizzSound;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
|
||||
@ -294,12 +296,16 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
protected function flowIntoBlock(Block $block, int $newFlowDecay) : void{
|
||||
if($this->canFlowInto($block) and !($block instanceof Liquid)){
|
||||
if($block->getId() > 0){
|
||||
$this->level->useBreakOn($block);
|
||||
}
|
||||
$ev = new BlockSpreadEvent($block, $this, BlockFactory::get($this->getId(), $newFlowDecay));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
if($block->getId() > 0){
|
||||
$this->level->useBreakOn($block);
|
||||
}
|
||||
|
||||
$this->level->setBlock($block, BlockFactory::get($this->getId(), $newFlowDecay), true, true);
|
||||
$this->level->scheduleDelayedBlockUpdate($block, $this->tickRate());
|
||||
$this->level->setBlock($block, $ev->getNewState(), true, true);
|
||||
$this->level->scheduleDelayedBlockUpdate($block, $this->tickRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,10 +431,12 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
protected function liquidCollide(Block $cause, Block $result) : bool{
|
||||
//TODO: add events
|
||||
|
||||
$this->level->setBlock($this, $result, true, true);
|
||||
$this->level->broadcastLevelSoundEvent($this->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_FIZZ, (int) ((2.6 + (lcg_value() - lcg_value()) * 0.8) * 1000));
|
||||
$ev = new BlockFormEvent($this, $result);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->level->setBlock($this, $ev->getNewState(), true, true);
|
||||
$this->level->addSound(new FizzSound($this->add(0.5, 0.5, 0.5), 2.6 + (lcg_value() - lcg_value()) * 0.8));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Server;
|
||||
|
||||
class MelonStem extends Crops{
|
||||
|
||||
@ -46,7 +45,8 @@ class MelonStem extends Crops{
|
||||
if($this->meta < 0x07){
|
||||
$block = clone $this;
|
||||
++$block->meta;
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block));
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($this, $ev->getNewState(), true);
|
||||
}
|
||||
@ -60,7 +60,8 @@ class MelonStem extends Crops{
|
||||
$side = $this->getSide(mt_rand(2, 5));
|
||||
$d = $side->getSide(Vector3::SIDE_DOWN);
|
||||
if($side->getId() === self::AIR and ($d->getId() === self::FARMLAND or $d->getId() === self::GRASS or $d->getId() === self::DIRT)){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($side, BlockFactory::get(Block::MELON_BLOCK)));
|
||||
$ev = new BlockGrowEvent($side, BlockFactory::get(Block::MELON_BLOCK));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($side, $ev->getNewState(), true);
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Mycelium extends Solid{
|
||||
|
||||
@ -67,7 +66,8 @@ class Mycelium extends Solid{
|
||||
$block = $this->getLevel()->getBlockAt($x, $y, $z);
|
||||
if($block->getId() === Block::DIRT){
|
||||
if($block->getSide(Vector3::SIDE_UP) instanceof Transparent){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockSpreadEvent($block, $this, BlockFactory::get(Block::MYCELIUM)));
|
||||
$ev = new BlockSpreadEvent($block, $this, BlockFactory::get(Block::MYCELIUM));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($block, $ev->getNewState());
|
||||
}
|
||||
|
@ -68,8 +68,8 @@ class NetherWartPlant extends Flowable{
|
||||
if($this->meta < 3 and mt_rand(0, 10) === 0){ //Still growing
|
||||
$block = clone $this;
|
||||
$block->meta++;
|
||||
$this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block));
|
||||
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($this, $ev->getNewState(), false, true);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
class PoweredRail extends Rail{
|
||||
class PoweredRail extends RedstoneRail{
|
||||
protected $id = self::POWERED_RAIL;
|
||||
|
||||
public function getName() : string{
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Server;
|
||||
|
||||
class PumpkinStem extends Crops{
|
||||
|
||||
@ -46,7 +45,8 @@ class PumpkinStem extends Crops{
|
||||
if($this->meta < 0x07){
|
||||
$block = clone $this;
|
||||
++$block->meta;
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block));
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($this, $ev->getNewState(), true);
|
||||
}
|
||||
@ -60,7 +60,8 @@ class PumpkinStem extends Crops{
|
||||
$side = $this->getSide(mt_rand(2, 5));
|
||||
$d = $side->getSide(Vector3::SIDE_DOWN);
|
||||
if($side->getId() === self::AIR and ($d->getId() === self::FARMLAND or $d->getId() === self::GRASS or $d->getId() === self::DIRT)){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($side, BlockFactory::get(Block::PUMPKIN)));
|
||||
$ev = new BlockGrowEvent($side, BlockFactory::get(Block::PUMPKIN));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($side, $ev->getNewState(), true);
|
||||
}
|
||||
|
@ -23,54 +23,71 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Rail extends Flowable{
|
||||
class Rail extends BaseRail{
|
||||
|
||||
public const STRAIGHT_NORTH_SOUTH = 0;
|
||||
public const STRAIGHT_EAST_WEST = 1;
|
||||
public const ASCENDING_EAST = 2;
|
||||
public const ASCENDING_WEST = 3;
|
||||
public const ASCENDING_NORTH = 4;
|
||||
public const ASCENDING_SOUTH = 5;
|
||||
/* extended meta values for regular rails, to allow curving */
|
||||
public const CURVE_SOUTHEAST = 6;
|
||||
public const CURVE_SOUTHWEST = 7;
|
||||
public const CURVE_NORTHWEST = 8;
|
||||
public const CURVE_NORTHEAST = 9;
|
||||
|
||||
protected $id = self::RAIL;
|
||||
private const CURVE_CONNECTIONS = [
|
||||
self::CURVE_SOUTHEAST => [
|
||||
Vector3::SIDE_SOUTH,
|
||||
Vector3::SIDE_EAST
|
||||
],
|
||||
self::CURVE_SOUTHWEST => [
|
||||
Vector3::SIDE_SOUTH,
|
||||
Vector3::SIDE_WEST
|
||||
],
|
||||
self::CURVE_NORTHWEST => [
|
||||
Vector3::SIDE_NORTH,
|
||||
Vector3::SIDE_WEST
|
||||
],
|
||||
self::CURVE_NORTHEAST => [
|
||||
Vector3::SIDE_NORTH,
|
||||
Vector3::SIDE_EAST
|
||||
]
|
||||
];
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
$this->meta = $meta;
|
||||
}
|
||||
protected $id = self::RAIL;
|
||||
|
||||
public function getName() : string{
|
||||
return "Rail";
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0.7;
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
if(!$blockReplace->getSide(Vector3::SIDE_DOWN)->isTransparent()){
|
||||
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
}else{
|
||||
//TODO: Update rail connectivity
|
||||
protected function getMetaForState(array $connections) : int{
|
||||
try{
|
||||
return self::searchState($connections, self::CURVE_CONNECTIONS);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
return parent::getMetaForState($connections);
|
||||
}
|
||||
}
|
||||
|
||||
public function getVariantBitmask() : int{
|
||||
return 0;
|
||||
protected function getConnectionsForState() : array{
|
||||
return self::CURVE_CONNECTIONS[$this->meta] ?? self::CONNECTIONS[$this->meta];
|
||||
}
|
||||
|
||||
protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{
|
||||
static $horizontal = [
|
||||
Vector3::SIDE_NORTH,
|
||||
Vector3::SIDE_SOUTH,
|
||||
Vector3::SIDE_WEST,
|
||||
Vector3::SIDE_EAST
|
||||
];
|
||||
|
||||
$possible = parent::getPossibleConnectionDirectionsOneConstraint($constraint);
|
||||
|
||||
if(($constraint & self::FLAG_ASCEND) === 0){
|
||||
foreach($horizontal as $d){
|
||||
if($constraint !== $d){
|
||||
$possible[$d] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $possible;
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ class RedstoneOre extends Solid{
|
||||
}
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
|
||||
return false; //this shouldn't prevent block placement
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
32
src/pocketmine/block/RedstoneRail.php
Normal file
32
src/pocketmine/block/RedstoneRail.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
class RedstoneRail extends BaseRail{
|
||||
protected const FLAG_POWERED = 0x08;
|
||||
|
||||
protected function getConnectionsForState() : array{
|
||||
return self::CONNECTIONS[$this->meta & ~self::FLAG_POWERED];
|
||||
}
|
||||
}
|
@ -71,18 +71,20 @@ class Skull extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
private function getItem() : Item{
|
||||
$tile = $this->level->getTile($this);
|
||||
if($tile instanceof TileSkull){
|
||||
return [
|
||||
ItemFactory::get(Item::SKULL, $tile->getType())
|
||||
];
|
||||
}
|
||||
return ItemFactory::get(Item::SKULL, $tile instanceof TileSkull ? $tile->getType() : 0);
|
||||
}
|
||||
|
||||
return [];
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [$this->getItem()];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return $this->getItem();
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Sugarcane extends Flowable{
|
||||
|
||||
@ -49,7 +48,8 @@ class Sugarcane extends Flowable{
|
||||
for($y = 1; $y < 3; ++$y){
|
||||
$b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z);
|
||||
if($b->getId() === self::AIR){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK)));
|
||||
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\item\FlintSteel;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -56,6 +57,16 @@ class TNT extends Solid{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity) : void{
|
||||
if($entity instanceof Arrow and $entity->isOnFire()){
|
||||
$this->ignite();
|
||||
}
|
||||
}
|
||||
|
||||
public function ignite(int $fuse = 80){
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true);
|
||||
|
||||
|
@ -100,11 +100,7 @@ abstract class Thin extends Transparent{
|
||||
}
|
||||
|
||||
//FIXME: currently there's no proper way to tell if a block is a full-block, so we check the bounding box size
|
||||
$bbs = $block->getCollisionBoxes();
|
||||
if(count($bbs) === 1){
|
||||
return $bbs[0]->getAverageEdgeLength() >= 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
$bb = $block->getBoundingBox();
|
||||
return $bb !== null and $bb->getAverageEdgeLength() >= 1;
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ class Torch extends Flowable{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$below = $this->getSide(Vector3::SIDE_DOWN);
|
||||
$side = $this->getDamage();
|
||||
$faces = [
|
||||
$meta = $this->getDamage();
|
||||
static $faces = [
|
||||
0 => Vector3::SIDE_DOWN,
|
||||
1 => Vector3::SIDE_WEST,
|
||||
2 => Vector3::SIDE_EAST,
|
||||
@ -54,8 +54,9 @@ class Torch extends Flowable{
|
||||
4 => Vector3::SIDE_SOUTH,
|
||||
5 => Vector3::SIDE_DOWN
|
||||
];
|
||||
$face = $faces[$meta] ?? Vector3::SIDE_DOWN;
|
||||
|
||||
if($this->getSide($faces[$side])->isTransparent() and !($side === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
|
||||
if($this->getSide($face)->isTransparent() and !($face === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ class UnknownBlock extends Transparent{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function canBePlaced() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ namespace pocketmine\command;
|
||||
|
||||
use pocketmine\lang\TextContainer;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\TextFormat;
|
||||
@ -293,7 +294,7 @@ abstract class Command{
|
||||
$m = clone $message;
|
||||
$result = "[" . $source->getName() . ": " . ($source->getServer()->getLanguage()->get($m->getText()) !== $m->getText() ? "%" : "") . $m->getText() . "]";
|
||||
|
||||
$users = $source->getServer()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
|
||||
$users = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
|
||||
$colored = TextFormat::GRAY . TextFormat::ITALIC . $result;
|
||||
|
||||
$m->setText($result);
|
||||
@ -301,7 +302,7 @@ abstract class Command{
|
||||
$m->setText($colored);
|
||||
$colored = clone $m;
|
||||
}else{
|
||||
$users = $source->getServer()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
|
||||
$users = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
|
||||
$result = new TranslationContainer("chat.type.admin", [$source->getName(), $message]);
|
||||
$colored = new TranslationContainer(TextFormat::GRAY . TextFormat::ITALIC . "%chat.type.admin", [$source->getName(), $message]);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace pocketmine\command;
|
||||
interface CommandMap{
|
||||
|
||||
/**
|
||||
* @param string $fallbackPrefix
|
||||
* @param string $fallbackPrefix
|
||||
* @param Command[] $commands
|
||||
*/
|
||||
public function registerAll(string $fallbackPrefix, array $commands);
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\command;
|
||||
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\Thread;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class CommandReader extends Thread{
|
||||
|
||||
@ -47,9 +48,9 @@ class CommandReader extends Thread{
|
||||
$this->buffer = new \Threaded;
|
||||
$this->notifier = $notifier;
|
||||
|
||||
$opts = getopt("", ["disable-readline"]);
|
||||
$opts = getopt("", ["disable-readline", "enable-readline"]);
|
||||
|
||||
if(extension_loaded("readline") and !isset($opts["disable-readline"]) and !$this->isPipe(STDIN)){
|
||||
if(extension_loaded("readline") and (Utils::getOS() === "win" ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){
|
||||
$this->type = self::TYPE_READLINE;
|
||||
}
|
||||
}
|
||||
@ -94,6 +95,7 @@ class CommandReader extends Thread{
|
||||
* Checks if the specified stream is a FIFO pipe.
|
||||
*
|
||||
* @param resource $stream
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isPipe($stream) : bool{
|
||||
|
@ -54,6 +54,7 @@ interface CommandSender extends Permissible{
|
||||
|
||||
/**
|
||||
* Sets the line height used for command output pagination for this command sender. `null` will reset it to default.
|
||||
*
|
||||
* @param int|null $height
|
||||
*/
|
||||
public function setScreenLineHeight(int $height = null);
|
||||
|
@ -64,7 +64,7 @@ class ConsoleCommandSender implements CommandSender{
|
||||
/**
|
||||
* @param Plugin $plugin
|
||||
* @param string $name
|
||||
* @param bool $value
|
||||
* @param bool $value
|
||||
*
|
||||
* @return PermissionAttachment
|
||||
*/
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
@ -49,17 +48,12 @@ class FormattedCommandAlias extends Command{
|
||||
$commands[] = $this->buildCommand($formatString, $args);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$sender->sendMessage(TextFormat::RED . $e->getMessage());
|
||||
return false;
|
||||
}catch(\Throwable $e){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception"));
|
||||
$sender->getServer()->getLogger()->logException($e);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($commands as $command){
|
||||
$result |= Server::getInstance()->dispatchCommand($sender, $command);
|
||||
$result |= Server::getInstance()->dispatchCommand($sender, $command, true);
|
||||
}
|
||||
|
||||
return (bool) $result;
|
||||
|
@ -65,9 +65,7 @@ use pocketmine\command\defaults\VanillaCommand;
|
||||
use pocketmine\command\defaults\VersionCommand;
|
||||
use pocketmine\command\defaults\WhitelistCommand;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class SimpleCommandMap implements CommandMap{
|
||||
|
||||
@ -193,9 +191,9 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
/**
|
||||
* @param Command $command
|
||||
* @param bool $isAlias
|
||||
* @param string $fallbackPrefix
|
||||
* @param string $label
|
||||
* @param bool $isAlias
|
||||
* @param string $fallbackPrefix
|
||||
* @param string $label
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@ -258,14 +256,10 @@ class SimpleCommandMap implements CommandMap{
|
||||
$target->execute($sender, $sentCommandLabel, $args);
|
||||
}catch(InvalidCommandSyntaxException $e){
|
||||
$sender->sendMessage($this->server->getLanguage()->translateString("commands.generic.usage", [$target->getUsage()]));
|
||||
}catch(\Throwable $e){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception"));
|
||||
$this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.command.exception", [$commandLine, (string) $target, $e->getMessage()]));
|
||||
$sender->getServer()->getLogger()->logException($e);
|
||||
}finally{
|
||||
$target->timings->stopTiming();
|
||||
}
|
||||
|
||||
$target->timings->stopTiming();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -302,9 +296,9 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
|
||||
$targets = [];
|
||||
$bad = [];
|
||||
$recursive = [];
|
||||
|
||||
$bad = "";
|
||||
$recursive = "";
|
||||
foreach($commandStrings as $commandString){
|
||||
$args = explode(" ", $commandString);
|
||||
$commandName = "";
|
||||
@ -312,27 +306,21 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
|
||||
if($command === null){
|
||||
if(strlen($bad) > 0){
|
||||
$bad .= ", ";
|
||||
}
|
||||
$bad .= $commandString;
|
||||
$bad[] = $commandString;
|
||||
}elseif($commandName === $alias){
|
||||
if($recursive !== ""){
|
||||
$recursive .= ", ";
|
||||
}
|
||||
$recursive .= $commandString;
|
||||
$recursive[] = $commandString;
|
||||
}else{
|
||||
$targets[] = $commandString;
|
||||
}
|
||||
}
|
||||
|
||||
if($recursive !== ""){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, $recursive]));
|
||||
if(!empty($recursive)){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, implode(", ", $recursive)]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(strlen($bad) > 0){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, $bad]));
|
||||
if(!empty($bad)){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, implode(", ", $bad)]));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@ class PardonCommand extends VanillaCommand{
|
||||
parent::__construct(
|
||||
$name,
|
||||
"%pocketmine.command.unban.player.description",
|
||||
"%commands.unban.usage"
|
||||
"%commands.unban.usage",
|
||||
["unban"]
|
||||
);
|
||||
$this->setPermission("pocketmine.command.unban.player");
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ class PardonIpCommand extends VanillaCommand{
|
||||
parent::__construct(
|
||||
$name,
|
||||
"%pocketmine.command.unban.ip.description",
|
||||
"%commands.unbanip.usage"
|
||||
"%commands.unbanip.usage",
|
||||
["unban-ip"]
|
||||
);
|
||||
$this->setPermission("pocketmine.command.unban.ip");
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\particle\AngryVillagerParticle;
|
||||
use pocketmine\level\particle\BlockForceFieldParticle;
|
||||
use pocketmine\level\particle\BubbleParticle;
|
||||
@ -84,14 +85,18 @@ class ParticleCommand extends VanillaCommand{
|
||||
|
||||
if($sender instanceof Player){
|
||||
$level = $sender->getLevel();
|
||||
$pos = new Vector3(
|
||||
$this->getRelativeDouble($sender->getX(), $sender, $args[1]),
|
||||
$this->getRelativeDouble($sender->getY(), $sender, $args[2], 0, Level::Y_MAX),
|
||||
$this->getRelativeDouble($sender->getZ(), $sender, $args[3])
|
||||
);
|
||||
}else{
|
||||
$level = $sender->getServer()->getDefaultLevel();
|
||||
$pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]);
|
||||
}
|
||||
|
||||
$name = strtolower($args[0]);
|
||||
|
||||
$pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]);
|
||||
|
||||
$xd = (float) $args[4];
|
||||
$yd = (float) $args[5];
|
||||
$zd = (float) $args[6];
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class PluginsCommand extends VanillaCommand{
|
||||
@ -43,20 +44,12 @@ class PluginsCommand extends VanillaCommand{
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
$this->sendPluginList($sender);
|
||||
|
||||
$list = array_map(function(Plugin $plugin) : string{
|
||||
return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName();
|
||||
}, $sender->getServer()->getPluginManager()->getPlugins());
|
||||
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($list), implode(TextFormat::WHITE . ", ", $list)]));
|
||||
return true;
|
||||
}
|
||||
|
||||
private function sendPluginList(CommandSender $sender){
|
||||
$list = "";
|
||||
foreach(($plugins = $sender->getServer()->getPluginManager()->getPlugins()) as $plugin){
|
||||
if(strlen($list) > 0){
|
||||
$list .= TextFormat::WHITE . ", ";
|
||||
}
|
||||
$list .= $plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED;
|
||||
$list .= $plugin->getDescription()->getFullName();
|
||||
}
|
||||
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($plugins), $list]));
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class TeleportCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
$args = array_values(array_filter($args, function($arg){
|
||||
return strlen($arg) > 0;
|
||||
return $arg !== "";
|
||||
}));
|
||||
if(count($args) < 1 or count($args) > 6){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\Player;
|
||||
use pocketmine\scheduler\BulkCurlTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\InternetException;
|
||||
|
||||
class TimingsCommand extends VanillaCommand{
|
||||
|
||||
@ -131,7 +132,7 @@ class TimingsCommand extends VanillaCommand{
|
||||
return;
|
||||
}
|
||||
$result = $this->getResult()[0];
|
||||
if($result instanceof \RuntimeException){
|
||||
if($result instanceof InternetException){
|
||||
$server->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ class Attribute{
|
||||
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);
|
||||
self::addAttribute(self::EXHAUSTION, "minecraft:player.exhaustion", 0.00, 5.00, 0.0, false);
|
||||
self::addAttribute(self::KNOCKBACK_RESISTANCE, "minecraft:knockback_resistance", 0.00, 1.00, 0.00);
|
||||
self::addAttribute(self::HEALTH, "minecraft:health", 0.00, 20.00, 20.00);
|
||||
self::addAttribute(self::MOVEMENT_SPEED, "minecraft:movement", 0.00, 340282346638528859811704183484516925440.00, 0.10);
|
||||
@ -127,8 +127,8 @@ class Attribute{
|
||||
}
|
||||
|
||||
public function setMinValue(float $minValue){
|
||||
if($minValue > $this->getMaxValue()){
|
||||
throw new \InvalidArgumentException("Value $minValue is bigger than the maxValue!");
|
||||
if($minValue > ($max = $this->getMaxValue())){
|
||||
throw new \InvalidArgumentException("Minimum $minValue is greater than the maximum $max");
|
||||
}
|
||||
|
||||
if($this->minValue != $minValue){
|
||||
@ -143,8 +143,8 @@ class Attribute{
|
||||
}
|
||||
|
||||
public function setMaxValue(float $maxValue){
|
||||
if($maxValue < $this->getMinValue()){
|
||||
throw new \InvalidArgumentException("Value $maxValue is bigger than the minValue!");
|
||||
if($maxValue < ($min = $this->getMinValue())){
|
||||
throw new \InvalidArgumentException("Maximum $maxValue is less than the minimum $min");
|
||||
}
|
||||
|
||||
if($this->maxValue != $maxValue){
|
||||
@ -160,7 +160,7 @@ class Attribute{
|
||||
|
||||
public function setDefaultValue(float $defaultValue){
|
||||
if($defaultValue > $this->getMaxValue() or $defaultValue < $this->getMinValue()){
|
||||
throw new \InvalidArgumentException("Value $defaultValue exceeds the range!");
|
||||
throw new \InvalidArgumentException("Default $defaultValue is outside the range " . $this->getMinValue() . " - " . $this->getMaxValue());
|
||||
}
|
||||
|
||||
if($this->defaultValue !== $defaultValue){
|
||||
@ -171,7 +171,7 @@ class Attribute{
|
||||
}
|
||||
|
||||
public function resetToDefault() : void{
|
||||
$this->setValue($this->getDefaultValue());
|
||||
$this->setValue($this->getDefaultValue(), true);
|
||||
}
|
||||
|
||||
public function getValue() : float{
|
||||
@ -188,7 +188,7 @@ class Attribute{
|
||||
public function setValue(float $value, bool $fit = false, bool $forceSend = false){
|
||||
if($value > $this->getMaxValue() or $value < $this->getMinValue()){
|
||||
if(!$fit){
|
||||
throw new \InvalidArgumentException("Value $value exceeds the range!");
|
||||
throw new \InvalidArgumentException("Value $value is outside the range " . $this->getMinValue() . " - " . $this->getMaxValue());
|
||||
}
|
||||
$value = min(max($value, $this->getMinValue()), $this->getMaxValue());
|
||||
}
|
||||
|
@ -143,7 +143,8 @@ class DataPropertyManager{
|
||||
*/
|
||||
public function getItem(int $key) : ?Item{
|
||||
$value = $this->getPropertyValue($key, Entity::DATA_TYPE_SLOT);
|
||||
assert($value instanceof Item or $value === null);
|
||||
assert($value instanceof Item or $value === null);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@ class Effect{
|
||||
public const SATURATION = 23;
|
||||
public const LEVITATION = 24; //TODO
|
||||
public const FATAL_POISON = 25;
|
||||
public const CONDUIT_POWER = 26;
|
||||
|
||||
/** @var Effect[] */
|
||||
protected static $effects = [];
|
||||
@ -86,6 +87,7 @@ class Effect{
|
||||
self::registerEffect(new Effect(Effect::SATURATION, "%potion.saturation", new Color(0xf8, 0x24, 0x23), false, 1));
|
||||
self::registerEffect(new Effect(Effect::LEVITATION, "%potion.levitation", new Color(0xce, 0xff, 0xff)));
|
||||
self::registerEffect(new Effect(Effect::FATAL_POISON, "%potion.poison", new Color(0x4e, 0x93, 0x31), true));
|
||||
self::registerEffect(new Effect(Effect::CONDUIT_POWER, "%potion.conduitPower", new Color(0x1d, 0xc2, 0xd1)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,6 +81,7 @@ class EffectInstance{
|
||||
|
||||
/**
|
||||
* @param int $duration
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return $this
|
||||
|
@ -175,6 +175,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_SCORE_TAG = 83; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 84; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 85; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 86; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 87; //long
|
||||
/* 88 (float) related to panda sitting
|
||||
* 89 (float) related to panda sitting
|
||||
* 90 (unknown) */
|
||||
public const DATA_FLAGS2 = 91; //long (extended data flags)
|
||||
/* 92 (float) related to panda lying down
|
||||
* 93 (float) related to panda lying down */
|
||||
|
||||
|
||||
public const DATA_FLAG_ONFIRE = 0;
|
||||
@ -206,34 +214,48 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_FLAG_INTERESTED = 26;
|
||||
public const DATA_FLAG_CHARGED = 27;
|
||||
public const DATA_FLAG_TAMED = 28;
|
||||
public const DATA_FLAG_LEASHED = 29;
|
||||
public const DATA_FLAG_SHEARED = 30;
|
||||
public const DATA_FLAG_GLIDING = 31;
|
||||
public const DATA_FLAG_ELDER = 32;
|
||||
public const DATA_FLAG_MOVING = 33;
|
||||
public const DATA_FLAG_BREATHING = 34;
|
||||
public const DATA_FLAG_CHESTED = 35;
|
||||
public const DATA_FLAG_STACKABLE = 36;
|
||||
public const DATA_FLAG_SHOWBASE = 37;
|
||||
public const DATA_FLAG_REARING = 38;
|
||||
public const DATA_FLAG_VIBRATING = 39;
|
||||
public const DATA_FLAG_IDLING = 40;
|
||||
public const DATA_FLAG_EVOKER_SPELL = 41;
|
||||
public const DATA_FLAG_CHARGE_ATTACK = 42;
|
||||
public const DATA_FLAG_WASD_CONTROLLED = 43;
|
||||
public const DATA_FLAG_CAN_POWER_JUMP = 44;
|
||||
public const DATA_FLAG_LINGER = 45;
|
||||
public const DATA_FLAG_HAS_COLLISION = 46;
|
||||
public const DATA_FLAG_AFFECTED_BY_GRAVITY = 47;
|
||||
public const DATA_FLAG_FIRE_IMMUNE = 48;
|
||||
public const DATA_FLAG_DANCING = 49;
|
||||
public const DATA_FLAG_ENCHANTED = 50;
|
||||
public const DATA_FLAG_SHOW_TRIDENT_ROPE = 51; // tridents show an animated rope when enchanted with loyalty after they are thrown and return to their owner. To be combined with DATA_OWNER_EID
|
||||
public const DATA_FLAG_CONTAINER_PRIVATE = 52; //inventory is private, doesn't drop contents when killed if true
|
||||
//53 TransformationComponent
|
||||
public const DATA_FLAG_SPIN_ATTACK = 54;
|
||||
public const DATA_FLAG_SWIMMING = 55;
|
||||
public const DATA_FLAG_BRIBED = 56; //dolphins have this set when they go to find treasure for the player
|
||||
public const DATA_FLAG_ORPHANED = 29;
|
||||
public const DATA_FLAG_LEASHED = 30;
|
||||
public const DATA_FLAG_SHEARED = 31;
|
||||
public const DATA_FLAG_GLIDING = 32;
|
||||
public const DATA_FLAG_ELDER = 33;
|
||||
public const DATA_FLAG_MOVING = 34;
|
||||
public const DATA_FLAG_BREATHING = 35;
|
||||
public const DATA_FLAG_CHESTED = 36;
|
||||
public const DATA_FLAG_STACKABLE = 37;
|
||||
public const DATA_FLAG_SHOWBASE = 38;
|
||||
public const DATA_FLAG_REARING = 39;
|
||||
public const DATA_FLAG_VIBRATING = 40;
|
||||
public const DATA_FLAG_IDLING = 41;
|
||||
public const DATA_FLAG_EVOKER_SPELL = 42;
|
||||
public const DATA_FLAG_CHARGE_ATTACK = 43;
|
||||
public const DATA_FLAG_WASD_CONTROLLED = 44;
|
||||
public const DATA_FLAG_CAN_POWER_JUMP = 45;
|
||||
public const DATA_FLAG_LINGER = 46;
|
||||
public const DATA_FLAG_HAS_COLLISION = 47;
|
||||
public const DATA_FLAG_AFFECTED_BY_GRAVITY = 48;
|
||||
public const DATA_FLAG_FIRE_IMMUNE = 49;
|
||||
public const DATA_FLAG_DANCING = 50;
|
||||
public const DATA_FLAG_ENCHANTED = 51;
|
||||
public const DATA_FLAG_SHOW_TRIDENT_ROPE = 52; // tridents show an animated rope when enchanted with loyalty after they are thrown and return to their owner. To be combined with DATA_OWNER_EID
|
||||
public const DATA_FLAG_CONTAINER_PRIVATE = 53; //inventory is private, doesn't drop contents when killed if true
|
||||
public const DATA_FLAG_TRANSFORMING = 54;
|
||||
public const DATA_FLAG_SPIN_ATTACK = 55;
|
||||
public const DATA_FLAG_SWIMMING = 56;
|
||||
public const DATA_FLAG_BRIBED = 57; //dolphins have this set when they go to find treasure for the player
|
||||
public const DATA_FLAG_PREGNANT = 58;
|
||||
public const DATA_FLAG_LAYING_EGG = 59;
|
||||
public const DATA_FLAG_RIDER_CAN_PICK = 60; //???
|
||||
public const DATA_FLAG_TRANSITION_SITTING = 61;
|
||||
public const DATA_FLAG_EATING = 62;
|
||||
public const DATA_FLAG_LAYING_DOWN = 63;
|
||||
public const DATA_FLAG_SNEEZING = 64;
|
||||
public const DATA_FLAG_TRUSTING = 65;
|
||||
public const DATA_FLAG_ROLLING = 66;
|
||||
public const DATA_FLAG_SCARED = 67;
|
||||
public const DATA_FLAG_IN_SCAFFOLDING = 68;
|
||||
public const DATA_FLAG_OVER_SCAFFOLDING = 69;
|
||||
public const DATA_FLAG_FALL_THROUGH_SCAFFOLDING = 70;
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
@ -260,8 +282,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
Entity::registerEntity(Snowball::class, false, ['Snowball', 'minecraft:snowball']);
|
||||
Entity::registerEntity(SplashPotion::class, false, ['ThrownPotion', 'minecraft:potion', 'thrownpotion']);
|
||||
Entity::registerEntity(Squid::class, false, ['Squid', 'minecraft:squid']);
|
||||
Entity::registerEntity(Villager::class, false, ['Villager', 'minecraft:villager']);
|
||||
Entity::registerEntity(Zombie::class, false, ['Zombie', 'minecraft:zombie']);
|
||||
Entity::registerEntity(Villager::class, false, ['Villager', 'minecraft:villager']);
|
||||
Entity::registerEntity(Zombie::class, false, ['Zombie', 'minecraft:zombie']);
|
||||
|
||||
Entity::registerEntity(Human::class, true);
|
||||
|
||||
@ -285,6 +307,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public static function createEntity($type, Level $level, CompoundTag $nbt, ...$args) : ?Entity{
|
||||
if(isset(self::$knownEntities[$type])){
|
||||
$class = self::$knownEntities[$type];
|
||||
/** @see Entity::__construct() */
|
||||
return new $class($level, $nbt, ...$args);
|
||||
}
|
||||
|
||||
@ -341,7 +364,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*
|
||||
* @return CompoundTag
|
||||
*/
|
||||
public static function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null , float $yaw = 0.0, float $pitch = 0.0) : CompoundTag{
|
||||
public static function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null, float $yaw = 0.0, float $pitch = 0.0) : CompoundTag{
|
||||
return new CompoundTag("", [
|
||||
new ListTag("Pos", [
|
||||
new DoubleTag("", $pos->x),
|
||||
@ -407,8 +430,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public $boundingBox;
|
||||
/** @var bool */
|
||||
public $onGround;
|
||||
/** @var int */
|
||||
protected $age = 0;
|
||||
|
||||
/** @var float */
|
||||
public $eyeHeight = null;
|
||||
@ -513,7 +534,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
$this->recalculateBoundingBox();
|
||||
|
||||
$this->chunk = $this->level->getChunk($this->getFloorX() >> 4, $this->getFloorZ() >> 4, false);
|
||||
$this->chunk = $this->level->getChunkAtPosition($this, false);
|
||||
if($this->chunk === null){
|
||||
throw new \InvalidStateException("Cannot create entities in unloaded chunks");
|
||||
}
|
||||
@ -560,7 +581,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->level->addEntity($this);
|
||||
|
||||
$this->lastUpdate = $this->server->getTick();
|
||||
$this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
|
||||
(new EntitySpawnEvent($this))->call();
|
||||
|
||||
$this->scheduleUpdate();
|
||||
|
||||
@ -609,6 +630,20 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->propertyManager->setByte(self::DATA_ALWAYS_SHOW_NAMETAG, $value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScoreTag() : ?string{
|
||||
return $this->propertyManager->getString(self::DATA_SCORE_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $score
|
||||
*/
|
||||
public function setScoreTag(string $score) : void{
|
||||
$this->propertyManager->setString(self::DATA_SCORE_TAG, $score);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
@ -620,6 +655,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param float $value
|
||||
*/
|
||||
public function setScale(float $value) : void{
|
||||
if($value <= 0){
|
||||
throw new \InvalidArgumentException("Scale must be greater than 0");
|
||||
}
|
||||
$multiplier = $value / $this->getScale();
|
||||
|
||||
$this->width *= $multiplier;
|
||||
@ -734,7 +772,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public function getOwningEntity() : ?Entity{
|
||||
$eid = $this->getOwningEntityId();
|
||||
if($eid !== null){
|
||||
return $this->server->findEntity($eid, $this->level);
|
||||
return $this->server->findEntity($eid);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -774,7 +812,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public function getTargetEntity() : ?Entity{
|
||||
$eid = $this->getTargetEntityId();
|
||||
if($eid !== null){
|
||||
return $this->server->findEntity($eid, $this->level);
|
||||
return $this->server->findEntity($eid);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -888,7 +926,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param EntityDamageEvent $source
|
||||
*/
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
$source->call();
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
@ -902,7 +940,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param EntityRegainHealthEvent $source
|
||||
*/
|
||||
public function heal(EntityRegainHealthEvent $source) : void{
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
$source->call();
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
@ -919,6 +957,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* Called to tick entities while dead. Returns whether the entity should be flagged for despawn yet.
|
||||
*
|
||||
* @param int $tickDiff
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function onDeathUpdate(int $tickDiff) : bool{
|
||||
@ -1015,8 +1054,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->isOnFire()){
|
||||
$hasUpdate = ($hasUpdate || $this->doOnFireTick($tickDiff));
|
||||
if($this->isOnFire() and $this->doOnFireTick($tickDiff)){
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->noDamageTicks > 0){
|
||||
@ -1026,7 +1065,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
$this->age += $tickDiff;
|
||||
$this->ticksLived += $tickDiff;
|
||||
|
||||
return $hasUpdate;
|
||||
@ -1133,34 +1171,30 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
protected function broadcastMovement(bool $teleport = false) : void{
|
||||
if($this->chunk !== null){
|
||||
$pk = new MoveEntityAbsolutePacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->position = $this->getOffsetPosition($this);
|
||||
$pk = new MoveEntityAbsolutePacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->position = $this->getOffsetPosition($this);
|
||||
|
||||
//this looks very odd but is correct as of 1.5.0.7
|
||||
//for arrows this is actually x/y/z rotation
|
||||
//for mobs x and z are used for pitch and yaw, and y is used for headyaw
|
||||
$pk->xRot = $this->pitch;
|
||||
$pk->yRot = $this->yaw; //TODO: head yaw
|
||||
$pk->zRot = $this->yaw;
|
||||
//this looks very odd but is correct as of 1.5.0.7
|
||||
//for arrows this is actually x/y/z rotation
|
||||
//for mobs x and z are used for pitch and yaw, and y is used for headyaw
|
||||
$pk->xRot = $this->pitch;
|
||||
$pk->yRot = $this->yaw; //TODO: head yaw
|
||||
$pk->zRot = $this->yaw;
|
||||
|
||||
if($teleport){
|
||||
$pk->flags |= MoveEntityAbsolutePacket::FLAG_TELEPORT;
|
||||
}
|
||||
|
||||
$this->level->addChunkPacket($this->chunk->getX(), $this->chunk->getZ(), $pk);
|
||||
if($teleport){
|
||||
$pk->flags |= MoveEntityAbsolutePacket::FLAG_TELEPORT;
|
||||
}
|
||||
|
||||
$this->level->broadcastPacketToViewers($this, $pk);
|
||||
}
|
||||
|
||||
protected function broadcastMotion() : void{
|
||||
if($this->chunk !== null){
|
||||
$pk = new SetEntityMotionPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk = new SetEntityMotionPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->motion = $this->getMotion();
|
||||
|
||||
$this->level->addChunkPacket($this->chunk->getX(), $this->chunk->getZ(), $pk);
|
||||
}
|
||||
$this->level->broadcastPacketToViewers($this, $pk);
|
||||
}
|
||||
|
||||
protected function applyDragBeforeGravity() : bool{
|
||||
@ -1378,7 +1412,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
Timings::$timerEntityBaseTick->stopTiming();
|
||||
|
||||
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
||||
//if($this->isStatic())
|
||||
@ -1792,7 +1825,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->chunk = $this->level->getChunk($chunkX, $chunkZ, true);
|
||||
|
||||
if(!$this->justCreated){
|
||||
$newChunk = $this->level->getChunkPlayers($chunkX, $chunkZ);
|
||||
$newChunk = $this->level->getViewersForPosition($this);
|
||||
foreach($this->hasSpawned as $player){
|
||||
if(!isset($newChunk[$player->getLoaderId()])){
|
||||
$this->despawnFrom($player);
|
||||
@ -1825,7 +1858,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
public function setMotion(Vector3 $motion) : bool{
|
||||
if(!$this->justCreated){
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion));
|
||||
$ev = new EntityMotionEvent($this, $motion);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
@ -1858,7 +1892,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
$from = Position::fromObject($this, $this->level);
|
||||
$to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevel() : $this->level);
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityTeleportEvent($this, $from, $to));
|
||||
$ev = new EntityTeleportEvent($this, $from, $to);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
@ -1884,7 +1919,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
if($this->isValid()){
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->level, $targetLevel));
|
||||
$ev = new EntityLevelChangeEvent($this, $this->level, $targetLevel);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
@ -1937,7 +1973,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param 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())])){
|
||||
if(!isset($this->hasSpawned[$player->getLoaderId()])){
|
||||
$this->hasSpawned[$player->getLoaderId()] = $player;
|
||||
|
||||
$this->sendSpawnPacket($player);
|
||||
@ -1948,7 +1984,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
if($this->chunk === null or $this->closed){
|
||||
return;
|
||||
}
|
||||
foreach($this->level->getChunkPlayers($this->chunk->getX(), $this->chunk->getZ()) as $player){
|
||||
foreach($this->level->getViewersForPosition($this) as $player){
|
||||
if($player->isOnline()){
|
||||
$this->spawnTo($player);
|
||||
}
|
||||
@ -2010,7 +2046,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*/
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
$this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this));
|
||||
(new EntityDespawnEvent($this))->call();
|
||||
$this->closed = true;
|
||||
|
||||
$this->despawnFromAll();
|
||||
@ -2059,10 +2095,11 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* Wrapper around {@link Entity#getDataFlag} for generic data flag reading.
|
||||
*
|
||||
* @param int $flagId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getGenericFlag(int $flagId) : bool{
|
||||
return $this->getDataFlag(self::DATA_FLAGS, $flagId);
|
||||
return $this->getDataFlag($flagId >= 64 ? self::DATA_FLAGS2 : self::DATA_FLAGS, $flagId % 64);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2072,7 +2109,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setGenericFlag(int $flagId, bool $value = true) : void{
|
||||
$this->setDataFlag(self::DATA_FLAGS, $flagId, $value, self::DATA_TYPE_LONG);
|
||||
$this->setDataFlag($flagId >= 64 ? self::DATA_FLAGS2 : self::DATA_FLAGS, $flagId % 64, $value, self::DATA_TYPE_LONG);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,8 +71,9 @@ interface EntityIds{
|
||||
public const ENDER_DRAGON = 53;
|
||||
public const SHULKER = 54;
|
||||
public const ENDERMITE = 55;
|
||||
public const LEARN_TO_CODE_MASCOT = 56;
|
||||
public const AGENT = 56, LEARN_TO_CODE_MASCOT = 56;
|
||||
public const VINDICATOR = 57;
|
||||
public const PHANTOM = 58;
|
||||
|
||||
public const ARMOR_STAND = 61;
|
||||
public const TRIPOD_CAMERA = 62;
|
||||
@ -86,8 +87,9 @@ 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 THROWN_TRIDENT = 73, TRIDENT = 73;
|
||||
public const TURTLE = 74;
|
||||
public const CAT = 75;
|
||||
public const SHULKER_BULLET = 76;
|
||||
public const FISHING_HOOK = 77;
|
||||
public const CHALKBOARD = 78;
|
||||
@ -97,7 +99,7 @@ interface EntityIds{
|
||||
public const EGG = 82;
|
||||
public const PAINTING = 83;
|
||||
public const MINECART = 84;
|
||||
public const LARGE_FIREBALL = 85;
|
||||
public const FIREBALL = 85, LARGE_FIREBALL = 85;
|
||||
public const SPLASH_POTION = 86;
|
||||
public const ENDER_PEARL = 87;
|
||||
public const LEASH_KNOT = 88;
|
||||
@ -122,6 +124,7 @@ interface EntityIds{
|
||||
public const PUFFERFISH = 108;
|
||||
public const SALMON = 109;
|
||||
public const DROWNED = 110;
|
||||
public const TROPICAL_FISH = 111;
|
||||
public const FISH = 112;
|
||||
public const TROPICALFISH = 111, TROPICAL_FISH = 111;
|
||||
public const COD = 112, FISH = 112;
|
||||
public const PANDA = 113;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\inventory\EntityInventoryEventProcessor;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\inventory\PlayerInventory;
|
||||
use pocketmine\item\Consumable;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\item\FoodSource;
|
||||
use pocketmine\item\Item;
|
||||
@ -268,7 +269,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
* @return float the amount of exhaustion level increased
|
||||
*/
|
||||
public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerExhaustEvent($this, $amount, $cause));
|
||||
$ev = new PlayerExhaustEvent($this, $amount, $cause);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return 0.0;
|
||||
}
|
||||
@ -448,7 +450,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
private function playLevelUpSound(int $newLevel) : void{
|
||||
$volume = 0x10000000 * (min(30, $newLevel) / 5); //No idea why such odd numbers, but this works...
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_LEVELUP, 1, (int) $volume);
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_LEVELUP, (int) $volume);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -465,7 +467,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
protected function setXpAndProgress(?int $level, ?float $progress) : bool{
|
||||
if(!$this->justCreated){
|
||||
$ev = new PlayerExperienceChangeEvent($this, $this->getXpLevel(), $this->getXpProgress(), $level, $progress);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
@ -518,6 +520,42 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return $this->xpCooldown === 0;
|
||||
}
|
||||
|
||||
public function onPickupXp(int $xpValue) : void{
|
||||
static $mainHandIndex = -1;
|
||||
|
||||
//TODO: replace this with a more generic equipment getting/setting interface
|
||||
/** @var Durable[] $equipment */
|
||||
$equipment = [];
|
||||
|
||||
if(($item = $this->inventory->getItemInHand()) instanceof Durable and $item->hasEnchantment(Enchantment::MENDING)){
|
||||
$equipment[$mainHandIndex] = $item;
|
||||
}
|
||||
//TODO: check offhand
|
||||
foreach($this->armorInventory->getContents() as $k => $item){
|
||||
if($item instanceof Durable and $item->hasEnchantment(Enchantment::MENDING)){
|
||||
$equipment[$k] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($equipment)){
|
||||
$repairItem = $equipment[$k = array_rand($equipment)];
|
||||
if($repairItem->getDamage() > 0){
|
||||
$repairAmount = min($repairItem->getDamage(), $xpValue * 2);
|
||||
$repairItem->setDamage($repairItem->getDamage() - $repairAmount);
|
||||
$xpValue -= (int) ceil($repairAmount / 2);
|
||||
|
||||
if($k === $mainHandIndex){
|
||||
$this->inventory->setItemInHand($repairItem);
|
||||
}else{
|
||||
$this->armorInventory->setItem($k, $repairItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addXp($xpValue); //this will still get fired even if the value is 0 due to mending, to play sounds
|
||||
$this->resetXpCooldown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration in ticks until the human can pick up another XP orb.
|
||||
*
|
||||
@ -804,7 +842,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->getName(), 0, $this->skin)];
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)];
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
@ -851,6 +889,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
* Wrapper around {@link Entity#getDataFlag} for player-specific data flag reading.
|
||||
*
|
||||
* @param int $flagId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getPlayerFlag(int $flagId) : bool{
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\inventory\ArmorInventory;
|
||||
use pocketmine\inventory\ArmorInventoryEventProcessor;
|
||||
use pocketmine\item\Armor;
|
||||
use pocketmine\item\Consumable;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -204,7 +205,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
if(isset($this->effects[$effectId])){
|
||||
$effect = $this->effects[$effectId];
|
||||
$hasExpired = $effect->hasExpired();
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityEffectRemoveEvent($this, $effect));
|
||||
$ev = new EntityEffectRemoveEvent($this, $effect);
|
||||
$ev->call();
|
||||
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);
|
||||
@ -277,7 +279,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
$ev = new EntityEffectAddEvent($this, $effect, $oldEffect);
|
||||
$ev->setCancelled($cancelled);
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
@ -328,6 +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) : void{
|
||||
@ -476,6 +479,26 @@ abstract class Living extends Entity implements Damageable{
|
||||
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
|
||||
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
|
||||
$this->damageArmor($source->getBaseDamage());
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$damage = 0;
|
||||
foreach($this->armorInventory->getContents() as $k => $item){
|
||||
if($item instanceof Armor and ($thornsLevel = $item->getEnchantmentLevel(Enchantment::THORNS)) > 0){
|
||||
if(mt_rand(0, 99) < $thornsLevel * 15){
|
||||
$this->damageItem($item, 3);
|
||||
$damage += ($thornsLevel > 10 ? $thornsLevel - 10 : 1 + mt_rand(0, 3));
|
||||
}else{
|
||||
$this->damageItem($item, 1); //thorns causes an extra +1 durability loss even if it didn't activate
|
||||
}
|
||||
|
||||
$this->armorInventory->setItem($k, $item);
|
||||
}
|
||||
}
|
||||
|
||||
if($damage > 0){
|
||||
$source->getDamager()->attack(new EntityDamageByEntityEvent($this, $source->getDamager(), EntityDamageEvent::CAUSE_MAGIC, $damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -490,16 +513,20 @@ abstract class Living extends Entity implements Damageable{
|
||||
$armor = $this->armorInventory->getContents(true);
|
||||
foreach($armor as $item){
|
||||
if($item instanceof Armor){
|
||||
$item->applyDamage($durabilityRemoved);
|
||||
if($item->isBroken()){
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_BREAK);
|
||||
}
|
||||
$this->damageItem($item, $durabilityRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
$this->armorInventory->setContents($armor);
|
||||
}
|
||||
|
||||
private function damageItem(Durable $item, int $durabilityRemoved) : void{
|
||||
$item->applyDamage($durabilityRemoved);
|
||||
if($item->isBroken()){
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_BREAK);
|
||||
}
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
if($this->attackTime > 0 or $this->noDamageTicks > 0){
|
||||
$lastCause = $this->getLastDamageCause();
|
||||
@ -535,7 +562,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attackTime = 10; //0.5 seconds cooldown
|
||||
$this->attackTime = $source->getAttackCooldown();
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
@ -544,7 +571,10 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
if($e !== null){
|
||||
if($e->isOnFire()){
|
||||
if((
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_PROJECTILE or
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_ENTITY_ATTACK
|
||||
) and $e->isOnFire()){
|
||||
$this->setOnFire(2 * $this->level->getDifficulty());
|
||||
}
|
||||
|
||||
@ -596,7 +626,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
protected function onDeath() : void{
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops()));
|
||||
$ev = new EntityDeathEvent($this, $this->getDrops());
|
||||
$ev->call();
|
||||
foreach($ev->getDrops() as $item){
|
||||
$this->getLevel()->dropItem($this, $item);
|
||||
}
|
||||
@ -629,21 +660,19 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
$this->doEffectsTick($tickDiff);
|
||||
|
||||
if($this->isAlive()){
|
||||
if($this->doEffectsTick($tickDiff)){
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->isInsideOfSolid()){
|
||||
$hasUpdate = true;
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 1);
|
||||
$this->attack($ev);
|
||||
}
|
||||
|
||||
if(!$this->canBreathe()){
|
||||
$this->setBreathing(false);
|
||||
$this->doAirSupplyTick($tickDiff);
|
||||
}elseif(!$this->isBreathing()){
|
||||
$this->setBreathing(true);
|
||||
$this->setAirSupplyTicks($this->getMaxAirSupplyTicks());
|
||||
if($this->doAirSupplyTick($tickDiff)){
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -656,7 +685,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function doEffectsTick(int $tickDiff = 1) : void{
|
||||
protected function doEffectsTick(int $tickDiff = 1) : bool{
|
||||
foreach($this->effects as $instance){
|
||||
$type = $instance->getType();
|
||||
if($type->canTick($instance)){
|
||||
@ -667,25 +696,47 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->removeEffect($instance->getId());
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($this->effects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ticks the entity's air supply when it cannot breathe.
|
||||
* Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water.
|
||||
*
|
||||
* @param int $tickDiff
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doAirSupplyTick(int $tickDiff) : void{
|
||||
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or
|
||||
lcg_value() <= (1 / ($respirationLevel + 1))
|
||||
){
|
||||
$ticks = $this->getAirSupplyTicks() - $tickDiff;
|
||||
protected function doAirSupplyTick(int $tickDiff) : bool{
|
||||
$ticks = $this->getAirSupplyTicks();
|
||||
$oldTicks = $ticks;
|
||||
if(!$this->canBreathe()){
|
||||
$this->setBreathing(false);
|
||||
|
||||
if($ticks <= -20){
|
||||
$this->setAirSupplyTicks(0);
|
||||
$this->onAirExpired();
|
||||
}else{
|
||||
$this->setAirSupplyTicks($ticks);
|
||||
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or
|
||||
lcg_value() <= (1 / ($respirationLevel + 1))
|
||||
){
|
||||
$ticks -= $tickDiff;
|
||||
if($ticks <= -20){
|
||||
$ticks = 0;
|
||||
$this->onAirExpired();
|
||||
}
|
||||
}
|
||||
}elseif(!$this->isBreathing()){
|
||||
if($ticks < ($max = $this->getMaxAirSupplyTicks())){
|
||||
$ticks += $tickDiff * 5;
|
||||
}
|
||||
if($ticks >= $max){
|
||||
$ticks = $max;
|
||||
$this->setBreathing(true);
|
||||
}
|
||||
}
|
||||
|
||||
if($ticks !== $oldTicks){
|
||||
$this->setAirSupplyTicks($ticks);
|
||||
}
|
||||
|
||||
return $ticks !== $oldTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -693,7 +744,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
* @return bool
|
||||
*/
|
||||
public function canBreathe() : bool{
|
||||
return $this->hasEffect(Effect::WATER_BREATHING) or !$this->isUnderwater();
|
||||
return $this->hasEffect(Effect::WATER_BREATHING) or $this->hasEffect(Effect::CONDUIT_POWER) or !$this->isUnderwater();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -726,6 +777,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) : void{
|
||||
@ -742,6 +794,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) : void{
|
||||
|
@ -88,6 +88,9 @@ class ExperienceOrb extends Entity{
|
||||
public $gravity = 0.04;
|
||||
public $drag = 0.02;
|
||||
|
||||
/** @var int */
|
||||
protected $age = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* Ticker used for determining interval in which to look for new target players.
|
||||
@ -144,7 +147,7 @@ class ExperienceOrb extends Entity{
|
||||
return null;
|
||||
}
|
||||
|
||||
$entity = $this->server->findEntity($this->targetPlayerRuntimeId, $this->level);
|
||||
$entity = $this->server->findEntity($this->targetPlayerRuntimeId);
|
||||
if($entity instanceof Human){
|
||||
return $entity;
|
||||
}
|
||||
@ -159,6 +162,7 @@ class ExperienceOrb extends Entity{
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
$this->age += $tickDiff;
|
||||
if($this->age > 6000){
|
||||
$this->flagForDespawn();
|
||||
return true;
|
||||
@ -200,10 +204,7 @@ class ExperienceOrb extends Entity{
|
||||
if($currentTarget->canPickupXp() and $this->boundingBox->intersectsWith($currentTarget->getBoundingBox())){
|
||||
$this->flagForDespawn();
|
||||
|
||||
$currentTarget->addXp($this->getXpValue());
|
||||
$currentTarget->resetXpCooldown();
|
||||
|
||||
//TODO: check Mending enchantment
|
||||
$currentTarget->onPickupXp($this->getXpValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ class FallingBlock extends Entity{
|
||||
|
||||
$this->block = BlockFactory::get($blockId, $damage);
|
||||
|
||||
$this->propertyManager->setInt(self::DATA_VARIANT, BlockFactory::toStaticRuntimeId($this->block->getId(), $this->block->getDamage()));
|
||||
$this->propertyManager->setInt(self::DATA_VARIANT, $this->block->getRuntimeId());
|
||||
}
|
||||
|
||||
public function canCollideWith(Entity $entity) : bool{
|
||||
@ -113,7 +113,8 @@ class FallingBlock extends Entity{
|
||||
//FIXME: anvils are supposed to destroy torches
|
||||
$this->getLevel()->dropItem($this, ItemFactory::get($this->getBlock(), $this->getDamage()));
|
||||
}else{
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityBlockChangeEvent($this, $block, $blockTarget ?? $this->block));
|
||||
$ev = new EntityBlockChangeEvent($this, $block, $blockTarget ?? $this->block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($pos, $ev->getTo(), true);
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ class ItemEntity extends Entity{
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
/** @var int */
|
||||
protected $age = 0;
|
||||
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
@ -70,9 +73,12 @@ class ItemEntity extends Entity{
|
||||
}
|
||||
|
||||
$this->item = Item::nbtDeserialize($itemTag);
|
||||
if($this->item->isNull()){
|
||||
throw new \UnexpectedValueException("Item for " . get_class($this) . " is invalid");
|
||||
}
|
||||
|
||||
|
||||
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
|
||||
(new ItemSpawnEvent($this))->call();
|
||||
}
|
||||
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
@ -82,16 +88,16 @@ class ItemEntity extends Entity{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if(!$this->isFlaggedForDespawn()){
|
||||
if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay
|
||||
$this->pickupDelay -= $tickDiff;
|
||||
if($this->pickupDelay < 0){
|
||||
$this->pickupDelay = 0;
|
||||
}
|
||||
if(!$this->isFlaggedForDespawn() and $this->pickupDelay > -1 and $this->pickupDelay < 32767){ //Infinite delay
|
||||
$this->pickupDelay -= $tickDiff;
|
||||
if($this->pickupDelay < 0){
|
||||
$this->pickupDelay = 0;
|
||||
}
|
||||
|
||||
$this->age += $tickDiff;
|
||||
if($this->age > 6000){
|
||||
$this->server->getPluginManager()->callEvent($ev = new ItemDespawnEvent($this));
|
||||
$ev = new ItemDespawnEvent($this);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->age = 0;
|
||||
}else{
|
||||
@ -99,7 +105,6 @@ class ItemEntity extends Entity{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
@ -204,11 +209,12 @@ class ItemEntity extends Entity{
|
||||
$item = $this->getItem();
|
||||
$playerInventory = $player->getInventory();
|
||||
|
||||
if(!($item instanceof Item) or ($player->isSurvival() and !$playerInventory->canAddItem($item))){
|
||||
if($player->isSurvival() and !$playerInventory->canAddItem($item)){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($playerInventory, $this));
|
||||
$ev = new InventoryPickupItemEvent($playerInventory, $this);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ class Painting extends Entity{
|
||||
|
||||
$this->namedtag->setByte("Facing", (int) $this->direction);
|
||||
$this->namedtag->setByte("Direction", (int) $this->direction); //Save both for full compatibility
|
||||
|
||||
$this->namedtag->setString("Motive", $this->motive);
|
||||
}
|
||||
|
||||
public function kill() : void{
|
||||
|
@ -73,6 +73,7 @@ class PaintingMotive{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return PaintingMotive|null
|
||||
*/
|
||||
public static function getMotiveByName(string $name) : ?PaintingMotive{
|
||||
|
@ -102,8 +102,8 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
}
|
||||
|
||||
public function explode() : void{
|
||||
$this->server->getPluginManager()->callEvent($ev = new ExplosionPrimeEvent($this, 4));
|
||||
|
||||
$ev = new ExplosionPrimeEvent($this, 4);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$explosion = new Explosion($this, $ev->getForce(), $this);
|
||||
if($ev->isBlockBreaking()){
|
||||
|
@ -40,19 +40,49 @@ use pocketmine\Player;
|
||||
class Arrow extends Projectile{
|
||||
public const NETWORK_ID = self::ARROW;
|
||||
|
||||
public const PICKUP_NONE = 0;
|
||||
public const PICKUP_ANY = 1;
|
||||
public const PICKUP_CREATIVE = 2;
|
||||
|
||||
private const TAG_PICKUP = "pickup"; //TAG_Byte
|
||||
|
||||
public $width = 0.25;
|
||||
public $height = 0.25;
|
||||
|
||||
protected $gravity = 0.05;
|
||||
protected $drag = 0.01;
|
||||
|
||||
protected $damage = 2;
|
||||
/** @var float */
|
||||
protected $damage = 2.0;
|
||||
|
||||
/** @var int */
|
||||
protected $pickupMode = self::PICKUP_ANY;
|
||||
|
||||
/** @var float */
|
||||
protected $punchKnockback = 0.0;
|
||||
|
||||
/** @var int */
|
||||
protected $collideTicks = 0;
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){
|
||||
parent::__construct($level, $nbt, $shootingEntity);
|
||||
$this->setCritical($critical);
|
||||
}
|
||||
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
$this->pickupMode = $this->namedtag->getByte(self::TAG_PICKUP, self::PICKUP_ANY, true);
|
||||
$this->collideTicks = $this->namedtag->getShort("life", $this->collideTicks);
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setByte(self::TAG_PICKUP, $this->pickupMode, true);
|
||||
$this->namedtag->setShort("life", $this->collideTicks);
|
||||
}
|
||||
|
||||
public function isCritical() : bool{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_CRITICAL);
|
||||
}
|
||||
@ -70,6 +100,20 @@ class Arrow extends Projectile{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getPunchKnockback() : float{
|
||||
return $this->punchKnockback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $punchKnockback
|
||||
*/
|
||||
public function setPunchKnockback(float $punchKnockback) : void{
|
||||
$this->punchKnockback = $punchKnockback;
|
||||
}
|
||||
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
if($this->closed){
|
||||
return false;
|
||||
@ -77,9 +121,14 @@ class Arrow extends Projectile{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if($this->age > 1200){
|
||||
$this->flagForDespawn();
|
||||
$hasUpdate = true;
|
||||
if($this->isCollided){
|
||||
$this->collideTicks += $tickDiff;
|
||||
if($this->collideTicks > 1200){
|
||||
$this->flagForDespawn();
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}else{
|
||||
$this->collideTicks = 0;
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
@ -95,6 +144,31 @@ class Arrow extends Projectile{
|
||||
$this->broadcastEntityEvent(EntityEventPacket::ARROW_SHAKE, 7); //7 ticks
|
||||
}
|
||||
|
||||
protected function onHitEntity(Entity $entityHit, RayTraceResult $hitResult) : void{
|
||||
parent::onHitEntity($entityHit, $hitResult);
|
||||
if($this->punchKnockback > 0){
|
||||
$horizontalSpeed = sqrt($this->motion->x ** 2 + $this->motion->z ** 2);
|
||||
if($horizontalSpeed > 0){
|
||||
$multiplier = $this->punchKnockback * 0.6 / $horizontalSpeed;
|
||||
$entityHit->setMotion($entityHit->getMotion()->add($this->motion->x * $multiplier, 0.1, $this->motion->z * $multiplier));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPickupMode() : int{
|
||||
return $this->pickupMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pickupMode
|
||||
*/
|
||||
public function setPickupMode(int $pickupMode) : void{
|
||||
$this->pickupMode = $pickupMode;
|
||||
}
|
||||
|
||||
public function onCollideWithPlayer(Player $player) : void{
|
||||
if($this->blockHit === null){
|
||||
return;
|
||||
@ -107,7 +181,12 @@ class Arrow extends Projectile{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupArrowEvent($playerInventory, $this));
|
||||
$ev = new InventoryPickupArrowEvent($playerInventory, $this);
|
||||
if($this->pickupMode === self::PICKUP_NONE or ($this->pickupMode === self::PICKUP_CREATIVE and !$player->isCreative())){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
@ -66,7 +66,5 @@ class EnderPearl extends Throwable{
|
||||
|
||||
$owner->attack(new EntityDamageEvent($owner, EntityDamageEvent::CAUSE_FALL, 5));
|
||||
}
|
||||
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,5 @@ class ExperienceBottle extends Throwable{
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_GLASS);
|
||||
|
||||
$this->level->dropExperience($this, mt_rand(3, 11));
|
||||
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ abstract class Projectile extends Entity{
|
||||
|
||||
public const DATA_SHOOTER_ID = 17;
|
||||
|
||||
protected $damage = 0;
|
||||
/** @var float */
|
||||
protected $damage = 0.0;
|
||||
|
||||
/** @var Vector3|null */
|
||||
protected $blockHit;
|
||||
@ -73,7 +74,7 @@ abstract class Projectile extends Entity{
|
||||
|
||||
$this->setMaxHealth(1);
|
||||
$this->setHealth(1);
|
||||
$this->age = $this->namedtag->getShort("Age", $this->age);
|
||||
$this->damage = $this->namedtag->getDouble("damage", $this->damage);
|
||||
|
||||
do{
|
||||
$blockHit = null;
|
||||
@ -112,6 +113,25 @@ abstract class Projectile extends Entity{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base damage applied on collision. This is multiplied by the projectile's speed to give a result
|
||||
* damage.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getBaseDamage() : float{
|
||||
return $this->damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base amount of damage applied by the projectile.
|
||||
*
|
||||
* @param float $damage
|
||||
*/
|
||||
public function setBaseDamage(float $damage) : void{
|
||||
$this->damage = $damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of damage this projectile will deal to the entity it hits.
|
||||
* @return int
|
||||
@ -123,7 +143,7 @@ abstract class Projectile extends Entity{
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setShort("Age", $this->age);
|
||||
$this->namedtag->setDouble("damage", $this->damage);
|
||||
|
||||
if($this->blockHit !== null){
|
||||
$this->namedtag->setInt("tileX", $this->blockHit->x);
|
||||
@ -140,17 +160,19 @@ abstract class Projectile extends Entity{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasMovementUpdate() : bool{
|
||||
$parent = parent::hasMovementUpdate();
|
||||
if($parent and $this->blockHit !== null){
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->blockHit !== null){
|
||||
$blockIn = $this->level->getBlockAt($this->blockHit->x, $this->blockHit->y, $this->blockHit->z);
|
||||
|
||||
if($blockIn->getId() === $this->blockHitId and $blockIn->getDamage() === $this->blockHitData){
|
||||
return false;
|
||||
if($blockIn->getId() !== $this->blockHitId or $blockIn->getDamage() !== $this->blockHitData){
|
||||
$this->blockHit = $this->blockHitId = $this->blockHitData = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $parent;
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
|
||||
public function hasMovementUpdate() : bool{
|
||||
return $this->blockHit === null and parent::hasMovementUpdate();
|
||||
}
|
||||
|
||||
public function move(float $dx, float $dy, float $dz) : void{
|
||||
@ -219,7 +241,7 @@ abstract class Projectile extends Entity{
|
||||
}
|
||||
|
||||
if($ev !== null){
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
$this->onHit($ev);
|
||||
|
||||
if($ev instanceof ProjectileHitEntityEvent){
|
||||
@ -293,7 +315,7 @@ abstract class Projectile extends Entity{
|
||||
|
||||
if($this->fireTicks > 0){
|
||||
$ev = new EntityCombustByEntityEvent($this, $entityHit, 5);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$entityHit->setOnFire($ev->getDuration());
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\entity\EffectInstance;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\event\entity\ProjectileHitEntityEvent;
|
||||
use pocketmine\event\entity\ProjectileHitBlockEvent;
|
||||
use pocketmine\event\entity\ProjectileHitEntityEvent;
|
||||
use pocketmine\event\entity\ProjectileHitEvent;
|
||||
use pocketmine\item\Potion;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
@ -124,8 +124,6 @@ class SplashPotion extends Throwable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,6 +151,7 @@ class SplashPotion extends Throwable{
|
||||
|
||||
/**
|
||||
* Sets whether this splash potion will create an area-effect-cloud when it lands.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setLinger(bool $value = true) : void{
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity\projectile;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
|
||||
abstract class Throwable extends Projectile{
|
||||
|
||||
public $width = 0.25;
|
||||
@ -31,18 +34,8 @@ abstract class Throwable extends Projectile{
|
||||
protected $gravity = 0.03;
|
||||
protected $drag = 0.01;
|
||||
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
if($this->closed){
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if($this->age > 1200 or $this->isCollided){
|
||||
$this->flagForDespawn();
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{
|
||||
parent::onHitBlock($blockHit, $hitResult);
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ abstract class ExperienceUtils{
|
||||
* Calculates and returns the amount of XP needed to get from level 0 to level $level
|
||||
*
|
||||
* @param int $level
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getXpToReachLevel(int $level) : int{
|
||||
|
@ -27,6 +27,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\event;
|
||||
|
||||
abstract class Event{
|
||||
private const MAX_EVENT_CALL_DEPTH = 50;
|
||||
/** @var int */
|
||||
private static $eventCallDepth = 1;
|
||||
|
||||
/** @var string|null */
|
||||
protected $eventName = null;
|
||||
@ -47,7 +50,7 @@ abstract class Event{
|
||||
*/
|
||||
public function isCancelled() : bool{
|
||||
if(!($this instanceof Cancellable)){
|
||||
throw new \BadMethodCallException("Event is not Cancellable");
|
||||
throw new \BadMethodCallException(get_class($this) . " is not Cancellable");
|
||||
}
|
||||
|
||||
/** @var Event $this */
|
||||
@ -61,10 +64,43 @@ abstract class Event{
|
||||
*/
|
||||
public function setCancelled(bool $value = true) : void{
|
||||
if(!($this instanceof Cancellable)){
|
||||
throw new \BadMethodCallException("Event is not Cancellable");
|
||||
throw new \BadMethodCallException(get_class($this) . " is not Cancellable");
|
||||
}
|
||||
|
||||
/** @var Event $this */
|
||||
$this->isCancelled = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls event handlers registered for this event.
|
||||
*
|
||||
* @throws \RuntimeException if event call recursion reaches the max depth limit
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function call() : void{
|
||||
if(self::$eventCallDepth >= self::MAX_EVENT_CALL_DEPTH){
|
||||
//this exception will be caught by the parent event call if all else fails
|
||||
throw new \RuntimeException("Recursive event call detected (reached max depth of " . self::MAX_EVENT_CALL_DEPTH . " calls)");
|
||||
}
|
||||
|
||||
$handlerList = HandlerList::getHandlerListFor(get_class($this));
|
||||
assert($handlerList !== null, "Called event should have a valid HandlerList");
|
||||
|
||||
++self::$eventCallDepth;
|
||||
try{
|
||||
foreach(EventPriority::ALL as $priority){
|
||||
$currentList = $handlerList;
|
||||
while($currentList !== null){
|
||||
foreach($currentList->getListenersByPriority($priority) as $registration){
|
||||
$registration->callEvent($this);
|
||||
}
|
||||
|
||||
$currentList = $currentList->getParent();
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
--self::$eventCallDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,37 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
|
||||
use pocketmine\plugin\PluginManager;
|
||||
|
||||
/**
|
||||
* Classes implementing this interface can be registered to receive called Events.
|
||||
* @see PluginManager::registerEvents()
|
||||
*
|
||||
* A function in a Listener class must meet the following criteria to be registered as an event handler:
|
||||
*
|
||||
* - MUST be public
|
||||
* - MUST NOT be static
|
||||
* - MUST accept EXACTLY ONE class parameter which:
|
||||
* - MUST be a VALID class extending Event
|
||||
* - MUST NOT be abstract, UNLESS it has an `@allowHandle` annotation
|
||||
*
|
||||
* Event handlers do not have to have any particular name - they are detected using reflection.
|
||||
* They SHOULD NOT return any values (but this is not currently enforced).
|
||||
*
|
||||
* Functions which meet the criteria can have the following annotations in their doc comments:
|
||||
*
|
||||
* - `@notHandler`: Marks a function as NOT being an event handler. Only needed if the function meets the above criteria.
|
||||
* - `@softDepend [PluginName]`: Handler WILL NOT be registered if its event doesn't exist. Useful for soft-depending
|
||||
* on plugin events. Plugin name is optional.
|
||||
* Example: `@softDepend SimpleAuth`
|
||||
* - `@ignoreCancelled`: Cancelled events WILL NOT be passed to this handler.
|
||||
* - `@priority <PRIORITY>`: Sets the priority at which this event handler will receive events.
|
||||
* Example: `@priority HIGHEST`
|
||||
* @see EventPriority for a list of possible options.
|
||||
*
|
||||
* Event handlers will receive any instanceof the Event class they have chosen to receive. For example, an
|
||||
* EntityDamageEvent handler will also receive any subclass of EntityDamageEvent.
|
||||
*/
|
||||
interface Listener{
|
||||
|
||||
}
|
||||
|
@ -51,6 +51,6 @@ class EntityDamageByChildEntityEvent extends EntityDamageByEntityEvent{
|
||||
* @return Entity|null
|
||||
*/
|
||||
public function getChild() : ?Entity{
|
||||
return $this->getEntity()->getLevel()->getServer()->findEntity($this->childEntityEid, $this->getEntity()->getLevel());
|
||||
return $this->getEntity()->getLevel()->getServer()->findEntity($this->childEntityEid);
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
|
||||
* @return Entity|null
|
||||
*/
|
||||
public function getDamager() : ?Entity{
|
||||
return $this->getEntity()->getLevel()->getServer()->findEntity($this->damagerEntityId, $this->getEntity()->getLevel());
|
||||
return $this->getEntity()->getLevel()->getServer()->findEntity($this->damagerEntityId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,6 +38,7 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
public const MODIFIER_ARMOR_ENCHANTMENTS = 6;
|
||||
public const MODIFIER_CRITICAL = 7;
|
||||
public const MODIFIER_TOTEM = 8;
|
||||
public const MODIFIER_WEAPON_ENCHANTMENTS = 9;
|
||||
|
||||
public const CAUSE_CONTACT = 0;
|
||||
public const CAUSE_ENTITY_ATTACK = 1;
|
||||
@ -68,6 +69,9 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
/** @var float[] */
|
||||
private $originals;
|
||||
|
||||
/** @var int */
|
||||
private $attackCooldown = 10;
|
||||
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
@ -196,4 +200,24 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cooldown in ticks before the target entity can be attacked again.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAttackCooldown() : int{
|
||||
return $this->attackCooldown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cooldown in ticks before the target entity can be attacked again.
|
||||
*
|
||||
* NOTE: This value is not used in non-Living entities
|
||||
*
|
||||
* @param int $attackCooldown
|
||||
*/
|
||||
public function setAttackCooldown(int $attackCooldown) : void{
|
||||
$this->attackCooldown = $attackCooldown;
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ class FurnaceBurnEvent extends BlockEvent implements Cancellable{
|
||||
|
||||
/**
|
||||
* @param Furnace $furnace
|
||||
* @param Item $fuel
|
||||
* @param int $burnTime
|
||||
* @param Item $fuel
|
||||
* @param int $burnTime
|
||||
*/
|
||||
public function __construct(Furnace $furnace, Item $fuel, int $burnTime){
|
||||
parent::__construct($furnace->getBlock());
|
||||
|
@ -38,8 +38,8 @@ class FurnaceSmeltEvent extends BlockEvent implements Cancellable{
|
||||
|
||||
/**
|
||||
* @param Furnace $furnace
|
||||
* @param Item $source
|
||||
* @param Item $result
|
||||
* @param Item $source
|
||||
* @param Item $result
|
||||
*/
|
||||
public function __construct(Furnace $furnace, Item $source, Item $result){
|
||||
parent::__construct($furnace->getBlock());
|
||||
|
@ -63,6 +63,7 @@ class PlayerChangeSkinEvent extends PlayerEvent implements Cancellable{
|
||||
|
||||
/**
|
||||
* @param Skin $skin
|
||||
*
|
||||
* @throws \InvalidArgumentException if the specified skin is not valid
|
||||
*/
|
||||
public function setNewSkin(Skin $skin) : void{
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
@ -55,7 +56,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
$this->format = $format;
|
||||
|
||||
if($recipients === null){
|
||||
$this->recipients = Server::getInstance()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS);
|
||||
$this->recipients = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS);
|
||||
}else{
|
||||
$this->recipients = $recipients;
|
||||
}
|
||||
|
@ -23,9 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityDeathEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\lang\TextContainer;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\Player;
|
||||
|
||||
class PlayerDeathEvent extends EntityDeathEvent{
|
||||
@ -37,13 +43,13 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
private $keepInventory = false;
|
||||
|
||||
/**
|
||||
* @param Player $entity
|
||||
* @param Item[] $drops
|
||||
* @param string|TextContainer $deathMessage
|
||||
* @param Player $entity
|
||||
* @param Item[] $drops
|
||||
* @param string|TextContainer|null $deathMessage Null will cause the default vanilla message to be used
|
||||
*/
|
||||
public function __construct(Player $entity, array $drops, $deathMessage){
|
||||
public function __construct(Player $entity, array $drops, $deathMessage = null){
|
||||
parent::__construct($entity, $drops);
|
||||
$this->deathMessage = $deathMessage;
|
||||
$this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,4 +87,123 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
public function setKeepInventory(bool $keepInventory) : void{
|
||||
$this->keepInventory = $keepInventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vanilla death message for the given death cause.
|
||||
*
|
||||
* @param string $name
|
||||
* @param null|EntityDamageEvent $deathCause
|
||||
*
|
||||
* @return TranslationContainer
|
||||
*/
|
||||
public static function deriveMessage(string $name, ?EntityDamageEvent $deathCause) : TranslationContainer{
|
||||
$message = "death.attack.generic";
|
||||
$params = [$name];
|
||||
|
||||
switch($deathCause === null ? EntityDamageEvent::CAUSE_CUSTOM : $deathCause->getCause()){
|
||||
case EntityDamageEvent::CAUSE_ENTITY_ATTACK:
|
||||
if($deathCause instanceof EntityDamageByEntityEvent){
|
||||
$e = $deathCause->getDamager();
|
||||
if($e instanceof Player){
|
||||
$message = "death.attack.player";
|
||||
$params[] = $e->getDisplayName();
|
||||
break;
|
||||
}elseif($e instanceof Living){
|
||||
$message = "death.attack.mob";
|
||||
$params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName();
|
||||
break;
|
||||
}else{
|
||||
$params[] = "Unknown";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EntityDamageEvent::CAUSE_PROJECTILE:
|
||||
if($deathCause instanceof EntityDamageByEntityEvent){
|
||||
$e = $deathCause->getDamager();
|
||||
if($e instanceof Player){
|
||||
$message = "death.attack.arrow";
|
||||
$params[] = $e->getDisplayName();
|
||||
}elseif($e instanceof Living){
|
||||
$message = "death.attack.arrow";
|
||||
$params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName();
|
||||
break;
|
||||
}else{
|
||||
$params[] = "Unknown";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EntityDamageEvent::CAUSE_SUICIDE:
|
||||
$message = "death.attack.generic";
|
||||
break;
|
||||
case EntityDamageEvent::CAUSE_VOID:
|
||||
$message = "death.attack.outOfWorld";
|
||||
break;
|
||||
case EntityDamageEvent::CAUSE_FALL:
|
||||
if($deathCause instanceof EntityDamageEvent){
|
||||
if($deathCause->getFinalDamage() > 2){
|
||||
$message = "death.fell.accident.generic";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$message = "death.attack.fall";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_SUFFOCATION:
|
||||
$message = "death.attack.inWall";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_LAVA:
|
||||
$message = "death.attack.lava";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_FIRE:
|
||||
$message = "death.attack.onFire";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_FIRE_TICK:
|
||||
$message = "death.attack.inFire";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_DROWNING:
|
||||
$message = "death.attack.drown";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_CONTACT:
|
||||
if($deathCause instanceof EntityDamageByBlockEvent){
|
||||
if($deathCause->getDamager()->getId() === Block::CACTUS){
|
||||
$message = "death.attack.cactus";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_BLOCK_EXPLOSION:
|
||||
case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION:
|
||||
if($deathCause instanceof EntityDamageByEntityEvent){
|
||||
$e = $deathCause->getDamager();
|
||||
if($e instanceof Player){
|
||||
$message = "death.attack.explosion.player";
|
||||
$params[] = $e->getDisplayName();
|
||||
}elseif($e instanceof Living){
|
||||
$message = "death.attack.explosion.player";
|
||||
$params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName();
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
$message = "death.attack.explosion";
|
||||
}
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_MAGIC:
|
||||
$message = "death.attack.magic";
|
||||
break;
|
||||
|
||||
case EntityDamageEvent::CAUSE_CUSTOM:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new TranslationContainer($message, $params);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,11 @@ use pocketmine\lang\TextContainer;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when a player joins the server, after sending all the spawn packets
|
||||
* Called when the player spawns in the world after logging in, when they first see the terrain.
|
||||
*
|
||||
* Note: A lot of data is sent to the player between login and this event. Disconnecting the player during this event
|
||||
* will cause this data to be wasted. Prefer disconnecting at login-time if possible to minimize bandwidth wastage.
|
||||
* @see PlayerLoginEvent
|
||||
*/
|
||||
class PlayerJoinEvent extends PlayerEvent{
|
||||
/** @var string|TextContainer */
|
||||
|
@ -50,6 +50,13 @@ class PlayerKickEvent extends PlayerEvent implements Cancellable{
|
||||
$this->reason = $reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $reason
|
||||
*/
|
||||
public function setReason(string $reason) : void{
|
||||
$this->reason = $reason;
|
||||
}
|
||||
|
||||
public function getReason() : string{
|
||||
return $this->reason;
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ use pocketmine\event\Cancellable;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when a player joins, after things have been correctly set up (you can change anything now)
|
||||
* Called after the player has successfully authenticated, before it spawns. The player is on the loading screen when
|
||||
* this is called.
|
||||
* Cancelling this event will cause the player to be disconnected with the kick message set.
|
||||
*/
|
||||
class PlayerLoginEvent extends PlayerEvent implements Cancellable{
|
||||
/** @var string */
|
||||
|
@ -34,7 +34,7 @@ class PlayerMoveEvent extends PlayerEvent implements Cancellable{
|
||||
private $to;
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
* @param Player $player
|
||||
* @param Location $from
|
||||
* @param Location $to
|
||||
*/
|
||||
|
@ -27,7 +27,17 @@ use pocketmine\event\Cancellable;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when the player logs in, before things have been set up
|
||||
* Called when a player connects to the server, prior to authentication taking place.
|
||||
* Cancelling this event will cause the player to be disconnected with the kick message set.
|
||||
*
|
||||
* This event should be used to decide if the player may continue to login to the server. Do things like checking
|
||||
* bans, whitelisting, server-full etc here.
|
||||
*
|
||||
* WARNING: Any information about the player CANNOT be trusted at this stage, because they are not authenticated and
|
||||
* could be a hacker posing as another player.
|
||||
*
|
||||
* WARNING: Due to internal bad architecture, the player is not fully constructed at this stage, and errors might occur
|
||||
* when calling API methods on the player. Tread with caution.
|
||||
*/
|
||||
class PlayerPreLoginEvent extends PlayerEvent implements Cancellable{
|
||||
/** @var string */
|
||||
|
73
src/pocketmine/event/server/CommandEvent.php
Normal file
73
src/pocketmine/event/server/CommandEvent.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\server;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\event\Cancellable;
|
||||
|
||||
/**
|
||||
* Called when any CommandSender runs a command, early in the process
|
||||
*
|
||||
* You don't want to use this except for a few cases like logging commands,
|
||||
* blocking commands on certain places, or applying modifiers.
|
||||
*
|
||||
* The message DOES NOT contain a slash at the start
|
||||
*/
|
||||
class CommandEvent extends ServerEvent implements Cancellable{
|
||||
/** @var string */
|
||||
protected $command;
|
||||
|
||||
/** @var CommandSender */
|
||||
protected $sender;
|
||||
|
||||
/**
|
||||
* @param CommandSender $sender
|
||||
* @param string $command
|
||||
*/
|
||||
public function __construct(CommandSender $sender, string $command){
|
||||
$this->sender = $sender;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommandSender
|
||||
*/
|
||||
public function getSender() : CommandSender{
|
||||
return $this->sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCommand() : string{
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $command
|
||||
*/
|
||||
public function setCommand(string $command) : void{
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ class DataPacketSendEvent extends ServerEvent implements Cancellable{
|
||||
private $player;
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
* @param Player $player
|
||||
* @param DataPacket $packet
|
||||
*/
|
||||
public function __construct(Player $player, DataPacket $packet){
|
||||
|
@ -26,7 +26,8 @@ namespace pocketmine\event\server;
|
||||
use pocketmine\network\SourceInterface;
|
||||
|
||||
/**
|
||||
* Called when a network interface crashes, with relevant crash information.
|
||||
* Never called. Should never have come into this world. Nothing to see here.
|
||||
* @deprecated
|
||||
*/
|
||||
class NetworkInterfaceCrashEvent extends NetworkInterfaceEvent{
|
||||
/**
|
||||
|
@ -27,6 +27,8 @@ use pocketmine\command\CommandSender;
|
||||
|
||||
/**
|
||||
* This event is called when a command is received over RCON.
|
||||
*
|
||||
* @deprecated Use CommandEvent instead.
|
||||
*/
|
||||
class RemoteServerCommandEvent extends ServerCommandEvent{
|
||||
|
||||
|
@ -33,6 +33,8 @@ use pocketmine\event\Cancellable;
|
||||
* blocking commands on certain places, or applying modifiers.
|
||||
*
|
||||
* The message DOES NOT contain a slash at the start
|
||||
*
|
||||
* @deprecated Use CommandEvent instead.
|
||||
*/
|
||||
class ServerCommandEvent extends ServerEvent implements Cancellable{
|
||||
/** @var string */
|
||||
|
43
src/pocketmine/form/Form.php
Normal file
43
src/pocketmine/form/Form.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\form;
|
||||
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Form implementations must implement this interface to be able to utilize the Player form-sending mechanism.
|
||||
* There is no restriction on custom implementations other than that they must implement this.
|
||||
*/
|
||||
interface Form extends \JsonSerializable{
|
||||
|
||||
/**
|
||||
* Handles a form response from a player.
|
||||
*
|
||||
* @param Player $player
|
||||
* @param mixed $data
|
||||
*
|
||||
* @throws FormValidationException if the data could not be processed
|
||||
*/
|
||||
public function handleResponse(Player $player, $data) : void;
|
||||
}
|
28
src/pocketmine/form/FormValidationException.php
Normal file
28
src/pocketmine/form/FormValidationException.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\form;
|
||||
|
||||
class FormValidationException extends \RuntimeException{
|
||||
|
||||
}
|
@ -26,7 +26,6 @@ namespace pocketmine\inventory;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityArmorChangeEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Server;
|
||||
|
||||
class ArmorInventoryEventProcessor implements InventoryEventProcessor{
|
||||
/** @var Entity */
|
||||
@ -37,7 +36,8 @@ class ArmorInventoryEventProcessor implements InventoryEventProcessor{
|
||||
}
|
||||
|
||||
public function onSlotChange(Inventory $inventory, int $slot, Item $oldItem, Item $newItem) : ?Item{
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->entity, $oldItem, $newItem, $slot));
|
||||
$ev = new EntityArmorChangeEvent($this->entity, $oldItem, $newItem, $slot);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return null;
|
||||
}
|
||||
|
@ -384,6 +384,7 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
/**
|
||||
* Removes the inventory window from all players currently viewing it.
|
||||
*
|
||||
* @param bool $force Force removal of permanent windows such as the player's own inventory. Used internally.
|
||||
*/
|
||||
public function removeAllViewers(bool $force = false) : void{
|
||||
@ -398,7 +399,8 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
public function open(Player $who) : bool{
|
||||
$who->getServer()->getPluginManager()->callEvent($ev = new InventoryOpenEvent($this, $who));
|
||||
$ev = new InventoryOpenEvent($this, $who);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
|
@ -61,19 +61,29 @@ class ChestInventory extends ContainerInventory{
|
||||
return $this->holder;
|
||||
}
|
||||
|
||||
protected function getOpenSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_CHEST_OPEN;
|
||||
}
|
||||
|
||||
protected function getCloseSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_CHEST_CLOSED;
|
||||
}
|
||||
|
||||
public function onOpen(Player $who) : void{
|
||||
parent::onOpen($who);
|
||||
|
||||
if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){
|
||||
//TODO: this crap really shouldn't be managed by the inventory
|
||||
$this->broadcastBlockEventPacket(true);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_CHEST_OPEN);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getOpenSound());
|
||||
}
|
||||
}
|
||||
|
||||
public function onClose(Player $who) : void{
|
||||
if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){
|
||||
//TODO: this crap really shouldn't be managed by the inventory
|
||||
$this->broadcastBlockEventPacket(false);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_CHEST_CLOSED);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getCloseSound());
|
||||
}
|
||||
parent::onClose($who);
|
||||
}
|
||||
@ -87,6 +97,6 @@ class ChestInventory extends ContainerInventory{
|
||||
$pk->z = (int) $holder->z;
|
||||
$pk->eventType = 1; //it's always 1 for a chest
|
||||
$pk->eventData = $isOpen ? 1 : 0;
|
||||
$holder->getLevel()->addChunkPacket($holder->getFloorX() >> 4, $holder->getFloorZ() >> 4, $pk);
|
||||
$holder->getLevel()->broadcastPacketToViewers($holder, $pk);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\WindowTypes;
|
||||
use pocketmine\tile\EnderChest;
|
||||
|
||||
@ -58,6 +59,14 @@ class EnderChestInventory extends ChestInventory{
|
||||
$this->holder->setLevel($enderChest->getLevel());
|
||||
}
|
||||
|
||||
protected function getOpenSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_ENDERCHEST_OPEN;
|
||||
}
|
||||
|
||||
protected function getCloseSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_ENDERCHEST_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* This override is here for documentation and code completion purposes only.
|
||||
* @return Position
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user