Compare commits

...

106 Commits

Author SHA1 Message Date
15ffab659c Release 3.28.0 2022-02-08 20:09:52 +00:00
ca76a928c1 Protocol changes for 1.18.10 2022-02-08 20:06:46 +00:00
60ef2db892 3.27.1 is next 2022-01-22 00:36:48 +00:00
e21446e583 Release 3.27.0 2022-01-22 00:36:47 +00:00
e5a9123522 PocketMine.php: require ext-crypto 2022-01-22 00:30:05 +00:00
09201ac14b Fixed chunk sending
we can't cache the encapsulated stuff anymore because of encryption.
2022-01-22 00:24:31 +00:00
0697c7d316 Clean up according to newer php-cs-fixer 2022-01-21 23:45:49 +00:00
1eae133118 fixed PHPStan build 2022-01-21 23:39:37 +00:00
d28be4eaf2 Quick and dirty backport of encryption, preserving BC 2022-01-21 23:05:21 +00:00
b33a75a6d1 Updated transient dependency junk 2022-01-21 20:45:36 +00:00
bac6a2a1eb cs: enable fully_qualified_strict_types rule 2022-01-16 22:12:51 +00:00
b9b76eaed2 Server: add notice about obsoletion 2022-01-16 22:11:50 +00:00
bee2aba813 Updated PHPStan baseline 2022-01-07 21:46:35 +00:00
af81f80cf3 Updated PHPStan 2022-01-07 21:45:35 +00:00
ed2145b6a4 php-cs-fixer: enforce EOF newlines 2022-01-07 20:12:21 +00:00
e8893dd91f 3.26.6 is next 2022-01-04 20:47:31 +00:00
a4af1609ea Release 3.26.5 2022-01-04 20:47:31 +00:00
8c4b8a9042 CS 2022-01-04 20:44:10 +00:00
958a9dbf0f Merge pull request from GHSA-c6fg-99pr-25m9
* Skin: impose length limits on skinID, geometryName and geometryData fields

* Skin: remove extra newline
2022-01-04 20:40:55 +00:00
68f3399cfd Merge pull request from GHSA-p62j-hrxm-xcxf
This checks the following things:
- Validity of UTF-8 encoding of title, author, and page content
- Maximum soft and hard lengths of title, author, and page content (soft
  limits may be bypassed by uncancelling PlayerEditBookEvent; hard
  limits may not be bypassed)
- Maximum number of pages. Books with more than 50 pages may still be
  edited, but may not have new pages added.
2022-01-04 20:39:02 +00:00
d9c70cb176 start.cmd: prevent idiotic behaviour when paths contain characters such as brackets
god I hate this shit so much
2021-12-27 21:54:32 +00:00
9979a64ad2 3.26.5 is next 2021-12-16 01:23:22 +00:00
75a72786f9 Release 3.26.4 2021-12-16 01:23:21 +00:00
3d205c6e5f Updated transient dependency junk 2021-12-16 01:20:05 +00:00
2955a92837 Updated pocketmine/nbt to 0.2.19 2021-12-16 01:19:30 +00:00
7fb1669c6d php-cs-fixer: added binary_operator_spaces and unary_operator_spaces rules 2021-12-14 23:14:39 +00:00
a09817864b php-cs-fixer: add return_type_declaration space_before 2021-12-14 22:50:43 +00:00
f5bbd30dbb Fixed skins appearing black when using RTX resource packs, closes #4537 2021-12-13 12:35:55 +00:00
69d5bfa0d4 3.26.4 is next 2021-12-10 17:55:11 +00:00
549fb923bf Release 3.26.3 2021-12-10 17:55:07 +00:00
6d5c463bdd PlayerExperienceChangeEvent: added range checks to setNewProgress()
WE FINALLY FUCKING FOUND IT

This took several years to identify because PHP's exception stack traces don't show the actual values of parameters, but rather the values of the variables they were assigned to.

This means that if the parameter variable is mutated, the exception trace will show the value of the variable inside the function, not the value that was actually passed.
2021-12-10 17:29:57 +00:00
911ad344c9 Human: do not mutate parameter variables in setXpAndProgress()
this caused a mystery that took 3 entire years to debug.
2021-12-10 17:27:28 +00:00
06eaf9f273 3.26.3 is next 2021-12-09 00:27:03 +00:00
1e56ed2ea3 Release 3.26.2 2021-12-09 00:26:59 +00:00
40895a86e5 draft-release: stick a banner on the release notes to declare obsolescence 2021-12-08 23:55:43 +00:00
b081394125 Do not restrict the allowed update channels client-side
we really should have an endpoint on the server that deals with this.
2021-12-08 21:57:16 +00:00
f48cf68cac updater: log a message when an update was found, but it's an older version 2021-12-08 21:55:44 +00:00
264cff70ec Release new PM3 builds onto pm3 channel 2021-12-08 21:55:12 +00:00
3aabfa4ab0 bootstrap: display value of PHPRC when PHP binary is borked
PHPRC overrides the search path for php.ini, which might break the php.ini locating.
2021-12-08 20:48:44 +00:00
cb0af44ccb start.sh: improve errors when PHP isn't found 2021-11-30 23:51:35 +00:00
d535f02096 Make nicer errors for PHP binary not being found 2021-11-30 23:45:25 +00:00
7665f4f443 start.sh: remove 7 2021-11-30 23:43:17 +00:00
20d6b69813 3.26.2 is next 2021-11-30 22:27:42 +00:00
6b7d0307af Release 3.26.1 2021-11-30 22:27:42 +00:00
baeac2eb07 Fixed tiles not being sent with chunks 2021-11-30 22:19:28 +00:00
d5f81fe261 3.26.1 is next 2021-11-30 18:53:36 +00:00
0aeac3af7d Release 3.26.0 2021-11-30 18:53:36 +00:00
9931c1d50a Protocol changes for 1.18.0 2021-11-30 18:46:29 +00:00
8079ae341a Updated build/php submodule to pmmp/php-build-scripts@bd329dba08 2021-11-30 01:19:14 +00:00
ba295dc7dc Always use LF in .neon files 2021-11-30 01:16:28 +00:00
c19174a174 3.25.7 is next 2021-11-26 23:37:47 +00:00
f95142f6b6 Release 3.25.6 2021-11-26 23:37:46 +00:00
7ace24caab Fixed borked build number
this was a problem before the recent clean-up; the only reason it just decided to show now is because 2000+25 is valid PHP code, so PHP saved our asses.
2021-11-26 23:36:19 +00:00
32f619ac49 3.25.6 is next 2021-11-26 23:20:48 +00:00
1bb6ac4fb6 Release 3.25.5 2021-11-26 23:20:40 +00:00
52a891ba73 shut 2021-11-26 22:32:25 +00:00
71b813d4f9 Define pocketmine\BUILD_NUMBER from phar metadata
this way we don't have to patch the code (no idea why we were doing that anyway).
2021-11-26 22:27:58 +00:00
f2540a72ad Backport improved make-release.php from PM4 2021-11-26 22:10:46 +00:00
7e0f6c02a1 Updated build/php submodule to pmmp/php-build-scripts@a59722c676 2021-11-26 21:59:39 +00:00
c023c02b6c MemoryManager: Removed obsolete workaround for $GLOBALS not being defined on threads
this was long since fixed, and everyone has since been forced to upgrade to pthreads 4.0.0, which definitely has the fix.
2021-11-24 23:57:55 +00:00
adff561483 phpstan: go nuclear on OPcache
when using dynamic reflection (which is the default), any time static reflection comes into play, bad shit starts to happen because of FileReadTrapStreamWrapper.
I attempted to fix these issues (phpstan/phpstan-src#801) and failed miserably.
So, to save the hassle, it's time to just remove OPcache from the picture (which, unfortunately, also means that PHPStan will not benefit from JIT).
2021-11-24 23:40:54 +00:00
472ffb28ff ScriptPluginLoader: use parseDocComment() instead of reinventing the wheel 2021-11-24 17:22:49 +00:00
726c5652f7 ScriptPluginLoader: fixed reading @tags from non-docblock lines preceding the first docblock 2021-11-24 17:07:34 +00:00
fc7d297f60 Added missing fields of StructureSettings 2021-11-21 20:51:35 +00:00
7b4ef293bd NetworkBinaryStream: fixed incorrect field types for StructureSettings 2021-11-21 20:49:00 +00:00
3683884b9c Updated build/php submodule to pmmp/php-build-scripts@7a2ab5b922 2021-11-20 18:27:43 +00:00
db135788b9 Updated transient dependencies 2021-11-20 18:19:27 +00:00
7210db25b0 Bump phpstan/phpstan from 1.1.2 to 1.2.0 (#4583)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.1.2 to 1.2.0.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/master/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.1.2...1.2.0)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-19 14:42:01 +00:00
ada469bc45 README: do not show beta releases on badge
[ci skip]
2021-11-12 01:35:39 +00:00
dc8243f88b Bump phpstan/phpstan from 1.1.1 to 1.1.2 (#4564)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/1.1.1...1.1.2)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-12 00:24:23 +00:00
1beec348f9 3.25.5 is next 2021-11-08 22:33:09 +00:00
7306a2d939 Release 3.25.4 2021-11-08 22:33:08 +00:00
4bf338f783 Player: fixed removeWindow() causing all other inventories to be unopenable 2021-11-08 22:29:14 +00:00
255ff63fda 3.25.4 is next 2021-11-08 20:35:15 +00:00
d72f6a3ac6 Release 3.25.3 2021-11-08 20:35:14 +00:00
3b34268ed6 Human: try to trap this stupid float cast bug in the wild 2021-11-08 19:48:39 +00:00
76dad46e13 Bump phpstan/phpstan from 1.0.2 to 1.1.1 (#4560)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.0.2 to 1.1.1.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/1.0.2...1.1.1)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 13:36:06 +00:00
eb3530b6e6 Use pmmp/setup-php-action to compile PHP 2021-11-07 23:13:56 +00:00
b392651354 pocketmine.yml: always refer to worlds as worlds in config comments, not levels 2021-11-06 02:22:14 +00:00
e0b07ff308 Human: do not add more XP if totalXp limit was already reached
this matches the vanilla behaviour. For some reason it doesn't consider levels (so you can have a level higher or lower than this without actually having that amount of XP), but this matches Java behaviour as of 1.10.

fixes #4543
2021-11-03 20:45:55 +00:00
729f831b8f PHPStan 1.0.2 2021-11-03 20:26:32 +00:00
29e2d92098 Bump phpstan/phpstan from 1.0.0 to 1.0.1 (#4541)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/1.0.0...1.0.1)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-03 11:27:42 +00:00
f75a05d7fa 3.25.3 is next 2021-11-02 17:21:49 +00:00
3dae873731 Release 3.25.2 2021-11-02 17:21:44 +00:00
5257755dc5 shut 2021-11-01 21:15:58 +00:00
3214da8642 pthreads 4.0.0 2021-11-01 21:01:59 +00:00
794142fe49 Merge l7/l8/l9 baselines into actual-problems
it doesn't serve any practical purpose to keep these separated, particularly since it's getting so difficult to figure out which errors are coming from which levels (since we always use 9, it doesn't really make any difference).
2021-11-01 17:27:31 +00:00
ff27c5f7db PHPStan 1.0.0 2021-11-01 17:24:20 +00:00
4d4362801f AvailableCommandsPacket: remove dead code 2021-11-01 17:01:26 +00:00
0babe0a1ab LevelDB: remove unused private method 2021-11-01 16:55:05 +00:00
d696ebcda3 Level: do not use static:: to access levelIdCounter
the field is private.
2021-11-01 16:52:27 +00:00
9f5c16bc46 Projectile: use closure instead of do/while for reading id/data of block
not ideal, but whatever I guess... this at least provides scope isolation
2021-11-01 15:56:28 +00:00
8865bb73ba BanEntry: remove useless do/while 2021-11-01 15:52:55 +00:00
2dee1dbc28 Remove ridiculous code in ResourcePackManager 2021-11-01 15:41:21 +00:00
0f0b6f0efa Utils: eliminate usages of backtick operator 2021-11-01 15:25:56 +00:00
d5f13d8be2 Timezone: make PHPStan 1.0 happy 2021-11-01 15:24:16 +00:00
27ae959e89 Terminal: backport shell_exec() code from PM4 to make PHPStan 1.0 happy 2021-11-01 15:23:36 +00:00
f8f39687e2 Achievement: declare proper type for $list static property 2021-11-01 15:22:33 +00:00
94737934de PlayerDeathEvent: fixed LSP violation reported by PHPStan 1.0 2021-11-01 14:17:54 +00:00
debb469de1 Updated PHPUnit dependency junk 2021-11-01 13:54:04 +00:00
73dc0598e4 CrashDump: remove derp space 2021-10-30 23:22:37 +01:00
141fbde660 Player: fixed getting re-banned on rejoin after unban from hardcore death
closes #2175
2021-10-30 16:58:03 +01:00
69952ae2af Human: limit lifetime total XP range to INT32_MAX
closes #4484
2021-10-30 16:05:10 +01:00
71f2a34616 Entity: spawnTo() must silently swallow errors
Player->showPlayer() assumes that spawnTo() will take care of all the checks necessary to ensure we don't actually spawn a player to someone it shouldn't be able to see. In PM3, there's nothing we can do about that.
This could be a problem if anything decides to override spawnTo() to do additional stuff and assumes that the function will always succeed; however, there's not much reason to do that (plugins sending packets should override sendSpawnPacket() instead).
2021-10-30 15:38:27 +01:00
d17cd65803 3.25.2 is next 2021-10-29 22:23:28 +01:00
a8d5e8c5f6 Release 3.25.1 2021-10-29 22:23:22 +01:00
107 changed files with 5766 additions and 3858 deletions

1
.gitattributes vendored
View File

@ -4,6 +4,7 @@
*.sh text eol=lf
*.txt text eol=lf
*.properties text eol=lf
*.neon text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

View File

@ -35,11 +35,12 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
- name: Patch VersionInfo
- name: Calculate build number
id: build-number
run: |
BUILD_NUMBER=2000+$GITHUB_RUN_NUMBER #to stay above jenkins
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
echo "Build number: $BUILD_NUMBER"
sed -i "s/const BUILD_NUMBER = 0/const BUILD_NUMBER = ${BUILD_NUMBER}/" src/pocketmine/VersionInfo.php
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
- name: Minify BedrockData JSON files
run: php src/pocketmine/resources/vanilla/.minify_json.php
@ -68,7 +69,7 @@ jobs:
done
- name: Build PocketMine-MP.phar
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }}
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }} --build ${{ steps.build-number.outputs.BUILD_NUMBER }}
- name: Get PocketMine-MP release version
id: get-pm-version
@ -79,7 +80,7 @@ jobs:
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
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} > build_info.json
- name: Upload release artifacts
uses: actions/upload-artifact@v2
@ -104,6 +105,11 @@ jobs:
Please see the [changelogs](/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
## WARNING
The 3.x line of releases is now OBSOLETE. It will be discontinued after **March 1st, 2022**.
Please prepare to upgrade to 4.0 or newer before that date.
- name: Upload preprocessor diffs
uses: actions/upload-artifact@v2
if: always()

View File

@ -16,17 +16,11 @@ jobs:
php: [8.0.11]
steps:
- uses: actions/checkout@v2 #needed for build.sh
- name: Check for PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Build and prepare PHP cache
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Compile PHP
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: ./tests/gh-actions/build.sh "${{ matrix.php }}"
php-version: ${{ matrix.php }}
install-path: "./bin"
phpstan:
name: PHPStan analysis
@ -42,23 +36,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -92,23 +74,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -144,23 +114,11 @@ jobs:
with:
submodules: true
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -196,23 +154,11 @@ jobs:
with:
submodules: true
- name: Restore PHP build cache
id: php-build-cache
uses: actions/cache@v2
- name: Setup PHP
uses: pmmp/setup-php-action@e232f72a4330a07aae8418e8aa56b64efcdda636
with:
path: "./bin"
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
- name: Kill build on PHP build cache miss (should never happen)
if: steps.php-build-cache.outputs.cache-hit != 'true'
run: exit 1
- name: Install cached PHP's dependencies
if: steps.php-build-cache.outputs.cache-hit == 'true'
run: ./tests/gh-actions/install-dependencies.sh
- name: Prefix PHP to PATH
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
php-version: ${{ matrix.php }}
install-path: "./bin"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php

View File

@ -19,6 +19,9 @@ return (new PhpCsFixer\Config)
'array_syntax' => [
'syntax' => 'short'
],
'binary_operator_spaces' => [
'default' => 'single_space'
],
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => [
@ -34,6 +37,7 @@ return (new PhpCsFixer\Config)
],
'declare_strict_types' => true,
'elseif' => true,
'fully_qualified_strict_types' => true,
'global_namespace_import' => [
'import_constants' => true,
'import_functions' => true,
@ -69,8 +73,13 @@ return (new PhpCsFixer\Config)
],
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'return_type_declaration' => [
'space_before' => 'one'
],
'single_blank_line_at_eof' => true,
'single_import_per_statement' => true,
'strict_param' => true,
'unary_operator_spaces' => true,
])
->setFinder($finder)
->setIndent("\t")

View File

@ -5,7 +5,7 @@
<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>
<img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver">
<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>

View File

