mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-13 12:55:06 +00:00
Compare commits
391 Commits
Author | SHA1 | Date | |
---|---|---|---|
d670bc8bf8 | |||
7bd8d09023 | |||
5a970541f9 | |||
e561fa6222 | |||
025dde19ce | |||
28bfe9fbb4 | |||
cd53fa162b | |||
748e9705ba | |||
219ed81389 | |||
6a7196530c | |||
01fe497c49 | |||
3bbd088a09 | |||
85effa5240 | |||
81c5b83bd9 | |||
ef31a9fc66 | |||
dc56a99473 | |||
9477034a4a | |||
5844b59b12 | |||
3af18917f0 | |||
61528393c2 | |||
59b8453228 | |||
8cb48bfe1d | |||
8fb4d7b982 | |||
13f114f30e | |||
eccd258aeb | |||
bd5f379c4c | |||
2588895ab0 | |||
432999a7d7 | |||
68699dbff9 | |||
bfbe3d83de | |||
c32c21464c | |||
16faafa1ef | |||
4d6ac57a26 | |||
73ba1ba224 | |||
8b79253d3b | |||
1122131c8d | |||
a50ef2af6f | |||
72e4660800 | |||
e6285a0312 | |||
5420e76a9a | |||
de46a81cf4 | |||
0cdc04f9b5 | |||
8b1a84f2bf | |||
155e516f74 | |||
ea0a47dff7 | |||
0a6ce18322 | |||
11d73e5bb8 | |||
492ad6bb66 | |||
82e8eb46ac | |||
c7868a810d | |||
2cc22a29c1 | |||
8e2829bdd4 | |||
af79e787ea | |||
65c53e6512 | |||
5503fadf13 | |||
99964709c9 | |||
b85f3bd149 | |||
57d274901d | |||
f3e1c82246 | |||
908b627846 | |||
afb21c1df2 | |||
988be2a96b | |||
afe67f7502 | |||
b257348a3a | |||
12c66d3362 | |||
92fd2d35a4 | |||
61077c48f1 | |||
e0c61071e1 | |||
813d431208 | |||
ee112b992d | |||
c9b33e2b9f | |||
2f2be84b12 | |||
5a7736b179 | |||
e648f1c91e | |||
5116e11cea | |||
9c05e37fbe | |||
fb4796f35e | |||
0c2ba66078 | |||
c29cd8e2eb | |||
a13b0c98a1 | |||
e6ff908e18 | |||
c47ecb55c0 | |||
c19d2fe891 | |||
f195f24f4f | |||
4b5f279a3e | |||
89260d788c | |||
a7cd081002 | |||
361be8fe36 | |||
b01e4ab417 | |||
177b963d8e | |||
8d1a1628de | |||
6f80b8979d | |||
3c8eb29d4e | |||
b94bbf6f5e | |||
314a8a1297 | |||
547503e8f4 | |||
f74ff1fcd4 | |||
6c351357ab | |||
3433406cff | |||
b307cd0aa1 | |||
c9b83d7276 | |||
e22b6ff566 | |||
af88f49a21 | |||
599d5253db | |||
54cb5ee0fa | |||
cdae8b42eb | |||
a45a4a91ae | |||
21378b7f27 | |||
502aed41b0 | |||
125837324f | |||
609dff1aae | |||
b03212053c | |||
fd4ac885bb | |||
f35886f18a | |||
a9eaa55427 | |||
5c41f79be4 | |||
34c2b62ffe | |||
e42a691da9 | |||
3b3fb5e662 | |||
01ffe8bf57 | |||
4abf4aecad | |||
b29f83ee99 | |||
4bc57f00b8 | |||
ff61e1e018 | |||
fb20bb3832 | |||
3333df31df | |||
c09fcb2df2 | |||
c7cdaeae85 | |||
4416cd5a28 | |||
cb93095857 | |||
c8f396ecbc | |||
cb06be615a | |||
0d3c11699c | |||
3667e95ff6 | |||
0e2dc51ec8 | |||
eaf85b028a | |||
9479a1a0ab | |||
1bb2d162ab | |||
ee868bcccc | |||
cbc8576d4a | |||
edcf296086 | |||
9e27c47116 | |||
a0368a843e | |||
3f64906263 | |||
19bb8a00df | |||
4816a66fb8 | |||
06f4e1e4c2 | |||
5b8166c1f0 | |||
30c5cad5b3 | |||
a7a7fe3895 | |||
55ac2f07dc | |||
0660888029 | |||
0476e6bcfc | |||
72bd37e442 | |||
f95fcecb5b | |||
61391b6e23 | |||
fbb6f1f81c | |||
712df04bc4 | |||
1563e25378 | |||
ed84252942 | |||
3d90625020 | |||
9f6b914925 | |||
0e614ea8fd | |||
401bd09d60 | |||
649671cc69 | |||
e755e1dc23 | |||
e34a444dde | |||
78f9985377 | |||
fac2bd3379 | |||
fcfd51dfc7 | |||
ccc76cf338 | |||
10a73488ed | |||
9c5114084b | |||
11630ab1aa | |||
a3068b39a2 | |||
edbc73a72c | |||
3a0c8dd594 | |||
16fa26405a | |||
57423540f0 | |||
8f8821c904 | |||
094102fe92 | |||
2aef83e7d7 | |||
5c3e78e1d3 | |||
18666e5a60 | |||
f72163c173 | |||
7407e504b6 | |||
0e396dc47d | |||
d2204da1d5 | |||
2a51269305 | |||
928041ddf1 | |||
007f4f9350 | |||
46e9f0cec6 | |||
606d56b55d | |||
78a62a8b27 | |||
c5bdd7dd64 | |||
8ee37a3033 | |||
7e3e63f342 | |||
8ef1e54e20 | |||
eaf3a86981 | |||
317a48d9b0 | |||
bc14660e55 | |||
b1bb9fbd1c | |||
96181f8cf5 | |||
4771e3dc28 | |||
2e9117d102 | |||
06493da7d9 | |||
bd303b1062 | |||
fe731b9018 | |||
1a24afc6d1 | |||
bef906b0f0 | |||
37e8dd6444 | |||
02ee0f23c0 | |||
cda472333c | |||
47cf58be8a | |||
ccf9691927 | |||
16fa958416 | |||
bac57c159f | |||
38b2d83799 | |||
c134b1cd8a | |||
62deafda48 | |||
31b6df4376 | |||
b296ae1b87 | |||
f9e42b716a | |||
34c1d455a7 | |||
af8936dba5 | |||
e8ffab1787 | |||
ecc1e1f698 | |||
ea5931e274 | |||
988cf7f535 | |||
e156fb47e8 | |||
efc5f34877 | |||
dd0d8842d5 | |||
7bdc564ccc | |||
69fff23f1a | |||
ae43698e88 | |||
0987e03c03 | |||
97c124edf9 | |||
56501178b7 | |||
da663deea1 | |||
972c911485 | |||
0d8858f948 | |||
da71540fce | |||
ec9b39862b | |||
efca8077d5 | |||
5066d5225b | |||
aefaf73685 | |||
15401d740f | |||
5920b0ba40 | |||
dea75a0687 | |||
873e8740e0 | |||
260c55f23a | |||
9ed430acb9 | |||
f0241043de | |||
135f1c95e4 | |||
5431807e43 | |||
d49ae832e8 | |||
ff9d013005 | |||
b0e1317818 | |||
8653afb0fb | |||
995b56aaa0 | |||
3ecddf312d | |||
470243ca6f | |||
3f21e59917 | |||
fdd74a4f46 | |||
a43b46a93c | |||
0604dfc9e5 | |||
dd2c3db285 | |||
c95e283507 | |||
6afbd1f55c | |||
0682c93f5a | |||
da90ae85da | |||
e87127f309 | |||
0237a50d90 | |||
8b53e4150e | |||
1c43538238 | |||
68887105b2 | |||
104e90b794 | |||
994062f6dc | |||
69a41a5ed4 | |||
3903b70ef5 | |||
692e63ad7c | |||
4d1be4d41d | |||
5f0310a8b6 | |||
9b01fb3d89 | |||
f28405fcfb | |||
9c07c206f6 | |||
d0d701f232 | |||
07cae8a129 | |||
6869ee1c2d | |||
26155acff2 | |||
b550cf5163 | |||
48fa19fdcd | |||
bac986d0b2 | |||
215bac8dd7 | |||
3709ba172b | |||
ef034f2d68 | |||
ab18332572 | |||
48595630fc | |||
4102205ba6 | |||
9e85ee4a7a | |||
e8e6b9304c | |||
23849b7f63 | |||
d2f68836c6 | |||
d19db5d2e4 | |||
98cdc80d37 | |||
8273f789ee | |||
29eccba5f0 | |||
9984b15de6 | |||
6ea01e0dd4 | |||
46331df7db | |||
691c49fb32 | |||
db815360d1 | |||
6e297168c2 | |||
95dbb00d4c | |||
50e29a5ed8 | |||
9f3fb935b5 | |||
e30b1ee2c7 | |||
574b7f6343 | |||
e8b6b56330 | |||
c368ebb5e7 | |||
fa920aa868 | |||
a421d32273 | |||
6c21c23444 | |||
55e0d9c520 | |||
37ee3f2775 | |||
bfdcc12e81 | |||
b2299e08e0 | |||
d7741050c5 | |||
6cff08cd65 | |||
fec42f16ba | |||
deb0cee8a0 | |||
c0dafe7872 | |||
340881d590 | |||
e2e960e43d | |||
500fd2d842 | |||
0b550b346b | |||
1424114cf2 | |||
a8980a0f67 | |||
69aa7c5ac1 | |||
11b74868ee | |||
9a53de0903 | |||
0f8101d4a6 | |||
55ecac4c80 | |||
2a1d1e90a2 | |||
4444a79468 | |||
4cbeee3ab8 | |||
a251960c1c | |||
52f734799e | |||
42171f6e06 | |||
1fe4fdc67c | |||
af4f30d1c8 | |||
3e2926441d | |||
0b33762be0 | |||
e6f89213dc | |||
f8d249b240 | |||
b39afa20d1 | |||
7027a9b972 | |||
b02f3f4090 | |||
8564912149 | |||
873535f719 | |||
78f4fcf6ab | |||
90b749c260 | |||
d5398b2781 | |||
d7a66ad755 | |||
b3f88e7b73 | |||
55adc1ef63 | |||
868d236ddc | |||
59e9c84806 | |||
a110317d1b | |||
b169d89291 | |||
74bef7f423 | |||
8db7867881 | |||
d8f8afe531 | |||
7dabf305f8 | |||
ed0053d0ee | |||
28255e35d1 | |||
0fc9170bbf | |||
7e2efae024 | |||
e9fa07b550 | |||
8ac32824a2 | |||
1322defead | |||
d084b7a34b | |||
c2d0605b1e | |||
0ff0b33047 | |||
114df07622 | |||
4a88db7f43 | |||
d3ea29d527 | |||
d2f1a3cf5b | |||
f9c2ed6200 | |||
e47a711494 | |||
2ea7a9e216 |
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -20,8 +20,9 @@ assignees: ''
|
||||
<!-- try the `version` command | LATEST IS NOT A VALID VERSION -->
|
||||
* PocketMine-MP:
|
||||
* PHP:
|
||||
* Using JIT: yes/no (delete as appropriate) <!-- look for the giant yellow warning in the log that says you're using JIT -->
|
||||
* Server OS:
|
||||
* Game version: PE/Win10 (delete as appropriate)
|
||||
* Game version: Android/iOS/Win10/Xbox/PS4/Switch (delete as appropriate)
|
||||
|
||||
### Plugins
|
||||
<!--- use the `plugins` command and paste the output below -->
|
||||
|
1
.github/ISSUE_TEMPLATE/crash.md
vendored
1
.github/ISSUE_TEMPLATE/crash.md
vendored
@ -9,6 +9,7 @@ assignees: ''
|
||||
|
||||
<!--- submit crashdump files to https://crash.pmmp.io -->
|
||||
<!--- or, copy the data between ===BEGIN CRASH DUMP=== and ===END CRASH DUMP and paste it on a site like https://pastebin.com -->
|
||||
<!--- DON'T JUST PASTE the crashdump into an issue -->
|
||||
Link to crashdump:
|
||||
|
||||
<!--- write additional information about the crash to help us find the problem -->
|
||||
|
8
.github/dependabot.yml
vendored
Normal file
8
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: composer
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "10:00"
|
||||
open-pull-requests-limit: 10
|
16
.github/support.yml
vendored
16
.github/support.yml
vendored
@ -1,16 +0,0 @@
|
||||
# Configuration for support-requests - https://github.com/dessant/support-requests
|
||||
|
||||
# Label used to mark issues as support requests
|
||||
supportLabel: "Support request"
|
||||
# Comment to post on issues marked as support requests. Add a link
|
||||
# to a support page, or set to `false` to disable
|
||||
supportComment: >
|
||||
Thanks, but this issue tracker is not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
|
||||
|
||||
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
|
||||
|
||||
# Whether to close issues marked as support requests
|
||||
close: true
|
||||
# Whether to lock issues marked as support requests
|
||||
lock: false
|
113
.github/workflows/draft-release.yml
vendored
Normal file
113
.github/workflows/draft-release.yml
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
name: Draft release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: "*"
|
||||
|
||||
jobs:
|
||||
draft:
|
||||
name: Create GitHub draft release
|
||||
if: "startsWith(github.event.head_commit.message, 'Release ')"
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.12.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
|
||||
|
||||
- name: Patch VersionInfo
|
||||
run: |
|
||||
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/pocketmine/VersionInfo.php
|
||||
|
||||
- name: Minify BedrockData JSON files
|
||||
run: php src/pocketmine/resources/vanilla/.minify_json.php
|
||||
|
||||
- name: Run preprocessor
|
||||
run: |
|
||||
PM_PREPROCESSOR_PATH="$GITHUB_WORKSPACE/build/preprocessor"
|
||||
php "$PM_PREPROCESSOR_PATH/PreProcessor.php" --path=src --multisize || (echo "Preprocessor exited with code $?" && exit 1)
|
||||
#dump the diff of preprocessor replacements to a patch in case it has bugs
|
||||
git diff > preprocessor_diff.patch
|
||||
VENDOR_PM="$GITHUB_WORKSPACE/vendor"
|
||||
VENDOR_PM_BACKUP="$GITHUB_WORKSPACE/vendor-before-preprocess"
|
||||
cp -r "$VENDOR_PM" "$VENDOR_PM_BACKUP"
|
||||
for f in $(ls $VENDOR_PM/pocketmine); do
|
||||
echo "Processing directory \"$f\"..."
|
||||
php "$PM_PREPROCESSOR_PATH/PreProcessor.php" --path="$VENDOR_PM/pocketmine/$f" --multisize || (echo "Preprocessor exited with code $?" && exit 1)
|
||||
echo "Checking for changes in \"$f\"..."
|
||||
DIFF=$(git diff --no-index "$VENDOR_PM_BACKUP/pocketmine/$f" "$VENDOR_PM/pocketmine/$f" || true)
|
||||
if [ "$DIFF" != "" ]; then
|
||||
PATCH="$GITHUB_WORKSPACE/preprocessor_diff_$f.patch"
|
||||
echo "$DIFF" > "$PATCH"
|
||||
echo "Generated patch file \"$PATCH\""
|
||||
else
|
||||
echo "No diff generated for \"$f\" (preprocessor made no changes)"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Build PocketMine-MP.phar
|
||||
run: composer make-server
|
||||
|
||||
- name: Get PocketMine-MP release version
|
||||
id: get-pm-version
|
||||
run: |
|
||||
echo ::set-output name=PM_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\BASE_VERSION;')
|
||||
echo ::set-output name=MCPE_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;')
|
||||
echo ::set-output name=PM_VERSION_SHORT::$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\BASE_VERSION); array_pop($v); echo implode(".", $v);')
|
||||
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\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
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: release_artifacts
|
||||
path: |
|
||||
${{ github.workspace }}/PocketMine-MP.phar
|
||||
${{ github.workspace }}/start.*
|
||||
${{ github.workspace }}/build_info.json
|
||||
|
||||
- name: Create draft release
|
||||
uses: ncipollo/release-action@v1.8.6
|
||||
with:
|
||||
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
|
||||
commit: ${{ github.sha }}
|
||||
draft: true
|
||||
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
|
||||
|
||||
Please see the [changelogs](/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
|
||||
|
||||
- name: Upload preprocessor diffs
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: preprocessor_diffs
|
||||
path: ${{ github.workspace }}/preprocessor_diff*.patch
|
||||
|
286
.github/workflows/main.yml
vendored
Normal file
286
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,286 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-php:
|
||||
name: Prepare PHP
|
||||
runs-on: ${{ matrix.image }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.4.22, 8.0.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2 #needed for build.sh
|
||||
- name: Check for 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: Compile PHP
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: ./tests/gh-actions/build.sh "${{ matrix.php }}"
|
||||
|
||||
phpstan:
|
||||
name: PHPStan analysis
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- php: 8.0.9
|
||||
config: phpstan.neon.dist
|
||||
image: ubuntu-20.04
|
||||
- php: 7.4.22
|
||||
config: phpstan.php7.neon
|
||||
image: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G -c ${{ matrix.config }}
|
||||
|
||||
phpunit:
|
||||
name: PHPUnit tests
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.4.22, 8.0.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPUnit tests
|
||||
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
|
||||
integration:
|
||||
name: Integration tests
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.4.22, 8.0.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
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
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run integration tests
|
||||
run: ./tests/travis.sh -t4
|
||||
|
||||
preprocessor:
|
||||
name: Preprocessor tests
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.4.22, 8.0.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
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
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run preprocessor
|
||||
run: |
|
||||
PM_PREPROCESSOR_PATH="$GITHUB_WORKSPACE/build/preprocessor"
|
||||
php "$PM_PREPROCESSOR_PATH/PreProcessor.php" --path=src --multisize || (echo "Preprocessor exited with code $?" && exit 1)
|
||||
|
||||
#dump the diff of preprocessor replacements to a patch in case it has bugs
|
||||
git diff > preprocessor_diff.patch
|
||||
|
||||
VENDOR_PM="$GITHUB_WORKSPACE/vendor"
|
||||
VENDOR_PM_BACKUP="$GITHUB_WORKSPACE/vendor-before-preprocess"
|
||||
cp -r "$VENDOR_PM" "$VENDOR_PM_BACKUP"
|
||||
for f in $(ls $VENDOR_PM/pocketmine); do
|
||||
echo "Processing directory \"$f\"..."
|
||||
php "$PM_PREPROCESSOR_PATH/PreProcessor.php" --path="$VENDOR_PM/pocketmine/$f" --multisize || (echo "Preprocessor exited with code $?" && exit 1)
|
||||
echo "Checking for changes in \"$f\"..."
|
||||
DIFF=$(git diff --no-index "$VENDOR_PM_BACKUP/pocketmine/$f" "$VENDOR_PM/pocketmine/$f" || true)
|
||||
if [ "$DIFF" != "" ]; then
|
||||
PATCH="$GITHUB_WORKSPACE/preprocessor_diff_$f.patch"
|
||||
echo "$DIFF" > "$PATCH"
|
||||
echo "Generated patch file \"$PATCH\""
|
||||
else
|
||||
echo "No diff generated for \"$f\" (preprocessor made no changes)"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Upload preprocessor diffs
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: preprocessor_diffs_${{ matrix.php }}_${{ matrix.image }}
|
||||
path: ${{ github.workspace }}/preprocessor_diff*.patch
|
||||
|
||||
codestyle:
|
||||
name: Code Style checks
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.9.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff
|
22
.github/workflows/support.yml
vendored
Normal file
22
.github/workflows/support.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: 'Manage support request issues'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled, unlabeled, reopened]
|
||||
|
||||
jobs:
|
||||
support:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/support-requests@v2
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
support-label: "Support request"
|
||||
issue-comment: >
|
||||
Thanks, but this issue tracker is not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
|
||||
|
||||
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
|
||||
|
||||
close-issue: true
|
||||
lock-issue: false
|
51
.github/workflows/update-php-versions.php
vendored
Normal file
51
.github/workflows/update-php-versions.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?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);
|
||||
|
||||
const VERSIONS = [
|
||||
"7.3",
|
||||
"7.4",
|
||||
"8.0"
|
||||
];
|
||||
|
||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||
$newWorkflowFile = $workflowFile;
|
||||
foreach(VERSIONS as $v){
|
||||
$releaseInfo = file_get_contents("https://secure.php.net/releases?json&version=$v");
|
||||
if($releaseInfo === false){
|
||||
throw new \RuntimeException("Failed to contact php.net API");
|
||||
}
|
||||
$data = json_decode($releaseInfo, true);
|
||||
if(!is_array($data) || !isset($data["version"]) || !is_string($data["version"]) || preg_match('/^\d+\.\d+\.\d+(-[A-Za-z\d]+)?$/', $data["version"]) === 0){
|
||||
throw new \RuntimeException("Invalid data returned by API");
|
||||
}
|
||||
$updated = preg_replace("/$v\.\d+/", $data["version"], $newWorkflowFile);
|
||||
if($updated !== $newWorkflowFile){
|
||||
echo "Updated $v revision to " . $data["version"] . "\n";
|
||||
}
|
||||
$newWorkflowFile = $updated;
|
||||
}
|
||||
|
||||
if($workflowFile !== $newWorkflowFile){
|
||||
echo "Writing modified workflow file\n";
|
||||
file_put_contents(__DIR__ . '/main.yml', $newWorkflowFile);
|
||||
}
|
45
.github/workflows/update-updater-api.yml
vendored
Normal file
45
.github/workflows/update-updater-api.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Update update.pmmp.io API info
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Install jq
|
||||
run: sudo apt update && sudo apt install jq -y
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: pmmp/update.pmmp.io
|
||||
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
|
||||
|
||||
- name: Get actual tag name
|
||||
id: tag-name
|
||||
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
||||
|
||||
- name: Download new release information
|
||||
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
|
||||
|
||||
- name: Detect channel
|
||||
id: channel
|
||||
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
|
||||
|
||||
- name: Copy release information
|
||||
run: |
|
||||
cp new_build_info.json channels/${{ steps.channel.outputs.CHANNEL }}.json
|
||||
rm new_build_info.json
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git add .
|
||||
git diff-index --quiet HEAD || git commit -m "New ${{ steps.channel.outputs.CHANNEL }} release: ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }}"
|
||||
|
||||
- name: Push changes
|
||||
run: git push
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -16,7 +16,9 @@ server.lock
|
||||
/phpstan.neon
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
.idea/*
|
||||
!.idea/codeStyles/
|
||||
!.idea/fileTemplates/
|
||||
nbproject/*
|
||||
|
||||
# Windows image file caches
|
||||
@ -47,3 +49,4 @@ Documentation/*
|
||||
|
||||
# php-cs-fixer
|
||||
/.php_cs.cache
|
||||
/.php-cs-fixer.cache
|
||||
|
71
.idea/codeStyles/Project.xml
generated
Normal file
71
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,71 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_TEXT_WRAP" value="0" />
|
||||
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="thead,tbody,tfoot" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="VARIABLE_NAMING_STYLE" value="CAMEL_CASE" />
|
||||
<option name="SPACES_WITHIN_SHORT_ECHO_TAGS" value="false" />
|
||||
<option name="SPACE_BEFORE_CLOSURE_LEFT_PARENTHESIS" value="false" />
|
||||
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_COLON_IN_RETURN_TYPE" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<editorconfig>
|
||||
<option name="ENABLED" value="false" />
|
||||
</editorconfig>
|
||||
<codeStyleSettings language="HTML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="CLASS_BRACE_STYLE" value="1" />
|
||||
<option name="METHOD_BRACE_STYLE" value="1" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="true" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="neon">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
8
.idea/fileTemplates/code/PHP Constructor.php
generated
Normal file
8
.idea/fileTemplates/code/PHP Constructor.php
generated
Normal file
@ -0,0 +1,8 @@
|
||||
#if(${THROWS_DOC} != "")
|
||||
/**
|
||||
${THROWS_DOC}
|
||||
*/
|
||||
#end
|
||||
public function __construct(${PARAM_LIST}) {
|
||||
${BODY}
|
||||
}
|
6
.idea/fileTemplates/code/PHP Fluent Setter Method.php
generated
Normal file
6
.idea/fileTemplates/code/PHP Fluent Setter Method.php
generated
Normal file
@ -0,0 +1,6 @@
|
||||
/** @return $this */
|
||||
public function set${NAME}(#if (${SCALAR_TYPE_HINT})${SCALAR_TYPE_HINT} #else#end$${PARAM_NAME})#if(${RETURN_TYPE}): self#else#end
|
||||
{
|
||||
$this->${FIELD_NAME} = $${PARAM_NAME};
|
||||
return $this;
|
||||
}
|
3
.idea/fileTemplates/code/PHP Getter Method.php
generated
Normal file
3
.idea/fileTemplates/code/PHP Getter Method.php
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#if(${TYPE_HINT} != ${RETURN_TYPE} && ${TYPE_HINT} != "")/** @return ${TYPE_HINT} */#end
|
||||
public ${STATIC} function ${GET_OR_IS}${NAME}()#if(${RETURN_TYPE}): ${RETURN_TYPE}#else#end
|
||||
{ return #if(${STATIC} == "static")self::$${FIELD_NAME};#else$this->${FIELD_NAME};#end }
|
2
.idea/fileTemplates/code/PHP Setter Method.php
generated
Normal file
2
.idea/fileTemplates/code/PHP Setter Method.php
generated
Normal file
@ -0,0 +1,2 @@
|
||||
public ${STATIC} function set${NAME}(#if (${SCALAR_TYPE_HINT})${SCALAR_TYPE_HINT} #end$${PARAM_NAME})#if (${VOID_RETURN_TYPE}):void #end
|
||||
{#if (${STATIC} == "static") self::$${FIELD_NAME} = $${PARAM_NAME}; #else $this->${FIELD_NAME} = $${PARAM_NAME}; #end}
|
0
.idea/fileTemplates/includes/PHP Class Doc Comment.php
generated
Normal file
0
.idea/fileTemplates/includes/PHP Class Doc Comment.php
generated
Normal file
21
.idea/fileTemplates/includes/PHP File Header.php
generated
Normal file
21
.idea/fileTemplates/includes/PHP File Header.php
generated
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* 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);
|
5
.idea/fileTemplates/includes/PHP Function Doc Comment.php
generated
Normal file
5
.idea/fileTemplates/includes/PHP Function Doc Comment.php
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#if (${THROWS_DOC} != "")
|
||||
/**
|
||||
${THROWS_DOC}
|
||||
*/
|
||||
#end
|
0
.idea/fileTemplates/includes/PHP Interface Doc Comment.php
generated
Normal file
0
.idea/fileTemplates/includes/PHP Interface Doc Comment.php
generated
Normal file
1
.idea/fileTemplates/includes/PHP Property Doc Comment.php
generated
Normal file
1
.idea/fileTemplates/includes/PHP Property Doc Comment.php
generated
Normal file
@ -0,0 +1 @@
|
||||
/** @var ${TYPE_HINT} */
|
0
.idea/fileTemplates/includes/PHP Trait Doc Comment.php
generated
Normal file
0
.idea/fileTemplates/includes/PHP Trait Doc Comment.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Class.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Class.php
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
#if (${NAMESPACE})
|
||||
namespace ${NAMESPACE};
|
||||
#end
|
||||
|
||||
final class ${NAME} {
|
||||
|
||||
}
|
4
.idea/fileTemplates/internal/PHP File.php
generated
Normal file
4
.idea/fileTemplates/internal/PHP File.php
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
|
10
.idea/fileTemplates/internal/PHP Interface.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Interface.php
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
#if (${NAMESPACE})
|
||||
namespace ${NAMESPACE};
|
||||
#end
|
||||
|
||||
interface ${NAME} {
|
||||
|
||||
}
|
10
.idea/fileTemplates/internal/PHP Trait.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Trait.php
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
#if (${NAMESPACE})
|
||||
namespace ${NAMESPACE};
|
||||
#end
|
||||
|
||||
trait ${NAME} {
|
||||
|
||||
}
|
71
.php-cs-fixer.php
Normal file
71
.php-cs-fixer.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/build')
|
||||
->in(__DIR__ . '/tests')
|
||||
->notPath('plugins/DevTools')
|
||||
->notPath('preprocessor')
|
||||
->notContains('#ifndef COMPILE') //preprocessor will break if these are changed
|
||||
->notName('PocketMine.php');
|
||||
|
||||
return (new PhpCsFixer\Config)
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'align_multiline_comment' => [
|
||||
'comment_type' => 'phpdocs_only'
|
||||
],
|
||||
'array_indentation' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short'
|
||||
],
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare'
|
||||
]
|
||||
],
|
||||
'cast_spaces' => [
|
||||
'space' => 'single'
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one'
|
||||
],
|
||||
'declare_strict_types' => true,
|
||||
'elseif' => true,
|
||||
'global_namespace_import' => [
|
||||
'import_constants' => true,
|
||||
'import_functions' => true,
|
||||
'import_classes' => null,
|
||||
],
|
||||
'indentation_type' => true,
|
||||
'native_function_invocation' => [
|
||||
'scope' => 'namespaced'
|
||||
],
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
'no_superfluous_phpdoc_tags' => [
|
||||
'allow_mixed' => true,
|
||||
],
|
||||
'no_trailing_whitespace' => true,
|
||||
'no_trailing_whitespace_in_comment' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'no_unused_imports' => true,
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha'
|
||||
],
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_trim_consecutive_blank_line_separation' => true,
|
||||
'single_import_per_statement' => true,
|
||||
'strict_param' => true,
|
||||
])
|
||||
->setFinder($finder)
|
||||
->setIndent("\t")
|
||||
->setLineEnding("\n");
|
17
.travis.yml
17
.travis.yml
@ -1,17 +0,0 @@
|
||||
import:
|
||||
source: ./tests/travis/setup-php.yml
|
||||
|
||||
script:
|
||||
- composer install --prefer-dist
|
||||
- ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
|
||||
- ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
- composer install --no-dev --prefer-dist
|
||||
- ./tests/travis.sh -t4
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache/files
|
||||
- $HOME/.composer/cache/vcs
|
||||
|
||||
notifications:
|
||||
email: false
|
12
BUILDING.md
12
BUILDING.md
@ -2,13 +2,13 @@
|
||||
## Pre-requisites
|
||||
- A bash shell (git bash is sufficient for Windows)
|
||||
- [`git`](https://git-scm.com) available in your shell
|
||||
- PHP 7.3 or newer available in your shell
|
||||
- PHP 7.4 or newer available in your shell
|
||||
- [`composer`](https://getcomposer.org) available in your shell
|
||||
|
||||
## Custom PHP binaries
|
||||
Because PocketMine-MP requires several non-standard PHP extensions and configuration, PMMP provides scripts to build custom binaries for running PocketMine-MP, as well as prebuilt binaries.
|
||||
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.3-Aggregate)
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.4-Aggregate)
|
||||
- [Compile scripts](https://github.com/pmmp/php-build-scripts) are provided as a submodule in the path `build/php`
|
||||
|
||||
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`.
|
||||
@ -30,9 +30,15 @@ If you use a custom binary, you'll need to replace `composer` usages in this gui
|
||||
Preprocessor requires that the `cpp` (c preprocessor) is available in your PATH.
|
||||
|
||||
## Building `PocketMine-MP.phar`
|
||||
Run `build/server-phar.php` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
|
||||
Run `composer make-server` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
|
||||
|
||||
You can also use the `--out` option to change the output filename.
|
||||
|
||||
There is a bug in PHP that might cause an error which looks like this:
|
||||
```
|
||||
Fatal error: Uncaught BadMethodCallException: unable to create temporary file in PocketMine-MP/build/server-phar.php:119
|
||||
```
|
||||
You can work around it by setting `ulimit -n` to some bigger number, e.g. `8192`, or by updating your PHP version to at least 7.4.16 or 8.0.3.
|
||||
|
||||
## Running PocketMine-MP from source code
|
||||
Run `src/pocketmine/PocketMine.php` using your preferred PHP binary.
|
||||
|
@ -3,7 +3,12 @@
|
||||
<b>A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP</b>
|
||||
</p>
|
||||
|
||||
[](https://travis-ci.com/pmmp/PocketMine-MP)
|
||||
<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://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>
|
||||
</p>
|
||||
|
||||
## Getting started
|
||||
- [Documentation](http://pmmp.readthedocs.org/)
|
||||
@ -18,6 +23,7 @@
|
||||
|
||||
## 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
|
||||
* [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
|
||||
|
43
build/generate-build-info-json.php
Normal file
43
build/generate-build-info-json.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 4){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)>");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
|
||||
"base_version" => \pocketmine\BASE_VERSION,
|
||||
"build" => \pocketmine\BUILD_NUMBER,
|
||||
"is_dev" => \pocketmine\IS_DEVELOPMENT_BUILD,
|
||||
"channel" => \pocketmine\BUILD_CHANNEL,
|
||||
"git_commit" => $argv[1],
|
||||
"mcpe_version" => \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
||||
"date" => time(), //TODO: maybe we should embed this in VersionInfo?
|
||||
"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";
|
@ -24,21 +24,23 @@ declare(strict_types=1);
|
||||
namespace pocketmine\build\make_release;
|
||||
|
||||
use pocketmine\utils\VersionString;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function fgets;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function preg_replace;
|
||||
use function sleep;
|
||||
use function sprintf;
|
||||
use function system;
|
||||
use const pocketmine\BASE_VERSION;
|
||||
use const STDERR;
|
||||
use const STDIN;
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev) : void{
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
|
||||
$versionInfo = file_get_contents($versionInfoPath);
|
||||
$versionInfo = preg_replace(
|
||||
$pattern = '/^const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
||||
@ -50,6 +52,11 @@ function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev
|
||||
'const IS_DEVELOPMENT_BUILD = ' . ($isDev ? 'true' : 'false') . ';',
|
||||
$versionInfo
|
||||
);
|
||||
$versionInfo = preg_replace(
|
||||
'/^const BUILD_CHANNEL = ".*";$/m',
|
||||
'const BUILD_CHANNEL = "' . $channel . '";',
|
||||
$versionInfo
|
||||
);
|
||||
file_put_contents($versionInfoPath, $versionInfo);
|
||||
}
|
||||
|
||||
@ -58,8 +65,12 @@ function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev
|
||||
* @phpstan-param list<string> $argv
|
||||
*/
|
||||
function main(array $argv) : void{
|
||||
if(isset($argv[1])){
|
||||
$currentVer = new VersionString($argv[1]);
|
||||
if(count($argv) < 2){
|
||||
fwrite(STDERR, "Arguments: <channel> [release version]\n");
|
||||
exit(1);
|
||||
}
|
||||
if(isset($argv[2])){
|
||||
$currentVer = new VersionString($argv[2]);
|
||||
}else{
|
||||
$currentVer = new VersionString(BASE_VERSION);
|
||||
}
|
||||
@ -70,15 +81,19 @@ function main(array $argv) : void{
|
||||
$currentVer->getPatch() + 1
|
||||
));
|
||||
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false);
|
||||
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
system('git add "' . dirname(__DIR__) . '/changelogs"');
|
||||
system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result);
|
||||
if($result === 0){
|
||||
echo "error: no changelog changes detected; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $argv[1]);
|
||||
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
|
||||
system('git tag ' . $currentVer->getBaseVersion());
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true);
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, "");
|
||||
system('git add "' . $versionInfoPath . '"');
|
||||
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
@ -86,6 +101,4 @@ function main(array $argv) : void{
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
main($argv);
|
||||
}
|
||||
main($argv);
|
||||
|
Submodule build/php updated: 3a3e3495c3...49971716b0
Submodule build/preprocessor updated: da363df5f1...1b9304de61
@ -26,7 +26,6 @@ namespace pocketmine\build\server_phar;
|
||||
use pocketmine\utils\Git;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function getcwd;
|
||||
@ -41,6 +40,7 @@ use function rtrim;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function unlink;
|
||||
use const PHP_EOL;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
@ -129,6 +129,10 @@ function main() : void{
|
||||
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
if(file_exists(dirname(__DIR__) . '/vendor/phpunit')){
|
||||
echo "Remove Composer dev dependencies before building (composer install --no-dev)" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$opts = getopt("", ["out:", "git:"]);
|
||||
if(isset($opts["git"])){
|
||||
@ -169,6 +173,4 @@ STUB
|
||||
}
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
main();
|
||||
}
|
||||
main();
|
||||
|
@ -27,3 +27,28 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Players no longer get pullbacks when sprinting on slabs, stairs and various other blocks when `player.anti-cheat.allow-movement-cheats` is set to `false`. (This bug has been around for over 5 years, so many of you will be used to its existence.)
|
||||
- Fixed entity collision box calculation not taking clip distance into account.
|
||||
- Entities now step up the correct height of the target block, instead of jumping into the air 0.6 blocks and falling back down.
|
||||
|
||||
# 3.15.2
|
||||
- Fixed issues with preloading `SubChunk`.
|
||||
- `/gc` and automatic garbage collection will now release unused heap blocks back to the OS. Previously, the PHP process might hold onto these blocks indefinitely even when not used, causing elevated real memory usage.
|
||||
- Added some documentation to `FurnaceBurnEvent`.
|
||||
|
||||
# 3.15.3
|
||||
- Fixed fall damage accumulation over continuous knockbacks (e.g. combo attacks in PvP).
|
||||
- Fixed a bug in `Human->addXp()` that would cause a crash when saving player data.
|
||||
- `Human->addXp()` will no longer modify the target's total XP if `PlayerExperienceChangeEvent` was cancelled.
|
||||
- `AsyncPool->getTaskQueueSizes()` has been added to allow external detection of async pool overload. This is planned to be implemented as a core feature in the future, but it hasn't been done yet.
|
||||
- `BaseInventory->canAddItem()` behaviour now matches `addItem()` by considering the max stack size of the given item.
|
||||
- Fixed a bug in generator options handling for worlds loaded via `pocketmine.yml`. This fix has the following side effects:
|
||||
- It's now possible to provide generator options as an `options` key when loading a world via `pocketmine.yml`.
|
||||
- If generator options are not provided, the options from `server.properties` will be used, instead of using an empty preset. (It's not clear whether this is desired behaviour, but it was clearly intended, since there is code to do this which was broken until this release. As such, this behaviour is subject to change in the future.)
|
||||
- Fixed a bug in region-based world loading where some files without filename extensions and names containing a region filename extension (e.g a file named `amca` in a McRegion world) would cause the world not to load. These files are now ignored.
|
||||
- Default network compression level has been lowered to 6, due to level 7 being 25% more expensive for only a marginal improvement in bandwidth.
|
||||
- Fixed a performance issue with chunk requesting when players trigger chunk generation on first join.
|
||||
- Setup wizard will now always show IP information, even if the user chose to skip the setup wizard when prompted. (This doesn't affect `--no-wizard` in any way.)
|
||||
- `Maximum memory (system)` is no longer reported in `/status` due to having a misleading output (it was the same as the current memory usage).
|
||||
- The `Player Chunk Send` timer on timings reports now actually reports measurements of chunk sending, not chunk loading.
|
||||
- A new parent timer `World Load` has been added to timings reports, which aggregates timings from `syncChunkLoad` and subtimings from all worlds.
|
||||
|
||||
# 3.15.4
|
||||
- Fixed a bug in the inventory transaction system that caused the server to freeze under some circumstances.
|
||||
|
24
changelogs/3.16.md
Normal file
24
changelogs/3.16.md
Normal file
@ -0,0 +1,24 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.100**
|
||||
|
||||
### 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.16.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.100.
|
||||
- Removed compatibility with earlier versions.
|
||||
- Added new custom composer commands `make-server` and `make-devtools` to ease setting up a development environment and building the server.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.16.1
|
||||
- Fixed incorrect encoding of skins in the protocol.
|
||||
- `/version` no longer crashes when a plugin provides `string[]` for the `author` field in `plugin.yml`.
|
||||
- `author` in `plugin.yml` now accepts arrays, just like `authors`.
|
||||
- Fixed `HellBiome` never being registered.
|
63
changelogs/3.17.md
Normal file
63
changelogs/3.17.md
Normal file
@ -0,0 +1,63 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.200**
|
||||
|
||||
### 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.17.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.200.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.17.1
|
||||
- Fixed some instances of plugin-caused crashes not being detected (eval()'d code, custom plugin paths).
|
||||
- Server uptime is now included in crash reports.
|
||||
- Hoes now take damage when used to break sponges.
|
||||
- Using lava as fuel in a furnace now leaves behind an empty bucket.
|
||||
|
||||
# 3.17.2
|
||||
- Fixed region header corruption when chunks with larger-than-expected lengths are found. These chunks are now treated as corrupted, instead of automatically attempting to salvage them (which usually fails anyway).
|
||||
- `RegionLoader->removeChunk()` now allows the space used by the removed chunk to be reused by future region saves.
|
||||
- Extracted `Living->applyConsumptionResults()` from `Living->consumeObject()` (preparation for a future bug fix).
|
||||
|
||||
# 3.17.3
|
||||
- Improved performance of chunk loading in Region-based worlds.
|
||||
- Improved performance of region header validation in Region-based worlds (indirect improvement to chunk loading performance).
|
||||
- Fixed some PHP 8.0 language-level compatibility issues.
|
||||
- Source installations will now exit with an error when Composer dependencies are not in sync with the current Git revision. Now, it's required to run `composer install` after every git pull to make sure the correct dependency versions are installed.
|
||||
|
||||
# 3.17.4
|
||||
- Removed `readline` support. This hasn't been maintained for many years, never worked correctly, and isn't thread-safe in any case.
|
||||
- Fixed false-positives of region corruption in Region-based worlds (outdated file stat cache).
|
||||
- Fixed more deprecation warnings on PHP 8.0 (optional parameter before required).
|
||||
- `CraftItemEvent->getInputs()` now returns a list starting at offset 0, instead of random offsets. (Note that the contents still won't be ordered.)
|
||||
- `CraftItemEvent->getOutputs()` now returns a list starting at offset 0, instead of random offsets. (Note that the contents still won't be ordered.)
|
||||
- Fixed a bug that broke synchronized building, bridging, towering and more.
|
||||
- Objects in memory dumps no longer show inherited properties multiple times.
|
||||
|
||||
# 3.17.5
|
||||
- Reduced CPU wastage by the logger thread.
|
||||
- Fixed LevelDB deprecation errors on PHP 8.0.
|
||||
- Added some protocol changes for 1.16.200 which were previously overlooked.
|
||||
- Player XUIDs are now tracked. If a player's XUID does not match the previously recorded XUID when they next join the server, they will be kicked. This can be disabled by the `player.verify-xuid` setting in `pocketmine.yml`.
|
||||
- `BUILDING.md` now has a note about `build/server-phar.php`'s compression bug (a bug in PHP).
|
||||
|
||||
# 3.17.6
|
||||
- Fixed core race conditions that could have led to server freezes (race conditions in pmmp/Snooze).
|
||||
- The log message about Xbox Live authentication being enabled has been reduced to INFO, and the tip on how to turn it off removed (disabling it should usually only be done by power users anyway).
|
||||
- Fixed `PlayerMoveEvent->getFrom()` returning incorrect results for players who experienced movement reversions.
|
||||
- Fixed a bug in `ResourcePackClientResponsePacket` decoding that caused unexpected results when decoding the packet twice.
|
||||
- XUID verification now compares XUIDs against players who are already on the server to detect mismatches to avoid unnecessary loading of playerdata.
|
||||
- Fixed an inventory duplication bug which could occur when the same player joined with two devices at the same time.
|
||||
- Fixed cursor item not being synced on inventory transaction rollbacks.
|
||||
- Fixed items with TAG_Float in their NBT not being able to be moved around in the inventory.
|
||||
|
||||
# 3.17.7
|
||||
- Fixed crash caused by preprocessor in 3.17.6.
|
31
changelogs/3.18.md
Normal file
31
changelogs/3.18.md
Normal file
@ -0,0 +1,31 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.210**
|
||||
|
||||
### 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.18.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.210.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
- Items can't be removed from item frames in Survival mode
|
||||
|
||||
# 3.18.1
|
||||
- UPnP is now supported on all platforms instead of just Windows. Note that it's still experimental. Please file issues for any bugs that you find.
|
||||
- Fixed server joining when default game mode is set to Spectator mode.
|
||||
- Fixed items not being able to be removed from item frames in Survival mode.
|
||||
- Fixed field order in ClientCacheBlobStatusPacket (hits and misses were inverted).
|
||||
- Fixed a deadlock that could occur when MainLogger->syncFlushBuffer() was used (usually only used during exception logging).
|
||||
- Updated constants for various things in the protocol.
|
||||
|
||||
# 3.18.2
|
||||
- Fixed `InventoryCloseEvent` not being called on server-initiated inventory closures.
|
||||
- `PlayerToggleFlightEvent` may now be pre-cancelled if the player attempted to enable flight when flying was not allowed. This replaces the previous behaviour of kicking the player.
|
||||
- Fixed being unable to change the item in hand from the inventory window when looking at an entity.
|
31
changelogs/3.19.md
Normal file
31
changelogs/3.19.md
Normal file
@ -0,0 +1,31 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.220**
|
||||
|
||||
### 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.19.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.220.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.19.1
|
||||
- Fixed some particles not working since 1.16.220.
|
||||
- Fixed issues with creative inventory items appearing in the wrong places since 1.16.220.
|
||||
- `Item->removeEnchantment()` now removes the `ench` tag from item NBT when removing the only enchantment on an item.
|
||||
- Fixed temporary memory leak of repeating tasks which cancelled themselves during their `onRun()` handler (they were pushed back onto the task queue even though cancelled, and only removed at their next attempted repeat).
|
||||
|
||||
# 3.19.2
|
||||
- Signs can now only be edited by the player who placed them. They will become finalized if the chunk containing them is unloaded and reloaded, or if the creating player quits the server.
|
||||
|
||||
# 3.19.3
|
||||
- Fixed `Worker->quit()` returning without stopping the thread.
|
||||
- Added some protocol constants in `SetDisplayObjectivePacket`.
|
||||
- Fixed possible client crash caused by `CraftingDataPacket`.
|
11
changelogs/3.20.md
Normal file
11
changelogs/3.20.md
Normal file
@ -0,0 +1,11 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.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.20.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.0
|
||||
- Removed compatibility with earlier versions.
|
15
changelogs/3.21.md
Normal file
15
changelogs/3.21.md
Normal file
@ -0,0 +1,15 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.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.21.0
|
||||
- PHP 7.4 is now required as a minimum.
|
||||
- A workaround for player movement added in 1.14.30 has been removed.
|
||||
|
||||
# 3.21.1
|
||||
- Fixed so-called 'pot lag' - a bug that could occur when using items while rapidly switching hotbar slots.
|
||||
- Plugins in crashdumps are now ordered alphabetically for easier reading.
|
26
changelogs/3.22.md
Normal file
26
changelogs/3.22.md
Normal file
@ -0,0 +1,26 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.10**
|
||||
|
||||
### 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.22.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.10.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
# 3.22.1
|
||||
- Added a giant yellow startup warning if using OPcache JIT on PHP 8.0. JIT is currently considered highly unstable and not recommended for production use.
|
||||
- When using a bad PHP binary, the bootstrap will now report the used `php.ini` path. This can be useful for debugging issues with extension loading when the wrong `php.ini` file is loaded for some reason.
|
||||
- Fixed `start.cmd` causing the wrong `php.ini` to be loaded on Windows when a global PHP installation is used.
|
||||
- A debug message is now logged containing expected vs. actual XUID of players when an XUID mismatch is detected.
|
||||
- Improved the documentation for `Player->getUniqueId()`.
|
||||
- Fixed a mistake in the documentation of `PlayerAuthInputFlags`.
|
||||
- Fixed incorrect encoding of `NpcDialoguePacket`.
|
||||
|
||||
# 3.22.2
|
||||
- PHP 8.0 is now the primary supported version. PHP 7.4 is still supported for now, but it will be dropped in the coming weeks. It's recommended to start testing your servers on 8.0 now so you're ready.
|
||||
- Fixed items in the crafting grid getting deleted on server-side disconnect or session timeout.
|
||||
- A warning is now logged on startup if a debug build of PHP is used to run the server (because it has a significant performance impact).
|
||||
- GitHub Actions is now used to build new releases, instead of Jenkins. You can see build logs [here](https://github.com/pmmp/PocketMine-MP/actions/workflows/draft-release.yml).
|
@ -5,11 +5,10 @@
|
||||
"homepage": "https://pmmp.io",
|
||||
"license": "LGPL-3.0",
|
||||
"require": {
|
||||
"php": ">=7.3.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"php-64bit": "*",
|
||||
"ext-bcmath": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-date": "*",
|
||||
"ext-hash": "*",
|
||||
"ext-json": "*",
|
||||
@ -19,26 +18,27 @@
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-spl": "*",
|
||||
"ext-yaml": ">=2.0.0",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"pocketmine/raklib": "^0.12.7",
|
||||
"pocketmine/spl": "^0.4.0",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"pocketmine/binaryutils": "^0.1.9",
|
||||
"pocketmine/nbt": "^0.2.10",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.1.0",
|
||||
"pocketmine/log": "^0.2.0",
|
||||
"pocketmine/log-pthreads": "^0.1.0",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"adhocore/json-comment": "^0.1.0",
|
||||
"ocramius/package-versions": "^1.5"
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/nbt": "^0.2.18",
|
||||
"pocketmine/raklib": "^0.12.7",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"pocketmine/spl": "^0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.40",
|
||||
"phpstan/phpstan": "0.12.92",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
@ -57,5 +57,18 @@
|
||||
"psr-4": {
|
||||
"pocketmine\\": "tests/phpunit/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.4.0"
|
||||
},
|
||||
"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-server": [
|
||||
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
|
||||
"@php -dphar.readonly=0 build/server-phar.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
969
composer.lock
generated
969
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,4 +7,4 @@ This site can be accessed via https://apidoc.pmmp.io.
|
||||
### Additional developer resources
|
||||
- [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
|
||||
- [DeveloperDocs](https://github.com/pmmp/DeveloperDocs/) - Reference, guides and specifications for the PocketMine-MP API
|
||||
- [DeveloperDocs](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers
|
||||
|
@ -1,11 +1,10 @@
|
||||
includes:
|
||||
- tests/phpstan/configs/actual-problems.neon
|
||||
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
|
||||
- tests/phpstan/configs/com-dotnet-magic.neon
|
||||
- tests/phpstan/configs/custom-leveldb.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/l7-baseline.neon
|
||||
- tests/phpstan/configs/l8-baseline.neon
|
||||
- tests/phpstan/configs/php74-compat.neon
|
||||
- tests/phpstan/configs/php-bugs.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
- tests/phpstan/configs/phpunit-wiring-tests.neon
|
||||
@ -18,6 +17,7 @@ includes:
|
||||
parameters:
|
||||
level: 8
|
||||
checkExplicitMixed: true
|
||||
checkMissingCallableSignature: true
|
||||
bootstrapFiles:
|
||||
- tests/phpstan/bootstrap.php
|
||||
scanDirectories:
|
||||
@ -42,3 +42,11 @@ parameters:
|
||||
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
|
||||
staticReflectionClassNamePatterns:
|
||||
- "#^COM$#"
|
||||
typeAliases:
|
||||
#variadics don't work for this - mixed probably shouldn't work either, but for now it does
|
||||
#what we actually need is something that accepts an infinite number of parameters, but in the absence of that,
|
||||
#we'll just fill it with 10 - it's very unlikely to encounter a callable with 10 parameters anyway.
|
||||
anyCallable: 'callable(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed) : mixed'
|
||||
anyClosure: '\Closure(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed) : mixed'
|
||||
PhpSocket: '\Socket'
|
||||
PhpCurlHandle: '\CurlHandle'
|
||||
|
9
phpstan.php7.neon
Normal file
9
phpstan.php7.neon
Normal file
@ -0,0 +1,9 @@
|
||||
includes:
|
||||
- phpstan.neon.dist
|
||||
- tests/phpstan/configs/php7.neon
|
||||
|
||||
parameters:
|
||||
phpVersion: 70400
|
||||
typeAliases:
|
||||
PhpSocket: resource
|
||||
PhpCurlHandle: resource
|
@ -23,11 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use PackageVersions\Versions;
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\plugin\PluginLoadOrder;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use function base64_encode;
|
||||
@ -45,7 +46,9 @@ 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 microtime;
|
||||
use function mkdir;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
@ -54,10 +57,10 @@ 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 time;
|
||||
use function zend_version;
|
||||
use function zlib_encode;
|
||||
use const E_COMPILE_ERROR;
|
||||
@ -79,6 +82,7 @@ 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{
|
||||
|
||||
@ -88,7 +92,7 @@ class CrashDump{
|
||||
* having their content changed, version format changing, etc.
|
||||
* It is not necessary to increase this when adding new fields.
|
||||
*/
|
||||
private const FORMAT_VERSION = 3;
|
||||
private const FORMAT_VERSION = 4;
|
||||
|
||||
private const PLUGIN_INVOLVEMENT_NONE = "none";
|
||||
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
|
||||
@ -98,7 +102,7 @@ class CrashDump{
|
||||
private $server;
|
||||
/** @var resource */
|
||||
private $fp;
|
||||
/** @var int */
|
||||
/** @var float */
|
||||
private $time;
|
||||
/**
|
||||
* @var mixed[]
|
||||
@ -111,12 +115,12 @@ class CrashDump{
|
||||
private $path;
|
||||
|
||||
public function __construct(Server $server){
|
||||
$this->time = time();
|
||||
$this->time = microtime(true);
|
||||
$this->server = $server;
|
||||
if(!is_dir($this->server->getDataPath() . "crashdumps")){
|
||||
mkdir($this->server->getDataPath() . "crashdumps");
|
||||
}
|
||||
$this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log";
|
||||
$this->path = $this->server->getDataPath() . "crashdumps/" . 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");
|
||||
@ -124,7 +128,8 @@ class CrashDump{
|
||||
$this->fp = $fp;
|
||||
$this->data["format_version"] = self::FORMAT_VERSION;
|
||||
$this->data["time"] = $this->time;
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time));
|
||||
$this->data["uptime"] = $this->time - \pocketmine\START_TIME;
|
||||
$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();
|
||||
@ -165,7 +170,9 @@ class CrashDump{
|
||||
if($json === false){
|
||||
throw new \RuntimeException("Failed to encode crashdump JSON: " . json_last_error_msg());
|
||||
}
|
||||
$this->encodedData = zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9);
|
||||
$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);
|
||||
}
|
||||
@ -177,7 +184,9 @@ class CrashDump{
|
||||
$this->addLine();
|
||||
$this->addLine("Loaded plugins:");
|
||||
$this->data["plugins"] = [];
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $p){
|
||||
$plugins = $this->server->getPluginManager()->getPlugins();
|
||||
ksort($plugins, SORT_STRING);
|
||||
foreach($plugins as $p){
|
||||
$d = $p->getDescription();
|
||||
$this->data["plugins"][$d->getName()] = [
|
||||
"name" => $d->getName(),
|
||||
@ -201,9 +210,16 @@ class CrashDump{
|
||||
|
||||
if($this->server->getProperty("auto-report.send-settings", true) !== false){
|
||||
$this->data["parameters"] = (array) $argv;
|
||||
$this->data["server.properties"] = @file_get_contents($this->server->getDataPath() . "server.properties");
|
||||
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $this->data["server.properties"]);
|
||||
$this->data["pocketmine.yml"] = @file_get_contents($this->server->getDataPath() . "pocketmine.yml");
|
||||
if(($serverDotProperties = @file_get_contents($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($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"] = "";
|
||||
@ -309,8 +325,8 @@ class CrashDump{
|
||||
}
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
$frameCleanPath = Utils::cleanPath($filePath); //this will be empty in phar stub
|
||||
if(strpos($frameCleanPath, "plugins") === 0 and file_exists($filePath)){
|
||||
$frameCleanPath = Utils::cleanPath($filePath);
|
||||
if(strpos($frameCleanPath, Utils::CLEAN_PATH_SRC_PREFIX) !== 0){
|
||||
$this->addLine();
|
||||
if($crashFrame){
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
@ -320,15 +336,17 @@ class CrashDump{
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
if(file_exists($filePath)){
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -338,6 +356,15 @@ class CrashDump{
|
||||
|
||||
private function generalData() : void{
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
$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"] = \pocketmine\BASE_VERSION;
|
||||
@ -350,7 +377,7 @@ class CrashDump{
|
||||
$this->data["general"]["zend"] = zend_version();
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->data["general"]["composer_libraries"] = Versions::VERSIONS;
|
||||
$this->data["general"]["composer_libraries"] = $composerLibraries;
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
@ -358,7 +385,7 @@ class CrashDump{
|
||||
$this->addLine("Zend version: " . zend_version());
|
||||
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
|
||||
$this->addLine("Composer libraries: ");
|
||||
foreach(Versions::VERSIONS as $library => $libraryVersion){
|
||||
foreach($composerLibraries as $library => $libraryVersion){
|
||||
$this->addLine("- $library $libraryVersion");
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ use function fwrite;
|
||||
use function gc_collect_cycles;
|
||||
use function gc_disable;
|
||||
use function gc_enable;
|
||||
use function gc_mem_caches;
|
||||
use function get_class;
|
||||
use function get_declared_classes;
|
||||
use function implode;
|
||||
@ -273,6 +274,7 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
$cycles = gc_collect_cycles();
|
||||
gc_mem_caches();
|
||||
|
||||
Timings::$garbageCollectorTimer->stopTiming();
|
||||
|
||||
@ -421,8 +423,12 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
$name = $property->getName();
|
||||
if($reflection !== $original and !$property->isPublic()){
|
||||
$name = $reflection->getName() . ":" . $name;
|
||||
if($reflection !== $original){
|
||||
if($property->isPrivate()){
|
||||
$name = $reflection->getName() . ":" . $name;
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
|
@ -100,6 +100,8 @@ use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
@ -148,11 +150,19 @@ use pocketmine\network\mcpe\protocol\types\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandParameter;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\network\mcpe\protocol\types\GameMode;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor;
|
||||
use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
|
||||
@ -191,7 +201,9 @@ use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_infinite;
|
||||
use function is_int;
|
||||
use function is_nan;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
@ -203,6 +215,7 @@ use function min;
|
||||
use function preg_match;
|
||||
use function round;
|
||||
use function spl_object_hash;
|
||||
use function sprintf;
|
||||
use function sqrt;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
@ -406,8 +419,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/** @var float */
|
||||
protected $lastRightClickTime = 0.0;
|
||||
/** @var Vector3|null */
|
||||
protected $lastRightClickPos = null;
|
||||
/** @var UseItemTransactionData|null */
|
||||
protected $lastRightClickData = null;
|
||||
|
||||
/**
|
||||
* @return TranslationContainer|string
|
||||
@ -470,19 +483,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player's UUID. This should be preferred over their Xbox user ID (XUID) because UUID is a standard
|
||||
* format which will never change, and all players will have one regardless of whether they are logged into Xbox
|
||||
* Live.
|
||||
* Returns the player's UUID. This should be the preferred method to identify a player.
|
||||
* It does not change if the player changes their username.
|
||||
*
|
||||
* The UUID is comprised of:
|
||||
* - when logged into XBL: a hash of their XUID (and as such will not change for the lifetime of the XBL account)
|
||||
* - when NOT logged into XBL: a hash of their name + clientID + secret device ID.
|
||||
* All players will have a UUID, regardless of whether they are logged into Xbox Live or not. However, note that
|
||||
* non-XBL players can fake their UUIDs.
|
||||
*
|
||||
* WARNING: UUIDs of players **not logged into Xbox Live** CAN BE FAKED and SHOULD NOT be trusted!
|
||||
*
|
||||
* (In the olden days this method used to return a fake UUID computed by the server, which was used by plugins such
|
||||
* as SimpleAuth for authentication. This is NOT SAFE anymore as this UUID is now what was given by the client, NOT
|
||||
* a server-computed UUID.)
|
||||
* WARNING: DO NOT trust this before PlayerLoginEvent. Before PlayerLoginEvent, the player hasn't yet been
|
||||
* authenticated, and any of their data might be faked.
|
||||
*/
|
||||
public function getUniqueId() : ?UUID{
|
||||
return parent::getUniqueId();
|
||||
@ -1650,13 +1658,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$deltaAngle = abs($this->lastYaw - $to->yaw) + abs($this->lastPitch - $to->pitch);
|
||||
|
||||
if($delta > 0.0001 or $deltaAngle > 1.0){
|
||||
$this->lastX = $to->x;
|
||||
$this->lastY = $to->y;
|
||||
$this->lastZ = $to->z;
|
||||
|
||||
$this->lastYaw = $to->yaw;
|
||||
$this->lastPitch = $to->pitch;
|
||||
|
||||
$ev = new PlayerMoveEvent($this, $from, $to);
|
||||
|
||||
$ev->call();
|
||||
@ -1671,6 +1672,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lastX = $to->x;
|
||||
$this->lastY = $to->y;
|
||||
$this->lastZ = $to->z;
|
||||
|
||||
$this->lastYaw = $to->yaw;
|
||||
$this->lastPitch = $to->pitch;
|
||||
$this->broadcastMovement();
|
||||
|
||||
$distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
|
||||
@ -1693,13 +1700,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
protected function revertMovement(Location $from) : void{
|
||||
$this->lastX = $from->x;
|
||||
$this->lastY = $from->y;
|
||||
$this->lastZ = $from->z;
|
||||
|
||||
$this->lastYaw = $from->yaw;
|
||||
$this->lastPitch = $from->pitch;
|
||||
|
||||
$this->setPosition($from);
|
||||
$this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET);
|
||||
}
|
||||
@ -1939,7 +1939,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$animation["ImageWidth"],
|
||||
base64_decode($animation["Image"], true)),
|
||||
$animation["Type"],
|
||||
$animation["Frames"]
|
||||
$animation["Frames"],
|
||||
$animation["AnimationExpression"]
|
||||
);
|
||||
}
|
||||
|
||||
@ -1961,6 +1962,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$skinData = new SkinData(
|
||||
$packet->clientData["SkinId"],
|
||||
$packet->clientData["PlayFabId"],
|
||||
base64_decode($packet->clientData["SkinResourcePatch"] ?? "", true),
|
||||
new SkinImage(
|
||||
$packet->clientData["SkinImageHeight"],
|
||||
@ -2077,17 +2079,43 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return void
|
||||
*/
|
||||
protected function processLogin(){
|
||||
$checkXUID = (bool) $this->server->getProperty("player.verify-xuid", true);
|
||||
$kickForXUIDMismatch = function(string $xuid) use ($checkXUID) : bool{
|
||||
if($checkXUID && $this->xuid !== $xuid){
|
||||
$this->server->getLogger()->debug($this->getName() . " XUID mismatch: expected '$xuid', but got '$this->xuid'");
|
||||
if($this->kick("XUID does not match (possible impersonation attempt)", false)){
|
||||
//TODO: Longer term, we should be identifying playerdata using something more reliable, like XUID or UUID.
|
||||
//However, that would be a very disruptive change, so this will serve as a stopgap for now.
|
||||
//Side note: this will also prevent offline players hijacking XBL playerdata on online servers, since their
|
||||
//XUID will always be empty.
|
||||
return true;
|
||||
}
|
||||
$this->server->getLogger()->debug("XUID mismatch for " . $this->getName() . ", but plugin cancelled event allowing them to join anyway");
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
foreach($this->server->getLoggedInPlayers() as $p){
|
||||
if($p !== $this and ($p->iusername === $this->iusername or $this->getUniqueId()->equals($p->getUniqueId()))){
|
||||
if($kickForXUIDMismatch($p->getXuid())){
|
||||
return;
|
||||
}
|
||||
if(!$p->kick("logged in from another location")){
|
||||
$this->close($this->getLeaveMessage(), "Logged in from another location");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->namedtag = $this->server->getOfflinePlayerData($this->username);
|
||||
if($checkXUID){
|
||||
$recordedXUID = $this->namedtag->getTag("LastKnownXUID");
|
||||
if(!($recordedXUID instanceof StringTag)){
|
||||
$this->server->getLogger()->debug("No previous XUID recorded for " . $this->getName() . ", no choice but to trust this player");
|
||||
}elseif(!$kickForXUIDMismatch($recordedXUID->getValue())){
|
||||
$this->server->getLogger()->debug("XUID match for " . $this->getName());
|
||||
}
|
||||
}
|
||||
|
||||
$this->playedBefore = ($this->getLastPlayed() - $this->getFirstPlayed()) > 1; // microtime(true) - microtime(true) may have less than one millisecond difference
|
||||
$this->namedtag->setString("NameTag", $this->username);
|
||||
@ -2179,6 +2207,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
//but it does have an annoying side-effect when true: it makes
|
||||
//the client remove its own non-server-supplied resource packs.
|
||||
$pk->mustAccept = false;
|
||||
$pk->experiments = new Experiments([], false);
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
@ -2244,6 +2273,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->commandsEnabled = true;
|
||||
$pk->levelId = "";
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$pk->experiments = new Experiments([], false);
|
||||
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
|
||||
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
|
||||
$pk->serverSoftwareVersion = sprintf("%s %s", \pocketmine\NAME, \pocketmine\VERSION);
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
|
||||
@ -2329,8 +2362,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleMovePlayer(MovePlayerPacket $packet) : bool{
|
||||
$newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0);
|
||||
$rawPos = $packet->position;
|
||||
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $packet->yaw, $packet->headYaw, $packet->pitch] as $float){
|
||||
if(is_infinite($float) || is_nan($float)){
|
||||
$this->server->getLogger()->debug("Invalid movement from " . $this->getName() . ", contains NAN/INF components");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$newPos = $rawPos->round(4)->subtract(0, $this->baseOffset, 0);
|
||||
if($this->forceMoveSync !== null and $newPos->distanceSquared($this->forceMoveSync) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
|
||||
$this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3());
|
||||
//Still getting movements from before teleport, ignore them
|
||||
@ -2398,14 +2438,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var InventoryAction[] $actions */
|
||||
$actions = [];
|
||||
$isCraftingPart = false;
|
||||
foreach($packet->actions as $networkInventoryAction){
|
||||
foreach($packet->trData->getActions() as $networkInventoryAction){
|
||||
if(
|
||||
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
|
||||
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
|
||||
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
|
||||
) or (
|
||||
$this->craftingTransaction !== null &&
|
||||
!$networkInventoryAction->oldItem->equalsExact($networkInventoryAction->newItem) &&
|
||||
!$networkInventoryAction->oldItem->getItemStack()->equalsExact($networkInventoryAction->newItem->getItemStack()) &&
|
||||
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER &&
|
||||
$networkInventoryAction->windowId === ContainerIds::UI &&
|
||||
$networkInventoryAction->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT
|
||||
@ -2456,317 +2496,325 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->craftingTransaction = null;
|
||||
}
|
||||
|
||||
switch($packet->transactionType){
|
||||
case InventoryTransactionPacket::TYPE_NORMAL:
|
||||
$this->setUsingItem(false);
|
||||
$transaction = new InventoryTransaction($this, $actions);
|
||||
if($packet->trData instanceof NormalTransactionData){
|
||||
$this->setUsingItem(false);
|
||||
$transaction = new InventoryTransaction($this, $actions);
|
||||
|
||||
try{
|
||||
$transaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage());
|
||||
$this->server->getLogger()->debug("Actions: " . json_encode($packet->actions));
|
||||
try{
|
||||
$transaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage());
|
||||
$this->server->getLogger()->debug("Actions: " . json_encode($packet->trData->getActions()));
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_MISMATCH:
|
||||
if(count($packet->actions) > 0){
|
||||
$this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions));
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
$this->sendAllInventories();
|
||||
return true;
|
||||
}elseif($packet->trData instanceof MismatchTransactionData){
|
||||
if(count($packet->trData->getActions()) > 0){
|
||||
$this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->trData->getActions()) . ", " . json_encode($packet->trData->getActions()));
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
$this->sendAllInventories();
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM:
|
||||
$blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z);
|
||||
$face = $packet->trData->face;
|
||||
return true;
|
||||
}elseif($packet->trData instanceof UseItemTransactionData){
|
||||
|
||||
$type = $packet->trData->actionType;
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$spamBug = ($this->lastRightClickPos !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickPos = clone $packet->trData->clickPos;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
$blockVector = $packet->trData->getBlockPos();
|
||||
$face = $packet->trData->getFace();
|
||||
|
||||
$this->setUsingItem(false);
|
||||
|
||||
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){
|
||||
}elseif($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){
|
||||
return true;
|
||||
}
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
if($blockVector->distanceSquared($this) > 10000){
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
$block = $target->getSide($face);
|
||||
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
if($this->inventory->getHeldItemIndex() !== $packet->trData->getHotbarSlot()){
|
||||
$this->inventory->equipItem($packet->trData->getHotbarSlot());
|
||||
}
|
||||
|
||||
switch($packet->trData->getActionType()){
|
||||
case UseItemTransactionData::ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$spamBug = ($this->lastRightClickData !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickData->getPlayerPos()->distanceSquared($packet->trData->getPlayerPos()) < 0.00001 and
|
||||
$this->lastRightClickData->getBlockPos()->equals($packet->trData->getBlockPos()) and
|
||||
$this->lastRightClickData->getClickPos()->distanceSquared($packet->trData->getClickPos()) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickData = $packet->trData;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
|
||||
$this->doCloseInventory();
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
|
||||
$this->setUsingItem(false);
|
||||
|
||||
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){
|
||||
}elseif($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){
|
||||
return true;
|
||||
}
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand()->getItemStack())){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
|
||||
if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){
|
||||
if($this->isSurvival()){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
|
||||
$this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING);
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = $target->getAllSides();
|
||||
$blocks[] = $target;
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
|
||||
foreach($blocks as $b){
|
||||
$tile = $this->level->getTile($b);
|
||||
if($tile instanceof Spawnable){
|
||||
$tile->spawnTo($this);
|
||||
}
|
||||
}
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
if($blockVector->distanceSquared($this) > 10000){
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
|
||||
if($this->isUsingItem()){
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
$this->resetItemCooldown($slot);
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
$block = $target->getSide($face);
|
||||
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$this->doCloseInventory();
|
||||
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
|
||||
if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){
|
||||
if($this->isSurvival()){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
}
|
||||
$directionVector = $this->getDirectionVector();
|
||||
|
||||
if($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
|
||||
if($this->hasItemCooldown($item) or $this->isSpectator()){
|
||||
$ev->setCancelled();
|
||||
$this->inventory->sendContents($this);
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = $target->getAllSides();
|
||||
$blocks[] = $target;
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
|
||||
foreach($blocks as $b){
|
||||
$tile = $this->level->getTile($b);
|
||||
if($tile instanceof Spawnable){
|
||||
$tile->spawnTo($this);
|
||||
}
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if($item->onClickAir($this, $directionVector)){
|
||||
$this->resetItemCooldown($item);
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
if($this->isUsingItem()){
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
$this->resetItemCooldown($slot);
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
}
|
||||
$directionVector = $this->getDirectionVector();
|
||||
|
||||
if($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand()->getItemStack())){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
}
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
|
||||
if($this->hasItemCooldown($item) or $this->isSpectator()){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if($item->onClickAir($this, $directionVector)){
|
||||
$this->resetItemCooldown($item);
|
||||
if($this->isSurvival()){
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setUsingItem(true);
|
||||
|
||||
return true;
|
||||
default:
|
||||
//unknown
|
||||
break;
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}elseif($packet->trData instanceof UseItemOnEntityTransactionData){
|
||||
$target = $this->level->getEntity($packet->trData->getEntityRuntimeId());
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->inventory->getHeldItemIndex() !== $packet->trData->getHotbarSlot()){
|
||||
$this->inventory->equipItem($packet->trData->getHotbarSlot());
|
||||
}
|
||||
|
||||
switch($packet->trData->getActionType()){
|
||||
case UseItemOnEntityTransactionData::ACTION_INTERACT:
|
||||
break; //TODO
|
||||
case UseItemOnEntityTransactionData::ACTION_ATTACK:
|
||||
if(!$target->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($target instanceof ItemEntity or $target instanceof Arrow){
|
||||
$this->kick("Attempting to attack an invalid entity");
|
||||
$this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()]));
|
||||
return false;
|
||||
}
|
||||
|
||||
$cancelled = false;
|
||||
|
||||
$heldItem = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $heldItem;
|
||||
|
||||
if(!$this->canInteract($target, 8) or $this->isSpectator()){
|
||||
$cancelled = true;
|
||||
}elseif($target instanceof Player){
|
||||
if(!$this->server->getConfigBool("pvp")){
|
||||
$cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
$ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints());
|
||||
|
||||
$meleeEnchantmentDamage = 0;
|
||||
/** @var EnchantmentInstance[] $meleeEnchantments */
|
||||
$meleeEnchantments = [];
|
||||
foreach($heldItem->getEnchantments() as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){
|
||||
$meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel());
|
||||
$meleeEnchantments[] = $enchantment;
|
||||
}
|
||||
}
|
||||
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
|
||||
|
||||
if($cancelled){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
|
||||
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||
}
|
||||
|
||||
$target->attack($ev);
|
||||
|
||||
if($ev->isCancelled()){
|
||||
if($heldItem instanceof Durable and $this->isSurvival()){
|
||||
$this->inventory->sendContents($this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
|
||||
$pk = new AnimatePacket();
|
||||
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
|
||||
$pk->entityRuntimeId = $target->getId();
|
||||
$this->server->broadcastPacket($target->getViewers(), $pk);
|
||||
if($target instanceof Player){
|
||||
$target->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($meleeEnchantments as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
assert($type instanceof MeleeWeaponEnchantment);
|
||||
$type->onPostAttack($this, $target, $enchantment->getLevel());
|
||||
}
|
||||
|
||||
if($this->isAlive()){
|
||||
//reactive damage like thorns might cause us to be killed by attacking another mob, which
|
||||
//would mean we'd already have dropped the inventory by the time we reached here
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
break; //unknown
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}elseif($packet->trData instanceof ReleaseItemTransactionData){
|
||||
if($this->inventory->getHeldItemIndex() !== $packet->trData->getHotbarSlot()){
|
||||
$this->inventory->equipItem($packet->trData->getHotbarSlot());
|
||||
}
|
||||
|
||||
try{
|
||||
switch($packet->trData->getActionType()){
|
||||
case ReleaseItemTransactionData::ACTION_RELEASE:
|
||||
if($this->isUsingItem()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->hasItemCooldown($item)){
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
if($item->onReleaseUsing($this)){
|
||||
$this->resetItemCooldown($item);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->setUsingItem(true);
|
||||
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
//unknown
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY:
|
||||
$target = $this->level->getEntity($packet->trData->entityRuntimeId);
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $packet->trData->actionType;
|
||||
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT:
|
||||
break; //TODO
|
||||
case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK:
|
||||
if(!$target->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($target instanceof ItemEntity or $target instanceof Arrow){
|
||||
$this->kick("Attempting to attack an invalid entity");
|
||||
$this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()]));
|
||||
return false;
|
||||
}
|
||||
|
||||
$cancelled = false;
|
||||
|
||||
$heldItem = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $heldItem;
|
||||
|
||||
if(!$this->canInteract($target, 8) or $this->isSpectator()){
|
||||
$cancelled = true;
|
||||
}elseif($target instanceof Player){
|
||||
if(!$this->server->getConfigBool("pvp")){
|
||||
$cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
$ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints());
|
||||
|
||||
$meleeEnchantmentDamage = 0;
|
||||
/** @var EnchantmentInstance[] $meleeEnchantments */
|
||||
$meleeEnchantments = [];
|
||||
foreach($heldItem->getEnchantments() as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){
|
||||
$meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel());
|
||||
$meleeEnchantments[] = $enchantment;
|
||||
}
|
||||
}
|
||||
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
|
||||
|
||||
if($cancelled){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
|
||||
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||
}
|
||||
|
||||
$target->attack($ev);
|
||||
|
||||
if($ev->isCancelled()){
|
||||
if($heldItem instanceof Durable and $this->isSurvival()){
|
||||
$this->inventory->sendContents($this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
|
||||
$pk = new AnimatePacket();
|
||||
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
|
||||
$pk->entityRuntimeId = $target->getId();
|
||||
$this->server->broadcastPacket($target->getViewers(), $pk);
|
||||
if($target instanceof Player){
|
||||
$target->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($meleeEnchantments as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
assert($type instanceof MeleeWeaponEnchantment);
|
||||
$type->onPostAttack($this, $target, $enchantment->getLevel());
|
||||
}
|
||||
|
||||
if($this->isAlive()){
|
||||
//reactive damage like thorns might cause us to be killed by attacking another mob, which
|
||||
//would mean we'd already have dropped the inventory by the time we reached here
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
break; //unknown
|
||||
}
|
||||
|
||||
break;
|
||||
case InventoryTransactionPacket::TYPE_RELEASE_ITEM:
|
||||
try{
|
||||
$type = $packet->trData->actionType;
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE:
|
||||
if($this->isUsingItem()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->hasItemCooldown($item)){
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
if($item->onReleaseUsing($this)){
|
||||
$this->resetItemCooldown($item);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}finally{
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
default:
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}finally{
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}else{
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false; //TODO
|
||||
}
|
||||
|
||||
public function handleMobEquipment(MobEquipmentPacket $packet) : bool{
|
||||
@ -2776,8 +2824,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$item = $this->inventory->getItem($packet->hotbarSlot);
|
||||
|
||||
if(!$item->equals($packet->item)){
|
||||
$this->server->getLogger()->debug("Tried to equip " . $packet->item . " but have " . $item . " in target slot");
|
||||
if(!$item->equals($packet->item->getItemStack())){
|
||||
$this->server->getLogger()->debug("Tried to equip " . $packet->item->getItemStack() . " but have " . $item . " in target slot");
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
@ -2793,15 +2841,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){
|
||||
//TODO HACK: silence useless spam (MCPE 1.8)
|
||||
//this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang
|
||||
//hacks, it also sends it when changing the held item now, which causes us to think the inventory was closed
|
||||
//when it wasn't.
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->doCloseInventory();
|
||||
if($packet->action !== InteractPacket::ACTION_MOUSEOVER){
|
||||
//mouseover fires when the player swaps their held itemstack in the inventory menu
|
||||
$this->doCloseInventory();
|
||||
}
|
||||
|
||||
$target = $this->level->getEntity($packet->target);
|
||||
if($target === null){
|
||||
@ -2870,7 +2914,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE_REQUEST)){
|
||||
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN)){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2896,6 +2940,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
break;
|
||||
}
|
||||
|
||||
$tile = $this->level->getTile($pos);
|
||||
if($tile instanceof ItemFrame and $tile->hasItem()){
|
||||
if (lcg_value() <= $tile->getItemDropChance()){
|
||||
$this->level->dropItem($tile->getBlock(), $tile->getItem());
|
||||
}
|
||||
$tile->setItem(null);
|
||||
$tile->setItemRotation(0);
|
||||
break;
|
||||
}
|
||||
|
||||
$block = $target->getSide($packet->face);
|
||||
if($block->getId() === Block::FIRE){
|
||||
$this->level->setBlock($block, BlockFactory::get(Block::AIR));
|
||||
@ -2947,7 +3001,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case PlayerActionPacket::ACTION_START_GLIDE:
|
||||
case PlayerActionPacket::ACTION_STOP_GLIDE:
|
||||
break; //TODO
|
||||
case PlayerActionPacket::ACTION_CONTINUE_BREAK:
|
||||
case PlayerActionPacket::ACTION_CRACK_BREAK:
|
||||
$block = $this->level->getBlock($pos);
|
||||
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, $block->getRuntimeId() | ($packet->face << 24));
|
||||
//TODO: destroy-progress level event
|
||||
@ -2957,7 +3011,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case PlayerActionPacket::ACTION_STOP_SWIMMING:
|
||||
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //ignored (for now)
|
||||
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //TODO: ignored (for now)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_CREATIVE_PLAYER_DESTROY_BLOCK:
|
||||
//TODO: do we need to handle this?
|
||||
break;
|
||||
default:
|
||||
$this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName());
|
||||
@ -3039,6 +3096,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var int|null */
|
||||
private $closingWindowId = null;
|
||||
|
||||
/** @internal */
|
||||
public function getClosingWindowId() : ?int{ return $this->closingWindowId; }
|
||||
|
||||
public function handleContainerClose(ContainerClosePacket $packet) : bool{
|
||||
if(!$this->spawned){
|
||||
return true;
|
||||
@ -3050,12 +3113,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
unset($this->openHardcodedWindows[$packet->windowId]);
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowId = $packet->windowId;
|
||||
$pk->server = false;
|
||||
$this->sendDataPacket($pk);
|
||||
return true;
|
||||
}
|
||||
if(isset($this->windowIndex[$packet->windowId])){
|
||||
(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call();
|
||||
$this->closingWindowId = $packet->windowId;
|
||||
$this->removeWindow($this->windowIndex[$packet->windowId]);
|
||||
$this->closingWindowId = null;
|
||||
//removeWindow handles sending the appropriate
|
||||
return true;
|
||||
}
|
||||
@ -3071,11 +3136,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$handled = false;
|
||||
|
||||
$isFlying = $packet->getFlag(AdventureSettingsPacket::FLYING);
|
||||
if($isFlying and !$this->allowFlight){
|
||||
$this->kick($this->server->getLanguage()->translateString("kick.reason.cheat", ["%ability.flight"]));
|
||||
return true;
|
||||
}elseif($isFlying !== $this->isFlying()){
|
||||
if($isFlying !== $this->isFlying()){
|
||||
$ev = new PlayerToggleFlightEvent($this, $isFlying);
|
||||
if($isFlying and !$this->allowFlight){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->sendSettings();
|
||||
@ -3639,6 +3705,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->stopSleep();
|
||||
|
||||
if($this->spawned){
|
||||
$this->doCloseInventory();
|
||||
|
||||
$ev = new PlayerQuitEvent($this, $message, $reason);
|
||||
$ev->call();
|
||||
if($ev->getQuitMessage() != ""){
|
||||
@ -3734,6 +3802,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setString("LastKnownXUID", $this->xuid);
|
||||
|
||||
if($this->isValid()){
|
||||
$this->namedtag->setString("Level", $this->level->getFolderName());
|
||||
}
|
||||
@ -4074,6 +4144,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
if($id !== null){
|
||||
(new InventoryCloseEvent($inventory, $this))->call();
|
||||
$inventory->close($this);
|
||||
unset($this->windows[$hash], $this->windowIndex[$id], $this->permanentWindows[$id]);
|
||||
}
|
||||
@ -4122,7 +4193,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function onChunkChanged(Chunk $chunk){
|
||||
if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
$hasSent = $this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())] ?? false;
|
||||
if($hasSent){
|
||||
$this->usedChunks[$hash] = false;
|
||||
$this->nextChunkOrderRun = 0;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine {
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\utils\Git;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Process;
|
||||
@ -35,7 +36,7 @@ namespace pocketmine {
|
||||
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
|
||||
const MIN_PHP_VERSION = "7.3.0";
|
||||
const MIN_PHP_VERSION = "7.4.0";
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
@ -73,7 +74,6 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
$extensions = [
|
||||
"bcmath" => "BC Math",
|
||||
"curl" => "cURL",
|
||||
"ctype" => "ctype",
|
||||
"date" => "Date",
|
||||
@ -127,6 +127,9 @@ namespace pocketmine {
|
||||
* @return void
|
||||
*/
|
||||
function emit_performance_warnings(\Logger $logger){
|
||||
if(PHP_DEBUG !== 0){
|
||||
$logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
|
||||
}
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
|
||||
}
|
||||
@ -139,6 +142,24 @@ namespace pocketmine {
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged installation detected. This will degrade autoloading speed and make startup times longer.");
|
||||
}
|
||||
if(function_exists('opcache_get_status') && ($opcacheStatus = opcache_get_status(false)) !== false){
|
||||
$jitEnabled = $opcacheStatus["jit"]["on"] ?? false;
|
||||
if($jitEnabled !== false){
|
||||
$logger->warning(<<<'JIT_WARNING'
|
||||
|
||||
|
||||
--------------------------------------- ! WARNING ! ---------------------------------------
|
||||
You're using PHP 8.0 with JIT enabled. This provides significant performance improvements.
|
||||
HOWEVER, it is EXPERIMENTAL, and has already been seen to cause weird and unexpected bugs.
|
||||
Proceed with caution.
|
||||
If you want to report any bugs, make sure to mention that you are using PHP 8.0 with JIT.
|
||||
To turn off JIT, change `opcache.jit` to `0` in your php.ini file.
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
JIT_WARNING
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,10 +180,12 @@ namespace pocketmine {
|
||||
if(count($messages = check_platform_dependencies()) > 0){
|
||||
echo PHP_EOL;
|
||||
$binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
|
||||
critical_error("Selected PHP binary ($binary) does not satisfy some requirements.");
|
||||
critical_error("Selected PHP binary does not satisfy some requirements.");
|
||||
foreach($messages as $m){
|
||||
echo " - $m" . PHP_EOL;
|
||||
}
|
||||
critical_error("PHP binary used: " . $binary);
|
||||
critical_error("Loaded php.ini: " . (($file = php_ini_loaded_file()) !== false ? $file : "none"));
|
||||
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);
|
||||
@ -206,6 +229,19 @@ namespace pocketmine {
|
||||
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
|
||||
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
|
||||
if($composerGitHash !== null){
|
||||
$currentGitHash = explode("-", \pocketmine\GIT_COMMIT)[0];
|
||||
if($currentGitHash !== $composerGitHash){
|
||||
critical_error("Composer dependencies and/or autoloader are out of sync.");
|
||||
critical_error("- Current revision is $currentGitHash");
|
||||
critical_error("- Composer dependencies were last synchronized for revision $composerGitHash");
|
||||
critical_error("Out-of-sync Composer dependencies may result in crashes and classes not being found.");
|
||||
critical_error("Please synchronize Composer dependencies before running the server.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
|
||||
define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR);
|
||||
@ -215,7 +251,12 @@ namespace pocketmine {
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
}
|
||||
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\DATA . 'server.lock', "a+b"));
|
||||
$lockFile = fopen(\pocketmine\DATA . 'server.lock', "a+b");
|
||||
if($lockFile === false){
|
||||
critical_error("Unable to open server.lock file. Please check that the current user has read/write permissions to it.");
|
||||
exit(1);
|
||||
}
|
||||
define('pocketmine\LOCK_FILE', $lockFile);
|
||||
if(!flock(\pocketmine\LOCK_FILE, LOCK_EX | LOCK_NB)){
|
||||
//wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the
|
||||
//other server wrote its PID and released exclusive lock before we get our lock
|
||||
@ -280,7 +321,7 @@ namespace pocketmine {
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
$logger->debug("Some threads could not be stopped, performing a force-kill");
|
||||
Process::kill(getmypid());
|
||||
Process::kill(Process::pid());
|
||||
}
|
||||
}while(false);
|
||||
|
||||
@ -300,7 +341,5 @@ namespace pocketmine {
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
\pocketmine\server();
|
||||
}
|
||||
\pocketmine\server();
|
||||
}
|
||||
|
@ -126,7 +126,6 @@ use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function getmypid;
|
||||
use function getopt;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
@ -1400,10 +1399,10 @@ class Server{
|
||||
Network::$BATCH_THRESHOLD = -1;
|
||||
}
|
||||
|
||||
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 7);
|
||||
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 6);
|
||||
if($this->networkCompressionLevel < 1 or $this->networkCompressionLevel > 9){
|
||||
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 7");
|
||||
$this->networkCompressionLevel = 7;
|
||||
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 6");
|
||||
$this->networkCompressionLevel = 6;
|
||||
}
|
||||
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
|
||||
|
||||
@ -1462,8 +1461,7 @@ class Server{
|
||||
|
||||
$this->onlineMode = $this->getConfigBool("xbox-auth", true);
|
||||
if($this->onlineMode){
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.auth.enabled"));
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.authProperty.enabled"));
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.auth.enabled"));
|
||||
}else{
|
||||
$this->logger->warning($this->getLanguage()->translateString("pocketmine.server.auth.disabled"));
|
||||
$this->logger->warning($this->getLanguage()->translateString("pocketmine.server.authWarning"));
|
||||
@ -1543,7 +1541,7 @@ class Server{
|
||||
if(isset($options["generator"])){
|
||||
$generatorOptions = explode(":", $options["generator"]);
|
||||
$generator = GeneratorManager::getGenerator(array_shift($generatorOptions));
|
||||
if(count($options) > 0){
|
||||
if(count($generatorOptions) > 0){
|
||||
$options["preset"] = implode(":", $generatorOptions);
|
||||
}
|
||||
}else{
|
||||
@ -1939,7 +1937,7 @@ class Server{
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
$this->logger->emergency("Crashed while crashing, killing process");
|
||||
@Process::kill(getmypid());
|
||||
@Process::kill(Process::pid());
|
||||
}
|
||||
|
||||
}
|
||||
@ -2132,7 +2130,7 @@ class Server{
|
||||
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
sleep($spacing);
|
||||
}
|
||||
@Process::kill(getmypid());
|
||||
@Process::kill(Process::pid());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.15.1";
|
||||
const BASE_VERSION = "3.22.2";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
const BUILD_CHANNEL = "stable";
|
||||
|
@ -95,7 +95,7 @@ abstract class Worker extends \Worker{
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
||||
if($this->isRunning()){
|
||||
if(!$this->isShutdown()){
|
||||
while($this->unstack() !== null);
|
||||
$this->notify();
|
||||
$this->shutdown();
|
||||
|
@ -35,5 +35,6 @@ interface BlockToolType{
|
||||
public const TYPE_PICKAXE = 1 << 2;
|
||||
public const TYPE_AXE = 1 << 3;
|
||||
public const TYPE_SHEARS = 1 << 4;
|
||||
public const TYPE_HOE = 1 << 5;
|
||||
|
||||
}
|
||||
|
@ -418,6 +418,9 @@ abstract class Liquid extends Transparent{
|
||||
return $isOptimalFlowDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-impure This function modifies the adjacent sources count (premature optimisation)
|
||||
*/
|
||||
private function getSmallestFlowDecay(Block $block, int $decay) : int{
|
||||
$blockDecay = $this->getFlowDecay($block);
|
||||
|
||||
|
@ -68,7 +68,10 @@ class SignPost extends Transparent{
|
||||
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::WALL_SIGN, $this->meta), true);
|
||||
}
|
||||
|
||||
Tile::createTile(Tile::SIGN, $this->getLevelNonNull(), TileSign::createNBT($this, $face, $item, $player));
|
||||
$sign = Tile::createTile(Tile::SIGN, $this->getLevelNonNull(), TileSign::createNBT($this, $face, $item, $player));
|
||||
if($player !== null && $sign instanceof TileSign){
|
||||
$sign->setEditorEntityRuntimeId($player->getId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ class Sponge extends Solid{
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_HOE;
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0.6;
|
||||
}
|
||||
|
@ -25,23 +25,18 @@ namespace pocketmine\command;
|
||||
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\Thread;
|
||||
use pocketmine\utils\Utils;
|
||||
use function extension_loaded;
|
||||
use function fclose;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function fstat;
|
||||
use function getopt;
|
||||
use function is_resource;
|
||||
use function microtime;
|
||||
use function preg_replace;
|
||||
use function readline;
|
||||
use function readline_add_history;
|
||||
use function stream_isatty;
|
||||
use function stream_select;
|
||||
use function trim;
|
||||
use function usleep;
|
||||
use const STDIN;
|
||||
|
||||
class CommandReader extends Thread{
|
||||
|
||||
@ -65,12 +60,6 @@ class CommandReader extends Thread{
|
||||
public function __construct(?SleeperNotifier $notifier = null){
|
||||
$this->buffer = new \Threaded;
|
||||
$this->notifier = $notifier;
|
||||
|
||||
$opts = getopt("", ["disable-readline", "enable-readline"]);
|
||||
|
||||
if(extension_loaded("readline") and (Utils::getOS() === Utils::OS_WINDOWS ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){
|
||||
$this->type = self::TYPE_READLINE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,44 +116,28 @@ class CommandReader extends Thread{
|
||||
* @return bool if the main execution should continue reading lines
|
||||
*/
|
||||
private function readLine() : bool{
|
||||
$line = "";
|
||||
if($this->type === self::TYPE_READLINE){
|
||||
if(($raw = readline("> ")) !== false and ($line = trim($raw)) !== ""){
|
||||
readline_add_history($line);
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(!is_resource(self::$stdin)){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
switch($this->type){
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case self::TYPE_STREAM:
|
||||
//stream_select doesn't work on piped streams for some reason
|
||||
$r = [self::$stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
case self::TYPE_PIPED:
|
||||
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
|
||||
$this->initStdin();
|
||||
$this->synchronized(function() : void{
|
||||
$this->wait(200000);
|
||||
}); //prevent CPU waste if it's end of pipe
|
||||
return true; //loop back round
|
||||
}
|
||||
|
||||
$line = trim($raw);
|
||||
break;
|
||||
}
|
||||
if(!is_resource(self::$stdin)){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
$r = [self::$stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
|
||||
$this->initStdin();
|
||||
$this->synchronized(function() : void{
|
||||
$this->wait(200000);
|
||||
}); //prevent CPU waste if it's end of pipe
|
||||
return true; //loop back round
|
||||
}
|
||||
|
||||
$line = trim($raw);
|
||||
|
||||
if($line !== ""){
|
||||
$this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
||||
if($this->notifier !== null){
|
||||
@ -193,17 +166,11 @@ class CommandReader extends Thread{
|
||||
*/
|
||||
public function run(){
|
||||
$this->registerClassLoader();
|
||||
|
||||
if($this->type !== self::TYPE_READLINE){
|
||||
$this->initStdin();
|
||||
}
|
||||
$this->initStdin();
|
||||
|
||||
while(!$this->shutdown and $this->readLine());
|
||||
|
||||
if($this->type !== self::TYPE_READLINE){
|
||||
fclose(self::$stdin);
|
||||
}
|
||||
|
||||
fclose(self::$stdin);
|
||||
}
|
||||
|
||||
public function getThreadName() : string{
|
||||
|
@ -54,9 +54,9 @@ class StatusCommand extends VanillaCommand{
|
||||
$server = $sender->getServer();
|
||||
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
|
||||
|
||||
$time = microtime(true) - \pocketmine\START_TIME;
|
||||
$time = (int) (microtime(true) - \pocketmine\START_TIME);
|
||||
|
||||
$seconds = floor($time % 60);
|
||||
$seconds = $time % 60;
|
||||
$minutes = null;
|
||||
$hours = null;
|
||||
$days = null;
|
||||
@ -100,7 +100,6 @@ class StatusCommand extends VanillaCommand{
|
||||
$sender->sendMessage(TextFormat::GOLD . "Total memory: " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2), 2) . " MB.");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Total virtual memory: " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2), 2) . " MB.");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Heap memory: " . TextFormat::RED . number_format(round(($rUsage[0] / 1024) / 1024, 2), 2) . " MB.");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (system): " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2), 2) . " MB.");
|
||||
|
||||
if($server->getProperty("memory.global-limit") > 0){
|
||||
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (manager): " . TextFormat::RED . number_format(round($server->getProperty("memory.global-limit"), 2), 2) . " MB.");
|
||||
|
@ -77,9 +77,9 @@ use function abs;
|
||||
use function assert;
|
||||
use function cos;
|
||||
use function count;
|
||||
use function current;
|
||||
use function deg2rad;
|
||||
use function floor;
|
||||
use function fmod;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
@ -175,56 +175,56 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_RIDER_ROTATION_LOCKED = 57; //byte
|
||||
public const DATA_RIDER_MAX_ROTATION = 58; //float
|
||||
public const DATA_RIDER_MIN_ROTATION = 59; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS = 60; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_WAITING = 61; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 62; //int
|
||||
/* 63 (int) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_FACE = 64; //byte
|
||||
/* 65 (short) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_POS = 66; //block coords
|
||||
public const DATA_TRADING_PLAYER_EID = 67; //long
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS = 61; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_WAITING = 62; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 63; //int
|
||||
/* 64 (int) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_FACE = 65; //byte
|
||||
/* 66 (short) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_POS = 67; //block coords
|
||||
public const DATA_TRADING_PLAYER_EID = 68; //long
|
||||
|
||||
/* 69 (byte) command-block */
|
||||
public const DATA_COMMAND_BLOCK_COMMAND = 70; //string
|
||||
public const DATA_COMMAND_BLOCK_LAST_OUTPUT = 71; //string
|
||||
public const DATA_COMMAND_BLOCK_TRACK_OUTPUT = 72; //byte
|
||||
public const DATA_CONTROLLING_RIDER_SEAT_NUMBER = 73; //byte
|
||||
public const DATA_STRENGTH = 74; //int
|
||||
public const DATA_MAX_STRENGTH = 75; //int
|
||||
/* 76 (int) */
|
||||
public const DATA_LIMITED_LIFE = 77;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 80; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 81; //byte
|
||||
/* 82 (unknown) */
|
||||
public const DATA_SCORE_TAG = 83; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 84; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 85; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 86; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 87; //long
|
||||
/* 88 (float) related to panda sitting
|
||||
* 89 (float) related to panda sitting */
|
||||
public const DATA_EAT_COUNTER = 90; //int (used by pandas)
|
||||
public const DATA_FLAGS2 = 91; //long (extended data flags)
|
||||
/* 92 (float) related to panda lying down
|
||||
* 93 (float) related to panda lying down */
|
||||
public const DATA_AREA_EFFECT_CLOUD_DURATION = 94; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_SPAWN_TIME = 95; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_PER_TICK = 96; //float, usually negative
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP = 97; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_PICKUP_COUNT = 98; //int
|
||||
public const DATA_INTERACTIVE_TAG = 99; //string (button text)
|
||||
public const DATA_TRADE_TIER = 100; //int
|
||||
public const DATA_MAX_TRADE_TIER = 101; //int
|
||||
public const DATA_TRADE_XP = 102; //int
|
||||
public const DATA_SKIN_ID = 103; //int ???
|
||||
/* 104 (int) related to wither */
|
||||
public const DATA_COMMAND_BLOCK_TICK_DELAY = 105; //int
|
||||
public const DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 106; //byte
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_MIN = 107; //float
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_RANGE = 108; //float
|
||||
public const DATA_AMBIENT_SOUND_EVENT = 109; //string
|
||||
/* 70 (byte) command-block */
|
||||
public const DATA_COMMAND_BLOCK_COMMAND = 71; //string
|
||||
public const DATA_COMMAND_BLOCK_LAST_OUTPUT = 72; //string
|
||||
public const DATA_COMMAND_BLOCK_TRACK_OUTPUT = 73; //byte
|
||||
public const DATA_CONTROLLING_RIDER_SEAT_NUMBER = 74; //byte
|
||||
public const DATA_STRENGTH = 75; //int
|
||||
public const DATA_MAX_STRENGTH = 76; //int
|
||||
/* 77 (int) */
|
||||
public const DATA_LIMITED_LIFE = 78;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 79; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 80; //int
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 81; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 82; //byte
|
||||
/* 83 (unknown) */
|
||||
public const DATA_SCORE_TAG = 84; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 85; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 86; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 87; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 88; //long
|
||||
/* 89 (float) related to panda sitting
|
||||
* 90 (float) related to panda sitting */
|
||||
public const DATA_EAT_COUNTER = 91; //int (used by pandas)
|
||||
public const DATA_FLAGS2 = 92; //long (extended data flags)
|
||||
/* 93 (float) related to panda lying down
|
||||
* 94 (float) related to panda lying down */
|
||||
public const DATA_AREA_EFFECT_CLOUD_DURATION = 95; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_SPAWN_TIME = 96; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_PER_TICK = 97; //float, usually negative
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP = 98; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_PICKUP_COUNT = 99; //int
|
||||
public const DATA_INTERACTIVE_TAG = 100; //string (button text)
|
||||
public const DATA_TRADE_TIER = 101; //int
|
||||
public const DATA_MAX_TRADE_TIER = 102; //int
|
||||
public const DATA_TRADE_XP = 103; //int
|
||||
public const DATA_SKIN_ID = 104; //int ???
|
||||
/* 105 (int) related to wither */
|
||||
public const DATA_COMMAND_BLOCK_TICK_DELAY = 106; //int
|
||||
public const DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 107; //byte
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_MIN = 108; //float
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_RANGE = 109; //float
|
||||
public const DATA_AMBIENT_SOUND_EVENT = 110; //string
|
||||
|
||||
public const DATA_FLAG_ONFIRE = 0;
|
||||
public const DATA_FLAG_SNEAKING = 1;
|
||||
@ -333,8 +333,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*/
|
||||
private static $knownEntities = [];
|
||||
/**
|
||||
* @var string[][]
|
||||
* @phpstan-var array<class-string<Entity>, list<string>>
|
||||
* @var string[]
|
||||
* @phpstan-var array<class-string<Entity>, string>
|
||||
*/
|
||||
private static $saveNames = [];
|
||||
|
||||
@ -413,7 +413,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
self::$knownEntities[$name] = $className;
|
||||
}
|
||||
|
||||
self::$saveNames[$className] = $saveNames;
|
||||
self::$saveNames[$className] = reset($saveNames);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -871,8 +871,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
if(!isset(self::$saveNames[static::class])){
|
||||
throw new \InvalidStateException("Entity " . static::class . " is not registered");
|
||||
}
|
||||
reset(self::$saveNames[static::class]);
|
||||
return current(self::$saveNames[static::class]);
|
||||
return self::$saveNames[static::class];
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
@ -1312,7 +1311,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
public function getDirection() : ?int{
|
||||
$rotation = ($this->yaw - 90) % 360;
|
||||
$rotation = fmod($this->yaw - 90, 360);
|
||||
if($rotation < 0){
|
||||
$rotation += 360.0;
|
||||
}
|
||||
@ -1450,8 +1449,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->fall($this->fallDistance);
|
||||
$this->resetFallDistance();
|
||||
}
|
||||
}elseif($distanceThisTick < 0){
|
||||
}elseif($distanceThisTick < $this->fallDistance){
|
||||
//we've fallen some distance (distanceThisTick is negative)
|
||||
//or we ascended back towards where fall distance was measured from initially (distanceThisTick is positive but less than existing fallDistance)
|
||||
$this->fallDistance -= $distanceThisTick;
|
||||
}else{
|
||||
//we ascended past the apex where fall distance was originally being measured from
|
||||
//reset it so it will be measured starting from the new, higher position
|
||||
$this->fallDistance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,9 +51,9 @@ use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\Player;
|
||||
@ -300,17 +300,20 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
if($consumable instanceof MaybeConsumable and !$consumable->canBeConsumed()){
|
||||
return false;
|
||||
}
|
||||
if($consumable instanceof FoodSource && $consumable->requiresHunger() and !$this->isHungry()){
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::consumeObject($consumable);
|
||||
}
|
||||
|
||||
protected function applyConsumptionResults(Consumable $consumable) : void{
|
||||
if($consumable instanceof FoodSource){
|
||||
if($consumable->requiresHunger() and !$this->isHungry()){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addFood($consumable->getFoodRestore());
|
||||
$this->addSaturation($consumable->getSaturationRestore());
|
||||
}
|
||||
|
||||
return parent::consumeObject($consumable);
|
||||
parent::applyConsumptionResults($consumable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,12 +403,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
* @param bool $playSound Whether to play level-up and XP gained sounds.
|
||||
*/
|
||||
public function addXp(int $amount, bool $playSound = true) : bool{
|
||||
$this->totalXp += $amount;
|
||||
|
||||
$oldLevel = $this->getXpLevel();
|
||||
$oldTotal = $this->getCurrentTotalXp();
|
||||
|
||||
if($this->setCurrentTotalXp($oldTotal + $amount)){
|
||||
if($amount > 0){
|
||||
$this->totalXp += $amount;
|
||||
}
|
||||
|
||||
if($playSound){
|
||||
$newLevel = $this->getXpLevel();
|
||||
if((int) ($newLevel / 5) > (int) ($oldLevel / 5)){
|
||||
@ -808,7 +813,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->item = $this->getInventory()->getItemInHand();
|
||||
$pk->item = ItemStackWrapper::legacy($this->getInventory()->getItemInHand());
|
||||
$pk->metadata = $this->propertyManager->getAll();
|
||||
$player->dataPacket($pk);
|
||||
|
||||
@ -825,23 +830,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
public function broadcastMovement(bool $teleport = false) : void{
|
||||
//TODO: workaround 1.14.30 bug: MoveActor(Absolute|Delta)Packet don't work on players anymore :(
|
||||
$pk = new MovePlayerPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = $this->getOffsetPosition($this);
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->headYaw = $this->yaw;
|
||||
$pk->mode = $teleport ? MovePlayerPacket::MODE_TELEPORT : MovePlayerPacket::MODE_NORMAL;
|
||||
|
||||
//we can't assume that everyone who is using our chunk wants to see this movement,
|
||||
//because this human might be a player who shouldn't be receiving his own movement.
|
||||
//this didn't matter when we were able to use MoveActorPacket because
|
||||
//the client just ignored MoveActor for itself, but it doesn't ignore MovePlayer for itself.
|
||||
$this->server->broadcastPacket($this->hasSpawned, $pk);
|
||||
}
|
||||
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
if($this->inventory !== null){
|
||||
|
@ -363,13 +363,20 @@ abstract class Living extends Entity implements Damageable{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->applyConsumptionResults($consumable);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies effects from consuming the object. This shouldn't do any can-consume checks (those are expected to be
|
||||
* handled by the caller).
|
||||
*/
|
||||
protected function applyConsumptionResults(Consumable $consumable) : void{
|
||||
foreach($consumable->getAdditionalEffects() as $effect){
|
||||
$this->addEffect($effect);
|
||||
}
|
||||
|
||||
$consumable->onConsume($this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\event\inventory\InventoryPickupItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\Player;
|
||||
use function get_class;
|
||||
|
||||
@ -173,7 +174,7 @@ class ItemEntity extends Entity{
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = $this->asVector3();
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->item = $this->getItem();
|
||||
$pk->item = ItemStackWrapper::legacy($this->getItem());
|
||||
$pk->metadata = $this->propertyManager->getAll();
|
||||
|
||||
$player->dataPacket($pk);
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\utils;
|
||||
|
||||
use pocketmine\math\Math;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function count;
|
||||
use function max;
|
||||
|
||||
abstract class ExperienceUtils{
|
||||
|
@ -48,7 +48,7 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
|
||||
/**
|
||||
* @param Item[] $drops
|
||||
*/
|
||||
public function __construct(Player $player, Block $block, Item $item, bool $instaBreak = false, array $drops, int $xpDrops = 0){
|
||||
public function __construct(Player $player, Block $block, Item $item, bool $instaBreak = false, array $drops = [], int $xpDrops = 0){
|
||||
parent::__construct($block);
|
||||
$this->item = $item;
|
||||
$this->player = $player;
|
||||
|
@ -28,6 +28,9 @@ use pocketmine\event\Cancellable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\tile\Furnace;
|
||||
|
||||
/**
|
||||
* Called when a furnace is about to consume a new fuel item.
|
||||
*/
|
||||
class FurnaceBurnEvent extends BlockEvent implements Cancellable{
|
||||
/** @var Furnace */
|
||||
private $furnace;
|
||||
@ -53,18 +56,31 @@ class FurnaceBurnEvent extends BlockEvent implements Cancellable{
|
||||
return $this->fuel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ticks that the furnace will be powered for.
|
||||
*/
|
||||
public function getBurnTime() : int{
|
||||
return $this->burnTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ticks that the given fuel will power the furnace for.
|
||||
*/
|
||||
public function setBurnTime(int $burnTime) : void{
|
||||
$this->burnTime = $burnTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the fuel item will be consumed.
|
||||
*/
|
||||
public function isBurning() : bool{
|
||||
return $this->burning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the fuel will be consumed. If false, the furnace will smelt as if it consumed fuel, but no fuel
|
||||
* will be deducted.
|
||||
*/
|
||||
public function setBurning(bool $burning) : void{
|
||||
$this->burning = $burning;
|
||||
}
|
||||
|
@ -39,14 +39,22 @@ class PlayerCreationEvent extends Event{
|
||||
/** @var int */
|
||||
private $port;
|
||||
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<Player>
|
||||
*/
|
||||
private $baseClass;
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<Player>
|
||||
*/
|
||||
private $playerClass;
|
||||
|
||||
/**
|
||||
* @param string $baseClass
|
||||
* @param string $playerClass
|
||||
* @phpstan-param class-string<Player> $baseClass
|
||||
* @phpstan-param class-string<Player> $playerClass
|
||||
*/
|
||||
public function __construct(SourceInterface $interface, $baseClass, $playerClass, string $address, int $port){
|
||||
$this->interface = $interface;
|
||||
@ -80,6 +88,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @phpstan-return class-string<Player>
|
||||
*/
|
||||
public function getBaseClass(){
|
||||
return $this->baseClass;
|
||||
@ -87,6 +96,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @phpstan-param class-string<Player> $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -100,6 +110,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @phpstan-return class-string<Player>
|
||||
*/
|
||||
public function getPlayerClass(){
|
||||
return $this->playerClass;
|
||||
@ -107,6 +118,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @phpstan-param class-string<Player> $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -98,10 +98,10 @@ class ArmorInventory extends BaseInventory{
|
||||
|
||||
$pk = new MobArmorEquipmentPacket();
|
||||
$pk->entityRuntimeId = $this->getHolder()->getId();
|
||||
$pk->head = $this->getHelmet();
|
||||
$pk->chest = $this->getChestplate();
|
||||
$pk->legs = $this->getLeggings();
|
||||
$pk->feet = $this->getBoots();
|
||||
$pk->head = ItemStackWrapper::legacy($this->getHelmet());
|
||||
$pk->chest = ItemStackWrapper::legacy($this->getChestplate());
|
||||
$pk->legs = ItemStackWrapper::legacy($this->getLeggings());
|
||||
$pk->feet = ItemStackWrapper::legacy($this->getBoots());
|
||||
$pk->encode();
|
||||
|
||||
foreach($target as $player){
|
||||
@ -126,10 +126,10 @@ class ArmorInventory extends BaseInventory{
|
||||
|
||||
$pk = new MobArmorEquipmentPacket();
|
||||
$pk->entityRuntimeId = $this->getHolder()->getId();
|
||||
$pk->head = $this->getHelmet();
|
||||
$pk->chest = $this->getChestplate();
|
||||
$pk->legs = $this->getLeggings();
|
||||
$pk->feet = $this->getBoots();
|
||||
$pk->head = ItemStackWrapper::legacy($this->getHelmet());
|
||||
$pk->chest = ItemStackWrapper::legacy($this->getChestplate());
|
||||
$pk->legs = ItemStackWrapper::legacy($this->getLeggings());
|
||||
$pk->feet = ItemStackWrapper::legacy($this->getBoots());
|
||||
$pk->encode();
|
||||
|
||||
foreach($target as $player){
|
||||
|
@ -252,11 +252,11 @@ abstract class BaseInventory implements Inventory{
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$slot = $this->getItem($i);
|
||||
if($item->equals($slot)){
|
||||
if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){
|
||||
if(($diff = min($slot->getMaxStackSize(), $item->getMaxStackSize()) - $slot->getCount()) > 0){
|
||||
$count -= $diff;
|
||||
}
|
||||
}elseif($slot->isNull()){
|
||||
$count -= $this->getMaxStackSize();
|
||||
$count -= min($this->getMaxStackSize(), $item->getMaxStackSize());
|
||||
}
|
||||
|
||||
if($count <= 0){
|
||||
|
@ -64,6 +64,7 @@ abstract class ContainerInventory extends BaseInventory{
|
||||
public function onClose(Player $who) : void{
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowId = $who->getWindowId($this);
|
||||
$pk->server = $who->getClosingWindowId() !== $pk->windowId;
|
||||
$who->dataPacket($pk);
|
||||
parent::onClose($who);
|
||||
}
|
||||
|
@ -53,4 +53,13 @@ class PlayerCursorInventory extends BaseInventory{
|
||||
public function getHolder(){
|
||||
return $this->holder;
|
||||
}
|
||||
|
||||
public function sendContents($target) : void{
|
||||
//TODO: HACK!
|
||||
//Since 1.13, this is now part of a larger "UI inventory", and sending contents for this larger inventory does
|
||||
//not work the way it's intended to. Even if it did, it would be necessary to send all 51 slots just to update
|
||||
//this one, which is just not worth it.
|
||||
//This workaround isn't great, but it's at least simple.
|
||||
$this->sendSlot(0, $target);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\network\mcpe\protocol\CreativeContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\Player;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
@ -165,7 +166,7 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
$pk = new MobEquipmentPacket();
|
||||
$pk->entityRuntimeId = $this->getHolder()->getId();
|
||||
$pk->item = $item;
|
||||
$pk->item = ItemStackWrapper::legacy($item);
|
||||
$pk->inventorySlot = $pk->hotbarSlot = $this->getHeldItemIndex();
|
||||
$pk->windowId = ContainerIds::INVENTORY;
|
||||
|
||||
|
@ -108,9 +108,6 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
}
|
||||
}
|
||||
|
||||
if($iterations < 1){
|
||||
throw new TransactionValidationException("Tried to craft zero times");
|
||||
}
|
||||
if(count($txItems) > 0){
|
||||
//all items should be destroyed in this process
|
||||
throw new TransactionValidationException("Expected 0 ingredients left over, have " . count($txItems));
|
||||
@ -166,6 +163,7 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
*/
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID;
|
||||
$pk->server = true;
|
||||
$this->source->dataPacket($pk);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Player;
|
||||
use function array_keys;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function get_class;
|
||||
@ -168,6 +169,8 @@ class InventoryTransaction{
|
||||
}
|
||||
}
|
||||
}
|
||||
$needItems = array_values($needItems);
|
||||
$haveItems = array_values($haveItems);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,21 +233,34 @@ class InventoryTransaction{
|
||||
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
|
||||
assert(count($possibleActions) > 0);
|
||||
|
||||
$candidate = null;
|
||||
$newList = $possibleActions;
|
||||
foreach($possibleActions as $i => $action){
|
||||
if($action->getSourceItem()->equalsExact($needOrigin)){
|
||||
$newList = $possibleActions;
|
||||
if($candidate !== null){
|
||||
/*
|
||||
* we found multiple possible actions that match the origin action
|
||||
* this means that there are multiple ways that this chain could play out
|
||||
* if we cared so much about this, we could build all the possible chains in parallel and see which
|
||||
* variation managed to complete the chain, but this has an extremely high complexity which is not
|
||||
* worth the trouble for this scenario (we don't usually expect to see chains longer than a couple
|
||||
* of actions in here anyway), and might still result in multiple possible results.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
$candidate = $action;
|
||||
unset($newList[$i]);
|
||||
if(count($newList) === 0){
|
||||
return $action->getTargetItem();
|
||||
}
|
||||
$result = $this->findResultItem($action->getTargetItem(), $newList);
|
||||
if($result !== null){
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($candidate === null){
|
||||
//chaining is not possible with this origin, none of the actions are valid
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
if(count($newList) === 0){
|
||||
return $candidate->getTargetItem();
|
||||
}
|
||||
return $this->findResultItem($candidate->getTargetItem(), $newList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,6 +50,14 @@ class Bucket extends Item implements MaybeConsumable{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getFuelResidue() : Item{
|
||||
if($this->meta === Block::LAVA or $this->meta === Block::FLOWING_LAVA){
|
||||
return ItemFactory::get(Item::BUCKET);
|
||||
}
|
||||
|
||||
return parent::getFuelResidue();
|
||||
}
|
||||
|
||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
||||
$resultBlock = BlockFactory::get($this->meta);
|
||||
|
||||
|
@ -23,11 +23,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockToolType;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class Hoe extends TieredTool{
|
||||
|
||||
public function getBlockToolType() : int{
|
||||
return BlockToolType::TYPE_HOE;
|
||||
}
|
||||
|
||||
public function onAttackEntity(Entity $victim) : bool{
|
||||
return $this->applyDamage(1);
|
||||
}
|
||||
|
||||
public function onDestroyBlock(Block $block) : bool{
|
||||
if($block->getHardness() > 0){
|
||||
return $this->applyDamage(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +339,11 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
}
|
||||
}
|
||||
|
||||
$this->setNamedTagEntry($ench);
|
||||
if($ench->getCount() > 0){
|
||||
$this->setNamedTagEntry($ench);
|
||||
}else{
|
||||
$this->removeNamedTagEntry(self::TAG_ENCH);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeEnchantments() : void{
|
||||
@ -655,6 +659,16 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an item after burning fuel
|
||||
*/
|
||||
public function getFuelResidue() : Item{
|
||||
$item = clone $this;
|
||||
$item->pop();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many points of damage this item will deal to an entity when used as a weapon.
|
||||
*/
|
||||
|
Submodule src/pocketmine/lang/locale updated: 85343cfb7f...c85a7b79f3
@ -154,7 +154,6 @@ class Explosion{
|
||||
* and creating sounds and particles.
|
||||
*/
|
||||
public function explodeB() : bool{
|
||||
$send = [];
|
||||
$updateBlocks = [];
|
||||
|
||||
$source = (new Vector3($this->source->x, $this->source->y, $this->source->z))->floor();
|
||||
@ -254,7 +253,6 @@ class Explosion{
|
||||
$updateBlocks[$index] = true;
|
||||
}
|
||||
}
|
||||
$send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z);
|
||||
}
|
||||
|
||||
$this->level->addParticle(new HugeExplodeSeedParticle($source));
|
||||
|
@ -53,7 +53,6 @@ use pocketmine\level\format\io\ChunkRequestTask;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\level\format\io\LevelProvider;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\GeneratorManager;
|
||||
use pocketmine\level\generator\GeneratorRegisterTask;
|
||||
use pocketmine\level\generator\GeneratorUnregisterTask;
|
||||
@ -289,7 +288,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var bool */
|
||||
private $doingTick = false;
|
||||
|
||||
/** @var string|Generator */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<\pocketmine\level\generator\Generator>
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/** @var bool */
|
||||
|
@ -80,15 +80,14 @@ class LevelTimings{
|
||||
$this->entityTick = new TimingsHandler("** " . $name . "entityTick");
|
||||
$this->tileEntityTick = new TimingsHandler("** " . $name . "tileEntityTick");
|
||||
|
||||
$this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend");
|
||||
$this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare");
|
||||
Timings::init(); //make sure the timers we want are available
|
||||
$this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend", Timings::$playerChunkSendTimer);
|
||||
$this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare", Timings::$playerChunkSendTimer);
|
||||
|
||||
$this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad");
|
||||
$this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad", Timings::$worldLoadTimer);
|
||||
$this->syncChunkLoadDataTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Data");
|
||||
$this->syncChunkLoadEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Entities");
|
||||
$this->syncChunkLoadTileEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - TileEntities");
|
||||
|
||||
Timings::init(); //make sure the timer we want is available
|
||||
$this->syncChunkSaveTimer = new TimingsHandler("** " . $name . "syncChunkSave", Timings::$worldSaveTimer);
|
||||
|
||||
$this->doTick = new TimingsHandler($name . "doTick");
|
||||
|
@ -99,6 +99,8 @@ abstract class Biome{
|
||||
self::register(self::SWAMP, new SwampBiome());
|
||||
self::register(self::RIVER, new RiverBiome());
|
||||
|
||||
self::register(self::HELL, new HellBiome());
|
||||
|
||||
self::register(self::ICE_PLAINS, new IcePlainsBiome());
|
||||
|
||||
self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome());
|
||||
|
@ -921,7 +921,9 @@ class Chunk{
|
||||
|
||||
$biomeIds = $stream->get(256);
|
||||
if($lightPopulated){
|
||||
$heightMap = array_values(unpack("v*", $stream->get(512)));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("v*", $stream->get(512)); //unpack() will never fail here
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -929,6 +931,7 @@ class Chunk{
|
||||
$chunk->setGenerated($terrainGenerated);
|
||||
$chunk->setPopulated($terrainPopulated);
|
||||
$chunk->setLightPopulated($lightPopulated);
|
||||
$chunk->setChanged(false);
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ if(!defined(__NAMESPACE__ . '\ZERO_NIBBLE_ARRAY')){
|
||||
}
|
||||
|
||||
class SubChunk implements SubChunkInterface{
|
||||
private const ZERO_NIBBLE_ARRAY = ZERO_NIBBLE_ARRAY;
|
||||
|
||||
/** @var string */
|
||||
protected $ids;
|
||||
/** @var string */
|
||||
@ -68,7 +70,7 @@ class SubChunk implements SubChunkInterface{
|
||||
substr_count($this->ids, "\x00") === 4096 and
|
||||
(!$checkLight or (
|
||||
substr_count($this->skyLight, "\xff") === 2048 and
|
||||
$this->blockLight === ZERO_NIBBLE_ARRAY
|
||||
$this->blockLight === self::ZERO_NIBBLE_ARRAY
|
||||
))
|
||||
);
|
||||
}
|
||||
@ -230,14 +232,14 @@ class SubChunk implements SubChunkInterface{
|
||||
* reference to the const instead of duplicating the whole string. The string will only be duplicated when
|
||||
* modified, which is perfect for this purpose.
|
||||
*/
|
||||
if($this->data === ZERO_NIBBLE_ARRAY){
|
||||
$this->data = ZERO_NIBBLE_ARRAY;
|
||||
if($this->data === self::ZERO_NIBBLE_ARRAY){
|
||||
$this->data = self::ZERO_NIBBLE_ARRAY;
|
||||
}
|
||||
if($this->skyLight === ZERO_NIBBLE_ARRAY){
|
||||
$this->skyLight = ZERO_NIBBLE_ARRAY;
|
||||
if($this->skyLight === self::ZERO_NIBBLE_ARRAY){
|
||||
$this->skyLight = self::ZERO_NIBBLE_ARRAY;
|
||||
}
|
||||
if($this->blockLight === ZERO_NIBBLE_ARRAY){
|
||||
$this->blockLight = ZERO_NIBBLE_ARRAY;
|
||||
if($this->blockLight === self::ZERO_NIBBLE_ARRAY){
|
||||
$this->blockLight = self::ZERO_NIBBLE_ARRAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,6 @@ abstract class LevelProviderManager{
|
||||
throw new \InvalidArgumentException("Class $class cannot be constructed");
|
||||
}
|
||||
|
||||
/** @var LevelProvider $class */
|
||||
self::$providers[strtolower($class::getProviderName())] = $class;
|
||||
}
|
||||
|
||||
|
@ -183,8 +183,6 @@ class LevelDB extends BaseLevelProvider{
|
||||
if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
|
||||
$this->levelData->setString("generatorOptions", "");
|
||||
}
|
||||
|
||||
$db->close();
|
||||
}
|
||||
|
||||
public static function getProviderName() : string{
|
||||
@ -269,9 +267,6 @@ class LevelDB extends BaseLevelProvider{
|
||||
$db->put(self::ENTRY_FLAT_WORLD_LAYERS, $out); //Add vanilla flatworld layers to allow terrain generation by MCPE to continue seamlessly
|
||||
}
|
||||
}
|
||||
|
||||
$db->close();
|
||||
|
||||
}
|
||||
|
||||
public function saveLevelData(){
|
||||
@ -368,7 +363,9 @@ class LevelDB extends BaseLevelProvider{
|
||||
if(($maps2d = $this->db->get($index . self::TAG_DATA_2D)) !== false){
|
||||
$binaryStream->setBuffer($maps2d, 0);
|
||||
|
||||
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("v*", $binaryStream->get(512)); //unpack() will never fail here
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
$biomeIds = $binaryStream->get(256);
|
||||
}
|
||||
break;
|
||||
@ -411,8 +408,13 @@ class LevelDB extends BaseLevelProvider{
|
||||
$subChunks[$yy] = new SubChunk($ids, $data, $skyLight, $blockLight);
|
||||
}
|
||||
|
||||
$heightMap = array_values(unpack("C*", $binaryStream->get(256)));
|
||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("C*", $binaryStream->get(256)); //unpack() will never fail here, but static analysers don't know that
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
|
||||
/** @var int[] $unpackedBiomeIds */
|
||||
$unpackedBiomeIds = unpack("N*", $binaryStream->get(1024)); //nor here
|
||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values($unpackedBiomeIds));
|
||||
break;
|
||||
default:
|
||||
//TODO: set chunks read-only so the version on disk doesn't get overwritten
|
||||
@ -548,6 +550,6 @@ class LevelDB extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->db->close();
|
||||
unset($this->db);
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,9 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
$heightMap = [];
|
||||
if($chunk->hasTag("HeightMap", ByteArrayTag::class)){
|
||||
$heightMap = array_values(unpack("C*", $chunk->getByteArray("HeightMap")));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("C*", $chunk->getByteArray("HeightMap")); //unpack() will never fail here
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
}elseif($chunk->hasTag("HeightMap", IntArrayTag::class)){
|
||||
$heightMap = $chunk->getIntArray("HeightMap"); #blameshoghicp
|
||||
}
|
||||
@ -245,11 +247,13 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
if($isValid){
|
||||
$files = array_filter(scandir($path . "/region/", SCANDIR_SORT_NONE), function(string $file) : bool{
|
||||
return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file
|
||||
$extPos = strrpos($file, ".");
|
||||
return $extPos !== false && substr($file, $extPos + 1, 2) === "mc"; //region file
|
||||
});
|
||||
|
||||
foreach($files as $f){
|
||||
if(substr($f, strrpos($f, ".") + 1) !== static::REGION_FILE_EXTENSION){
|
||||
$extPos = strrpos($f, ".");
|
||||
if($extPos !== false && substr($f, $extPos + 1) !== static::REGION_FILE_EXTENSION){
|
||||
$isValid = false;
|
||||
break;
|
||||
}
|
||||
@ -389,6 +393,9 @@ class McRegion extends BaseLevelProvider{
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
assert(is_int($regionX) and is_int($regionZ));
|
||||
|
||||
if(!file_exists($this->pathToRegion($regionX, $regionZ))){
|
||||
return null;
|
||||
}
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$chunkData = $this->getRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f);
|
||||
|
@ -69,11 +69,14 @@ final class RegionGarbageMap{
|
||||
/** @var RegionLocationTableEntry|null $prevEntry */
|
||||
$prevEntry = null;
|
||||
foreach($usedMap as $firstSector => $entry){
|
||||
$expectedStart = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
|
||||
$actualStart = $entry->getFirstSector();
|
||||
if($expectedStart < $actualStart){
|
||||
$prevEndPlusOne = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
|
||||
$currentStart = $entry->getFirstSector();
|
||||
if($prevEndPlusOne < $currentStart){
|
||||
//found a gap in the table
|
||||
$garbageMap[$expectedStart] = new RegionLocationTableEntry($expectedStart, $actualStart - $expectedStart, 0);
|
||||
$garbageMap[$prevEndPlusOne] = new RegionLocationTableEntry($prevEndPlusOne, $currentStart - $prevEndPlusOne, 0);
|
||||
}elseif($prevEndPlusOne > $currentStart){
|
||||
//current entry starts inside the previous. This would be a bug since RegionLoader should prevent this
|
||||
throw new AssumptionFailedError("Overlapping entries detected");
|
||||
}
|
||||
$prevEntry = $entry;
|
||||
}
|
||||
|
@ -27,12 +27,13 @@ use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use function assert;
|
||||
use function ceil;
|
||||
use function chr;
|
||||
use function clearstatcache;
|
||||
use function fclose;
|
||||
use function feof;
|
||||
use function file_exists;
|
||||
use function filesize;
|
||||
use function fopen;
|
||||
@ -43,14 +44,12 @@ use function fwrite;
|
||||
use function is_resource;
|
||||
use function ksort;
|
||||
use function max;
|
||||
use function ord;
|
||||
use function pack;
|
||||
use function str_pad;
|
||||
use function str_repeat;
|
||||
use function stream_set_read_buffer;
|
||||
use function stream_set_write_buffer;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function time;
|
||||
use function touch;
|
||||
use function unpack;
|
||||
@ -99,6 +98,7 @@ class RegionLoader{
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
public function open(){
|
||||
clearstatcache(false, $this->filePath);
|
||||
$exists = file_exists($this->filePath);
|
||||
if(!$exists){
|
||||
touch($this->filePath);
|
||||
@ -146,37 +146,34 @@ class RegionLoader{
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index]->getFirstSector() << 12);
|
||||
|
||||
$prefix = fread($this->filePointer, 4);
|
||||
if($prefix === false or strlen($prefix) !== 4){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (unexpected end of file reading length prefix)");
|
||||
}
|
||||
$length = Binary::readInt($prefix);
|
||||
/*
|
||||
* this might cause us to read some junk, but under normal circumstances it won't be any more than 4096 bytes wasted.
|
||||
* doing this in a single call is faster than making two seeks and reads to fetch the chunk.
|
||||
* this relies on the assumption that the end of the file is always padded to a multiple of 4096 bytes.
|
||||
*/
|
||||
$bytesToRead = $this->locationTable[$index]->getSectorCount() << 12;
|
||||
$payload = fread($this->filePointer, $bytesToRead);
|
||||
|
||||
if($length <= 0){ //TODO: if we reached here, the locationTable probably needs updating
|
||||
return null;
|
||||
}
|
||||
if($length > self::MAX_SECTOR_LENGTH){ //corrupted
|
||||
throw new CorruptedChunkException("Length for chunk x=$x,z=$z ($length) is larger than maximum " . self::MAX_SECTOR_LENGTH);
|
||||
if($payload === false || strlen($payload) !== $bytesToRead){
|
||||
throw new CorruptedChunkException("Corrupted chunk detected (unexpected EOF, truncated or non-padded chunk found)");
|
||||
}
|
||||
$stream = new BinaryStream($payload);
|
||||
|
||||
if($length > ($this->locationTable[$index]->getSectorCount() << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index]->getSectorCount() << 12) . " sectors, got $length sectors)");
|
||||
$old = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($old->getFirstSector(), $length >> 12, time());
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
try{
|
||||
$length = $stream->getInt();
|
||||
if($length <= 0){ //TODO: if we reached here, the locationTable probably needs updating
|
||||
return null;
|
||||
}
|
||||
|
||||
$chunkData = fread($this->filePointer, $length);
|
||||
if($chunkData === false or strlen($chunkData) !== $length){
|
||||
throw new CorruptedChunkException("Corrupted chunk detected (unexpected end of file reading chunk data)");
|
||||
}
|
||||
$compression = $stream->getByte();
|
||||
if($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
|
||||
throw new CorruptedChunkException("Invalid compression type (got $compression, expected " . self::COMPRESSION_ZLIB . " or " . self::COMPRESSION_GZIP . ")");
|
||||
}
|
||||
|
||||
$compression = ord($chunkData[0]);
|
||||
if($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
|
||||
throw new CorruptedChunkException("Invalid compression type (got $compression, expected " . self::COMPRESSION_ZLIB . " or " . self::COMPRESSION_GZIP . ")");
|
||||
return $stream->get($length - 1); //length prefix includes the compression byte
|
||||
}catch(BinaryDataException $e){
|
||||
throw new CorruptedChunkException("Corrupted chunk detected: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
return substr($chunkData, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,6 +183,23 @@ class RegionLoader{
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
private function disposeGarbageArea(RegionLocationTableEntry $oldLocation) : void{
|
||||
/* release the area containing the old copy to the garbage pool */
|
||||
$this->garbageTable->add($oldLocation);
|
||||
|
||||
$endGarbage = $this->garbageTable->end();
|
||||
$nextSector = $this->nextSector;
|
||||
for(; $endGarbage !== null and $endGarbage->getLastSector() + 1 === $nextSector; $endGarbage = $this->garbageTable->end()){
|
||||
$nextSector = $endGarbage->getFirstSector();
|
||||
$this->garbageTable->remove($endGarbage);
|
||||
}
|
||||
|
||||
if($nextSector !== $this->nextSector){
|
||||
$this->nextSector = $nextSector;
|
||||
ftruncate($this->filePointer, $this->nextSector << 12);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws ChunkException
|
||||
@ -230,20 +244,7 @@ class RegionLoader{
|
||||
$this->writeLocationIndex($index);
|
||||
|
||||
if($oldLocation !== null){
|
||||
/* release the area containing the old copy to the garbage pool */
|
||||
$this->garbageTable->add($oldLocation);
|
||||
|
||||
$endGarbage = $this->garbageTable->end();
|
||||
$nextSector = $this->nextSector;
|
||||
for(; $endGarbage !== null and $endGarbage->getLastSector() + 1 === $nextSector; $endGarbage = $this->garbageTable->end()){
|
||||
$nextSector = $endGarbage->getFirstSector();
|
||||
$this->garbageTable->remove($endGarbage);
|
||||
}
|
||||
|
||||
if($nextSector !== $this->nextSector){
|
||||
$this->nextSector = $nextSector;
|
||||
ftruncate($this->filePointer, $this->nextSector << 12);
|
||||
}
|
||||
$this->disposeGarbageArea($oldLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,8 +254,12 @@ class RegionLoader{
|
||||
*/
|
||||
public function removeChunk(int $x, int $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$oldLocation = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = null;
|
||||
$this->writeLocationIndex($index);
|
||||
if($oldLocation !== null){
|
||||
$this->disposeGarbageArea($oldLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,6 +308,7 @@ class RegionLoader{
|
||||
throw new CorruptedRegionException("Corrupted region header (unexpected end of file)");
|
||||
}
|
||||
|
||||
/** @var int[] $data */
|
||||
$data = unpack("N*", $headerRaw);
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
@ -335,6 +341,8 @@ class RegionLoader{
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
|
||||
$fileSize = filesize($this->filePath);
|
||||
if($fileSize === false) throw new AssumptionFailedError("filesize() should not return false here");
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$entry = $this->locationTable[$i];
|
||||
if($entry === null){
|
||||
@ -347,8 +355,7 @@ class RegionLoader{
|
||||
|
||||
//TODO: more validity checks
|
||||
|
||||
fseek($this->filePointer, $fileOffset);
|
||||
if(feof($this->filePointer)){
|
||||
if($fileOffset >= $fileSize){
|
||||
throw new CorruptedRegionException("Region file location offset x=$x,z=$z points to invalid file location $fileOffset");
|
||||
}
|
||||
if(isset($usedOffsets[$offset])){
|
||||
@ -401,6 +408,7 @@ class RegionLoader{
|
||||
fwrite($this->filePointer, Binary::writeInt($entry !== null ? ($entry->getFirstSector() << 8) | $entry->getSectorCount() : 0), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($entry !== null ? $entry->getTimestamp() : 0), 4);
|
||||
clearstatcache(false, $this->filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,10 @@ use function unserialize;
|
||||
|
||||
class GeneratorRegisterTask extends AsyncTask{
|
||||
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<Generator>
|
||||
*/
|
||||
public $generatorClass;
|
||||
/** @var string */
|
||||
public $settings;
|
||||
@ -48,6 +51,7 @@ class GeneratorRegisterTask extends AsyncTask{
|
||||
|
||||
/**
|
||||
* @param mixed[] $generatorSettings
|
||||
* @phpstan-param class-string<Generator> $generatorClass
|
||||
* @phpstan-param array<string, mixed> $generatorSettings
|
||||
*/
|
||||
public function __construct(Level $level, string $generatorClass, array $generatorSettings = []){
|
||||
|
@ -144,16 +144,9 @@ class Normal extends Generator{
|
||||
return Biome::BIRCH_FOREST;
|
||||
}
|
||||
}else{
|
||||
//FIXME: This will always cause River to be used since the rainfall is always greater than 0.8 if we
|
||||
//reached this branch. However I don't think that substituting temperature for rainfall is correct given
|
||||
//that mountain biomes are supposed to be pretty cold.
|
||||
if($rainfall < 0.25){
|
||||
return Biome::MOUNTAINS;
|
||||
}elseif($rainfall < 0.70){
|
||||
return Biome::SMALL_MOUNTAINS;
|
||||
}else{
|
||||
return Biome::RIVER;
|
||||
}
|
||||
//Previously here, we had a (broken) condition to generate mountains, but fixing it would have
|
||||
//caused generation changes on a patch release, so we can't keep it here for now.
|
||||
return Biome::RIVER;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\utils\UUID;
|
||||
@ -104,7 +105,7 @@ class FloatingTextParticle extends Particle{
|
||||
$pk->username = $name;
|
||||
$pk->entityRuntimeId = $this->entityId;
|
||||
$pk->position = $this->asVector3(); //TODO: check offset
|
||||
$pk->item = ItemFactory::get(Item::AIR, 0, 0);
|
||||
$pk->item = ItemStackWrapper::legacy(ItemFactory::get(Item::AIR, 0, 0));
|
||||
|
||||
$flags = (
|
||||
1 << Entity::DATA_FLAG_IMMOBILE
|
||||
|
@ -36,65 +36,79 @@ abstract class Particle extends Vector3{
|
||||
public const TYPE_EXPLODE = 6;
|
||||
public const TYPE_EVAPORATION = 7;
|
||||
public const TYPE_FLAME = 8;
|
||||
public const TYPE_LAVA = 9;
|
||||
public const TYPE_LARGE_SMOKE = 10;
|
||||
public const TYPE_REDSTONE = 11;
|
||||
public const TYPE_RISING_RED_DUST = 12;
|
||||
public const TYPE_ITEM_BREAK = 13;
|
||||
public const TYPE_SNOWBALL_POOF = 14;
|
||||
public const TYPE_HUGE_EXPLODE = 15;
|
||||
public const TYPE_HUGE_EXPLODE_SEED = 16;
|
||||
public const TYPE_MOB_FLAME = 17;
|
||||
public const TYPE_HEART = 18;
|
||||
public const TYPE_TERRAIN = 19;
|
||||
public const TYPE_SUSPENDED_TOWN = 20, TYPE_TOWN_AURA = 20;
|
||||
public const TYPE_PORTAL = 21;
|
||||
//22 same as 21
|
||||
public const TYPE_SPLASH = 23, TYPE_WATER_SPLASH = 23;
|
||||
public const TYPE_WATER_SPLASH_MANUAL = 24;
|
||||
public const TYPE_WATER_WAKE = 25;
|
||||
public const TYPE_DRIP_WATER = 26;
|
||||
public const TYPE_DRIP_LAVA = 27;
|
||||
public const TYPE_DRIP_HONEY = 28;
|
||||
public const TYPE_FALLING_DUST = 29, TYPE_DUST = 29;
|
||||
public const TYPE_MOB_SPELL = 30;
|
||||
public const TYPE_MOB_SPELL_AMBIENT = 31;
|
||||
public const TYPE_MOB_SPELL_INSTANTANEOUS = 32;
|
||||
public const TYPE_INK = 33;
|
||||
public const TYPE_SLIME = 34;
|
||||
public const TYPE_RAIN_SPLASH = 35;
|
||||
public const TYPE_VILLAGER_ANGRY = 36;
|
||||
public const TYPE_VILLAGER_HAPPY = 37;
|
||||
public const TYPE_ENCHANTMENT_TABLE = 38;
|
||||
public const TYPE_TRACKING_EMITTER = 39;
|
||||
public const TYPE_NOTE = 40;
|
||||
public const TYPE_WITCH_SPELL = 41;
|
||||
public const TYPE_CARROT = 42;
|
||||
public const TYPE_MOB_APPEARANCE = 43;
|
||||
public const TYPE_END_ROD = 44;
|
||||
public const TYPE_DRAGONS_BREATH = 45;
|
||||
public const TYPE_SPIT = 46;
|
||||
public const TYPE_TOTEM = 47;
|
||||
public const TYPE_FOOD = 48;
|
||||
public const TYPE_FIREWORKS_STARTER = 49;
|
||||
public const TYPE_FIREWORKS_SPARK = 50;
|
||||
public const TYPE_FIREWORKS_OVERLAY = 51;
|
||||
public const TYPE_BALLOON_GAS = 52;
|
||||
public const TYPE_COLORED_FLAME = 53;
|
||||
public const TYPE_SPARKLER = 54;
|
||||
public const TYPE_CONDUIT = 55;
|
||||
public const TYPE_BUBBLE_COLUMN_UP = 56;
|
||||
public const TYPE_BUBBLE_COLUMN_DOWN = 57;
|
||||
public const TYPE_SNEEZE = 58;
|
||||
public const TYPE_SHULKER_BULLET = 59;
|
||||
public const TYPE_BLEACH = 60;
|
||||
public const TYPE_DRAGON_DESTROY_BLOCK = 61;
|
||||
public const TYPE_MYCELIUM_DUST = 62;
|
||||
public const TYPE_FALLING_RED_DUST = 63;
|
||||
public const TYPE_CAMPFIRE_SMOKE = 64;
|
||||
public const TYPE_TALL_CAMPFIRE_SMOKE = 65;
|
||||
public const TYPE_DRAGON_BREATH_FIRE = 66;
|
||||
public const TYPE_DRAGON_BREATH_TRAIL = 67;
|
||||
public const TYPE_CANDLE_FLAME = 9;
|
||||
public const TYPE_LAVA = 10;
|
||||
public const TYPE_LARGE_SMOKE = 11;
|
||||
public const TYPE_REDSTONE = 12;
|
||||
public const TYPE_RISING_RED_DUST = 13;
|
||||
public const TYPE_ITEM_BREAK = 14;
|
||||
public const TYPE_SNOWBALL_POOF = 15;
|
||||
public const TYPE_HUGE_EXPLODE = 16;
|
||||
public const TYPE_HUGE_EXPLODE_SEED = 17;
|
||||
public const TYPE_MOB_FLAME = 18;
|
||||
public const TYPE_HEART = 19;
|
||||
public const TYPE_TERRAIN = 20;
|
||||
public const TYPE_SUSPENDED_TOWN = 21, TYPE_TOWN_AURA = 21;
|
||||
public const TYPE_PORTAL = 22;
|
||||
//23 same as 22
|
||||
public const TYPE_SPLASH = 24, TYPE_WATER_SPLASH = 24;
|
||||
public const TYPE_WATER_SPLASH_MANUAL = 25;
|
||||
public const TYPE_WATER_WAKE = 26;
|
||||
public const TYPE_DRIP_WATER = 27;
|
||||
public const TYPE_DRIP_LAVA = 28;
|
||||
public const TYPE_DRIP_HONEY = 29;
|
||||
public const TYPE_STALACTITE_DRIP_WATER = 30;
|
||||
public const TYPE_STALACTITE_DRIP_LAVA = 31;
|
||||
public const TYPE_FALLING_DUST = 32, TYPE_DUST = 32;
|
||||
public const TYPE_MOB_SPELL = 33;
|
||||
public const TYPE_MOB_SPELL_AMBIENT = 34;
|
||||
public const TYPE_MOB_SPELL_INSTANTANEOUS = 35;
|
||||
public const TYPE_INK = 36;
|
||||
public const TYPE_SLIME = 37;
|
||||
public const TYPE_RAIN_SPLASH = 38;
|
||||
public const TYPE_VILLAGER_ANGRY = 39;
|
||||
public const TYPE_VILLAGER_HAPPY = 40;
|
||||
public const TYPE_ENCHANTMENT_TABLE = 41;
|
||||
public const TYPE_TRACKING_EMITTER = 42;
|
||||
public const TYPE_NOTE = 43;
|
||||
public const TYPE_WITCH_SPELL = 44;
|
||||
public const TYPE_CARROT = 45;
|
||||
public const TYPE_MOB_APPEARANCE = 46;
|
||||
public const TYPE_END_ROD = 47;
|
||||
public const TYPE_DRAGONS_BREATH = 48;
|
||||
public const TYPE_SPIT = 49;
|
||||
public const TYPE_TOTEM = 50;
|
||||
public const TYPE_FOOD = 51;
|
||||
public const TYPE_FIREWORKS_STARTER = 52;
|
||||
public const TYPE_FIREWORKS_SPARK = 53;
|
||||
public const TYPE_FIREWORKS_OVERLAY = 54;
|
||||
public const TYPE_BALLOON_GAS = 55;
|
||||
public const TYPE_COLORED_FLAME = 56;
|
||||
public const TYPE_SPARKLER = 57;
|
||||
public const TYPE_CONDUIT = 58;
|
||||
public const TYPE_BUBBLE_COLUMN_UP = 59;
|
||||
public const TYPE_BUBBLE_COLUMN_DOWN = 60;
|
||||
public const TYPE_SNEEZE = 61;
|
||||
public const TYPE_SHULKER_BULLET = 62;
|
||||
public const TYPE_BLEACH = 63;
|
||||
public const TYPE_DRAGON_DESTROY_BLOCK = 64;
|
||||
public const TYPE_MYCELIUM_DUST = 65;
|
||||
public const TYPE_FALLING_RED_DUST = 66;
|
||||
public const TYPE_CAMPFIRE_SMOKE = 67;
|
||||
public const TYPE_TALL_CAMPFIRE_SMOKE = 68;
|
||||
public const TYPE_DRAGON_BREATH_FIRE = 69;
|
||||
public const TYPE_DRAGON_BREATH_TRAIL = 70;
|
||||
public const TYPE_BLUE_FLAME = 71;
|
||||
public const TYPE_SOUL = 72;
|
||||
public const TYPE_OBSIDIAN_TEAR = 73;
|
||||
public const TYPE_PORTAL_REVERSE = 74;
|
||||
public const TYPE_SNOWFLAKE = 75;
|
||||
public const TYPE_VIBRATION_SIGNAL = 76;
|
||||
public const TYPE_SCULK_SENSOR_REDSTONE = 77;
|
||||
public const TYPE_SPORE_BLOSSOM_SHOWER = 78;
|
||||
public const TYPE_SPORE_BLOSSOM_AMBIENT = 79;
|
||||
public const TYPE_WAX = 80;
|
||||
public const TYPE_ELECTRIC_SPARK = 81;
|
||||
|
||||
/**
|
||||
* @return DataPacket|DataPacket[]
|
||||
|
@ -29,7 +29,10 @@ namespace pocketmine\metadata;
|
||||
use pocketmine\plugin\Plugin;
|
||||
|
||||
abstract class MetadataStore{
|
||||
/** @var \SplObjectStorage[]|MetadataValue[][] */
|
||||
/**
|
||||
* @var \SplObjectStorage[]|MetadataValue[][]
|
||||
* @phpstan-var array<string, \SplObjectStorage<Plugin, MetadataValue>>
|
||||
*/
|
||||
private $metadataMap;
|
||||
|
||||
/**
|
||||
@ -41,6 +44,7 @@ abstract class MetadataStore{
|
||||
$owningPlugin = $newMetadataValue->getOwningPlugin();
|
||||
|
||||
if(!isset($this->metadataMap[$key])){
|
||||
/** @phpstan-var \SplObjectStorage<Plugin, MetadataValue> $entry */
|
||||
$entry = new \SplObjectStorage();
|
||||
$this->metadataMap[$key] = $entry;
|
||||
}else{
|
||||
@ -92,7 +96,6 @@ abstract class MetadataStore{
|
||||
* @return void
|
||||
*/
|
||||
public function invalidateAll(Plugin $owningPlugin){
|
||||
/** @var \SplObjectStorage|MetadataValue[] $values */
|
||||
foreach($this->metadataMap as $values){
|
||||
if(isset($values[$owningPlugin])){
|
||||
$values[$owningPlugin]->invalidate();
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\block\BlockIds;
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\Durable;
|
||||
@ -32,9 +33,14 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\ItemIds;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\LittleEndianNBTStream;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\NamedTag;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\GameRuleType;
|
||||
@ -47,6 +53,7 @@ use pocketmine\network\mcpe\protocol\types\StructureEditorData;
|
||||
use pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\UUID;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function strlen;
|
||||
|
||||
@ -54,6 +61,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
|
||||
private const DAMAGE_TAG = "Damage"; //TAG_Int
|
||||
private const DAMAGE_TAG_CONFLICT_RESOLUTION = "___Damage_ProtocolCollisionResolution___";
|
||||
private const PM_META_TAG = "___Meta___";
|
||||
|
||||
public function getString() : string{
|
||||
return $this->get($this->getUnsignedVarInt());
|
||||
@ -83,6 +91,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
|
||||
public function getSkin() : SkinData{
|
||||
$skinId = $this->getString();
|
||||
$skinPlayFabId = $this->getString();
|
||||
$skinResourcePatch = $this->getString();
|
||||
$skinData = $this->getSkinImage();
|
||||
$animationCount = $this->getLInt();
|
||||
@ -91,7 +100,8 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$skinImage = $this->getSkinImage();
|
||||
$animationType = $this->getLInt();
|
||||
$animationFrames = $this->getLFloat();
|
||||
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames);
|
||||
$expressionType = $this->getLInt();
|
||||
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
|
||||
}
|
||||
$capeData = $this->getSkinImage();
|
||||
$geometryData = $this->getString();
|
||||
@ -128,7 +138,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
);
|
||||
}
|
||||
|
||||
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId, $armSize, $skinColor, $personaPieces, $pieceTintColors);
|
||||
return new SkinData($skinId, $skinPlayFabId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId, $armSize, $skinColor, $personaPieces, $pieceTintColors);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,6 +146,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
*/
|
||||
public function putSkin(SkinData $skin){
|
||||
$this->putString($skin->getSkinId());
|
||||
$this->putString($skin->getPlayFabId());
|
||||
$this->putString($skin->getResourcePatch());
|
||||
$this->putSkinImage($skin->getSkinImage());
|
||||
$this->putLInt(count($skin->getAnimations()));
|
||||
@ -143,6 +154,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putSkinImage($animation->getImage());
|
||||
$this->putLInt($animation->getType());
|
||||
$this->putLFloat($animation->getFrames());
|
||||
$this->putLInt($animation->getExpressionType());
|
||||
}
|
||||
$this->putSkinImage($skin->getCapeImage());
|
||||
$this->putString($skin->getGeometryData());
|
||||
@ -185,82 +197,134 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putString($image->getData());
|
||||
}
|
||||
|
||||
public function getSlot() : Item{
|
||||
$id = $this->getVarInt();
|
||||
if($id === 0){
|
||||
public function getItemStackWithoutStackId() : Item{
|
||||
return $this->getItemStack(function() : void{
|
||||
//NOOP
|
||||
});
|
||||
}
|
||||
|
||||
public function putItemStackWithoutStackId(Item $item) : void{
|
||||
$this->putItemStack($item, function() : void{
|
||||
//NOOP
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure(NetworkBinaryStream) : void $readExtraCrapInTheMiddle
|
||||
*/
|
||||
public function getItemStack(\Closure $readExtraCrapInTheMiddle) : Item{
|
||||
$netId = $this->getVarInt();
|
||||
if($netId === 0){
|
||||
return ItemFactory::get(0, 0, 0);
|
||||
}
|
||||
|
||||
$auxValue = $this->getVarInt();
|
||||
$data = $auxValue >> 8;
|
||||
$cnt = $auxValue & 0xff;
|
||||
$cnt = $this->getLShort();
|
||||
$netData = $this->getUnsignedVarInt();
|
||||
|
||||
$nbtLen = $this->getLShort();
|
||||
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkId($netId, $netData);
|
||||
|
||||
/** @var CompoundTag|null $nbt */
|
||||
$nbt = null;
|
||||
if($nbtLen === 0xffff){
|
||||
$nbtDataVersion = $this->getByte();
|
||||
if($nbtDataVersion !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT data version $nbtDataVersion");
|
||||
$readExtraCrapInTheMiddle($this);
|
||||
|
||||
$this->getVarInt();
|
||||
|
||||
$extraData = new NetworkBinaryStream($this->getString());
|
||||
return (static function() use ($extraData, $netId, $id, $meta, $cnt) : Item{
|
||||
$nbtLen = $extraData->getLShort();
|
||||
|
||||
/** @var CompoundTag|null $nbt */
|
||||
$nbt = null;
|
||||
if($nbtLen === 0xffff){
|
||||
$nbtDataVersion = $extraData->getByte();
|
||||
if($nbtDataVersion !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT data version $nbtDataVersion");
|
||||
}
|
||||
$decodedNBT = (new LittleEndianNBTStream())->read($extraData->buffer, false, $extraData->offset, 512);
|
||||
if(!($decodedNBT instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Unexpected root tag type for itemstack");
|
||||
}
|
||||
$nbt = $decodedNBT;
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
$decodedNBT = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
if(!($decodedNBT instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Unexpected root tag type for itemstack");
|
||||
|
||||
//TODO
|
||||
for($i = 0, $canPlaceOn = $extraData->getLInt(); $i < $canPlaceOn; ++$i){
|
||||
$extraData->get($extraData->getLShort());
|
||||
}
|
||||
$nbt = $decodedNBT;
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
|
||||
//TODO
|
||||
for($i = 0, $canPlaceOn = $this->getVarInt(); $i < $canPlaceOn; ++$i){
|
||||
$this->getString();
|
||||
}
|
||||
//TODO
|
||||
for($i = 0, $canDestroy = $extraData->getLInt(); $i < $canDestroy; ++$i){
|
||||
$extraData->get($extraData->getLShort());
|
||||
}
|
||||
|
||||
//TODO
|
||||
for($i = 0, $canDestroy = $this->getVarInt(); $i < $canDestroy; ++$i){
|
||||
$this->getString();
|
||||
}
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$extraData->getLLong(); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
|
||||
if($id === ItemIds::SHIELD){
|
||||
$this->getVarLong(); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
if($nbt !== null){
|
||||
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
|
||||
$data = $nbt->getInt(self::DAMAGE_TAG);
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
if($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
goto end;
|
||||
if(!$extraData->feof()){
|
||||
throw new \UnexpectedValueException("Unexpected trailing extradata for network item $netId");
|
||||
}
|
||||
|
||||
if($nbt !== null){
|
||||
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
|
||||
$meta = $nbt->getInt(self::DAMAGE_TAG);
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$conflicted->setName(self::DAMAGE_TAG);
|
||||
$nbt->setTag($conflicted);
|
||||
}elseif($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
}
|
||||
}elseif(($metaTag = $nbt->getTag(self::PM_META_TAG)) instanceof IntTag){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
$meta = $metaTag->getValue();
|
||||
$nbt->removeTag(self::PM_META_TAG);
|
||||
if($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$conflicted->setName(self::DAMAGE_TAG);
|
||||
$nbt->setTag($conflicted);
|
||||
}
|
||||
}
|
||||
end:
|
||||
return ItemFactory::get($id, $data, $cnt, $nbt);
|
||||
return ItemFactory::get($id, $meta, $cnt, $nbt);
|
||||
})();
|
||||
}
|
||||
|
||||
public function putSlot(Item $item) : void{
|
||||
/**
|
||||
* @phpstan-param \Closure(NetworkBinaryStream) : void $writeExtraCrapInTheMiddle
|
||||
*/
|
||||
public function putItemStack(Item $item, \Closure $writeExtraCrapInTheMiddle) : void{
|
||||
if($item->getId() === 0){
|
||||
$this->putVarInt(0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->putVarInt($item->getId());
|
||||
$auxValue = (($item->getDamage() & 0x7fff) << 8) | $item->getCount();
|
||||
$this->putVarInt($auxValue);
|
||||
$coreData = $item->getDamage();
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $coreData);
|
||||
|
||||
$this->putVarInt($netId);
|
||||
$this->putLShort($item->getCount());
|
||||
$this->putUnsignedVarInt($netData);
|
||||
|
||||
$writeExtraCrapInTheMiddle($this);
|
||||
|
||||
$blockRuntimeId = 0;
|
||||
$isBlockItem = $item->getId() < 256;
|
||||
if($isBlockItem){
|
||||
$block = $item->getBlock();
|
||||
if($block->getId() !== BlockIds::AIR){
|
||||
$blockRuntimeId = RuntimeBlockMapping::toStaticRuntimeId($block->getId(), $block->getDamage());
|
||||
}
|
||||
}
|
||||
$this->putVarInt($blockRuntimeId);
|
||||
|
||||
$nbt = null;
|
||||
if($item->hasCompoundTag()){
|
||||
$nbt = clone $item->getNamedTag();
|
||||
}
|
||||
if($item instanceof Durable and $item->getDamage() > 0){
|
||||
if($item instanceof Durable and $coreData > 0){
|
||||
if($nbt !== null){
|
||||
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
@ -270,34 +334,46 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
}else{
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $item->getDamage());
|
||||
$nbt->setInt(self::DAMAGE_TAG, $coreData);
|
||||
}elseif($isBlockItem && $coreData !== 0){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_META_TAG, $coreData);
|
||||
}
|
||||
|
||||
if($nbt !== null){
|
||||
$this->putLShort(0xffff);
|
||||
$this->putByte(1); //TODO: NBT data version (?)
|
||||
$this->put((new NetworkLittleEndianNBTStream())->write($nbt));
|
||||
}else{
|
||||
$this->putLShort(0);
|
||||
}
|
||||
$this->putString(
|
||||
(static function() use ($nbt, $netId) : string{
|
||||
$extraData = new NetworkBinaryStream();
|
||||
|
||||
$this->putVarInt(0); //CanPlaceOn entry count (TODO)
|
||||
$this->putVarInt(0); //CanDestroy entry count (TODO)
|
||||
if($nbt !== null){
|
||||
$extraData->putLShort(0xffff);
|
||||
$extraData->putByte(1); //TODO: NBT data version (?)
|
||||
$extraData->put((new LittleEndianNBTStream())->write($nbt));
|
||||
}else{
|
||||
$extraData->putLShort(0);
|
||||
}
|
||||
|
||||
if($item->getId() === ItemIds::SHIELD){
|
||||
$this->putVarLong(0); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
$extraData->putLInt(0); //CanPlaceOn entry count (TODO)
|
||||
$extraData->putLInt(0); //CanDestroy entry count (TODO)
|
||||
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$extraData->putLLong(0); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
return $extraData->getBuffer();
|
||||
})());
|
||||
}
|
||||
|
||||
public function getRecipeIngredient() : Item{
|
||||
$id = $this->getVarInt();
|
||||
if($id === 0){
|
||||
$netId = $this->getVarInt();
|
||||
if($netId === 0){
|
||||
return ItemFactory::get(ItemIds::AIR, 0, 0);
|
||||
}
|
||||
$meta = $this->getVarInt();
|
||||
if($meta === 0x7fff){
|
||||
$meta = -1;
|
||||
}
|
||||
$netData = $this->getVarInt();
|
||||
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($netId, $netData);
|
||||
$count = $this->getVarInt();
|
||||
return ItemFactory::get($id, $meta, $count);
|
||||
}
|
||||
@ -306,8 +382,14 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
if($item->isNull()){
|
||||
$this->putVarInt(0);
|
||||
}else{
|
||||
$this->putVarInt($item->getId());
|
||||
$this->putVarInt($item->getDamage() & 0x7fff);
|
||||
if($item->hasAnyDamageValue()){
|
||||
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($item->getId(), 0);
|
||||
$netData = 0x7fff;
|
||||
}else{
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
|
||||
}
|
||||
$this->putVarInt($netId);
|
||||
$this->putVarInt($netData);
|
||||
$this->putVarInt($item->getCount());
|
||||
}
|
||||
}
|
||||
@ -589,14 +671,15 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
* Reads gamerules
|
||||
* TODO: implement this properly
|
||||
*
|
||||
* @return mixed[][], members are in the structure [name => [type, value]]
|
||||
* @phpstan-return array<string, array{0: int, 1: bool|int|float}>
|
||||
* @return mixed[][], members are in the structure [name => [type, value, isPlayerModifiable]]
|
||||
* @phpstan-return array<string, array{0: int, 1: bool|int|float, 2: bool}>
|
||||
*/
|
||||
public function getGameRules() : array{
|
||||
$count = $this->getUnsignedVarInt();
|
||||
$rules = [];
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$name = $this->getString();
|
||||
$isPlayerModifiable = $this->getBool();
|
||||
$type = $this->getUnsignedVarInt();
|
||||
$value = null;
|
||||
switch($type){
|
||||
@ -611,23 +694,24 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
break;
|
||||
}
|
||||
|
||||
$rules[$name] = [$type, $value];
|
||||
$rules[$name] = [$type, $value, $isPlayerModifiable];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a gamerule array, members should be in the structure [name => [type, value]]
|
||||
* Writes a gamerule array, members should be in the structure [name => [type, value, isPlayerModifiable]]
|
||||
* TODO: implement this properly
|
||||
*
|
||||
* @param mixed[][] $rules
|
||||
* @phpstan-param array<string, array{0: int, 1: bool|int|float}> $rules
|
||||
* @phpstan-param array<string, array{0: int, 1: bool|int|float, 2: bool}> $rules
|
||||
*/
|
||||
public function putGameRules(array $rules) : void{
|
||||
$this->putUnsignedVarInt(count($rules));
|
||||
foreach($rules as $name => $rule){
|
||||
$this->putString($name);
|
||||
$this->putBool($rule[2]);
|
||||
$this->putUnsignedVarInt($rule[0]);
|
||||
switch($rule[0]){
|
||||
case GameRuleType::BOOL:
|
||||
@ -750,6 +834,25 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putVarInt($structureEditorData->structureRedstoneSaveMove);
|
||||
}
|
||||
|
||||
public function getNbtRoot() : NamedTag{
|
||||
$offset = $this->getOffset();
|
||||
try{
|
||||
$result = (new NetworkLittleEndianNBTStream())->read($this->getBuffer(), false, $offset, 512);
|
||||
assert($result instanceof NamedTag, "doMultiple is false so we should definitely have a NamedTag here");
|
||||
return $result;
|
||||
}finally{
|
||||
$this->setOffset($offset);
|
||||
}
|
||||
}
|
||||
|
||||
public function getNbtCompoundRoot() : CompoundTag{
|
||||
$root = $this->getNbtRoot();
|
||||
if(!($root instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Expected TAG_Compound root");
|
||||
}
|
||||
return $root;
|
||||
}
|
||||
|
||||
public function readGenericTypeNetworkId() : int{
|
||||
return $this->getVarInt();
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorFallPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddBehaviorTreePacket;
|
||||
@ -32,7 +31,9 @@ use pocketmine\network\mcpe\protocol\AddEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPaintingPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddVolumeEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimateEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AnvilDamagePacket;
|
||||
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
|
||||
@ -45,8 +46,10 @@ use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\BookEditPacket;
|
||||
use pocketmine\network\mcpe\protocol\BossEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\CameraPacket;
|
||||
use pocketmine\network\mcpe\protocol\CameraShakePacket;
|
||||
use pocketmine\network\mcpe\protocol\ChangeDimensionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundDebugRendererPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientCacheBlobStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientCacheMissResponsePacket;
|
||||
@ -60,6 +63,7 @@ use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\CorrectPlayerMovePredictionPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
|
||||
@ -70,6 +74,7 @@ use pocketmine\network\mcpe\protocol\EducationSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmoteListPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmotePacket;
|
||||
use pocketmine\network\mcpe\protocol\EventPacket;
|
||||
use pocketmine\network\mcpe\protocol\FilterTextPacket;
|
||||
use pocketmine\network\mcpe\protocol\GameRulesChangedPacket;
|
||||
use pocketmine\network\mcpe\protocol\GuiDataPickItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\HurtArmorPacket;
|
||||
@ -77,6 +82,7 @@ use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemComponentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
|
||||
@ -96,6 +102,7 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\MotionPredictionHintsPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
@ -103,6 +110,7 @@ use pocketmine\network\mcpe\protocol\MultiplayerSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
|
||||
use pocketmine\network\mcpe\protocol\NpcDialoguePacket;
|
||||
use pocketmine\network\mcpe\protocol\NpcRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\OnScreenTextureAnimationPacket;
|
||||
use pocketmine\network\mcpe\protocol\PacketViolationWarningPacket;
|
||||
@ -111,6 +119,7 @@ use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerArmorDamagePacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerFogPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
@ -123,6 +132,7 @@ use pocketmine\network\mcpe\protocol\PurchaseReceiptPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveObjectivePacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveVolumeEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
@ -157,6 +167,7 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowProfilePacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowStoreOfferPacket;
|
||||
use pocketmine\network\mcpe\protocol\SimpleEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\SimulationTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnParticleEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\StartGamePacket;
|
||||
@ -165,13 +176,13 @@ use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureTemplateDataRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureTemplateDataResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\SyncActorPropertyPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\TickSyncPacket;
|
||||
use pocketmine\network\mcpe\protocol\TransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPropertiesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockSyncedPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateEquipPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdatePlayerGameTypePacket;
|
||||
@ -325,10 +336,6 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleActorFall(ActorFallPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleHurtArmor(HurtArmorPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
@ -705,10 +712,6 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleUpdateBlockProperties(UpdateBlockPropertiesPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleClientCacheBlobStatus(ClientCacheBlobStatusPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
@ -796,4 +799,56 @@ abstract class NetworkSession{
|
||||
public function handlePacketViolationWarning(PacketViolationWarningPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleMotionPredictionHints(MotionPredictionHintsPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleAnimateEntity(AnimateEntityPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleCameraShake(CameraShakePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handlePlayerFog(PlayerFogPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleCorrectPlayerMovePrediction(CorrectPlayerMovePredictionPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleItemComponent(ItemComponentPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleFilterText(FilterTextPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleClientboundDebugRenderer(ClientboundDebugRendererPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleSyncActorProperty(SyncActorPropertyPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleAddVolumeEntity(AddVolumeEntityPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleRemoveVolumeEntity(RemoveVolumeEntityPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleSimulationType(SimulationTypePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleNpcDialogue(NpcDialoguePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorFallPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
@ -174,10 +173,6 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
return $this->player->handlePlayerAction($packet);
|
||||
}
|
||||
|
||||
public function handleActorFall(ActorFallPacket $packet) : bool{
|
||||
return true; //Not used
|
||||
}
|
||||
|
||||
public function handleAnimate(AnimatePacket $packet) : bool{
|
||||
return $this->player->handleAnimate($packet);
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||
$info->getMaxPlayerCount(),
|
||||
$this->rakLib->getServerId(),
|
||||
$this->server->getName(),
|
||||
Server::getGamemodeName($this->server->getGamemode())
|
||||
Server::getGamemodeName(Player::getClientFriendlyGamemode($this->server->getGamemode()))
|
||||
]) . ";"
|
||||
);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user