Compare commits

...

35 Commits

Author SHA1 Message Date
a4af1609ea Release 3.26.5 2022-01-04 20:47:31 +00:00
8c4b8a9042 CS 2022-01-04 20:44:10 +00:00
958a9dbf0f Merge pull request from GHSA-c6fg-99pr-25m9
* Skin: impose length limits on skinID, geometryName and geometryData fields

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

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

This means that if the parameter variable is mutated, the exception trace will show the value of the variable inside the function, not the value that was actually passed.
2021-12-10 17:29:57 +00:00
911ad344c9 Human: do not mutate parameter variables in setXpAndProgress()
this caused a mystery that took 3 entire years to debug.
2021-12-10 17:27:28 +00:00
06eaf9f273 3.26.3 is next 2021-12-09 00:27:03 +00:00
1e56ed2ea3 Release 3.26.2 2021-12-09 00:26:59 +00:00
40895a86e5 draft-release: stick a banner on the release notes to declare obsolescence 2021-12-08 23:55:43 +00:00
b081394125 Do not restrict the allowed update channels client-side
we really should have an endpoint on the server that deals with this.
2021-12-08 21:57:16 +00:00
f48cf68cac updater: log a message when an update was found, but it's an older version 2021-12-08 21:55:44 +00:00
264cff70ec Release new PM3 builds onto pm3 channel 2021-12-08 21:55:12 +00:00
3aabfa4ab0 bootstrap: display value of PHPRC when PHP binary is borked
PHPRC overrides the search path for php.ini, which might break the php.ini locating.
2021-12-08 20:48:44 +00:00
cb0af44ccb start.sh: improve errors when PHP isn't found 2021-11-30 23:51:35 +00:00
d535f02096 Make nicer errors for PHP binary not being found 2021-11-30 23:45:25 +00:00
7665f4f443 start.sh: remove 7 2021-11-30 23:43:17 +00:00
20d6b69813 3.26.2 is next 2021-11-30 22:27:42 +00:00
6b7d0307af Release 3.26.1 2021-11-30 22:27:42 +00:00
baeac2eb07 Fixed tiles not being sent with chunks 2021-11-30 22:19:28 +00:00
d5f81fe261 3.26.1 is next 2021-11-30 18:53:36 +00:00
0aeac3af7d Release 3.26.0 2021-11-30 18:53:36 +00:00
9931c1d50a Protocol changes for 1.18.0 2021-11-30 18:46:29 +00:00
8079ae341a Updated build/php submodule to pmmp/php-build-scripts@bd329dba08 2021-11-30 01:19:14 +00:00
ba295dc7dc Always use LF in .neon files 2021-11-30 01:16:28 +00:00
c19174a174 3.25.7 is next 2021-11-26 23:37:47 +00:00
30 changed files with 319 additions and 103 deletions

1
.gitattributes vendored
View File

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

View File

@ -105,6 +105,11 @@ jobs:
Please see the [changelogs](/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
## WARNING
The 3.x line of releases is now OBSOLETE. It will be discontinued after **March 1st, 2022**.
Please prepare to upgrade to 4.0 or newer before that date.
- name: Upload preprocessor diffs
uses: actions/upload-artifact@v2
if: always()

View File

@ -19,6 +19,9 @@ return (new PhpCsFixer\Config)
'array_syntax' => [
'syntax' => 'short'
],
'binary_operator_spaces' => [
'default' => 'single_space'
],
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => [
@ -69,8 +72,12 @@ return (new PhpCsFixer\Config)
],
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'return_type_declaration' => [
'space_before' => 'one'
],
'single_import_per_statement' => true,
'strict_param' => true,
'unary_operator_spaces' => true,
])
->setFinder($finder)
->setIndent("\t")

32
changelogs/3.26.md Normal file
View File

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

View File

@ -7,6 +7,7 @@
"require": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
@ -32,7 +33,7 @@
"pocketmine/log": "^0.2.0",
"pocketmine/log-pthreads": "^0.1.0",
"pocketmine/math": "^0.2.0",
"pocketmine/nbt": "^0.2.18",
"pocketmine/nbt": "^0.2.19",
"pocketmine/raklib": "^0.12.7",
"pocketmine/snooze": "^0.1.0",
"pocketmine/spl": "^0.4.0"