@ -23,15 +23,15 @@ declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
if(count($argv) !== 4){
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)>");
if(count($argv) !== 5){
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number>");
exit(1);
}
echo json_encode([
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
"base_version" => \pocketmine\BASE_VERSION,
"build" => \pocketmine\BUILD_NUMBER,
"build" => (int) $argv[4],
"is_dev" => \pocketmine\IS_DEVELOPMENT_BUILD,
"channel" => \pocketmine\BUILD_CHANNEL,
"git_commit" => $argv[1],

View File

@ -24,64 +24,95 @@ declare(strict_types=1);
namespace pocketmine\build\make_release;
use pocketmine\utils\VersionString;
use function count;
use function array_keys;
use function array_map;
use function dirname;
use function fgets;
use function file_get_contents;
use function file_put_contents;
use function fwrite;
use function getopt;
use function is_string;
use function max;
use function preg_replace;
use function sleep;
use function sprintf;
use function str_pad;
use function strlen;
use function system;
use const pocketmine\BASE_VERSION;
use const pocketmine\BUILD_CHANNEL;
use const STDERR;
use const STDIN;
use const STDOUT;
use const STR_PAD_LEFT;
require_once dirname(__DIR__) . '/vendor/autoload.php';
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
$versionInfo = file_get_contents($versionInfoPath);
$versionInfo = preg_replace(
$pattern = '/^const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
'const BASE_VERSION = "' . $newVersion . '";',
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
'$1const BASE_VERSION = "' . $newVersion . '";',
$versionInfo
);
$versionInfo = preg_replace(
'/^const IS_DEVELOPMENT_BUILD = (?:true|false);$/m',
'const IS_DEVELOPMENT_BUILD = ' . ($isDev ? 'true' : 'false') . ';',
'/^([\t ]*public )?const IS_DEVELOPMENT_BUILD = (?:true|false);$/m',
'$1const IS_DEVELOPMENT_BUILD = ' . ($isDev ? 'true' : 'false') . ';',
$versionInfo
);
$versionInfo = preg_replace(
'/^const BUILD_CHANNEL = ".*";$/m',
'const BUILD_CHANNEL = "' . $channel . '";',
'/^([\t ]*public )?const BUILD_CHANNEL = ".*";$/m',
'$1const BUILD_CHANNEL = "' . $channel . '";',
$versionInfo
);
file_put_contents($versionInfoPath, $versionInfo);
}
/**
* @param string[] $argv
* @phpstan-param list<string> $argv
*/
function main(array $argv) : void{
if(count($argv) < 2){
fwrite(STDERR, "Arguments: <channel> [release version]\n");
exit(1);
const ACCEPTED_OPTS = [
"current" => "Version to insert and tag",
"next" => "Version to put in the file after tagging",
"channel" => "Release channel to post this build into"
];
function main() : void{
$filteredOpts = [];
foreach(getopt("", ["current:", "next:", "channel:", "help"]) as $optName => $optValue){
if($optName === "help"){
fwrite(STDOUT, "Options:\n");
$maxLength = max(array_map(fn(string $str) => strlen($str), array_keys(ACCEPTED_OPTS)));
foreach(ACCEPTED_OPTS as $acceptedName => $description){
fwrite(STDOUT, str_pad("--$acceptedName", $maxLength + 4, " ", STR_PAD_LEFT) . ": $description\n");
}
exit(0);
}
if(!is_string($optValue)){
fwrite(STDERR, "--$optName expects exactly 1 value\n");
exit(1);
}
$filteredOpts[$optName] = $optValue;
}
if(isset($argv[2])){
$currentVer = new VersionString($argv[2]);
if(isset($filteredOpts["current"])){
$currentVer = new VersionString($filteredOpts["current"]);
}else{
$currentVer = new VersionString(BASE_VERSION);
}
$nextVer = new VersionString(sprintf(
"%u.%u.%u",
$currentVer->getMajor(),
$currentVer->getMinor(),
$currentVer->getPatch() + 1
));
if(isset($filteredOpts["next"])){
$nextVer = new VersionString($filteredOpts["next"]);
}else{
$nextVer = new VersionString(sprintf(
"%u.%u.%u",
$currentVer->getMajor(),
$currentVer->getMinor(),
$currentVer->getPatch() + 1
));
}
$channel = $filteredOpts["channel"] ?? BUILD_CHANNEL;
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
echo "$currentVer will be published on release channel \"$channel\".\n";
echo "please add appropriate notes to the changelog and press enter...";
fgets(STDIN);
system('git add "' . dirname(__DIR__) . '/changelogs"');
@ -91,10 +122,10 @@ function main(array $argv) : void{
exit(1);
}
$versionInfoPath = dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $argv[1]);
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
system('git tag ' . $currentVer->getBaseVersion());
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, "");
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
system('git add "' . $versionInfoPath . '"');
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
echo "pushing changes in 5 seconds\n";
@ -102,4 +133,4 @@ function main(array $argv) : void{
system('git push origin HEAD ' . $currentVer->getBaseVersion());
}
main($argv);
main();

View File

@ -134,13 +134,18 @@ function main() : void{
exit(1);
}
$opts = getopt("", ["out:", "git:"]);
$opts = getopt("", ["out:", "git:", "build:"]);
if(isset($opts["git"])){
$gitHash = $opts["git"];
}else{
$gitHash = Git::getRepositoryStatePretty(dirname(__DIR__));
echo "Git hash detected as $gitHash" . PHP_EOL;
}
if(isset($opts["build"])){
$build = (int) $opts["build"];
}else{
$build = 0;
}
foreach(buildPhar(
$opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar",
dirname(__DIR__) . DIRECTORY_SEPARATOR,
@ -149,7 +154,8 @@ function main() : void{
'vendor'
],
[
'git' => $gitHash
'git' => $gitHash,
'build' => $build
],
<<<'STUB'
<?php

View File

@ -9,3 +9,30 @@ Plugin developers should **only** update their required API to this version if y
# 3.25.0
- Added support for Minecraft: Bedrock Edition 1.17.40.
- Removed compatibility with earlier versions.
# 3.25.1
- Fixed autosave bug that caused unmodified chunks to be saved at least once (during the first autosave after they were loaded).
- `Entity->spawnTo()` now has an additional sanity check for matching worlds (might expose a few new errors in plugins).
- Fixed a missing field in `CraftRecipeAuto` item stack request type.
# 3.25.2
- Now analysed using level 9 on PHPStan 1.0.0.
- `ext-pthreads` v4.0.0 or newer is now required.
- Fixed crash in `Player->showPlayer()` when the target is not in the same world.
- `Human->setLifetimeTotalXp()` now limits the maximum value to 2^31.
- Fixed players, who died in hardcore mode and were unbanned, getting re-banned on next server join.
# 3.25.3
- Fixed crash when players try to pickup XP while already having max XP.
- Added a sanity check to `Human->setCurrentTotalXp()` to try and catch an elusive bug that's been appearing in the wild - please get in touch if you know how to reproduce it!
# 3.25.4
- Fixed a long-standing issue with `Player->removeWindow()` breaking inventory UIs on the client.
# 3.25.5
- Protocol: Fixed incorrect encoding in `StructureSettings`
- Fixed reading tags from non-docblock comments in script plugins.
- Build number is now defined in phar metadata instead of being patched into the source code directly.
# 3.25.6
- Fixed borked build number in release build of 3.25.5.

32
changelogs/3.26.md Normal file
View File

@ -0,0 +1,32 @@
**For Minecraft: Bedrock Edition 1.18.0**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 3.26.0
- Added support for Minecraft: Bedrock Edition 1.18.0.
- Removed compatibility with earlier versions.
# 3.26.1
- Fixed a bug in chunk sending that caused double chests to not be paired, signs to be blank, and various other issues.
# 3.26.2
- Improved error messages shown by `start.cmd`, `start.sh` and `start.ps1` when the PHP binary was not found.
- The value of PHPRC is now shown when erroring out due to unsatisfied PHP requirements.
- Removed restriction on the range of valid channels for `auto-updater.channel` in `pocketmine.yml`.
# 3.26.3
- `PlayerExperienceChangeEvent->setNewProgress()` now performs range checks. This fixes the root of a very old and confusing crash bug which took several years to identify the cause of.
- Note that the defective plugin(s) which caused this problem will still cause a server crash, but the plugin responsible will now get blamed correctly.
# 3.26.4
- Fixed skins appearing black when using RTX resource packs.
- Fixed chunks containing furnaces in old worlds (pre-2017) being discarded as corrupted.
- This was caused by a strict corruption check detecting bad data created by a bug in PocketMine-MP that was fixed in 2017.
# 3.26.5
- Fixed several denial-of-service attack vectors related to writable book text length and encoding.
- Fixed several denial-of-service attack vectors related to skin data field lengths.

15
changelogs/3.27.md Normal file
View File

@ -0,0 +1,15 @@
**For Minecraft: Bedrock Edition 1.18.0**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 3.27.0
- Introduced support for protocol encryption.
- Encryption is enabled by default.
- Fixes login replay attacks.
- This may cause some performance degradation.
- Encryption can be disabled by setting `network.enable-encryption` to `false` in `pocketmine.yml`. DO NOT do this unless you understand the risks involved.
- An obsoletion notice has been added to the console during server startup.

10
changelogs/3.28.md Normal file
View File

@ -0,0 +1,10 @@
**For Minecraft: Bedrock Edition 1.18.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.28.0
- Added support for Minecraft: Bedrock Edition 1.18.10.

View File

@ -7,6 +7,8 @@
"require": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-crypto": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
@ -16,7 +18,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pthreads": "~3.2.0",
"ext-pthreads": "^4.0",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
@ -26,21 +28,22 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"pocketmine/binaryutils": "^0.1.9",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.1.0",
"pocketmine/log": "^0.2.0",
"pocketmine/log-pthreads": "^0.1.0",
"pocketmine/math": "^0.2.0",
"pocketmine/nbt": "^0.2.18",
"pocketmine/nbt": "^0.2.19",
"pocketmine/raklib": "^0.12.7",
"pocketmine/snooze": "^0.1.0",
"pocketmine/spl": "^0.4.0"
},
"require-dev": {
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-phpunit": "^0.12.6",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpstan/phpstan": "1.3.3",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.2"
},
"autoload": {

335
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cfba71d2ad0dd961ed00520b5d52e4d7",
"content-hash": "4ee772232d0936f6f9eda5d54ec2462d",
"packages": [
{
"name": "adhocore/json-comment",
@ -61,6 +61,81 @@
],
"time": "2021-04-09T03:06:06+00:00"
},
{
"name": "fgrosse/phpasn1",
"version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/fgrosse/PHPASN1.git",
"reference": "eef488991d53e58e60c9554b09b1201ca5ba9296"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296",
"reference": "eef488991d53e58e60c9554b09b1201ca5ba9296",
"shasum": ""
},
"require": {
"php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "~2.0",
"phpunit/phpunit": "^6.3 || ^7.0 || ^8.0"
},
"suggest": {
"ext-bcmath": "BCmath is the fallback extension for big integer calculations",
"ext-curl": "For loading OID information from the web if they have not bee defined statically",
"ext-gmp": "GMP is the preferred extension for big integer calculations",
"phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"FG\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Friedrich Große",
"email": "friedrich.grosse@gmail.com",
"homepage": "https://github.com/FGrosse",
"role": "Author"
},
{
"name": "All contributors",
"homepage": "https://github.com/FGrosse/PHPASN1/contributors"
}
],
"description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
"homepage": "https://github.com/FGrosse/PHPASN1",
"keywords": [
"DER",
"asn.1",
"asn1",
"ber",
"binary",
"decoding",
"encoding",
"x.509",
"x.690",
"x509",
"x690"
],
"support": {
"issues": "https://github.com/fgrosse/PHPASN1/issues",
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0"
},
"time": "2021-12-11T12:41:06+00:00"
},
{
"name": "pocketmine/binaryutils",
"version": "0.1.13",
@ -153,20 +228,20 @@
},
{
"name": "pocketmine/classloader",
"version": "0.1.2",
"version": "0.1.3",
"source": {
"type": "git",
"url": "https://github.com/pmmp/ClassLoader.git",
"reference": "9757928424652393b178a3760073113aa7c9911b"
"reference": "3c484a27787f7732ce842ed694928a29ba340961"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/9757928424652393b178a3760073113aa7c9911b",
"reference": "9757928424652393b178a3760073113aa7c9911b",
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/3c484a27787f7732ce842ed694928a29ba340961",
"reference": "3c484a27787f7732ce842ed694928a29ba340961",
"shasum": ""
},
"require": {
"ext-pthreads": "~3.2.0",
"ext-pthreads": "~3.2.0 || ^4.0",
"ext-reflection": "*",
"php": "^7.2 || ^8.0"
},
@ -175,7 +250,7 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.66",
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-strict-rules": "^0.12.4"
},
"type": "library",
@ -191,9 +266,9 @@
"description": "Ad-hoc autoloading components used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/ClassLoader/issues",
"source": "https://github.com/pmmp/ClassLoader/tree/0.1.2"
"source": "https://github.com/pmmp/ClassLoader/tree/0.1.3"
},
"time": "2021-01-15T00:40:47+00:00"
"time": "2021-11-01T20:13:55+00:00"
},
{
"name": "pocketmine/log",
@ -238,20 +313,20 @@
},
{
"name": "pocketmine/log-pthreads",
"version": "0.1.3",
"version": "0.1.4",
"source": {
"type": "git",
"url": "https://github.com/pmmp/LogPthreads.git",
"reference": "e477ecf6ec214fdd4415ea1da3fdd9d73bf699ea"
"reference": "01620c3628cdaa6b4a21122cff4c5d2f70b5c1d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/e477ecf6ec214fdd4415ea1da3fdd9d73bf699ea",
"reference": "e477ecf6ec214fdd4415ea1da3fdd9d73bf699ea",
"url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/01620c3628cdaa6b4a21122cff4c5d2f70b5c1d3",
"reference": "01620c3628cdaa6b4a21122cff4c5d2f70b5c1d3",
"shasum": ""
},
"require": {
"ext-pthreads": "~3.2.0",
"ext-pthreads": "~3.2.0 || ^4.0",
"php": "^7.2 || ^8.0",
"pocketmine/log": "^0.2.0"
},
@ -260,7 +335,7 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.66",
"phpstan/phpstan": "0.12.80",
"phpstan/phpstan-strict-rules": "^0.12.4"
},
"type": "library",
@ -276,9 +351,9 @@
"description": "Logging components specialized for pthreads used by PocketMine-MP and related projects",
"support": {
"issues": "https://github.com/pmmp/LogPthreads/issues",
"source": "https://github.com/pmmp/LogPthreads/tree/0.1.3"
"source": "https://github.com/pmmp/LogPthreads/tree/0.1.4"
},
"time": "2021-01-15T00:35:49+00:00"
"time": "2021-11-01T20:36:53+00:00"
},
{
"name": "pocketmine/math",
@ -322,16 +397,16 @@
},
{
"name": "pocketmine/nbt",
"version": "0.2.18",
"version": "0.2.19",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82"
"reference": "8567c65e8e099c2f7436cfea3d886b3dcd332283"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82",
"reference": "9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/8567c65e8e099c2f7436cfea3d886b3dcd332283",
"reference": "8567c65e8e099c2f7436cfea3d886b3dcd332283",
"shasum": ""
},
"require": {
@ -343,7 +418,7 @@
"require-dev": {
"irstea/phpunit-shim": "^7.5 || ^8.0",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.80",
"phpstan/phpstan": "0.12.85",
"phpstan/phpstan-strict-rules": "^0.12.4"
},
"type": "library",
@ -359,26 +434,26 @@
"description": "PHP library for working with Named Binary Tags",
"support": {
"issues": "https://github.com/pmmp/NBT/issues",
"source": "https://github.com/pmmp/NBT/tree/0.2.18"
"source": "https://github.com/pmmp/NBT/tree/0.2.19"
},
"time": "2021-03-11T00:09:04+00:00"
"time": "2021-12-16T01:15:41+00:00"
},
{
"name": "pocketmine/raklib",
"version": "0.12.11",
"version": "0.12.12",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "9cce458b8bfde3e4dfdbf70c659fc7b7fe26b5c4"
"reference": "5abe22043352e94099e4edfcef5fb3644578ddc1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/9cce458b8bfde3e4dfdbf70c659fc7b7fe26b5c4",
"reference": "9cce458b8bfde3e4dfdbf70c659fc7b7fe26b5c4",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/5abe22043352e94099e4edfcef5fb3644578ddc1",
"reference": "5abe22043352e94099e4edfcef5fb3644578ddc1",
"shasum": ""
},
"require": {
"ext-pthreads": "~3.2.0",
"ext-pthreads": "~3.2.0 || ^4.0",
"ext-sockets": "*",
"php": "^7.2 || ^8.0",
"php-64bit": "*",
@ -389,7 +464,7 @@
"pocketmine/snooze": "^0.1.0"
},
"require-dev": {
"phpstan/phpstan": "0.12.76",
"phpstan/phpstan": "0.12.87",
"phpstan/phpstan-strict-rules": "^0.12.2"
},
"type": "library",
@ -405,26 +480,26 @@
"description": "A RakNet server implementation written in PHP",
"support": {
"issues": "https://github.com/pmmp/RakLib/issues",
"source": "https://github.com/pmmp/RakLib/tree/0.12.11"
"source": "https://github.com/pmmp/RakLib/tree/0.12.12"
},
"time": "2021-02-15T11:21:05+00:00"
"time": "2021-11-01T20:52:51+00:00"
},
{
"name": "pocketmine/snooze",
"version": "0.1.5",
"version": "0.1.6",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Snooze.git",
"reference": "70b5e7937a06878dd321a3182ceb76d56298f2cd"
"reference": "92abf1e988c71635d466abb777f61f89e5a9c990"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/70b5e7937a06878dd321a3182ceb76d56298f2cd",
"reference": "70b5e7937a06878dd321a3182ceb76d56298f2cd",
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/92abf1e988c71635d466abb777f61f89e5a9c990",
"reference": "92abf1e988c71635d466abb777f61f89e5a9c990",
"shasum": ""
},
"require": {
"ext-pthreads": ">=3.1.7dev",
"ext-pthreads": "~3.2.0 || ^4.0",
"php-64bit": "^7.2 || ^8.0"
},
"require-dev": {
@ -445,9 +520,9 @@
"description": "Thread notification management library for code using the pthreads extension",
"support": {
"issues": "https://github.com/pmmp/Snooze/issues",
"source": "https://github.com/pmmp/Snooze/tree/0.1.5"
"source": "https://github.com/pmmp/Snooze/tree/0.1.6"
},
"time": "2021-02-22T16:16:12+00:00"
"time": "2021-11-01T20:48:46+00:00"
},
{
"name": "pocketmine/spl",
@ -484,6 +559,7 @@
"issues": "https://github.com/pmmp/SPL/issues",
"source": "https://github.com/pmmp/SPL/tree/0.4.2"
},
"abandoned": true,
"time": "2021-01-15T15:15:23+00:00"
}
],
@ -574,9 +650,6 @@
"require": {
"php": "^7.1 || ^8.0"
},
"replace": {
"myclabs/deep-copy": "self.version"
},
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
@ -617,16 +690,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.13.0",
"version": "v4.13.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "50953a2691a922aa1769461637869a0a2faa3f53"
"reference": "210577fe3cf7badcc5814d99455df46564f3c077"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53",
"reference": "50953a2691a922aa1769461637869a0a2faa3f53",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
"reference": "210577fe3cf7badcc5814d99455df46564f3c077",
"shasum": ""
},
"require": {
@ -667,9 +740,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
},
"time": "2021-09-20T12:20:58+00:00"
"time": "2021-11-30T19:35:32+00:00"
},
{
"name": "phar-io/manifest",
@ -837,16 +910,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "5.2.2",
"version": "5.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
"reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
"shasum": ""
},
"require": {
@ -857,7 +930,8 @@
"webmozart/assert": "^1.9.1"
},
"require-dev": {
"mockery/mockery": "~1.3.2"
"mockery/mockery": "~1.3.2",
"psalm/phar": "^4.8"
},
"type": "library",
"extra": {
@ -887,22 +961,22 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
},
"time": "2020-09-03T19:13:55+00:00"
"time": "2021-10-19T17:43:47+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.5.1",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae"
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae",
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706",
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706",
"shasum": ""
},
"require": {
@ -937,22 +1011,22 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0"
},
"time": "2021-10-02T14:08:47+00:00"
"time": "2022-01-04T19:58:01+00:00"
},
{
"name": "phpspec/prophecy",
"version": "1.14.0",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e"
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
"shasum": ""
},
"require": {
@ -1004,22 +1078,22 @@
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/1.14.0"
"source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
},
"time": "2021-09-10T09:02:12+00:00"
"time": "2021-12-08T12:19:24+00:00"
},
{
"name": "phpstan/phpstan",
"version": "0.12.99",
"version": "1.3.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7"
"reference": "151a51f6149855785fbd883e79768c0abc96b75f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/151a51f6149855785fbd883e79768c0abc96b75f",
"reference": "151a51f6149855785fbd883e79768c0abc96b75f",
"shasum": ""
},
"require": {
@ -1035,7 +1109,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
"dev-master": "1.3-dev"
}
},
"autoload": {
@ -1050,7 +1124,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/0.12.99"
"source": "https://github.com/phpstan/phpstan/tree/1.3.3"
},
"funding": [
{
@ -1070,38 +1144,39 @@
"type": "tidelift"
}
],
"time": "2021-09-12T20:09:55+00:00"
"time": "2022-01-07T09:49:03+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "0.12.22",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc"
"reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc",
"reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
"reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"phpstan/phpstan": "^0.12.92"
"phpstan/phpstan": "^1.0"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-strict-rules": "^0.12.6",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
"dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@ -1122,37 +1197,38 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.22"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.0.0"
},
"time": "2021-08-12T10:53:43+00:00"
"time": "2021-10-14T08:03:54+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "0.12.11",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "2b72e8e17d2034145f239126e876e5fb659675e2"
"reference": "e12d55f74a8cca18c6e684c6450767e055ba7717"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/2b72e8e17d2034145f239126e876e5fb659675e2",
"reference": "2b72e8e17d2034145f239126e876e5fb659675e2",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/e12d55f74a8cca18c6e684c6450767e055ba7717",
"reference": "e12d55f74a8cca18c6e684c6450767e055ba7717",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"phpstan/phpstan": "^0.12.96"
"phpstan/phpstan": "^1.2.0"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-phpunit": "^0.12.16",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
"dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
@ -1172,29 +1248,29 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/0.12.11"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.1.0"
},
"time": "2021-08-21T11:36:27+00:00"
"time": "2021-11-18T09:30:29+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.7",
"version": "9.2.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
"reference": "d5850aaf931743067f4bfc1ae4cbd06468400687"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687",
"reference": "d5850aaf931743067f4bfc1ae4cbd06468400687",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.12.0",
"nikic/php-parser": "^4.13.0",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@ -1243,7 +1319,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10"
},
"funding": [
{
@ -1251,20 +1327,20 @@
"type": "github"
}
],
"time": "2021-09-17T05:39:03+00:00"
"time": "2021-12-05T09:12:13+00:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "3.0.5",
"version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "aa4be8575f26070b100fccb67faabb28f21f66f8"
"reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8",
"reference": "aa4be8575f26070b100fccb67faabb28f21f66f8",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
@ -1303,7 +1379,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
},
"funding": [
{
@ -1311,7 +1387,7 @@
"type": "github"
}
],
"time": "2020-09-28T05:57:25+00:00"
"time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-invoker",
@ -1496,16 +1572,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.10",
"version": "9.5.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
"reference": "93d4bf4c37aec6384bb9e5d390d9049a463a7256"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/93d4bf4c37aec6384bb9e5d390d9049a463a7256",
"reference": "93d4bf4c37aec6384bb9e5d390d9049a463a7256",
"shasum": ""
},
"require": {
@ -1583,11 +1659,11 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.12"
},
"funding": [
{
"url": "https://phpunit.de/donate.html",
"url": "https://phpunit.de/sponsors.html",
"type": "custom"
},
{
@ -1595,7 +1671,7 @@
"type": "github"
}
],
"time": "2021-09-25T07:38:51+00:00"
"time": "2022-01-21T05:54:47+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2026,16 +2102,16 @@
},
{
"name": "sebastian/exporter",
"version": "4.0.3",
"version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65"
"reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65",
"reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
"reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
"shasum": ""
},
"require": {
@ -2084,14 +2160,14 @@
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
"source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
},
"funding": [
{
@ -2099,7 +2175,7 @@
"type": "github"
}
],
"time": "2020-09-28T05:24:23+00:00"
"time": "2021-11-11T14:18:36+00:00"
},
{
"name": "sebastian/global-state",
@ -2563,21 +2639,24 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.23.0",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
@ -2622,7 +2701,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0"
},
"funding": [
{
@ -2638,7 +2717,7 @@
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00"
"time": "2021-10-20T20:35:02+00:00"
},
{
"name": "theseer/tokenizer",
@ -2757,6 +2836,8 @@
"platform": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-crypto": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
@ -2766,7 +2847,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pthreads": "~3.2.0",
"ext-pthreads": "^4.0",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
@ -2780,5 +2861,5 @@
"platform-overrides": {
"php": "8.0.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.2.0"
}

View File

@ -1,13 +1,9 @@
includes:
- tests/phpstan/configs/actual-problems.neon
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
- tests/phpstan/configs/gc-hacks.neon
- tests/phpstan/configs/l7-baseline.neon
- tests/phpstan/configs/l8-baseline.neon
- tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon
- tests/phpstan/configs/phpunit-wiring-tests.neon
- tests/phpstan/configs/pthreads-bugs.neon
- tests/phpstan/configs/runtime-type-checks.neon
- tests/phpstan/configs/spl-fixed-array-sucks.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
@ -15,9 +11,9 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
parameters:
level: 8
checkExplicitMixed: true
level: 9
checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false
bootstrapFiles:
- tests/phpstan/bootstrap.php
scanDirectories:
@ -43,6 +39,7 @@ parameters:
stubFiles:
- tests/phpstan/stubs/chunkutils.stub
- tests/phpstan/stubs/leveldb.stub
- tests/phpstan/stubs/phpasn1.stub
- tests/phpstan/stubs/pthreads.stub
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
staticReflectionClassNamePatterns:

View File

@ -30,7 +30,10 @@ use pocketmine\utils\TextFormat;
* Handles the achievement list and a bit more
*/
abstract class Achievement{
/** @var array[] */
/**
* @var mixed[][]
* @phpstan-var array<string, array{name: string, requires: list<string>}>
*/
public static $list = [
/*"openInventory" => array(
"name" => "Taking Inventory",

View File

@ -383,7 +383,7 @@ class CrashDump{
$this->addLine("uname -a: " . php_uname("a"));
$this->addLine("PHP Version: " . phpversion());
$this->addLine("Zend version: " . zend_version());
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
$this->addLine("OS: " . PHP_OS . ", " . Utils::getOS());
$this->addLine("Composer libraries: ");
foreach($composerLibraries as $library => $libraryVersion){
$this->addLine("- $library $libraryVersion");

View File

@ -353,35 +353,33 @@ class MemoryManager{
file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$logger->info("[Dump] Wrote $staticCount static properties");
if(isset($GLOBALS)){ //This might be null if we're on a different thread
$globalVariables = [];
$globalCount = 0;
$globalVariables = [];
$globalCount = 0;
$ignoredGlobals = [
'GLOBALS' => true,
'_SERVER' => true,
'_REQUEST' => true,
'_POST' => true,
'_GET' => true,
'_FILES' => true,
'_ENV' => true,
'_COOKIE' => true,
'_SESSION' => true
];
$ignoredGlobals = [
'GLOBALS' => true,
'_SERVER' => true,
'_REQUEST' => true,
'_POST' => true,
'_GET' => true,
'_FILES' => true,
'_ENV' => true,
'_COOKIE' => true,
'_SESSION' => true
];
foreach($GLOBALS as $varName => $value){
if(isset($ignoredGlobals[$varName])){
continue;
}
$globalCount++;
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
foreach($GLOBALS as $varName => $value){
if(isset($ignoredGlobals[$varName])){
continue;
}
file_put_contents($outputFolder . "/globalVariables.js", json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$logger->info("[Dump] Wrote $globalCount global variables");
$globalCount++;
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
file_put_contents($outputFolder . "/globalVariables.js", json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$logger->info("[Dump] Wrote $globalCount global variables");
$data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
do{

View File

@ -102,6 +102,8 @@ 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\encryption\EncryptionContext;
use pocketmine\network\mcpe\encryption\PrepareEncryptionTask;
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
@ -139,6 +141,7 @@ use pocketmine\network\mcpe\protocol\ResourcePackDataInfoPacket;
use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket;
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
use pocketmine\network\mcpe\protocol\RespawnPacket;
use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket;
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
use pocketmine\network\mcpe\protocol\SetTitlePacket;
@ -184,6 +187,7 @@ use pocketmine\tile\ItemFrame;
use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use pocketmine\utils\UUID;
use function abs;
@ -210,6 +214,7 @@ use function json_encode;
use function json_last_error_msg;
use function lcg_value;
use function max;
use function mb_strlen;
use function microtime;
use function min;
use function preg_match;
@ -284,6 +289,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var DataPacket[] */
private $batchedPackets = [];
private ?EncryptionContext $cipher = null;
/**
* @var int
* Last measurement of player's latency in milliseconds.
@ -299,6 +306,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var bool */
private $seenLoginPacket = false;
/** @var bool */
private $awaitingEncryptionHandshake = false;
/** @var bool */
private $resourcePacksDone = false;
/** @var bool */
@ -1117,7 +1126,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
if($this->getHealth() <= 0){
$this->respawn();
$this->actuallyRespawn();
}
}
@ -2072,9 +2081,49 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->xuid = $xuid;
}
//TODO: encryption
$identityPublicKey = base64_decode($packet->identityPublicKey, true);
if($identityPublicKey === false){
//if this is invalid it should have borked VerifyLoginTask
throw new AssumptionFailedError("We should never have reached here if the key is invalid");
}
if(EncryptionContext::$ENABLED){
$this->server->getAsyncPool()->submitTask(new PrepareEncryptionTask(
$identityPublicKey,
function(string $encryptionKey, string $handshakeJwt) : void{
if(!$this->isConnected()){
return;
}
$pk = new ServerToClientHandshakePacket();
$pk->jwt = $handshakeJwt;
$this->sendDataPacket($pk, false, true); //make sure this gets sent before encryption is enabled
$this->awaitingEncryptionHandshake = true;
$this->cipher = EncryptionContext::fakeGCM($encryptionKey);
$this->server->getLogger()->debug("Enabled encryption for " . $this->username);
}
));
}else{
$this->processLogin();
}
}
/**
* @internal
*/
public function onEncryptionHandshake() : bool{
if(!$this->awaitingEncryptionHandshake){
return false;
}
$this->awaitingEncryptionHandshake = false;
$this->server->getLogger()->debug("Encryption handshake completed for " . $this->username);
$this->processLogin();
return true;
}
/**
@ -2279,6 +2328,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
$pk->serverSoftwareVersion = sprintf("%s %s", \pocketmine\NAME, \pocketmine\VERSION);
$pk->blockPaletteChecksum = 0; //we don't bother with this (0 skips verification) - the preimage is some dumb stringified NBT, not even actual NBT
$this->dataPacket($pk);
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
@ -3124,10 +3174,22 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->removeWindow($this->windowIndex[$packet->windowId]);
$this->closingWindowId = null;
//removeWindow handles sending the appropriate
return true;
}else{
/*
* TODO: HACK!
* If we told the client to remove a window on our own (e.g. a plugin called removeWindow()), our
* first ContainerClose tricks the client into behaving as if it itself asked for the window to be closed.
* This means that it will send us a ContainerClose of its own, which we must respond to the same way as if
* the client closed the window by itself.
* If we don't, the client will not be able to open any new windows.
*/
$pk = new ContainerClosePacket();
$pk->windowId = $packet->windowId;
$pk->server = false;
$this->sendDataPacket($pk);
}
return false;
return true;
}
public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{
@ -3252,6 +3314,24 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
/**
* @throws \UnexpectedValueException
*/
private function checkBookText(string $string, string $fieldName, int $softLimit, int $hardLimit, bool &$cancel) : string{
if(strlen($string) > $hardLimit){
throw new \UnexpectedValueException(sprintf("Book %s must be at most %d bytes, but have %d bytes", $fieldName, $hardLimit, strlen($string)));
}
$result = TextFormat::clean($string, false);
//strlen() is O(1), mb_strlen() is O(n)
if(strlen($result) > $softLimit * 4 || mb_strlen($result, 'UTF-8') > $softLimit){
$cancel = true;
$this->server->getLogger()->debug(sprintf("Cancelled book edit by %s due to %s exceeded soft limit of %d chars", $this->getName(), $fieldName, $softLimit));
}
return $result;
}
public function handleBookEdit(BookEditPacket $packet) : bool{
/** @var WritableBook $oldBook */
$oldBook = $this->inventory->getItem($packet->inventorySlot);
@ -3261,10 +3341,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$newBook = clone $oldBook;
$modifiedPages = [];
$cancel = false;
switch($packet->type){
case BookEditPacket::TYPE_REPLACE_PAGE:
$newBook->setPageText($packet->pageNumber, $packet->text);
$text = self::checkBookText($packet->text, "page text", 256, 0x7fff, $cancel);
$newBook->setPageText($packet->pageNumber, $text);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_ADD_PAGE:
@ -3273,7 +3354,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//TODO: the client can send insert-before actions on trailing client-side pages which cause odd behaviour on the server
return false;
}
$newBook->insertPage($packet->pageNumber, $packet->text);
$text = self::checkBookText($packet->text, "page text", 256, 0x7fff, $cancel);
$newBook->insertPage($packet->pageNumber, $text);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_DELETE_PAGE:
@ -3292,17 +3374,36 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$modifiedPages = [$packet->pageNumber, $packet->secondaryPageNumber];
break;
case BookEditPacket::TYPE_SIGN_BOOK:
$title = self::checkBookText($packet->title, "title", 16, 0x7fff, $cancel);
//this one doesn't have a limit in vanilla, so we have to improvise
$author = self::checkBookText($packet->author, "author", 256, 0x7fff, $cancel);
/** @var WrittenBook $newBook */
$newBook = Item::get(Item::WRITTEN_BOOK, 0, 1, $newBook->getNamedTag());
$newBook->setAuthor($packet->author);
$newBook->setTitle($packet->title);
$newBook->setAuthor($author);
$newBook->setTitle($title);
$newBook->setGeneration(WrittenBook::GENERATION_ORIGINAL);
break;
default:
return false;
}
/*
* Plugins may have created books with more than 50 pages; we allow plugins to do this, but not players.
* Don't allow the page count to grow past 50, but allow deleting, swapping or altering text of existing pages.
*/
$oldPageCount = count($oldBook->getPages());
$newPageCount = count($newBook->getPages());
if(($newPageCount > $oldPageCount && $newPageCount > 50)){
$this->server->getLogger()->debug("Cancelled book edit by " . $this->getName() . " due to adding too many pages (new page count would be $newPageCount)");
$cancel = true;
}
$event = new PlayerEditBookEvent($this, $oldBook, $newBook, $packet->type, $modifiedPages);
if($cancel){
$event->setCancelled();
}
$event->call();
if($event->isCancelled()){
return true;
@ -3381,6 +3482,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
/**
* @internal
*/
public function getCipher() : ?EncryptionContext{
return $this->cipher;
}
/**
* @return bool|int
*/
@ -3891,6 +3999,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return;
}
$this->actuallyRespawn();
}
protected function actuallyRespawn() : void{
$ev = new PlayerRespawnEvent($this, $this->getSpawn());
$ev->call();

View File

@ -74,7 +74,9 @@ namespace pocketmine {
}
$extensions = [
"chunkutils2" => "PocketMine ChunkUtils v2",
"curl" => "cURL",
"crypto" => "php-crypto",
"ctype" => "ctype",
"date" => "Date",
"hash" => "Hash",
@ -103,8 +105,8 @@ namespace pocketmine {
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "3.2.0") < 0){
$messages[] = "pthreads >= 3.2.0 is required, while you have $pthreads_version.";
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") > 0){
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
}
}
@ -115,6 +117,16 @@ namespace pocketmine {
}
}
$chunkutils2_version = phpversion("chunkutils2");
$wantedVersionLock = "0.3";
$wantedVersionMin = "$wantedVersionLock.0";
if($chunkutils2_version !== false && (
version_compare($chunkutils2_version, $wantedVersionMin) < 0 ||
preg_match("/^" . preg_quote($wantedVersionLock, "/") . "\.\d+(?:-dev)?$/", $chunkutils2_version) === 0 //lock in at ^0.2, optionally at a patch release
)){
$messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
}
if(extension_loaded("pocketmine")){
$messages[] = "The native PocketMine extension is no longer supported.";
}
@ -186,6 +198,8 @@ JIT_WARNING
}
critical_error("PHP binary used: " . $binary);
critical_error("Loaded php.ini: " . (($file = php_ini_loaded_file()) !== false ? $file : "none"));
$phprc = getenv("PHPRC");
critical_error("Value of PHPRC environment variable: " . ($phprc === false ? "" : $phprc));
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
echo PHP_EOL;
exit(1);
@ -212,10 +226,8 @@ JIT_WARNING
set_error_handler([Utils::class, 'errorExceptionHandler']);
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
define('pocketmine\VERSION', $version->getFullVersion(true));
$gitHash = str_repeat("00", 20);
$buildNumber = 0;
if(\Phar::running(true) === ""){
$gitHash = Git::getRepositoryStatePretty(\pocketmine\PATH);
@ -225,9 +237,16 @@ JIT_WARNING
if(isset($meta["git"])){
$gitHash = $meta["git"];
}
if(isset($meta["build"]) && is_int($meta["build"])){
$buildNumber = $meta["build"];
}
}
define('pocketmine\GIT_COMMIT', $gitHash);
define('pocketmine\BUILD_NUMBER', $buildNumber);
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
define('pocketmine\VERSION', $version->getFullVersion(true));
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
if($composerGitHash !== null){

View File

@ -71,6 +71,7 @@ use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\AdvancedSourceInterface;
use pocketmine\network\CompressBatchedTask;
use pocketmine\network\mcpe\encryption\EncryptionContext;
use pocketmine\network\mcpe\protocol\BatchPacket;
use pocketmine\network\mcpe\protocol\DataPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
@ -1406,6 +1407,8 @@ class Server{
}
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
EncryptionContext::$ENABLED = (bool) $this->getProperty("network.enable-encryption", true);
$this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes();
$consoleSender = new ConsoleCommandSender();
@ -1490,6 +1493,11 @@ class Server{
$this->getName(),
(\pocketmine\IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET
]));
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.obsolete.warning1", ["3.x", "4.0"]));
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.obsolete.warning2", ["3.x", "2022-03-01"]));
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.obsolete.warning3", ["https://github.com/pmmp/PocketMine-MP/issues/4701"]));
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.license", [$this->getName()]));
Timings::init();

