Compare commits

..

39 Commits

Author SHA1 Message Date
d560cf17fc Release 4.0.0-BETA15 2021-11-30 19:27:04 +00:00
3f6efd0018 Merge branch 'stable' 2021-11-30 19:20:40 +00:00
aea124af74 Fix inconsistent class name 2021-11-30 19:17:26 +00:00
8620e67d88 Protocol changes for 1.18.0 2021-11-30 19:16:38 +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
d21a3d8750 4.0.0-BETA15 is next 2021-11-30 01:26:07 +00:00
6d62b06ce6 Release 4.0.0-BETA14 2021-11-30 01:26:07 +00:00
8be92d16fe Merge branch 'stable' 2021-11-30 01:19:54 +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
38325c8573 Updated translations 2021-11-30 01:14:21 +00:00
f239b077b9 Fixed PHPStan complaints 2021-11-30 00:36:38 +00:00
6f8f460a6c Partially revert "ConsoleReaderChildProcess: Commit suicide in more cases"
This reverts commit cbe0f44c4f.

This achieves the same result as the reverted commit wrt. process in the
same manner (writing a keepalive into the socket and checking if it
failed to send). However, it does _not_ allow the process to die on
reaching pipe EOF, since this can cause many spams of subprocesses when
stdin is actually not a tty (e.g. in a Docker container).
2021-11-30 00:27:52 +00:00
882df94bcb ConsoleReaderThread: fixed zombie process leak 2021-11-29 23:45:10 +00:00
4a8ca603a1 Log a message when forceShutdown() is called for anything other than a graceful shutdown 2021-11-28 18:53:34 +00:00
52f0c4f3ed Removed dodgy test using invalid block metadata 2021-11-27 22:51:14 +00:00
e2815eed60 BlockFactory: remap a bunch more invalid states 2021-11-27 20:07:58 +00:00
932a88764c composer commands suck 2021-11-27 04:07:25 +00:00
9540193766 Fixed everything lighting on fire 2021-11-27 03:54:30 +00:00
cc23e0b7a1 Updated DevTools submodule to pmmp/DevTools@6af57741e6 2021-11-27 03:52:32 +00:00
1f9400f901 World: automatically remap invalid blockstates on chunk load
this fixes a wide range of blocks with invalid blockstates becoming update! blocks on the client.

The most common occurrence of this was air with nonzero metadata left behind by world editors which set blockIDs but not block metadata. This caused large ghost structures of update! blocks to appear from nowhere.

The performance impact of this is very minimal (20 microseconds per chunk load in timings, compared to average 660 microseconds to load tiles).
2021-11-27 01:12:30 +00:00
e5149756a8 WorldTimings: fixed merge error introduced by 3bf87378ef 2021-11-27 00:06:09 +00:00
bc18969a09 Merge branch 'stable' 2021-11-26 23:45:09 +00:00
c19174a174 3.25.7 is next 2021-11-26 23:37:47 +00:00
f95142f6b6 Release 3.25.6 2021-11-26 23:37:46 +00:00
7ace24caab Fixed borked build number
this was a problem before the recent clean-up; the only reason it just decided to show now is because 2000+25 is valid PHP code, so PHP saved our asses.
2021-11-26 23:36:19 +00:00
32f619ac49 3.25.6 is next 2021-11-26 23:20:48 +00:00
1bb6ac4fb6 Release 3.25.5 2021-11-26 23:20:40 +00:00
533d3aae8b Merge branch 'stable' 2021-11-26 22:41:18 +00:00
52a891ba73 shut 2021-11-26 22:32:25 +00:00
71b813d4f9 Define pocketmine\BUILD_NUMBER from phar metadata
this way we don't have to patch the code (no idea why we were doing that anyway).
2021-11-26 22:27:58 +00:00
f2540a72ad Backport improved make-release.php from PM4 2021-11-26 22:10:46 +00:00
03f13495b7 Merge branch 'stable' 2021-11-26 21:59:55 +00:00
7e0f6c02a1 Updated build/php submodule to pmmp/php-build-scripts@a59722c676 2021-11-26 21:59:39 +00:00
1bc7869f6e Added remapping for almost 4000 invalid blockstates
when a block has sole ownership of an ID, the state bitmask can be ignored and we can just claim the whole metadata range for that single block.
This fixes a large number of issues with unknown blocks on older worlds where world editors did not remove the metadata, although update blocks will currently still appear on initial chunk send due to lack of AOT conversion (TODO).
2021-11-26 01:58:52 +00:00
5556861000 ItemFactory: move SweetBerries registration to the correct place 2021-11-26 00:46:35 +00:00
7dd5d0b593 4.0.0-BETA14 is next 2021-11-25 00:40:43 +00:00
29 changed files with 721 additions and 483 deletions