65
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f13c4ed7311b99ac07dca31d2f77ad1d",
"content-hash": "03871ed11c5fe042f6417bceefe9bd4a",
"packages": [
{
"name": "adhocore/json-comment",
@ -322,16 +322,16 @@
},
{
"name": "pocketmine/nbt",
"version": "0.2.18",
"version": "0.2.19",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82"
"reference": "8567c65e8e099c2f7436cfea3d886b3dcd332283"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82",
"reference": "9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/8567c65e8e099c2f7436cfea3d886b3dcd332283",
"reference": "8567c65e8e099c2f7436cfea3d886b3dcd332283",
"shasum": ""
},
"require": {
@ -343,7 +343,7 @@
"require-dev": {
"irstea/phpunit-shim": "^7.5 || ^8.0",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.80",
"phpstan/phpstan": "0.12.85",
"phpstan/phpstan-strict-rules": "^0.12.4"
},
"type": "library",
@ -359,9 +359,9 @@
"description": "PHP library for working with Named Binary Tags",
"support": {
"issues": "https://github.com/pmmp/NBT/issues",
"source": "https://github.com/pmmp/NBT/tree/0.2.18"
"source": "https://github.com/pmmp/NBT/tree/0.2.19"
},
"time": "2021-03-11T00:09:04+00:00"
"time": "2021-12-16T01:15:41+00:00"
},
{
"name": "pocketmine/raklib",
@ -618,16 +618,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": {
@ -668,9 +668,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",
@ -945,16 +945,16 @@
},
{
"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": {
@ -1006,9 +1006,9 @@
],
"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",
@ -1182,16 +1182,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": {
@ -1247,7 +1247,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": [
{
@ -1255,20 +1255,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": {
@ -1307,7 +1307,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": [
{
@ -1315,7 +1315,7 @@
"type": "github"
}
],
"time": "2020-09-28T05:57:25+00:00"
"time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-invoker",
@ -2761,6 +2761,7 @@
"platform": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",

View File

@ -210,6 +210,7 @@ use function json_encode;
use function json_last_error_msg;
use function lcg_value;
use function max;
use function mb_strlen;
use function microtime;
use function min;
use function preg_match;
@ -2279,6 +2280,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
$pk->serverSoftwareVersion = sprintf("%s %s", \pocketmine\NAME, \pocketmine\VERSION);
$pk->blockPaletteChecksum = 0; //we don't bother with this (0 skips verification) - the preimage is some dumb stringified NBT, not even actual NBT
$this->dataPacket($pk);
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
@ -3264,6 +3266,24 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
/**
* @throws \UnexpectedValueException
*/
private function checkBookText(string $string, string $fieldName, int $softLimit, int $hardLimit, bool &$cancel) : string{
if(strlen($string) > $hardLimit){
throw new \UnexpectedValueException(sprintf("Book %s must be at most %d bytes, but have %d bytes", $fieldName, $hardLimit, strlen($string)));
}
$result = TextFormat::clean($string, false);
//strlen() is O(1), mb_strlen() is O(n)
if(strlen($result) > $softLimit * 4 || mb_strlen($result, 'UTF-8') > $softLimit){
$cancel = true;
$this->server->getLogger()->debug(sprintf("Cancelled book edit by %s due to %s exceeded soft limit of %d chars", $this->getName(), $fieldName, $softLimit));
}
return $result;
}
public function handleBookEdit(BookEditPacket $packet) : bool{
/** @var WritableBook $oldBook */
$oldBook = $this->inventory->getItem($packet->inventorySlot);
@ -3273,10 +3293,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$newBook = clone $oldBook;
$modifiedPages = [];
$cancel = false;
switch($packet->type){
case BookEditPacket::TYPE_REPLACE_PAGE:
$newBook->setPageText($packet->pageNumber, $packet->text);
$text = self::checkBookText($packet->text, "page text", 256, 0x7fff, $cancel);
$newBook->setPageText($packet->pageNumber, $text);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_ADD_PAGE:
@ -3285,7 +3306,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//TODO: the client can send insert-before actions on trailing client-side pages which cause odd behaviour on the server
return false;
}
$newBook->insertPage($packet->pageNumber, $packet->text);
$text = self::checkBookText($packet->text, "page text", 256, 0x7fff, $cancel);
$newBook->insertPage($packet->pageNumber, $text);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_DELETE_PAGE:
@ -3304,17 +3326,36 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$modifiedPages = [$packet->pageNumber, $packet->secondaryPageNumber];
break;
case BookEditPacket::TYPE_SIGN_BOOK:
$title = self::checkBookText($packet->title, "title", 16, 0x7fff, $cancel);
//this one doesn't have a limit in vanilla, so we have to improvise
$author = self::checkBookText($packet->author, "author", 256, 0x7fff, $cancel);
/** @var WrittenBook $newBook */
$newBook = Item::get(Item::WRITTEN_BOOK, 0, 1, $newBook->getNamedTag());
$newBook->setAuthor($packet->author);
$newBook->setTitle($packet->title);
$newBook->setAuthor($author);
$newBook->setTitle($title);
$newBook->setGeneration(WrittenBook::GENERATION_ORIGINAL);
break;
default:
return false;
}
/*
* Plugins may have created books with more than 50 pages; we allow plugins to do this, but not players.
* Don't allow the page count to grow past 50, but allow deleting, swapping or altering text of existing pages.
*/
$oldPageCount = count($oldBook->getPages());
$newPageCount = count($newBook->getPages());
if(($newPageCount > $oldPageCount && $newPageCount > 50)){
$this->server->getLogger()->debug("Cancelled book edit by " . $this->getName() . " due to adding too many pages (new page count would be $newPageCount)");
$cancel = true;
}
$event = new PlayerEditBookEvent($this, $oldBook, $newBook, $packet->type, $modifiedPages);
if($cancel){
$event->setCancelled();
}
$event->call();
if($event->isCancelled()){
return true;

View File

@ -74,6 +74,7 @@ namespace pocketmine {
}
$extensions = [
"chunkutils2" => "PocketMine ChunkUtils v2",
"curl" => "cURL",
"ctype" => "ctype",
"date" => "Date",
@ -115,6 +116,16 @@ namespace pocketmine {
}
}
$chunkutils2_version = phpversion("chunkutils2");
$wantedVersionLock = "0.3";
$wantedVersionMin = "$wantedVersionLock.0";
if($chunkutils2_version !== false && (
version_compare($chunkutils2_version, $wantedVersionMin) < 0 ||
preg_match("/^" . preg_quote($wantedVersionLock, "/") . "\.\d+(?:-dev)?$/", $chunkutils2_version) === 0 //lock in at ^0.2, optionally at a patch release
)){
$messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
}
if(extension_loaded("pocketmine")){
$messages[] = "The native PocketMine extension is no longer supported.";
}
@ -186,6 +197,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);

View File

@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.25.6";
const BASE_VERSION = "3.26.5";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_CHANNEL = "stable";
const BUILD_CHANNEL = "pm3";

View File

@ -619,6 +619,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->propertyManager->setFloat(self::DATA_SCALE, 1);
$this->propertyManager->setFloat(self::DATA_BOUNDING_BOX_WIDTH, $this->width);
$this->propertyManager->setFloat(self::DATA_BOUNDING_BOX_HEIGHT, $this->height);
$this->propertyManager->setFloat(self::DATA_COLOR, 0);
$this->fireTicks = $this->namedtag->getShort("Fire", 0);
if($this->isOnFire()){
@ -1229,10 +1230,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$diffZ = $z - $floorZ;
if(BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY, $floorZ)]){
$westNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX - 1, $floorY, $floorZ)];
$eastNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX + 1, $floorY, $floorZ)];
$downNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY - 1, $floorZ)];
$upNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY + 1, $floorZ)];
$westNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX - 1, $floorY, $floorZ)];
$eastNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX + 1, $floorY, $floorZ)];
$downNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY - 1, $floorZ)];
$upNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY + 1, $floorZ)];
$northNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY, $floorZ - 1)];
$southNonSolid = !BlockFactory::$solid[$this->level->getBlockIdAt($floorX, $floorY, $floorZ + 1)];

