mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
79 Commits
4.0.0-BETA
...
3.27.0
Author | SHA1 | Date | |
---|---|---|---|
e21446e583 | |||
e5a9123522 | |||
09201ac14b | |||
0697c7d316 | |||
1eae133118 | |||
d28be4eaf2 | |||
b33a75a6d1 | |||
bac6a2a1eb | |||
b9b76eaed2 | |||
bee2aba813 | |||
af81f80cf3 | |||
ed2145b6a4 | |||
e8893dd91f | |||
a4af1609ea | |||
8c4b8a9042 | |||
958a9dbf0f | |||
68f3399cfd | |||
d9c70cb176 | |||
9979a64ad2 | |||
75a72786f9 | |||
3d205c6e5f | |||
2955a92837 | |||
7fb1669c6d | |||
a09817864b | |||
f5bbd30dbb | |||
69d5bfa0d4 | |||
549fb923bf | |||
6d5c463bdd | |||
911ad344c9 | |||
06eaf9f273 | |||
1e56ed2ea3 | |||
40895a86e5 | |||
b081394125 | |||
f48cf68cac | |||
264cff70ec | |||
3aabfa4ab0 | |||
cb0af44ccb | |||
d535f02096 | |||
7665f4f443 | |||
20d6b69813 | |||
6b7d0307af | |||
baeac2eb07 | |||
d5f81fe261 | |||
0aeac3af7d | |||
9931c1d50a | |||
8079ae341a | |||
ba295dc7dc | |||
c19174a174 | |||
f95142f6b6 | |||
7ace24caab | |||
32f619ac49 | |||
1bb6ac4fb6 | |||
52a891ba73 | |||
71b813d4f9 | |||
f2540a72ad | |||
7e0f6c02a1 | |||
c023c02b6c | |||
adff561483 | |||
472ffb28ff | |||
726c5652f7 | |||
fc7d297f60 | |||
7b4ef293bd | |||
3683884b9c | |||
db135788b9 | |||
7210db25b0 | |||
ada469bc45 | |||
dc8243f88b | |||
1beec348f9 | |||
7306a2d939 | |||
4bf338f783 | |||
255ff63fda | |||
d72f6a3ac6 | |||
3b34268ed6 | |||
76dad46e13 | |||
eb3530b6e6 | |||
b392651354 | |||
e0b07ff308 | |||
729f831b8f | |||
29e2d92098 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -4,6 +4,7 @@
|
||||
*.sh text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.neon text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
*.ps1 text eol=crlf
|
||||
|
16
.github/workflows/draft-release.yml
vendored
16
.github/workflows/draft-release.yml
vendored
@ -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()
|
||||
|
94
.github/workflows/main.yml
vendored
94
.github/workflows/main.yml
vendored
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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>
|
||||
|
@ -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],
|
||||
|
@ -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();
|
||||
|
Submodule build/php updated: 365356dec1...bd329dba08
@ -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
|
||||
|
@ -21,3 +21,18 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Fixed crash in `Player->showPlayer()` when the target is not in the same world.
|
||||
- `Human->setLifetimeTotalXp()` now limits the maximum value to 2^31.
|
||||
- Fixed players, who died in hardcore mode and were unbanned, getting re-banned on next server join.
|
||||
|
||||
# 3.25.3
|
||||
- Fixed crash when players try to pickup XP while already having max XP.
|
||||
- Added a sanity check to `Human->setCurrentTotalXp()` to try and catch an elusive bug that's been appearing in the wild - please get in touch if you know how to reproduce it!
|
||||
|
||||
# 3.25.4
|
||||
- Fixed a long-standing issue with `Player->removeWindow()` breaking inventory UIs on the client.
|
||||
|
||||
# 3.25.5
|
||||
- Protocol: Fixed incorrect encoding in `StructureSettings`
|
||||
- Fixed reading tags from non-docblock comments in script plugins.
|
||||
- Build number is now defined in phar metadata instead of being patched into the source code directly.
|
||||
|
||||
# 3.25.6
|
||||
- Fixed borked build number in release build of 3.25.5.
|
||||
|
32
changelogs/3.26.md
Normal file
32
changelogs/3.26.md
Normal file
@ -0,0 +1,32 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.26.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.18.0.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
# 3.26.1
|
||||
- Fixed a bug in chunk sending that caused double chests to not be paired, signs to be blank, and various other issues.
|
||||
|
||||
# 3.26.2
|
||||
- Improved error messages shown by `start.cmd`, `start.sh` and `start.ps1` when the PHP binary was not found.
|
||||
- The value of PHPRC is now shown when erroring out due to unsatisfied PHP requirements.
|
||||
- Removed restriction on the range of valid channels for `auto-updater.channel` in `pocketmine.yml`.
|
||||
|
||||
# 3.26.3
|
||||
- `PlayerExperienceChangeEvent->setNewProgress()` now performs range checks. This fixes the root of a very old and confusing crash bug which took several years to identify the cause of.
|
||||
- Note that the defective plugin(s) which caused this problem will still cause a server crash, but the plugin responsible will now get blamed correctly.
|
||||
|
||||
# 3.26.4
|
||||
- Fixed skins appearing black when using RTX resource packs.
|
||||
- Fixed chunks containing furnaces in old worlds (pre-2017) being discarded as corrupted.
|
||||
- This was caused by a strict corruption check detecting bad data created by a bug in PocketMine-MP that was fixed in 2017.
|
||||
|
||||
# 3.26.5
|
||||
- Fixed several denial-of-service attack vectors related to writable book text length and encoding.
|
||||
- Fixed several denial-of-service attack vectors related to skin data field lengths.
|
15
changelogs/3.27.md
Normal file
15
changelogs/3.27.md
Normal file
@ -0,0 +1,15 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.27.0
|
||||
- Introduced support for protocol encryption.
|
||||
- Encryption is enabled by default.
|
||||
- Fixes login replay attacks.
|
||||
- This may cause some performance degradation.
|
||||
- Encryption can be disabled by setting `network.enable-encryption` to `false` in `pocketmine.yml`. DO NOT do this unless you understand the risks involved.
|
||||
- An obsoletion notice has been added to the console during server startup.
|
@ -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": "*",
|
||||
@ -26,19 +28,20 @@
|
||||
"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": "1.0.0",
|
||||
"phpstan/phpstan": "1.3.3",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
230
composer.lock
generated
230
composer.lock
generated
@ -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": "c55df4140fb828ab8add33e0a0e84136",
|
||||
"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",
|
||||
@ -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,9 +434,9 @@
|
||||
"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",
|
||||
@ -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",
|
||||
@ -894,16 +967,16 @@
|
||||
},
|
||||
{
|
||||
"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": {
|
||||
@ -938,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": {
|
||||
@ -1005,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": "1.0.0",
|
||||
"version": "1.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "0d13a99513182e521271d46bde8f28caa4f84d97"
|
||||
"reference": "151a51f6149855785fbd883e79768c0abc96b75f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d13a99513182e521271d46bde8f28caa4f84d97",
|
||||
"reference": "0d13a99513182e521271d46bde8f28caa4f84d97",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/151a51f6149855785fbd883e79768c0abc96b75f",
|
||||
"reference": "151a51f6149855785fbd883e79768c0abc96b75f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1036,7 +1109,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
"dev-master": "1.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1051,7 +1124,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.0.0"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1071,7 +1144,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-01T06:38:20+00:00"
|
||||
"time": "2022-01-07T09:49:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
@ -1130,21 +1203,21 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "7f50eb112f37fda2ef956813d3f1e9b1e69d7940"
|
||||
"reference": "e12d55f74a8cca18c6e684c6450767e055ba7717"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7f50eb112f37fda2ef956813d3f1e9b1e69d7940",
|
||||
"reference": "7f50eb112f37fda2ef956813d3f1e9b1e69d7940",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/e12d55f74a8cca18c6e684c6450767e055ba7717",
|
||||
"reference": "e12d55f74a8cca18c6e684c6450767e055ba7717",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"phpstan/phpstan": "^1.0"
|
||||
"phpstan/phpstan": "^1.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
@ -1175,22 +1248,22 @@
|
||||
"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/1.0.0"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.1.0"
|
||||
},
|
||||
"time": "2021-10-11T06:57:58+00:00"
|
||||
"time": "2021-11-18T09:30:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.8",
|
||||
"version": "9.2.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e"
|
||||
"reference": "d5850aaf931743067f4bfc1ae4cbd06468400687"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e",
|
||||
"reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687",
|
||||
"reference": "d5850aaf931743067f4bfc1ae4cbd06468400687",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1246,7 +1319,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.8"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1254,20 +1327,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-30T08:01:38+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": {
|
||||
@ -1306,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": [
|
||||
{
|
||||
@ -1314,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",
|
||||
@ -1499,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": {
|
||||
@ -1586,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"
|
||||
},
|
||||
{
|
||||
@ -1598,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",
|
||||
@ -2029,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": {
|
||||
@ -2087,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": [
|
||||
{
|
||||
@ -2102,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",
|
||||
@ -2566,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"
|
||||
},
|
||||
@ -2625,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": [
|
||||
{
|
||||
@ -2641,7 +2717,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
@ -2760,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": "*",
|
||||
@ -2783,5 +2861,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.0.0"
|
||||
},
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ includes:
|
||||
- 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
|
||||
@ -40,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:
|
||||
|
@ -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{
|
||||
|
@ -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 */
|
||||
@ -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
|
||||
*/
|
||||
|
@ -74,7 +74,9 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
$extensions = [
|
||||
"chunkutils2" => "PocketMine ChunkUtils v2",
|
||||
"curl" => "cURL",
|
||||
"crypto" => "php-crypto",
|
||||
"ctype" => "ctype",
|
||||
"date" => "Date",
|
||||
"hash" => "Hash",
|
||||
@ -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){
|
||||
|
@ -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();
|
||||
|
@ -33,7 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.25.3";
|
||||
const IS_DEVELOPMENT_BUILD = true;
|
||||
const BUILD_NUMBER = 0;
|
||||
const BUILD_CHANNEL = "";
|
||||
const BASE_VERSION = "3.27.0";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_CHANNEL = "pm3";
|
||||
|
@ -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)];
|
||||
|
||||
|
@ -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;
|
||||
|
@ -25,4 +25,4 @@ namespace pocketmine\entity;
|
||||
|
||||
final class InvalidSkinException extends \InvalidArgumentException{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Submodule src/pocketmine/lang/locale updated: c85a7b79f3...ab3ac724e8
@ -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){
|
||||
|
@ -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
|
||||
|
@ -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::withoutCache($this->chunkX, $this->chunkZ, $chunk->getSubChunkSendCount() + 4, $chunk->networkSerialize($this->tiles));
|
||||
|
||||
$batch = new BatchPacket();
|
||||
$batch->addPacket($pk);
|
||||
|
@ -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++;
|
||||
|
28
src/pocketmine/network/mcpe/JwtException.php
Normal file
28
src/pocketmine/network/mcpe/JwtException.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
final class JwtException extends \RuntimeException{
|
||||
|
||||
}
|
211
src/pocketmine/network/mcpe/JwtUtils.php
Normal file
211
src/pocketmine/network/mcpe/JwtUtils.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
|
@ -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{
|
||||
|
||||
}
|
119
src/pocketmine/network/mcpe/encryption/EncryptionContext.php
Normal file
119
src/pocketmine/network/mcpe/encryption/EncryptionContext.php
Normal 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);
|
||||
}
|
||||
}
|
68
src/pocketmine/network/mcpe/encryption/EncryptionUtils.php
Normal file
68
src/pocketmine/network/mcpe/encryption/EncryptionUtils.php
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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){
|
||||
|
@ -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:
|
||||
|
@ -106,7 +106,7 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
$this->subChunkCount = $this->getUnsignedVarInt();
|
||||
$this->cacheEnabled = $this->getBool();
|
||||
if($this->cacheEnabled){
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->usedBlobHashes[] = $this->getLLong();
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ interface ProtocolInfo{
|
||||
*/
|
||||
|
||||
/** Actual Minecraft: PE protocol version */
|
||||
public const CURRENT_PROTOCOL = 471;
|
||||
public const CURRENT_PROTOCOL = 475;
|
||||
/** 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.0';
|
||||
/** Version number sent to clients in ping responses. */
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.17.40';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.18.0';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
|
@ -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{
|
||||
|
@ -39,8 +39,9 @@ class SubChunkPacket extends DataPacket{
|
||||
private string $data;
|
||||
private int $requestResult;
|
||||
private ?SubChunkPacketHeightMapInfo $heightMapData = null;
|
||||
private ?int $usedBlobHash = null;
|
||||
|
||||
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData) : self{
|
||||
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData, ?int $usedBlobHash) : self{
|
||||
$result = new self;
|
||||
$result->dimension = $dimension;
|
||||
$result->subChunkX = $subChunkX;
|
||||
@ -49,6 +50,7 @@ class SubChunkPacket extends DataPacket{
|
||||
$result->data = $data;
|
||||
$result->requestResult = $requestResult;
|
||||
$result->heightMapData = $heightMapData;
|
||||
$result->usedBlobHash = $usedBlobHash;
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -66,6 +68,8 @@ class SubChunkPacket extends DataPacket{
|
||||
|
||||
public function getHeightMapData() : ?SubChunkPacketHeightMapInfo{ return $this->heightMapData; }
|
||||
|
||||
public function getUsedBlobHash() : ?int{ return $this->usedBlobHash; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->dimension = $this->getVarInt();
|
||||
$this->subChunkX = $this->getVarInt();
|
||||
@ -81,6 +85,7 @@ class SubChunkPacket extends DataPacket{
|
||||
SubChunkPacketHeightMapType::ALL_TOO_LOW => SubChunkPacketHeightMapInfo::allTooLow(),
|
||||
default => throw new \UnexpectedValueException("Unknown heightmap data type $heightMapDataType")
|
||||
};
|
||||
$this->usedBlobHash = $this->getBool() ? $this->getLLong() : null;
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
@ -101,6 +106,11 @@ class SubChunkPacket extends DataPacket{
|
||||
$this->putByte(SubChunkPacketHeightMapType::DATA);
|
||||
$heightMapData->write($this);
|
||||
}
|
||||
$usedBlobHash = $this->usedBlobHash;
|
||||
$this->putBool($usedBlobHash !== null);
|
||||
if($usedBlobHash !== null){
|
||||
$this->putLLong($usedBlobHash);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
|
@ -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;
|
||||
|
@ -74,4 +74,4 @@ final class PersonaSkinPiece{
|
||||
public function getProductId() : string{
|
||||
return $this->productId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -86,4 +86,4 @@ class SubChunkPacketHeightMapInfo{
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +29,4 @@ final class SubChunkPacketHeightMapType{
|
||||
public const DATA = 1;
|
||||
public const ALL_TOO_HIGH = 2;
|
||||
public const ALL_TOO_LOW = 3;
|
||||
}
|
||||
}
|
||||
|
@ -31,4 +31,4 @@ final class SubChunkRequestResult{
|
||||
public const WRONG_DIMENSION = 3;
|
||||
public const NULL_PLAYER = 4;
|
||||
public const Y_INDEX_OUT_OF_BOUNDS = 5;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Submodule src/pocketmine/resources/vanilla updated: f29b7be8fa...482c679aa5
@ -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"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -45,4 +45,4 @@ trait SingletonTrait{
|
||||
public static function reset() : void{
|
||||
self::$instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
17
start.cmd
17
start.cmd
@ -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 (
|
||||
|
@ -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 ""){
|
||||
|
5
start.sh
5
start.sh
@ -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
|
||||
|
@ -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=@4.0.0 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
|
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
sudo apt update && sudo apt install -y \
|
||||
libzip5
|
@ -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');
|
||||
|
@ -495,6 +495,11 @@ parameters:
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/PocketMine.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'build' on mixed\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/pocketmine/PocketMine.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'git' on mixed\\.$#"
|
||||
count: 2
|
||||
@ -3405,6 +3410,16 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\network\\\\mcpe\\\\encryption\\\\EncryptionUtils\\:\\:generateKey\\(\\) should return string but returns string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/network/mcpe/encryption/EncryptionUtils.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\encryption\\\\PrepareEncryptionTask\\:\\:\\$serverPrivateKey \\(string\\) does not accept string\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/network/mcpe/encryption/PrepareEncryptionTask.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\AddActorPacket\\:\\:\\$metadata \\(array\\<int, array\\{int, mixed\\}\\>\\) does not accept array\\<int, mixed\\>\\.$#"
|
||||
count: 1
|
||||
@ -3740,11 +3755,6 @@ parameters:
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$code of class pocketmine\\\\resourcepacks\\\\ResourcePackException constructor expects int, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
|
@ -81,14 +81,9 @@ parameters:
|
||||
path: ../../../src/pocketmine/network/mcpe/protocol/DataPacket.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$array of function array_values expects array, array given\\.$#"
|
||||
message: "#^Parameter \\#1 \\$yamlString of class pocketmine\\\\plugin\\\\PluginDescription constructor expects array\\|string, array\\<string\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$array of function array_map expects array, array given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
path: ../../../src/pocketmine/plugin/ScriptPluginLoader.php
|
||||
|
||||
-
|
||||
message: "#^Dead catch \\- ReflectionException is never thrown in the try block\\.$#"
|
||||
|
@ -1,7 +0,0 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Variable \\$GLOBALS in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/MemoryManager.php
|
||||
|
22
tests/phpstan/stubs/phpasn1.stub
Normal file
22
tests/phpstan/stubs/phpasn1.stub
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the PHPASN1 library.
|
||||
*
|
||||
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FG\ASN1\Universal;
|
||||
|
||||
class Integer
|
||||
{
|
||||
/**
|
||||
* @param int|string $value
|
||||
*/
|
||||
public function __construct($value){}
|
||||
|
||||
/** @return int|string */
|
||||
public function getContent(){}
|
||||
}
|
Reference in New Issue
Block a user