View File

@ -33,7 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.25.1";
const IS_DEVELOPMENT_BUILD = true;
const BUILD_NUMBER = 0;
const BUILD_CHANNEL = "";
const BASE_VERSION = "3.28.0";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_CHANNEL = "pm3";

View File

@ -619,6 +619,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->propertyManager->setFloat(self::DATA_SCALE, 1);
$this->propertyManager->setFloat(self::DATA_BOUNDING_BOX_WIDTH, $this->width);
$this->propertyManager->setFloat(self::DATA_BOUNDING_BOX_HEIGHT, $this->height);
$this->propertyManager->setFloat(self::DATA_COLOR, 0);
$this->fireTicks = $this->namedtag->getShort("Fire", 0);
if($this->isOnFire()){
@ -1229,10 +1230,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$diffZ = $z - $floorZ;
if(BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY, $floorZ)]){
$westNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX - 1, $floorY, $floorZ)];
$eastNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX + 1, $floorY, $floorZ)];
$downNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY - 1, $floorZ)];
$upNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY + 1, $floorZ)];
$westNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX - 1, $floorY, $floorZ)];
$eastNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX + 1, $floorY, $floorZ)];
$downNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY - 1, $floorZ)];
$upNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY + 1, $floorZ)];
$northNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY, $floorZ - 1)];
$southNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY, $floorZ + 1)];
@ -1950,12 +1951,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
public function spawnTo(Player $player) : void{
if($player->getLevelNonNull() !== $this->level){
throw new \InvalidArgumentException("Player is not in the same world");
}
if(
!isset($this->hasSpawned[$player->getLoaderId()]) and
$this->chunk !== null and
$player->getLevelNonNull() === $this->level and
isset($player->usedChunks[$chunkHash = Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())]) and
$player->usedChunks[$chunkHash] === true
){

View File

@ -393,7 +393,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
public function setCurrentTotalXp(int $amount) : bool{
$newLevel = ExperienceUtils::getLevelFromXp($amount);
return $this->setXpAndProgress((int) $newLevel, $newLevel - ((int) $newLevel));
$xpLevel = (int) $newLevel;
$xpProgress = $newLevel - (int) $newLevel;
return $this->setXpAndProgress($xpLevel, $xpProgress);
}
/**
@ -403,6 +405,7 @@ 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{
$amount = min($amount, INT32_MAX - $this->totalXp);
$oldLevel = $this->getXpLevel();
$oldTotal = $this->getCurrentTotalXp();
@ -439,24 +442,26 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
protected function setXpAndProgress(?int $level, ?float $progress) : bool{
$newLevel = $level;
$newProgress = $progress;
if(!$this->justCreated){
$ev = new PlayerExperienceChangeEvent($this, $this->getXpLevel(), $this->getXpProgress(), $level, $progress);
$ev = new PlayerExperienceChangeEvent($this, $this->getXpLevel(), $this->getXpProgress(), $newLevel, $newProgress);
$ev->call();
if($ev->isCancelled()){
return false;
}
$level = $ev->getNewLevel();
$progress = $ev->getNewProgress();
$newLevel = $ev->getNewLevel();
$newProgress = $ev->getNewProgress();
}
if($level !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE_LEVEL)->setValue($level);
if($newLevel !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE_LEVEL)->setValue($newLevel);
}
if($progress !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE)->setValue($progress);
if($newProgress !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE)->setValue($newProgress);
}
return true;
@ -475,8 +480,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* score when they die. (TODO: add this when MCPE supports it)
*/
public function setLifetimeTotalXp(int $amount) : void{
if($amount < 0){
throw new \InvalidArgumentException("XP must be greater than 0");
if($amount < 0 || $amount > INT32_MAX){
throw new \InvalidArgumentException("XP must be greater than 0 and less than " . INT32_MAX);
}
$this->totalXp = $amount;

View File

@ -25,4 +25,4 @@ namespace pocketmine\entity;
final class InvalidSkinException extends \InvalidArgumentException{
}
}

View File

@ -28,6 +28,7 @@ use function implode;
use function in_array;
use function json_encode;
use function strlen;
use const INT32_MAX;
class Skin{
public const ACCEPTED_SKIN_SIZES = [
@ -67,10 +68,20 @@ class Skin{
}
}
private static function checkLength(string $string, string $name, int $maxLength) : void{
if(strlen($string) > $maxLength){
throw new InvalidSkinException("$name must be at most $maxLength bytes, but have " . strlen($string) . " bytes");
}
}
/**
* @throws InvalidSkinException
*/
public function validate() : void{
self::checkLength($this->skinId, "Skin ID", 32767);
self::checkLength($this->geometryName, "Geometry name", 32767);
self::checkLength($this->geometryData, "Geometry data", INT32_MAX);
if($this->skinId === ""){
throw new InvalidSkinException("Skin ID must not be empty");
}

View File

@ -80,33 +80,29 @@ abstract class Projectile extends Entity{
$this->setHealth(1);
$this->damage = $this->namedtag->getDouble("damage", $this->damage);
do{
$blockHit = null;
$blockId = null;
$blockData = null;
(function() : void{
if($this->namedtag->hasTag("tileX", IntTag::class) and $this->namedtag->hasTag("tileY", IntTag::class) and $this->namedtag->hasTag("tileZ", IntTag::class)){
$blockHit = new Vector3($this->namedtag->getInt("tileX"), $this->namedtag->getInt("tileY"), $this->namedtag->getInt("tileZ"));
}else{
break;
return;
}
if($this->namedtag->hasTag("blockId", IntTag::class)){
$blockId = $this->namedtag->getInt("blockId");
}else{
break;
return;
}
if($this->namedtag->hasTag("blockData", ByteTag::class)){
$blockData = $this->namedtag->getByte("blockData");
}else{
break;
return;
}
$this->blockHit = $blockHit;
$this->blockHitId = $blockId;
$this->blockHitData = $blockData;
}while(false);
})();
}
public function canCollideWith(Entity $entity) : bool{

View File

@ -100,8 +100,6 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
/**
* Variadic hack for easy array member type enforcement.
*
* @param Item ...$drops
*/
public function setDropsVariadic(Item ...$drops) : void{
$this->blockDrops = $drops;

View File

@ -36,7 +36,7 @@ use pocketmine\Player;
class PlayerDeathEvent extends EntityDeathEvent{
/** @var Player */
protected $entity;
protected $player;
/** @var TextContainer|string */
private $deathMessage;
@ -49,6 +49,7 @@ class PlayerDeathEvent extends EntityDeathEvent{
*/
public function __construct(Player $entity, array $drops, $deathMessage = null, int $xp = 0){
parent::__construct($entity, $drops, $xp);
$this->player = $entity;
$this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause());
}
@ -56,11 +57,11 @@ class PlayerDeathEvent extends EntityDeathEvent{
* @return Player
*/
public function getEntity(){
return $this->entity;
return $this->player;
}
public function getPlayer() : Player{
return $this->entity;
return $this->player;
}
/**

View File

@ -79,6 +79,9 @@ class PlayerExperienceChangeEvent extends EntityEvent implements Cancellable{
}
public function setNewProgress(?float $newProgress) : void{
if($newProgress < 0.0 || $newProgress > 1.0){
throw new \InvalidArgumentException("XP progress must be in range 0-1");
}
$this->newProgress = $newProgress;
}
}

View File

@ -58,8 +58,6 @@ interface Inventory{
*
* Returns the Items that did not fit.
*
* @param Item ...$slots
*
* @return Item[]
*/
public function addItem(Item ...$slots) : array;
@ -73,8 +71,6 @@ interface Inventory{
* Removes the given Item from the inventory.
* It will return the Items that couldn't be removed.
*
* @param Item ...$slots
*
* @return Item[]
*/
public function removeItem(Item ...$slots) : array;

View File

@ -370,7 +370,7 @@ class Level implements ChunkManager, Metadatable{
*/
public function __construct(Server $server, string $name, LevelProvider $provider){
$this->blockStates = BlockFactory::getBlockStatesArray();
$this->levelId = static::$levelIdCounter++;
$this->levelId = self::$levelIdCounter++;
$this->blockMetadata = new BlockMetadataStore($this);
$this->server = $server;
$this->autoSave = $server->getAutoSave();
@ -761,8 +761,6 @@ class Level implements ChunkManager, Metadatable{
/**
* @internal
*
* @param Player ...$targets If empty, will send to all players in the level.
*
* @return void
*/
public function sendTime(Player ...$targets){
@ -2929,8 +2927,6 @@ class Level implements ChunkManager, Metadatable{
}
/**
* @param Player ...$targets
*
* @return void
*/
public function sendDifficulty(Player ...$targets){

View File

@ -28,6 +28,7 @@ namespace pocketmine\level\format;
use pocketmine\block\BlockFactory;
use pocketmine\entity\Entity;
use pocketmine\level\biome\Biome;
use pocketmine\level\Level;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
@ -35,13 +36,20 @@ use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
use pocketmine\world\format\PalettedBlockArray;
use function array_fill;
use function array_filter;
use function array_flip;
use function array_values;
use function assert;
use function chr;
use function count;
use function file_get_contents;
use function is_array;
use function json_decode;
use function ord;
use function pack;
use function str_repeat;
@ -838,15 +846,37 @@ class Chunk{
/**
* Serializes the chunk for sending to players
*/
public function networkSerialize() : string{
public function networkSerialize(?string $networkSerializedTiles) : string{
$result = "";
$subChunkCount = $this->getSubChunkSendCount();
//TODO: HACK! fill in fake subchunks to make up for the new negative space client-side
for($y = 0; $y < 4; ++$y){
$result .= chr(8); //subchunk version 8
$result .= chr(0); //0 layers - client will treat this as all-air
}
for($y = 0; $y < $subChunkCount; ++$y){
$result .= $this->subChunks[$y]->networkSerialize();
}
$result .= $this->biomeIds . chr(0); //border block array count
//TODO: right now we don't support 3D natively, so we just 3Dify our 2D biomes so they fill the column
$encodedBiomePalette = $this->networkSerializeBiomesAsPalette();
$result .= str_repeat($encodedBiomePalette, 25);
$result .= chr(0); //border block array count
//Border block entry format: 1 byte (4 bits X, 4 bits Z). These are however useless since they crash the regular client.
$result .= $networkSerializedTiles ?? $this->networkSerializeTiles();
return $result;
}
/**
* Serializes all tiles in network format for chunk sending. This is necessary because fastSerialize() doesn't
* include tiles; they have to be encoded on the main thread.
*/
public function networkSerializeTiles() : string{
$result = "";
foreach($this->tiles as $tile){
if($tile instanceof Spawnable){
$result .= $tile->getSerializedSpawnCompound();
@ -856,6 +886,49 @@ class Chunk{
return $result;
}
private function networkSerializeBiomesAsPalette() : string{
/** @var string[]|null $biomeIdMap */
static $biomeIdMap = null;
if($biomeIdMap === null){
$biomeIdMapRaw = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/biome_id_map.json');
if($biomeIdMapRaw === false) throw new AssumptionFailedError();
$biomeIdMapDecoded = json_decode($biomeIdMapRaw, true);
if(!is_array($biomeIdMapDecoded)) throw new AssumptionFailedError();
$biomeIdMap = array_flip($biomeIdMapDecoded);
}
$biomePalette = new PalettedBlockArray($this->getBiomeId(0, 0));
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
$biomeId = $this->getBiomeId($x, $z);
if(!isset($biomeIdMap[$biomeId])){
//make sure we aren't sending bogus biomes - the 1.18.0 client crashes if we do this
$biomeId = Biome::OCEAN;
}
for($y = 0; $y < 16; ++$y){
$biomePalette->set($x, $y, $z, $biomeId);
}
}
}
$biomePaletteBitsPerBlock = $biomePalette->getBitsPerBlock();
$encodedBiomePalette =
chr(($biomePaletteBitsPerBlock << 1) | 1) . //the last bit is non-persistence (like for blocks), though it has no effect on biomes since they always use integer IDs
$biomePalette->getWordArray();
//these LSHIFT by 1 uvarints are optimizations: the client expects zigzag varints here
//but since we know they are always unsigned, we can avoid the extra fcall overhead of
//zigzag and just shift directly.
$biomePaletteArray = $biomePalette->getPalette();
if($biomePaletteBitsPerBlock !== 0){
$encodedBiomePalette .= Binary::writeUnsignedVarInt(count($biomePaletteArray) << 1);
}
foreach($biomePaletteArray as $p){
$encodedBiomePalette .= Binary::writeUnsignedVarInt($p << 1);
}
return $encodedBiomePalette;
}
/**
* Fast-serializes the chunk for passing between threads
* TODO: tiles and entities

View File

@ -39,6 +39,7 @@ class ChunkRequestTask extends AsyncTask{
/** @var string */
protected $chunk;
private string $tiles;
/** @var int */
protected $chunkX;
/** @var int */
@ -47,21 +48,20 @@ class ChunkRequestTask extends AsyncTask{
/** @var int */
protected $compressionLevel;
/** @var int */
private $subChunkCount;
public function __construct(Level $level, int $chunkX, int $chunkZ, Chunk $chunk){
$this->levelId = $level->getId();
$this->compressionLevel = $level->getServer()->networkCompressionLevel;
$this->chunk = $chunk->networkSerialize();
$this->chunk = $chunk->fastSerialize();
$this->tiles = $chunk->networkSerializeTiles();
$this->chunkX = $chunkX;
$this->chunkZ = $chunkZ;
$this->subChunkCount = $chunk->getSubChunkSendCount();
}
public function onRun(){
$pk = LevelChunkPacket::withoutCache($this->chunkX, $this->chunkZ, $this->subChunkCount, $this->chunk);
$chunk = Chunk::fastDeserialize($this->chunk);
$pk = LevelChunkPacket::create($this->chunkX, $this->chunkZ, $chunk->getSubChunkSendCount() + 4, false, null, $chunk->networkSerialize($this->tiles));
$batch = new BatchPacket();
$batch->addPacket($pk);

View File

@ -81,7 +81,7 @@ if(!extension_loaded('pocketmine_chunkutils')){
}else{
$i1 = ord($array[$j]);
$i2 = ord($array[$j80]);
$result[$i] = chr(($i2 << 4) | ($i1 & 0x0f));
$result[$i] = chr(($i2 << 4) | ($i1 & 0x0f));
$result[$i | 0x80] = chr(($i1 >> 4) | ($i2 & 0xf0));
}
$i++;

View File

@ -545,10 +545,6 @@ class LevelDB extends BaseLevelProvider{
return Binary::writeLInt($chunkX) . Binary::writeLInt($chunkZ);
}
private function chunkExists(int $chunkX, int $chunkZ) : bool{
return $this->db->get(LevelDB::chunkIndex($chunkX, $chunkZ) . self::TAG_VERSION) !== false;
}
public function close(){
unset($this->db);
}

View File

@ -0,0 +1,28 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe;
final class JwtException extends \RuntimeException{
}

View File

@ -0,0 +1,211 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe;
use FG\ASN1\Exception\ParserException;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\Sequence;
use pocketmine\utils\AssumptionFailedError;
use function base64_decode;
use function base64_encode;
use function count;
use function explode;
use function gmp_export;
use function gmp_import;
use function gmp_init;
use function gmp_strval;
use function is_array;
use function json_decode;
use function json_encode;
use function json_last_error_msg;
use function openssl_error_string;
use function openssl_pkey_get_details;
use function openssl_pkey_get_public;
use function openssl_sign;
use function openssl_verify;
use function preg_match;
use function rtrim;
use function sprintf;
use function str_pad;
use function str_repeat;
use function str_replace;
use function str_split;
use function strlen;
use function strtr;
use const GMP_BIG_ENDIAN;
use const GMP_MSW_FIRST;
use const JSON_THROW_ON_ERROR;
use const OPENSSL_ALGO_SHA384;
use const STR_PAD_LEFT;
final class JwtUtils{
/**
* @return string[]
* @phpstan-return array{string, string, string}
* @throws JwtException
*/
public static function split(string $jwt) : array{
$v = explode(".", $jwt);
if(count($v) !== 3){
throw new JwtException("Expected exactly 3 JWT parts, got " . count($v));
}
return [$v[0], $v[1], $v[2]]; //workaround phpstan bug
}
/**
* TODO: replace this result with an object
*
* @return mixed[]
* @phpstan-return array{mixed[], mixed[], string}
*
* @throws JwtException
*/
public static function parse(string $token) : array{
$v = self::split($token);
$header = json_decode(self::b64UrlDecode($v[0]), true);
if(!is_array($header)){
throw new JwtException("Failed to decode JWT header JSON: " . json_last_error_msg());
}
$body = json_decode(self::b64UrlDecode($v[1]), true);
if(!is_array($body)){
throw new JwtException("Failed to decode JWT payload JSON: " . json_last_error_msg());
}
$signature = self::b64UrlDecode($v[2]);
return [$header, $body, $signature];
}
/**
* @throws JwtException
*/
public static function verify(string $jwt, \OpenSSLAsymmetricKey $signingKey) : bool{
[$header, $body, $signature] = self::split($jwt);
$plainSignature = self::b64UrlDecode($signature);
if(strlen($plainSignature) !== 96){
throw new JwtException("JWT signature has unexpected length, expected 96, got " . strlen($plainSignature));
}
[$rString, $sString] = str_split($plainSignature, 48);
$convert = fn(string $str) => gmp_strval(gmp_import($str, 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST), 10);
$sequence = new Sequence(
new Integer($convert($rString)),
new Integer($convert($sString))
);
$v = openssl_verify(
$header . '.' . $body,
$sequence->getBinary(),
$signingKey,
OPENSSL_ALGO_SHA384
);
switch($v){
case 0: return false;
case 1: return true;
case -1: throw new JwtException("Error verifying JWT signature: " . openssl_error_string());
default: throw new AssumptionFailedError("openssl_verify() should only return -1, 0 or 1");
}
}
/**
* @phpstan-param array<string, mixed> $header
* @phpstan-param array<string, mixed> $claims
*/
public static function create(array $header, array $claims, \OpenSSLAsymmetricKey $signingKey) : string{
$jwtBody = JwtUtils::b64UrlEncode(json_encode($header, JSON_THROW_ON_ERROR)) . "." . JwtUtils::b64UrlEncode(json_encode($claims, JSON_THROW_ON_ERROR));
openssl_sign(
$jwtBody,
$rawDerSig,
$signingKey,
OPENSSL_ALGO_SHA384
);
try{
$asnObject = Sequence::fromBinary($rawDerSig);
}catch(ParserException $e){
throw new AssumptionFailedError("Failed to parse OpenSSL signature: " . $e->getMessage(), 0, $e);
}
if(count($asnObject) !== 2){
throw new AssumptionFailedError("OpenSSL produced invalid signature, expected exactly 2 parts");
}
[$r, $s] = [$asnObject[0], $asnObject[1]];
if(!($r instanceof Integer) || !($s instanceof Integer)){
throw new AssumptionFailedError("OpenSSL produced invalid signature, expected 2 INTEGER parts");
}
$rString = $r->getContent();
$sString = $s->getContent();
$toBinary = fn($str) => str_pad(
gmp_export(gmp_init($str, 10), 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST),
48,
"\x00",
STR_PAD_LEFT
);
$jwtSig = JwtUtils::b64UrlEncode($toBinary($rString) . $toBinary($sString));
return "$jwtBody.$jwtSig";
}
public static function b64UrlEncode(string $str) : string{
return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
}
public static function b64UrlDecode(string $str) : string{
if(($len = strlen($str) % 4) !== 0){
$str .= str_repeat('=', 4 - $len);
}
$decoded = base64_decode(strtr($str, '-_', '+/'), true);
if($decoded === false){
throw new JwtException("Malformed base64url encoded payload could not be decoded");
}
return $decoded;
}
public static function emitDerPublicKey(\OpenSSLAsymmetricKey $opensslKey) : string{
$details = openssl_pkey_get_details($opensslKey);
if($details === false){
throw new AssumptionFailedError("Failed to get details from OpenSSL key resource");
}
/** @var string $pemKey */
$pemKey = $details['key'];
if(preg_match("@^-----BEGIN[A-Z\d ]+PUBLIC KEY-----\n([A-Za-z\d+/\n]+)\n-----END[A-Z\d ]+PUBLIC KEY-----\n$@", $pemKey, $matches) === 1){
$derKey = base64_decode(str_replace("\n", "", $matches[1]), true);
if($derKey !== false){
return $derKey;
}
}
throw new AssumptionFailedError("OpenSSL resource contains invalid public key");
}
public static function parseDerPublicKey(string $derKey) : \OpenSSLAsymmetricKey{
$signingKeyOpenSSL = openssl_pkey_get_public(sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", base64_encode($derKey)));
if($signingKeyOpenSSL === false){
throw new JwtException("OpenSSL failed to parse key: " . openssl_error_string());
}
return $signingKeyOpenSSL;
}
}

View File

@ -540,8 +540,6 @@ class NetworkBinaryStream extends BinaryStream{
/**
* Writes a list of Attributes to the packet buffer using the standard format.
*
* @param Attribute ...$attributes
*/
public function putAttributeList(Attribute ...$attributes) : void{
$this->putUnsignedVarInt(count($attributes));
@ -786,8 +784,10 @@ class NetworkBinaryStream extends BinaryStream{
$result->lastTouchedByPlayerID = $this->getEntityUniqueId();
$result->rotation = $this->getByte();
$result->mirror = $this->getByte();
$result->integrityValue = $this->getFloat();
$result->integritySeed = $this->getInt();
$result->animationMode = $this->getByte();
$result->animationSeconds = $this->getLFloat();
$result->integrityValue = $this->getLFloat();
$result->integritySeed = $this->getLInt();
$result->pivot = $this->getVector3();
return $result;
@ -805,8 +805,10 @@ class NetworkBinaryStream extends BinaryStream{
$this->putEntityUniqueId($structureSettings->lastTouchedByPlayerID);
$this->putByte($structureSettings->rotation);
$this->putByte($structureSettings->mirror);
$this->putFloat($structureSettings->integrityValue);
$this->putInt($structureSettings->integritySeed);
$this->putByte($structureSettings->animationMode);
$this->putLFloat($structureSettings->animationSeconds);
$this->putLFloat($structureSettings->integrityValue);
$this->putLInt($structureSettings->integritySeed);
$this->putVector3($structureSettings->pivot);
}

View File

@ -56,6 +56,7 @@ use pocketmine\network\mcpe\protocol\ClientCacheMissResponsePacket;
use pocketmine\network\mcpe\protocol\ClientCacheStatusPacket;
use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket;
use pocketmine\network\mcpe\protocol\CodeBuilderPacket;
use pocketmine\network\mcpe\protocol\CodeBuilderSourcePacket;
use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket;
use pocketmine\network\mcpe\protocol\CommandOutputPacket;
use pocketmine\network\mcpe\protocol\CommandRequestPacket;
@ -128,6 +129,7 @@ use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\PlayerStartItemCooldownPacket;
use pocketmine\network\mcpe\protocol\PlaySoundPacket;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\PositionTrackingDBClientRequestPacket;
@ -146,6 +148,7 @@ use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket;
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
use pocketmine\network\mcpe\protocol\RespawnPacket;
use pocketmine\network\mcpe\protocol\ScriptCustomEventPacket;
use pocketmine\network\mcpe\protocol\ScriptMessagePacket;
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
use pocketmine\network\mcpe\protocol\ServerSettingsResponsePacket;
use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket;
@ -881,4 +884,16 @@ abstract class NetworkSession{
public function handleSubChunkRequest(SubChunkRequestPacket $packet) : bool{
return false;
}
public function handlePlayerStartItemCooldown(PlayerStartItemCooldownPacket $packet) : bool{
return false;
}
public function handleScriptMessage(ScriptMessagePacket $packet) : bool{
return false;
}
public function handleCodeBuilderSource(CodeBuilderSourcePacket $packet) : bool{
return false;
}
}

View File

@ -118,7 +118,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
}
public function handleClientToServerHandshake(ClientToServerHandshakePacket $packet) : bool{
return false; //TODO
return $this->player->onEncryptionHandshake();
}
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{

View File

@ -45,6 +45,7 @@ use function get_class;
use function implode;
use function rtrim;
use function spl_object_hash;
use function substr;
use function unserialize;
use const PTHREADS_INHERIT_CONSTANTS;
@ -55,6 +56,8 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
*/
private const MCPE_RAKNET_PROTOCOL_VERSION = 10;
private const MCPE_RAKNET_PACKET_ID = "\xfe";
/** @var Server */
private $server;
@ -163,9 +166,18 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
//get this now for blocking in case the player was closed before the exception was raised
$player = $this->players[$identifier];
$address = $player->getAddress();
try{
if($packet->buffer !== ""){
$pk = new BatchPacket($packet->buffer);
if($packet->buffer[0] !== self::MCPE_RAKNET_PACKET_ID){
throw new \UnexpectedValueException("Unexpected non-FE packet");
}
$cipher = $player->getCipher();
$buffer = substr($packet->buffer, 1);
$buffer = $cipher !== null ? $cipher->decrypt($buffer) : $buffer;
$pk = new BatchPacket(self::MCPE_RAKNET_PACKET_ID . $buffer);
$player->handleDataPacket($pk);
}
}catch(\Throwable $e){
@ -245,22 +257,15 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
}
if($packet instanceof BatchPacket){
if($needACK){
$pk = new EncapsulatedPacket();
$pk->identifierACK = $this->identifiersACK[$identifier]++;
$pk->buffer = $packet->buffer;
$pk->reliability = PacketReliability::RELIABLE_ORDERED;
$pk->orderChannel = 0;
}else{
if(!isset($packet->__encapsulatedPacket)){
$packet->__encapsulatedPacket = new CachedEncapsulatedPacket;
$packet->__encapsulatedPacket->identifierACK = null;
$packet->__encapsulatedPacket->buffer = $packet->buffer;
$packet->__encapsulatedPacket->reliability = PacketReliability::RELIABLE_ORDERED;
$packet->__encapsulatedPacket->orderChannel = 0;
}
$pk = $packet->__encapsulatedPacket;
}
$cipher = $player->getCipher();
$rawBuffer = substr($packet->buffer, 1);
$buffer = self::MCPE_RAKNET_PACKET_ID . ($cipher !== null ? $cipher->encrypt($rawBuffer) : $rawBuffer);
$pk = new EncapsulatedPacket();
$pk->identifierACK = $needACK ? $this->identifiersACK[$identifier]++ : null;
$pk->buffer = $buffer;
$pk->reliability = PacketReliability::RELIABLE_ORDERED;
$pk->orderChannel = 0;
$this->interface->sendEncapsulated($identifier, $pk, ($needACK ? RakLib::FLAG_NEED_ACK : 0) | ($immediate ? RakLib::PRIORITY_IMMEDIATE : RakLib::PRIORITY_NORMAL));
return $pk->identifierACK;

View File

@ -0,0 +1,28 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\encryption;
final class DecryptionException extends \RuntimeException{
}

View File

@ -0,0 +1,119 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\encryption;
use Crypto\Cipher;
use pocketmine\utils\Binary;
use function bin2hex;
use function openssl_digest;
use function openssl_error_string;
use function strlen;
use function substr;
class EncryptionContext{
private const CHECKSUM_ALGO = "sha256";
/** @var bool */
public static $ENABLED = true;
/** @var string */
private $key;
/** @var Cipher */
private $decryptCipher;
/** @var int */
private $decryptCounter = 0;
/** @var Cipher */
private $encryptCipher;
/** @var int */
private $encryptCounter = 0;
public function __construct(string $encryptionKey, string $algorithm, string $iv){
$this->key = $encryptionKey;
$this->decryptCipher = new Cipher($algorithm);
$this->decryptCipher->decryptInit($this->key, $iv);
$this->encryptCipher = new Cipher($algorithm);
$this->encryptCipher->encryptInit($this->key, $iv);
}
/**
* Returns an EncryptionContext suitable for decrypting Minecraft packets from 1.16.200 and up.
*
* MCPE uses GCM, but without the auth tag, which defeats the whole purpose of using GCM.
* GCM is just a wrapper around CTR which adds the auth tag, so CTR can replace GCM for this case.
* However, since GCM passes only the first 12 bytes of the IV followed by 0002, we must do the same for
* compatibility with MCPE.
* In PM, we could skip this and just use GCM directly (since we use OpenSSL), but this way is more portable, and
* better for developers who come digging in the PM code looking for answers.
*/
public static function fakeGCM(string $encryptionKey) : self{
return new EncryptionContext(
$encryptionKey,
"AES-256-CTR",
substr($encryptionKey, 0, 12) . "\x00\x00\x00\x02"
);
}
public static function cfb8(string $encryptionKey) : self{
return new EncryptionContext(
$encryptionKey,
"AES-256-CFB8",
substr($encryptionKey, 0, 16)
);
}
/**
* @throws DecryptionException
*/
public function decrypt(string $encrypted) : string{
if(strlen($encrypted) < 9){
throw new DecryptionException("Payload is too short");
}
$decrypted = $this->decryptCipher->decryptUpdate($encrypted);
$payload = substr($decrypted, 0, -8);
$packetCounter = $this->decryptCounter++;
if(($expected = $this->calculateChecksum($packetCounter, $payload)) !== ($actual = substr($decrypted, -8))){
throw new DecryptionException("Encrypted packet $packetCounter has invalid checksum (expected " . bin2hex($expected) . ", got " . bin2hex($actual) . ")");
}
return $payload;
}
public function encrypt(string $payload) : string{
return $this->encryptCipher->encryptUpdate($payload . $this->calculateChecksum($this->encryptCounter++, $payload));
}
private function calculateChecksum(int $counter, string $payload) : string{
$hash = openssl_digest(Binary::writeLLong($counter) . $payload . $this->key, self::CHECKSUM_ALGO, true);
if($hash === false){
throw new \RuntimeException("openssl_digest() error: " . openssl_error_string());
}
return substr($hash, 0, 8);
}
}

View File

@ -0,0 +1,68 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\encryption;
use pocketmine\network\mcpe\JwtUtils;
use function base64_encode;
use function bin2hex;
use function gmp_init;
use function gmp_strval;
use function hex2bin;
use function openssl_digest;
use function openssl_error_string;
use function openssl_pkey_derive;
use function str_pad;
final class EncryptionUtils{
private function __construct(){
//NOOP
}
public static function generateSharedSecret(\OpenSSLAsymmetricKey $localPriv, \OpenSSLAsymmetricKey $remotePub) : \GMP{
$hexSecret = openssl_pkey_derive($remotePub, $localPriv, 48);
if($hexSecret === false){
throw new \InvalidArgumentException("Failed to derive shared secret: " . openssl_error_string());
}
return gmp_init(bin2hex($hexSecret), 16);
}
public static function generateKey(\GMP $secret, string $salt) : string{
return openssl_digest($salt . hex2bin(str_pad(gmp_strval($secret, 16), 96, "0", STR_PAD_LEFT)), 'sha256', true);
}
public static function generateServerHandshakeJwt(\OpenSSLAsymmetricKey $serverPriv, string $salt) : string{
$derPublicKey = JwtUtils::emitDerPublicKey($serverPriv);
return JwtUtils::create(
[
"x5u" => base64_encode($derPublicKey),
"alg" => "ES384"
],
[
"salt" => base64_encode($salt)
],
$serverPriv
);
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\encryption;
use pocketmine\network\mcpe\JwtUtils;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
use pocketmine\utils\AssumptionFailedError;
use function igbinary_serialize;
use function igbinary_unserialize;
use function openssl_error_string;
use function openssl_free_key;
use function openssl_pkey_get_details;
use function openssl_pkey_new;
use function random_bytes;
class PrepareEncryptionTask extends AsyncTask{
private static ?\OpenSSLAsymmetricKey $SERVER_PRIVATE_KEY = null;
/** @var string */
private $serverPrivateKey;
/** @var string|null */
private $aesKey = null;
/** @var string|null */
private $handshakeJwt = null;
/** @var string */
private $clientPub;
/**
* @phpstan-param \Closure(string $encryptionKey, string $handshakeJwt) : void $onCompletion
*/
public function __construct(string $clientPub, \Closure $onCompletion){
if(self::$SERVER_PRIVATE_KEY === null){
$serverPrivateKey = openssl_pkey_new(["ec" => ["curve_name" => "secp384r1"]]);
if($serverPrivateKey === false){
throw new \RuntimeException("openssl_pkey_new() failed: " . openssl_error_string());
}
self::$SERVER_PRIVATE_KEY = $serverPrivateKey;
}
$this->serverPrivateKey = igbinary_serialize(openssl_pkey_get_details(self::$SERVER_PRIVATE_KEY));
$this->clientPub = $clientPub;
$this->storeLocal($onCompletion);
}
public function onRun() : void{
/** @var mixed[] $serverPrivDetails */
$serverPrivDetails = igbinary_unserialize($this->serverPrivateKey);
$serverPriv = openssl_pkey_new($serverPrivDetails);
if($serverPriv === false) throw new AssumptionFailedError("Failed to restore server signing key from details");
$clientPub = JwtUtils::parseDerPublicKey($this->clientPub);
$sharedSecret = EncryptionUtils::generateSharedSecret($serverPriv, $clientPub);
$salt = random_bytes(16);
$this->aesKey = EncryptionUtils::generateKey($sharedSecret, $salt);
$this->handshakeJwt = EncryptionUtils::generateServerHandshakeJwt($serverPriv, $salt);
@openssl_free_key($serverPriv);
@openssl_free_key($clientPub);
}
public function onCompletion(Server $server) : void{
/**
* @var \Closure $callback
* @phpstan-var \Closure(string $encryptionKey, string $handshakeJwt) : void $callback
*/
$callback = $this->fetchLocal();
if($this->aesKey === null || $this->handshakeJwt === null){
throw new AssumptionFailedError("Something strange happened here ...");
}
$callback($this->aesKey, $this->handshakeJwt);
}
}

View File

@ -108,9 +108,9 @@ class AdventureSettingsPacket extends DataPacket{
*/
public function setFlag(int $flag, bool $value){
if(($flag & self::BITFLAG_SECOND_SET) !== 0){
$flagSet =& $this->flags2;
$flagSet = &$this->flags2;
}else{
$flagSet =& $this->flags;
$flagSet = &$this->flags;
}
if($value){

View File

@ -48,27 +48,27 @@ class AvailableCommandsPacket extends DataPacket{
* Basic parameter types. These must be combined with the ARG_FLAG_VALID constant.
* ARG_FLAG_VALID | (type const)
*/
public const ARG_TYPE_INT = 0x01;
public const ARG_TYPE_FLOAT = 0x03;
public const ARG_TYPE_VALUE = 0x04;
public const ARG_TYPE_WILDCARD_INT = 0x05;
public const ARG_TYPE_OPERATOR = 0x06;
public const ARG_TYPE_TARGET = 0x07;
public const ARG_TYPE_INT = 0x01;
public const ARG_TYPE_FLOAT = 0x03;
public const ARG_TYPE_VALUE = 0x04;
public const ARG_TYPE_WILDCARD_INT = 0x05;
public const ARG_TYPE_OPERATOR = 0x06;
public const ARG_TYPE_TARGET = 0x07;
public const ARG_TYPE_WILDCARD_TARGET = 0x08;
public const ARG_TYPE_FILEPATH = 0x10;
public const ARG_TYPE_STRING = 0x20;
public const ARG_TYPE_STRING = 0x20;
public const ARG_TYPE_POSITION = 0x28;
public const ARG_TYPE_MESSAGE = 0x2c;
public const ARG_TYPE_MESSAGE = 0x2c;
public const ARG_TYPE_RAWTEXT = 0x2e;
public const ARG_TYPE_RAWTEXT = 0x2e;
public const ARG_TYPE_JSON = 0x32;
public const ARG_TYPE_JSON = 0x32;
public const ARG_TYPE_COMMAND = 0x3f;
public const ARG_TYPE_COMMAND = 0x3f;
/**
* Enums are a little different: they are composed as follows:
@ -359,49 +359,6 @@ class AvailableCommandsPacket extends DataPacket{
}
}
/**
* @param string[] $postfixes
* @phpstan-param array<int, string> $postfixes
*/
private function argTypeToString(int $argtype, array $postfixes) : string{
if(($argtype & self::ARG_FLAG_VALID) !== 0){
if(($argtype & self::ARG_FLAG_ENUM) !== 0){
return "stringenum (" . ($argtype & 0xffff) . ")";
}
switch($argtype & 0xffff){
case self::ARG_TYPE_INT:
return "int";
case self::ARG_TYPE_FLOAT:
return "float";
case self::ARG_TYPE_VALUE:
return "mixed";
case self::ARG_TYPE_TARGET:
return "target";
case self::ARG_TYPE_STRING:
return "string";
case self::ARG_TYPE_POSITION:
return "xyz";
case self::ARG_TYPE_MESSAGE:
return "message";
case self::ARG_TYPE_RAWTEXT:
return "text";
case self::ARG_TYPE_JSON:
return "json";
case self::ARG_TYPE_COMMAND:
return "command";
}
}elseif(($argtype & self::ARG_FLAG_POSTFIX) !== 0){
$postfix = $postfixes[$argtype & 0xffff];
return "int (postfix $postfix)";
}else{
throw new \UnexpectedValueException("Unknown arg type 0x" . dechex($argtype));
}
return "unknown ($argtype)";
}
protected function encodePayload(){
/** @var int[] $enumValueIndexes */
$enumValueIndexes = [];

View File

@ -46,6 +46,8 @@ class BossEventPacket extends DataPacket{
public const TYPE_UNKNOWN_6 = 6;
/* S2C: Not implemented :( Intended to alter bar appearance, but these currently produce no effect on client-side whatsoever. */
public const TYPE_TEXTURE = 7;
/* C2S: Client asking the server to resend all boss data. */
public const TYPE_QUERY = 8;
/** @var int */
public $bossEid;
@ -71,6 +73,7 @@ class BossEventPacket extends DataPacket{
switch($this->eventType){
case self::TYPE_REGISTER_PLAYER:
case self::TYPE_UNREGISTER_PLAYER:
case self::TYPE_QUERY:
$this->playerEid = $this->getEntityUniqueId();
break;
/** @noinspection PhpMissingBreakStatementInspection */
@ -101,6 +104,7 @@ class BossEventPacket extends DataPacket{
switch($this->eventType){
case self::TYPE_REGISTER_PLAYER:
case self::TYPE_UNREGISTER_PLAYER:
case self::TYPE_QUERY:
$this->putEntityUniqueId($this->playerEid);
break;
/** @noinspection PhpMissingBreakStatementInspection */

View File

@ -0,0 +1,67 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
class CodeBuilderSourcePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::CODE_BUILDER_SOURCE_PACKET;
private int $operation;
private int $category;
private string $value;
/**
* @generate-create-func
*/
public static function create(int $operation, int $category, string $value) : self{
$result = new self;
$result->operation = $operation;
$result->category = $category;
$result->value = $value;
return $result;
}
public function getOperation() : int{ return $this->operation; }
public function getCategory() : int{ return $this->category; }
public function getValue() : string{ return $this->value; }
protected function decodePayload() : void{
$this->operation = $this->getByte();
$this->category = $this->getByte();
$this->value = $this->getString();
}
protected function encodePayload() : void{
$this->putByte($this->operation);
$this->putByte($this->category);
$this->putString($this->value);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleCodeBuilderSource($this);
}
}

View File

@ -23,53 +23,43 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use function count;
use const PHP_INT_MAX;
class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::LEVEL_CHUNK_PACKET;
/** @var int */
private $chunkX;
/** @var int */
private $chunkZ;
/** @var int */
private $subChunkCount;
/** @var bool */
private $cacheEnabled;
/** @var int[] */
private $usedBlobHashes = [];
/** @var string */
private $extraPayload;
/**
* Client will request all subchunks as needed up to the top of the world
*/
private const CLIENT_REQUEST_FULL_COLUMN_FAKE_COUNT = 0xff_ff_ff_ff;
/**
* Client will request subchunks as needed up to the height written in the packet, and assume that anything above
* that height is air (wtf mojang ...)
*/
private const CLIENT_REQUEST_TRUNCATED_COLUMN_FAKE_COUNT = 0xff_ff_ff_fe;
public static function withoutCache(int $chunkX, int $chunkZ, int $subChunkCount, string $payload) : self{
$result = new self;
$result->chunkX = $chunkX;
$result->chunkZ = $chunkZ;
$result->subChunkCount = $subChunkCount;
$result->extraPayload = $payload;
$result->cacheEnabled = false;
return $result;
}
private int $chunkX;
private int $chunkZ;
private int $subChunkCount;
private bool $clientSubChunkRequestsEnabled;
/** @var int[]|null */
private ?array $usedBlobHashes = null;
private string $extraPayload;
/**
* @generate-create-func
* @param int[] $usedBlobHashes
*/
public static function withCache(int $chunkX, int $chunkZ, int $subChunkCount, array $usedBlobHashes, string $extraPayload) : self{
(static function(int ...$hashes) : void{})(...$usedBlobHashes);
public static function create(int $chunkX, int $chunkZ, int $subChunkCount, bool $clientSubChunkRequestsEnabled, ?array $usedBlobHashes, string $extraPayload) : self{
$result = new self;
$result->chunkX = $chunkX;
$result->chunkZ = $chunkZ;
$result->subChunkCount = $subChunkCount;
$result->extraPayload = $extraPayload;
$result->cacheEnabled = true;
$result->clientSubChunkRequestsEnabled = $clientSubChunkRequestsEnabled;
$result->usedBlobHashes = $usedBlobHashes;
$result->extraPayload = $extraPayload;
return $result;
}
@ -85,14 +75,18 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
return $this->subChunkCount;
}
public function isClientSubChunkRequestEnabled() : bool{
return $this->clientSubChunkRequestsEnabled;
}
public function isCacheEnabled() : bool{
return $this->cacheEnabled;
return $this->usedBlobHashes !== null;
}
/**
* @return int[]
* @return int[]|null
*/
public function getUsedBlobHashes() : array{
public function getUsedBlobHashes() : ?array{
return $this->usedBlobHashes;
}
@ -103,10 +97,23 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
protected function decodePayload() : void{
$this->chunkX = $this->getVarInt();
$this->chunkZ = $this->getVarInt();
$this->subChunkCount = $this->getUnsignedVarInt();
$this->cacheEnabled = $this->getBool();
if($this->cacheEnabled){
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$subChunkCountButNotReally = $this->getUnsignedVarInt();
if($subChunkCountButNotReally === self::CLIENT_REQUEST_FULL_COLUMN_FAKE_COUNT){
$this->clientSubChunkRequestsEnabled = true;
$this->subChunkCount = PHP_INT_MAX;
}elseif($subChunkCountButNotReally === self::CLIENT_REQUEST_TRUNCATED_COLUMN_FAKE_COUNT){
$this->clientSubChunkRequestsEnabled = true;
$this->subChunkCount = $this->getLShort();
}else{
$this->clientSubChunkRequestsEnabled = false;
$this->subChunkCount = $subChunkCountButNotReally;
}
$cacheEnabled = $this->getBool();
if($cacheEnabled){
$this->usedBlobHashes = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->usedBlobHashes[] = $this->getLLong();
}
}
@ -116,11 +123,23 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
protected function encodePayload() : void{
$this->putVarInt($this->chunkX);
$this->putVarInt($this->chunkZ);
$this->putUnsignedVarInt($this->subChunkCount);
$this->putBool($this->cacheEnabled);
if($this->cacheEnabled){
$this->putUnsignedVarInt(count($this->usedBlobHashes));
foreach($this->usedBlobHashes as $hash){
if($this->clientSubChunkRequestsEnabled){
if($this->subChunkCount === PHP_INT_MAX){
$this->putUnsignedVarInt(self::CLIENT_REQUEST_FULL_COLUMN_FAKE_COUNT);
}else{
$this->putUnsignedVarInt(self::CLIENT_REQUEST_TRUNCATED_COLUMN_FAKE_COUNT);
$this->putLShort($this->subChunkCount);
}
}else{
$this->putUnsignedVarInt($this->subChunkCount);
}
$this->putBool($this->usedBlobHashes !== null);
if($this->usedBlobHashes !== null){
$usedBlobHashes = $this->usedBlobHashes;
$this->putUnsignedVarInt(count($usedBlobHashes));
foreach($usedBlobHashes as $hash){
$this->putLLong($hash);
}
}

View File

@ -206,6 +206,9 @@ class PacketPool{
static::registerPacket(new PhotoInfoRequestPacket());
static::registerPacket(new SubChunkPacket());
static::registerPacket(new SubChunkRequestPacket());
static::registerPacket(new PlayerStartItemCooldownPacket());
static::registerPacket(new ScriptMessagePacket());
static::registerPacket(new CodeBuilderSourcePacket());
}
/**

View File

@ -0,0 +1,61 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
class PlayerStartItemCooldownPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::PLAYER_START_ITEM_COOLDOWN_PACKET;
private string $itemCategory;
private int $cooldownTicks;
/**
* @generate-create-func
*/
public static function create(string $itemCategory, int $cooldownTicks) : self{
$result = new self;
$result->itemCategory = $itemCategory;
$result->cooldownTicks = $cooldownTicks;
return $result;
}
public function getItemCategory() : string{ return $this->itemCategory; }
public function getCooldownTicks() : int{ return $this->cooldownTicks; }
protected function decodePayload() : void{
$this->itemCategory = $this->getString();
$this->cooldownTicks = $this->getVarInt();
}
protected function encodePayload() : void{
$this->putString($this->itemCategory);
$this->putVarInt($this->cooldownTicks);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handlePlayerStartItemCooldown($this);
}
}

View File

@ -37,11 +37,11 @@ interface ProtocolInfo{
*/
/** Actual Minecraft: PE protocol version */
public const CURRENT_PROTOCOL = 471;
public const CURRENT_PROTOCOL = 486;
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
public const MINECRAFT_VERSION = 'v1.17.40';
public const MINECRAFT_VERSION = 'v1.18.11';
/** Version number sent to clients in ping responses. */
public const MINECRAFT_VERSION_NETWORK = '1.17.40';
public const MINECRAFT_VERSION_NETWORK = '1.18.11';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;
@ -218,5 +218,8 @@ interface ProtocolInfo{
public const PHOTO_INFO_REQUEST_PACKET = 0xad;
public const SUB_CHUNK_PACKET = 0xae;
public const SUB_CHUNK_REQUEST_PACKET = 0xaf;
public const PLAYER_START_ITEM_COOLDOWN_PACKET = 0xb0;
public const SCRIPT_MESSAGE_PACKET = 0xb1;
public const CODE_BUILDER_SOURCE_PACKET = 0xb2;
}

View File

@ -0,0 +1,61 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
class ScriptMessagePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::SCRIPT_MESSAGE_PACKET;
private string $messageId;
private string $value;
/**
* @generate-create-func
*/
public static function create(string $messageId, string $value) : self{
$result = new self;
$result->messageId = $messageId;
$result->value = $value;
return $result;
}
public function getMessageId() : string{ return $this->messageId; }
public function getValue() : string{ return $this->value; }
protected function decodePayload() : void{
$this->messageId = $this->getString();
$this->value = $this->getString();
}
protected function encodePayload() : void{
$this->putString($this->messageId);
$this->putString($this->value);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleScriptMessage($this);
}
}

View File

@ -182,6 +182,8 @@ class StartGamePacket extends DataPacket{
/** @var string */
public $serverSoftwareVersion;
public int $blockPaletteChecksum;
protected function decodePayload(){
$this->entityUniqueId = $this->getEntityUniqueId();
$this->entityRuntimeId = $this->getEntityRuntimeId();
@ -265,6 +267,7 @@ class StartGamePacket extends DataPacket{
$this->multiplayerCorrelationId = $this->getString();
$this->enableNewInventorySystem = $this->getBool();
$this->serverSoftwareVersion = $this->getString();
$this->blockPaletteChecksum = $this->getLLong();
}
protected function encodePayload(){
@ -346,6 +349,7 @@ class StartGamePacket extends DataPacket{
$this->putString($this->multiplayerCorrelationId);
$this->putBool($this->enableNewInventorySystem);
$this->putString($this->serverSoftwareVersion);
$this->putLLong($this->blockPaletteChecksum);
}
public function handle(NetworkSession $session) : bool{

View File

@ -23,83 +23,70 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\SubChunkPacketHeightMapInfo;
use pocketmine\network\mcpe\protocol\types\SubChunkPacketHeightMapType;
use pocketmine\network\mcpe\protocol\types\SubChunkPacketEntryWithCache as EntryWithBlobHash;
use pocketmine\network\mcpe\protocol\types\SubChunkPacketEntryWithCacheList as ListWithBlobHashes;
use pocketmine\network\mcpe\protocol\types\SubChunkPacketEntryWithoutCache as EntryWithoutBlobHash;
use pocketmine\network\mcpe\protocol\types\SubChunkPacketEntryWithoutCacheList as ListWithoutBlobHashes;
use pocketmine\network\mcpe\protocol\types\SubChunkPosition;
use function count;
class SubChunkPacket extends DataPacket{
class SubChunkPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::SUB_CHUNK_PACKET;
private int $dimension;
private int $subChunkX;
private int $subChunkY;
private int $subChunkZ;
private string $data;
private int $requestResult;
private ?SubChunkPacketHeightMapInfo $heightMapData = null;
private SubChunkPosition $baseSubChunkPosition;
private ListWithBlobHashes|ListWithoutBlobHashes $entries;
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData) : self{
/**
* @generate-create-func
*/
public static function create(int $dimension, SubChunkPosition $baseSubChunkPosition, ListWithBlobHashes|ListWithoutBlobHashes $entries) : self{
$result = new self;
$result->dimension = $dimension;
$result->subChunkX = $subChunkX;
$result->subChunkY = $subChunkY;
$result->subChunkZ = $subChunkZ;
$result->data = $data;
$result->requestResult = $requestResult;
$result->heightMapData = $heightMapData;
$result->baseSubChunkPosition = $baseSubChunkPosition;
$result->entries = $entries;
return $result;
}
public function isCacheEnabled() : bool{ return $this->entries instanceof ListWithBlobHashes; }
public function getDimension() : int{ return $this->dimension; }
public function getSubChunkX() : int{ return $this->subChunkX; }
public function getBaseSubChunkPosition() : SubChunkPosition{ return $this->baseSubChunkPosition; }
public function getSubChunkY() : int{ return $this->subChunkY; }
public function getSubChunkZ() : int{ return $this->subChunkZ; }
public function getData() : string{ return $this->data; }
public function getRequestResult() : int{ return $this->requestResult; }
public function getHeightMapData() : ?SubChunkPacketHeightMapInfo{ return $this->heightMapData; }
public function getEntries() : ListWithBlobHashes|ListWithoutBlobHashes{ return $this->entries; }
protected function decodePayload() : void{
$cacheEnabled = $this->getBool();
$this->dimension = $this->getVarInt();
$this->subChunkX = $this->getVarInt();
$this->subChunkY = $this->getVarInt();
$this->subChunkZ = $this->getVarInt();
$this->data = $this->getString();
$this->requestResult = $this->getVarInt();
$heightMapDataType = $this->getByte();
$this->heightMapData = match($heightMapDataType){
SubChunkPacketHeightMapType::NO_DATA => null,
SubChunkPacketHeightMapType::DATA => SubChunkPacketHeightMapInfo::read($this),
SubChunkPacketHeightMapType::ALL_TOO_HIGH => SubChunkPacketHeightMapInfo::allTooHigh(),
SubChunkPacketHeightMapType::ALL_TOO_LOW => SubChunkPacketHeightMapInfo::allTooLow(),
default => throw new \UnexpectedValueException("Unknown heightmap data type $heightMapDataType")
};
$this->baseSubChunkPosition = SubChunkPosition::read($this);
$count = $this->getLInt();
if($cacheEnabled){
$entries = [];
for($i = 0; $i < $count; $i++){
$entries[] = EntryWithBlobHash::read($this);
}
$this->entries = new ListWithBlobHashes($entries);
}else{
$entries = [];
for($i = 0; $i < $count; $i++){
$entries[] = EntryWithoutBlobHash::read($this);
}
$this->entries = new ListWithoutBlobHashes($entries);
}
}
protected function encodePayload() : void{
$this->putBool($this->entries instanceof ListWithBlobHashes);
$this->putVarInt($this->dimension);
$this->putVarInt($this->subChunkX);
$this->putVarInt($this->subChunkY);
$this->putVarInt($this->subChunkZ);
$this->putString($this->data);
$this->putVarInt($this->requestResult);
if($this->heightMapData === null){
$this->putByte(SubChunkPacketHeightMapType::NO_DATA);
}elseif($this->heightMapData->isAllTooLow()){
$this->putByte(SubChunkPacketHeightMapType::ALL_TOO_LOW);
}elseif($this->heightMapData->isAllTooHigh()){
$this->putByte(SubChunkPacketHeightMapType::ALL_TOO_HIGH);
}else{
$heightMapData = $this->heightMapData; //avoid PHPStan purity issue
$this->putByte(SubChunkPacketHeightMapType::DATA);
$heightMapData->write($this);
$this->baseSubChunkPosition->write($this);
$this->putLInt(count($this->entries->getEntries()));
foreach($this->entries->getEntries() as $entry){
$entry->write($this);
}
}

View File

@ -23,47 +23,63 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\SubChunkPosition;
use pocketmine\network\mcpe\protocol\types\SubChunkPositionOffset;
use function count;
class SubChunkRequestPacket extends DataPacket/* implements ServerboundPacket*/{
public const NETWORK_ID = ProtocolInfo::SUB_CHUNK_REQUEST_PACKET;
private int $dimension;
private int $subChunkX;
private int $subChunkY;
private int $subChunkZ;
private SubChunkPosition $basePosition;
/**
* @var SubChunkPositionOffset[]
* @phpstan-var list<SubChunkPositionOffset>
*/
private array $entries;
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ) : self{
/**
* @generate-create-func
* @param SubChunkPositionOffset[] $entries
* @phpstan-param list<SubChunkPositionOffset> $entries
*/
public static function create(int $dimension, SubChunkPosition $basePosition, array $entries) : self{
$result = new self;
$result->dimension = $dimension;
$result->subChunkX = $subChunkX;
$result->subChunkY = $subChunkY;
$result->subChunkZ = $subChunkZ;
$result->basePosition = $basePosition;
$result->entries = $entries;
return $result;
}
public function getDimension() : int{ return $this->dimension; }
public function getSubChunkX() : int{ return $this->subChunkX; }
public function getBasePosition() : SubChunkPosition{ return $this->basePosition; }
public function getSubChunkY() : int{ return $this->subChunkY; }
public function getSubChunkZ() : int{ return $this->subChunkZ; }
/**
* @return SubChunkPositionOffset[]
* @phpstan-return list<SubChunkPositionOffset>
*/
public function getEntries() : array{ return $this->entries; }
protected function decodePayload() : void{
$this->dimension = $this->getVarInt();
$this->subChunkX = $this->getVarInt();
$this->subChunkY = $this->getVarInt();
$this->subChunkZ = $this->getVarInt();
$this->basePosition = SubChunkPosition::read($this);
$this->entries = [];
for($i = 0, $count = $this->getLInt(); $i < $count; $i++){
$this->entries[] = SubChunkPositionOffset::read($this);
}
}
protected function encodePayload() : void{
$this->putVarInt($this->dimension);
$this->putVarInt($this->subChunkX);
$this->putVarInt($this->subChunkY);
$this->putVarInt($this->subChunkZ);
$this->basePosition->write($this);
$this->putLInt(count($this->entries));
foreach($this->entries as $entry){
$entry->write($this);
}
}
public function handle(NetworkSession $handler) : bool{

View File

@ -30,11 +30,11 @@ use pocketmine\network\mcpe\NetworkSession;
class UpdateBlockPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::UPDATE_BLOCK_PACKET;
public const FLAG_NONE = 0b0000;
public const FLAG_NONE = 0b0000;
public const FLAG_NEIGHBORS = 0b0001;
public const FLAG_NETWORK = 0b0010;
public const FLAG_NETWORK = 0b0010;
public const FLAG_NOGRAPHIC = 0b0100;
public const FLAG_PRIORITY = 0b1000;
public const FLAG_PRIORITY = 0b1000;
public const FLAG_ALL = self::FLAG_NEIGHBORS | self::FLAG_NETWORK;
public const FLAG_ALL_PRIORITY = self::FLAG_ALL | self::FLAG_PRIORITY;

View File

@ -74,4 +74,4 @@ final class PersonaSkinPiece{
public function getProductId() : string{
return $this->productId;
}
}
}

View File

@ -50,6 +50,10 @@ class StructureSettings{
public $rotation;
/** @var int */
public $mirror;
/** @var int */
public $animationMode;
/** @var float */
public $animationSeconds;
/** @var float */
public $integrityValue;
/** @var int */

View File

@ -0,0 +1,91 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
final class SubChunkPacketEntryCommon{
public function __construct(
private SubChunkPositionOffset $offset,
private int $requestResult,
private string $terrainData,
private ?SubChunkPacketHeightMapInfo $heightMap
){}
public function getOffset() : SubChunkPositionOffset{ return $this->offset; }
public function getRequestResult() : int{ return $this->requestResult; }
public function getTerrainData() : string{ return $this->terrainData; }
/** @return SubChunkPacketHeightMapInfo|null */
public function getHeightMap() : ?SubChunkPacketHeightMapInfo{ return $this->heightMap; }
public static function read(NetworkBinaryStream $in, bool $cacheEnabled) : self{
$offset = SubChunkPositionOffset::read($in);
$requestResult = $in->getByte();
$data = !$cacheEnabled || $requestResult !== SubChunkRequestResult::SUCCESS_ALL_AIR ? $in->getString() : "";
$heightMapDataType = $in->getByte();
$heightMapData = match ($heightMapDataType) {
SubChunkPacketHeightMapType::NO_DATA => null,
SubChunkPacketHeightMapType::DATA => SubChunkPacketHeightMapInfo::read($in),
SubChunkPacketHeightMapType::ALL_TOO_HIGH => SubChunkPacketHeightMapInfo::allTooHigh(),
SubChunkPacketHeightMapType::ALL_TOO_LOW => SubChunkPacketHeightMapInfo::allTooLow(),
default => throw new \UnexpectedValueException("Unknown heightmap data type $heightMapDataType")
};
return new self(
$offset,
$requestResult,
$data,
$heightMapData
);
}
public function write(NetworkBinaryStream $out, bool $cacheEnabled) : void{
$this->offset->write($out);
$out->putByte($this->requestResult);
if(!$cacheEnabled || $this->requestResult !== SubChunkRequestResult::SUCCESS_ALL_AIR){
$out->putString($this->terrainData);
}
if($this->heightMap === null){
$out->putByte(SubChunkPacketHeightMapType::NO_DATA);
}elseif($this->heightMap->isAllTooLow()){
$out->putByte(SubChunkPacketHeightMapType::ALL_TOO_LOW);
}elseif($this->heightMap->isAllTooHigh()){
$out->putByte(SubChunkPacketHeightMapType::ALL_TOO_HIGH);
}else{
$heightMapData = $this->heightMap; //avoid PHPStan purity issue
$out->putByte(SubChunkPacketHeightMapType::DATA);
$heightMapData->write($out);
}
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
final class SubChunkPacketEntryWithCache{
public function __construct(
private SubChunkPacketEntryCommon $base,
private int $usedBlobHash
){}
public function getBase() : SubChunkPacketEntryCommon{ return $this->base; }
public function getUsedBlobHash() : int{ return $this->usedBlobHash; }
public static function read(NetworkBinaryStream $in) : self{
$base = SubChunkPacketEntryCommon::read($in, true);
$usedBlobHash = $in->getLLong();
return new self($base, $usedBlobHash);
}
public function write(NetworkBinaryStream $out) : void{
$this->base->write($out, true);
$out->putLLong($this->usedBlobHash);
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
final class SubChunkPacketEntryWithCacheList{
/**
* @param SubChunkPacketEntryWithCache[] $entries
*/
public function __construct(
private array $entries
){}
/**
* @return SubChunkPacketEntryWithCache[]
*/
public function getEntries() : array{ return $this->entries; }
}

View File

@ -0,0 +1,43 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
final class SubChunkPacketEntryWithoutCache{
public function __construct(
private SubChunkPacketEntryCommon $base
){}
public function getBase() : SubChunkPacketEntryCommon{ return $this->base; }
public static function read(NetworkBinaryStream $in) : self{
return new self(SubChunkPacketEntryCommon::read($in, false));
}
public function write(NetworkBinaryStream $out) : void{
$this->base->write($out, false);
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
final class SubChunkPacketEntryWithoutCacheList{
/**
* @param SubChunkPacketEntryWithoutCache[] $entries
*/
public function __construct(
private array $entries
){}
/**
* @return SubChunkPacketEntryWithoutCache[]
*/
public function getEntries() : array{ return $this->entries; }
}

View File

@ -86,4 +86,4 @@ class SubChunkPacketHeightMapInfo{
}
return true;
}
}
}

View File

@ -29,4 +29,4 @@ final class SubChunkPacketHeightMapType{
public const DATA = 1;
public const ALL_TOO_HIGH = 2;
public const ALL_TOO_LOW = 3;
}
}

View File

@ -0,0 +1,55 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
final class SubChunkPosition{
public function __construct(
private int $x,
private int $y,
private int $z,
){}
public function getX() : int{ return $this->x; }
public function getY() : int{ return $this->y; }
public function getZ() : int{ return $this->z; }
public static function read(NetworkBinaryStream $in) : self{
$x = $in->getVarInt();
$y = $in->getVarInt();
$z = $in->getVarInt();
return new self($x, $y, $z);
}
public function write(NetworkBinaryStream $out) : void{
$out->putVarInt($this->x);
$out->putVarInt($this->y);
$out->putVarInt($this->z);
}
}

View File

@ -0,0 +1,66 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\utils\Binary;
final class SubChunkPositionOffset{
public function __construct(
private int $xOffset,
private int $yOffset,
private int $zOffset,
){
self::clampOffset($this->xOffset);
self::clampOffset($this->yOffset);
self::clampOffset($this->zOffset);
}
private static function clampOffset(int $v) : void{
if($v < -128 || $v > 127){
throw new \InvalidArgumentException("Offsets must be within the range of a byte (-128 ... 127)");
}
}
public function getXOffset() : int{ return $this->xOffset; }
public function getYOffset() : int{ return $this->yOffset; }
public function getZOffset() : int{ return $this->zOffset; }
public static function read(NetworkBinaryStream $in) : self{
$xOffset = Binary::signByte($in->getByte());
$yOffset = Binary::signByte($in->getByte());
$zOffset = Binary::signByte($in->getByte());
return new self($xOffset, $yOffset, $zOffset);
}
public function write(NetworkBinaryStream $out) : void{
$out->putByte($this->xOffset);
$out->putByte($this->yOffset);
$out->putByte($this->zOffset);
}
}

View File

@ -31,4 +31,5 @@ final class SubChunkRequestResult{
public const WRONG_DIMENSION = 3;
public const NULL_PLAYER = 4;
public const Y_INDEX_OUT_OF_BOUNDS = 5;
}
public const SUCCESS_ALL_AIR = 6;
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of BedrockProtocol.
* Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
*
* BedrockProtocol 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.
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\inventory\stackrequest;
use pocketmine\network\mcpe\NetworkBinaryStream;
/**
* Repair and/or remove enchantments from an item in a grindstone.
*/
final class GrindstoneStackRequestAction extends ItemStackRequestAction{
public static function getTypeId() : int{
return ItemStackRequestActionType::CRAFTING_GRINDSTONE;
}
public function __construct(
private int $recipeId,
private int $repairCost
){}
public function getRecipeId() : int{ return $this->recipeId; }
/** WARNING: This may be negative */
public function getRepairCost() : int{ return $this->repairCost; }
public static function read(NetworkBinaryStream $in) : self{
$recipeId = $in->readGenericTypeNetworkId();
$repairCost = $in->getVarInt(); //WHY!!!!
return new self($recipeId, $repairCost);
}
public function write(NetworkBinaryStream $out) : void{
$out->writeGenericTypeNetworkId($this->recipeId);
$out->putVarInt($this->repairCost);
}
}

View File

@ -69,6 +69,8 @@ final class ItemStackRequest{
case DestroyStackRequestAction::getTypeId(): return DestroyStackRequestAction::read($in);
case CraftingConsumeInputStackRequestAction::getTypeId(): return CraftingConsumeInputStackRequestAction::read($in);
case CraftingMarkSecondaryResultStackRequestAction::getTypeId(): return CraftingMarkSecondaryResultStackRequestAction::read($in);
case PlaceIntoBundleStackRequestAction::getTypeId(): return PlaceIntoBundleStackRequestAction::read($in);
case TakeFromBundleStackRequestAction::getTypeId(): return TakeFromBundleStackRequestAction::read($in);
case LabTableCombineStackRequestAction::getTypeId(): return LabTableCombineStackRequestAction::read($in);
case BeaconPaymentStackRequestAction::getTypeId(): return BeaconPaymentStackRequestAction::read($in);
case MineBlockStackRequestAction::getTypeId(): return MineBlockStackRequestAction::read($in);
@ -76,6 +78,8 @@ final class ItemStackRequest{
case CraftRecipeAutoStackRequestAction::getTypeId(): return CraftRecipeAutoStackRequestAction::read($in);
case CreativeCreateStackRequestAction::getTypeId(): return CreativeCreateStackRequestAction::read($in);
case CraftRecipeOptionalStackRequestAction::getTypeId(): return CraftRecipeOptionalStackRequestAction::read($in);
case GrindstoneStackRequestAction::getTypeId(): return GrindstoneStackRequestAction::read($in);
case LoomStackRequestAction::getTypeId(): return LoomStackRequestAction::read($in);
case DeprecatedCraftingNonImplementedStackRequestAction::getTypeId(): return DeprecatedCraftingNonImplementedStackRequestAction::read($in);
case DeprecatedCraftingResultsStackRequestAction::getTypeId(): return DeprecatedCraftingResultsStackRequestAction::read($in);
}

View File

@ -1,23 +1,14 @@
<?php
/*
* This file is part of BedrockProtocol.
* Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* BedrockProtocol 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);
@ -36,13 +27,17 @@ final class ItemStackRequestActionType{
public const DESTROY = 4;
public const CRAFTING_CONSUME_INPUT = 5;
public const CRAFTING_MARK_SECONDARY_RESULT_SLOT = 6;
public const LAB_TABLE_COMBINE = 7;
public const BEACON_PAYMENT = 8;
public const MINE_BLOCK = 9;
public const CRAFTING_RECIPE = 10;
public const CRAFTING_RECIPE_AUTO = 11; //recipe book?
public const CREATIVE_CREATE = 12;
public const CRAFTING_RECIPE_OPTIONAL = 13; //anvil/cartography table rename
public const CRAFTING_NON_IMPLEMENTED_DEPRECATED_ASK_TY_LAING = 14;
public const CRAFTING_RESULTS_DEPRECATED_ASK_TY_LAING = 15; //no idea what this is for
public const PLACE_INTO_BUNDLE = 7;
public const TAKE_FROM_BUNDLE = 8;
public const LAB_TABLE_COMBINE = 9;
public const BEACON_PAYMENT = 10;
public const MINE_BLOCK = 11;
public const CRAFTING_RECIPE = 12;
public const CRAFTING_RECIPE_AUTO = 13; //recipe book?
public const CREATIVE_CREATE = 14;
public const CRAFTING_RECIPE_OPTIONAL = 15; //anvil/cartography table rename
public const CRAFTING_GRINDSTONE = 16;
public const CRAFTING_LOOM = 17;
public const CRAFTING_NON_IMPLEMENTED_DEPRECATED_ASK_TY_LAING = 18;
public const CRAFTING_RESULTS_DEPRECATED_ASK_TY_LAING = 19; //no idea what this is for
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of BedrockProtocol.
* Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
*
* BedrockProtocol 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.
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\inventory\stackrequest;
use pocketmine\network\mcpe\NetworkBinaryStream;
/**
* Apply a pattern to a banner using a loom.
*/
final class LoomStackRequestAction extends ItemStackRequestAction{
public static function getTypeId() : int{
return ItemStackRequestActionType::CRAFTING_LOOM;
}
public function __construct(
private string $patternId
){}
public function getPatternId() : string{ return $this->patternId; }
public static function read(NetworkBinaryStream $in) : self{
return new self($in->getString());
}
public function write(NetworkBinaryStream $out) : void{
$out->putString($this->patternId);
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of BedrockProtocol.
* Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
*
* BedrockProtocol 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.
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\inventory\stackrequest;
/**
* Insert an item into a bundle.
*/
final class PlaceIntoBundleStackRequestAction extends ItemStackRequestAction{
use TakeOrPlaceStackRequestActionTrait;
public static function getTypeId() : int{
return ItemStackRequestActionType::PLACE_INTO_BUNDLE;
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of BedrockProtocol.
* Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
*
* BedrockProtocol 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.
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\inventory\stackrequest;
/**
* Take an item out of a bundle.
*/
final class TakeFromBundleStackRequestAction extends ItemStackRequestAction{
use TakeOrPlaceStackRequestActionTrait;
public static function getTypeId() : int{
return ItemStackRequestActionType::TAKE_FROM_BUNDLE;
}
}

View File

@ -163,32 +163,28 @@ class BanEntry{
}else{
$str = explode("|", trim($str));
$entry = new BanEntry(trim(array_shift($str)));
do{
if(count($str) === 0){
break;
}
if(count($str) === 0){
return $entry;
}
$entry->setCreated(self::parseDate(array_shift($str)));
if(count($str) === 0){
break;
}
$entry->setCreated(self::parseDate(array_shift($str)));
if(count($str) === 0){
return $entry;
}
$entry->setSource(trim(array_shift($str)));
if(count($str) === 0){
break;
}
$expire = trim(array_shift($str));
if($expire !== "" and strtolower($expire) !== "forever"){
$entry->setExpires(self::parseDate($expire));
}
if(count($str) === 0){
break;
}
$entry->setReason(trim(array_shift($str)));
}while(false);
$entry->setSource(trim(array_shift($str)));
if(count($str) === 0){
return $entry;
}
$expire = trim(array_shift($str));
if($expire !== "" and strtolower($expire) !== "forever"){
$entry->setExpires(self::parseDate($expire));
}
if(count($str) === 0){
return $entry;
}
$entry->setReason(trim(array_shift($str)));
return $entry;
}
}

View File

@ -365,8 +365,6 @@ class PluginManager{
/**
* Returns whether a specified API version string is considered compatible with the server's API version.
*
* @param string ...$versions
*/
public function isCompatibleApi(string ...$versions) : bool{
$serverString = $this->server->getApiVersion();

View File

@ -23,13 +23,14 @@ declare(strict_types=1);
namespace pocketmine\plugin;
use pocketmine\utils\Utils;
use function count;
use function file;
use function implode;
use function is_file;
use function preg_match;
use function strlen;
use function strpos;
use function substr;
use function trim;
use const FILE_IGNORE_NEW_LINES;
use const FILE_SKIP_EMPTY_LINES;
@ -60,30 +61,27 @@ class ScriptPluginLoader implements PluginLoader{
return null;
}
$data = [];
$insideHeader = false;
$docCommentLines = [];
foreach($content as $line){
if(!$insideHeader and strpos($line, "/**") !== false){
$insideHeader = true;
}
if(preg_match("/^[ \t]+\\*[ \t]+@([a-zA-Z]+)([ \t]+(.*))?$/", $line, $matches) > 0){
$key = $matches[1];
$content = trim($matches[3] ?? "");
if($key === "notscript"){
return null;
if(!$insideHeader){
if(strpos($line, "/**") !== false){
$insideHeader = true;
}else{
continue;
}
$data[$key] = $content;
}
if($insideHeader and strpos($line, "*/") !== false){
$docCommentLines[] = $line;
if(strpos($line, "*/") !== false){
break;
}
}
if($insideHeader){
$data = Utils::parseDocComment(implode("\n", $docCommentLines));
if(count($data) !== 0){
return new PluginDescription($data);
}

View File

@ -31,6 +31,9 @@ use function file_exists;
use function gettype;
use function is_array;
use function is_dir;
use function is_float;
use function is_int;
use function is_string;
use function mkdir;
use function strtolower;
use const DIRECTORY_SEPARATOR;
@ -78,14 +81,12 @@ class ResourcePackManager{
}
foreach($resourceStack as $pos => $pack){
try{
$pack = (string) $pack;
}catch(\ErrorException $e){
if(!is_string($pack) && !is_int($pack) && !is_float($pack)){
$logger->critical("Found invalid entry in resource pack list at offset $pos of type " . gettype($pack));
continue;
}
$pack = (string) $pack;
try{
/** @var string $pack */
$packPath = $this->path . DIRECTORY_SEPARATOR . $pack;
if(!file_exists($packPath)){
throw new ResourcePackException("File or directory not found");

View File

@ -92,6 +92,9 @@ network:
#Maximum size in bytes of packets sent over the network (default 1492 bytes). Packets larger than this will be
#fragmented or split into smaller parts. Clients can request MTU sizes up to but not more than this number.
max-mtu-size: 1492
#Enable encryption of Minecraft network traffic. This has an impact on performance, but prevents hackers from stealing sessions and pretending to be other players.
#DO NOT DISABLE THIS unless you understand the risks involved.
enable-encryption: true
debug:
#If > 1, it will show debug messages in the console
@ -108,7 +111,7 @@ player:
allow-movement-cheats: true
level-settings:
#The default format that levels will use when created
#The default format that worlds will use when created
default-format: pmanvil
chunk-sending:
@ -176,7 +179,7 @@ aliases:
#savestop: [save-all, stop]
worlds:
#These settings will override the generator set in server.properties and allows loading multiple levels
#These settings will override the generator set in server.properties and allows loading multiple worlds
#Example:
#world:
# seed: 404

View File

@ -192,6 +192,8 @@ class AutoUpdater{
if($currentVersion->compare($newVersion) > 0 and ($currentVersion->getFullVersion() !== $newVersion->getFullVersion() or $currentVersion->getBuild() > 0)){
$this->newVersion = $newVersion;
}else{
$this->server->getLogger()->debug("[AutoUpdater] API reported version is an older version or the same version (" . $newVersion->getFullVersion() . "), not showing notification");
}
}
@ -199,12 +201,7 @@ class AutoUpdater{
* Returns the channel used for update checking (stable, beta, dev)
*/
public function getChannel() : string{
$channel = strtolower($this->server->getProperty("auto-updater.preferred-channel", "stable"));
if($channel !== "stable" and $channel !== "beta" and $channel !== "alpha" and $channel !== "development"){
$channel = "stable";
}
return $channel;
return strtolower($this->server->getProperty("auto-updater.preferred-channel", "stable"));
}
/**

View File

@ -109,8 +109,6 @@ class Color{
/**
* Mixes the supplied list of colours together to produce a result colour.
*
* @param Color ...$colors
*/
public static function mix(Color ...$colors) : Color{
$count = count($colors);

View File

@ -368,14 +368,14 @@ class Config{
$this->config[$base] = [];
}
$base =& $this->config[$base];
$base = &$this->config[$base];
while(count($vars) > 0){
$baseKey = array_shift($vars);
if(!isset($base[$baseKey])){
$base[$baseKey] = [];
}
$base =& $base[$baseKey];
$base = &$base[$baseKey];
}
$base = $value;
@ -420,14 +420,14 @@ class Config{
$vars = explode(".", $key);
$currentNode =& $this->config;
$currentNode = &$this->config;
while(count($vars) > 0){
$nodeName = array_shift($vars);
if(isset($currentNode[$nodeName])){
if(count($vars) === 0){ //final node
unset($currentNode[$nodeName]);
}elseif(is_array($currentNode[$nodeName])){
$currentNode =& $currentNode[$nodeName];
$currentNode = &$currentNode[$nodeName];
}
}else{
break;

View File

@ -45,4 +45,4 @@ trait SingletonTrait{
public static function reset() : void{
self::$instance = null;
}
}
}

View File

@ -28,7 +28,9 @@ use function fopen;
use function function_exists;
use function getenv;
use function is_array;
use function is_string;
use function sapi_windows_vt100_support;
use function shell_exec;
use function stream_isatty;
abstract class Terminal{
@ -137,41 +139,44 @@ abstract class Terminal{
* @return void
*/
protected static function getEscapeCodes(){
self::$FORMAT_BOLD = `tput bold`;
self::$FORMAT_OBFUSCATED = `tput smacs`;
self::$FORMAT_ITALIC = `tput sitm`;
self::$FORMAT_UNDERLINE = `tput smul`;
$tput = fn(string $args) => is_string($result = shell_exec("tput $args")) ? $result : "";
$setaf = fn(int $code) => $tput("setaf $code");
self::$FORMAT_BOLD = $tput("bold");
self::$FORMAT_OBFUSCATED = $tput("smacs");
self::$FORMAT_ITALIC = $tput("sitm");
self::$FORMAT_UNDERLINE = $tput("smul");
self::$FORMAT_STRIKETHROUGH = "\x1b[9m"; //`tput `;
self::$FORMAT_RESET = `tput sgr0`;
self::$FORMAT_RESET = $tput("sgr0");
$colors = (int) `tput colors`;
$colors = (int) $tput("colors");
if($colors > 8){
self::$COLOR_BLACK = $colors >= 256 ? `tput setaf 16` : `tput setaf 0`;
self::$COLOR_DARK_BLUE = $colors >= 256 ? `tput setaf 19` : `tput setaf 4`;
self::$COLOR_DARK_GREEN = $colors >= 256 ? `tput setaf 34` : `tput setaf 2`;
self::$COLOR_DARK_AQUA = $colors >= 256 ? `tput setaf 37` : `tput setaf 6`;
self::$COLOR_DARK_RED = $colors >= 256 ? `tput setaf 124` : `tput setaf 1`;
self::$COLOR_PURPLE = $colors >= 256 ? `tput setaf 127` : `tput setaf 5`;
self::$COLOR_GOLD = $colors >= 256 ? `tput setaf 214` : `tput setaf 3`;
self::$COLOR_GRAY = $colors >= 256 ? `tput setaf 145` : `tput setaf 7`;
self::$COLOR_DARK_GRAY = $colors >= 256 ? `tput setaf 59` : `tput setaf 8`;
self::$COLOR_BLUE = $colors >= 256 ? `tput setaf 63` : `tput setaf 12`;
self::$COLOR_GREEN = $colors >= 256 ? `tput setaf 83` : `tput setaf 10`;
self::$COLOR_AQUA = $colors >= 256 ? `tput setaf 87` : `tput setaf 14`;
self::$COLOR_RED = $colors >= 256 ? `tput setaf 203` : `tput setaf 9`;
self::$COLOR_LIGHT_PURPLE = $colors >= 256 ? `tput setaf 207` : `tput setaf 13`;
self::$COLOR_YELLOW = $colors >= 256 ? `tput setaf 227` : `tput setaf 11`;
self::$COLOR_WHITE = $colors >= 256 ? `tput setaf 231` : `tput setaf 15`;
self::$COLOR_BLACK = $colors >= 256 ? $setaf(16) : $setaf(0);
self::$COLOR_DARK_BLUE = $colors >= 256 ? $setaf(19) : $setaf(4);
self::$COLOR_DARK_GREEN = $colors >= 256 ? $setaf(34) : $setaf(2);
self::$COLOR_DARK_AQUA = $colors >= 256 ? $setaf(37) : $setaf(6);
self::$COLOR_DARK_RED = $colors >= 256 ? $setaf(124) : $setaf(1);
self::$COLOR_PURPLE = $colors >= 256 ? $setaf(127) : $setaf(5);
self::$COLOR_GOLD = $colors >= 256 ? $setaf(214) : $setaf(3);
self::$COLOR_GRAY = $colors >= 256 ? $setaf(145) : $setaf(7);
self::$COLOR_DARK_GRAY = $colors >= 256 ? $setaf(59) : $setaf(8);
self::$COLOR_BLUE = $colors >= 256 ? $setaf(63) : $setaf(12);
self::$COLOR_GREEN = $colors >= 256 ? $setaf(83) : $setaf(10);
self::$COLOR_AQUA = $colors >= 256 ? $setaf(87) : $setaf(14);
self::$COLOR_RED = $colors >= 256 ? $setaf(203) : $setaf(9);
self::$COLOR_LIGHT_PURPLE = $colors >= 256 ? $setaf(207) : $setaf(13);
self::$COLOR_YELLOW = $colors >= 256 ? $setaf(227) : $setaf(11);
self::$COLOR_WHITE = $colors >= 256 ? $setaf(231) : $setaf(15);
}else{
self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = `tput setaf 0`;
self::$COLOR_RED = self::$COLOR_DARK_RED = `tput setaf 1`;
self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = `tput setaf 2`;
self::$COLOR_YELLOW = self::$COLOR_GOLD = `tput setaf 3`;
self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = `tput setaf 4`;
self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = `tput setaf 5`;
self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = `tput setaf 6`;
self::$COLOR_GRAY = self::$COLOR_WHITE = `tput setaf 7`;
self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = $setaf(0);
self::$COLOR_RED = self::$COLOR_DARK_RED = $setaf(1);
self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = $setaf(2);
self::$COLOR_YELLOW = self::$COLOR_GOLD = $setaf(3);
self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = $setaf(4);
self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = $setaf(5);
self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = $setaf(6);
self::$COLOR_GRAY = self::$COLOR_WHITE = $setaf(7);
}
}

View File

@ -31,6 +31,7 @@ use function file_get_contents;
use function implode;
use function ini_get;
use function ini_set;
use function is_array;
use function is_string;
use function json_decode;
use function parse_ini_file;
@ -58,50 +59,49 @@ abstract class Timezone{
*/
public static function init() : array{
$messages = [];
do{
$timezone = self::get();
if($timezone !== ""){
/*
* This is here so that people don't come to us complaining and fill up the issue tracker when they put
* an incorrect timezone abbreviation in php.ini apparently.
*/
if(strpos($timezone, "/") === false){
$default_timezone = timezone_name_from_abbr($timezone);
if($default_timezone !== false){
ini_set("date.timezone", $default_timezone);
date_default_timezone_set($default_timezone);
break;
}else{
//Bad php.ini value, try another method to detect timezone
$messages[] = "Timezone \"$timezone\" could not be parsed as a valid timezone from php.ini, falling back to auto-detection";
}
}else{
date_default_timezone_set($timezone);
break;
$timezone = self::get();
if($timezone !== ""){
/*
* This is here so that people don't come to us complaining and fill up the issue tracker when they put
* an incorrect timezone abbreviation in php.ini apparently.
*/
if(strpos($timezone, "/") === false){
$default_timezone = timezone_name_from_abbr($timezone);
if($default_timezone !== false){
ini_set("date.timezone", $default_timezone);
date_default_timezone_set($default_timezone);
return $messages;
}
//Bad php.ini value, try another method to detect timezone
$messages[] = "Timezone \"$timezone\" could not be parsed as a valid timezone from php.ini, falling back to auto-detection";
}else{
date_default_timezone_set($timezone);
return $messages;
}
}
if(($timezone = self::detectSystemTimezone()) and date_default_timezone_set($timezone)){
//Success! Timezone has already been set and validated in the if statement.
//This here is just for redundancy just in case some program wants to read timezone data from the ini.
ini_set("date.timezone", $timezone);
break;
}
if(($timezone = self::detectSystemTimezone()) !== false and date_default_timezone_set($timezone)){
//Success! Timezone has already been set and validated in the if statement.
//This here is just for redundancy just in case some program wants to read timezone data from the ini.
ini_set("date.timezone", $timezone);
return $messages;
}
if(($response = Internet::getURL("http://ip-api.com/json")) !== false //If system timezone detection fails or timezone is an invalid value.
and $ip_geolocation_data = json_decode($response, true)
and $ip_geolocation_data['status'] !== 'fail'
and date_default_timezone_set($ip_geolocation_data['timezone'])
){
//Again, for redundancy.
ini_set("date.timezone", $ip_geolocation_data['timezone']);
break;
}
if(($response = Internet::getURL("http://ip-api.com/json")) !== false //If system timezone detection fails or timezone is an invalid value.
and is_array($ip_geolocation_data = json_decode($response, true))
and isset($ip_geolocation_data['status'])
and $ip_geolocation_data['status'] !== 'fail'
and is_string($ip_geolocation_data['timezone'])
and date_default_timezone_set($ip_geolocation_data['timezone'])
){
//Again, for redundancy.
ini_set("date.timezone", $ip_geolocation_data['timezone']);
return $messages;
}
ini_set("date.timezone", "UTC");
date_default_timezone_set("UTC");
$messages[] = "Timezone could not be automatically determined or was set to an invalid value. An incorrect timezone will result in incorrect timestamps on console logs. It has been set to \"UTC\" by default. You can change it on the php.ini file.";
}while(false);
ini_set("date.timezone", "UTC");
date_default_timezone_set("UTC");
$messages[] = "Timezone could not be automatically determined or was set to an invalid value. An incorrect timezone will result in incorrect timestamps on console logs. It has been set to \"UTC\" by default. You can change it on the php.ini file.";
return $messages;
}

View File

@ -84,8 +84,6 @@ class UUID{
/**
* Creates an UUIDv3 from binary data or list of binary data
*
* @param string ...$data
*/
public static function fromData(string ...$data) : UUID{
$hash = hash("md5", implode($data), true);

View File

@ -71,6 +71,7 @@ use function rmdir;
use function rtrim;
use function scandir;
use function sha1;
use function shell_exec;
use function spl_object_hash;
use function str_pad;
use function str_replace;
@ -235,7 +236,7 @@ class Utils{
}elseif($os === Utils::OS_ANDROID){
$machine .= @file_get_contents("/system/build.prop");
}elseif($os === Utils::OS_MACOS){
$machine .= `system_profiler SPHardwareDataType | grep UUID`;
$machine .= shell_exec("system_profiler SPHardwareDataType | grep UUID");
}
$data = $machine . PHP_MAXPATHLEN;
$data .= PHP_INT_MAX;
@ -358,7 +359,7 @@ class Utils{
break;
case Utils::OS_BSD:
case Utils::OS_MACOS:
$processors = (int) `sysctl -n hw.ncpu`;
$processors = (int) shell_exec("sysctl -n hw.ncpu");
break;
case Utils::OS_WINDOWS:
$processors = (int) getenv("NUMBER_OF_PROCESSORS");

View File

@ -2,11 +2,24 @@
TITLE PocketMine-MP server software for Minecraft: Bedrock Edition
cd /d %~dp0
set PHP_BINARY=
where /q php.exe
if %ERRORLEVEL%==0 (
set PHP_BINARY=php
)
if exist bin\php\php.exe (
rem always use the local PHP binary if it exists
set PHPRC=""
set PHP_BINARY=bin\php\php.exe
) else (
set PHP_BINARY=php
)
if "%PHP_BINARY%"=="" (
echo Couldn't find a PHP binary in system PATH or "%~dp0bin\php"
echo Please refer to the installation instructions at https://doc.pmmp.io/en/rtfd/installation.html
pause
exit 1
)
if exist PocketMine-MP.phar (

View File

@ -11,8 +11,13 @@ if($php -ne ""){
}elseif(Test-Path "bin\php\php.exe"){
$env:PHPRC = ""
$binary = "bin\php\php.exe"
}else{
}elseif((Get-Command php -ErrorAction SilentlyContinue)){
$binary = "php"
}else{
echo "Couldn't find a PHP binary in system PATH or $pwd\bin\php"
echo "Please refer to the installation instructions at https://doc.pmmp.io/en/rtfd/installation.html"
pause
exit 1
}
if($file -eq ""){

View File

@ -23,10 +23,11 @@ if [ "$PHP_BINARY" == "" ]; then
if [ -f ./bin/php7/bin/php ]; then
export PHPRC=""
PHP_BINARY="./bin/php7/bin/php"
elif [[ ! -z $(type php) ]]; then
elif [[ ! -z $(type php 2> /dev/null) ]]; then
PHP_BINARY=$(type -p php)
else
echo "Couldn't find a working PHP 7 binary, please use the installer."
echo "Couldn't find a PHP binary in system PATH or $PWD/bin/php7/bin"
echo "Please refer to the installation instructions at https://doc.pmmp.io/en/rtfd/installation.html"
exit 1
fi
fi

View File

@ -1,25 +0,0 @@
VERSION="$1"
sudo apt update && sudo apt install -y \
re2c \
libtool \
libtool-bin \
zlib1g-dev \
libcurl4-openssl-dev \
libxml2-dev \
libyaml-dev \
libgmp-dev \
libzip-dev \
libssl-dev
INSTALL_DIR="$(pwd)/bin/php7"
export CFLAGS="$CFLAGS -march=x86-64"
export CXXFLAGS="$CXXFLAGS -march=x86-64"
git clone https://github.com/pmmp/php-build.git
cd php-build
./install-dependencies.sh
echo '"pthreads",,"https://github.com/pmmp/pthreads.git",,,"extension",' >> share/php-build/extension/definition
PHP_BUILD_INSTALL_EXTENSION='pthreads=@a6afc0434f91c1e9541444aef6ac7a1f16c595be yaml=2.2.1' PHP_BUILD_ZTS_ENABLE=on ./bin/php-build "$VERSION" "$INSTALL_DIR" || exit 1
rm "$INSTALL_DIR/etc/conf.d/xdebug.ini" || true

View File

@ -1,3 +0,0 @@
#!/bin/bash
sudo apt update && sudo apt install -y \
libzip5

View File

@ -30,6 +30,10 @@ if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 2) . '/vendor/autoload.php');
define('pocketmine\DATA', '');
define('pocketmine\GIT_COMMIT', str_repeat('00', 20));
define('pocketmine\BUILD_NUMBER', 0);
define('pocketmine\PLUGIN_PATH', '');
define('pocketmine\START_TIME', microtime(true));
define('pocketmine\VERSION', '9.9.9');
//opcache breaks PHPStan when dynamic reflection is used - see https://github.com/phpstan/phpstan-src/pull/801#issuecomment-978431013
ini_set('opcache.enable', 'off');

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More