View File

@ -57,7 +57,6 @@ use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\UUID;
use function array_filter;
use function array_merge;
@ -70,7 +69,6 @@ use function max;
use function min;
use function mt_rand;
use function random_int;
use function sprintf;
use function strlen;
use const INT32_MAX;
use const INT32_MIN;
@ -397,9 +395,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$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);
}
@ -447,24 +442,26 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
protected function setXpAndProgress(?int $level, ?float $progress) : bool{
$newLevel = $level;
$newProgress = $progress;
if(!$this->justCreated){
$ev = new PlayerExperienceChangeEvent($this, $this->getXpLevel(), $this->getXpProgress(), $level, $progress);
$ev = new PlayerExperienceChangeEvent($this, $this->getXpLevel(), $this->getXpProgress(), $newLevel, $newProgress);
$ev->call();
if($ev->isCancelled()){
return false;
}
$level = $ev->getNewLevel();
$progress = $ev->getNewProgress();
$newLevel = $ev->getNewLevel();
$newProgress = $ev->getNewProgress();
}
if($level !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE_LEVEL)->setValue($level);
if($newLevel !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE_LEVEL)->setValue($newLevel);
}
if($progress !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE)->setValue($progress);
if($newProgress !== null){
$this->getAttributeMap()->getAttribute(Attribute::EXPERIENCE)->setValue($newProgress);
}
return true;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -48,27 +48,27 @@ class AvailableCommandsPacket extends DataPacket{
* Basic parameter types. These must be combined with the ARG_FLAG_VALID constant.
* ARG_FLAG_VALID | (type const)
*/
public const ARG_TYPE_INT = 0x01;
public const ARG_TYPE_FLOAT = 0x03;
public const ARG_TYPE_VALUE = 0x04;
public const ARG_TYPE_WILDCARD_INT = 0x05;
public const ARG_TYPE_OPERATOR = 0x06;
public const ARG_TYPE_TARGET = 0x07;
public const ARG_TYPE_INT = 0x01;
public const ARG_TYPE_FLOAT = 0x03;
public const ARG_TYPE_VALUE = 0x04;
public const ARG_TYPE_WILDCARD_INT = 0x05;
public const ARG_TYPE_OPERATOR = 0x06;
public const ARG_TYPE_TARGET = 0x07;
public const ARG_TYPE_WILDCARD_TARGET = 0x08;
public const ARG_TYPE_FILEPATH = 0x10;
public const ARG_TYPE_STRING = 0x20;
public const ARG_TYPE_STRING = 0x20;
public const ARG_TYPE_POSITION = 0x28;
public const ARG_TYPE_MESSAGE = 0x2c;
public const ARG_TYPE_MESSAGE = 0x2c;
public const ARG_TYPE_RAWTEXT = 0x2e;
public const ARG_TYPE_RAWTEXT = 0x2e;
public const ARG_TYPE_JSON = 0x32;
public const ARG_TYPE_JSON = 0x32;
public const ARG_TYPE_COMMAND = 0x3f;
public const ARG_TYPE_COMMAND = 0x3f;
/**
* Enums are a little different: they are composed as follows:

View File

@ -106,7 +106,7 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
$this->subChunkCount = $this->getUnsignedVarInt();
$this->cacheEnabled = $this->getBool();
if($this->cacheEnabled){
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->usedBlobHashes[] = $this->getLLong();
}
}

View File

@ -37,11 +37,11 @@ interface ProtocolInfo{
*/
/** Actual Minecraft: PE protocol version */
public const CURRENT_PROTOCOL = 471;
public const CURRENT_PROTOCOL = 475;
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
public const MINECRAFT_VERSION = 'v1.17.40';
public const MINECRAFT_VERSION = 'v1.18.0';
/** Version number sent to clients in ping responses. */
public const MINECRAFT_VERSION_NETWORK = '1.17.40';
public const MINECRAFT_VERSION_NETWORK = '1.18.0';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;

View File

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

View File

@ -39,8 +39,9 @@ class SubChunkPacket extends DataPacket{
private string $data;
private int $requestResult;
private ?SubChunkPacketHeightMapInfo $heightMapData = null;
private ?int $usedBlobHash = null;
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData) : self{
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData, ?int $usedBlobHash) : self{
$result = new self;
$result->dimension = $dimension;
$result->subChunkX = $subChunkX;
@ -49,6 +50,7 @@ class SubChunkPacket extends DataPacket{
$result->data = $data;
$result->requestResult = $requestResult;
$result->heightMapData = $heightMapData;
$result->usedBlobHash = $usedBlobHash;
return $result;
}
@ -66,6 +68,8 @@ class SubChunkPacket extends DataPacket{
public function getHeightMapData() : ?SubChunkPacketHeightMapInfo{ return $this->heightMapData; }
public function getUsedBlobHash() : ?int{ return $this->usedBlobHash; }
protected function decodePayload() : void{
$this->dimension = $this->getVarInt();
$this->subChunkX = $this->getVarInt();
@ -81,6 +85,7 @@ class SubChunkPacket extends DataPacket{
SubChunkPacketHeightMapType::ALL_TOO_LOW => SubChunkPacketHeightMapInfo::allTooLow(),
default => throw new \UnexpectedValueException("Unknown heightmap data type $heightMapDataType")
};
$this->usedBlobHash = $this->getBool() ? $this->getLLong() : null;
}
protected function encodePayload() : void{
@ -101,6 +106,11 @@ class SubChunkPacket extends DataPacket{
$this->putByte(SubChunkPacketHeightMapType::DATA);
$heightMapData->write($this);
}
$usedBlobHash = $this->usedBlobHash;
$this->putBool($usedBlobHash !== null);
if($usedBlobHash !== null){
$this->putLLong($usedBlobHash);
}
}
public function handle(NetworkSession $handler) : bool{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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