mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 10:53:05 +00:00
Compare commits
223 Commits
4.0.0-BETA
...
4.0.8
Author | SHA1 | Date | |
---|---|---|---|
8a4bc72b34 | |||
32b07e0940 | |||
99f087e5e1 | |||
aaf7a88de7 | |||
67a0ae0246 | |||
b3fec3d86f | |||
60ef2db892 | |||
e21446e583 | |||
7bf0bc2ca7 | |||
e5a9123522 | |||
09201ac14b | |||
0697c7d316 | |||
1eae133118 | |||
d28be4eaf2 | |||
b33a75a6d1 | |||
f9c8c0e34d | |||
58ba4f680f | |||
8c5cc67e07 | |||
ab8b24bcd2 | |||
94c4f58667 | |||
c10eda5eae | |||
ed312863a7 | |||
387c13beff | |||
56fe71d939 | |||
73d8c87b76 | |||
061d851fbd | |||
a67aef0477 | |||
858024afb7 | |||
eaaf00ca2b | |||
f1723acfd3 | |||
8da27ea0aa | |||
388622d55d | |||
bac6a2a1eb | |||
b9b76eaed2 | |||
9f4fcfafdb | |||
6679c53e56 | |||
9d061e86af | |||
f7d25f251e | |||
0ccb47fb07 | |||
0973472842 | |||
f126479c37 | |||
d34f4b28b3 | |||
8a65fd273a | |||
248cc0ef49 | |||
d1726aa20c | |||
58e1e7bd6f | |||
0a5b146189 | |||
b4e1871899 | |||
78eaa0993d | |||
bee2aba813 | |||
af81f80cf3 | |||
43c5d08042 | |||
6d249026cc | |||
ed2145b6a4 | |||
3e6c157217 | |||
86beeb8255 | |||
230a3c9839 | |||
35f205b476 | |||
e7d17eb4d3 | |||
73168a0e39 | |||
e8893dd91f | |||
a4af1609ea | |||
8c4b8a9042 | |||
6492cac5c1 | |||
958a9dbf0f | |||
3ed57ce49a | |||
68f3399cfd | |||
aeab19a616 | |||
7bee72ef2d | |||
0d595e4324 | |||
e43e0189df | |||
decd1da2d0 | |||
bcc0f1e733 | |||
f62cfe8ae3 | |||
b903e90dc2 | |||
c8247786d7 | |||
f486b5f4a7 | |||
54d6b83fc2 | |||
eedea38669 | |||
3c6146b5e0 | |||
72f2c794ab | |||
38b6b39cb3 | |||
fcc4757209 | |||
d9c70cb176 | |||
4aab0565c0 | |||
8943d8a2a7 | |||
0da29beb1d | |||
157048264c | |||
b55aa78aec | |||
091673d8f1 | |||
e3614d1a82 | |||
93caf72f34 | |||
e6e1bca676 | |||
795ebd1824 | |||
5f03887b47 | |||
9979a64ad2 | |||
75a72786f9 | |||
3d205c6e5f | |||
2955a92837 | |||
e70f81a111 | |||
57e1509c3a | |||
0da1810aaa | |||
4d37b79ff7 | |||
ea1fceece2 | |||
7fb1669c6d | |||
929abb04be | |||
a09817864b | |||
45c4a9673d | |||
4ad8cb02a5 | |||
7e6bbcc393 | |||
c334e6dec7 | |||
89a766b799 | |||
7e99e5167c | |||
f5bbd30dbb | |||
3be8472ae2 | |||
22bb1ce8e0 | |||
178dcb71a9 | |||
0a58fd5472 | |||
e06eefeab0 | |||
ede07c4314 | |||
cba00bf1e2 | |||
e81bee3866 | |||
e6b85988b2 | |||
b50591303b | |||
448f26cefc | |||
fa48100da5 | |||
bcf8a3424c | |||
69d5bfa0d4 | |||
549fb923bf | |||
6d5c463bdd | |||
911ad344c9 | |||
3b77462935 | |||
6b40ed7bf8 | |||
1ed9302f5a | |||
b3dab0beef | |||
6ddaed97fa | |||
036b90d247 | |||
d909cd8a91 | |||
06eaf9f273 | |||
1e56ed2ea3 | |||
dccb8a3595 | |||
0ace807756 | |||
40895a86e5 | |||
b081394125 | |||
f48cf68cac | |||
264cff70ec | |||
3aabfa4ab0 | |||
0793e7e094 | |||
3d9e19546f | |||
e0eeb87ea0 | |||
78ffad5ffc | |||
5a351d3b17 | |||
0c012ca5d9 | |||
0530cb72df | |||
ee060f3e02 | |||
e7deffa9af | |||
6e4b73c183 | |||
62f150586f | |||
8ed9551ac9 | |||
4d2d0f1d35 | |||
cac9db9bcc | |||
300d194185 | |||
13340a21d3 | |||
27f599793a | |||
527e975fa9 | |||
8e37f86480 | |||
8e8cee45b8 | |||
1a046c6cd5 | |||
e61aaaccca | |||
1b86355c40 | |||
1669d33f7e | |||
2da65c5a6e | |||
468faa464b | |||
59de045ecb | |||
bd8308cc6f | |||
edc3bae172 | |||
06e7030817 | |||
cb0af44ccb | |||
d535f02096 | |||
7665f4f443 | |||
20d6b69813 | |||
6b7d0307af | |||
baeac2eb07 | |||
2850ea1e89 | |||
d560cf17fc | |||
3f6efd0018 | |||
aea124af74 | |||
8620e67d88 | |||
d5f81fe261 | |||
0aeac3af7d | |||
9931c1d50a | |||
d21a3d8750 | |||
6d62b06ce6 | |||
8be92d16fe | |||
8079ae341a | |||
ba295dc7dc | |||
38325c8573 | |||
f239b077b9 | |||
6f8f460a6c | |||
882df94bcb | |||
4a8ca603a1 | |||
52f0c4f3ed | |||
e2815eed60 | |||
932a88764c | |||
9540193766 | |||
cc23e0b7a1 | |||
1f9400f901 | |||
e5149756a8 | |||
bc18969a09 | |||
c19174a174 | |||
f95142f6b6 | |||
7ace24caab | |||
32f619ac49 | |||
1bb6ac4fb6 | |||
533d3aae8b | |||
52a891ba73 | |||
71b813d4f9 | |||
f2540a72ad | |||
03f13495b7 | |||
7e0f6c02a1 | |||
1bc7869f6e | |||
5556861000 | |||
7dd5d0b593 |
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
|
||||
|
11
.github/workflows/draft-release.yml
vendored
11
.github/workflows/draft-release.yml
vendored
@ -35,17 +35,18 @@ jobs:
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
|
||||
|
||||
- name: Patch VersionInfo
|
||||
- name: Calculate build number
|
||||
id: build-number
|
||||
run: |
|
||||
BUILD_NUMBER=2000+$GITHUB_RUN_NUMBER #to stay above jenkins
|
||||
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
|
||||
echo "Build number: $BUILD_NUMBER"
|
||||
sed -i "s/const BUILD_NUMBER = 0/const BUILD_NUMBER = ${BUILD_NUMBER}/" src/VersionInfo.php
|
||||
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
|
||||
|
||||
- name: Minify BedrockData JSON files
|
||||
run: php vendor/pocketmine/bedrock-data/.minify_json.php
|
||||
|
||||
- name: Build PocketMine-MP.phar
|
||||
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }}
|
||||
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }} --build ${{ steps.build-number.outputs.BUILD_NUMBER }}
|
||||
|
||||
- name: Get PocketMine-MP release version
|
||||
id: get-pm-version
|
||||
@ -56,7 +57,7 @@ jobs:
|
||||
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
|
||||
|
||||
- name: Generate build info
|
||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} > build_info.json
|
||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} > build_info.json
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
|
12
.github/workflows/main.yml
vendored
12
.github/workflows/main.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.14]
|
||||
|
||||
steps:
|
||||
- name: Build and prepare PHP cache
|
||||
@ -31,7 +31,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -69,7 +69,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -107,7 +107,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -147,7 +147,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.11]
|
||||
php: [8.0.14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -201,4 +201,4 @@ jobs:
|
||||
tools: php-cs-fixer:3.2
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff
|
||||
run: php-cs-fixer fix --dry-run --diff --ansi
|
||||
|
@ -18,6 +18,9 @@ return (new PhpCsFixer\Config)
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short'
|
||||
],
|
||||
'binary_operator_spaces' => [
|
||||
'default' => 'single_space'
|
||||
],
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_statement' => [
|
||||
@ -33,6 +36,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,
|
||||
@ -68,8 +72,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")
|
||||
|
@ -4,10 +4,13 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" />
|
||||
<img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver">
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/actions/workflows/main.yml"><img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" /></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver"></a>
|
||||
<a href="https://hub.docker.com/r/pmmp/pocketmine-mp"><img src="https://img.shields.io/docker/v/pmmp/pocketmine-mp?logo=docker&label=image" alt="Docker image version (latest semver)" /></a>
|
||||
<a href="https://discord.gg/bmSAZBG"><img src="https://img.shields.io/discord/373199722573201408?label=discord&color=7289DA&logo=discord" alt="Discord" /></a>
|
||||
<br>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases"><img alt="GitHub all releases" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/total?label=downloads%40total"></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest by SemVer)" src="https://img.shields.io/github/downloads/pmmp/PocketMine-MP/latest/total?sort=semver"></a>
|
||||
</p>
|
||||
|
||||
## Getting started
|
||||
|
@ -23,15 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 4){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)>");
|
||||
if(count($argv) !== 5){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number>");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
|
||||
"base_version" => \pocketmine\VersionInfo::BASE_VERSION,
|
||||
"build" => \pocketmine\VersionInfo::BUILD_NUMBER,
|
||||
"build" => (int) $argv[4],
|
||||
"is_dev" => \pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"channel" => \pocketmine\VersionInfo::BUILD_CHANNEL,
|
||||
"git_commit" => $argv[1],
|
||||
|
@ -41,6 +41,7 @@ use function preg_match_all;
|
||||
use function str_replace;
|
||||
use function strtoupper;
|
||||
use const INI_SCANNER_RAW;
|
||||
use const SORT_NUMERIC;
|
||||
use const SORT_STRING;
|
||||
use const STDERR;
|
||||
|
||||
@ -95,6 +96,8 @@ function generate_known_translation_keys(array $languageDefinitions) : void{
|
||||
/**
|
||||
* This class contains constants for all the translations known to PocketMine-MP as per the used version of pmmp/Language.
|
||||
* This class is generated automatically, do NOT modify it by hand.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class KnownTranslationKeys{
|
||||
|
||||
@ -127,6 +130,8 @@ function generate_known_translation_factory(array $languageDefinitions) : void{
|
||||
* This class contains factory methods for all the translations known to PocketMine-MP as per the used version of
|
||||
* pmmp/Language.
|
||||
* This class is generated automatically, do NOT modify it by hand.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class KnownTranslationFactory{
|
||||
|
||||
@ -138,15 +143,20 @@ HEADER;
|
||||
$translationContainerClass = (new \ReflectionClass(Translatable::class))->getShortName();
|
||||
foreach(Utils::stringifyKeys($languageDefinitions) as $key => $value){
|
||||
$parameters = [];
|
||||
$allParametersPositional = true;
|
||||
if(preg_match_all($parameterRegex, $value, $matches) > 0){
|
||||
foreach($matches[1] as $parameterName){
|
||||
if(is_numeric($parameterName)){
|
||||
$parameters[$parameterName] = "param$parameterName";
|
||||
}else{
|
||||
$parameters[$parameterName] = $parameterName;
|
||||
$allParametersPositional = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($allParametersPositional){
|
||||
ksort($parameters, SORT_NUMERIC);
|
||||
}
|
||||
echo "\tpublic static function " .
|
||||
functionify($key) .
|
||||
"(" . implode(", ", array_map(fn(string $paramName) => "$translationContainerClass|string \$$paramName", $parameters)) . ") : $translationContainerClass{\n";
|
||||
|
@ -116,4 +116,3 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
|
||||
echo "No changes made to file $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,14 @@ const ACCEPTED_OPTS = [
|
||||
"channel" => "Release channel to post this build into"
|
||||
];
|
||||
|
||||
function systemWrapper(string $command, string $errorMessage) : void{
|
||||
system($command, $result);
|
||||
if($result !== 0){
|
||||
echo "error: $errorMessage; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function main() : void{
|
||||
$filteredOpts = [];
|
||||
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help"])) as $optName => $optValue){
|
||||
@ -115,7 +123,7 @@ function main() : void{
|
||||
echo "$currentVer will be published on release channel \"$channel\".\n";
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
system('git add "' . dirname(__DIR__) . '/changelogs"');
|
||||
systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes");
|
||||
system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result);
|
||||
if($result === 0){
|
||||
echo "error: no changelog changes detected; aborting\n";
|
||||
@ -123,14 +131,15 @@ function main() : void{
|
||||
}
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
|
||||
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
|
||||
system('git tag ' . $currentVer->getBaseVersion());
|
||||
systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit");
|
||||
systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag");
|
||||
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
|
||||
system('git add "' . $versionInfoPath . '"');
|
||||
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
|
||||
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
|
||||
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
sleep(5);
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
systemWrapper('git push origin HEAD ' . $currentVer->getBaseVersion(), "failed to push changes to remote");
|
||||
}
|
||||
|
||||
main();
|
||||
|
Submodule build/php updated: 7a2ab5b922...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,
|
||||
@ -150,7 +155,8 @@ function main() : void{
|
||||
'vendor'
|
||||
],
|
||||
[
|
||||
'git' => $gitHash
|
||||
'git' => $gitHash,
|
||||
'build' => $build
|
||||
],
|
||||
<<<'STUB'
|
||||
<?php
|
||||
|
@ -28,3 +28,11 @@ Plugin developers should **only** update their required API to this version if y
|
||||
|
||||
# 3.25.4
|
||||
- Fixed a long-standing issue with `Player->removeWindow()` breaking inventory UIs on the client.
|
||||
|
||||
# 3.25.5
|
||||
- Protocol: Fixed incorrect encoding in `StructureSettings`
|
||||
- Fixed reading tags from non-docblock comments in script plugins.
|
||||
- Build number is now defined in phar metadata instead of being patched into the source code directly.
|
||||
|
||||
# 3.25.6
|
||||
- Fixed borked build number in release build of 3.25.5.
|
||||
|
32
changelogs/3.26.md
Normal file
32
changelogs/3.26.md
Normal file
@ -0,0 +1,32 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.26.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.18.0.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
# 3.26.1
|
||||
- Fixed a bug in chunk sending that caused double chests to not be paired, signs to be blank, and various other issues.
|
||||
|
||||
# 3.26.2
|
||||
- Improved error messages shown by `start.cmd`, `start.sh` and `start.ps1` when the PHP binary was not found.
|
||||
- The value of PHPRC is now shown when erroring out due to unsatisfied PHP requirements.
|
||||
- Removed restriction on the range of valid channels for `auto-updater.channel` in `pocketmine.yml`.
|
||||
|
||||
# 3.26.3
|
||||
- `PlayerExperienceChangeEvent->setNewProgress()` now performs range checks. This fixes the root of a very old and confusing crash bug which took several years to identify the cause of.
|
||||
- Note that the defective plugin(s) which caused this problem will still cause a server crash, but the plugin responsible will now get blamed correctly.
|
||||
|
||||
# 3.26.4
|
||||
- Fixed skins appearing black when using RTX resource packs.
|
||||
- Fixed chunks containing furnaces in old worlds (pre-2017) being discarded as corrupted.
|
||||
- This was caused by a strict corruption check detecting bad data created by a bug in PocketMine-MP that was fixed in 2017.
|
||||
|
||||
# 3.26.5
|
||||
- Fixed several denial-of-service attack vectors related to writable book text length and encoding.
|
||||
- Fixed several denial-of-service attack vectors related to skin data field lengths.
|
15
changelogs/3.27.md
Normal file
15
changelogs/3.27.md
Normal file
@ -0,0 +1,15 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.27.0
|
||||
- Introduced support for protocol encryption.
|
||||
- Encryption is enabled by default.
|
||||
- Fixes login replay attacks.
|
||||
- This may cause some performance degradation.
|
||||
- Encryption can be disabled by setting `network.enable-encryption` to `false` in `pocketmine.yml`. DO NOT do this unless you understand the risks involved.
|
||||
- An obsoletion notice has been added to the console during server startup.
|
1767
changelogs/4.0-beta.md
Normal file
1767
changelogs/4.0-beta.md
Normal file
File diff suppressed because it is too large
Load Diff
1139
changelogs/4.0.md
1139
changelogs/4.0.md
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php-64bit": "*",
|
||||
"ext-chunkutils2": "^0.3.0",
|
||||
"ext-chunkutils2": "^0.3.1",
|
||||
"ext-crypto": "^0.3.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
@ -34,18 +34,18 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-data": "^1.4.0+bedrock-1.17.40",
|
||||
"pocketmine/bedrock-protocol": "^6.0.0+bedrock-1.17.40",
|
||||
"pocketmine/bedrock-data": "~1.5.0+bedrock-1.18.0",
|
||||
"pocketmine/bedrock-protocol": "~7.1.0+bedrock-1.18.0",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/errorhandler": "^0.3.0",
|
||||
"pocketmine/locale-data": "^2.0.16",
|
||||
"pocketmine/locale-data": "~2.3.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.4.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
"pocketmine/nbt": "^0.3.0",
|
||||
"pocketmine/nbt": "^0.3.2",
|
||||
"pocketmine/raklib": "^0.14.2",
|
||||
"pocketmine/raklib-ipc": "^0.1.0",
|
||||
"pocketmine/snooze": "^0.3.0",
|
||||
@ -53,7 +53,7 @@
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.2.0",
|
||||
"phpstan/phpstan": "1.3.3",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
@ -79,7 +79,7 @@
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/DevTools/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
|
||||
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
|
||||
"make-server": [
|
||||
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
|
||||
"@php -dphar.readonly=0 build/server-phar.php"
|
||||
|
260
composer.lock
generated
260
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": "fb545e4c8e17b0b07e8e20986b64e5a6",
|
||||
"content-hash": "055bec16a9bbc839f4410d09b09f2703",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -123,24 +123,24 @@
|
||||
},
|
||||
{
|
||||
"name": "fgrosse/phpasn1",
|
||||
"version": "v2.3.0",
|
||||
"version": "v2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fgrosse/PHPASN1.git",
|
||||
"reference": "20299033c35f4300eb656e7e8e88cf52d1d6694e"
|
||||
"reference": "eef488991d53e58e60c9554b09b1201ca5ba9296"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/20299033c35f4300eb656e7e8e88cf52d1d6694e",
|
||||
"reference": "20299033c35f4300eb656e7e8e88cf52d1d6694e",
|
||||
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296",
|
||||
"reference": "eef488991d53e58e60c9554b09b1201ca5ba9296",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0"
|
||||
"php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~6.3",
|
||||
"satooshi/php-coveralls": "~2.0"
|
||||
"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",
|
||||
@ -192,9 +192,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/fgrosse/PHPASN1/issues",
|
||||
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.3.0"
|
||||
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0"
|
||||
},
|
||||
"time": "2021-04-24T19:01:55+00:00"
|
||||
"time": "2021-12-11T12:41:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "netresearch/jsonmapper",
|
||||
@ -249,16 +249,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "1.4.0+bedrock-1.17.40",
|
||||
"version": "1.5.0+bedrock-1.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774"
|
||||
"reference": "482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/f29b7be8fa3046d2ee4c6421485b97b3f5b07774",
|
||||
"reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c",
|
||||
"reference": "482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -269,22 +269,22 @@
|
||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.17.40"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.0"
|
||||
},
|
||||
"time": "2021-10-19T16:55:41+00:00"
|
||||
"time": "2021-11-30T18:30:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "6.0.0+bedrock-1.17.40",
|
||||
"version": "7.1.0+bedrock-1.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "906bafec4fc41f548749ce01d120902b25c1bbfe"
|
||||
"reference": "42f2a00634c17c346fd98c05b2daf29d1fbf5805"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/906bafec4fc41f548749ce01d120902b25c1bbfe",
|
||||
"reference": "906bafec4fc41f548749ce01d120902b25c1bbfe",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/42f2a00634c17c346fd98c05b2daf29d1fbf5805",
|
||||
"reference": "42f2a00634c17c346fd98c05b2daf29d1fbf5805",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -316,22 +316,22 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/6.0.0+bedrock-1.17.40"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/7.1.0+bedrock-1.18.0"
|
||||
},
|
||||
"time": "2021-11-21T20:56:18+00:00"
|
||||
"time": "2021-12-15T03:07:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9"
|
||||
"reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9",
|
||||
"reference": "f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
|
||||
"reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -340,8 +340,10 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
"phpstan/phpstan": "1.3.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -356,9 +358,9 @@
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.2"
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.4"
|
||||
},
|
||||
"time": "2021-10-22T19:54:16+00:00"
|
||||
"time": "2022-01-12T18:06:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/callback-validator",
|
||||
@ -533,16 +535,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "2.0.17",
|
||||
"version": "2.3.33",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "30e4a64d5674bac556c4e2b9842b19a981471ac4"
|
||||
"reference": "44998ca9c055f872a33e59cd4d2736d081ba84b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/30e4a64d5674bac556c4e2b9842b19a981471ac4",
|
||||
"reference": "30e4a64d5674bac556c4e2b9842b19a981471ac4",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/44998ca9c055f872a33e59cd4d2736d081ba84b5",
|
||||
"reference": "44998ca9c055f872a33e59cd4d2736d081ba84b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -550,9 +552,9 @@
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/2.0.17"
|
||||
"source": "https://github.com/pmmp/Language/tree/2.3.33"
|
||||
},
|
||||
"time": "2021-11-12T00:59:39+00:00"
|
||||
"time": "2022-01-16T22:08:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
@ -641,16 +643,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "6d64e2555bd2e95ed024574f75d1cefc135c89fc"
|
||||
"reference": "aacc3759a508a69dfa5bc4dfa770ab733c5c94bf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/6d64e2555bd2e95ed024574f75d1cefc135c89fc",
|
||||
"reference": "6d64e2555bd2e95ed024574f75d1cefc135c89fc",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/aacc3759a508a69dfa5bc4dfa770ab733c5c94bf",
|
||||
"reference": "aacc3759a508a69dfa5bc4dfa770ab733c5c94bf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -658,10 +660,10 @@
|
||||
"php-64bit": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"irstea/phpunit-shim": "^8.5 || ^9.5",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
"phpstan/phpstan": "1.2.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^8.5 || ^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -676,22 +678,22 @@
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Math/issues",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.4.0"
|
||||
"source": "https://github.com/pmmp/Math/tree/0.4.2"
|
||||
},
|
||||
"time": "2021-10-29T20:33:10+00:00"
|
||||
"time": "2021-12-05T01:15:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "98c4a04b55a915e18f83d3b0c9beb24a71abcd31"
|
||||
"reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/98c4a04b55a915e18f83d3b0c9beb24a71abcd31",
|
||||
"reference": "98c4a04b55a915e18f83d3b0c9beb24a71abcd31",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/3e0d9ef6b6c5fb45e3745a121296e75631b3eefe",
|
||||
"reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -700,10 +702,10 @@
|
||||
"pocketmine/binaryutils": "^0.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"irstea/phpunit-shim": "^9.5",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.85",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
"phpstan/phpstan": "1.2.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -718,22 +720,22 @@
|
||||
"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.3.0"
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.3.2"
|
||||
},
|
||||
"time": "2021-05-18T15:46:33+00:00"
|
||||
"time": "2021-12-16T01:02:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.14.2",
|
||||
"version": "0.14.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "e3a861187470e1facc6625040128f447ebbcbaec"
|
||||
"reference": "4798576fec0364266dce23b368a7fec5e5de7927"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/e3a861187470e1facc6625040128f447ebbcbaec",
|
||||
"reference": "e3a861187470e1facc6625040128f447ebbcbaec",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/4798576fec0364266dce23b368a7fec5e5de7927",
|
||||
"reference": "4798576fec0364266dce23b368a7fec5e5de7927",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -745,8 +747,8 @@
|
||||
"pocketmine/log": "^0.3.0 || ^0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2"
|
||||
"phpstan/phpstan": "1.3.3",
|
||||
"phpstan/phpstan-strict-rules": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -761,9 +763,9 @@
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.14.2"
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.14.3"
|
||||
},
|
||||
"time": "2021-10-04T20:39:11+00:00"
|
||||
"time": "2022-01-10T21:29:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib-ipc",
|
||||
@ -1025,21 +1027,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"
|
||||
},
|
||||
@ -1084,7 +1089,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": [
|
||||
{
|
||||
@ -1100,20 +1105,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.23.1",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
|
||||
"reference": "57b712b08eddb97c762a8caa32c84e037892d2e9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
|
||||
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9",
|
||||
"reference": "57b712b08eddb97c762a8caa32c84e037892d2e9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1167,7 +1172,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1183,20 +1188,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-28T13:41:28+00:00"
|
||||
"time": "2021-09-13T13:58:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.23.0",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "e66119f3de95efc359483f810c4c3e6436279436"
|
||||
"reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436",
|
||||
"reference": "e66119f3de95efc359483f810c4c3e6436279436",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
|
||||
"reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1246,7 +1251,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0"
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.24.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1262,7 +1267,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-21T13:25:03+00:00"
|
||||
"time": "2021-09-13T13:58:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
@ -1461,9 +1466,6 @@
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.6",
|
||||
@ -1504,16 +1506,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.13.1",
|
||||
"version": "v4.13.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd"
|
||||
"reference": "210577fe3cf7badcc5814d99455df46564f3c077"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd",
|
||||
"reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
|
||||
"reference": "210577fe3cf7badcc5814d99455df46564f3c077",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1554,9 +1556,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
|
||||
},
|
||||
"time": "2021-11-03T20:52:16+00:00"
|
||||
"time": "2021-11-30T19:35:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1781,16 +1783,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": {
|
||||
@ -1825,22 +1827,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": {
|
||||
@ -1892,22 +1894,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.2.0",
|
||||
"version": "1.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "cbe085f9fdead5b6d62e4c022ca52dc9427a10ee"
|
||||
"reference": "151a51f6149855785fbd883e79768c0abc96b75f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cbe085f9fdead5b6d62e4c022ca52dc9427a10ee",
|
||||
"reference": "cbe085f9fdead5b6d62e4c022ca52dc9427a10ee",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/151a51f6149855785fbd883e79768c0abc96b75f",
|
||||
"reference": "151a51f6149855785fbd883e79768c0abc96b75f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1923,7 +1925,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2-dev"
|
||||
"dev-master": "1.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1938,7 +1940,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.2.0"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1958,7 +1960,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-18T14:09:01+00:00"
|
||||
"time": "2022-01-07T09:49:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
@ -2068,16 +2070,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.9",
|
||||
"version": "9.2.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "f301eb1453c9e7a1bc912ee8b0ea9db22c60223b"
|
||||
"reference": "d5850aaf931743067f4bfc1ae4cbd06468400687"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f301eb1453c9e7a1bc912ee8b0ea9db22c60223b",
|
||||
"reference": "f301eb1453c9e7a1bc912ee8b0ea9db22c60223b",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687",
|
||||
"reference": "d5850aaf931743067f4bfc1ae4cbd06468400687",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2133,7 +2135,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.9"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2141,20 +2143,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-19T15:21:02+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": {
|
||||
@ -2193,7 +2195,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": [
|
||||
{
|
||||
@ -2201,7 +2203,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-28T05:57:25+00:00"
|
||||
"time": "2021-12-02T12:48:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-invoker",
|
||||
@ -2386,16 +2388,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.10",
|
||||
"version": "9.5.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
|
||||
"reference": "597cb647654ede35e43b137926dfdfef0fb11743"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
|
||||
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/597cb647654ede35e43b137926dfdfef0fb11743",
|
||||
"reference": "597cb647654ede35e43b137926dfdfef0fb11743",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2473,11 +2475,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.13"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://phpunit.de/donate.html",
|
||||
"url": "https://phpunit.de/sponsors.html",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
@ -2485,7 +2487,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-25T07:38:51+00:00"
|
||||
"time": "2022-01-24T07:33:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -3510,7 +3512,7 @@
|
||||
"platform": {
|
||||
"php": "^8.0",
|
||||
"php-64bit": "*",
|
||||
"ext-chunkutils2": "^0.3.0",
|
||||
"ext-chunkutils2": "^0.3.1",
|
||||
"ext-crypto": "^0.3.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
@ -3539,5 +3541,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.0.0"
|
||||
},
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
@ -325,6 +325,9 @@ class MemoryManager{
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
if(!$property->isInitialized()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$staticCount++;
|
||||
$staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
@ -448,6 +451,9 @@ class MemoryManager{
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
if(!$property->isInitialized($object)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
@ -213,6 +213,8 @@ JIT_WARNING
|
||||
}
|
||||
critical_error("PHP binary used: " . $binary);
|
||||
critical_error("Loaded php.ini: " . (($file = php_ini_loaded_file()) !== false ? $file : "none"));
|
||||
$phprc = getenv("PHPRC");
|
||||
critical_error("Value of PHPRC environment variable: " . ($phprc === false ? "" : $phprc));
|
||||
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
|
||||
echo PHP_EOL;
|
||||
exit(1);
|
||||
|
@ -537,9 +537,13 @@ class Server{
|
||||
if(!$ev->isCancelled()){
|
||||
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$contents = zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP);
|
||||
if($contents === false){
|
||||
throw new AssumptionFailedError("zlib_encode() failed unexpectedly");
|
||||
}
|
||||
try{
|
||||
file_put_contents($this->getPlayerDataPath($name), zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP));
|
||||
}catch(\ErrorException $e){
|
||||
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
|
||||
}catch(\RuntimeException | \ErrorException $e){
|
||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
@ -690,7 +694,13 @@ class Server{
|
||||
}
|
||||
|
||||
public function removeOp(string $name) : void{
|
||||
$this->operators->remove(strtolower($name));
|
||||
$lowercaseName = strtolower($name);
|
||||
foreach($this->operators->getAll() as $operatorName => $_){
|
||||
$operatorName = (string) $operatorName;
|
||||
if($lowercaseName === strtolower($operatorName)){
|
||||
$this->operators->remove($operatorName);
|
||||
}
|
||||
}
|
||||
|
||||
if(($player = $this->getPlayerExact($name)) !== null){
|
||||
$player->unsetBasePermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
@ -1143,7 +1153,9 @@ class Server{
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
|
||||
if($rakLibRegistered){
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
|
||||
}
|
||||
if($useQuery){
|
||||
if(!$rakLibRegistered){
|
||||
//RakLib would normally handle the transport for Query packets
|
||||
@ -1413,6 +1425,9 @@ class Server{
|
||||
echo "\x1b]0;\x07";
|
||||
}
|
||||
|
||||
if($this->isRunning){
|
||||
$this->logger->emergency("Forcing server shutdown");
|
||||
}
|
||||
try{
|
||||
if(!$this->isRunning()){
|
||||
$this->sendUsage(SendUsageTask::TYPE_CLOSE);
|
||||
|
@ -25,14 +25,15 @@ namespace pocketmine;
|
||||
|
||||
use pocketmine\utils\Git;
|
||||
use pocketmine\utils\VersionString;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.0.0-BETA13";
|
||||
public const BASE_VERSION = "4.0.8";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_NUMBER = 0;
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
@ -61,12 +62,29 @@ final class VersionInfo{
|
||||
return self::$gitHash;
|
||||
}
|
||||
|
||||
private static ?int $buildNumber = null;
|
||||
|
||||
public static function BUILD_NUMBER() : int{
|
||||
if(self::$buildNumber === null){
|
||||
self::$buildNumber = 0;
|
||||
if(\Phar::running(true) !== ""){
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){
|
||||
self::$buildNumber = $meta["build"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$buildNumber;
|
||||
}
|
||||
|
||||
/** @var VersionString|null */
|
||||
private static $fullVersion = null;
|
||||
|
||||
public static function VERSION() : VersionString{
|
||||
if(self::$fullVersion === null){
|
||||
self::$fullVersion = new VersionString(self::BASE_VERSION, self::IS_DEVELOPMENT_BUILD, self::BUILD_NUMBER);
|
||||
self::$fullVersion = new VersionString(self::BASE_VERSION, self::IS_DEVELOPMENT_BUILD, self::BUILD_NUMBER());
|
||||
}
|
||||
return self::$fullVersion;
|
||||
}
|
||||
|
@ -37,8 +37,6 @@ use function assert;
|
||||
use function strlen;
|
||||
|
||||
abstract class BaseSign extends Transparent{
|
||||
//TODO: conditionally useless properties, find a way to fix
|
||||
|
||||
protected SignText $text;
|
||||
protected ?int $editorEntityRuntimeId = null;
|
||||
|
||||
|
@ -145,7 +145,7 @@ class Bed extends Transparent{
|
||||
$isNight = ($time >= World::TIME_NIGHT and $time < World::TIME_SUNRISE);
|
||||
|
||||
if(!$isNight){
|
||||
$player->sendMessage(KnownTranslationFactory::tile_bed_tooFar()->prefix(TextFormat::GRAY));
|
||||
$player->sendMessage(KnownTranslationFactory::tile_bed_noSleep()->prefix(TextFormat::GRAY));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Tile;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class BlockIdentifier{
|
||||
|
||||
@ -40,6 +41,10 @@ class BlockIdentifier{
|
||||
$this->blockId = $blockId;
|
||||
$this->variant = $variant;
|
||||
$this->itemId = $itemId;
|
||||
|
||||
if($tileClass !== null){
|
||||
Utils::testValidInstance($tileClass, Tile::class);
|
||||
}
|
||||
$this->tileClass = $tileClass;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,9 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
|
||||
use pocketmine\block\utils\BrewingStandSlot;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function array_key_exists;
|
||||
@ -67,6 +70,19 @@ class BrewingStand extends Transparent{
|
||||
return 0b111;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
//bottom slab part - in PC this is also inset on X/Z by 1/16, but Bedrock sucks
|
||||
AxisAlignedBB::one()->trim(Facing::UP, 7 / 8),
|
||||
|
||||
//center post
|
||||
AxisAlignedBB::one()
|
||||
->squash(Axis::X, 7 / 16)
|
||||
->squash(Axis::Z, 7 / 16)
|
||||
->trim(Facing::UP, 1 / 8)
|
||||
];
|
||||
}
|
||||
|
||||
public function hasSlot(BrewingStandSlot $slot) : bool{
|
||||
return array_key_exists($slot->id(), $this->slots);
|
||||
}
|
||||
|
@ -66,4 +66,8 @@ class EnderChest extends Transparent{
|
||||
VanillaBlocks::OBSIDIAN()->asItem()->setCount(8)
|
||||
];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\PillarRotationInMetadataTrait;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class HayBale extends Opaque{
|
||||
use PillarRotationInMetadataTrait;
|
||||
@ -35,4 +36,9 @@ class HayBale extends Opaque{
|
||||
public function getFlammability() : int{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public function onEntityLand(Entity $entity) : ?float{
|
||||
$entity->fallDistance *= 0.2;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,4 @@ final class CraftingTableInventory extends CraftingGrid implements BlockInventor
|
||||
$this->holder = $holder;
|
||||
parent::__construct(CraftingGrid::SIZE_BIG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,11 +48,14 @@ trait ContainerTrait{
|
||||
$inventory = $this->getRealInventory();
|
||||
$listeners = $inventory->getListeners()->toArray();
|
||||
$inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
|
||||
$inventory->clearAll();
|
||||
|
||||
$newContents = [];
|
||||
/** @var CompoundTag $itemNBT */
|
||||
foreach($inventoryTag as $itemNBT){
|
||||
$inventory->setItem($itemNBT->getByte("Slot"), Item::nbtDeserialize($itemNBT));
|
||||
$newContents[$itemNBT->getByte("Slot")] = Item::nbtDeserialize($itemNBT);
|
||||
}
|
||||
$inventory->setContents($newContents);
|
||||
|
||||
$inventory->getListeners()->add(...$listeners);
|
||||
}
|
||||
|
||||
|
@ -206,7 +206,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
foreach($matches[0] as $k => $_){
|
||||
for($i = 1; $i <= 2; ++$i){
|
||||
if($matches[$i][$k] !== ""){
|
||||
$args[$k] = stripslashes($matches[$i][$k]);
|
||||
$args[$k] = $i === 1 ? stripslashes($matches[$i][$k]) : $matches[$i][$k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class KickCommand extends VanillaCommand{
|
||||
$reason = trim(implode(" ", $args));
|
||||
|
||||
if(($player = $sender->getServer()->getPlayerByPrefix($name)) instanceof Player){
|
||||
$player->kick("Kicked by admin." . ($reason !== "" ? "Reason: " . $reason : ""));
|
||||
$player->kick("Kicked by admin." . ($reason !== "" ? " Reason: " . $reason : ""));
|
||||
if($reason !== ""){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kick_success_reason($player->getName(), $reason));
|
||||
}else{
|
||||
|
@ -27,14 +27,24 @@ use pocketmine\utils\AssumptionFailedError;
|
||||
use function fclose;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function is_resource;
|
||||
use function stream_select;
|
||||
use function trim;
|
||||
use function usleep;
|
||||
|
||||
final class ConsoleReader{
|
||||
/** @var resource */
|
||||
private $stdin;
|
||||
|
||||
public function __construct(){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
private function initStdin() : void{
|
||||
if(is_resource($this->stdin)){
|
||||
fclose($this->stdin);
|
||||
}
|
||||
|
||||
$stdin = fopen("php://stdin", "r");
|
||||
if($stdin === false) throw new AssumptionFailedError("Opening stdin should never fail");
|
||||
$this->stdin = $stdin;
|
||||
@ -42,19 +52,23 @@ final class ConsoleReader{
|
||||
|
||||
/**
|
||||
* Reads a line from the console and adds it to the buffer. This method may block the thread.
|
||||
* @throws ConsoleReaderException
|
||||
*/
|
||||
public function readLine() : ?string{
|
||||
if(!is_resource($this->stdin)){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
$r = [$this->stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return null;
|
||||
}elseif($count === false){ //stream error
|
||||
throw new ConsoleReaderException("Unexpected EOF on select()");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(($raw = fgets($this->stdin)) === false){ //broken pipe or EOF
|
||||
throw new ConsoleReaderException("Unexpected EOF on fgets()");
|
||||
usleep(200000); //prevent CPU waste if it's end of pipe
|
||||
return null; //loop back round
|
||||
}
|
||||
|
||||
$line = trim($raw);
|
||||
|
@ -45,14 +45,7 @@ if($socket === false){
|
||||
}
|
||||
$consoleReader = new ConsoleReader();
|
||||
while(!feof($socket)){
|
||||
try{
|
||||
$line = $consoleReader->readLine();
|
||||
}catch(ConsoleReaderException $e){
|
||||
//Encountered unexpected EOF. This might be because the user did something stupid, or because the parent process
|
||||
//has died. In either case, commit suicide. If the parent process is still alive, it will start a new console
|
||||
//reader.
|
||||
break;
|
||||
}
|
||||
$line = $consoleReader->readLine();
|
||||
if(@fwrite($socket, ($line ?? "") . "\n") === false){
|
||||
//Always send even if there's no line, to check if the parent is alive
|
||||
//If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return
|
||||
|
@ -31,6 +31,7 @@ use function base64_encode;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function preg_replace;
|
||||
use function proc_close;
|
||||
use function proc_open;
|
||||
use function proc_terminate;
|
||||
use function sprintf;
|
||||
@ -86,7 +87,7 @@ final class ConsoleReaderThread extends Thread{
|
||||
//Windows sucks, and likes to corrupt UTF-8 file paths when they travel to the subprocess, so we base64 encode
|
||||
//the path to avoid the problem. This is an abysmally shitty hack, but here we are :(
|
||||
$sub = proc_open(
|
||||
[PHP_BINARY, '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address],
|
||||
[PHP_BINARY, '-dopcache.enable_cli=0', '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address],
|
||||
[
|
||||
2 => fopen("php://stderr", "w"),
|
||||
],
|
||||
@ -130,6 +131,7 @@ final class ConsoleReaderThread extends Thread{
|
||||
//gets stuck in a blocking fgets() read because stream_select() is a hunk of junk (hence the separate process in
|
||||
//the first place).
|
||||
proc_terminate($sub);
|
||||
proc_close($sub);
|
||||
stream_socket_shutdown($client, STREAM_SHUT_RDWR);
|
||||
}
|
||||
|
||||
|
@ -65,4 +65,4 @@ final class FurnaceRecipeManager{
|
||||
public function match(Item $input) : ?FurnaceRecipe{
|
||||
return $this->furnaceRecipes[$input->getId() . ":" . $input->getMeta()] ?? $this->furnaceRecipes[$input->getId() . ":?"] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ class CrashDump{
|
||||
$this->data->general = new CrashDumpDataGeneral(
|
||||
name: $this->server->getName(),
|
||||
base_version: VersionInfo::BASE_VERSION,
|
||||
build: VersionInfo::BUILD_NUMBER,
|
||||
build: VersionInfo::BUILD_NUMBER(),
|
||||
is_dev: VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
protocol: ProtocolInfo::CURRENT_PROTOCOL,
|
||||
git: VersionInfo::GIT_HASH(),
|
||||
|
@ -81,4 +81,4 @@ final class CrashDumpData implements \JsonSerializable{
|
||||
$result["server.properties"] = $this->serverDotProperties;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,4 +43,4 @@ final class CrashDumpDataGeneral{
|
||||
public string $os,
|
||||
public array $composer_libraries,
|
||||
){}
|
||||
}
|
||||
}
|
||||
|
@ -42,4 +42,4 @@ final class CrashDumpDataPluginEntry{
|
||||
public string $load,
|
||||
public string $website,
|
||||
){}
|
||||
}
|
||||
}
|
||||
|
@ -100,4 +100,4 @@ final class CrashDumpRenderer{
|
||||
public function addLine(string $line = "") : void{
|
||||
fwrite($this->fp, $line . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,4 @@ namespace pocketmine\data;
|
||||
|
||||
final class SavedDataLoadingException extends \RuntimeException{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,15 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\console;
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
final class ConsoleReaderException extends \RuntimeException{
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use Webmozart\PathUtil\Path;
|
||||
|
||||
}
|
||||
final class LegacyBiomeIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'biome_id_map.json'));
|
||||
}
|
||||
}
|
@ -832,10 +832,10 @@ abstract class Entity{
|
||||
$diffZ = $z - $floorZ;
|
||||
|
||||
if($world->getBlockAt($floorX, $floorY, $floorZ)->isSolid()){
|
||||
$westNonSolid = !$world->getBlockAt($floorX - 1, $floorY, $floorZ)->isSolid();
|
||||
$eastNonSolid = !$world->getBlockAt($floorX + 1, $floorY, $floorZ)->isSolid();
|
||||
$downNonSolid = !$world->getBlockAt($floorX, $floorY - 1, $floorZ)->isSolid();
|
||||
$upNonSolid = !$world->getBlockAt($floorX, $floorY + 1, $floorZ)->isSolid();
|
||||
$westNonSolid = !$world->getBlockAt($floorX - 1, $floorY, $floorZ)->isSolid();
|
||||
$eastNonSolid = !$world->getBlockAt($floorX + 1, $floorY, $floorZ)->isSolid();
|
||||
$downNonSolid = !$world->getBlockAt($floorX, $floorY - 1, $floorZ)->isSolid();
|
||||
$upNonSolid = !$world->getBlockAt($floorX, $floorY + 1, $floorZ)->isSolid();
|
||||
$northNonSolid = !$world->getBlockAt($floorX, $floorY, $floorZ - 1)->isSolid();
|
||||
$southNonSolid = !$world->getBlockAt($floorX, $floorY, $floorZ + 1)->isSolid();
|
||||
|
||||
@ -1589,8 +1589,8 @@ abstract class Entity{
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
$properties->setByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG, $this->alwaysShowNameTag ? 1 : 0);
|
||||
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->size->getHeight());
|
||||
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->size->getWidth());
|
||||
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->size->getHeight() / $this->scale);
|
||||
$properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->size->getWidth() / $this->scale);
|
||||
$properties->setFloat(EntityMetadataProperties::SCALE, $this->scale);
|
||||
$properties->setLong(EntityMetadataProperties::LEAD_HOLDER_EID, -1);
|
||||
$properties->setLong(EntityMetadataProperties::OWNER_EID, $this->ownerId ?? -1);
|
||||
@ -1599,7 +1599,7 @@ abstract class Entity{
|
||||
$properties->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag);
|
||||
$properties->setByte(EntityMetadataProperties::COLOR, 0);
|
||||
|
||||
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, $this->gravityEnabled);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $this->nameTagVisible);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, true);
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\entity\utils\ExperienceUtils;
|
||||
use pocketmine\event\player\PlayerExperienceChangeEvent;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\world\sound\XpCollectSound;
|
||||
use pocketmine\world\sound\XpLevelUpSound;
|
||||
@ -36,7 +35,6 @@ use function ceil;
|
||||
use function count;
|
||||
use function max;
|
||||
use function min;
|
||||
use function sprintf;
|
||||
|
||||
class ExperienceManager{
|
||||
|
||||
@ -146,9 +144,6 @@ class ExperienceManager{
|
||||
|
||||
$xpLevel = (int) $newLevel;
|
||||
$xpProgress = $newLevel - (int) $newLevel;
|
||||
if($xpProgress > 1.0){
|
||||
throw new AssumptionFailedError(sprintf("newLevel - (int) newLevel should never be bigger than 1, but have %.53f (newLevel=%.53f)", $xpProgress, $newLevel));
|
||||
}
|
||||
return $this->setXpAndProgress($xpLevel, $xpProgress);
|
||||
}
|
||||
|
||||
|
@ -217,6 +217,19 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
$this->uuid = Uuid::uuid3(Uuid::NIL, ((string) $this->getId()) . $this->skin->getSkinData() . $this->getNameTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
* @phpstan-param array<int, Item> $items
|
||||
*/
|
||||
private static function populateInventoryFromListTag(Inventory $inventory, array $items) : void{
|
||||
$listeners = $inventory->getListeners()->toArray();
|
||||
$inventory->getListeners()->clear();
|
||||
|
||||
$inventory->setContents($items);
|
||||
|
||||
$inventory->getListeners()->add(...$listeners);
|
||||
}
|
||||
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
@ -247,10 +260,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
|
||||
$inventoryTag = $nbt->getListTag("Inventory");
|
||||
if($inventoryTag !== null){
|
||||
$armorListeners = $this->armorInventory->getListeners()->toArray();
|
||||
$this->armorInventory->getListeners()->clear();
|
||||
$inventoryListeners = $this->inventory->getListeners()->toArray();
|
||||
$this->inventory->getListeners()->clear();
|
||||
$inventoryItems = [];
|
||||
$armorInventoryItems = [];
|
||||
|
||||
/** @var CompoundTag $item */
|
||||
foreach($inventoryTag as $i => $item){
|
||||
@ -258,14 +269,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
if($slot >= 0 and $slot < 9){ //Hotbar
|
||||
//Old hotbar saving stuff, ignore it
|
||||
}elseif($slot >= 100 and $slot < 104){ //Armor
|
||||
$this->armorInventory->setItem($slot - 100, Item::nbtDeserialize($item));
|
||||
$armorInventoryItems[$slot - 100] = Item::nbtDeserialize($item);
|
||||
}elseif($slot >= 9 and $slot < $this->inventory->getSize() + 9){
|
||||
$this->inventory->setItem($slot - 9, Item::nbtDeserialize($item));
|
||||
$inventoryItems[$slot - 9] = Item::nbtDeserialize($item);
|
||||
}
|
||||
}
|
||||
|
||||
$this->armorInventory->getListeners()->add(...$armorListeners);
|
||||
$this->inventory->getListeners()->add(...$inventoryListeners);
|
||||
self::populateInventoryFromListTag($this->inventory, $inventoryItems);
|
||||
self::populateInventoryFromListTag($this->armorInventory, $armorInventoryItems);
|
||||
}
|
||||
$offHand = $nbt->getCompoundTag("OffHandItem");
|
||||
if($offHand !== null){
|
||||
@ -279,10 +290,13 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
|
||||
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
|
||||
if($enderChestInventoryTag !== null){
|
||||
$enderChestInventoryItems = [];
|
||||
|
||||
/** @var CompoundTag $item */
|
||||
foreach($enderChestInventoryTag as $i => $item){
|
||||
$this->enderInventory->setItem($item->getByte("Slot"), Item::nbtDeserialize($item));
|
||||
$enderChestInventoryItems[$item->getByte("Slot")] = Item::nbtDeserialize($item);
|
||||
}
|
||||
self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems);
|
||||
}
|
||||
|
||||
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
|
||||
|
@ -25,4 +25,4 @@ namespace pocketmine\entity;
|
||||
|
||||
final class InvalidSkinException extends \InvalidArgumentException{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use Ahc\Json\Comment as CommentedJsonDecoder;
|
||||
use pocketmine\utils\Limits;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function json_encode;
|
||||
@ -48,7 +49,17 @@ final class Skin{
|
||||
/** @var string */
|
||||
private $geometryData;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(string $skinId, string $skinData, string $capeData = "", string $geometryName = "", string $geometryData = ""){
|
||||
self::checkLength($skinId, "Skin ID", Limits::INT16_MAX);
|
||||
self::checkLength($geometryName, "Geometry name", Limits::INT16_MAX);
|
||||
self::checkLength($geometryData, "Geometry data", Limits::INT32_MAX);
|
||||
|
||||
if($skinId === ""){
|
||||
throw new InvalidSkinException("Skin ID must not be empty");
|
||||
}
|
||||
|
@ -70,4 +70,4 @@ final class StringToEffectParser extends StringToTParser{
|
||||
public function parse(string $input) : ?Effect{
|
||||
return parent::parse($input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\entity\object;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntitySizeInfo;
|
||||
use pocketmine\entity\Location;
|
||||
@ -71,7 +72,7 @@ class FallingBlock extends Entity{
|
||||
}
|
||||
|
||||
if($blockId === 0){
|
||||
throw new \UnexpectedValueException("Missing block info from NBT");
|
||||
throw new SavedDataLoadingException("Missing block info from NBT");
|
||||
}
|
||||
|
||||
$damage = $nbt->getByte("Data", 0);
|
||||
|
@ -68,7 +68,7 @@ class ItemEntity extends Entity{
|
||||
if($item->isNull()){
|
||||
throw new \InvalidArgumentException("Item entity must have a non-air item with a count of at least 1");
|
||||
}
|
||||
$this->item = $item;
|
||||
$this->item = clone $item;
|
||||
parent::__construct($location, $nbt);
|
||||
}
|
||||
|
||||
|
@ -103,8 +103,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;
|
||||
|
@ -57,4 +57,4 @@ class BlockItemPickupEvent extends BlockEvent implements Cancellable{
|
||||
public function setInventory(?Inventory $inventory) : void{
|
||||
$this->inventory = $inventory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,4 +37,4 @@ class StructureGrowEvent extends BlockEvent implements Cancellable{
|
||||
public function getPlayer() : ?Player{
|
||||
return $this->player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class EntityShootBowEvent extends EntityEvent implements Cancellable{
|
||||
|
||||
/** @var Item */
|
||||
private $bow;
|
||||
/** @var Projectile */
|
||||
/** @var Entity */
|
||||
private $projectile;
|
||||
/** @var float */
|
||||
private $force;
|
||||
|
@ -45,4 +45,4 @@ class PlayerDisplayNameChangeEvent extends PlayerEvent{
|
||||
public function getNewName() : string{
|
||||
return $this->newName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,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;
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Called when a player interacts or touches a block (including air?)
|
||||
* Called when a player interacts or touches a block.
|
||||
* This is called for both left click (start break) and right click (use).
|
||||
*/
|
||||
class PlayerInteractEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
@ -52,8 +52,6 @@ interface Inventory{
|
||||
*
|
||||
* Returns the Items that did not fit.
|
||||
*
|
||||
* @param Item ...$slots
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function addItem(Item ...$slots) : array;
|
||||
@ -72,8 +70,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;
|
||||
|
@ -33,4 +33,4 @@ final class PlayerCraftingInventory extends CraftingGrid implements TemporaryInv
|
||||
}
|
||||
|
||||
public function getHolder() : Player{ return $this->holder; }
|
||||
}
|
||||
}
|
||||
|
@ -71,10 +71,10 @@ class SimpleInventory extends BaseInventory{
|
||||
|
||||
protected function internalSetContents(array $items) : void{
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
if(!isset($items[$i])){
|
||||
$this->clear($i);
|
||||
if(!isset($items[$i]) || $items[$i]->isNull()){
|
||||
$this->slots[$i] = null;
|
||||
}else{
|
||||
$this->setItem($i, $items[$i]);
|
||||
$this->slots[$i] = clone $items[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,4 @@ namespace pocketmine\inventory;
|
||||
|
||||
interface TemporaryInventory extends Inventory{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -252,6 +252,7 @@ class ItemFactory{
|
||||
$this->register(new Steak(new ItemIdentifier(ItemIds::STEAK, 0), "Steak"));
|
||||
$this->register(new Stick(new ItemIdentifier(ItemIds::STICK, 0), "Stick"));
|
||||
$this->register(new StringItem(new ItemIdentifier(ItemIds::STRING, 0), "String"));
|
||||
$this->register(new SweetBerries(new ItemIdentifier(ItemIds::SWEET_BERRIES, 0), "Sweet Berries"));
|
||||
$this->register(new Totem(new ItemIdentifier(ItemIds::TOTEM, 0), "Totem of Undying"));
|
||||
$this->register(new WheatSeeds(new ItemIdentifier(ItemIds::WHEAT_SEEDS, 0), "Wheat Seeds"));
|
||||
$this->register(new WritableBook(new ItemIdentifier(ItemIds::WRITABLE_BOOK, 0), "Book & Quill"));
|
||||
@ -327,7 +328,6 @@ class ItemFactory{
|
||||
//TODO: minecraft:shield
|
||||
//TODO: minecraft:sparkler
|
||||
//TODO: minecraft:spawn_egg
|
||||
$this->register(new SweetBerries(new ItemIdentifier(ItemIds::SWEET_BERRIES, 0), "Sweet Berries"));
|
||||
//TODO: minecraft:tnt_minecart
|
||||
//TODO: minecraft:trident
|
||||
//TODO: minecraft:turtle_helmet
|
||||
|
@ -97,7 +97,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("anvil", fn() => VanillaBlocks::ANVIL());
|
||||
$result->registerBlock("ateupd_block", fn() => VanillaBlocks::INFO_UPDATE2());
|
||||
$result->registerBlock("azure_bluet", fn() => VanillaBlocks::AZURE_BLUET());
|
||||
$result->registerBlock("bamboo", fn() => VanillaBlocks::BAMBOO_SAPLING());
|
||||
$result->registerBlock("bamboo", fn() => VanillaBlocks::BAMBOO());
|
||||
$result->registerBlock("bamboo_sapling", fn() => VanillaBlocks::BAMBOO_SAPLING());
|
||||
$result->registerBlock("banner", fn() => VanillaBlocks::BANNER());
|
||||
$result->registerBlock("barrel", fn() => VanillaBlocks::BARREL());
|
||||
@ -798,13 +798,15 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("sea_lantern", fn() => VanillaBlocks::SEA_LANTERN());
|
||||
$result->registerBlock("sea_pickle", fn() => VanillaBlocks::SEA_PICKLE());
|
||||
$result->registerBlock("sealantern", fn() => VanillaBlocks::SEA_LANTERN());
|
||||
$result->registerBlock("shulker_box", fn() => VanillaBlocks::DYED_SHULKER_BOX());
|
||||
$result->registerBlock("shulker_box", fn() => VanillaBlocks::SHULKER_BOX());
|
||||
$result->registerBlock("sign", fn() => VanillaBlocks::OAK_SIGN());
|
||||
$result->registerBlock("sign_post", fn() => VanillaBlocks::OAK_SIGN());
|
||||
$result->registerBlock("silver_glazed_terracotta", fn() => VanillaBlocks::LIGHT_GRAY_GLAZED_TERRACOTTA());
|
||||
$result->registerBlock("skull_block", fn() => VanillaBlocks::MOB_HEAD());
|
||||
$result->registerBlock("slab", fn() => VanillaBlocks::SMOOTH_STONE_SLAB());
|
||||
$result->registerBlock("slabs", fn() => VanillaBlocks::SMOOTH_STONE_SLAB());
|
||||
$result->registerBlock("slime", fn() => VanillaBlocks::SLIME());
|
||||
$result->registerBlock("slime_block", fn() => VanillaBlocks::SLIME());
|
||||
$result->registerBlock("smoker", fn() => VanillaBlocks::SMOKER());
|
||||
$result->registerBlock("smooth_quartz", fn() => VanillaBlocks::SMOOTH_QUARTZ());
|
||||
$result->registerBlock("smooth_quartz_slab", fn() => VanillaBlocks::SMOOTH_QUARTZ_SLAB());
|
||||
@ -859,11 +861,11 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("stone_bricks", fn() => VanillaBlocks::STONE_BRICKS());
|
||||
$result->registerBlock("stone_button", fn() => VanillaBlocks::STONE_BUTTON());
|
||||
$result->registerBlock("stone_pressure_plate", fn() => VanillaBlocks::STONE_PRESSURE_PLATE());
|
||||
$result->registerBlock("stone_slab", fn() => VanillaBlocks::SMOOTH_STONE_SLAB());
|
||||
$result->registerBlock("stone_slab", fn() => VanillaBlocks::STONE_SLAB());
|
||||
$result->registerBlock("stone_slab2", fn() => VanillaBlocks::RED_SANDSTONE_SLAB());
|
||||
$result->registerBlock("stone_slab3", fn() => VanillaBlocks::END_STONE_BRICK_SLAB());
|
||||
$result->registerBlock("stone_slab4", fn() => VanillaBlocks::MOSSY_STONE_BRICK_SLAB());
|
||||
$result->registerBlock("stone_stairs", fn() => VanillaBlocks::COBBLESTONE_STAIRS());
|
||||
$result->registerBlock("stone_stairs", fn() => VanillaBlocks::STONE_STAIRS());
|
||||
$result->registerBlock("stone_wall", fn() => VanillaBlocks::COBBLESTONE_WALL());
|
||||
$result->registerBlock("stonebrick", fn() => VanillaBlocks::STONE_BRICKS());
|
||||
$result->registerBlock("stonecutter", fn() => VanillaBlocks::LEGACY_STONECUTTER());
|
||||
@ -885,7 +887,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("sugarcane_block", fn() => VanillaBlocks::SUGARCANE());
|
||||
$result->registerBlock("sunflower", fn() => VanillaBlocks::SUNFLOWER());
|
||||
$result->registerBlock("sweet_berry_bush", fn() => VanillaBlocks::SWEET_BERRY_BUSH());
|
||||
$result->registerBlock("tall_grass", fn() => VanillaBlocks::FERN());
|
||||
$result->registerBlock("tall_grass", fn() => VanillaBlocks::TALL_GRASS());
|
||||
$result->registerBlock("tallgrass", fn() => VanillaBlocks::FERN());
|
||||
$result->registerBlock("terracotta", fn() => VanillaBlocks::STAINED_CLAY());
|
||||
$result->registerBlock("tnt", fn() => VanillaBlocks::TNT());
|
||||
|
@ -31,6 +31,7 @@ use function array_push;
|
||||
use function array_slice;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function mb_scrub;
|
||||
|
||||
abstract class WritableBookBase extends Item{
|
||||
public const TAG_PAGES = "pages"; //TAG_List<TAG_Compound>
|
||||
@ -168,12 +169,12 @@ abstract class WritableBookBase extends Item{
|
||||
if($pages->getTagType() === NBT::TAG_Compound){ //PE format
|
||||
/** @var CompoundTag $page */
|
||||
foreach($pages as $page){
|
||||
$this->pages[] = new WritableBookPage($page->getString(self::TAG_PAGE_TEXT), $page->getString(self::TAG_PAGE_PHOTONAME, ""));
|
||||
$this->pages[] = new WritableBookPage(mb_scrub($page->getString(self::TAG_PAGE_TEXT), 'UTF-8'), $page->getString(self::TAG_PAGE_PHOTONAME, ""));
|
||||
}
|
||||
}elseif($pages->getTagType() === NBT::TAG_String){ //PC format
|
||||
/** @var StringTag $page */
|
||||
foreach($pages as $page){
|
||||
$this->pages[] = new WritableBookPage($page->getValue());
|
||||
$this->pages[] = new WritableBookPage(mb_scrub($page->getValue(), 'UTF-8'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,17 +23,32 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\Utils;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
class WritableBookPage{
|
||||
public const PAGE_LENGTH_HARD_LIMIT_BYTES = Limits::INT16_MAX;
|
||||
public const PHOTO_NAME_LENGTH_HARD_LIMIT_BYTES = Limits::INT16_MAX;
|
||||
|
||||
/** @var string */
|
||||
private $text;
|
||||
/** @var string */
|
||||
private $photoName;
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private static function checkLength(string $string, string $name, int $maxLength) : void{
|
||||
if(strlen($string) > $maxLength){
|
||||
throw new \InvalidArgumentException(sprintf("$name must be at most %d bytes, but have %d bytes", $maxLength, strlen($string)));
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(string $text, string $photoName = ""){
|
||||
//TODO: data validation
|
||||
self::checkLength($text, "Text", self::PAGE_LENGTH_HARD_LIMIT_BYTES);
|
||||
self::checkLength($photoName, "Photo name", self::PHOTO_NAME_LENGTH_HARD_LIMIT_BYTES);
|
||||
Utils::checkUTF8($text);
|
||||
$this->text = $text;
|
||||
$this->photoName = $photoName;
|
||||
|
@ -24,7 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\Utils;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
class WrittenBook extends WritableBookBase{
|
||||
|
||||
@ -85,6 +88,9 @@ class WrittenBook extends WritableBookBase{
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthor(string $authorName) : self{
|
||||
if(strlen($authorName) > Limits::INT16_MAX){
|
||||
throw new \InvalidArgumentException(sprintf("Author must be at most %d bytes, but have %d bytes", Limits::INT16_MAX, strlen($authorName)));
|
||||
}
|
||||
Utils::checkUTF8($authorName);
|
||||
$this->author = $authorName;
|
||||
return $this;
|
||||
@ -103,6 +109,9 @@ class WrittenBook extends WritableBookBase{
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle(string $title) : self{
|
||||
if(strlen($title) > Limits::INT16_MAX){
|
||||
throw new \InvalidArgumentException(sprintf("Title must be at most %d bytes, but have %d bytes", Limits::INT16_MAX, strlen($title)));
|
||||
}
|
||||
Utils::checkUTF8($title);
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
|
@ -63,4 +63,4 @@ final class StringToEnchantmentParser extends StringToTParser{
|
||||
public function parse(string $input) : ?Enchantment{
|
||||
return parent::parse($input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ namespace pocketmine\lang;
|
||||
* This class contains factory methods for all the translations known to PocketMine-MP as per the used version of
|
||||
* pmmp/Language.
|
||||
* This class is generated automatically, do NOT modify it by hand.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class KnownTranslationFactory{
|
||||
public static function ability_flight() : Translatable{
|
||||
@ -242,10 +244,10 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_ENCHANT_USAGE, []);
|
||||
}
|
||||
|
||||
public static function commands_gamemode_success_other(Translatable|string $param1, Translatable|string $param0) : Translatable{
|
||||
public static function commands_gamemode_success_other(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_GAMEMODE_SUCCESS_OTHER, [
|
||||
1 => $param1,
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -915,6 +917,12 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::GAMEMODE_INFO, []);
|
||||
}
|
||||
|
||||
public static function gamemode_options(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::GAMEMODE_OPTIONS, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function invalid_port() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::INVALID_PORT, []);
|
||||
}
|
||||
@ -1966,6 +1974,26 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_server_obsolete_warning1(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_OBSOLETE_WARNING1, [
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_server_obsolete_warning2(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_OBSOLETE_WARNING2, [
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_server_obsolete_warning3(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_OBSOLETE_WARNING3, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_server_query_running(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_QUERY_RUNNING, [
|
||||
0 => $param0,
|
||||
@ -2123,6 +2151,14 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::SERVER_PORT, []);
|
||||
}
|
||||
|
||||
public static function server_port_v4() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::SERVER_PORT_V4, []);
|
||||
}
|
||||
|
||||
public static function server_port_v6() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::SERVER_PORT_V6, []);
|
||||
}
|
||||
|
||||
public static function server_properties() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::SERVER_PROPERTIES, []);
|
||||
}
|
||||
@ -2147,6 +2183,10 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::TILE_BED_TOOFAR, []);
|
||||
}
|
||||
|
||||
public static function view_distance() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::VIEW_DISTANCE, []);
|
||||
}
|
||||
|
||||
public static function welcome_to_pocketmine(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::WELCOME_TO_POCKETMINE, [
|
||||
0 => $param0,
|
||||
|
@ -26,6 +26,8 @@ namespace pocketmine\lang;
|
||||
/**
|
||||
* This class contains constants for all the translations known to PocketMine-MP as per the used version of pmmp/Language.
|
||||
* This class is generated automatically, do NOT modify it by hand.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class KnownTranslationKeys{
|
||||
public const ABILITY_FLIGHT = "ability.flight";
|
||||
@ -201,6 +203,7 @@ final class KnownTranslationKeys{
|
||||
public const GAMEMODE_SPECTATOR = "gameMode.spectator";
|
||||
public const GAMEMODE_SURVIVAL = "gameMode.survival";
|
||||
public const GAMEMODE_INFO = "gamemode_info";
|
||||
public const GAMEMODE_OPTIONS = "gamemode_options";
|
||||
public const INVALID_PORT = "invalid_port";
|
||||
public const IP_CONFIRM = "ip_confirm";
|
||||
public const IP_GET = "ip_get";
|
||||
@ -403,6 +406,9 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_SERVER_LICENSE = "pocketmine.server.license";
|
||||
public const POCKETMINE_SERVER_NETWORKSTART = "pocketmine.server.networkStart";
|
||||
public const POCKETMINE_SERVER_NETWORKSTARTFAILED = "pocketmine.server.networkStartFailed";
|
||||
public const POCKETMINE_SERVER_OBSOLETE_WARNING1 = "pocketmine.server.obsolete.warning1";
|
||||
public const POCKETMINE_SERVER_OBSOLETE_WARNING2 = "pocketmine.server.obsolete.warning2";
|
||||
public const POCKETMINE_SERVER_OBSOLETE_WARNING3 = "pocketmine.server.obsolete.warning3";
|
||||
public const POCKETMINE_SERVER_QUERY_RUNNING = "pocketmine.server.query.running";
|
||||
public const POCKETMINE_SERVER_START = "pocketmine.server.start";
|
||||
public const POCKETMINE_SERVER_STARTFINISHED = "pocketmine.server.startFinished";
|
||||
@ -440,12 +446,15 @@ final class KnownTranslationKeys{
|
||||
public const QUERY_WARNING1 = "query_warning1";
|
||||
public const QUERY_WARNING2 = "query_warning2";
|
||||
public const SERVER_PORT = "server_port";
|
||||
public const SERVER_PORT_V4 = "server_port_v4";
|
||||
public const SERVER_PORT_V6 = "server_port_v6";
|
||||
public const SERVER_PROPERTIES = "server_properties";
|
||||
public const SETTING_UP_SERVER_NOW = "setting_up_server_now";
|
||||
public const SKIP_INSTALLER = "skip_installer";
|
||||
public const TILE_BED_NOSLEEP = "tile.bed.noSleep";
|
||||
public const TILE_BED_OCCUPIED = "tile.bed.occupied";
|
||||
public const TILE_BED_TOOFAR = "tile.bed.tooFar";
|
||||
public const VIEW_DISTANCE = "view_distance";
|
||||
public const WELCOME_TO_POCKETMINE = "welcome_to_pocketmine";
|
||||
public const WHITELIST_ENABLE = "whitelist_enable";
|
||||
public const WHITELIST_INFO = "whitelist_info";
|
||||
|
@ -29,4 +29,4 @@ namespace pocketmine\network;
|
||||
*/
|
||||
final class NetworkInterfaceStartException extends \RuntimeException{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
public function onRun() : void{
|
||||
$chunk = FastChunkSerializer::deserializeTerrain($this->chunk);
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk);
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk) + ChunkSerializer::LOWER_PADDING_SIZE;
|
||||
$encoderContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, RuntimeBlockMapping::getInstance(), $encoderContext, $this->tiles);
|
||||
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create($this->chunkX, $this->chunkZ, $subCount, null, $payload))->getBuffer()));
|
||||
|
6
src/network/mcpe/cache/ChunkCache.php
vendored
6
src/network/mcpe/cache/ChunkCache.php
vendored
@ -128,10 +128,12 @@ class ChunkCache implements ChunkListener{
|
||||
$chunk,
|
||||
$this->caches[$chunkHash],
|
||||
$this->compressor,
|
||||
function() use ($chunkX, $chunkZ) : void{
|
||||
function() use ($chunkHash, $chunkX, $chunkZ) : void{
|
||||
$this->world->getLogger()->error("Failed preparing chunk $chunkX $chunkZ, retrying");
|
||||
|
||||
$this->restartPendingRequest($chunkX, $chunkZ);
|
||||
if(isset($this->caches[$chunkHash])){
|
||||
$this->restartPendingRequest($chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -33,4 +33,4 @@ interface Compressor{
|
||||
public function decompress(string $payload) : string;
|
||||
|
||||
public function compress(string $payload) : string;
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,4 @@ namespace pocketmine\network\mcpe\compression;
|
||||
|
||||
final class DecompressionException extends \RuntimeException{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\BaseSign;
|
||||
use pocketmine\block\ItemFrame;
|
||||
use pocketmine\block\utils\SignText;
|
||||
use pocketmine\entity\animation\ConsumingItemAnimation;
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\InvalidSkinException;
|
||||
use pocketmine\event\player\PlayerEditBookEvent;
|
||||
use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
@ -36,7 +37,9 @@ use pocketmine\inventory\transaction\TransactionException;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\item\WritableBook;
|
||||
use pocketmine\item\WritableBookPage;
|
||||
use pocketmine\item\WrittenBook;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
@ -95,28 +98,34 @@ use pocketmine\network\mcpe\protocol\types\PlayerAction;
|
||||
use pocketmine\network\PacketHandlingException;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function array_push;
|
||||
use function base64_encode;
|
||||
use function count;
|
||||
use function fmod;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function max;
|
||||
use function mb_strlen;
|
||||
use function microtime;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function trim;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
|
||||
/**
|
||||
* This handler handles packets related to general gameplay.
|
||||
*/
|
||||
class InGamePacketHandler extends PacketHandler{
|
||||
private const MAX_FORM_RESPONSE_DEPTH = 2; //modal/simple will be 1, custom forms 2 - they will never contain anything other than string|int|float|bool|null
|
||||
|
||||
/** @var Player */
|
||||
private $player;
|
||||
@ -355,6 +364,8 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
|
||||
self::validateFacing($data->getFace());
|
||||
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){
|
||||
@ -371,6 +382,8 @@ class InGamePacketHandler extends PacketHandler{
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
if($this->player->isUsingItem()){
|
||||
if(!$this->player->consumeHeldItem()){
|
||||
$hungerAttr = $this->player->getAttributeMap()->get(Attribute::HUNGER) ?? throw new AssumptionFailedError();
|
||||
$hungerAttr->markSynchronized(false);
|
||||
$this->inventoryManager->syncSlot($this->player->getInventory(), $this->player->getInventory()->getHeldItemIndex());
|
||||
}
|
||||
return true;
|
||||
@ -384,6 +397,15 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PacketHandlingException
|
||||
*/
|
||||
private static function validateFacing(int $facing) : void{
|
||||
if(!in_array($facing, Facing::ALL, true)){
|
||||
throw new PacketHandlingException("Invalid facing value $facing");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used to execute rollbacks when an action fails on a block.
|
||||
*/
|
||||
@ -496,6 +518,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
switch($packet->action){
|
||||
case PlayerAction::START_BREAK:
|
||||
self::validateFacing($packet->face);
|
||||
if(!$this->player->attackBlock($pos, $packet->face)){
|
||||
$this->onFailedBlockAction($pos, $packet->face);
|
||||
}
|
||||
@ -539,6 +562,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
case PlayerAction::STOP_GLIDE:
|
||||
break; //TODO
|
||||
case PlayerAction::CRACK_BREAK:
|
||||
self::validateFacing($packet->face);
|
||||
$this->player->continueBreakBlock($pos, $packet->face);
|
||||
break;
|
||||
case PlayerAction::START_SWIMMING:
|
||||
@ -708,6 +732,24 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return false; //TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PacketHandlingException
|
||||
*/
|
||||
private function checkBookText(string $string, string $fieldName, int $softLimit, int $hardLimit, bool &$cancel) : string{
|
||||
if(strlen($string) > $hardLimit){
|
||||
throw new PacketHandlingException(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->session->getLogger()->debug("Cancelled book edit due to $fieldName exceeded soft limit of $softLimit chars");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function handleBookEdit(BookEditPacket $packet) : bool{
|
||||
//TODO: break this up into book API things
|
||||
$oldBook = $this->player->getInventory()->getItem($packet->inventorySlot);
|
||||
@ -717,10 +759,11 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
$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, WritableBookPage::PAGE_LENGTH_HARD_LIMIT_BYTES, $cancel);
|
||||
$newBook->setPageText($packet->pageNumber, $text);
|
||||
$modifiedPages[] = $packet->pageNumber;
|
||||
break;
|
||||
case BookEditPacket::TYPE_ADD_PAGE:
|
||||
@ -729,7 +772,8 @@ class InGamePacketHandler extends PacketHandler{
|
||||
//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, WritableBookPage::PAGE_LENGTH_HARD_LIMIT_BYTES, $cancel);
|
||||
$newBook->insertPage($packet->pageNumber, $text);
|
||||
$modifiedPages[] = $packet->pageNumber;
|
||||
break;
|
||||
case BookEditPacket::TYPE_DELETE_PAGE:
|
||||
@ -748,18 +792,46 @@ class InGamePacketHandler extends PacketHandler{
|
||||
$modifiedPages = [$packet->pageNumber, $packet->secondaryPageNumber];
|
||||
break;
|
||||
case BookEditPacket::TYPE_SIGN_BOOK:
|
||||
/** @var WrittenBook $newBook */
|
||||
$title = self::checkBookText($packet->title, "title", 16, Limits::INT16_MAX, $cancel);
|
||||
//this one doesn't have a limit in vanilla, so we have to improvise
|
||||
$author = self::checkBookText($packet->author, "author", 256, Limits::INT16_MAX, $cancel);
|
||||
|
||||
$newBook = VanillaItems::WRITTEN_BOOK()
|
||||
->setPages($oldBook->getPages())
|
||||
->setAuthor($packet->author)
|
||||
->setTitle($packet->title)
|
||||
->setAuthor($author)
|
||||
->setTitle($title)
|
||||
->setGeneration(WrittenBook::GENERATION_ORIGINAL);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
$event = new PlayerEditBookEvent($this->player, $oldBook, $newBook, $packet->type, $modifiedPages);
|
||||
//for redundancy, in case of protocol changes, we don't want to pass these directly
|
||||
$action = match($packet->type){
|
||||
BookEditPacket::TYPE_REPLACE_PAGE => PlayerEditBookEvent::ACTION_REPLACE_PAGE,
|
||||
BookEditPacket::TYPE_ADD_PAGE => PlayerEditBookEvent::ACTION_ADD_PAGE,
|
||||
BookEditPacket::TYPE_DELETE_PAGE => PlayerEditBookEvent::ACTION_DELETE_PAGE,
|
||||
BookEditPacket::TYPE_SWAP_PAGES => PlayerEditBookEvent::ACTION_SWAP_PAGES,
|
||||
BookEditPacket::TYPE_SIGN_BOOK => PlayerEditBookEvent::ACTION_SIGN_BOOK,
|
||||
default => throw new AssumptionFailedError("We already filtered unknown types in the switch above")
|
||||
};
|
||||
|
||||
/*
|
||||
* 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->session->getLogger()->debug("Cancelled book edit due to adding too many pages (new page count would be $newPageCount)");
|
||||
$cancel = true;
|
||||
}
|
||||
|
||||
$event = new PlayerEditBookEvent($this->player, $oldBook, $newBook, $action, $modifiedPages);
|
||||
if($cancel){
|
||||
$event->cancel();
|
||||
}
|
||||
|
||||
$event->call();
|
||||
if($event->isCancelled()){
|
||||
return true;
|
||||
@ -808,14 +880,18 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
$fixed = "[" . implode(",", $newParts) . "]";
|
||||
if(($ret = json_decode($fixed, $assoc)) === null){
|
||||
throw new \InvalidArgumentException("Failed to fix JSON: " . json_last_error_msg() . "(original: $json, modified: $fixed)");
|
||||
try{
|
||||
return json_decode($fixed, $assoc, self::MAX_FORM_RESPONSE_DEPTH, JSON_THROW_ON_ERROR);
|
||||
}catch(\JsonException $e){
|
||||
throw PacketHandlingException::wrap($e, "Failed to fix JSON (original: $json, modified: $fixed)");
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return json_decode($json, $assoc);
|
||||
try{
|
||||
return json_decode($json, $assoc, self::MAX_FORM_RESPONSE_DEPTH, JSON_THROW_ON_ERROR);
|
||||
}catch(\JsonException $e){
|
||||
throw PacketHandlingException::wrap($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleServerSettingsRequest(ServerSettingsRequestPacket $packet) : bool{
|
||||
|
@ -104,6 +104,7 @@ class PreSpawnPacketHandler extends PacketHandler{
|
||||
false,
|
||||
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
|
||||
[],
|
||||
0,
|
||||
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries()
|
||||
));
|
||||
|
||||
|
@ -59,4 +59,4 @@ final class RakLibThreadCrashInfo{
|
||||
public function makePrettyMessage() : string{
|
||||
return sprintf("%s: \"%s\" in %s on line %d", $this->class ?? "Fatal error", $this->message, Filesystem::cleanPath($this->file), $this->line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe\serializer;
|
||||
|
||||
use pocketmine\block\tile\Spawnable;
|
||||
use pocketmine\data\bedrock\BiomeIds;
|
||||
use pocketmine\data\bedrock\LegacyBiomeIdToStringIdMap;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
@ -32,10 +34,14 @@ use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\PalettedBlockArray;
|
||||
use pocketmine\world\format\SubChunk;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function str_repeat;
|
||||
|
||||
final class ChunkSerializer{
|
||||
public const LOWER_PADDING_SIZE = 4;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
@ -58,11 +64,22 @@ final class ChunkSerializer{
|
||||
|
||||
public static function serializeFullChunk(Chunk $chunk, RuntimeBlockMapping $blockMapper, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{
|
||||
$stream = PacketSerializer::encoder($encoderContext);
|
||||
|
||||
//TODO: HACK! fill in fake subchunks to make up for the new negative space client-side
|
||||
for($y = 0; $y < self::LOWER_PADDING_SIZE; $y++){
|
||||
$stream->putByte(8); //subchunk version 8
|
||||
$stream->putByte(0); //0 layers - client will treat this as all-air
|
||||
}
|
||||
|
||||
$subChunkCount = self::getSubChunkCount($chunk);
|
||||
for($y = Chunk::MIN_SUBCHUNK_INDEX, $writtenCount = 0; $writtenCount < $subChunkCount; ++$y, ++$writtenCount){
|
||||
self::serializeSubChunk($chunk->getSubChunk($y), $blockMapper, $stream, false);
|
||||
}
|
||||
$stream->put($chunk->getBiomeIdArray());
|
||||
|
||||
//TODO: right now we don't support 3D natively, so we just 3Dify our 2D biomes so they fill the column
|
||||
$encodedBiomePalette = self::serializeBiomesAsPalette($chunk);
|
||||
$stream->put(str_repeat($encodedBiomePalette, 25));
|
||||
|
||||
$stream->putByte(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.
|
||||
|
||||
@ -116,4 +133,39 @@ final class ChunkSerializer{
|
||||
|
||||
return $stream->getBuffer();
|
||||
}
|
||||
|
||||
private static function serializeBiomesAsPalette(Chunk $chunk) : string{
|
||||
$biomeIdMap = LegacyBiomeIdToStringIdMap::getInstance();
|
||||
$biomePalette = new PalettedBlockArray($chunk->getBiomeId(0, 0));
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
$biomeId = $chunk->getBiomeId($x, $z);
|
||||
if($biomeIdMap->legacyToString($biomeId) === null){
|
||||
//make sure we aren't sending bogus biomes - the 1.18.0 client crashes if we do this
|
||||
$biomeId = BiomeIds::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;
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ use function floor;
|
||||
use function get_class;
|
||||
use function is_int;
|
||||
use function max;
|
||||
use function mb_strlen;
|
||||
use function microtime;
|
||||
use function min;
|
||||
use function preg_match;
|
||||
@ -1080,6 +1081,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$bb->minY = $this->location->y - 0.2;
|
||||
$bb->maxY = $this->location->y + 0.2;
|
||||
|
||||
//we're already at the new position at this point; check if there are blocks we might have landed on between
|
||||
//the old and new positions (running down stairs necessitates this)
|
||||
$bb = $bb->addCoord(-$dx, -$dy, -$dz);
|
||||
|
||||
$this->onGround = $this->isCollided = count($this->getWorld()->getCollisionBlocks($bb, true)) > 0;
|
||||
}
|
||||
}
|
||||
@ -1320,7 +1325,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
$message = TextFormat::clean($message, false);
|
||||
foreach(explode("\n", $message) as $messagePart){
|
||||
if(trim($messagePart) !== "" and strlen($messagePart) <= 255 and $this->messageCounter-- > 0){
|
||||
if(trim($messagePart) !== "" and strlen($messagePart) <= 512 * 4 and mb_strlen($messagePart, 'UTF-8') <= 512 and $this->messageCounter-- > 0){
|
||||
if(strpos($messagePart, './') === 0){
|
||||
$messagePart = substr($messagePart, 1);
|
||||
}
|
||||
@ -1667,13 +1672,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
$entity->attack($ev);
|
||||
$this->broadcastAnimation(new ArmSwingAnimation($this), $this->getViewers());
|
||||
|
||||
$soundPos = $entity->getPosition()->add(0, $entity->size->getHeight() / 2, 0);
|
||||
if($ev->isCancelled()){
|
||||
$this->getWorld()->addSound($soundPos, new EntityAttackNoDamageSound());
|
||||
return false;
|
||||
}
|
||||
$this->broadcastAnimation(new ArmSwingAnimation($this), $this->getViewers());
|
||||
$this->getWorld()->addSound($soundPos, new EntityAttackSound());
|
||||
|
||||
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0 and $entity instanceof Living){
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\permission\PermissionParser;
|
||||
use pocketmine\permission\PermissionParserException;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function get_debug_type;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function preg_match;
|
||||
@ -85,7 +86,18 @@ class PluginDescription{
|
||||
* @param string|mixed[] $yamlString
|
||||
*/
|
||||
public function __construct($yamlString){
|
||||
$this->loadMap(!is_array($yamlString) ? yaml_parse($yamlString) : $yamlString);
|
||||
if(is_string($yamlString)){
|
||||
$map = yaml_parse($yamlString);
|
||||
if($map === false){
|
||||
throw new PluginDescriptionParseException("YAML parsing error in plugin manifest");
|
||||
}
|
||||
if(!is_array($map)){
|
||||
throw new PluginDescriptionParseException("Invalid structure of plugin manifest, expected array but have " . get_debug_type($map));
|
||||
}
|
||||
}else{
|
||||
$map = $yamlString;
|
||||
}
|
||||
$this->loadMap($map);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,4 +39,4 @@ final class PluginLoadTriageEntry{
|
||||
public function getLoader() : PluginLoader{ return $this->loader; }
|
||||
|
||||
public function getDescription() : PluginDescription{ return $this->description; }
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use function array_intersect;
|
||||
use function count;
|
||||
use function extension_loaded;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function phpversion;
|
||||
@ -77,9 +78,13 @@ final class PluginLoadabilityChecker{
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($description->getRequiredExtensions()) as $extensionName => $versionConstrs){
|
||||
if(!extension_loaded($extensionName)){
|
||||
return KnownTranslationFactory::pocketmine_plugin_extensionNotLoaded($extensionName);
|
||||
}
|
||||
$gotVersion = phpversion($extensionName);
|
||||
if($gotVersion === false){
|
||||
return KnownTranslationFactory::pocketmine_plugin_extensionNotLoaded($extensionName);
|
||||
//extensions may set NULL as the extension version, in which case phpversion() may return false
|
||||
$gotVersion = "**UNKNOWN**";
|
||||
}
|
||||
|
||||
foreach($versionConstrs as $k => $constr){ // versionConstrs_loop
|
||||
@ -105,4 +110,4 @@ final class PluginLoadabilityChecker{
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,4 +52,4 @@ final class Promise{
|
||||
public function isResolved() : bool{
|
||||
return $this->shared->resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,4 +72,4 @@ final class PromiseResolver{
|
||||
public function getPromise() : Promise{
|
||||
return $this->promise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\thread;
|
||||
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use const PTHREADS_INHERIT_NONE;
|
||||
|
||||
/**
|
||||
|
@ -153,6 +153,8 @@ class UpdateChecker{
|
||||
|
||||
if($currentVersion->getBuild() > 0 && $currentVersion->compare($newVersion) > 0){
|
||||
$this->updateInfo = $updateInfo;
|
||||
}else{
|
||||
$this->logger->debug("API reported version is an older version or the same version (" . $newVersion->getFullVersion() . "), not showing notification");
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,12 +162,7 @@ class UpdateChecker{
|
||||
* Returns the channel used for update checking (stable, beta, dev)
|
||||
*/
|
||||
public function getChannel() : string{
|
||||
$channel = strtolower($this->server->getConfigGroup()->getPropertyString("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->getConfigGroup()->getPropertyString("auto-updater.preferred-channel", "stable"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,6 @@ use function date;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
@ -53,6 +52,7 @@ use function yaml_parse;
|
||||
use const CASE_LOWER;
|
||||
use const JSON_BIGINT_AS_STRING;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
|
||||
/**
|
||||
* Config Class for simple config manipulation of multiple formats.
|
||||
@ -212,7 +212,7 @@ class Config{
|
||||
$content = self::writeProperties($this->config);
|
||||
break;
|
||||
case Config::JSON:
|
||||
$content = json_encode($this->config, $this->jsonOptions);
|
||||
$content = json_encode($this->config, $this->jsonOptions | JSON_THROW_ON_ERROR);
|
||||
break;
|
||||
case Config::YAML:
|
||||
$content = yaml_emit($this->config, YAML_UTF8_ENCODING);
|
||||
@ -227,7 +227,7 @@ class Config{
|
||||
throw new AssumptionFailedError("Config type is unknown, has not been set or not detected");
|
||||
}
|
||||
|
||||
file_put_contents($this->file, $content);
|
||||
Filesystem::safeFilePutContents($this->file, $content);
|
||||
|
||||
$this->changed = false;
|
||||
}
|
||||
@ -341,14 +341,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;
|
||||
@ -393,14 +393,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;
|
||||
|
@ -26,9 +26,11 @@ namespace pocketmine\utils;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function copy;
|
||||
use function dirname;
|
||||
use function disk_free_space;
|
||||
use function fclose;
|
||||
use function fflush;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function flock;
|
||||
use function fopen;
|
||||
use function ftruncate;
|
||||
@ -40,6 +42,7 @@ use function ltrim;
|
||||
use function mkdir;
|
||||
use function preg_match;
|
||||
use function realpath;
|
||||
use function rename;
|
||||
use function rmdir;
|
||||
use function rtrim;
|
||||
use function scandir;
|
||||
@ -221,4 +224,74 @@ final class Filesystem{
|
||||
@unlink($lockFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around file_put_contents() which writes to a temporary file before overwriting the original. If the disk
|
||||
* is full, writing to the temporary file will fail before the original file is modified, leaving it untouched.
|
||||
*
|
||||
* This is necessary because file_put_contents() destroys the data currently in the file if it fails to write the
|
||||
* new contents.
|
||||
*
|
||||
* @param resource|null $context Context to pass to file_put_contents
|
||||
*/
|
||||
public static function safeFilePutContents(string $fileName, string $contents, int $flags = 0, $context = null) : void{
|
||||
$directory = dirname($fileName);
|
||||
if(!is_dir($directory)){
|
||||
throw new \RuntimeException("Target directory path does not exist or is not a directory");
|
||||
}
|
||||
if(is_dir($fileName)){
|
||||
throw new \RuntimeException("Target file path already exists and is not a file");
|
||||
}
|
||||
|
||||
$counter = 0;
|
||||
do{
|
||||
//we don't care about overwriting any preexisting tmpfile but we can't write if a directory is already here
|
||||
$temporaryFileName = $fileName . ".$counter.tmp";
|
||||
$counter++;
|
||||
}while(is_dir($temporaryFileName));
|
||||
|
||||
$writeTemporaryFileResult = $context !== null ?
|
||||
file_put_contents($temporaryFileName, $contents, $flags, $context) :
|
||||
file_put_contents($temporaryFileName, $contents, $flags);
|
||||
|
||||
if($writeTemporaryFileResult !== strlen($contents)){
|
||||
$context !== null ?
|
||||
@unlink($temporaryFileName, $context) :
|
||||
@unlink($temporaryFileName);
|
||||
$diskSpace = disk_free_space($directory);
|
||||
if($diskSpace !== false && $diskSpace < strlen($contents)){
|
||||
throw new \RuntimeException("Failed to write to temporary file $temporaryFileName (out of free disk space)");
|
||||
}
|
||||
throw new \RuntimeException("Failed to write to temporary file $temporaryFileName (possibly out of free disk space)");
|
||||
}
|
||||
|
||||
//TODO: the @ prevents us receiving the actual error message, but right now it's necessary since we can't assume
|
||||
//that the error handler has been set :(
|
||||
$renameTemporaryFileResult = $context !== null ?
|
||||
@rename($temporaryFileName, $fileName, $context) :
|
||||
@rename($temporaryFileName, $fileName);
|
||||
if(!$renameTemporaryFileResult){
|
||||
/*
|
||||
* The following code works around a bug in Windows where rename() will periodically decide to give us a
|
||||
* spurious "Access is denied (code: 5)" error. As far as I could determine, the fault comes from Windows
|
||||
* itself, but since I couldn't reliably reproduce the issue it's very hard to debug.
|
||||
*
|
||||
* The following code can be used to test. Usually it will fail anywhere before 100,000 iterations.
|
||||
*
|
||||
* for($i = 0; $i < 10_000_000; ++$i){
|
||||
* file_put_contents('ops.txt.0.tmp', 'some data ' . $i, 0);
|
||||
* if(!rename('ops.txt.0.tmp', 'ops.txt')){
|
||||
* throw new \Error("something weird happened");
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
$copyTemporaryFileResult = $context !== null ?
|
||||
copy($temporaryFileName, $fileName, $context) :
|
||||
copy($temporaryFileName, $fileName);
|
||||
if(!$copyTemporaryFileResult){
|
||||
throw new \RuntimeException("Failed to move temporary file contents into target file");
|
||||
}
|
||||
@unlink($temporaryFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,4 +45,4 @@ trait SingletonTrait{
|
||||
public static function reset() : void{
|
||||
self::$instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,4 +79,4 @@ abstract class StringToTParser{
|
||||
public function getKnownAliases() : array{
|
||||
return array_keys($this->callbackMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -503,9 +503,6 @@ final class Utils{
|
||||
*/
|
||||
public static function parseDocComment(string $docComment) : array{
|
||||
$rawDocComment = substr($docComment, 3, -2); //remove the opening and closing markers
|
||||
if($rawDocComment === false){ //usually empty doc comment, but this is safer and statically analysable
|
||||
return [];
|
||||
}
|
||||
preg_match_all('/(*ANYCRLF)^[\t ]*(?:\* )?@([a-zA-Z\-]+)(?:[\t ]+(.+?))?[\t ]*$/m', $rawDocComment, $matches);
|
||||
|
||||
return array_combine($matches[1], $matches[2]);
|
||||
|
@ -35,4 +35,4 @@ use pocketmine\utils\NotSerializable;
|
||||
final class ChunkLockId{
|
||||
use NotCloneable;
|
||||
use NotSerializable;
|
||||
}
|
||||
}
|
||||
|
@ -755,8 +755,6 @@ class World implements ChunkManager{
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @param Player ...$targets If empty, will send to all players in the world.
|
||||
*/
|
||||
public function sendTime(Player ...$targets) : void{
|
||||
if(count($targets) === 0){
|
||||
@ -856,7 +854,7 @@ class World implements ChunkManager{
|
||||
//Update entities that need update
|
||||
Timings::$tickEntity->startTiming();
|
||||
foreach($this->updateEntities as $id => $entity){
|
||||
if($entity->isClosed() or !$entity->onUpdate($currentTick)){
|
||||
if($entity->isClosed() or $entity->isFlaggedForDespawn() or !$entity->onUpdate($currentTick)){
|
||||
unset($this->updateEntities[$id]);
|
||||
}
|
||||
if($entity->isFlaggedForDespawn()){
|
||||
@ -949,6 +947,8 @@ class World implements ChunkManager{
|
||||
public function createBlockUpdatePackets(array $blocks) : array{
|
||||
$packets = [];
|
||||
|
||||
$blockMapping = RuntimeBlockMapping::getInstance();
|
||||
|
||||
foreach($blocks as $b){
|
||||
if(!($b instanceof Vector3)){
|
||||
throw new \TypeError("Expected Vector3 in blocks array, got " . (is_object($b) ? get_class($b) : gettype($b)));
|
||||
@ -958,7 +958,7 @@ class World implements ChunkManager{
|
||||
$blockPosition = BlockPosition::fromVector3($b);
|
||||
$packets[] = UpdateBlockPacket::create(
|
||||
$blockPosition,
|
||||
RuntimeBlockMapping::getInstance()->toRuntimeId($fullBlock->getFullId()),
|
||||
$blockMapping->toRuntimeId($fullBlock->getFullId()),
|
||||
UpdateBlockPacket::FLAG_NETWORK,
|
||||
UpdateBlockPacket::DATA_LAYER_NORMAL
|
||||
);
|
||||
@ -1107,6 +1107,7 @@ class World implements ChunkManager{
|
||||
$entity->onRandomUpdate();
|
||||
}
|
||||
|
||||
$blockFactory = BlockFactory::getInstance();
|
||||
foreach($chunk->getSubChunks() as $Y => $subChunk){
|
||||
if(!$subChunk->isEmptyFast()){
|
||||
$k = 0;
|
||||
@ -1123,8 +1124,7 @@ class World implements ChunkManager{
|
||||
$state = $subChunk->getFullBlock($x, $y, $z);
|
||||
|
||||
if(isset($this->randomTickBlocks[$state])){
|
||||
/** @var Block $block */
|
||||
$block = BlockFactory::getInstance()->fromFullBlock($state);
|
||||
$block = $blockFactory->fromFullBlock($state);
|
||||
$block->position($this, $chunkX * Chunk::EDGE_LENGTH + $x, ($Y << SubChunk::COORD_BIT_SIZE) + $y, $chunkZ * Chunk::EDGE_LENGTH + $z);
|
||||
$block->onRandomTick();
|
||||
}
|
||||
@ -2258,6 +2258,11 @@ class World implements ChunkManager{
|
||||
$previousSpawn = $this->getSpawnLocation();
|
||||
$this->provider->getWorldData()->setSpawn($pos);
|
||||
(new SpawnChangeEvent($this, $previousSpawn))->call();
|
||||
|
||||
$location = Position::fromObject($pos, $this);
|
||||
foreach($this->players as $player){
|
||||
$player->getNetworkSession()->syncWorldSpawnPoint($location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2373,6 +2378,9 @@ class World implements ChunkManager{
|
||||
if(!$pos->isValid() || $pos->getWorld() !== $this){
|
||||
throw new \InvalidArgumentException("Invalid Tile world");
|
||||
}
|
||||
if(!$this->isInWorld($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ())){
|
||||
throw new \InvalidArgumentException("Tile position is outside the world bounds");
|
||||
}
|
||||
|
||||
$chunkX = $pos->getFloorX() >> Chunk::COORD_BIT_SIZE;
|
||||
$chunkZ = $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE;
|
||||
@ -2466,6 +2474,27 @@ class World implements ChunkManager{
|
||||
|
||||
private function initChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{
|
||||
$logger = new \PrefixedLogger($this->logger, "Loading chunk $chunkX $chunkZ");
|
||||
|
||||
$this->timings->syncChunkLoadFixInvalidBlocks->startTiming();
|
||||
$blockFactory = BlockFactory::getInstance();
|
||||
$invalidBlocks = 0;
|
||||
foreach($chunkData->getChunk()->getSubChunks() as $subChunk){
|
||||
foreach($subChunk->getBlockLayers() as $blockLayer){
|
||||
foreach($blockLayer->getPalette() as $blockStateId){
|
||||
$mappedStateId = $blockFactory->getMappedStateId($blockStateId);
|
||||
if($mappedStateId !== $blockStateId){
|
||||
$blockLayer->replaceAll($blockStateId, $mappedStateId);
|
||||
$invalidBlocks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($invalidBlocks > 0){
|
||||
$logger->debug("Fixed $invalidBlocks invalid blockstates");
|
||||
$chunkData->getChunk()->setTerrainDirtyFlag(Chunk::DIRTY_FLAG_BLOCKS, true);
|
||||
}
|
||||
$this->timings->syncChunkLoadFixInvalidBlocks->stopTiming();
|
||||
|
||||
if(count($chunkData->getEntityNBT()) !== 0){
|
||||
$this->timings->syncChunkLoadEntities->startTiming();
|
||||
$entityFactory = EntityFactory::getInstance();
|
||||
@ -2507,9 +2536,15 @@ class World implements ChunkManager{
|
||||
}
|
||||
if($tile === null){
|
||||
$logger->warning("Deleted unknown tile entity type " . $nbt->getString("id", "<unknown>"));
|
||||
}elseif(!$this->isChunkLoaded($tile->getPosition()->getFloorX() >> Chunk::COORD_BIT_SIZE, $tile->getPosition()->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$tilePosition = $tile->getPosition();
|
||||
if(!$this->isChunkLoaded($tilePosition->getFloorX() >> Chunk::COORD_BIT_SIZE, $tilePosition->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
|
||||
$logger->error("Found tile saved on wrong chunk - unable to fix due to correct chunk not loaded");
|
||||
}elseif($this->getTile($tilePosition = $tile->getPosition()) !== null){
|
||||
}elseif(!$this->isInWorld($tilePosition->getFloorX(), $tilePosition->getFloorY(), $tilePosition->getFloorZ())){
|
||||
$logger->error("Cannot add tile with position outside the world bounds: x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z");
|
||||
}elseif($this->getTile($tilePosition) !== null){
|
||||
$logger->error("Cannot add tile at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z: Another tile is already at that position");
|
||||
}else{
|
||||
$this->addTile($tile);
|
||||
|
@ -45,6 +45,7 @@ class WorldTimings{
|
||||
|
||||
public TimingsHandler $syncChunkLoad;
|
||||
public TimingsHandler $syncChunkLoadData;
|
||||
public TimingsHandler $syncChunkLoadFixInvalidBlocks;
|
||||
public TimingsHandler $syncChunkLoadEntities;
|
||||
public TimingsHandler $syncChunkLoadTileEntities;
|
||||
public TimingsHandler $syncChunkSave;
|
||||
@ -64,14 +65,15 @@ class WorldTimings{
|
||||
$this->entityTick = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Tick Entities");
|
||||
|
||||
Timings::init(); //make sure the timers we want are available
|
||||
$this->syncChunkSend = new TimingsHandler("** " . $name . "Player Send Chunks", Timings::$playerChunkSend);
|
||||
$this->syncChunkSendPrepare = new TimingsHandler("** " . $name . "Player Send Chunk Prepare", Timings::$playerChunkSend);
|
||||
$this->syncChunkSend = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Player Send Chunks", Timings::$playerChunkSend);
|
||||
$this->syncChunkSendPrepare = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Player Send Chunk Prepare", Timings::$playerChunkSend);
|
||||
|
||||
$this->syncChunkLoad = new TimingsHandler("** " . $name . "Chunk Load", Timings::$worldLoad);
|
||||
$this->syncChunkLoadData = new TimingsHandler("** " . $name . "Chunk Load - Data");
|
||||
$this->syncChunkLoadEntities = new TimingsHandler("** " . $name . "Chunk Load - Entities");
|
||||
$this->syncChunkLoadTileEntities = new TimingsHandler("** " . $name . "Chunk Load - TileEntities");
|
||||
$this->syncChunkSave = new TimingsHandler("** " . $name . "Chunk Save", Timings::$worldSave);
|
||||
$this->syncChunkLoad = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Chunk Load", Timings::$worldLoad);
|
||||
$this->syncChunkLoadData = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Chunk Load - Data");
|
||||
$this->syncChunkLoadFixInvalidBlocks = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Chunk Load - Fix Invalid Blocks");
|
||||
$this->syncChunkLoadEntities = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Chunk Load - Entities");
|
||||
$this->syncChunkLoadTileEntities = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Chunk Load - TileEntities");
|
||||
$this->syncChunkSave = new TimingsHandler(Timings::INCLUDED_BY_OTHER_TIMINGS_PREFIX . $name . "Chunk Save", Timings::$worldSave);
|
||||
|
||||
$this->doTick = new TimingsHandler($name . "World Tick");
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\world\format\io\exception\CorruptedWorldException;
|
||||
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
|
||||
@ -165,7 +166,7 @@ class BedrockWorldData extends BaseNbtWorldData{
|
||||
|
||||
$nbt = new LittleEndianNbtSerializer();
|
||||
$buffer = $nbt->write(new TreeRoot($this->compoundTag));
|
||||
file_put_contents($this->dataPath, Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
|
||||
Filesystem::safeFilePutContents($this->dataPath, Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
|
||||
}
|
||||
|
||||
public function getDifficulty() : int{
|
||||
|
@ -29,6 +29,8 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\world\format\io\exception\CorruptedWorldException;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\World;
|
||||
@ -111,7 +113,10 @@ class JavaWorldData extends BaseNbtWorldData{
|
||||
public function save() : void{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$buffer = zlib_encode($nbt->write(new TreeRoot(CompoundTag::create()->setTag("Data", $this->compoundTag))), ZLIB_ENCODING_GZIP);
|
||||
file_put_contents($this->dataPath, $buffer);
|
||||
if($buffer === false){
|
||||
throw new AssumptionFailedError("zlib_encode() failed unexpectedly");
|
||||
}
|
||||
Filesystem::safeFilePutContents($this->dataPath, $buffer);
|
||||
}
|
||||
|
||||
public function getDifficulty() : int{
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\data\bedrock\BiomeIds;
|
||||
use pocketmine\data\bedrock\LegacyBlockIdToStringIdMap;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\utils\Binary;
|
||||
@ -157,6 +158,9 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
BedrockWorldData::generate($path, $name, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedChunkException
|
||||
*/
|
||||
protected function deserializePaletted(BinaryStream $stream) : PalettedBlockArray{
|
||||
$bitsPerBlock = $stream->getByte() >> 1;
|
||||
|
||||
@ -169,18 +173,18 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
$palette = [];
|
||||
$idMap = LegacyBlockIdToStringIdMap::getInstance();
|
||||
for($i = 0, $paletteSize = $stream->getLInt(); $i < $paletteSize; ++$i){
|
||||
$offset = $stream->getOffset();
|
||||
$tag = $nbt->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$stream->setOffset($offset);
|
||||
try{
|
||||
$offset = $stream->getOffset();
|
||||
|
||||
$id = $idMap->stringToLegacy($tag->getString("name")) ?? BlockLegacyIds::INFO_UPDATE;
|
||||
$data = $tag->getShort("val");
|
||||
if($id === BlockLegacyIds::AIR){
|
||||
//TODO: quick and dirty hack for artifacts left behind by broken world editors
|
||||
//we really need a proper state fixer, but this is a pressing issue.
|
||||
$data = 0;
|
||||
$tag = $nbt->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$stream->setOffset($offset);
|
||||
|
||||
$id = $idMap->stringToLegacy($tag->getString("name")) ?? BlockLegacyIds::INFO_UPDATE;
|
||||
$data = $tag->getShort("val");
|
||||
$palette[] = ($id << Block::INTERNAL_METADATA_BITS) | $data;
|
||||
}catch(NbtException $e){
|
||||
throw new CorruptedChunkException("Invalid blockstate NBT at offset $i in paletted storage: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
$palette[] = ($id << Block::INTERNAL_METADATA_BITS) | $data;
|
||||
}
|
||||
|
||||
//TODO: exceptions
|
||||
|
@ -124,4 +124,4 @@ final class FlatGeneratorOptions{
|
||||
return new self($structure, $biomeId, $options);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user