1
.gitattributes vendored
View File

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

View File

@ -35,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

View File

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

View File

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

View File

@ -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.

11
changelogs/3.26.md Normal file
View File

@ -0,0 +1,11 @@
**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.

View File

@ -1748,3 +1748,20 @@ Released 25th November 2021.
### Utils
- `Utils::parseDocComment()` now allows `-` in tag names.
# 4.0.0-BETA14
Released 30th November 2021.
## General
- The server will now log an EMERGENCY-level message when `forceShutdown()` is used for any other reason than a graceful shutdown.
- The server will now attempt to translate invalid blocks to valid equivalents when loading chunks. This fixes many issues with `update!` blocks appearing in worlds, particularly ghost structures (these would appear when world editors previously erased some blocks by setting their IDs but not metadata).
## Fixes
- Fixed `ConsoleReaderThread` spawning many zombie processes when running a server inside a Docker container.
# 4.0.0-BETA15
Released 30th November 2021.
## General
- Added support for Minecraft: Bedrock 1.18.0
- Removed support for earlier versions.

View File

@ -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,8 +34,8 @@
"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.0.0+bedrock-1.18.0",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
@ -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"

40
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": "fb545e4c8e17b0b07e8e20986b64e5a6",
"content-hash": "b515e7eebbf12a6251d2df817ce56dbc",
"packages": [
{
"name": "adhocore/json-comment",
@ -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.0.0+bedrock-1.18.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "906bafec4fc41f548749ce01d120902b25c1bbfe"
"reference": "040a883a4abb8c9b93114d5278cb5fecc635da82"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/906bafec4fc41f548749ce01d120902b25c1bbfe",
"reference": "906bafec4fc41f548749ce01d120902b25c1bbfe",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/040a883a4abb8c9b93114d5278cb5fecc635da82",
"reference": "040a883a4abb8c9b93114d5278cb5fecc635da82",
"shasum": ""
},
"require": {
@ -316,9 +316,9 @@
"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.0.0+bedrock-1.18.0"
},
"time": "2021-11-21T20:56:18+00:00"
"time": "2021-11-30T19:02:41+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -533,16 +533,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.0.17",
"version": "2.0.20",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "30e4a64d5674bac556c4e2b9842b19a981471ac4"
"reference": "a6e4eb22587e0014f6d658732cf633262723f8d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/30e4a64d5674bac556c4e2b9842b19a981471ac4",
"reference": "30e4a64d5674bac556c4e2b9842b19a981471ac4",
"url": "https://api.github.com/repos/pmmp/Language/zipball/a6e4eb22587e0014f6d658732cf633262723f8d3",
"reference": "a6e4eb22587e0014f6d658732cf633262723f8d3",
"shasum": ""
},
"type": "library",
@ -550,9 +550,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.0.20"
},
"time": "2021-11-12T00:59:39+00:00"
"time": "2021-11-25T20:56:12+00:00"
},
{
"name": "pocketmine/log",
@ -3510,7 +3510,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": "*",

View File

@ -1413,6 +1413,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);

View File

@ -25,13 +25,14 @@ 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.0-BETA15";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_NUMBER = 0;
public const BUILD_CHANNEL = "beta";
private function __construct(){
@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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

View File

@ -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;
@ -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);
}

View File

@ -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(),

View File

@ -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'));
}
}

View File

@ -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

View File

@ -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()));

View File

@ -104,6 +104,7 @@ class PreSpawnPacketHandler extends PacketHandler{
false,
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
[],
0,
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries()
));

View File

@ -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;
}
}

View File

@ -2466,6 +2466,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();

View File

@ -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");
}

View File

@ -505,6 +505,11 @@ parameters:
count: 1
path: ../../../src/command/defaults/TimingsCommand.php
-
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
count: 2
path: ../../../src/console/ConsoleReader.php
-
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 1

View File

@ -107,7 +107,6 @@ class BlockTest extends TestCase{
public function blockGetProvider() : array{
return [
[BlockLegacyIds::STONE, 5],
[BlockLegacyIds::STONE, 15],
[BlockLegacyIds::GOLD_BLOCK, 0],
[BlockLegacyIds::WOODEN_PLANKS, 5],
[BlockLegacyIds::SAND, 0],

File diff suppressed because one or more lines are too long

View File

@ -20,7 +20,7 @@ mkdir "$DATA_DIR"
mkdir "$PLUGINS_DIR"
cd tests/plugins/DevTools
php -dphar.readonly=0 ./src/DevTools/ConsoleScript.php --make ./ --relative ./ --out "$PLUGINS_DIR/DevTools.phar"
php -dphar.readonly=0 ./src/ConsoleScript.php --make ./ --relative ./ --out "$PLUGINS_DIR/DevTools.phar"
cd ../../..
composer make-server