mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
581 Commits
4.0.0-BETA
...
4.2.4
Author | SHA1 | Date | |
---|---|---|---|
05a5e5eac1 | |||
c8e1cfcbee | |||
869dda9a45 | |||
e2c647ef91 | |||
05fdd94754 | |||
4a599b58ff | |||
05792826bc | |||
fbf7ad4295 | |||
6c1c0c867e | |||
5c0eb92d81 | |||
1e88412a8f | |||
f97ce6afef | |||
879476d8e0 | |||
6d584cf008 | |||
8efa299c65 | |||
b6e7ad187a | |||
5ef73ca9aa | |||
c50518a4ca | |||
4c98780bdb | |||
856fd2a33b | |||
581bbfe255 | |||
b4e1edaa64 | |||
025f6407e2 | |||
10c0d83fa5 | |||
9a6ec14cbf | |||
f77fec0c3c | |||
5d970cf5bd | |||
58e186440b | |||
6a39caa204 | |||
91f81d4c8e | |||
dfd8c4e4b8 | |||
c9c50e16ec | |||
3e90c3072a | |||
12946fbe46 | |||
7cd394b0fb | |||
0bca098707 | |||
d47a7f48bd | |||
f181c60209 | |||
784c34f784 | |||
61265604fb | |||
29909e7f44 | |||
3232a83965 | |||
c816bbdb6e | |||
4f25ab10e9 | |||
f04099c5de | |||
fdb82f5fb8 | |||
75d4c47384 | |||
bd4c2b5245 | |||
d60dba2de0 | |||
51a3043dfd | |||
7098bcec8c | |||
6d65512531 | |||
b26b1cd32f | |||
54db842d16 | |||
31a0085efb | |||
715355e148 | |||
55dfacea8d | |||
55c744cc00 | |||
7e903fde5b | |||
4f44a067b0 | |||
44818e6d14 | |||
325131dd30 | |||
1eb59fb9b5 | |||
1c60aa9769 | |||
e9dd9df0a0 | |||
032b15efe0 | |||
256826d9c7 | |||
c273b29dec | |||
7ddd547190 | |||
38e34093cf | |||
dd1ebb5915 | |||
df1cdbe921 | |||
7846ea8acc | |||
1dc0d5f96a | |||
712ffb3e31 | |||
2a4111868c | |||
123701ed76 | |||
28dce8783f | |||
3781b62d35 | |||
859f062267 | |||
cfdbfa3d58 | |||
e9a6c0ba58 | |||
d16b6fe61e | |||
7a75fcda44 | |||
8d289ab01d | |||
40c7497efe | |||
6ccb1ff114 | |||
1d2593208a | |||
a7bdef69e2 | |||
d9ea647925 | |||
6673289c33 | |||
822af4f7f5 | |||
3155c90396 | |||
1dbfedce4c | |||
3ab5b5a79d | |||
8a4bc72b34 | |||
6cbc14f2b2 | |||
75d0fc4749 | |||
ea161af4e5 | |||
0bf5f97fe9 | |||
b9f1bcf0e4 | |||
32b07e0940 | |||
99f087e5e1 | |||
22a4117109 | |||
aaf7a88de7 | |||
e0da99a973 | |||
b2630a0920 | |||
67a0ae0246 | |||
5ae20459dd | |||
587da478a6 | |||
419bb9eba6 | |||
82f1c2766c | |||
b3fec3d86f | |||
60ef2db892 | |||
e21446e583 | |||
7bf0bc2ca7 | |||
e5a9123522 | |||
09201ac14b | |||
0697c7d316 | |||
1eae133118 | |||
d28be4eaf2 | |||
b33a75a6d1 | |||
f9c8c0e34d | |||
58ba4f680f | |||
8c5cc67e07 | |||
ab8b24bcd2 | |||
94c4f58667 | |||
c10eda5eae | |||
ed312863a7 | |||
387c13beff | |||
56fe71d939 | |||
73d8c87b76 | |||
fc53f3721a | |||
345ac75aac | |||
32db27af78 | |||
4e956d5d1d | |||
61f8144280 | |||
ae03c70dfc | |||
e986a0a4f2 | |||
b85fe0e72a | |||
03f47d0a78 | |||
3f8f5cd200 | |||
aa6bd4438a | |||
22fb02c4e6 | |||
373880e582 | |||
8f525ab399 | |||
be1996752a | |||
2bcb629d78 | |||
aae5962f6a | |||
282b430b1f | |||
c47dfa1fb8 | |||
8db137882c | |||
79d1feff9c | |||
2f32bd877a | |||
22bc3bc3f9 | |||
6846f1e78a | |||
4d55935bd8 | |||
9c328690f8 | |||
b60dd1e9b4 | |||
86bcc49972 | |||
061d851fbd | |||
a67aef0477 | |||
088745cf3b | |||
8cdfef7861 | |||
a0bb7059c1 | |||
858024afb7 | |||
eaaf00ca2b | |||
f1723acfd3 | |||
8da27ea0aa | |||
388622d55d | |||
bac6a2a1eb | |||
b9b76eaed2 | |||
9f4fcfafdb | |||
853ecd2408 | |||
33421258b6 | |||
c221484fc3 | |||
d9deb571ed | |||
42d07c74d7 | |||
1366c49f1f | |||
6679c53e56 | |||
ee6548aa50 | |||
9d061e86af | |||
f7d25f251e | |||
0ccb47fb07 | |||
0973472842 | |||
f126479c37 | |||
d34f4b28b3 | |||
8a65fd273a | |||
248cc0ef49 | |||
d1726aa20c | |||
58e1e7bd6f | |||
a5c0958adf | |||
fd880d8465 | |||
a323fb7bb5 | |||
0a5b146189 | |||
1948b00008 | |||
b4e1871899 | |||
78eaa0993d | |||
bee2aba813 | |||
af81f80cf3 | |||
dbbbc4f9c9 | |||
51f2a78dcf | |||
5128bc02bb | |||
4f4aa62479 | |||
c267e7b3c2 | |||
3faeb5a556 | |||
0bc578b8fc | |||
661848c5e7 | |||
75fc7a2d1f | |||
43c5d08042 | |||
6d249026cc | |||
ed2145b6a4 | |||
3e6c157217 | |||
4f8a0bad25 | |||
fb29653ed7 | |||
ffa8cf3ec3 | |||
86beeb8255 | |||
230a3c9839 | |||
35f205b476 | |||
e7d17eb4d3 | |||
73168a0e39 | |||
e8893dd91f | |||
a4af1609ea | |||
8c4b8a9042 | |||
6492cac5c1 | |||
958a9dbf0f | |||
3ed57ce49a | |||
68f3399cfd | |||
aeab19a616 | |||
8532e9c8e0 | |||
7bee72ef2d | |||
0d595e4324 | |||
e43e0189df | |||
decd1da2d0 | |||
bcc0f1e733 | |||
e04dfe96af | |||
f62cfe8ae3 | |||
b903e90dc2 | |||
c8247786d7 | |||
f486b5f4a7 | |||
54d6b83fc2 | |||
eedea38669 | |||
3c6146b5e0 | |||
72f2c794ab | |||
193a1b3f4e | |||
62afa2f28d | |||
207f7ec309 | |||
e0a6bc1d4a | |||
5c994e4a24 | |||
d94578a420 | |||
0a0de018a5 | |||
a1d217e12b | |||
e102339637 | |||
7124d44b92 | |||
38b6b39cb3 | |||
767dfd9947 | |||
fcc4757209 | |||
d9c70cb176 | |||
4aab0565c0 | |||
87170ab067 | |||
74ac0f5862 | |||
f5144d49b1 | |||
8943d8a2a7 | |||
0da29beb1d | |||
157048264c | |||
95b6cb21f2 | |||
c858c0dc79 | |||
b55aa78aec | |||
091673d8f1 | |||
18e26d975b | |||
d41f933e7b | |||
65dabefa3b | |||
44e8603a6d | |||
e3614d1a82 | |||
16fd5456aa | |||
93caf72f34 | |||
089f22d903 | |||
fc3a6c6984 | |||
1ab285f573 | |||
aa56c66a3c | |||
920462bdcc | |||
e6e1bca676 | |||
795ebd1824 | |||
5f03887b47 | |||
9979a64ad2 | |||
75a72786f9 | |||
3d205c6e5f | |||
2955a92837 | |||
e70f81a111 | |||
482bc462d3 | |||
de82424fb2 | |||
d487e43766 | |||
57e1509c3a | |||
6494375a53 | |||
4466166f8b | |||
0da1810aaa | |||
3aa34b59a5 | |||
c04b00d09d | |||
6e67c7532a | |||
5f8ebd81d7 | |||
79b5109953 | |||
4d37b79ff7 | |||
60938c8c9d | |||
49a8afd126 | |||
dbad5dd611 | |||
ea1fceece2 | |||
7fb1669c6d | |||
a41404bd8a | |||
4b06fe73f2 | |||
929abb04be | |||
a09817864b | |||
45c4a9673d | |||
4ad8cb02a5 | |||
1c6907c636 | |||
7e6bbcc393 | |||
7184c02bb6 | |||
8a94aa10a4 | |||
c334e6dec7 | |||
89a766b799 | |||
7e99e5167c | |||
f5bbd30dbb | |||
3be8472ae2 | |||
22bb1ce8e0 | |||
178dcb71a9 | |||
0a58fd5472 | |||
e06eefeab0 | |||
ede07c4314 | |||
cba00bf1e2 | |||
e81bee3866 | |||
e6b85988b2 | |||
b50591303b | |||
9e75c1463a | |||
a94b88424e | |||
448f26cefc | |||
fa48100da5 | |||
bcf8a3424c | |||
69d5bfa0d4 | |||
549fb923bf | |||
6d5c463bdd | |||
911ad344c9 | |||
3b77462935 | |||
6b40ed7bf8 | |||
1ed9302f5a | |||
b3dab0beef | |||
7ad1afee89 | |||
292827a311 | |||
f8ed23cc1e | |||
6ddaed97fa | |||
036b90d247 | |||
d909cd8a91 | |||
06eaf9f273 | |||
1e56ed2ea3 | |||
dccb8a3595 | |||
0ace807756 | |||
40895a86e5 | |||
b081394125 | |||
f48cf68cac | |||
264cff70ec | |||
3aabfa4ab0 | |||
922ce2e312 | |||
0793e7e094 | |||
7a385ddc8b | |||
2254f31bec | |||
77a74d84e2 | |||
5b868e6d5e | |||
889d048ca3 | |||
8b73549355 | |||
c6466a6da9 | |||
3d9e19546f | |||
45b4fa0e96 | |||
bf29409a45 | |||
503c888838 | |||
e0eeb87ea0 | |||
78ffad5ffc | |||
1d14c8cb6b | |||
49d0d01f9f | |||
ed4978c31b | |||
3728ddbf24 | |||
5a351d3b17 | |||
0c012ca5d9 | |||
0530cb72df | |||
8f2ca92f02 | |||
ce54d268f2 | |||
cd850b111d | |||
ee060f3e02 | |||
e7deffa9af | |||
6e4b73c183 | |||
62f150586f | |||
8ed9551ac9 | |||
2486dabd8a | |||
4d2d0f1d35 | |||
4f3a60ac90 | |||
98c31cf07b | |||
9256afd439 | |||
cac9db9bcc | |||
300d194185 | |||
13340a21d3 | |||
27f599793a | |||
527e975fa9 | |||
8e37f86480 | |||
8e8cee45b8 | |||
1a046c6cd5 | |||
e61aaaccca | |||
1b86355c40 | |||
1669d33f7e | |||
2da65c5a6e | |||
468faa464b | |||
59de045ecb | |||
bd8308cc6f | |||
edc3bae172 | |||
06e7030817 | |||
cb0af44ccb | |||
d535f02096 | |||
7665f4f443 | |||
20d6b69813 | |||
6b7d0307af | |||
baeac2eb07 | |||
2850ea1e89 | |||
d560cf17fc | |||
3f6efd0018 | |||
aea124af74 | |||
8620e67d88 | |||
d5f81fe261 | |||
0aeac3af7d | |||
9931c1d50a | |||
d21a3d8750 | |||
6d62b06ce6 | |||
8be92d16fe | |||
8079ae341a | |||
ba295dc7dc | |||
38325c8573 | |||
f239b077b9 | |||
6f8f460a6c | |||
882df94bcb | |||
4a8ca603a1 | |||
52f0c4f3ed | |||
e2815eed60 | |||
932a88764c | |||
9540193766 | |||
cc23e0b7a1 | |||
1f9400f901 | |||
e5149756a8 | |||
bc18969a09 | |||
c19174a174 | |||
f95142f6b6 | |||
7ace24caab | |||
32f619ac49 | |||
1bb6ac4fb6 | |||
533d3aae8b | |||
52a891ba73 | |||
71b813d4f9 | |||
f2540a72ad | |||
03f13495b7 | |||
7e0f6c02a1 | |||
1bc7869f6e | |||
5556861000 | |||
7dd5d0b593 | |||
9338d42742 | |||
9346ecdc39 | |||
c023c02b6c | |||
bb7683158f | |||
fad96b77ce | |||
40575a6dcf | |||
40f8f042da | |||
0fe6038c41 | |||
adff561483 | |||
ad56392d95 | |||
472ffb28ff | |||
726c5652f7 | |||
b784a04e08 | |||
5c7125f190 | |||
eb0cf52d81 | |||
d8f0fd0a7e | |||
fb0eebc0dc | |||
020cd7b966 | |||
c37c261c0f | |||
2bb97d8904 | |||
d3878b2d57 | |||
cbe0f44c4f | |||
37622e02b8 | |||
ed8b4950a3 | |||
fc7d297f60 | |||
7b4ef293bd | |||
c72d66f370 | |||
3683884b9c | |||
37e8b1ee8c | |||
046dafc34f | |||
db135788b9 | |||
b34e6f53eb | |||
b4b954cc5f | |||
7210db25b0 | |||
4599913034 | |||
c48aa274e7 | |||
269231c228 | |||
4cad552909 | |||
f2d5455c5e | |||
65247b7248 | |||
2f408708f0 | |||
3dd03075cb | |||
82b5bca83e | |||
639867a640 | |||
d4a382d568 | |||
399824c31c | |||
ada469bc45 | |||
dc8243f88b | |||
7668171c56 | |||
e4754ab029 | |||
3276047497 | |||
49a8eff11e | |||
73592349cd | |||
635a9143de | |||
c3ec9c0948 | |||
09a2e006a8 | |||
fed59d3ebe | |||
c7beb0a702 | |||
5be429a8c4 | |||
ab002ca06d | |||
6efb1db107 | |||
6fdcfb01c8 | |||
1beec348f9 | |||
7306a2d939 | |||
4bf338f783 | |||
255ff63fda | |||
d72f6a3ac6 | |||
93a1e84ad9 | |||
c33f97ae41 | |||
cc4bb91fcb | |||
eb9012401b | |||
3b34268ed6 | |||
4c07078586 | |||
19a3efe893 | |||
a1ecdc27e5 | |||
f93b5be789 | |||
1fb60b5b3a | |||
08420c2556 | |||
18f5fb66bb | |||
a6f6b60bed | |||
c6c992a1f0 | |||
df39a1ca07 | |||
be6d1843de | |||
2b0b9bd8ed | |||
76dad46e13 | |||
eb3530b6e6 | |||
4131bcef08 | |||
b84f7c18ec | |||
45edb94607 | |||
6b316dc29a | |||
d9d37f7fa6 | |||
4cb6c7dc1e | |||
b392651354 | |||
f81c55ce6c | |||
002feacf8e | |||
b8523f7a18 | |||
640e88009b | |||
6cd272c9e1 | |||
566c57bcd3 | |||
3c754b079c | |||
dbf9a33160 | |||
07b1cff306 | |||
0989c77037 | |||
5107d0df4e | |||
579ef63663 | |||
8abc952c74 | |||
4c3a5fdd73 | |||
54f287feb6 | |||
84f8b3eb2d | |||
15fca84f3b | |||
c60144210f | |||
8ac999cbd4 | |||
4f8501ff34 | |||
2405e45b35 | |||
e0b07ff308 | |||
729f831b8f | |||
0356716e8e | |||
5c81b04813 | |||
1ebb206762 | |||
29e2d92098 | |||
ef82a2cd79 | |||
87031627bf | |||
f066199971 | |||
a0e9eec652 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -4,6 +4,7 @@
|
||||
*.sh text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.neon text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
*.ps1 text eol=crlf
|
||||
|
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@ -6,3 +6,8 @@ updates:
|
||||
interval: daily
|
||||
time: "10:00"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
|
11
.github/workflows/draft-release.yml
vendored
11
.github/workflows/draft-release.yml
vendored
@ -35,17 +35,18 @@ jobs:
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
|
||||
|
||||
- name: Patch VersionInfo
|
||||
- name: Calculate build number
|
||||
id: build-number
|
||||
run: |
|
||||
BUILD_NUMBER=2000+$GITHUB_RUN_NUMBER #to stay above jenkins
|
||||
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
|
||||
echo "Build number: $BUILD_NUMBER"
|
||||
sed -i "s/const BUILD_NUMBER = 0/const BUILD_NUMBER = ${BUILD_NUMBER}/" src/VersionInfo.php
|
||||
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
|
||||
|
||||
- name: Minify BedrockData JSON files
|
||||
run: php vendor/pocketmine/bedrock-data/.minify_json.php
|
||||
|
||||
- name: Build PocketMine-MP.phar
|
||||
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }}
|
||||
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }} --build ${{ steps.build-number.outputs.BUILD_NUMBER }}
|
||||
|
||||
- name: Get PocketMine-MP release version
|
||||
id: get-pm-version
|
||||
@ -56,7 +57,7 @@ jobs:
|
||||
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
|
||||
|
||||
- name: Generate build info
|
||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} > build_info.json
|
||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} > build_info.json
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
|
114
.github/workflows/main.yml
vendored
114
.github/workflows/main.yml
vendored
@ -13,20 +13,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2 #needed for build.sh
|
||||
- name: Check for PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
- name: Build and prepare PHP cache
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Compile PHP
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: ./tests/gh-actions/build.sh "${{ matrix.php }}"
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
|
||||
phpstan:
|
||||
name: PHPStan analysis
|
||||
@ -37,28 +31,16 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
@ -87,30 +69,16 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
@ -139,30 +107,18 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
@ -191,30 +147,16 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
@ -259,4 +201,4 @@ jobs:
|
||||
tools: php-cs-fixer:3.2
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff
|
||||
run: php-cs-fixer fix --dry-run --diff --ansi
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "resources/locale"]
|
||||
path = resources/locale
|
||||
url = https://github.com/pmmp/Language.git
|
||||
[submodule "tests/plugins/DevTools"]
|
||||
path = tests/plugins/DevTools
|
||||
url = https://github.com/pmmp/DevTools.git
|
||||
|
@ -18,6 +18,9 @@ return (new PhpCsFixer\Config)
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short'
|
||||
],
|
||||
'binary_operator_spaces' => [
|
||||
'default' => 'single_space'
|
||||
],
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_statement' => [
|
||||
@ -33,12 +36,14 @@ return (new PhpCsFixer\Config)
|
||||
],
|
||||
'declare_strict_types' => true,
|
||||
'elseif' => true,
|
||||
'fully_qualified_strict_types' => true,
|
||||
'global_namespace_import' => [
|
||||
'import_constants' => true,
|
||||
'import_functions' => true,
|
||||
'import_classes' => null,
|
||||
],
|
||||
'indentation_type' => true,
|
||||
'logical_operators' => true,
|
||||
'native_function_invocation' => [
|
||||
'scope' => 'namespaced',
|
||||
'include' => ['@all'],
|
||||
@ -68,8 +73,13 @@ return (new PhpCsFixer\Config)
|
||||
],
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_trim_consecutive_blank_line_separation' => true,
|
||||
'return_type_declaration' => [
|
||||
'space_before' => 'one'
|
||||
],
|
||||
'single_blank_line_at_eof' => true,
|
||||
'single_import_per_statement' => true,
|
||||
'strict_param' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
])
|
||||
->setFinder($finder)
|
||||
->setIndent("\t")
|
||||
|
@ -14,13 +14,12 @@ Because PocketMine-MP requires several non-standard PHP extensions and configura
|
||||
If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`.
|
||||
|
||||
## Setting up environment
|
||||
1. `git clone --recursive https://github.com/pmmp/PocketMine-MP.git`
|
||||
1. `git clone https://github.com/pmmp/PocketMine-MP.git`
|
||||
2. `composer install`
|
||||
|
||||
## Checking out a different branch to build
|
||||
1. `git checkout <branch to checkout>`
|
||||
2. `git submodule update --init`
|
||||
3. Re-run `composer install` to synchronize dependencies.
|
||||
2. Re-run `composer install` to synchronize dependencies.
|
||||
|
||||
## Optimizing for release builds
|
||||
1. Add the flags `--no-dev --classmap-authoritative` to your `composer install` command. This will reduce build size and improve autoloading speed.
|
||||
|
10
README.md
10
README.md
@ -4,10 +4,13 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" />
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases"><img src="https://img.shields.io/github/v/tag/pmmp/PocketMine-MP?label=release&logo=github" alt="GitHub tag (latest semver)" /></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/actions/workflows/main.yml"><img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" /></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver"></a>
|
||||
<a href="https://hub.docker.com/r/pmmp/pocketmine-mp"><img src="https://img.shields.io/docker/v/pmmp/pocketmine-mp?logo=docker&label=image" alt="Docker image version (latest semver)" /></a>
|
||||
<a href="https://discord.gg/bmSAZBG"><img src="https://img.shields.io/discord/373199722573201408?label=discord&color=7289DA&logo=discord" alt="Discord" /></a>
|
||||
<br>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases"><img alt="GitHub all releases" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/total?label=downloads%40total"></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest by SemVer)" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/latest/total?sort=semver"></a>
|
||||
</p>
|
||||
|
||||
## Getting started
|
||||
@ -24,7 +27,8 @@
|
||||
## For developers
|
||||
* [Building and running from source](BUILDING.md)
|
||||
* [Developer documentation](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers
|
||||
* [Latest API documentation](https://jenkins.pmmp.io/job/PocketMine-MP-doc/doxygen/) - Doxygen documentation generated from development
|
||||
* [Latest release API documentation](https://apidoc.pmmp.io) - Doxygen API documentation generated for each release
|
||||
* [Latest bleeding-edge API documentation](https://apidoc-dev.pmmp.io) - Doxygen API documentation generated weekly from `next-major` branch
|
||||
* [DevTools](https://github.com/pmmp/DevTools/) - Development tools plugin for creating plugins
|
||||
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
* [Contributing Guidelines](CONTRIBUTING.md)
|
||||
|
@ -23,15 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 4){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)>");
|
||||
if(count($argv) !== 5){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number>");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
|
||||
"base_version" => \pocketmine\VersionInfo::BASE_VERSION,
|
||||
"build" => \pocketmine\VersionInfo::BUILD_NUMBER,
|
||||
"build" => (int) $argv[4],
|
||||
"is_dev" => \pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"channel" => \pocketmine\VersionInfo::BUILD_CHANNEL,
|
||||
"git_commit" => $argv[1],
|
||||
@ -40,4 +40,4 @@ echo json_encode([
|
||||
"details_url" => "https://github.com/$argv[3]/releases/tag/$argv[2]",
|
||||
"download_url" => "https://github.com/$argv[3]/releases/download/$argv[2]/PocketMine-MP.phar",
|
||||
"source_url" => "https://github.com/$argv[3]/tree/$argv[2]",
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\build\generate_known_translation_apis;
|
||||
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\utils\Utils;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function array_map;
|
||||
use function count;
|
||||
@ -40,6 +41,7 @@ use function preg_match_all;
|
||||
use function str_replace;
|
||||
use function strtoupper;
|
||||
use const INI_SCANNER_RAW;
|
||||
use const SORT_NUMERIC;
|
||||
use const SORT_STRING;
|
||||
use const STDERR;
|
||||
|
||||
@ -94,13 +96,15 @@ function generate_known_translation_keys(array $languageDefinitions) : void{
|
||||
/**
|
||||
* This class contains constants for all the translations known to PocketMine-MP as per the used version of pmmp/Language.
|
||||
* This class is generated automatically, do NOT modify it by hand.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class KnownTranslationKeys{
|
||||
|
||||
HEADER;
|
||||
|
||||
ksort($languageDefinitions, SORT_STRING);
|
||||
foreach($languageDefinitions as $k => $_){
|
||||
foreach(Utils::stringifyKeys($languageDefinitions) as $k => $_){
|
||||
echo "\tpublic const ";
|
||||
echo constantify($k);
|
||||
echo " = \"" . $k . "\";\n";
|
||||
@ -126,6 +130,8 @@ function generate_known_translation_factory(array $languageDefinitions) : void{
|
||||
* This class contains factory methods for all the translations known to PocketMine-MP as per the used version of
|
||||
* pmmp/Language.
|
||||
* This class is generated automatically, do NOT modify it by hand.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class KnownTranslationFactory{
|
||||
|
||||
@ -135,17 +141,22 @@ HEADER;
|
||||
$parameterRegex = '/{%(.+?)}/';
|
||||
|
||||
$translationContainerClass = (new \ReflectionClass(Translatable::class))->getShortName();
|
||||
foreach($languageDefinitions as $key => $value){
|
||||
foreach(Utils::stringifyKeys($languageDefinitions) as $key => $value){
|
||||
$parameters = [];
|
||||
$allParametersPositional = true;
|
||||
if(preg_match_all($parameterRegex, $value, $matches) > 0){
|
||||
foreach($matches[1] as $parameterName){
|
||||
if(is_numeric($parameterName)){
|
||||
$parameters[$parameterName] = "param$parameterName";
|
||||
}else{
|
||||
$parameters[$parameterName] = $parameterName;
|
||||
$allParametersPositional = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($allParametersPositional){
|
||||
ksort($parameters, SORT_NUMERIC);
|
||||
}
|
||||
echo "\tpublic static function " .
|
||||
functionify($key) .
|
||||
"(" . implode(", ", array_map(fn(string $paramName) => "$translationContainerClass|string \$$paramName", $parameters)) . ") : $translationContainerClass{\n";
|
||||
@ -172,7 +183,7 @@ HEADER;
|
||||
echo "Done generating KnownTranslationFactory.\n";
|
||||
}
|
||||
|
||||
$lang = parse_ini_file(Path::join(\pocketmine\RESOURCE_PATH, "locale", "eng.ini"), false, INI_SCANNER_RAW);
|
||||
$lang = parse_ini_file(Path::join(\pocketmine\LOCALE_DATA_PATH, "eng.ini"), false, INI_SCANNER_RAW);
|
||||
if($lang === false){
|
||||
fwrite(STDERR, "Missing language files!\n");
|
||||
exit(1);
|
||||
|
@ -58,7 +58,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
|
||||
$memberLines = [];
|
||||
foreach($members as $name => $member){
|
||||
$reflect = new \ReflectionClass($member);
|
||||
while($reflect !== false and $reflect->isAnonymous()){
|
||||
while($reflect !== false && $reflect->isAnonymous()){
|
||||
$reflect = $reflect->getParentClass();
|
||||
}
|
||||
if($reflect === false){
|
||||
@ -82,6 +82,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
/** @var string $file */
|
||||
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
|
||||
if(substr($file, -4) !== ".php"){
|
||||
continue;
|
||||
@ -91,7 +92,7 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
|
||||
throw new \RuntimeException("Failed to get contents of $file");
|
||||
}
|
||||
|
||||
if(preg_match("/^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match('/^((final|abstract)\s+)?class /m', $contents) !== 1){
|
||||
if(preg_match("/(*ANYCRLF)^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match('/(*ANYCRLF)^((final|abstract)\s+)?class /m', $contents) !== 1){
|
||||
continue;
|
||||
}
|
||||
$shortClassName = basename($file, ".php");
|
||||
@ -101,7 +102,7 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
|
||||
}
|
||||
$reflect = new \ReflectionClass($className);
|
||||
$docComment = $reflect->getDocComment();
|
||||
if($docComment === false || preg_match("/^\s*\*\s*@generate-registry-docblock$/m", $docComment) !== 1){
|
||||
if($docComment === false || preg_match("/(*ANYCRLF)^\s*\*\s*@generate-registry-docblock$/m", $docComment) !== 1){
|
||||
continue;
|
||||
}
|
||||
echo "Found registry in $file\n";
|
||||
@ -116,4 +117,3 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
|
||||
echo "No changes made to file $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\make_release;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\VersionInfo;
|
||||
use function array_keys;
|
||||
@ -49,7 +50,7 @@ use const STR_PAD_LEFT;
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
|
||||
$versionInfo = file_get_contents($versionInfoPath);
|
||||
$versionInfo = Utils::assumeNotFalse(file_get_contents($versionInfoPath), $versionInfoPath . " should always exist");
|
||||
$versionInfo = preg_replace(
|
||||
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
||||
'$1const BASE_VERSION = "' . $newVersion . '";',
|
||||
@ -74,9 +75,17 @@ const ACCEPTED_OPTS = [
|
||||
"channel" => "Release channel to post this build into"
|
||||
];
|
||||
|
||||
function systemWrapper(string $command, string $errorMessage) : void{
|
||||
system($command, $result);
|
||||
if($result !== 0){
|
||||
echo "error: $errorMessage; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function main() : void{
|
||||
$filteredOpts = [];
|
||||
foreach(getopt("", ["current:", "next:", "channel:", "help"]) as $optName => $optValue){
|
||||
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help"])) as $optName => $optValue){
|
||||
if($optName === "help"){
|
||||
fwrite(STDOUT, "Options:\n");
|
||||
|
||||
@ -114,7 +123,7 @@ function main() : void{
|
||||
echo "$currentVer will be published on release channel \"$channel\".\n";
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
system('git add "' . dirname(__DIR__) . '/changelogs"');
|
||||
systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes");
|
||||
system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result);
|
||||
if($result === 0){
|
||||
echo "error: no changelog changes detected; aborting\n";
|
||||
@ -122,14 +131,15 @@ function main() : void{
|
||||
}
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
|
||||
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
|
||||
system('git tag ' . $currentVer->getBaseVersion());
|
||||
systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit");
|
||||
systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag");
|
||||
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
|
||||
system('git add "' . $versionInfoPath . '"');
|
||||
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
|
||||
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
|
||||
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
sleep(5);
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
systemWrapper('git push origin HEAD ' . $currentVer->getBaseVersion(), "failed to push changes to remote");
|
||||
}
|
||||
|
||||
main();
|
||||
|
Submodule build/php updated: 58ea62d7ca...b6bb7114b3
@ -134,13 +134,18 @@ function main() : void{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$opts = getopt("", ["out:", "git:"]);
|
||||
$opts = getopt("", ["out:", "git:", "build:"]);
|
||||
if(isset($opts["git"])){
|
||||
$gitHash = $opts["git"];
|
||||
}else{
|
||||
$gitHash = Git::getRepositoryStatePretty(dirname(__DIR__));
|
||||
echo "Git hash detected as $gitHash" . PHP_EOL;
|
||||
}
|
||||
if(isset($opts["build"])){
|
||||
$build = (int) $opts["build"];
|
||||
}else{
|
||||
$build = 0;
|
||||
}
|
||||
foreach(buildPhar(
|
||||
$opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar",
|
||||
dirname(__DIR__) . DIRECTORY_SEPARATOR,
|
||||
@ -150,7 +155,8 @@ function main() : void{
|
||||
'vendor'
|
||||
],
|
||||
[
|
||||
'git' => $gitHash
|
||||
'git' => $gitHash,
|
||||
'build' => $build
|
||||
],
|
||||
<<<'STUB'
|
||||
<?php
|
||||
|
@ -21,3 +21,18 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Fixed crash in `Player->showPlayer()` when the target is not in the same world.
|
||||
- `Human->setLifetimeTotalXp()` now limits the maximum value to 2^31.
|
||||
- Fixed players, who died in hardcore mode and were unbanned, getting re-banned on next server join.
|
||||
|
||||
# 3.25.3
|
||||
- Fixed crash when players try to pickup XP while already having max XP.
|
||||
- Added a sanity check to `Human->setCurrentTotalXp()` to try and catch an elusive bug that's been appearing in the wild - please get in touch if you know how to reproduce it!
|
||||
|
||||
# 3.25.4
|
||||
- Fixed a long-standing issue with `Player->removeWindow()` breaking inventory UIs on the client.
|
||||
|
||||
# 3.25.5
|
||||
- Protocol: Fixed incorrect encoding in `StructureSettings`
|
||||
- Fixed reading tags from non-docblock comments in script plugins.
|
||||
- Build number is now defined in phar metadata instead of being patched into the source code directly.
|
||||
|
||||
# 3.25.6
|
||||
- Fixed borked build number in release build of 3.25.5.
|
||||
|
32
changelogs/3.26.md
Normal file
32
changelogs/3.26.md
Normal file
@ -0,0 +1,32 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.26.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.18.0.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
# 3.26.1
|
||||
- Fixed a bug in chunk sending that caused double chests to not be paired, signs to be blank, and various other issues.
|
||||
|
||||
# 3.26.2
|
||||
- Improved error messages shown by `start.cmd`, `start.sh` and `start.ps1` when the PHP binary was not found.
|
||||
- The value of PHPRC is now shown when erroring out due to unsatisfied PHP requirements.
|
||||
- Removed restriction on the range of valid channels for `auto-updater.channel` in `pocketmine.yml`.
|
||||
|
||||
# 3.26.3
|
||||
- `PlayerExperienceChangeEvent->setNewProgress()` now performs range checks. This fixes the root of a very old and confusing crash bug which took several years to identify the cause of.
|
||||
- Note that the defective plugin(s) which caused this problem will still cause a server crash, but the plugin responsible will now get blamed correctly.
|
||||
|
||||
# 3.26.4
|
||||
- Fixed skins appearing black when using RTX resource packs.
|
||||
- Fixed chunks containing furnaces in old worlds (pre-2017) being discarded as corrupted.
|
||||
- This was caused by a strict corruption check detecting bad data created by a bug in PocketMine-MP that was fixed in 2017.
|
||||
|
||||
# 3.26.5
|
||||
- Fixed several denial-of-service attack vectors related to writable book text length and encoding.
|
||||
- Fixed several denial-of-service attack vectors related to skin data field lengths.
|
15
changelogs/3.27.md
Normal file
15
changelogs/3.27.md
Normal file
@ -0,0 +1,15 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.27.0
|
||||
- Introduced support for protocol encryption.
|
||||
- Encryption is enabled by default.
|
||||
- Fixes login replay attacks.
|
||||
- This may cause some performance degradation.
|
||||
- Encryption can be disabled by setting `network.enable-encryption` to `false` in `pocketmine.yml`. DO NOT do this unless you understand the risks involved.
|
||||
- An obsoletion notice has been added to the console during server startup.
|
1767
changelogs/4.0-beta.md
Normal file
1767
changelogs/4.0-beta.md
Normal file
File diff suppressed because it is too large
Load Diff
1072
changelogs/4.0.md
1072
changelogs/4.0.md
File diff suppressed because it is too large
Load Diff
166
changelogs/4.1-beta.md
Normal file
166
changelogs/4.1-beta.md
Normal file
@ -0,0 +1,166 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.1.0-BETA1
|
||||
Released 22nd January 2022.
|
||||
|
||||
## General
|
||||
- Game mode names (e.g. `survival`, `creative`) may now be used for the `gamemode` property in `server.properties`.
|
||||
- Increased default maximum render distance to 16 chunks. Players with a render distance smaller than this will notice no difference.
|
||||
- The setup wizard now prompts for a maximum render distance value.
|
||||
- The setup wizard now prompts for an IPv6 port selection. Previously it would always use 19133.
|
||||
- `chunk-ticking.disable-block-ticking` now accepts block names like those used in the `/give` command.
|
||||
- The `/clear` command now behaves more like vanilla:
|
||||
- The order of inventories is now the same as Bedrock.
|
||||
- The cursor and offhand inventories are now cleared if necessary.
|
||||
|
||||
## Technical
|
||||
- `PlayerAuthInputPacket` is now used instead of `MovePlayerPacket` for processing movements. This improves position and rotation accuracy.
|
||||
- `&&` and `||` are now always used instead of `and` and `or`.
|
||||
- New version of `pocketmine/errorhandler` is used by this version, adding support for `ErrorToExceptionHandler::trap()`. This enables reliably capturing `E_WARNING` and `E_NOTICE` from functions such as `yaml_parse()` and friends.
|
||||
- New dependency versions are required by this version:
|
||||
- `pocketmine/bedrock-protocol` has been updated from 7.1.0 to [7.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/7.3.0%2Bbedrock-1.18.0).
|
||||
- `pocketmine/errorhandler` has been updated from 0.3.0 to [0.6.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.6.0).
|
||||
|
||||
## API
|
||||
### Block
|
||||
- The following classes have been added:
|
||||
- `Lectern`
|
||||
- `Pumpkin`
|
||||
- The following public API methods have been added:
|
||||
- `Block->getTypeId() : int` - returns an integer which uniquely identifies the block type, ignoring things like facing, colour etc.
|
||||
- `VanillaBlocks::LECTERN()`
|
||||
|
||||
### Entity
|
||||
- The following classes have been added:
|
||||
- `animation\ItemEntityStackSizeChangeAnimation`
|
||||
- The following public API methods have been added:
|
||||
- `object\ItemEntity->isMergeable(object\ItemEntity $other) : bool`
|
||||
- `object\ItemEntity->setStackSize(int $size) : void`
|
||||
- `object\ItemEntity->tryMergeInto(object\ItemEntity $other) : bool`
|
||||
- `ExperienceManager->canAttractXpOrbs() : bool`
|
||||
- `ExperienceManager->setCanAttractXpOrbs(bool $v = true) : void`
|
||||
- `Entity->getSize() : EntitySizeInfo`
|
||||
- `Living->isGliding() : bool`
|
||||
- `Living->isSwimming() : bool`
|
||||
- `Living->setGliding(bool $value = true) : void`
|
||||
- `Living->setSwimming(bool $value = true) : void`
|
||||
- The following protected API methods have been added:
|
||||
- `Entity->getBlocksIntersected(float $inset) : \Generator<int, Block, void, void>`
|
||||
|
||||
### Event
|
||||
- `BlockSpreadEvent` is now called when fire spreads to the positions of blocks it burns away.
|
||||
- `BlockFormEvent` is now called when concrete powder turns into concrete due to contact with water.
|
||||
- The following classes have been added:
|
||||
- `BlockMeltEvent` - called when ice or snow melts
|
||||
- `ChestPairEvent` - called when two chests try to form a pair
|
||||
- `PlayerToggleGlideEvent` - called when a player starts or stops gliding
|
||||
- `PlayerToggleSwimEvent` - called when a player starts or stops swimming
|
||||
|
||||
### Item
|
||||
- The following public API methods have been added:
|
||||
- `SplashPotion->getType() : PotionType`
|
||||
- `VanillaItems::AIR()`
|
||||
- The following API methods have been deprecated:
|
||||
- `ItemFactory::air()` - use `VanillaItems::AIR()` instead
|
||||
|
||||
### Player
|
||||
- The following public API methods have been added:
|
||||
- `Player->hasBlockCollision() : bool`
|
||||
- `Player->setHasBlockCollision(bool $value)` - allows controlling spectator-like no-clip behaviour without changing game mode
|
||||
- `Player->toggleSwim(bool $swim) : bool` - called by the network system when the client tries to start/stop swimming
|
||||
- `Player->toggleGlide(bool $glide) : bool` - called by the network system when the client tries to start/stop gliding
|
||||
|
||||
### Server
|
||||
- The following public API constants have been added:
|
||||
- `Server::DEFAULT_SERVER_NAME`
|
||||
- `Server::DEFAULT_MAX_PLAYERS`
|
||||
- `Server::DEFAULT_PORT_IPV4`
|
||||
- `Server::DEFAULT_PORT_IPV6`
|
||||
- `Server::DEFAULT_MAX_VIEW_DISTANCE`
|
||||
|
||||
### Utils
|
||||
- Config parsing errors are now always represented by `ConfigLoadException` and include the path to the file in the message.
|
||||
- Added `TextFormat::MINECOIN_GOLD`, and support for it to the various `TextFormat` methods.
|
||||
- The following public API methods have been added:
|
||||
- `Utils::assumeNotFalse()` - static analysis crutch to silence PHPStan errors without using `ignoreErrors` or `@phpstan-ignore-line`, which are both too coarse.
|
||||
- The following public API properties have been added:
|
||||
- `Terminal::$COLOR_MINECOIN_GOLD`
|
||||
- The following classes have been added:
|
||||
- `ConfigLoadException`
|
||||
- Fixed `Random->nextSignedInt()` to actually return a signed int. Previously it would return any integer value between 0 and 4,294,957,295.
|
||||
- Fixed `Random->nextSignedFloat()` to return a float between `-1.0` and `1.0`. Previously it would return any value between `0.0` and `2.0`.
|
||||
- `VersionString->getNumber()` output is now structured differently to fix overflow issues caused by the old format.
|
||||
|
||||
### World
|
||||
- The following classes have been added:
|
||||
- `sound\ItemUseOnBlockSound`
|
||||
- `sound\LecternPlaceBookSound`
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Fire now spreads.
|
||||
- Implemented lectern blocks.
|
||||
- Added missing sounds for hoeing grass and dirt.
|
||||
- Added missing sounds for using a shovel on grass to create grass path.
|
||||
- Pumpkins can now be carved using shears.
|
||||
|
||||
### Items
|
||||
- Dropped items of the same type now merge with each other.
|
||||
|
||||
### Misc
|
||||
- Implemented player swimming.
|
||||
|
||||
# 4.1.0-BETA2
|
||||
Released 27th January 2022.
|
||||
|
||||
## API
|
||||
### Block
|
||||
- The following API methods have been added:
|
||||
- `utils\BrewingStandSlot->getSlotNumber() : int`
|
||||
- `utils\FurnaceType->getCookSound() : Sound`
|
||||
- The following API constants have been added:
|
||||
- `tile\BrewingStand::BREW_TIME_TICKS`
|
||||
|
||||
### Crafting
|
||||
- The following API methods have been added:
|
||||
- `CraftingManager->getPotionContainerChangeRecipes() : array<int, array<string, PotionContainerChangeRecipe>>`
|
||||
- `CraftingManager->getPotionTypeRecipes() : array<string, array<string, PotionTypeRecipe>>`
|
||||
- `CraftingManager->registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void`
|
||||
- `CraftingManager->registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void`
|
||||
- The following classes have been added:
|
||||
- `BrewingRecipe`
|
||||
- `PotionContainerChangeRecipe`
|
||||
- `PotionTypeRecipe`
|
||||
|
||||
### Event
|
||||
- The following classes have been added:
|
||||
- `BrewItemEvent` - called when a brewing stand finishes brewing potions; this is called up to 3 times (once for each brewing slot, as needed)
|
||||
- `BrewingFuelUseEvent` - called when a brewing stand consumes blaze powder
|
||||
- `PlayerViewDistanceChangeEvent` - called whenever a player alters their render distance or requests one for the first time when connecting
|
||||
|
||||
### World
|
||||
#### Sound
|
||||
- The following classes have been added:
|
||||
- `BlastFurnaceSound` - the sound made by a blast furnace during smelting
|
||||
- `FurnaceSound` - the sound made by a regular furnace during cooking or smelting
|
||||
- `PotionFinishBrewingSound` - the sound made by a brewing stand when a potion finishes being brewed
|
||||
- `SmokerSound` - the sound made by a smoker during cooking
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Brewing stands can now be used for brewing potions.
|
||||
- The visual appearance of a brewing stand now updates correctly when the contents of its inventory changes (adding/removing potions).
|
||||
- Added missing sounds for furnace, blast furnace and smoker.
|
||||
- Fixed ender chest not dropping itself when mined with a Silk Touch pickaxe.
|
||||
- Cobwebs now drop themselves when mined using shears.
|
||||
- The correct amount of fall damage is now taken when falling from a height onto hay bales.
|
||||
- Fixed block updating bug introduced by beta1 which caused crops and other plants to never grow.
|
||||
|
||||
### Misc
|
||||
- Added a workaround for client hitbox size bug after swimming which caused the player to be able to fit into one-block-tall gaps.
|
142
changelogs/4.1.md
Normal file
142
changelogs/4.1.md
Normal file
@ -0,0 +1,142 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.1.0
|
||||
Released 7th February 2022.
|
||||
|
||||
## General
|
||||
- Game mode names (e.g. `survival`, `creative`) may now be used for the `gamemode` property in `server.properties`.
|
||||
- Increased default maximum render distance to 16 chunks. Players with a render distance smaller than this will notice no difference.
|
||||
- The setup wizard now prompts for a maximum render distance value.
|
||||
- The setup wizard now prompts for an IPv6 port selection. Previously it would always use 19133.
|
||||
- `chunk-ticking.disable-block-ticking` now accepts block names like those used in the `/give` command.
|
||||
- The `/clear` command now behaves more like vanilla:
|
||||
- The order of inventories is now the same as Bedrock.
|
||||
- The cursor and offhand inventories are now cleared if necessary.
|
||||
|
||||
## Technical
|
||||
- `PlayerAuthInputPacket` is now used instead of `MovePlayerPacket` for processing movements. This improves position and rotation accuracy.
|
||||
- `&&` and `||` are now always used instead of `and` and `or`.
|
||||
- New version of `pocketmine/errorhandler` is used by this version, adding support for `ErrorToExceptionHandler::trap()`. This enables reliably capturing `E_WARNING` and `E_NOTICE` from functions such as `yaml_parse()` and friends.
|
||||
- New dependency versions are required by this version:
|
||||
- `pocketmine/bedrock-protocol` has been updated from 7.1.0 to [7.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/7.3.0%2Bbedrock-1.18.0).
|
||||
- `pocketmine/errorhandler` has been updated from 0.3.0 to [0.6.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.6.0).
|
||||
|
||||
## API
|
||||
### Block
|
||||
- The following classes have been added:
|
||||
- `Lectern`
|
||||
- `Pumpkin`
|
||||
- The following public API methods have been added:
|
||||
- `Block->getTypeId() : int` - returns an integer which uniquely identifies the block type, ignoring things like facing, colour etc.
|
||||
- `VanillaBlocks::LECTERN()`
|
||||
- `utils\BrewingStandSlot->getSlotNumber() : int`
|
||||
- `utils\FurnaceType->getCookSound() : Sound`
|
||||
- The following API constants have been added:
|
||||
- `tile\BrewingStand::BREW_TIME_TICKS`
|
||||
|
||||
### Crafting
|
||||
- The following API methods have been added:
|
||||
- `CraftingManager->getPotionContainerChangeRecipes() : array<int, array<string, PotionContainerChangeRecipe>>`
|
||||
- `CraftingManager->getPotionTypeRecipes() : array<string, array<string, PotionTypeRecipe>>`
|
||||
- `CraftingManager->registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void`
|
||||
- `CraftingManager->registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void`
|
||||
- The following classes have been added:
|
||||
- `BrewingRecipe`
|
||||
- `PotionContainerChangeRecipe`
|
||||
- `PotionTypeRecipe`
|
||||
|
||||
### Entity
|
||||
- The following classes have been added:
|
||||
- `animation\ItemEntityStackSizeChangeAnimation`
|
||||
- The following public API methods have been added:
|
||||
- `object\ItemEntity->isMergeable(object\ItemEntity $other) : bool`
|
||||
- `object\ItemEntity->setStackSize(int $size) : void`
|
||||
- `object\ItemEntity->tryMergeInto(object\ItemEntity $other) : bool`
|
||||
- `ExperienceManager->canAttractXpOrbs() : bool`
|
||||
- `ExperienceManager->setCanAttractXpOrbs(bool $v = true) : void`
|
||||
- `Entity->getSize() : EntitySizeInfo`
|
||||
- `Living->isGliding() : bool`
|
||||
- `Living->isSwimming() : bool`
|
||||
- `Living->setGliding(bool $value = true) : void`
|
||||
- `Living->setSwimming(bool $value = true) : void`
|
||||
- The following protected API methods have been added:
|
||||
- `Entity->getBlocksIntersected(float $inset) : \Generator<int, Block, void, void>`
|
||||
|
||||
### Event
|
||||
- `BlockSpreadEvent` is now called when fire spreads to the positions of blocks it burns away.
|
||||
- `BlockFormEvent` is now called when concrete powder turns into concrete due to contact with water.
|
||||
- The following classes have been added:
|
||||
- `BlockMeltEvent` - called when ice or snow melts
|
||||
- `BrewItemEvent` - called when a brewing stand finishes brewing potions; this is called up to 3 times (once for each brewing slot, as needed)
|
||||
- `BrewingFuelUseEvent` - called when a brewing stand consumes blaze powder
|
||||
- `ChestPairEvent` - called when two chests try to form a pair
|
||||
- `PlayerToggleGlideEvent` - called when a player starts or stops gliding
|
||||
- `PlayerToggleSwimEvent` - called when a player starts or stops swimming
|
||||
- `PlayerViewDistanceChangeEvent` - called whenever a player alters their render distance or requests one for the first time when connecting
|
||||
|
||||
### Item
|
||||
- The following public API methods have been added:
|
||||
- `SplashPotion->getType() : PotionType`
|
||||
- `VanillaItems::AIR()`
|
||||
- The following API methods have been deprecated:
|
||||
- `ItemFactory::air()` - use `VanillaItems::AIR()` instead
|
||||
|
||||
### Player
|
||||
- The following public API methods have been added:
|
||||
- `Player->hasBlockCollision() : bool`
|
||||
- `Player->setHasBlockCollision(bool $value)` - allows controlling spectator-like no-clip behaviour without changing game mode
|
||||
- `Player->toggleSwim(bool $swim) : bool` - called by the network system when the client tries to start/stop swimming
|
||||
- `Player->toggleGlide(bool $glide) : bool` - called by the network system when the client tries to start/stop gliding
|
||||
|
||||
### Server
|
||||
- The following public API constants have been added:
|
||||
- `Server::DEFAULT_SERVER_NAME`
|
||||
- `Server::DEFAULT_MAX_PLAYERS`
|
||||
- `Server::DEFAULT_PORT_IPV4`
|
||||
- `Server::DEFAULT_PORT_IPV6`
|
||||
- `Server::DEFAULT_MAX_VIEW_DISTANCE`
|
||||
|
||||
### Utils
|
||||
- Config parsing errors are now always represented by `ConfigLoadException` and include the path to the file in the message.
|
||||
- Added `TextFormat::MINECOIN_GOLD`, and support for it to the various `TextFormat` methods.
|
||||
- The following public API methods have been added:
|
||||
- `Utils::assumeNotFalse()` - static analysis crutch to silence PHPStan errors without using `ignoreErrors` or `@phpstan-ignore-line`, which are both too coarse.
|
||||
- The following public API properties have been added:
|
||||
- `Terminal::$COLOR_MINECOIN_GOLD`
|
||||
- The following classes have been added:
|
||||
- `ConfigLoadException`
|
||||
- Fixed `Random->nextSignedInt()` to actually return a signed int. Previously it would return any integer value between 0 and 4,294,957,295.
|
||||
- Fixed `Random->nextSignedFloat()` to return a float between `-1.0` and `1.0`. Previously it would return any value between `0.0` and `2.0`.
|
||||
- `VersionString->getNumber()` output is now structured differently to fix overflow issues caused by the old format.
|
||||
|
||||
### World
|
||||
- The following classes have been added:
|
||||
- `sound\BlastFurnaceSound` - the sound made by a blast furnace during smelting
|
||||
- `sound\FurnaceSound` - the sound made by a regular furnace during cooking or smelting
|
||||
- `sound\ItemUseOnBlockSound`
|
||||
- `sound\LecternPlaceBookSound`
|
||||
- `sound\PotionFinishBrewingSound` - the sound made by a brewing stand when a potion finishes being brewed
|
||||
- `sound\SmokerSound` - the sound made by a smoker during cooking
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Fire now spreads.
|
||||
- Implemented lectern blocks.
|
||||
- Added missing sounds for hoeing grass and dirt.
|
||||
- Added missing sounds for using a shovel on grass to create grass path.
|
||||
- Pumpkins can now be carved using shears.
|
||||
- Brewing stands can now be used for brewing potions.
|
||||
- The visual appearance of a brewing stand now updates correctly when the contents of its inventory changes (adding/removing potions).
|
||||
- Added missing sounds for furnace, blast furnace and smoker.
|
||||
- Fixed ender chest not dropping itself when mined with a Silk Touch pickaxe.
|
||||
- Cobwebs now drop themselves when mined using shears.
|
||||
- The correct amount of fall damage is now taken when falling from a height onto hay bales.
|
||||
|
||||
### Items
|
||||
- Dropped items of the same type now merge with each other.
|
59
changelogs/4.2.md
Normal file
59
changelogs/4.2.md
Normal file
@ -0,0 +1,59 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.10**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.2.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.18.10.
|
||||
|
||||
# 4.2.1
|
||||
Released 19th February 2022.
|
||||
|
||||
## General
|
||||
- Improved performance of `Item::nbtSerialize()` - this will improve performance during world saves.
|
||||
- Added more missing changes to the 4.0.0 changelog.
|
||||
|
||||
## Fixes
|
||||
- Fixed multiple players being able to sleep in the same bed.
|
||||
- Fixed hitbox not resetting properly after swimming or gliding.
|
||||
|
||||
# 4.2.2
|
||||
Released 2nd March 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed crash in `/dumpmemory` due to usage of non-printable string keys in `CraftingManager`. Array contents in memory dumps are now rendered as prettified key-value pairs.
|
||||
- Fixed output directory for `/dumpmemory`.
|
||||
- `PlayerInventory->isHotbarSlot()` now correctly returns `false` when given `9`.
|
||||
- Fixed ghost items left in the inventory when dropping tools while mining.
|
||||
|
||||
# 4.2.3
|
||||
Released 9th March 2022.
|
||||
|
||||
## Technical
|
||||
- Now analysed using PHPStan 1.4.8.
|
||||
- Now using `pocketmine/bedrock-protocol` [`8.0.1`](https://github.com/pmmp/BedrockProtocol/releases/tag/8.0.1%2Bbedrock-1.18.10).
|
||||
|
||||
## Fixes
|
||||
### Core
|
||||
- Fixed a memory leak and other bugs related to plugins disabling themselves during `onEnable()`.
|
||||
|
||||
### Gameplay
|
||||
- Sweet berry bushes now absorb fall damage.
|
||||
- Fixed mycelium spreading onto coarse dirt.
|
||||
- Fixed blocks placed during `Block->onIncinerate()` getting overwritten.
|
||||
- Fixed shulker boxes being unopenable when underwater.
|
||||
- Fixed invisible fire on top of transparent non-flammable blocks.
|
||||
|
||||
### API
|
||||
- Various APIs accepting `Vector3`, `Position` or `Location` no longer accept objects containing `INF` or `NaN` in any component. Previously, this was allowed, but would cause lots of obscure crashes later on.
|
||||
- `Entity->setRotation()` no longer accepts `INF` or `NaN`.
|
||||
- Fixed missing bounds check for `ItemFrame->setItemDropChance()`.
|
||||
|
||||
# 4.2.4
|
||||
Released 18th March 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed a crash when handling out-of-bounds meta values on the network.
|
@ -7,7 +7,7 @@
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php-64bit": "*",
|
||||
"ext-chunkutils2": "^0.3.0",
|
||||
"ext-chunkutils2": "^0.3.1",
|
||||
"ext-crypto": "^0.3.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
@ -34,17 +34,18 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-data": "^1.4.0+bedrock-1.17.40",
|
||||
"pocketmine/bedrock-protocol": "^5.0.0+bedrock-1.17.40",
|
||||
"pocketmine/bedrock-data": "~1.6.0+bedrock-1.18.10",
|
||||
"pocketmine/bedrock-protocol": "~8.0.0+bedrock-1.18.10",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/errorhandler": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.4.2",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.4.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
"pocketmine/nbt": "^0.3.0",
|
||||
"pocketmine/nbt": "^0.3.2",
|
||||
"pocketmine/raklib": "^0.14.2",
|
||||
"pocketmine/raklib-ipc": "^0.1.0",
|
||||
"pocketmine/snooze": "^0.3.0",
|
||||
@ -52,7 +53,7 @@
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.0.0",
|
||||
"phpstan/phpstan": "1.4.10",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
@ -78,7 +79,7 @@
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/DevTools/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
|
||||
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
|
||||
"make-server": [
|
||||
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
|
||||
"@php -dphar.readonly=0 build/server-phar.php"
|
||||
|
453
composer.lock
generated
453
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.15
|
||||
# Doxyfile 1.9.3
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -93,14 +93,6 @@ ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all generated output in the proper direction.
|
||||
# Possible values are: None, LTR, RTL and Context.
|
||||
# The default value is: None.
|
||||
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
|
||||
# descriptions after the members that are listed in the file and class
|
||||
# documentation (similar to Javadoc). Set to NO to disable this.
|
||||
@ -197,6 +189,16 @@ SHORT_NAMES = NO
|
||||
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
|
||||
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
|
||||
# such as
|
||||
# /***************
|
||||
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
|
||||
# Javadoc-style will behave just like regular comments and it will not be
|
||||
# interpreted by doxygen.
|
||||
# The default value is: NO.
|
||||
|
||||
JAVADOC_BANNER = NO
|
||||
|
||||
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
|
||||
# line (until the first dot) of a Qt-style comment as the brief description. If
|
||||
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
|
||||
@ -217,6 +219,14 @@ QT_AUTOBRIEF = NO
|
||||
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
|
||||
# By default Python docstrings are displayed as preformatted text and doxygen's
|
||||
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
|
||||
# doxygen's special commands can be used and the contents of the docstring
|
||||
# documentation blocks is shown as doxygen documentation.
|
||||
# The default value is: YES.
|
||||
|
||||
PYTHON_DOCSTRING = YES
|
||||
|
||||
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
|
||||
# documentation from any documented member that it re-implements.
|
||||
# The default value is: YES.
|
||||
@ -240,25 +250,19 @@ TAB_SIZE = 4
|
||||
# the documentation. An alias has the form:
|
||||
# name=value
|
||||
# For example adding
|
||||
# "sideeffect=@par Side Effects:\n"
|
||||
# "sideeffect=@par Side Effects:^^"
|
||||
# will allow you to put the command \sideeffect (or @sideeffect) in the
|
||||
# documentation, which will result in a user-defined paragraph with heading
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines (in the resulting output). You can put ^^ in the value part of an
|
||||
# alias to insert a newline as if a physical newline was in the original file.
|
||||
# When you need a literal { or } or , in the value part of an alias you have to
|
||||
# escape them by means of a backslash (\), this can lead to conflicts with the
|
||||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
|
||||
# to insert newlines (in the resulting output). You can put ^^ in the value part
|
||||
# of an alias to insert a newline as if a physical newline was in the original
|
||||
# file. When you need a literal { or } or , in the value part of an alias you
|
||||
# have to escape them by means of a backslash (\), this can lead to conflicts
|
||||
# with the commands \{ and \} for these it is advised to use the version @{ and
|
||||
# @} or use a double escape (\\{ and \\})
|
||||
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
# instance, some of the names that are used will be different. The list of all
|
||||
@ -299,19 +303,22 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
||||
# parses. With this tag you can assign which parser to use for a given
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
||||
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
|
||||
# .inc files as Fortran files (default is PHP), and .f files as C (default is
|
||||
# Fortran), use: inc=Fortran f=C.
|
||||
# default for Fortran type files). For instance to make doxygen treat .inc files
|
||||
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
|
||||
# use: inc=Fortran f=C.
|
||||
#
|
||||
# Note: For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
|
||||
# the files are not read by doxygen.
|
||||
# the files are not read by doxygen. When specifying no_extension you should add
|
||||
# * to the FILE_PATTERNS.
|
||||
#
|
||||
# Note see also the list of default file extension mappings.
|
||||
|
||||
EXTENSION_MAPPING =
|
||||
|
||||
@ -329,7 +336,7 @@ MARKDOWN_SUPPORT = YES
|
||||
# to that level are automatically included in the table of contents, even if
|
||||
# they do not have an id attribute.
|
||||
# Note: This feature currently applies only to Markdown headings.
|
||||
# Minimum value: 0, maximum value: 99, default value: 0.
|
||||
# Minimum value: 0, maximum value: 99, default value: 5.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 0
|
||||
@ -445,6 +452,19 @@ TYPEDEF_HIDES_STRUCT = NO
|
||||
|
||||
LOOKUP_CACHE_SIZE = 0
|
||||
|
||||
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
|
||||
# during processing. When set to 0 doxygen will based this on the number of
|
||||
# cores available in the system. You can set it explicitly to a value larger
|
||||
# than 0 to get more control over the balance between CPU load and processing
|
||||
# speed. At this moment only the input processing can be done using multiple
|
||||
# threads. Since this is still an experimental feature the default is set to 1,
|
||||
# which effectively disables parallel processing. Please report any issues you
|
||||
# encounter. Generating dot graphs in parallel is controlled by the
|
||||
# DOT_NUM_THREADS setting.
|
||||
# Minimum value: 0, maximum value: 32, default value: 1.
|
||||
|
||||
NUM_PROC_THREADS = 1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
@ -465,6 +485,12 @@ EXTRACT_ALL = NO
|
||||
|
||||
EXTRACT_PRIVATE = NO
|
||||
|
||||
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
|
||||
# methods of a class will be included in the documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_PRIV_VIRTUAL = NO
|
||||
|
||||
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
|
||||
# scope will be included in the documentation.
|
||||
# The default value is: NO.
|
||||
@ -502,6 +528,13 @@ EXTRACT_LOCAL_METHODS = YES
|
||||
|
||||
EXTRACT_ANON_NSPACES = YES
|
||||
|
||||
# If this flag is set to YES, the name of an unnamed parameter in a declaration
|
||||
# will be determined by the corresponding definition. By default unnamed
|
||||
# parameters remain unnamed in the output.
|
||||
# The default value is: YES.
|
||||
|
||||
RESOLVE_UNNAMED_PARAMS = YES
|
||||
|
||||
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
|
||||
# undocumented members inside documented classes or files. If set to NO these
|
||||
# members will be included in the various overviews, but no documentation
|
||||
@ -519,8 +552,8 @@ HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# (class|struct|union) declarations. If set to NO, these declarations will be
|
||||
# included in the documentation.
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
@ -539,11 +572,18 @@ HIDE_IN_BODY_DOCS = YES
|
||||
|
||||
INTERNAL_DOCS = NO
|
||||
|
||||
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
|
||||
# names in lower-case letters. If set to YES, upper-case letters are also
|
||||
# allowed. This is useful if you have classes or files whose names only differ
|
||||
# in case and if your file system supports case sensitive file names. Windows
|
||||
# and Mac users are advised to set this option to NO.
|
||||
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
|
||||
# able to match the capabilities of the underlying filesystem. In case the
|
||||
# filesystem is case sensitive (i.e. it supports files in the same directory
|
||||
# whose names only differ in casing), the option must be set to YES to properly
|
||||
# deal with such files in case they appear in the input. For filesystems that
|
||||
# are not case sensitive the option should be be set to NO to properly deal with
|
||||
# output files written for symbols that only differ in casing, such as for two
|
||||
# classes, one named CLASS and the other named Class, and to also support
|
||||
# references to files without having to specify the exact matching casing. On
|
||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = NO
|
||||
@ -562,6 +602,12 @@ HIDE_SCOPE_NAMES = NO
|
||||
|
||||
HIDE_COMPOUND_REFERENCE= NO
|
||||
|
||||
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
|
||||
# will show which file needs to be included to use the class.
|
||||
# The default value is: YES.
|
||||
|
||||
SHOW_HEADERFILE = YES
|
||||
|
||||
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
|
||||
# the files that are included by a file in the documentation of that file.
|
||||
# The default value is: YES.
|
||||
@ -719,7 +765,8 @@ FILE_VERSION_FILTER =
|
||||
# output files in an output format independent way. To create the layout file
|
||||
# that represents doxygen's defaults, run doxygen with the -l option. You can
|
||||
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
|
||||
# will be used as the name of the layout file.
|
||||
# will be used as the name of the layout file. See also section "Changing the
|
||||
# layout of pages" for information.
|
||||
#
|
||||
# Note that if you run doxygen from a directory containing a file called
|
||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||
@ -765,24 +812,35 @@ WARNINGS = NO
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
# in a documented function, or documenting parameters that don't exist or using
|
||||
# markup commands wrongly.
|
||||
# potential errors in the documentation, such as documenting some parameters in
|
||||
# a documented function twice, or documenting parameters that don't exist or
|
||||
# using markup commands wrongly.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
|
||||
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
|
||||
# function parameter documentation. If set to NO, doxygen will accept that some
|
||||
# parameters have no documentation without warning.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_INCOMPLETE_DOC = YES
|
||||
|
||||
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
|
||||
# are documented, but have no documentation for their parameters or return
|
||||
# value. If set to NO, doxygen will only warn about wrong or incomplete
|
||||
# parameter documentation, but not about the absence of documentation. If
|
||||
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
|
||||
# value. If set to NO, doxygen will only warn about wrong parameter
|
||||
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
|
||||
# set to YES then this flag will automatically be disabled. See also
|
||||
# WARN_IF_INCOMPLETE_DOC
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered.
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
@ -799,7 +857,10 @@ WARN_FORMAT = "$file:$line: $text"
|
||||
|
||||
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
|
||||
# messages should be written. If left blank the output is written to standard
|
||||
# error (stderr).
|
||||
# error (stderr). In case the file specified cannot be opened for writing the
|
||||
# warning and error messages are written to standard error. When as file - is
|
||||
# specified the warning and error messages are written to standard output
|
||||
# (stdout).
|
||||
|
||||
WARN_LOGFILE =
|
||||
|
||||
@ -820,8 +881,8 @@ INPUT = ../src \
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
|
||||
# possible encodings.
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
@ -834,11 +895,15 @@ INPUT_ENCODING = UTF-8
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# read by doxygen.
|
||||
#
|
||||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.php
|
||||
|
||||
@ -883,7 +948,7 @@ EXCLUDE_PATTERNS = */bin/* \
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# AClass::ANamespace, ANamespace::*Test
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
@ -1059,16 +1124,24 @@ USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = YES
|
||||
|
||||
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
|
||||
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
|
||||
# cost of reduced performance. This can be particularly helpful with template
|
||||
# rich C++ code for which doxygen's built-in parser lacks the necessary type
|
||||
# information.
|
||||
# clang parser (see:
|
||||
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
|
||||
# performance. This can be particularly helpful with template rich C++ code for
|
||||
# which doxygen's built-in parser lacks the necessary type information.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# generated with the -Duse_libclang=ON option for CMake.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
|
||||
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
|
||||
# tag is set to YES then doxygen will add the directory of each input to the
|
||||
# include path.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
|
||||
|
||||
CLANG_ADD_INC_PATHS = YES
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the compiler with command
|
||||
# line options that you would normally use when invoking the compiler. Note that
|
||||
# the include paths will already be set by doxygen for the files and directories
|
||||
@ -1078,10 +1151,13 @@ CLANG_ASSISTED_PARSING = NO
|
||||
CLANG_OPTIONS =
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the clang parser with the
|
||||
# path to the compilation database (see:
|
||||
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
|
||||
# were built. This is equivalent to specifying the "-p" option to a clang tool,
|
||||
# such as clang-check. These options will then be passed to the parser.
|
||||
# path to the directory containing a file called compile_commands.json. This
|
||||
# file is the compilation database (see:
|
||||
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
|
||||
# options used when the source files were built. This is equivalent to
|
||||
# specifying the -p option to a clang tool, such as clang-check. These options
|
||||
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
|
||||
# will be added as well.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# generated with the -Duse_libclang=ON option for CMake.
|
||||
|
||||
@ -1098,13 +1174,6 @@ CLANG_DATABASE_PATH =
|
||||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
|
||||
# which the alphabetical index list will be split.
|
||||
# Minimum value: 1, maximum value: 20, default value: 5.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||
@ -1204,7 +1273,7 @@ HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a colorwheel, see
|
||||
# this color. Hue is specified as an angle on a color-wheel, see
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
|
||||
# purple, and 360 is red again.
|
||||
@ -1214,7 +1283,7 @@ HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 220
|
||||
|
||||
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
|
||||
# in the HTML output. For a value of 0 the output will use grayscales only. A
|
||||
# in the HTML output. For a value of 0 the output will use gray-scales only. A
|
||||
# value of 255 will produce the most vivid colors.
|
||||
# Minimum value: 0, maximum value: 255, default value: 100.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@ -1239,13 +1308,13 @@ HTML_COLORSTYLE_GAMMA = 80
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_TIMESTAMP = YES
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via Javascript. If disabled, the navigation index will
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
# consists of multiple levels of tabs that are statically embedded in every HTML
|
||||
# page. Disable this option to support browsers that do not have Javascript,
|
||||
# page. Disable this option to support browsers that do not have JavaScript,
|
||||
# like the Qt help browser.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@ -1275,10 +1344,11 @@ HTML_INDEX_NUM_ENTRIES = 100
|
||||
|
||||
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
|
||||
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
|
||||
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||
# that directory and running make install will install the docset in
|
||||
# environment (see:
|
||||
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
|
||||
# create a documentation set, doxygen will generate a Makefile in the HTML
|
||||
# output directory. Running make will produce the docset in that directory and
|
||||
# running make install will install the docset in
|
||||
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
|
||||
# genXcode/_index.html for more information.
|
||||
@ -1295,6 +1365,13 @@ GENERATE_DOCSET = NO
|
||||
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
|
||||
# This tag determines the URL of the docset feed. A documentation feed provides
|
||||
# an umbrella under which multiple documentation sets from a single provider
|
||||
# (such as a company or product suite) can be grouped.
|
||||
# This tag requires that the tag GENERATE_DOCSET is set to YES.
|
||||
|
||||
DOCSET_FEEDURL =
|
||||
|
||||
# This tag specifies a string that should uniquely identify the documentation
|
||||
# set bundle. This should be a reverse domain-name style string, e.g.
|
||||
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
|
||||
@ -1320,8 +1397,12 @@ DOCSET_PUBLISHER_NAME = Publisher
|
||||
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
|
||||
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
|
||||
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
|
||||
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# Windows.
|
||||
# on Windows. In the beginning of 2021 Microsoft took the original page, with
|
||||
# a.o. the download links, offline the HTML help workshop was already many years
|
||||
# in maintenance mode). You can download the HTML help workshop from the web
|
||||
# archives at Installation executable (see:
|
||||
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
|
||||
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
|
||||
#
|
||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
||||
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
|
||||
@ -1351,7 +1432,7 @@ CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
|
||||
# The GENERATE_CHI flag controls if a separate .chi index file is generated
|
||||
# (YES) or that it should be included in the master .chm file (NO).
|
||||
# (YES) or that it should be included in the main .chm file (NO).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
@ -1396,7 +1477,8 @@ QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# The default value is: org.doxygen.Project.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1404,8 +1486,8 @@ QHP_NAMESPACE = org.doxygen.Project
|
||||
|
||||
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
|
||||
# Help Project output. For more information please see Qt Help Project / Virtual
|
||||
# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# Folders (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1413,30 +1495,30 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
|
||||
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
|
||||
# filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
|
||||
# The QHG_LOCATION tag can be used to specify the location of Qt's
|
||||
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
|
||||
# generated .qhp file.
|
||||
# The QHG_LOCATION tag can be used to specify the location (absolute path
|
||||
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
|
||||
# run qhelpgenerator on the generated .qhp file.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHG_LOCATION =
|
||||
@ -1479,16 +1561,28 @@ DISABLE_INDEX = NO
|
||||
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
|
||||
# (i.e. any modern browser). Windows users are probably better off using the
|
||||
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
|
||||
# further fine-tune the look of the index. As an example, the default style
|
||||
# sheet generated by doxygen has an example that shows how to put an image at
|
||||
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
|
||||
# the same information as the tab index, you could consider setting
|
||||
# DISABLE_INDEX to YES when enabling this option.
|
||||
# further fine tune the look of the index (see "Fine-tuning the output"). As an
|
||||
# example, the default style sheet generated by doxygen has an example that
|
||||
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
|
||||
# Since the tree basically has the same information as the tab index, you could
|
||||
# consider setting DISABLE_INDEX to YES when enabling this option.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
GENERATE_TREEVIEW = YES
|
||||
|
||||
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
|
||||
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
|
||||
# area (value NO) or if it should extend to the full height of the window (value
|
||||
# YES). Setting this to YES gives a layout similar to
|
||||
# https://docs.readthedocs.io with more room for contents, but less room for the
|
||||
# project logo, title, and description. If either GENERATE_TREEVIEW or
|
||||
# DISABLE_INDEX is set to NO, this option has no effect.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FULL_SIDEBAR = NO
|
||||
|
||||
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
|
||||
# doxygen will group on one line in the generated HTML documentation.
|
||||
#
|
||||
@ -1513,6 +1607,24 @@ TREEVIEW_WIDTH = 250
|
||||
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
|
||||
# addresses.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
OBFUSCATE_EMAILS = YES
|
||||
|
||||
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
|
||||
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
|
||||
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
|
||||
# the HTML output. These images will generally look nicer at scaled resolutions.
|
||||
# Possible values are: png (the default) and svg (looks nicer but requires the
|
||||
# pdf2svg or inkscape tool).
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FORMULA_FORMAT = png
|
||||
|
||||
# Use this tag to change the font size of LaTeX formulas included as images in
|
||||
# the HTML documentation. When you change the font size after a successful
|
||||
# doxygen run you need to manually remove any form_*.png images from the HTML
|
||||
@ -1533,8 +1645,14 @@ FORMULA_FONTSIZE = 10
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
||||
FORMULA_MACROFILE =
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# https://www.mathjax.org) which uses client side JavaScript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
# installed or if you want to formulas look prettier in the HTML output. When
|
||||
# enabled you may also need to install MathJax separately and configure the path
|
||||
@ -1544,11 +1662,29 @@ FORMULA_TRANSPARENT = YES
|
||||
|
||||
USE_MATHJAX = YES
|
||||
|
||||
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
|
||||
# Note that the different versions of MathJax have different requirements with
|
||||
# regards to the different settings, so it is possible that also other MathJax
|
||||
# settings have to be changed when switching between the different MathJax
|
||||
# versions.
|
||||
# Possible values are: MathJax_2 and MathJax_3.
|
||||
# The default value is: MathJax_2.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_VERSION = MathJax_2
|
||||
|
||||
# When MathJax is enabled you can set the default output format to be used for
|
||||
# the MathJax output. See the MathJax site (see:
|
||||
# http://docs.mathjax.org/en/latest/output.html) for more details.
|
||||
# the MathJax output. For more details about the output format see MathJax
|
||||
# version 2 (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
|
||||
# (see:
|
||||
# http://docs.mathjax.org/en/latest/web/components/output.html).
|
||||
# Possible values are: HTML-CSS (which is slower, but has the best
|
||||
# compatibility), NativeMML (i.e. MathML) and SVG.
|
||||
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
|
||||
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
|
||||
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
|
||||
# is the name for Mathjax version 3, for MathJax version 2 this will be
|
||||
# translated into HTML-CSS) and SVG.
|
||||
# The default value is: HTML-CSS.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@ -1561,22 +1697,29 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
|
||||
# Content Delivery Network so you can quickly see the result without installing
|
||||
# MathJax. However, it is strongly recommended to install a local copy of
|
||||
# MathJax from https://www.mathjax.org before deployment.
|
||||
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
|
||||
# MathJax from https://www.mathjax.org before deployment. The default value is:
|
||||
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
|
||||
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest
|
||||
|
||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||
# extension names that should be enabled during MathJax rendering. For example
|
||||
# for MathJax version 2 (see
|
||||
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
|
||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||
# For example for MathJax version 3 (see
|
||||
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
||||
# MATHJAX_EXTENSIONS = ams
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_EXTENSIONS =
|
||||
|
||||
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
|
||||
# of code that will be used on startup of the MathJax code. See the MathJax site
|
||||
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
|
||||
# (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
|
||||
# example see the documentation.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@ -1604,7 +1747,7 @@ MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# implemented using a web server instead of a web client using JavaScript. There
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
@ -1623,7 +1766,8 @@ SERVER_BASED_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: https://xapian.org/).
|
||||
# Xapian (see:
|
||||
# https://xapian.org/).
|
||||
#
|
||||
# See the section "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
@ -1636,8 +1780,9 @@ EXTERNAL_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: https://xapian.org/). See the section "External Indexing and
|
||||
# Searching" for details.
|
||||
# Xapian (see:
|
||||
# https://xapian.org/). See the section "External Indexing and Searching" for
|
||||
# details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
SEARCHENGINE_URL =
|
||||
@ -1708,10 +1853,11 @@ LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
|
||||
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
|
||||
# generate index for LaTeX.
|
||||
# generate index for LaTeX. In case there is no backslash (\) as first character
|
||||
# it will be automatically added in the LaTeX code.
|
||||
# Note: This tag is used in the generated output file (.tex).
|
||||
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
|
||||
# The default value is: \makeindex.
|
||||
# The default value is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_MAKEINDEX_CMD = \makeindex
|
||||
@ -1745,29 +1891,31 @@ PAPER_TYPE = a4
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
# chapter. If it is left blank doxygen will generate a standard header. See
|
||||
# section "Doxygen usage" for information on how to let doxygen write the
|
||||
# default header to a separate file.
|
||||
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
|
||||
# the generated LaTeX document. The header should contain everything until the
|
||||
# first chapter. If it is left blank doxygen will generate a standard header. It
|
||||
# is highly recommended to start with a default header using
|
||||
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
|
||||
# and then modify the file new_header.tex. See also section "Doxygen usage" for
|
||||
# information on how to generate the default header that doxygen normally uses.
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
|
||||
# string, for the replacement values of the other commands the user is referred
|
||||
# to HTML_HEADER.
|
||||
# Note: Only use a user-defined header if you know what you are doing!
|
||||
# Note: The header is subject to change so you typically have to regenerate the
|
||||
# default header when upgrading to a newer version of doxygen. The following
|
||||
# commands have a special meaning inside the header (and footer): For a
|
||||
# description of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
|
||||
# the generated LaTeX document. The footer should contain everything after the
|
||||
# last chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# special commands can be used inside the footer. See also section "Doxygen
|
||||
# usage" for information on how to generate the default footer that doxygen
|
||||
# normally uses. Note: Only use a user-defined footer if you know what you are
|
||||
# doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
@ -1800,9 +1948,11 @@ LATEX_EXTRA_FILES =
|
||||
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
|
||||
# higher quality PDF documentation.
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
|
||||
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
|
||||
# files. Set this option to YES, to get a higher quality PDF documentation.
|
||||
#
|
||||
# See also section LATEX_CMD_NAME for selecting the engine.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@ -1810,8 +1960,7 @@ USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help. This option is also used
|
||||
# when generating formulas in HTML.
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@ -1824,16 +1973,6 @@ LATEX_BATCHMODE = NO
|
||||
|
||||
LATEX_HIDE_INDICES = NO
|
||||
|
||||
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
|
||||
# code with syntax highlighting in the LaTeX output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
@ -1914,16 +2053,6 @@ RTF_STYLESHEET_FILE =
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
|
||||
# with syntax highlighting in the RTF output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_SOURCE_CODE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2020,15 +2149,6 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2203,34 +2323,10 @@ EXTERNAL_GROUPS = YES
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
# The default file (with absolute path) is: /usr/bin/perl.
|
||||
|
||||
PERL_PATH = /usr/bin/perl
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
|
||||
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
|
||||
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
|
||||
# disabled, but it is recommended to install and use dot, since it yields more
|
||||
# powerful graphs.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_DIAGRAMS = NO
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. Doxygen will then run the mscgen tool (see:
|
||||
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
|
||||
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
@ -2287,11 +2383,14 @@ DOT_FONTSIZE = 10
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
|
||||
# each documented class showing the direct and indirect inheritance relations.
|
||||
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
CLASS_GRAPH = YES
|
||||
|
||||
@ -2328,10 +2427,32 @@ UML_LOOK = YES
|
||||
# but if the number exceeds 15, the total amount of fields shown is limited to
|
||||
# 10.
|
||||
# Minimum value: 0, maximum value: 100, default value: 10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
# This tag requires that the tag UML_LOOK is set to YES.
|
||||
|
||||
UML_LIMIT_NUM_FIELDS = 10
|
||||
|
||||
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
|
||||
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
|
||||
# tag is set to YES, doxygen will add type and arguments for attributes and
|
||||
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
|
||||
# will not generate fields with class member information in the UML graphs. The
|
||||
# class diagrams will look similar to the default class diagrams but using UML
|
||||
# notation for the relationships.
|
||||
# Possible values are: NO, YES and NONE.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag UML_LOOK is set to YES.
|
||||
|
||||
DOT_UML_DETAILS = NO
|
||||
|
||||
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
|
||||
# to display on a single line. If the actual line length exceeds this threshold
|
||||
# significantly it will wrapped across multiple lines. Some heuristics are apply
|
||||
# to avoid ugly line breaks.
|
||||
# Minimum value: 0, maximum value: 1000, default value: 17.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_WRAP_THRESHOLD = 17
|
||||
|
||||
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
|
||||
# collaboration graphs will show the relations between templates and their
|
||||
# instances.
|
||||
@ -2398,6 +2519,13 @@ GRAPHICAL_HIERARCHY = YES
|
||||
|
||||
DIRECTORY_GRAPH = YES
|
||||
|
||||
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
|
||||
# of child directories generated in directory dependency graphs by dot.
|
||||
# Minimum value: 1, maximum value: 25, default value: 1.
|
||||
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
|
||||
|
||||
DIR_GRAPH_MAX_DEPTH = 1
|
||||
|
||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
@ -2451,10 +2579,10 @@ MSCFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
|
||||
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
|
||||
# path where java can find the plantuml.jar file. If left blank, it is assumed
|
||||
# PlantUML is not used or called during a preprocessing step. Doxygen will
|
||||
# generate a warning when it encounters a \startuml command in this case and
|
||||
# will not generate output for the diagram.
|
||||
# path where java can find the plantuml.jar file or to the filename of jar file
|
||||
# to be used. If left blank, it is assumed PlantUML is not used or called during
|
||||
# a preprocessing step. Doxygen will generate a warning when it encounters a
|
||||
# \startuml command in this case and will not generate output for the diagram.
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
@ -2516,14 +2644,18 @@ DOT_MULTI_TARGETS = YES
|
||||
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
|
||||
# explaining the meaning of the various boxes and arrows in the dot generated
|
||||
# graphs.
|
||||
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
|
||||
# graphical representation for inheritance and collaboration diagrams is used.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
GENERATE_LEGEND = YES
|
||||
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
|
||||
# files that are used to generate the various graphs.
|
||||
#
|
||||
# Note: This setting is not only used for dot files but also for msc temporary
|
||||
# files.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
@ -4,7 +4,6 @@ includes:
|
||||
- tests/phpstan/configs/impossible-generics.neon
|
||||
- tests/phpstan/configs/php-bugs.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
- tests/phpstan/configs/pthreads-bugs.neon
|
||||
- tests/phpstan/configs/runtime-type-checks.neon
|
||||
- tests/phpstan/configs/spl-fixed-array-sucks.neon
|
||||
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||
@ -13,6 +12,7 @@ includes:
|
||||
|
||||
rules:
|
||||
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
|
||||
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
|
||||
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
|
||||
|
||||
parameters:
|
||||
|
Submodule resources/locale deleted from f9076e4a6e
@ -108,7 +108,7 @@ player:
|
||||
verify-xuid: true
|
||||
|
||||
level-settings:
|
||||
#The default format that levels will use when created
|
||||
#The default format that worlds will use when created
|
||||
default-format: leveldb
|
||||
|
||||
chunk-sending:
|
||||
@ -128,7 +128,9 @@ chunk-ticking:
|
||||
blocks-per-subchunk-per-tick: 3
|
||||
#IDs of blocks not to perform random ticking on.
|
||||
disable-block-ticking:
|
||||
#- 2 # grass
|
||||
#- grass
|
||||
#- ice
|
||||
#- fire
|
||||
|
||||
chunk-generation:
|
||||
#Max. amount of chunks in the waiting queue to be populated
|
||||
@ -176,7 +178,7 @@ aliases:
|
||||
#savestop: [save-all, stop]
|
||||
|
||||
worlds:
|
||||
#These settings will override the generator set in server.properties and allows loading multiple levels
|
||||
#These settings will override the generator set in server.properties and allows loading multiple worlds
|
||||
#Example:
|
||||
#world:
|
||||
# seed: 404
|
||||
|
@ -36,4 +36,5 @@ define('pocketmine\_CORE_CONSTANTS_INCLUDED', true);
|
||||
define('pocketmine\PATH', dirname(__DIR__) . '/');
|
||||
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
|
||||
define('pocketmine\BEDROCK_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-data/');
|
||||
define('pocketmine\LOCALE_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/locale-data/');
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');
|
||||
|
@ -1,382 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\errorhandler\ErrorTypeToStringMap;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function base64_encode;
|
||||
use function date;
|
||||
use function error_get_last;
|
||||
use function fclose;
|
||||
use function file;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function get_loaded_extensions;
|
||||
use function implode;
|
||||
use function is_dir;
|
||||
use function is_resource;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function ksort;
|
||||
use function max;
|
||||
use function mb_strtoupper;
|
||||
use function microtime;
|
||||
use function mkdir;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
use function ob_start;
|
||||
use function php_uname;
|
||||
use function phpinfo;
|
||||
use function phpversion;
|
||||
use function preg_replace;
|
||||
use function sprintf;
|
||||
use function str_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function zend_version;
|
||||
use function zlib_encode;
|
||||
use const FILE_IGNORE_NEW_LINES;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const PHP_EOL;
|
||||
use const PHP_OS;
|
||||
use const SORT_STRING;
|
||||
|
||||
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 = 4;
|
||||
|
||||
private const PLUGIN_INVOLVEMENT_NONE = "none";
|
||||
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
|
||||
private const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var resource */
|
||||
private $fp;
|
||||
/** @var float */
|
||||
private $time;
|
||||
/**
|
||||
* @var mixed[]
|
||||
* @phpstan-var array<string, mixed>
|
||||
*/
|
||||
private $data = [];
|
||||
/** @var string */
|
||||
private $encodedData = "";
|
||||
/** @var string */
|
||||
private $path;
|
||||
|
||||
private ?PluginManager $pluginManager;
|
||||
|
||||
public function __construct(Server $server, ?PluginManager $pluginManager){
|
||||
$this->time = microtime(true);
|
||||
$this->server = $server;
|
||||
$this->pluginManager = $pluginManager;
|
||||
|
||||
$crashPath = Path::join($this->server->getDataPath(), "crashdumps");
|
||||
if(!is_dir($crashPath)){
|
||||
mkdir($crashPath);
|
||||
}
|
||||
$this->path = Path::join($crashPath, date("D_M_j-H.i.s-T_Y", (int) $this->time) . ".log");
|
||||
$fp = @fopen($this->path, "wb");
|
||||
if(!is_resource($fp)){
|
||||
throw new \RuntimeException("Could not create Crash Dump");
|
||||
}
|
||||
$this->fp = $fp;
|
||||
$this->data["format_version"] = self::FORMAT_VERSION;
|
||||
$this->data["time"] = $this->time;
|
||||
$this->data["uptime"] = $this->time - $this->server->getStartTime();
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->time));
|
||||
$this->addLine();
|
||||
$this->baseCrash();
|
||||
$this->generalData();
|
||||
$this->pluginsData();
|
||||
|
||||
$this->extraData();
|
||||
|
||||
$this->encodeData();
|
||||
|
||||
fclose($this->fp);
|
||||
}
|
||||
|
||||
public function getPath() : string{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getEncodedData() : string{
|
||||
return $this->encodedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @phpstan-return array<string, mixed>
|
||||
*/
|
||||
public function getData() : array{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
private function encodeData() : void{
|
||||
$this->addLine();
|
||||
$this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
|
||||
$this->addLine();
|
||||
$this->addLine("===BEGIN CRASH DUMP===");
|
||||
$json = json_encode($this->data, JSON_UNESCAPED_SLASHES);
|
||||
if($json === false){
|
||||
throw new \RuntimeException("Failed to encode crashdump JSON: " . json_last_error_msg());
|
||||
}
|
||||
$zlibEncoded = zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9);
|
||||
if($zlibEncoded === false) throw new AssumptionFailedError("ZLIB compression failed");
|
||||
$this->encodedData = $zlibEncoded;
|
||||
foreach(str_split(base64_encode($this->encodedData), 76) as $line){
|
||||
$this->addLine($line);
|
||||
}
|
||||
$this->addLine("===END CRASH DUMP===");
|
||||
}
|
||||
|
||||
private function pluginsData() : void{
|
||||
if($this->pluginManager !== null){
|
||||
$plugins = $this->pluginManager->getPlugins();
|
||||
$this->addLine();
|
||||
$this->addLine("Loaded plugins:");
|
||||
$this->data["plugins"] = [];
|
||||
ksort($plugins, SORT_STRING);
|
||||
foreach($plugins as $p){
|
||||
$d = $p->getDescription();
|
||||
$this->data["plugins"][$d->getName()] = [
|
||||
"name" => $d->getName(),
|
||||
"version" => $d->getVersion(),
|
||||
"authors" => $d->getAuthors(),
|
||||
"api" => $d->getCompatibleApis(),
|
||||
"enabled" => $p->isEnabled(),
|
||||
"depends" => $d->getDepend(),
|
||||
"softDepends" => $d->getSoftDepend(),
|
||||
"main" => $d->getMain(),
|
||||
"load" => mb_strtoupper($d->getOrder()->name()),
|
||||
"website" => $d->getWebsite()
|
||||
];
|
||||
$this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function extraData() : void{
|
||||
global $argv;
|
||||
|
||||
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){
|
||||
$this->data["parameters"] = (array) $argv;
|
||||
if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){
|
||||
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties);
|
||||
}else{
|
||||
$this->data["server.properties"] = $serverDotProperties;
|
||||
}
|
||||
if(($pocketmineDotYml = @file_get_contents(Path::join($this->server->getDataPath(), "pocketmine.yml"))) !== false){
|
||||
$this->data["pocketmine.yml"] = $pocketmineDotYml;
|
||||
}else{
|
||||
$this->data["pocketmine.yml"] = "";
|
||||
}
|
||||
}else{
|
||||
$this->data["pocketmine.yml"] = "";
|
||||
$this->data["server.properties"] = "";
|
||||
$this->data["parameters"] = [];
|
||||
}
|
||||
$extensions = [];
|
||||
foreach(get_loaded_extensions() as $ext){
|
||||
$extensions[$ext] = phpversion($ext);
|
||||
}
|
||||
$this->data["extensions"] = $extensions;
|
||||
|
||||
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){
|
||||
ob_start();
|
||||
phpinfo();
|
||||
$this->data["phpinfo"] = ob_get_contents();
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
|
||||
private function baseCrash() : void{
|
||||
global $lastExceptionError, $lastError;
|
||||
|
||||
if(isset($lastExceptionError)){
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = error_get_last();
|
||||
if($error === null){
|
||||
throw new \RuntimeException("Crash error information missing - did something use exit()?");
|
||||
}
|
||||
$error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["fullFile"] = $error["file"];
|
||||
$error["file"] = Filesystem::cleanPath($error["file"]);
|
||||
try{
|
||||
$error["type"] = ErrorTypeToStringMap::get($error["type"]);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
//pass
|
||||
}
|
||||
if(($pos = strpos($error["message"], "\n")) !== false){
|
||||
$error["message"] = substr($error["message"], 0, $pos);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($lastError)){
|
||||
if(isset($lastError["trace"])){
|
||||
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
|
||||
}
|
||||
$this->data["lastError"] = $lastError;
|
||||
}
|
||||
|
||||
$this->data["error"] = $error;
|
||||
unset($this->data["error"]["fullFile"]);
|
||||
unset($this->data["error"]["trace"]);
|
||||
$this->addLine("Error: " . $error["message"]);
|
||||
$this->addLine("File: " . $error["file"]);
|
||||
$this->addLine("Line: " . $error["line"]);
|
||||
$this->addLine("Type: " . $error["type"]);
|
||||
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_NONE;
|
||||
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
|
||||
foreach($error["trace"] as $frame){
|
||||
if(!isset($frame["file"])){
|
||||
continue; //PHP core
|
||||
}
|
||||
if($this->determinePluginFromFile($frame["file"], false)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addLine();
|
||||
$this->addLine("Code:");
|
||||
$this->data["code"] = [];
|
||||
|
||||
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) and file_exists($error["fullFile"])){
|
||||
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
|
||||
if($file !== false){
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addLine();
|
||||
$this->addLine("Backtrace:");
|
||||
foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){
|
||||
$this->addLine($line);
|
||||
}
|
||||
$this->addLine();
|
||||
}
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
$frameCleanPath = Filesystem::cleanPath($filePath);
|
||||
if(strpos($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX) !== 0){
|
||||
$this->addLine();
|
||||
if($crashFrame){
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT;
|
||||
}else{
|
||||
$this->addLine("A PLUGIN WAS INVOLVED IN THIS CRASH");
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
|
||||
}
|
||||
|
||||
if(file_exists($filePath)){
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Filesystem::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function generalData() : void{
|
||||
$version = VersionInfo::VERSION();
|
||||
$composerLibraries = [];
|
||||
foreach(InstalledVersions::getInstalledPackages() as $package){
|
||||
$composerLibraries[$package] = sprintf(
|
||||
"%s@%s",
|
||||
InstalledVersions::getPrettyVersion($package) ?? "unknown",
|
||||
InstalledVersions::getReference($package) ?? "unknown"
|
||||
);
|
||||
}
|
||||
|
||||
$this->data["general"] = [];
|
||||
$this->data["general"]["name"] = $this->server->getName();
|
||||
$this->data["general"]["base_version"] = VersionInfo::BASE_VERSION;
|
||||
$this->data["general"]["build"] = VersionInfo::BUILD_NUMBER;
|
||||
$this->data["general"]["is_dev"] = VersionInfo::IS_DEVELOPMENT_BUILD;
|
||||
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
|
||||
$this->data["general"]["git"] = VersionInfo::GIT_HASH();
|
||||
$this->data["general"]["uname"] = php_uname("a");
|
||||
$this->data["general"]["php"] = phpversion();
|
||||
$this->data["general"]["zend"] = zend_version();
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->data["general"]["composer_libraries"] = $composerLibraries;
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . VersionInfo::GIT_HASH());
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
$this->addLine("PHP Version: " . phpversion());
|
||||
$this->addLine("Zend version: " . zend_version());
|
||||
$this->addLine("OS: " . PHP_OS . ", " . Utils::getOS());
|
||||
$this->addLine("Composer libraries: ");
|
||||
foreach($composerLibraries as $library => $libraryVersion){
|
||||
$this->addLine("- $library $libraryVersion");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $line
|
||||
*/
|
||||
public function addLine($line = "") : void{
|
||||
fwrite($this->fp, $line . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
*/
|
||||
public function add($str) : void{
|
||||
fwrite($this->fp, $str);
|
||||
}
|
||||
}
|
@ -28,7 +28,6 @@ use pocketmine\network\mcpe\cache\ChunkCache;
|
||||
use pocketmine\scheduler\DumpWorkerMemoryTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Process;
|
||||
use pocketmine\utils\Utils;
|
||||
use Webmozart\PathUtil\Path;
|
||||
@ -65,6 +64,7 @@ use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
@ -168,14 +168,14 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
public function canUseChunkCache() : bool{
|
||||
return !$this->lowMemory or !$this->lowMemDisableChunkCache;
|
||||
return !$this->lowMemory || !$this->lowMemDisableChunkCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the allowed chunk radius based on the current memory usage.
|
||||
*/
|
||||
public function getViewDistance(int $distance) : int{
|
||||
return ($this->lowMemory and $this->lowMemChunkRadiusOverride > 0) ? min($this->lowMemChunkRadiusOverride, $distance) : $distance;
|
||||
return ($this->lowMemory && $this->lowMemChunkRadiusOverride > 0) ? min($this->lowMemChunkRadiusOverride, $distance) : $distance;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,18 +214,18 @@ class MemoryManager{
|
||||
public function check() : void{
|
||||
Timings::$memoryManager->startTiming();
|
||||
|
||||
if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){
|
||||
if(($this->memoryLimit > 0 || $this->globalMemoryLimit > 0) && ++$this->checkTicker >= $this->checkRate){
|
||||
$this->checkTicker = 0;
|
||||
$memory = Process::getAdvancedMemoryUsage();
|
||||
$trigger = false;
|
||||
if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){
|
||||
if($this->memoryLimit > 0 && $memory[0] > $this->memoryLimit){
|
||||
$trigger = 0;
|
||||
}elseif($this->globalMemoryLimit > 0 and $memory[1] > $this->globalMemoryLimit){
|
||||
}elseif($this->globalMemoryLimit > 0 && $memory[1] > $this->globalMemoryLimit){
|
||||
$trigger = 1;
|
||||
}
|
||||
|
||||
if($trigger !== false){
|
||||
if($this->lowMemory and $this->continuousTrigger){
|
||||
if($this->lowMemory && $this->continuousTrigger){
|
||||
if(++$this->continuousTriggerTicker >= $this->continuousTriggerRate){
|
||||
$this->continuousTriggerTicker = 0;
|
||||
$this->trigger($memory[$trigger], $this->memoryLimit, $trigger > 0, ++$this->continuousTriggerCount);
|
||||
@ -240,7 +240,7 @@ class MemoryManager{
|
||||
}
|
||||
}
|
||||
|
||||
if($this->garbageCollectionPeriod > 0 and ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
|
||||
if($this->garbageCollectionPeriod > 0 && ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
|
||||
$this->garbageCollectionTicker = 0;
|
||||
$this->triggerGarbageCollector();
|
||||
}
|
||||
@ -291,8 +291,7 @@ class MemoryManager{
|
||||
* @param mixed $startingObject
|
||||
*/
|
||||
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
|
||||
$hardLimit = ini_get('memory_limit');
|
||||
if($hardLimit === false) throw new AssumptionFailedError("memory_limit INI directive should always exist");
|
||||
$hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
|
||||
ini_set('memory_limit', '-1');
|
||||
gc_disable();
|
||||
|
||||
@ -300,7 +299,7 @@ class MemoryManager{
|
||||
mkdir($outputFolder, 0777, true);
|
||||
}
|
||||
|
||||
$obData = fopen(Path::join($outputFolder, "objects.js"), "wb+");
|
||||
$obData = Utils::assumeNotFalse(fopen(Path::join($outputFolder, "objects.js"), "wb+"));
|
||||
|
||||
$objects = [];
|
||||
|
||||
@ -318,13 +317,16 @@ class MemoryManager{
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$staticProperties[$className] = [];
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if(!$property->isStatic() or $property->getDeclaringClass()->getName() !== $className){
|
||||
if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
if(!$property->isInitialized()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$staticCount++;
|
||||
$staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
@ -349,38 +351,36 @@ class MemoryManager{
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $staticCount static properties");
|
||||
|
||||
if(isset($GLOBALS)){ //This might be null if we're on a different thread
|
||||
$globalVariables = [];
|
||||
$globalCount = 0;
|
||||
$globalVariables = [];
|
||||
$globalCount = 0;
|
||||
|
||||
$ignoredGlobals = [
|
||||
'GLOBALS' => true,
|
||||
'_SERVER' => true,
|
||||
'_REQUEST' => true,
|
||||
'_POST' => true,
|
||||
'_GET' => true,
|
||||
'_FILES' => true,
|
||||
'_ENV' => true,
|
||||
'_COOKIE' => true,
|
||||
'_SESSION' => true
|
||||
];
|
||||
$ignoredGlobals = [
|
||||
'GLOBALS' => true,
|
||||
'_SERVER' => true,
|
||||
'_REQUEST' => true,
|
||||
'_POST' => true,
|
||||
'_GET' => true,
|
||||
'_FILES' => true,
|
||||
'_ENV' => true,
|
||||
'_COOKIE' => true,
|
||||
'_SESSION' => true
|
||||
];
|
||||
|
||||
foreach($GLOBALS as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$globalCount++;
|
||||
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
foreach(Utils::stringifyKeys($GLOBALS) as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
$logger->info("Wrote $globalCount global variables");
|
||||
$globalCount++;
|
||||
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $globalCount global variables");
|
||||
|
||||
foreach(get_defined_functions()["user"] as $function){
|
||||
$reflect = new \ReflectionFunction($function);
|
||||
|
||||
@ -393,7 +393,7 @@ class MemoryManager{
|
||||
$functionStaticVarsCount += count($vars);
|
||||
}
|
||||
}
|
||||
file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $functionStaticVarsCount function static variables");
|
||||
|
||||
$data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
@ -450,13 +450,16 @@ class MemoryManager{
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
if(!$property->isInitialized($object)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
|
||||
fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");
|
||||
}
|
||||
|
||||
}while($continue);
|
||||
@ -465,11 +468,11 @@ class MemoryManager{
|
||||
|
||||
fclose($obData);
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
|
||||
arsort($instanceCounts, SORT_NUMERIC);
|
||||
file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
|
||||
$logger->info("Finished!");
|
||||
|
||||
@ -505,8 +508,13 @@ class MemoryManager{
|
||||
return "(error) ARRAY RECURSION LIMIT REACHED";
|
||||
}
|
||||
$data = [];
|
||||
$numeric = 0;
|
||||
foreach($from as $key => $value){
|
||||
$data[$key] = self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize);
|
||||
$data[$numeric] = [
|
||||
"k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
|
||||
"v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
|
||||
];
|
||||
$numeric++;
|
||||
}
|
||||
}elseif(is_string($from)){
|
||||
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||
|
@ -32,13 +32,16 @@ namespace pocketmine {
|
||||
use pocketmine\utils\ServerKiller;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\Timezone;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function getcwd;
|
||||
use function phpversion;
|
||||
use function preg_match;
|
||||
use function preg_quote;
|
||||
use function strpos;
|
||||
use function realpath;
|
||||
use function version_compare;
|
||||
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
@ -111,8 +114,7 @@ namespace pocketmine {
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("pthreads")){
|
||||
$pthreads_version = phpversion("pthreads");
|
||||
if(($pthreads_version = phpversion("pthreads")) !== false){
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
@ -121,8 +123,7 @@ namespace pocketmine {
|
||||
}
|
||||
}
|
||||
|
||||
if(extension_loaded("leveldb")){
|
||||
$leveldb_version = phpversion("leveldb");
|
||||
if(($leveldb_version = phpversion("leveldb")) !== false){
|
||||
if(version_compare($leveldb_version, "0.2.1") < 0){
|
||||
$messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
|
||||
}
|
||||
@ -145,6 +146,10 @@ namespace pocketmine {
|
||||
$messages[] = "The native PocketMine extension is no longer supported.";
|
||||
}
|
||||
|
||||
if(!defined('AF_INET6')){
|
||||
$messages[] = "IPv6 support is required, but your PHP binary was built without IPv6 support.";
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
@ -208,6 +213,8 @@ JIT_WARNING
|
||||
}
|
||||
critical_error("PHP binary used: " . $binary);
|
||||
critical_error("Loaded php.ini: " . (($file = php_ini_loaded_file()) !== false ? $file : "none"));
|
||||
$phprc = getenv("PHPRC");
|
||||
critical_error("Value of PHPRC environment variable: " . ($phprc === false ? "" : $phprc));
|
||||
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);
|
||||
@ -246,8 +253,9 @@ JIT_WARNING
|
||||
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
|
||||
$dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR;
|
||||
$pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR;
|
||||
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
|
||||
$dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR;
|
||||
$pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR;
|
||||
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
|
||||
|
||||
if(!file_exists($dataPath)){
|
||||
@ -279,7 +287,7 @@ JIT_WARNING
|
||||
|
||||
$exitCode = 0;
|
||||
do{
|
||||
if(!file_exists(Path::join($dataPath, "server.properties")) and !isset($opts["no-wizard"])){
|
||||
if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts["no-wizard"])){
|
||||
$installer = new SetupWizard($dataPath);
|
||||
if(!$installer->run()){
|
||||
$exitCode = -1;
|
||||
|
164
src/Server.php
164
src/Server.php
@ -34,7 +34,8 @@ use pocketmine\console\ConsoleCommandSender;
|
||||
use pocketmine\console\ConsoleReaderThread;
|
||||
use pocketmine\crafting\CraftingManager;
|
||||
use pocketmine\crafting\CraftingManagerFromDataHelper;
|
||||
use pocketmine\data\java\GameModeIdMap;
|
||||
use pocketmine\crash\CrashDump;
|
||||
use pocketmine\crash\CrashDumpRenderer;
|
||||
use pocketmine\entity\EntityDataHelper;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\event\HandlerListManager;
|
||||
@ -120,13 +121,18 @@ use function base64_encode;
|
||||
use function cli_set_process_title;
|
||||
use function copy;
|
||||
use function count;
|
||||
use function date;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function fopen;
|
||||
use function get_class;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_resource;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function max;
|
||||
@ -171,6 +177,12 @@ class Server{
|
||||
public const BROADCAST_CHANNEL_ADMINISTRATIVE = "pocketmine.broadcast.admin";
|
||||
public const BROADCAST_CHANNEL_USERS = "pocketmine.broadcast.user";
|
||||
|
||||
public const DEFAULT_SERVER_NAME = VersionInfo::NAME . " Server";
|
||||
public const DEFAULT_MAX_PLAYERS = 20;
|
||||
public const DEFAULT_PORT_IPV4 = 19132;
|
||||
public const DEFAULT_PORT_IPV6 = 19133;
|
||||
public const DEFAULT_MAX_VIEW_DISTANCE = 16;
|
||||
|
||||
private static ?Server $instance = null;
|
||||
|
||||
private SleeperHandler $tickSleeper;
|
||||
@ -317,11 +329,15 @@ class Server{
|
||||
}
|
||||
|
||||
public function getPort() : int{
|
||||
return $this->configGroup->getConfigInt("server-port", 19132);
|
||||
return $this->configGroup->getConfigInt("server-port", self::DEFAULT_PORT_IPV4);
|
||||
}
|
||||
|
||||
public function getPortV6() : int{
|
||||
return $this->configGroup->getConfigInt("server-portv6", self::DEFAULT_PORT_IPV6);
|
||||
}
|
||||
|
||||
public function getViewDistance() : int{
|
||||
return max(2, $this->configGroup->getConfigInt("view-distance", 8));
|
||||
return max(2, $this->configGroup->getConfigInt("view-distance", self::DEFAULT_MAX_VIEW_DISTANCE));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,12 +352,17 @@ class Server{
|
||||
return $str !== "" ? $str : "0.0.0.0";
|
||||
}
|
||||
|
||||
public function getIpV6() : string{
|
||||
$str = $this->configGroup->getConfigString("server-ipv6");
|
||||
return $str !== "" ? $str : "::";
|
||||
}
|
||||
|
||||
public function getServerUniqueId() : UuidInterface{
|
||||
return $this->serverID;
|
||||
}
|
||||
|
||||
public function getGamemode() : GameMode{
|
||||
return GameModeIdMap::getInstance()->fromId($this->configGroup->getConfigInt("gamemode", 0)) ?? GameMode::SURVIVAL();
|
||||
return GameMode::fromString($this->configGroup->getConfigString("gamemode", GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL();
|
||||
}
|
||||
|
||||
public function getForceGamemode() : bool{
|
||||
@ -364,7 +385,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function getMotd() : string{
|
||||
return $this->configGroup->getConfigString("motd", VersionInfo::NAME . " Server");
|
||||
return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME);
|
||||
}
|
||||
|
||||
public function getLoader() : \DynamicClassLoader{
|
||||
@ -521,9 +542,10 @@ class Server{
|
||||
if(!$ev->isCancelled()){
|
||||
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
|
||||
try{
|
||||
file_put_contents($this->getPlayerDataPath($name), zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP));
|
||||
}catch(\ErrorException $e){
|
||||
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
|
||||
}catch(\RuntimeException $e){
|
||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
@ -539,7 +561,7 @@ class Server{
|
||||
$ev->call();
|
||||
$class = $ev->getPlayerClass();
|
||||
|
||||
if($offlinePlayerData !== null and ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
|
||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
|
||||
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
||||
$spawn = $playerPos->asVector3();
|
||||
}else{
|
||||
@ -674,7 +696,13 @@ class Server{
|
||||
}
|
||||
|
||||
public function removeOp(string $name) : void{
|
||||
$this->operators->remove(strtolower($name));
|
||||
$lowercaseName = strtolower($name);
|
||||
foreach($this->operators->getAll() as $operatorName => $_){
|
||||
$operatorName = (string) $operatorName;
|
||||
if($lowercaseName === strtolower($operatorName)){
|
||||
$this->operators->remove($operatorName);
|
||||
}
|
||||
}
|
||||
|
||||
if(($player = $this->getPlayerExact($name)) !== null){
|
||||
$player->unsetBasePermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
@ -693,7 +721,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function isWhitelisted(string $name) : bool{
|
||||
return !$this->hasWhitelist() or $this->operators->exists($name, true) or $this->whitelist->exists($name, true);
|
||||
return !$this->hasWhitelist() || $this->operators->exists($name, true) || $this->whitelist->exists($name, true);
|
||||
}
|
||||
|
||||
public function isOp(string $name) : bool{
|
||||
@ -771,7 +799,7 @@ class Server{
|
||||
$this->logger->info("Loading server configuration");
|
||||
$pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml");
|
||||
if(!file_exists($pocketmineYmlPath)){
|
||||
$content = file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml"));
|
||||
$content = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")), "Missing required resource file");
|
||||
if(VersionInfo::IS_DEVELOPMENT_BUILD){
|
||||
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
|
||||
}
|
||||
@ -781,11 +809,13 @@ class Server{
|
||||
$this->configGroup = new ServerConfigGroup(
|
||||
new Config($pocketmineYmlPath, Config::YAML, []),
|
||||
new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES, [
|
||||
"motd" => VersionInfo::NAME . " Server",
|
||||
"server-port" => 19132,
|
||||
"motd" => self::DEFAULT_SERVER_NAME,
|
||||
"server-port" => self::DEFAULT_PORT_IPV4,
|
||||
"server-portv6" => self::DEFAULT_PORT_IPV6,
|
||||
"enable-ipv6" => true,
|
||||
"white-list" => false,
|
||||
"max-players" => 20,
|
||||
"gamemode" => 0,
|
||||
"max-players" => self::DEFAULT_MAX_PLAYERS,
|
||||
"gamemode" => GameMode::SURVIVAL()->name(),
|
||||
"force-gamemode" => false,
|
||||
"hardcore" => false,
|
||||
"pvp" => true,
|
||||
@ -796,7 +826,7 @@ class Server{
|
||||
"level-type" => "DEFAULT",
|
||||
"enable-query" => true,
|
||||
"auto-save" => true,
|
||||
"view-distance" => 8,
|
||||
"view-distance" => self::DEFAULT_MAX_VIEW_DISTANCE,
|
||||
"xbox-auth" => true,
|
||||
"language" => "eng"
|
||||
])
|
||||
@ -865,7 +895,7 @@ class Server{
|
||||
}
|
||||
|
||||
$netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6);
|
||||
if($netCompressionLevel < 1 or $netCompressionLevel > 9){
|
||||
if($netCompressionLevel < 1 || $netCompressionLevel > 9){
|
||||
$this->logger->warning("Invalid network compression level $netCompressionLevel set, setting to default 6");
|
||||
$netCompressionLevel = 6;
|
||||
}
|
||||
@ -882,7 +912,7 @@ class Server{
|
||||
|
||||
$bannedTxt = Path::join($this->dataPath, "banned.txt");
|
||||
$bannedPlayersTxt = Path::join($this->dataPath, "banned-players.txt");
|
||||
if(file_exists($bannedTxt) and !file_exists($bannedPlayersTxt)){
|
||||
if(file_exists($bannedTxt) && !file_exists($bannedPlayersTxt)){
|
||||
@rename($bannedTxt, $bannedPlayersTxt);
|
||||
}
|
||||
@touch($bannedPlayersTxt);
|
||||
@ -893,7 +923,7 @@ class Server{
|
||||
$this->banByIP = new BanList($bannedIpsTxt);
|
||||
$this->banByIP->load();
|
||||
|
||||
$this->maxPlayers = $this->configGroup->getConfigInt("max-players", 20);
|
||||
$this->maxPlayers = $this->configGroup->getConfigInt("max-players", self::DEFAULT_MAX_PLAYERS);
|
||||
|
||||
$this->onlineMode = $this->configGroup->getConfigBool("xbox-auth", true);
|
||||
if($this->onlineMode){
|
||||
@ -904,7 +934,7 @@ class Server{
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
|
||||
}
|
||||
|
||||
if($this->configGroup->getConfigBool("hardcore", false) and $this->getDifficulty() < World::DIFFICULTY_HARD){
|
||||
if($this->configGroup->getConfigBool("hardcore", false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
|
||||
$this->configGroup->setConfigInt("difficulty", World::DIFFICULTY_HARD);
|
||||
}
|
||||
|
||||
@ -954,7 +984,7 @@ class Server{
|
||||
|
||||
$providerManager = new WorldProviderManager();
|
||||
if(
|
||||
($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null and
|
||||
($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null &&
|
||||
$format instanceof WritableWorldProviderManagerEntry
|
||||
){
|
||||
$providerManager->setDefault($format);
|
||||
@ -1113,25 +1143,44 @@ class Server{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function startupPrepareNetworkInterfaces() : bool{
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
|
||||
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
|
||||
$prettyIp = $ipV6 ? "[$ip]" : $ip;
|
||||
try{
|
||||
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this));
|
||||
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6));
|
||||
}catch(NetworkInterfaceStartException $e){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
|
||||
$this->getIp(),
|
||||
(string) $this->getPort(),
|
||||
$ip,
|
||||
(string) $port,
|
||||
$e->getMessage()
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
if(!$rakLibRegistered && $useQuery){
|
||||
//RakLib would normally handle the transport for Query packets
|
||||
//if it's not registered we need to make sure Query still works
|
||||
$this->network->registerInterface(new DedicatedQueryNetworkInterface($this->getIp(), $this->getPort(), new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
|
||||
if($rakLibRegistered){
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
|
||||
}
|
||||
if($useQuery){
|
||||
if(!$rakLibRegistered){
|
||||
//RakLib would normally handle the transport for Query packets
|
||||
//if it's not registered we need to make sure Query still works
|
||||
$this->network->registerInterface(new DedicatedQueryNetworkInterface($ip, $port, $ipV6, new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_query_running($prettyIp, (string) $port)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function startupPrepareNetworkInterfaces() : bool{
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
|
||||
if(
|
||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery) ||
|
||||
(
|
||||
$this->configGroup->getConfigBool("enable-ipv6", true) &&
|
||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery)
|
||||
)
|
||||
){
|
||||
return false;
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($this->getIp(), (string) $this->getPort())));
|
||||
|
||||
if($useQuery){
|
||||
$this->network->registerRawPacketHandler(new QueryHandler($this));
|
||||
@ -1172,7 +1221,7 @@ class Server{
|
||||
* Unsubscribes from all broadcast channels.
|
||||
*/
|
||||
public function unsubscribeFromAllBroadcastChannels(CommandSender $subscriber) : void{
|
||||
foreach($this->broadcastSubscribers as $channelId => $recipients){
|
||||
foreach(Utils::stringifyKeys($this->broadcastSubscribers) as $channelId => $recipients){
|
||||
$this->unsubscribeFromBroadcastChannel($channelId, $subscriber);
|
||||
}
|
||||
}
|
||||
@ -1332,7 +1381,7 @@ class Server{
|
||||
|
||||
public function enablePlugins(PluginEnableOrder $type) : void{
|
||||
foreach($this->pluginManager->getPlugins() as $plugin){
|
||||
if(!$plugin->isEnabled() and $plugin->getDescription()->getOrder()->equals($type)){
|
||||
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
|
||||
$this->pluginManager->enablePlugin($plugin);
|
||||
}
|
||||
}
|
||||
@ -1378,6 +1427,9 @@ class Server{
|
||||
echo "\x1b]0;\x07";
|
||||
}
|
||||
|
||||
if($this->isRunning){
|
||||
$this->logger->emergency("Forcing server shutdown");
|
||||
}
|
||||
try{
|
||||
if(!$this->isRunning()){
|
||||
$this->sendUsage(SendUsageTask::TYPE_CLOSE);
|
||||
@ -1476,6 +1528,25 @@ class Server{
|
||||
$this->crashDump();
|
||||
}
|
||||
|
||||
private function writeCrashDumpFile(CrashDump $dump) : string{
|
||||
$crashFolder = Path::join($this->getDataPath(), "crashdumps");
|
||||
if(!is_dir($crashFolder)){
|
||||
mkdir($crashFolder);
|
||||
}
|
||||
$crashDumpPath = Path::join($crashFolder, date("D_M_j-H.i.s-T_Y", (int) $dump->getData()->time) . ".log");
|
||||
|
||||
$fp = @fopen($crashDumpPath, "wb");
|
||||
if(!is_resource($fp)){
|
||||
throw new \RuntimeException("Unable to open new file to generate crashdump");
|
||||
}
|
||||
$writer = new CrashDumpRenderer($fp, $dump->getData());
|
||||
$writer->renderHumanReadable();
|
||||
$dump->encodeData($writer);
|
||||
|
||||
fclose($fp);
|
||||
return $crashDumpPath;
|
||||
}
|
||||
|
||||
public function crashDump() : void{
|
||||
while(@ob_end_flush()){}
|
||||
if(!$this->isRunning){
|
||||
@ -1492,32 +1563,35 @@ class Server{
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_create()));
|
||||
$dump = new CrashDump($this, $this->pluginManager ?? null);
|
||||
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($dump->getPath())));
|
||||
$crashDumpPath = $this->writeCrashDumpFile($dump);
|
||||
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath)));
|
||||
|
||||
if($this->configGroup->getPropertyBool("auto-report.enabled", true)){
|
||||
$report = true;
|
||||
|
||||
$stamp = Path::join($this->getDataPath(), "crashdumps", ".last_crash");
|
||||
$crashInterval = 120; //2 minutes
|
||||
if(file_exists($stamp) and !($report = (filemtime($stamp) + $crashInterval < time()))){
|
||||
if(($lastReportTime = @filemtime($stamp)) !== false && $lastReportTime + $crashInterval >= time()){
|
||||
$report = false;
|
||||
$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)){
|
||||
$plugin = $dump->getData()->plugin;
|
||||
if($plugin !== ""){
|
||||
$p = $this->pluginManager->getPlugin($plugin);
|
||||
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
|
||||
if($p instanceof Plugin && !($p->getPluginLoader() instanceof PharPluginLoader)){
|
||||
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
|
||||
$report = false;
|
||||
}
|
||||
}
|
||||
|
||||
if($dump->getData()["error"]["type"] === \ParseError::class){
|
||||
if($dump->getData()->error["type"] === \ParseError::class){
|
||||
$report = false;
|
||||
}
|
||||
|
||||
if(strrpos(VersionInfo::GIT_HASH(), "-dirty") !== false or VersionInfo::GIT_HASH() === str_repeat("00", 20)){
|
||||
if(strrpos(VersionInfo::GIT_HASH(), "-dirty") !== false || VersionInfo::GIT_HASH() === str_repeat("00", 20)){
|
||||
$this->logger->debug("Not sending crashdump due to locally modified");
|
||||
$report = false; //Don't send crashdumps for locally modified builds
|
||||
}
|
||||
@ -1532,8 +1606,8 @@ class Server{
|
||||
"reportPaste" => base64_encode($dump->getEncodedData())
|
||||
], 10, [], $postUrlError);
|
||||
|
||||
if($reply !== null and ($data = json_decode($reply->getBody())) !== null){
|
||||
if(isset($data->crashId) and isset($data->crashUrl)){
|
||||
if($reply !== null && ($data = json_decode($reply->getBody())) !== null){
|
||||
if(isset($data->crashId) && isset($data->crashUrl)){
|
||||
$reportId = $data->crashId;
|
||||
$reportUrl = $data->crashUrl;
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
|
||||
@ -1590,7 +1664,7 @@ class Server{
|
||||
public function addOnlinePlayer(Player $player) : bool{
|
||||
$ev = new PlayerLoginEvent($player, "Plugin reason");
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$player->isConnected()){
|
||||
if($ev->isCancelled() || !$player->isConnected()){
|
||||
$player->disconnect($ev->getKickMessage());
|
||||
|
||||
return false;
|
||||
@ -1719,7 +1793,7 @@ class Server{
|
||||
$this->network->getBandwidthTracker()->rotateAverageHistory();
|
||||
}
|
||||
|
||||
if($this->sendUsageTicker > 0 and --$this->sendUsageTicker === 0){
|
||||
if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){
|
||||
$this->sendUsageTicker = 6000;
|
||||
$this->sendUsage(SendUsageTask::TYPE_STATUS);
|
||||
}
|
||||
|
@ -25,14 +25,15 @@ namespace pocketmine;
|
||||
|
||||
use pocketmine\utils\Git;
|
||||
use pocketmine\utils\VersionString;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.0.0-BETA10";
|
||||
public const BASE_VERSION = "4.2.4";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_NUMBER = 0;
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
@ -61,12 +62,29 @@ final class VersionInfo{
|
||||
return self::$gitHash;
|
||||
}
|
||||
|
||||
private static ?int $buildNumber = null;
|
||||
|
||||
public static function BUILD_NUMBER() : int{
|
||||
if(self::$buildNumber === null){
|
||||
self::$buildNumber = 0;
|
||||
if(\Phar::running(true) !== ""){
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){
|
||||
self::$buildNumber = $meta["build"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$buildNumber;
|
||||
}
|
||||
|
||||
/** @var VersionString|null */
|
||||
private static $fullVersion = null;
|
||||
|
||||
public static function VERSION() : VersionString{
|
||||
if(self::$fullVersion === null){
|
||||
self::$fullVersion = new VersionString(self::BASE_VERSION, self::IS_DEVELOPMENT_BUILD, self::BUILD_NUMBER);
|
||||
self::$fullVersion = new VersionString(self::BASE_VERSION, self::IS_DEVELOPMENT_BUILD, self::BUILD_NUMBER());
|
||||
}
|
||||
return self::$fullVersion;
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ class Bamboo extends Transparent{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$below = $this->position->getWorld()->getBlock($this->position->down());
|
||||
if(!$this->canBeSupportedBy($below) and !$below->isSameType($this)){
|
||||
if(!$this->canBeSupportedBy($below) && !$below->isSameType($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
@ -36,14 +36,14 @@ final class BambooSapling extends Flowable{
|
||||
private bool $ready = false;
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->ready = ($stateMeta & BlockLegacyMetadata::SAPLING_FLAG_READY) !== 0;
|
||||
$this->ready = ($stateMeta & BlockLegacyMetadata::BAMBOO_SAPLING_FLAG_READY) !== 0;
|
||||
}
|
||||
|
||||
protected function writeStateToMeta() : int{
|
||||
return $this->ready ? BlockLegacyMetadata::SAPLING_FLAG_READY : 0;
|
||||
return $this->ready ? BlockLegacyMetadata::BAMBOO_SAPLING_FLAG_READY : 0;
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{ return 0b1000; }
|
||||
public function getStateBitmask() : int{ return 0b1; }
|
||||
|
||||
public function isReady() : bool{ return $this->ready; }
|
||||
|
||||
|
@ -130,7 +130,7 @@ abstract class BaseBanner extends Transparent{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
$drop = $this->asItem();
|
||||
if($drop instanceof ItemBanner and count($this->patterns) > 0){
|
||||
if($drop instanceof ItemBanner && count($this->patterns) > 0){
|
||||
$drop->setPatterns($this->patterns);
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ abstract class BaseBanner extends Transparent{
|
||||
|
||||
public function getPickedItem(bool $addUserData = false) : Item{
|
||||
$result = $this->asItem();
|
||||
if($addUserData and $result instanceof ItemBanner and count($this->patterns) > 0){
|
||||
if($addUserData && $result instanceof ItemBanner && count($this->patterns) > 0){
|
||||
$result->setPatterns($this->patterns);
|
||||
}
|
||||
return $result;
|
||||
|
@ -24,34 +24,17 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
abstract class BaseCoral extends Transparent{
|
||||
|
||||
protected CoralType $coralType;
|
||||
protected bool $dead = false;
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
$this->coralType = CoralType::TUBE();
|
||||
}
|
||||
|
||||
public function getCoralType() : CoralType{ return $this->coralType; }
|
||||
|
||||
/** @return $this */
|
||||
public function setCoralType(CoralType $coralType) : self{
|
||||
$this->coralType = $coralType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDead() : bool{ return $this->dead; }
|
||||
|
||||
/** @return $this */
|
||||
public function setDead(bool $dead) : self{
|
||||
$this->dead = $dead;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->dead){
|
||||
$world = $this->position->getWorld();
|
||||
|
@ -101,7 +101,7 @@ abstract class BaseRail extends Flowable{
|
||||
}
|
||||
|
||||
if(
|
||||
$other instanceof BaseRail and
|
||||
$other instanceof BaseRail &&
|
||||
in_array($otherConnection, $other->getCurrentShapeConnections(), true)
|
||||
){
|
||||
$connections[] = $connection;
|
||||
@ -179,7 +179,7 @@ abstract class BaseRail extends Flowable{
|
||||
$otherSide |= RailConnectionInfo::FLAG_ASCEND;
|
||||
}
|
||||
|
||||
if(!($other instanceof BaseRail) or count($otherConnections = $other->getConnectedDirections()) >= 2){
|
||||
if(!($other instanceof BaseRail) || count($otherConnections = $other->getConnectedDirections()) >= 2){
|
||||
//we can only connect to a rail that has less than 2 connections
|
||||
continue;
|
||||
}
|
||||
@ -224,7 +224,7 @@ abstract class BaseRail extends Flowable{
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach($this->getCurrentShapeConnections() as $connection){
|
||||
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 and $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
|
||||
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
break;
|
||||
}
|
||||
|
@ -37,8 +37,6 @@ use function assert;
|
||||
use function strlen;
|
||||
|
||||
abstract class BaseSign extends Transparent{
|
||||
//TODO: conditionally useless properties, find a way to fix
|
||||
|
||||
protected SignText $text;
|
||||
protected ?int $editorEntityRuntimeId = null;
|
||||
|
||||
|
@ -120,7 +120,7 @@ class Bed extends Transparent{
|
||||
|
||||
public function getOtherHalf() : ?Bed{
|
||||
$other = $this->getSide($this->getOtherHalfSide());
|
||||
if($other instanceof Bed and $other->head !== $this->head and $other->facing === $this->facing){
|
||||
if($other instanceof Bed && $other->head !== $this->head && $other->facing === $this->facing){
|
||||
return $other;
|
||||
}
|
||||
|
||||
@ -135,17 +135,17 @@ class Bed extends Transparent{
|
||||
$player->sendMessage(TextFormat::GRAY . "This bed is incomplete");
|
||||
|
||||
return true;
|
||||
}elseif($playerPos->distanceSquared($this->position) > 4 and $playerPos->distanceSquared($other->position) > 4){
|
||||
}elseif($playerPos->distanceSquared($this->position) > 4 && $playerPos->distanceSquared($other->position) > 4){
|
||||
$player->sendMessage(KnownTranslationFactory::tile_bed_tooFar()->prefix(TextFormat::GRAY));
|
||||
return true;
|
||||
}
|
||||
|
||||
$time = $this->position->getWorld()->getTimeOfDay();
|
||||
|
||||
$isNight = ($time >= World::TIME_NIGHT and $time < World::TIME_SUNRISE);
|
||||
$isNight = ($time >= World::TIME_NIGHT && $time < World::TIME_SUNRISE);
|
||||
|
||||
if(!$isNight){
|
||||
$player->sendMessage(KnownTranslationFactory::tile_bed_tooFar()->prefix(TextFormat::GRAY));
|
||||
$player->sendMessage(KnownTranslationFactory::tile_bed_noSleep()->prefix(TextFormat::GRAY));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -166,7 +166,7 @@ class Bed extends Transparent{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(($other = $this->getOtherHalf()) !== null and $other->occupied !== $this->occupied){
|
||||
if(!$this->head && ($other = $this->getOtherHalf()) !== null && $other->occupied !== $this->occupied){
|
||||
$this->occupied = $other->occupied;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
}
|
||||
@ -186,7 +186,7 @@ class Bed extends Transparent{
|
||||
$this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH;
|
||||
|
||||
$next = $this->getSide($this->getOtherHalfSide());
|
||||
if($next->canBeReplaced() and !$next->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($next->canBeReplaced() && !$next->getSide(Facing::DOWN)->isTransparent()){
|
||||
$nextState = clone $this;
|
||||
$nextState->head = true;
|
||||
$tx->addBlock($blockReplace->position, $this)->addBlock($next->position, $nextState);
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
@ -78,6 +79,28 @@ final class Bell extends Transparent{
|
||||
return 0b1111;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
if($this->attachmentType->equals(BellAttachmentType::FLOOR())){
|
||||
return [
|
||||
AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16)
|
||||
];
|
||||
}
|
||||
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
|
||||
return [
|
||||
AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4)
|
||||
];
|
||||
}
|
||||
|
||||
$box = AxisAlignedBB::one()
|
||||
->squash(Facing::axis(Facing::rotateY($this->facing, true)), 1 / 4)
|
||||
->trim(Facing::UP, 1 / 16)
|
||||
->trim(Facing::DOWN, 1 / 4);
|
||||
|
||||
return [
|
||||
$this->attachmentType->equals(BellAttachmentType::ONE_WALL()) ? $box->trim($this->facing, 3 / 16) : $box
|
||||
];
|
||||
}
|
||||
|
||||
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
|
||||
|
||||
/** @return $this */
|
||||
|
@ -148,14 +148,14 @@ class Block{
|
||||
$tileType = $this->idInfo->getTileClass();
|
||||
$oldTile = $this->position->getWorld()->getTile($this->position);
|
||||
if($oldTile !== null){
|
||||
if($tileType === null or !($oldTile instanceof $tileType)){
|
||||
if($tileType === null || !($oldTile instanceof $tileType)){
|
||||
$oldTile->close();
|
||||
$oldTile = null;
|
||||
}elseif($oldTile instanceof Spawnable){
|
||||
$oldTile->setDirty(); //destroy old network cache
|
||||
}
|
||||
}
|
||||
if($oldTile === null and $tileType !== null){
|
||||
if($oldTile === null && $tileType !== null){
|
||||
/**
|
||||
* @var Tile $tile
|
||||
* @see Tile::__construct()
|
||||
@ -166,13 +166,21 @@ class Block{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given block has an equivalent type to this one. This compares base legacy ID and variant.
|
||||
* Returns a type ID that identifies this type of block. This does not include information like facing, colour,
|
||||
* powered/unpowered, etc.
|
||||
*/
|
||||
public function getTypeId() : int{
|
||||
return ($this->idInfo->getBlockId() << Block::INTERNAL_METADATA_BITS) | $this->idInfo->getVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given block has an equivalent type to this one. This compares the type IDs.
|
||||
*
|
||||
* Note: This ignores additional IDs used to represent additional states. This means that, for example, a lit
|
||||
* furnace and unlit furnace are considered the same type.
|
||||
*/
|
||||
public function isSameType(Block $other) : bool{
|
||||
return $this->idInfo->getBlockId() === $other->idInfo->getBlockId() and $this->idInfo->getVariant() === $other->idInfo->getVariant();
|
||||
return $this->getTypeId() === $other->getTypeId();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,7 +361,7 @@ class Block{
|
||||
*/
|
||||
public function getDrops(Item $item) : array{
|
||||
if($this->breakInfo->isToolCompatible($item)){
|
||||
if($this->isAffectedBySilkTouch() and $item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
if($this->isAffectedBySilkTouch() && $item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
return $this->getSilkTouchDrops($item);
|
||||
}
|
||||
|
||||
@ -394,7 +402,7 @@ class Block{
|
||||
* Returns how much XP will be dropped by breaking this block with the given item.
|
||||
*/
|
||||
public function getXpDropForTool(Item $item) : int{
|
||||
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH()) or !$this->breakInfo->isToolCompatible($item)){
|
||||
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH()) || !$this->breakInfo->isToolCompatible($item)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -605,7 +613,7 @@ class Block{
|
||||
public function isFullCube() : bool{
|
||||
$bb = $this->getCollisionBoxes();
|
||||
|
||||
return count($bb) === 1 and $bb[0]->getAverageEdgeLength() >= 1 and $bb[0]->isCube();
|
||||
return count($bb) === 1 && $bb[0]->getAverageEdgeLength() >= 1 && $bb[0]->isCube();
|
||||
}
|
||||
|
||||
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{
|
||||
|
@ -119,8 +119,8 @@ class BlockBreakInfo{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->toolType === BlockToolType::NONE or $this->toolHarvestLevel === 0 or (
|
||||
($this->toolType & $tool->getBlockToolType()) !== 0 and $tool->getBlockToolHarvestLevel() >= $this->toolHarvestLevel);
|
||||
return $this->toolType === BlockToolType::NONE || $this->toolHarvestLevel === 0 || (
|
||||
($this->toolType & $tool->getBlockToolType()) !== 0 && $tool->getBlockToolHarvestLevel() >= $this->toolHarvestLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Tile;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class BlockIdentifier{
|
||||
|
||||
@ -40,6 +41,10 @@ class BlockIdentifier{
|
||||
$this->blockId = $blockId;
|
||||
$this->variant = $variant;
|
||||
$this->itemId = $itemId;
|
||||
|
||||
if($tileClass !== null){
|
||||
Utils::testValidInstance($tileClass, Tile::class);
|
||||
}
|
||||
$this->tileClass = $tileClass;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,8 @@ final class BlockLegacyMetadata{
|
||||
public const BAMBOO_LEAF_SIZE_SHIFT = 1;
|
||||
public const BAMBOO_LEAF_SIZE_MASK = 0x03;
|
||||
|
||||
public const BAMBOO_SAPLING_FLAG_READY = 0x01;
|
||||
|
||||
public const BARREL_FLAG_OPEN = 0x08;
|
||||
|
||||
public const BED_FLAG_HEAD = 0x08;
|
||||
@ -142,6 +144,8 @@ final class BlockLegacyMetadata{
|
||||
public const LEAVES_FLAG_NO_DECAY = 0x04;
|
||||
public const LEAVES_FLAG_CHECK_DECAY = 0x08;
|
||||
|
||||
public const LECTERN_FLAG_POWERED = 0x04;
|
||||
|
||||
public const LEVER_FLAG_POWERED = 0x08;
|
||||
|
||||
public const LIQUID_FLAG_FALLING = 0x08;
|
||||
|
@ -26,6 +26,9 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
|
||||
use pocketmine\block\utils\BrewingStandSlot;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function array_key_exists;
|
||||
@ -67,6 +70,19 @@ class BrewingStand extends Transparent{
|
||||
return 0b111;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
//bottom slab part - in PC this is also inset on X/Z by 1/16, but Bedrock sucks
|
||||
AxisAlignedBB::one()->trim(Facing::UP, 7 / 8),
|
||||
|
||||
//center post
|
||||
AxisAlignedBB::one()
|
||||
->squash(Axis::X, 7 / 16)
|
||||
->squash(Axis::Z, 7 / 16)
|
||||
->trim(Facing::UP, 1 / 8)
|
||||
];
|
||||
}
|
||||
|
||||
public function hasSlot(BrewingStandSlot $slot) : bool{
|
||||
return array_key_exists($slot->id(), $this->slots);
|
||||
}
|
||||
@ -100,7 +116,7 @@ class BrewingStand extends Transparent{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
$stand = $this->position->getWorld()->getTile($this->position);
|
||||
if($stand instanceof TileBrewingStand and $stand->canOpenWith($item->getCustomName())){
|
||||
if($stand instanceof TileBrewingStand && $stand->canOpenWith($item->getCustomName())){
|
||||
$player->setCurrentWindow($stand->getInventory());
|
||||
}
|
||||
}
|
||||
@ -109,6 +125,24 @@ class BrewingStand extends Transparent{
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
//TODO
|
||||
$brewing = $this->position->getWorld()->getTile($this->position);
|
||||
if($brewing instanceof TileBrewingStand){
|
||||
if($brewing->onUpdate()){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
|
||||
}
|
||||
|
||||
$changed = false;
|
||||
foreach(BrewingStandSlot::getAll() as $slot){
|
||||
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
|
||||
if($occupied !== $this->hasSlot($slot)){
|
||||
$this->setSlot($slot, $occupied);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ class Cactus extends Transparent{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getId() !== BlockLegacyIds::SAND and !$down->isSameType($this)){
|
||||
if($down->getId() !== BlockLegacyIds::SAND && !$down->isSameType($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
@ -129,7 +129,7 @@ class Cactus extends Transparent{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getId() === BlockLegacyIds::SAND or $down->isSameType($this)){
|
||||
if($down->getId() === BlockLegacyIds::SAND || $down->isSameType($this)){
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
if($this->getSide($side)->isSolid()){
|
||||
return false;
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
|
||||
use pocketmine\event\block\ChestPairEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -47,17 +48,21 @@ class Chest extends Transparent{
|
||||
public function onPostPlace() : void{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileChest){
|
||||
foreach([
|
||||
Facing::rotateY($this->facing, true),
|
||||
Facing::rotateY($this->facing, false)
|
||||
] as $side){
|
||||
foreach([false, true] as $clockwise){
|
||||
$side = Facing::rotateY($this->facing, $clockwise);
|
||||
$c = $this->getSide($side);
|
||||
if($c instanceof Chest and $c->isSameType($this) and $c->facing === $this->facing){
|
||||
$pair = $this->position->getWorld()->getTile($c->position);
|
||||
if($pair instanceof TileChest and !$pair->isPaired()){
|
||||
$pair->pairWith($tile);
|
||||
$tile->pairWith($pair);
|
||||
break;
|
||||
if($c instanceof Chest && $c->isSameType($this) && $c->facing === $this->facing){
|
||||
$world = $this->position->getWorld();
|
||||
$pair = $world->getTile($c->position);
|
||||
if($pair instanceof TileChest && !$pair->isPaired()){
|
||||
[$left, $right] = $clockwise ? [$c, $this] : [$this, $c];
|
||||
$ev = new ChestPairEvent($left, $right);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled() && $world->getBlock($this->position)->isSameType($this) && $world->getBlock($c->position)->isSameType($c)){
|
||||
$pair->pairWith($tile);
|
||||
$tile->pairWith($pair);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,8 +75,8 @@ class Chest extends Transparent{
|
||||
$chest = $this->position->getWorld()->getTile($this->position);
|
||||
if($chest instanceof TileChest){
|
||||
if(
|
||||
!$this->getSide(Facing::UP)->isTransparent() or
|
||||
(($pair = $chest->getPair()) !== null and !$pair->getBlock()->getSide(Facing::UP)->isTransparent()) or
|
||||
!$this->getSide(Facing::UP)->isTransparent() ||
|
||||
(($pair = $chest->getPair()) !== null && !$pair->getBlock()->getSide(Facing::UP)->isTransparent()) ||
|
||||
!$chest->canOpenWith($item->getCustomName())
|
||||
){
|
||||
return true;
|
||||
|
@ -39,6 +39,9 @@ class Cobweb extends Flowable{
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if(($item->getBlockToolType() & BlockToolType::SHEARS) !== 0){
|
||||
return [$this->asItem()];
|
||||
}
|
||||
return [
|
||||
VanillaItems::STRING()
|
||||
];
|
||||
|
@ -86,7 +86,7 @@ class CocoaBlock extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(Facing::axis($face) !== Axis::Y and $this->canAttachTo($blockClicked)){
|
||||
if(Facing::axis($face) !== Axis::Y && $this->canAttachTo($blockClicked)){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\event\block\BlockFormEvent;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
class ConcretePowder extends Opaque implements Fallable{
|
||||
@ -42,7 +43,11 @@ class ConcretePowder extends Opaque implements Fallable{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(($block = $this->checkAdjacentWater()) !== null){
|
||||
$this->position->getWorld()->setBlock($this->position, $block);
|
||||
$ev = new BlockFormEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
}
|
||||
}else{
|
||||
$this->startFalling();
|
||||
}
|
||||
|
@ -24,15 +24,14 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
use pocketmine\data\bedrock\CoralTypeIdMap;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
final class CoralBlock extends Opaque{
|
||||
|
||||
private CoralType $coralType;
|
||||
private bool $dead = false;
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->coralType = CoralType::TUBE();
|
||||
@ -60,22 +59,6 @@ final class CoralBlock extends Opaque{
|
||||
return 0b1111;
|
||||
}
|
||||
|
||||
public function getCoralType() : CoralType{ return $this->coralType; }
|
||||
|
||||
/** @return $this */
|
||||
public function setCoralType(CoralType $coralType) : self{
|
||||
$this->coralType = $coralType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDead() : bool{ return $this->dead; }
|
||||
|
||||
/** @return $this */
|
||||
public function setDead(bool $dead) : self{
|
||||
$this->dead = $dead;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->dead){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\crafting\CraftingGrid;
|
||||
use pocketmine\block\inventory\CraftingTableInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
@ -32,7 +32,7 @@ class CraftingTable extends Opaque{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
$player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG));
|
||||
$player->setCurrentWindow(new CraftingTableInventory($this->position));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -69,7 +69,7 @@ abstract class Crops extends Flowable{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->age < 7 and $item instanceof Fertilizer){
|
||||
if($this->age < 7 && $item instanceof Fertilizer){
|
||||
$block = clone $this;
|
||||
$block->age += mt_rand(2, 5);
|
||||
if($block->age > 7){
|
||||
@ -80,10 +80,9 @@ abstract class Crops extends Flowable{
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
$item->pop();
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -101,7 +100,7 @@ abstract class Crops extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->age < 7 and mt_rand(0, 2) === 1){
|
||||
if($this->age < 7 && mt_rand(0, 2) === 1){
|
||||
$block = clone $this;
|
||||
++$block->age;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\ItemUseOnBlockSound;
|
||||
|
||||
class Dirt extends Opaque{
|
||||
|
||||
@ -58,9 +59,12 @@ class Dirt extends Opaque{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face === Facing::UP and $item instanceof Hoe){
|
||||
if($face === Facing::UP && $item instanceof Hoe){
|
||||
$item->applyDamage(1);
|
||||
$this->position->getWorld()->setBlock($this->position, $this->coarse ? VanillaBlocks::DIRT() : VanillaBlocks::FARMLAND());
|
||||
|
||||
$newBlock = $this->coarse ? VanillaBlocks::DIRT() : VanillaBlocks::FARMLAND();
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
||||
$this->position->getWorld()->setBlock($this->position, $newBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class Door extends Transparent{
|
||||
|
||||
//copy door properties from other half
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
if($other instanceof Door and $other->isSameType($this)){
|
||||
if($other instanceof Door && $other->isSameType($this)){
|
||||
if($this->top){
|
||||
$this->facing = $other->facing;
|
||||
$this->open = $other->open;
|
||||
@ -129,7 +129,7 @@ class Door extends Transparent{
|
||||
if($face === Facing::UP){
|
||||
$blockUp = $this->getSide(Facing::UP);
|
||||
$blockDown = $this->getSide(Facing::DOWN);
|
||||
if(!$blockUp->canBeReplaced() or $blockDown->isTransparent()){
|
||||
if(!$blockUp->canBeReplaced() || $blockDown->isTransparent()){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ class Door extends Transparent{
|
||||
$next = $this->getSide(Facing::rotateY($this->facing, false));
|
||||
$next2 = $this->getSide(Facing::rotateY($this->facing, true));
|
||||
|
||||
if($next->isSameType($this) or (!$next2->isTransparent() and $next->isTransparent())){ //Door hinge
|
||||
if($next->isSameType($this) || (!$next2->isTransparent() && $next->isTransparent())){ //Door hinge
|
||||
$this->hingeRight = true;
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ class Door extends Transparent{
|
||||
$this->open = !$this->open;
|
||||
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
if($other instanceof Door and $other->isSameType($this)){
|
||||
if($other instanceof Door && $other->isSameType($this)){
|
||||
$other->open = $this->open;
|
||||
$this->position->getWorld()->setBlock($other->position, $other);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class DoublePlant extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$id = $blockReplace->getSide(Facing::DOWN)->getId();
|
||||
if(($id === BlockLegacyIds::GRASS or $id === BlockLegacyIds::DIRT) and $blockReplace->getSide(Facing::UP)->canBeReplaced()){
|
||||
if(($id === BlockLegacyIds::GRASS || $id === BlockLegacyIds::DIRT) && $blockReplace->getSide(Facing::UP)->canBeReplaced()){
|
||||
$top = clone $this;
|
||||
$top->top = true;
|
||||
$tx->addBlock($blockReplace->position, $this)->addBlock($blockReplace->position->getSide(Facing::UP), $top);
|
||||
@ -72,14 +72,14 @@ class DoublePlant extends Flowable{
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
|
||||
return (
|
||||
$other instanceof DoublePlant and
|
||||
$other->isSameType($this) and
|
||||
$other instanceof DoublePlant &&
|
||||
$other->isSameType($this) &&
|
||||
$other->top !== $this->top
|
||||
);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->isValidHalfPlant() or (!$this->top and $this->getSide(Facing::DOWN)->isTransparent())){
|
||||
if(!$this->isValidHalfPlant() || (!$this->top && $this->getSide(Facing::DOWN)->isTransparent())){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class DoubleTallGrass extends DoublePlant{
|
||||
}
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
if($this->top and mt_rand(0, 7) === 0){
|
||||
if($this->top && mt_rand(0, 7) === 0){
|
||||
return [VanillaItems::WHEAT_SEEDS()];
|
||||
}
|
||||
return [];
|
||||
|
@ -46,7 +46,7 @@ class EndRod extends Flowable{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
if($stateMeta !== 0 and $stateMeta !== 1){
|
||||
if($stateMeta !== 0 && $stateMeta !== 1){
|
||||
$stateMeta ^= 1;
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ class EndRod extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->facing = $face;
|
||||
if($blockClicked instanceof EndRod and $blockClicked->facing === $this->facing){
|
||||
if($blockClicked instanceof EndRod && $blockClicked->facing === $this->facing){
|
||||
$this->facing = Facing::opposite($face);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ class EnderChest extends Transparent{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
$enderChest = $this->position->getWorld()->getTile($this->position);
|
||||
if($enderChest instanceof TileEnderChest and $this->getSide(Facing::UP)->isTransparent()){
|
||||
if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){
|
||||
$enderChest->setViewerCount($enderChest->getViewerCount() + 1);
|
||||
$player->setCurrentWindow(new EnderChestInventory($this->position, $player->getEnderInventory()));
|
||||
}
|
||||
@ -66,4 +66,8 @@ class EnderChest extends Transparent{
|
||||
VanillaBlocks::OBSIDIAN()->asItem()->setCount(8)
|
||||
];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class Fence extends Transparent{
|
||||
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
$block = $this->getSide($facing);
|
||||
if($block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent())){
|
||||
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
|
||||
$this->connections[$facing] = true;
|
||||
}else{
|
||||
unset($this->connections[$facing]);
|
||||
@ -61,7 +61,7 @@ class Fence extends Transparent{
|
||||
$connectWest = isset($this->connections[Facing::WEST]);
|
||||
$connectEast = isset($this->connections[Facing::EAST]);
|
||||
|
||||
if($connectWest or $connectEast){
|
||||
if($connectWest || $connectEast){
|
||||
//X axis (west/east)
|
||||
$bbs[] = AxisAlignedBB::one()
|
||||
->squash(Axis::Z, $inset)
|
||||
@ -73,7 +73,7 @@ class Fence extends Transparent{
|
||||
$connectNorth = isset($this->connections[Facing::NORTH]);
|
||||
$connectSouth = isset($this->connections[Facing::SOUTH]);
|
||||
|
||||
if($connectNorth or $connectSouth){
|
||||
if($connectNorth || $connectSouth){
|
||||
//Z axis (north/south)
|
||||
$bbs[] = AxisAlignedBB::one()
|
||||
->squash(Axis::X, $inset)
|
||||
|
@ -80,7 +80,7 @@ class FenceGate extends Transparent{
|
||||
|
||||
private function checkInWall() : bool{
|
||||
return (
|
||||
$this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall or
|
||||
$this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall ||
|
||||
$this->getSide(Facing::rotateY($this->facing, true)) instanceof Wall
|
||||
);
|
||||
}
|
||||
@ -105,7 +105,7 @@ class FenceGate extends Transparent{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->open = !$this->open;
|
||||
if($this->open and $player !== null){
|
||||
if($this->open && $player !== null){
|
||||
$playerFacing = $player->getHorizontalFacing();
|
||||
if($playerFacing === Facing::opposite($this->facing)){
|
||||
$this->facing = $playerFacing;
|
||||
|
@ -27,11 +27,16 @@ use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\event\block\BlockBurnEvent;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\event\entity\EntityCombustByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\World;
|
||||
use function intdiv;
|
||||
use function max;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
@ -94,7 +99,7 @@ class Fire extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->isSolid() and !$this->hasAdjacentFlammableBlocks()){
|
||||
if($this->getSide(Facing::DOWN)->isTransparent() && !$this->hasAdjacentFlammableBlocks()){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
|
||||
}else{
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
@ -109,7 +114,7 @@ class Fire extends Flowable{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
|
||||
$result = null;
|
||||
if($this->age < 15 and mt_rand(0, 2) === 0){
|
||||
if($this->age < 15 && mt_rand(0, 2) === 0){
|
||||
$this->age++;
|
||||
$result = $this;
|
||||
}
|
||||
@ -118,13 +123,13 @@ class Fire extends Flowable{
|
||||
if(!$down->burnsForever()){
|
||||
//TODO: check rain
|
||||
if($this->age === 15){
|
||||
if(!$down->isFlammable() and mt_rand(0, 3) === 3){ //1/4 chance to extinguish
|
||||
if(!$down->isFlammable() && mt_rand(0, 3) === 3){ //1/4 chance to extinguish
|
||||
$canSpread = false;
|
||||
$result = VanillaBlocks::AIR();
|
||||
}
|
||||
}elseif(!$this->hasAdjacentFlammableBlocks()){
|
||||
$canSpread = false;
|
||||
if(!$down->isSolid() or $this->age > 3){
|
||||
if($down->isTransparent() || $this->age > 3){
|
||||
$result = VanillaBlocks::AIR();
|
||||
}
|
||||
}
|
||||
@ -137,17 +142,8 @@ class Fire extends Flowable{
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
|
||||
if($canSpread){
|
||||
//TODO: raise upper bound for chance in humid biomes
|
||||
|
||||
foreach($this->getHorizontalSides() as $side){
|
||||
$this->burnBlock($side, 300);
|
||||
}
|
||||
|
||||
//vanilla uses a 250 upper bound here, but I don't think they intended to increase the chance of incineration
|
||||
$this->burnBlock($this->getSide(Facing::UP), 350);
|
||||
$this->burnBlock($this->getSide(Facing::DOWN), 350);
|
||||
|
||||
//TODO: fire spread
|
||||
$this->burnBlocksAround();
|
||||
$this->spreadFire();
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,6 +161,18 @@ class Fire extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function burnBlocksAround() : void{
|
||||
//TODO: raise upper bound for chance in humid biomes
|
||||
|
||||
foreach($this->getHorizontalSides() as $side){
|
||||
$this->burnBlock($side, 300);
|
||||
}
|
||||
|
||||
//vanilla uses a 250 upper bound here, but I don't think they intended to increase the chance of incineration
|
||||
$this->burnBlock($this->getSide(Facing::UP), 350);
|
||||
$this->burnBlock($this->getSide(Facing::DOWN), 350);
|
||||
}
|
||||
|
||||
private function burnBlock(Block $block, int $chanceBound) : void{
|
||||
if(mt_rand(0, $chanceBound) < $block->getFlammability()){
|
||||
$ev = new BlockBurnEvent($block, $this);
|
||||
@ -172,14 +180,87 @@ class Fire extends Flowable{
|
||||
if(!$ev->isCancelled()){
|
||||
$block->onIncinerate();
|
||||
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
$fire->age = min(15, $fire->age + (mt_rand(0, 4) >> 2));
|
||||
$this->position->getWorld()->setBlock($block->position, $fire);
|
||||
}else{
|
||||
$this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR());
|
||||
if($this->position->getWorld()->getBlock($block->getPosition())->isSameState($block)){
|
||||
$spreadedFire = false;
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
$fire->age = min(15, $fire->age + (mt_rand(0, 4) >> 2));
|
||||
$spreadedFire = $this->spreadBlock($block, $fire);
|
||||
}
|
||||
if(!$spreadedFire){
|
||||
$this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function spreadFire() : void{
|
||||
$world = $this->position->getWorld();
|
||||
$difficultyChanceIncrease = $world->getDifficulty() * 7;
|
||||
$ageDivisor = $this->age + 30;
|
||||
|
||||
for($y = -1; $y <= 4; ++$y){
|
||||
$targetY = $y + (int) $this->position->y;
|
||||
if($targetY < World::Y_MIN || $targetY >= World::Y_MAX){
|
||||
continue;
|
||||
}
|
||||
//Higher blocks have a lower chance of catching fire
|
||||
$randomBound = 100 + ($y > 1 ? ($y - 1) * 100 : 0);
|
||||
|
||||
for($z = -1; $z <= 1; ++$z){
|
||||
$targetZ = $z + (int) $this->position->z;
|
||||
for($x = -1; $x <= 1; ++$x){
|
||||
if($x === 0 && $y === 0 && $z === 0){
|
||||
continue;
|
||||
}
|
||||
$targetX = $x + (int) $this->position->x;
|
||||
if(!$world->isInWorld($targetX, $targetY, $targetZ)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$world->isChunkLoaded($targetX >> Chunk::COORD_BIT_SIZE, $targetZ >> Chunk::COORD_BIT_SIZE)){
|
||||
continue;
|
||||
}
|
||||
$block = $world->getBlockAt($targetX, $targetY, $targetZ);
|
||||
if($block->getId() !== BlockLegacyIds::AIR){
|
||||
continue;
|
||||
}
|
||||
|
||||
//TODO: fire can't spread if it's raining in any horizontally adjacent block, or the current one
|
||||
|
||||
$encouragement = 0;
|
||||
foreach($block->position->sides() as $vector3){
|
||||
if($world->isInWorld($vector3->x, $vector3->y, $vector3->z)){
|
||||
$encouragement = max($encouragement, $world->getBlockAt($vector3->x, $vector3->y, $vector3->z)->getFlameEncouragement());
|
||||
}
|
||||
}
|
||||
|
||||
if($encouragement <= 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$maxChance = intdiv($encouragement + 40 + $difficultyChanceIncrease, $ageDivisor);
|
||||
//TODO: max chance is lowered by half in humid biomes
|
||||
|
||||
if($maxChance > 0 && mt_rand(0, $randomBound - 1) <= $maxChance){
|
||||
$new = clone $this;
|
||||
$new->age = min(15, $this->age + (mt_rand(0, 4) >> 2));
|
||||
$this->spreadBlock($block, $new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function spreadBlock(Block $block, Block $newState) : bool{
|
||||
$ev = new BlockSpreadEvent($block, $this, $newState);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$block->position->getWorld()->setBlock($block->position, $ev->getNewState());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class Flower extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getId() === BlockLegacyIds::GRASS or $down->getId() === BlockLegacyIds::DIRT or $down->getId() === BlockLegacyIds::FARMLAND){
|
||||
if($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::FARMLAND){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ class FlowerPot extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setPlant(?Block $plant) : self{
|
||||
if($plant === null or $plant instanceof Air){
|
||||
if($plant === null || $plant instanceof Air){
|
||||
$this->plant = null;
|
||||
}else{
|
||||
$this->plant = clone $plant;
|
||||
@ -83,12 +83,12 @@ class FlowerPot extends Flowable{
|
||||
}
|
||||
|
||||
return
|
||||
$block instanceof Cactus or
|
||||
$block instanceof DeadBush or
|
||||
$block instanceof Flower or
|
||||
$block instanceof RedMushroom or
|
||||
$block instanceof Sapling or
|
||||
($block instanceof TallGrass and $block->getIdInfo()->getVariant() === BlockLegacyMetadata::TALLGRASS_FERN); //TODO: clean up
|
||||
$block instanceof Cactus ||
|
||||
$block instanceof DeadBush ||
|
||||
$block instanceof Flower ||
|
||||
$block instanceof RedMushroom ||
|
||||
$block instanceof Sapling ||
|
||||
($block instanceof TallGrass && $block->getIdInfo()->getVariant() === BlockLegacyMetadata::TALLGRASS_FERN); //TODO: clean up
|
||||
//TODO: bamboo
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\event\block\BlockMeltEvent;
|
||||
use function mt_rand;
|
||||
|
||||
class FrostedIce extends Ice{
|
||||
@ -62,7 +63,7 @@ class FrostedIce extends Ice{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if((!$this->checkAdjacentBlocks(4) or mt_rand(0, 2) === 0) and
|
||||
if((!$this->checkAdjacentBlocks(4) || mt_rand(0, 2) === 0) &&
|
||||
$this->position->getWorld()->getHighestAdjacentFullLightAt($this->position->x, $this->position->y, $this->position->z) >= 12 - $this->age){
|
||||
if($this->tryMelt()){
|
||||
foreach($this->getAllSides() as $block){
|
||||
@ -84,11 +85,11 @@ class FrostedIce extends Ice{
|
||||
$found = 0;
|
||||
for($x = -1; $x <= 1; ++$x){
|
||||
for($z = -1; $z <= 1; ++$z){
|
||||
if($x === 0 and $z === 0){
|
||||
if($x === 0 && $z === 0){
|
||||
continue;
|
||||
}
|
||||
if(
|
||||
$this->position->getWorld()->getBlockAt($this->position->x + $x, $this->position->y, $this->position->z + $z) instanceof FrostedIce and
|
||||
$this->position->getWorld()->getBlockAt($this->position->x + $x, $this->position->y, $this->position->z + $z) instanceof FrostedIce &&
|
||||
++$found >= $requirement
|
||||
){
|
||||
return true;
|
||||
@ -105,7 +106,11 @@ class FrostedIce extends Ice{
|
||||
*/
|
||||
private function tryMelt() : bool{
|
||||
if($this->age >= 3){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER());
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class Furnace extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
@ -73,7 +74,7 @@ class Furnace extends Opaque{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
$furnace = $this->position->getWorld()->getTile($this->position);
|
||||
if($furnace instanceof TileFurnace and $furnace->canOpenWith($item->getCustomName())){
|
||||
if($furnace instanceof TileFurnace && $furnace->canOpenWith($item->getCustomName())){
|
||||
$player->setCurrentWindow($furnace->getInventory());
|
||||
}
|
||||
}
|
||||
@ -83,7 +84,10 @@ class Furnace extends Opaque{
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
$furnace = $this->position->getWorld()->getTile($this->position);
|
||||
if($furnace instanceof TileFurnace and $furnace->onUpdate()){
|
||||
if($furnace instanceof TileFurnace && $furnace->onUpdate()){
|
||||
if(mt_rand(1, 60) === 1){ //in vanilla this is between 1 and 5 seconds; try to average about 3
|
||||
$this->position->getWorld()->addSound($this->position, $furnace->getFurnaceType()->getCookSound());
|
||||
}
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1); //TODO: check this
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\world\generator\object\TallGrass as TallGrassObject;
|
||||
use pocketmine\world\sound\ItemUseOnBlockSound;
|
||||
use function mt_rand;
|
||||
|
||||
class Grass extends Opaque{
|
||||
@ -53,7 +54,7 @@ class Grass extends Opaque{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
$lightAbove = $this->position->getWorld()->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z);
|
||||
if($lightAbove < 4 and $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){
|
||||
if($lightAbove < 4 && $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){
|
||||
//grass dies
|
||||
$ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT());
|
||||
$ev->call();
|
||||
@ -69,9 +70,9 @@ class Grass extends Opaque{
|
||||
|
||||
$b = $this->position->getWorld()->getBlockAt($x, $y, $z);
|
||||
if(
|
||||
!($b instanceof Dirt) or
|
||||
$b->isCoarse() or
|
||||
$this->position->getWorld()->getFullLightAt($x, $y + 1, $z) < 4 or
|
||||
!($b instanceof Dirt) ||
|
||||
$b->isCoarse() ||
|
||||
$this->position->getWorld()->getFullLightAt($x, $y + 1, $z) < 4 ||
|
||||
$this->position->getWorld()->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2
|
||||
){
|
||||
continue;
|
||||
@ -97,12 +98,16 @@ class Grass extends Opaque{
|
||||
return true;
|
||||
}elseif($item instanceof Hoe){
|
||||
$item->applyDamage(1);
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::FARMLAND());
|
||||
$newBlock = VanillaBlocks::FARMLAND();
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
||||
$this->position->getWorld()->setBlock($this->position, $newBlock);
|
||||
|
||||
return true;
|
||||
}elseif($item instanceof Shovel and $this->getSide(Facing::UP)->getId() === BlockLegacyIds::AIR){
|
||||
}elseif($item instanceof Shovel && $this->getSide(Facing::UP)->getId() === BlockLegacyIds::AIR){
|
||||
$item->applyDamage(1);
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::GRASS_PATH());
|
||||
$newBlock = VanillaBlocks::GRASS_PATH();
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
||||
$this->position->getWorld()->setBlock($this->position, $newBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\PillarRotationInMetadataTrait;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class HayBale extends Opaque{
|
||||
use PillarRotationInMetadataTrait;
|
||||
@ -35,4 +36,9 @@ class HayBale extends Opaque{
|
||||
public function getFlammability() : int{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public function onEntityLand(Entity $entity) : ?float{
|
||||
$entity->fallDistance *= 0.2;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\event\block\BlockMeltEvent;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
@ -38,7 +39,7 @@ class Ice extends Transparent{
|
||||
}
|
||||
|
||||
public function onBreak(Item $item, ?Player $player = null) : bool{
|
||||
if(($player === null or $player->isSurvival()) and !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
if(($player === null || $player->isSurvival()) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::WATER());
|
||||
return true;
|
||||
}
|
||||
@ -51,7 +52,11 @@ class Ice extends Transparent{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->position->getWorld()->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER());
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,8 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
use function lcg_value;
|
||||
|
||||
class ItemFrame extends Flowable{
|
||||
@ -86,7 +88,7 @@ class ItemFrame extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setFramedItem(?Item $item) : self{
|
||||
if($item === null or $item->isNull()){
|
||||
if($item === null || $item->isNull()){
|
||||
$this->framedItem = null;
|
||||
$this->itemRotation = 0;
|
||||
}else{
|
||||
@ -111,6 +113,9 @@ class ItemFrame extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setItemDropChance(float $itemDropChance) : self{
|
||||
if($itemDropChance < 0.0 || $itemDropChance > 1.0 || is_nan($itemDropChance) || is_infinite($itemDropChance)){
|
||||
throw new \InvalidArgumentException("Drop chance must be in range 0-1");
|
||||
}
|
||||
$this->itemDropChance = $itemDropChance;
|
||||
return $this;
|
||||
}
|
||||
@ -161,7 +166,7 @@ class ItemFrame extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face === Facing::DOWN or $face === Facing::UP or !$blockClicked->isSolid()){
|
||||
if($face === Facing::DOWN || $face === Facing::UP || !$blockClicked->isSolid()){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -172,7 +177,7 @@ class ItemFrame extends Flowable{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
$drops = parent::getDropsForCompatibleTool($item);
|
||||
if($this->framedItem !== null and lcg_value() <= $this->itemDropChance){
|
||||
if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){
|
||||
$drops[] = clone $this->framedItem;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockClicked->isTransparent() and Facing::axis($face) !== Axis::Y){
|
||||
if(!$blockClicked->isTransparent() && Facing::axis($face) !== Axis::Y){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
@ -77,11 +77,11 @@ class Lantern extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->up())) and !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()))){
|
||||
if(!$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->up())) && !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()))){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->hanging = ($face === Facing::DOWN or !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down())));
|
||||
$this->hanging = ($face === Facing::DOWN || !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down())));
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
|
@ -91,8 +91,6 @@ class Lava extends Liquid{
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
$entity->fallDistance *= 0.5;
|
||||
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
$entity->attack($ev);
|
||||
|
||||
|
@ -96,7 +96,7 @@ class Leaves extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
if($block->getId() === $this->getId() and $distance <= 4){
|
||||
if($block->getId() === $this->getId() && $distance <= 4){
|
||||
foreach(Facing::ALL as $side){
|
||||
if($this->findLog($pos->getSide($side), $visited, $distance + 1)){
|
||||
return true;
|
||||
@ -108,7 +108,7 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->noDecay and !$this->checkDecay){
|
||||
if(!$this->noDecay && !$this->checkDecay){
|
||||
$this->checkDecay = true;
|
||||
$this->position->getWorld()->setBlock($this->position, $this, false);
|
||||
}
|
||||
@ -119,10 +119,10 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(!$this->noDecay and $this->checkDecay){
|
||||
if(!$this->noDecay && $this->checkDecay){
|
||||
$ev = new LeavesDecayEvent($this);
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or $this->findLog($this->position)){
|
||||
if($ev->isCancelled() || $this->findLog($this->position)){
|
||||
$this->checkDecay = false;
|
||||
$this->position->getWorld()->setBlock($this->position, $this, false);
|
||||
}else{
|
||||
@ -145,7 +145,7 @@ class Leaves extends Transparent{
|
||||
if(mt_rand(1, 20) === 1){ //Saplings
|
||||
$drops[] = ItemFactory::getInstance()->get(ItemIds::SAPLING, $this->treeType->getMagicNumber());
|
||||
}
|
||||
if(($this->treeType->equals(TreeType::OAK()) or $this->treeType->equals(TreeType::DARK_OAK())) and mt_rand(1, 200) === 1){ //Apples
|
||||
if(($this->treeType->equals(TreeType::OAK()) || $this->treeType->equals(TreeType::DARK_OAK())) && mt_rand(1, 200) === 1){ //Apples
|
||||
$drops[] = VanillaItems::APPLE();
|
||||
}
|
||||
|
||||
|
166
src/block/Lectern.php
Normal file
166
src/block/Lectern.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?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\block\tile\Lectern as TileLectern;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\WritableBookBase;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\LecternPlaceBookSound;
|
||||
use function count;
|
||||
|
||||
class Lectern extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected int $viewedPage = 0;
|
||||
protected ?WritableBookBase $book = null;
|
||||
protected bool $producingSignal = false;
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03);
|
||||
$this->producingSignal = ($stateMeta & BlockLegacyMetadata::LECTERN_FLAG_POWERED) !== 0;
|
||||
}
|
||||
|
||||
public function writeStateToMeta() : int{
|
||||
return BlockDataSerializer::writeLegacyHorizontalFacing($this->facing) | ($this->producingSignal ? BlockLegacyMetadata::LECTERN_FLAG_POWERED : 0);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileLectern){
|
||||
$this->viewedPage = $tile->getViewedPage();
|
||||
$this->book = $tile->getBook();
|
||||
}
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileLectern){
|
||||
$tile->setViewedPage($this->viewedPage);
|
||||
$tile->setBook($this->book);
|
||||
}
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
return 0b111;
|
||||
}
|
||||
|
||||
public function getFlammability() : int{
|
||||
return 30;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
$drops = parent::getDrops($item);
|
||||
if($this->book !== null){
|
||||
$drops[] = clone $this->book;
|
||||
}
|
||||
|
||||
return $drops;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 0.1)];
|
||||
}
|
||||
|
||||
public function isProducingSignal() : bool{ return $this->producingSignal; }
|
||||
|
||||
/** @return $this */
|
||||
public function setProducingSignal(bool $producingSignal) : self{
|
||||
$this->producingSignal = $producingSignal;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewedPage() : int{
|
||||
return $this->viewedPage;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setViewedPage(int $viewedPage) : self{
|
||||
$this->viewedPage = $viewedPage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBook() : ?WritableBookBase{
|
||||
return $this->book !== null ? clone $this->book : null;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setBook(?WritableBookBase $book) : self{
|
||||
$this->book = $book !== null && !$book->isNull() ? (clone $book)->setCount(1) : null;
|
||||
$this->viewedPage = 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->book === null && $item instanceof WritableBookBase){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setBook($item));
|
||||
$this->position->getWorld()->addSound($this->position, new LecternPlaceBookSound());
|
||||
$item->pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
|
||||
if($this->book !== null){
|
||||
$this->position->getWorld()->dropItem($this->position->up(), $this->book);
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setBook(null));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onPageTurn(int $newPage) : bool{
|
||||
if($newPage === $this->viewedPage){
|
||||
return true;
|
||||
}
|
||||
if($this->book === null || $newPage >= count($this->book->getPages()) || $newPage < 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->viewedPage = $newPage;
|
||||
if(!$this->producingSignal){
|
||||
$this->producingSignal = true;
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
|
||||
}
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
if($this->producingSignal){
|
||||
$this->producingSignal = false;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
}
|
@ -83,6 +83,9 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
/** @return $this */
|
||||
public function setDecay(int $decay) : self{
|
||||
if($decay < 0 || $decay > 7){
|
||||
throw new \InvalidArgumentException("Decay must be in range 0-7");
|
||||
}
|
||||
$this->decay = $decay;
|
||||
return $this;
|
||||
}
|
||||
@ -131,7 +134,7 @@ abstract class Liquid extends Transparent{
|
||||
abstract public function getBucketEmptySound() : Sound;
|
||||
|
||||
public function isSource() : bool{
|
||||
return !$this->falling and $this->decay === 0;
|
||||
return !$this->falling && $this->decay === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,7 +157,7 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
protected function getEffectiveFlowDecay(Block $block) : int{
|
||||
if(!($block instanceof Liquid) or !$block->isSameType($this)){
|
||||
if(!($block instanceof Liquid) || !$block->isSameType($this)){
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -279,7 +282,7 @@ abstract class Liquid extends Transparent{
|
||||
$newDecay = $smallestFlowDecay + $multiplier;
|
||||
$falling = false;
|
||||
|
||||
if($newDecay >= 8 or $smallestFlowDecay < 0){
|
||||
if($newDecay >= 8 || $smallestFlowDecay < 0){
|
||||
$newDecay = -1;
|
||||
}
|
||||
|
||||
@ -290,14 +293,14 @@ abstract class Liquid extends Transparent{
|
||||
$minAdjacentSources = $this->getMinAdjacentSourcesToFormSource();
|
||||
if($minAdjacentSources !== null && $this->adjacentSources >= $minAdjacentSources){
|
||||
$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
|
||||
if($bottomBlock->isSolid() or ($bottomBlock instanceof Liquid and $bottomBlock->isSameType($this) and $bottomBlock->isSource())){
|
||||
if($bottomBlock->isSolid() || ($bottomBlock instanceof Liquid && $bottomBlock->isSameType($this) && $bottomBlock->isSource())){
|
||||
$newDecay = 0;
|
||||
$falling = false;
|
||||
}
|
||||
}
|
||||
|
||||
if($falling !== $this->falling or (!$falling and $newDecay !== $this->decay)){
|
||||
if(!$falling and $newDecay < 0){
|
||||
if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
|
||||
if(!$falling && $newDecay < 0){
|
||||
$world->setBlock($this->position, VanillaBlocks::AIR());
|
||||
return;
|
||||
}
|
||||
@ -312,7 +315,7 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
$this->flowIntoBlock($bottomBlock, 0, true);
|
||||
|
||||
if($this->isSource() or !$bottomBlock->canBeFlowedInto()){
|
||||
if($this->isSource() || !$bottomBlock->canBeFlowedInto()){
|
||||
if($this->falling){
|
||||
$adjacentDecay = 1; //falling liquid behaves like source block
|
||||
}else{
|
||||
@ -331,7 +334,7 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
protected function flowIntoBlock(Block $block, int $newFlowDecay, bool $falling) : void{
|
||||
if($this->canFlowInto($block) and !($block instanceof Liquid)){
|
||||
if($this->canFlowInto($block) && !($block instanceof Liquid)){
|
||||
$new = clone $this;
|
||||
$new->falling = $falling;
|
||||
$new->decay = $falling ? 0 : $newFlowDecay;
|
||||
@ -339,7 +342,7 @@ abstract class Liquid extends Transparent{
|
||||
$ev = new BlockSpreadEvent($block, $this, $new);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
if($block->getId() > 0){
|
||||
if($block->getId() !== BlockLegacyIds::AIR){
|
||||
$this->position->getWorld()->useBreakOn($block->position);
|
||||
}
|
||||
|
||||
@ -350,7 +353,7 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
/** @phpstan-impure */
|
||||
private function getSmallestFlowDecay(Block $block, int $decay) : int{
|
||||
if(!($block instanceof Liquid) or !$block->isSameType($this)){
|
||||
if(!($block instanceof Liquid) || !$block->isSameType($this)){
|
||||
return $decay;
|
||||
}
|
||||
|
||||
@ -381,8 +384,8 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
protected function canFlowInto(Block $block) : bool{
|
||||
return
|
||||
$this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) and
|
||||
$block->canBeFlowedInto() and
|
||||
!($block instanceof Liquid and $block->isSource()); //TODO: I think this should only be liquids of the same type
|
||||
$this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) &&
|
||||
$block->canBeFlowedInto() &&
|
||||
!($block instanceof Liquid && $block->isSource()); //TODO: I think this should only be liquids of the same type
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class Magma extends Opaque{
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if($entity instanceof Living and !$entity->isSneaking()){
|
||||
if($entity instanceof Living && !$entity->isSneaking()){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class Mycelium extends Opaque{
|
||||
$y = mt_rand($this->position->y - 2, $this->position->y + 2);
|
||||
$z = mt_rand($this->position->z - 1, $this->position->z + 1);
|
||||
$block = $this->position->getWorld()->getBlockAt($x, $y, $z);
|
||||
if($block->getId() === BlockLegacyIds::DIRT){
|
||||
if($block instanceof Dirt && !$block->isCoarse()){
|
||||
if($block->getSide(Facing::UP) instanceof Transparent){
|
||||
$ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM());
|
||||
$ev->call();
|
||||
|
@ -53,7 +53,7 @@ class NetherPortal extends Transparent{
|
||||
* @return $this
|
||||
*/
|
||||
public function setAxis(int $axis) : self{
|
||||
if($axis !== Axis::X and $axis !== Axis::Z){
|
||||
if($axis !== Axis::X && $axis !== Axis::Z){
|
||||
throw new \InvalidArgumentException("Invalid axis");
|
||||
}
|
||||
$this->axis = $axis;
|
||||
|
@ -79,7 +79,7 @@ class NetherWartPlant extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->age < 3 and mt_rand(0, 10) === 0){ //Still growing
|
||||
if($this->age < 3 && mt_rand(0, 10) === 0){ //Still growing
|
||||
$block = clone $this;
|
||||
$block->age++;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
|
@ -59,7 +59,7 @@ class Note extends Opaque{
|
||||
|
||||
/** @return $this */
|
||||
public function setPitch(int $pitch) : self{
|
||||
if($pitch < self::MIN_PITCH or $pitch > self::MAX_PITCH){
|
||||
if($pitch < self::MIN_PITCH || $pitch > self::MAX_PITCH){
|
||||
throw new \InvalidArgumentException("Pitch must be in range " . self::MIN_PITCH . " - " . self::MAX_PITCH);
|
||||
}
|
||||
$this->pitch = $pitch;
|
||||
|
@ -23,6 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class Podzol extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaBlocks::DIRT()->asItem()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
45
src/block/Pumpkin.php
Normal file
45
src/block/Pumpkin.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?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\item\Shears;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function in_array;
|
||||
|
||||
class Pumpkin extends Opaque{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){
|
||||
$item->applyDamage(1);
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face));
|
||||
$this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), VanillaItems::PUMPKIN_SEEDS()->setCount(1));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -57,7 +57,7 @@ class RedstoneComparator extends Flowable{
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03);
|
||||
$this->isSubtractMode = ($stateMeta & BlockLegacyMetadata::REDSTONE_COMPARATOR_FLAG_SUBTRACT) !== 0;
|
||||
$this->powered = ($id === $this->idInfoFlattened->getSecondId() or ($stateMeta & BlockLegacyMetadata::REDSTONE_COMPARATOR_FLAG_POWERED) !== 0);
|
||||
$this->powered = ($id === $this->idInfoFlattened->getSecondId() || ($stateMeta & BlockLegacyMetadata::REDSTONE_COMPARATOR_FLAG_POWERED) !== 0);
|
||||
}
|
||||
|
||||
public function writeStateToMeta() : int{
|
||||
|
@ -68,7 +68,7 @@ class Sapling extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getId() === BlockLegacyIds::GRASS or $down->getId() === BlockLegacyIds::DIRT or $down->getId() === BlockLegacyIds::FARMLAND){
|
||||
if($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::FARMLAND){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
@ -76,9 +76,7 @@ class Sapling extends Flowable{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$this->grow($player);
|
||||
|
||||
if($item instanceof Fertilizer && $this->grow($player)){
|
||||
$item->pop();
|
||||
|
||||
return true;
|
||||
@ -98,7 +96,7 @@ class Sapling extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 and mt_rand(1, 7) === 1){
|
||||
if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 && mt_rand(1, 7) === 1){
|
||||
if($this->ready){
|
||||
$this->grow(null);
|
||||
}else{
|
||||
@ -108,19 +106,20 @@ class Sapling extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : void{
|
||||
private function grow(?Player $player) : bool{
|
||||
$random = new Random(mt_rand());
|
||||
$tree = TreeFactory::get($random, $this->treeType);
|
||||
$transaction = $tree?->getBlockTransaction($this->position->getWorld(), $this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), $random);
|
||||
if($transaction === null){
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$ev = new StructureGrowEvent($this, $transaction, $player);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$transaction->apply();
|
||||
return $transaction->apply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
|
@ -83,12 +83,12 @@ class SeaPickle extends Transparent{
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
//TODO: proper placement logic (needs a supporting face below)
|
||||
return ($blockReplace instanceof SeaPickle and $blockReplace->count < 4) or parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
return ($blockReplace instanceof SeaPickle && $blockReplace->count < 4) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->underwater = false; //TODO: implement this once we have new water logic in place
|
||||
if($blockReplace instanceof SeaPickle and $blockReplace->count < 4){
|
||||
if($blockReplace instanceof SeaPickle && $blockReplace->count < 4){
|
||||
$this->count = $blockReplace->count + 1;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ class ShulkerBox extends Opaque{
|
||||
$shulker = $this->position->getWorld()->getTile($this->position);
|
||||
if($shulker instanceof TileShulkerBox){
|
||||
if(
|
||||
$this->getSide($this->facing)->getId() !== BlockLegacyIds::AIR or
|
||||
$this->getSide($this->facing)->isSolid() ||
|
||||
!$shulker->canOpenWith($item->getCustomName())
|
||||
){
|
||||
return true;
|
||||
|
@ -124,8 +124,14 @@ class Skull extends Flowable{
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
//TODO: different bounds depending on attached face
|
||||
return [AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5)];
|
||||
$collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5);
|
||||
return match($this->facing){
|
||||
Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)],
|
||||
Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)],
|
||||
Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)],
|
||||
Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)],
|
||||
default => [$collisionBox]
|
||||
};
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -134,7 +140,7 @@ class Skull extends Flowable{
|
||||
}
|
||||
|
||||
$this->facing = $face;
|
||||
if($player !== null and $face === Facing::UP){
|
||||
if($player !== null && $face === Facing::UP){
|
||||
$this->rotation = ((int) floor(($player->getLocation()->getYaw() * 16 / 360) + 0.5)) & 0xf;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
|
@ -90,11 +90,11 @@ class Slab extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
if($blockReplace instanceof Slab and !$blockReplace->slabType->equals(SlabType::DOUBLE()) and $blockReplace->isSameType($this)){
|
||||
if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->isSameType($this)){
|
||||
if($blockReplace->slabType->equals(SlabType::TOP())){ //Trying to combine with top slab
|
||||
return $clickVector->y <= 0.5 or (!$isClickedBlock and $face === Facing::UP);
|
||||
return $clickVector->y <= 0.5 || (!$isClickedBlock && $face === Facing::UP);
|
||||
}else{
|
||||
return $clickVector->y >= 0.5 or (!$isClickedBlock and $face === Facing::DOWN);
|
||||
return $clickVector->y >= 0.5 || (!$isClickedBlock && $face === Facing::DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,9 +102,9 @@ class Slab extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockReplace instanceof Slab and !$blockReplace->slabType->equals(SlabType::DOUBLE()) and $blockReplace->isSameType($this) and (
|
||||
($blockReplace->slabType->equals(SlabType::TOP()) and ($clickVector->y <= 0.5 or $face === Facing::UP)) or
|
||||
($blockReplace->slabType->equals(SlabType::BOTTOM()) and ($clickVector->y >= 0.5 or $face === Facing::DOWN))
|
||||
if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->isSameType($this) && (
|
||||
($blockReplace->slabType->equals(SlabType::TOP()) && ($clickVector->y <= 0.5 || $face === Facing::UP)) ||
|
||||
($blockReplace->slabType->equals(SlabType::BOTTOM()) && ($clickVector->y >= 0.5 || $face === Facing::DOWN))
|
||||
)){
|
||||
//Clicked in empty half of existing slab
|
||||
$this->slabType = SlabType::DOUBLE();
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\event\block\BlockMeltEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -77,7 +78,7 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $b) : bool{
|
||||
return $b->isSolid() or ($b instanceof SnowLayer and $b->isSameType($this) and $b->layers === 8);
|
||||
return $b->isSolid() || ($b instanceof SnowLayer && $b->isSameType($this) && $b->layers === 8);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -100,7 +101,11 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->position->getWorld()->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR(), false);
|
||||
$ev = new BlockMeltEvent($this, VanillaBlocks::AIR());
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedGlass extends Glass{
|
||||
use ColorInMetadataTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedGlassPane extends GlassPane{
|
||||
use ColorInMetadataTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedHardenedClay extends HardenedClay{
|
||||
use ColorInMetadataTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedHardenedGlass extends HardenedGlass{
|
||||
use ColorInMetadataTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedHardenedGlassPane extends HardenedGlassPane{
|
||||
use ColorInMetadataTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
}
|
||||
|
@ -96,9 +96,9 @@ class Stair extends Transparent{
|
||||
->trim(Facing::opposite($topStepFace), 0.5)
|
||||
->trim(Facing::opposite($this->facing), 0.5);
|
||||
|
||||
if($this->shape->equals(StairShape::OUTER_LEFT()) or $this->shape->equals(StairShape::OUTER_RIGHT())){
|
||||
if($this->shape->equals(StairShape::OUTER_LEFT()) || $this->shape->equals(StairShape::OUTER_RIGHT())){
|
||||
$topStep->trim(Facing::rotateY($this->facing, $this->shape->equals(StairShape::OUTER_LEFT())), 0.5);
|
||||
}elseif($this->shape->equals(StairShape::INNER_LEFT()) or $this->shape->equals(StairShape::INNER_RIGHT())){
|
||||
}elseif($this->shape->equals(StairShape::INNER_LEFT()) || $this->shape->equals(StairShape::INNER_RIGHT())){
|
||||
//add an extra cube
|
||||
$bbs[] = AxisAlignedBB::one()
|
||||
->trim(Facing::opposite($topStepFace), 0.5)
|
||||
@ -114,8 +114,8 @@ class Stair extends Transparent{
|
||||
private function getPossibleCornerFacing(bool $oppositeFacing) : ?int{
|
||||
$side = $this->getSide($oppositeFacing ? Facing::opposite($this->facing) : $this->facing);
|
||||
return (
|
||||
$side instanceof Stair and
|
||||
$side->upsideDown === $this->upsideDown and
|
||||
$side instanceof Stair &&
|
||||
$side->upsideDown === $this->upsideDown &&
|
||||
Facing::axis($side->facing) !== Facing::axis($this->facing) //perpendicular
|
||||
) ? $side->facing : null;
|
||||
}
|
||||
@ -124,7 +124,7 @@ class Stair extends Transparent{
|
||||
if($player !== null){
|
||||
$this->facing = $player->getHorizontalFacing();
|
||||
}
|
||||
$this->upsideDown = (($clickVector->y > 0.5 and $face !== Facing::UP) or $face === Facing::DOWN);
|
||||
$this->upsideDown = (($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN);
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ abstract class Stem extends Crops{
|
||||
|
||||
$side = $this->getSide(Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]);
|
||||
$d = $side->getSide(Facing::DOWN);
|
||||
if($side->getId() === BlockLegacyIds::AIR and ($d->getId() === BlockLegacyIds::FARMLAND or $d->getId() === BlockLegacyIds::GRASS or $d->getId() === BlockLegacyIds::DIRT)){
|
||||
if($side->getId() === BlockLegacyIds::AIR && ($d->getId() === BlockLegacyIds::FARMLAND || $d->getId() === BlockLegacyIds::GRASS || $d->getId() === BlockLegacyIds::DIRT)){
|
||||
$ev = new BlockGrowEvent($side, $grow);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user