mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-03 18:42:37 +00:00
Merge branch 'minor-next' into major-next
This commit is contained in:
commit
12179aa03a
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
||||
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v5.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v5.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v5.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v5.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
2
.github/workflows/discord-release-notify.yml
vendored
2
.github/workflows/discord-release-notify.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.30.0
|
||||
uses: shivammathur/setup-php@2.30.4
|
||||
with:
|
||||
php-version: 8.2
|
||||
|
||||
|
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.30.0
|
||||
uses: shivammathur/setup-php@2.30.4
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
|
||||
|
13
.github/workflows/main-php-matrix.yml
vendored
13
.github/workflows/main-php-matrix.yml
vendored
@ -147,17 +147,8 @@ jobs:
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Regenerate registry annotations
|
||||
run: php build/generate-registry-annotations.php src
|
||||
|
||||
- name: Regenerate KnownTranslation APIs
|
||||
run: php build/generate-known-translation-apis.php
|
||||
|
||||
- name: Regenerate BedrockData available files constants
|
||||
run: php build/generate-bedrockdata-path-consts.php
|
||||
|
||||
- name: Regenerate YmlServerProperties constants
|
||||
run: php build/generate-pocketmine-yml-property-consts.php
|
||||
- name: Update generated code
|
||||
run: composer update-codegen
|
||||
|
||||
- name: Verify code is unchanged
|
||||
run: |
|
||||
|
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.30.0
|
||||
uses: shivammathur/setup-php@2.30.4
|
||||
with:
|
||||
php-version: 8.2
|
||||
tools: php-cs-fixer:3.49
|
||||
|
@ -102,6 +102,25 @@ function generateClassHeader(string $className) : string{
|
||||
return <<<HEADER
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace $namespace;
|
||||
|
@ -61,6 +61,25 @@ function generateItemIds(ItemTypeDictionary $dictionary, BlockItemIdMap $blockIt
|
||||
fwrite($file, <<<'HEADER'
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\item;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6f619bf7a0b00e72a7c90915eec6e5a28866aa55
|
||||
Subproject commit 084822aa9e381ca05591e902a2613fe971dff3fd
|
168
build/server-phar-stub.php
Normal file
168
build/server-phar-stub.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\server_phar_stub;
|
||||
|
||||
use function clearstatcache;
|
||||
use function copy;
|
||||
use function fclose;
|
||||
use function fflush;
|
||||
use function flock;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function getmypid;
|
||||
use function hrtime;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function mkdir;
|
||||
use function number_format;
|
||||
use function str_replace;
|
||||
use function stream_get_contents;
|
||||
use function sys_get_temp_dir;
|
||||
use function tempnam;
|
||||
use function unlink;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const LOCK_EX;
|
||||
use const LOCK_NB;
|
||||
use const LOCK_UN;
|
||||
|
||||
/**
|
||||
* Finds the appropriate tmp directory to store the decompressed phar cache, accounting for potential file name
|
||||
* collisions.
|
||||
*/
|
||||
function preparePharCacheDirectory() : string{
|
||||
clearstatcache();
|
||||
|
||||
$i = 0;
|
||||
do{
|
||||
$tmpPath = sys_get_temp_dir() . '/PocketMine-MP-phar-cache.' . $i;
|
||||
$i++;
|
||||
}while(is_file($tmpPath));
|
||||
if(!@mkdir($tmpPath) && !is_dir($tmpPath)){
|
||||
throw new \RuntimeException("Failed to create temporary directory $tmpPath. Please ensure the disk has enough space and that the current user has permission to write to this location.");
|
||||
}
|
||||
|
||||
return $tmpPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes caches left behind by previous server instances.
|
||||
* This ensures that the tmp directory doesn't get flooded by servers crashing in restart loops.
|
||||
*/
|
||||
function cleanupPharCache(string $tmpPath) : void{
|
||||
clearstatcache();
|
||||
|
||||
/** @var string[] $matches */
|
||||
foreach(new \RegexIterator(
|
||||
new \FilesystemIterator(
|
||||
$tmpPath,
|
||||
\FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS
|
||||
),
|
||||
'/(.+)\.lock$/',
|
||||
\RegexIterator::GET_MATCH
|
||||
) as $matches){
|
||||
$lockFilePath = $matches[0];
|
||||
$baseTmpPath = $matches[1];
|
||||
|
||||
$file = @fopen($lockFilePath, "rb");
|
||||
if($file === false){
|
||||
//another process probably deleted the lock file already
|
||||
continue;
|
||||
}
|
||||
|
||||
if(flock($file, LOCK_EX | LOCK_NB)){
|
||||
//this tmpfile is no longer in use
|
||||
flock($file, LOCK_UN);
|
||||
fclose($file);
|
||||
|
||||
unlink($lockFilePath);
|
||||
unlink($baseTmpPath . ".tar");
|
||||
unlink($baseTmpPath);
|
||||
echo "Deleted stale phar cache at $baseTmpPath\n";
|
||||
}else{
|
||||
$pid = stream_get_contents($file);
|
||||
fclose($file);
|
||||
|
||||
echo "Phar cache at $baseTmpPath is still in use by PID $pid\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertPharToTar(string $tmpName, string $pharPath) : string{
|
||||
$tmpPharPath = $tmpName . ".phar";
|
||||
copy($pharPath, $tmpPharPath);
|
||||
|
||||
$phar = new \Phar($tmpPharPath);
|
||||
//phar requires phar.readonly=0, and zip doesn't support disabling compression - tar is the only viable option
|
||||
//we don't need phar anyway since we don't need to directly execute the file, only require files from inside it
|
||||
$phar->convertToData(\Phar::TAR, \Phar::NONE);
|
||||
unset($phar);
|
||||
\Phar::unlinkArchive($tmpPharPath);
|
||||
|
||||
return $tmpName . ".tar";
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a phar tmp cache to prevent it from being deleted by other server instances.
|
||||
* This code looks similar to Filesystem::createLockFile(), but we can't use that because it's inside the compressed
|
||||
* phar.
|
||||
*/
|
||||
function lockPharCache(string $lockFilePath) : void{
|
||||
//this static variable will keep the file(s) locked until the process ends
|
||||
static $lockFiles = [];
|
||||
|
||||
$lockFile = fopen($lockFilePath, "wb");
|
||||
if($lockFile === false){
|
||||
throw new \RuntimeException("Failed to open temporary file");
|
||||
}
|
||||
flock($lockFile, LOCK_EX); //this tells other server instances not to delete this cache file
|
||||
fwrite($lockFile, (string) getmypid()); //maybe useful for debugging
|
||||
fflush($lockFile);
|
||||
$lockFiles[$lockFilePath] = $lockFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a decompressed .tar of PocketMine-MP.phar in the system temp directory for loading code from.
|
||||
*
|
||||
* @return string path to the temporary decompressed phar (actually a .tar)
|
||||
*/
|
||||
function preparePharCache(string $tmpPath, string $pharPath) : string{
|
||||
clearstatcache();
|
||||
|
||||
$tmpName = tempnam($tmpPath, "PMMP");
|
||||
if($tmpName === false){
|
||||
throw new \RuntimeException("Failed to create temporary file");
|
||||
}
|
||||
|
||||
lockPharCache($tmpName . ".lock");
|
||||
return convertPharToTar($tmpName, $pharPath);
|
||||
}
|
||||
|
||||
$tmpDir = preparePharCacheDirectory();
|
||||
cleanupPharCache($tmpDir);
|
||||
echo "Preparing PocketMine-MP.phar decompressed cache...\n";
|
||||
$start = hrtime(true);
|
||||
$cacheName = preparePharCache($tmpDir, __FILE__);
|
||||
echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n";
|
||||
|
||||
require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php';
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\server_phar;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Git;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function dirname;
|
||||
@ -169,21 +171,7 @@ function main() : void{
|
||||
'git' => $gitHash,
|
||||
'build' => $build
|
||||
],
|
||||
<<<'STUB'
|
||||
<?php
|
||||
|
||||
$tmpDir = sys_get_temp_dir();
|
||||
if(!is_readable($tmpDir) or !is_writable($tmpDir)){
|
||||
echo "ERROR: tmpdir $tmpDir is not accessible." . PHP_EOL;
|
||||
echo "Check that the directory exists, and that the current user has read/write permissions for it." . PHP_EOL;
|
||||
echo "Alternatively, set 'sys_temp_dir' to a different directory in your php.ini file." . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require("phar://" . __FILE__ . "/src/PocketMine.php");
|
||||
__HALT_COMPILER();
|
||||
STUB
|
||||
,
|
||||
Filesystem::fileGetContents(Path::join(__DIR__, 'server-phar-stub.php')) . "\n__HALT_COMPILER();",
|
||||
\Phar::SHA1,
|
||||
\Phar::GZ
|
||||
) as $line){
|
||||
|
94
changelogs/5.14.md
Normal file
94
changelogs/5.14.md
Normal file
@ -0,0 +1,94 @@
|
||||
# 5.14.0
|
||||
Released 5th April 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.70**
|
||||
|
||||
This is a minor feature release, including performance improvements, minor gameplay features, new API features, and various internal improvements.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## General
|
||||
- Added support for a `--no-log-file` command-line option, which disables the creation of a `server.log` file.
|
||||
- **Use this with caution.** If you don't have another mechanism for collecting logs (e.g. Docker), this may make debugging harder.
|
||||
- Added support for automatic `server.log` rotation. When the `server.log` exceeds 32 MB, it will be renamed and moved to the `log_archive` folder in the server's data directory.
|
||||
- Files in the `log_archive` folder can be safely modified or deleted without stopping the server.
|
||||
- We suggest a cron job or similar to manage old log files (e.g. deleting or compressing them).
|
||||
- Added a new cache mechanism for `PocketMine-MP.phar`. This has several advantages:
|
||||
- Caches are now reused by all threads - this significantly reduces `/tmp` usage (previously every thread generated its own cache, wasting lots of space)
|
||||
- Dead cache files are automatically cleaned up by new servers - this means that a server crash loop won't flood `/tmp` anymore
|
||||
- `/status` now reports a more accurate number of threads on Windows.
|
||||
- Large resource packs are now able to be properly downloaded from the server.
|
||||
- Larger player skin sizes are now accepted by the server.
|
||||
- Improved logging from world providers to reduce spam when chunks contain invalid data.
|
||||
- Added more error logging for Anvil, PMAnvil and MCRegion worlds.
|
||||
- PHP deprecation warnings no longer cause the server to crash. This should make it easier for server owners to update to newer PHP versions.
|
||||
|
||||
## Performance
|
||||
- Improved world loading performance. This was achieved through a combination of changes:
|
||||
- Improvements to `BlockStateUpgrader` to avoid unnecessary work
|
||||
- Improvements to `BlockStateUpgradeSchema` to clean up stupid code
|
||||
- Improvements to `BlockStateReader` unused state handling
|
||||
- Optimizations to `RegistryTrait` (see below)
|
||||
- Improved performance of `RegistryTrait::__callStatic()` accessor by introducing a fast-path optimization. Ensure that you access registries with the correct function name case to benefit from this.
|
||||
- This improves the performance of `VanillaBlocks::WHATEVER()`, `VanillaItems`, etc.
|
||||
|
||||
## Tools
|
||||
- `tools/generate-blockstate-upgrade-schema.php` now supports generating schemas using `flattenedValueRemaps` (described in [BlockStateUpgradeSchema](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/4.0.0)).
|
||||
|
||||
## Gameplay
|
||||
- Added sounds for armour equipping and unequipping.
|
||||
- Added sound for picking berries from a sweet berry bush.
|
||||
|
||||
## API
|
||||
### `pocketmine\block\utils`
|
||||
- The following enum cases have been added:
|
||||
- `BannerPatternType::GLOBE`
|
||||
- `BannerPatternType::PIGLIN`
|
||||
|
||||
### `pocketmine\event\player`
|
||||
- The following classes have been added:
|
||||
- `PlayerResourcePackOfferEvent` - called before the server tells a connecting client which resource packs are available to download - allows customizing the pack list and other options
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following API methods have been added:
|
||||
- `public ArmorMaterial->getEquipSound() : ?\pocketmine\world\Sound` - returns the sound to play when this armour is equipped or unequipped
|
||||
- The following API methods have signature changes:
|
||||
- `ArmorMaterial->__construct()` now accepts an optional `?Sound $equipSound` parameter
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following API methods have signature changes:
|
||||
- `MainLogger->__construct()` now accepts `null` for the `$logFile` parameter - this disables the creation of a logger thread and log file
|
||||
- `MainLogger->__construct()` now accepts an optional `?string $logArchiveDir` parameter. If set, this enables log archiving in the specified directory when the current log file exceeds 32 MB.
|
||||
|
||||
## Dependencies
|
||||
- Now uses [`pocketmine/bedrock-block-upgrade-schema` version 4.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/4.0.0).
|
||||
- Now uses [`pmmp/ext-pmmpthread` version 6.1.0](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.1.0).
|
||||
- Now uses [`pocketmine/errorhandler` version 0.7.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.7.0).
|
||||
- Now uses [`pocketmine/raklib` version 1.1.0](https://github.com/pmmp/RakLib/releases/tag/1.1.0).
|
||||
- Now uses [`pocketmine/raklib-ipc` version 1.0.0](https://github.com/pmmp/RakLibIpc/releases/tag/1.0.0).
|
||||
|
||||
## Internals
|
||||
- (Re)Added support for RakLib packet ACK receipts. This was used to throttle resource pack sending and prevent network overloading.
|
||||
- Added `NetworkSession->sendDataPacketWithReceipt()` to make use of this feature.
|
||||
- `PacketSender` now requires an additional `?int $receiptId` parameter.
|
||||
- `ResourcePackPacketHandler` now uses `sendDataPacketWithReceipt()` to send resource packs, and delays sending the next chunk until the current one is acknowledged.
|
||||
- `ResourcePackPacketHandler` now accepts resource pack info directly in the constructor, instead of `ResourcePackManager`. This eases the implementation of `PlayerResourcePackOfferEvent`.
|
||||
- Increased `ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE` to 8 MB (previously 2 MB). While this weakens server security, it appears to be necessary to deal with extremely bloated Persona skins.
|
||||
- Increased max split packet parts accepted by `RakLib` to 512 (previously 128). Again, this is necessary to deal with extremely bloated Persona skins.
|
||||
- Added a new cache mechanism for `PocketMine-MP.phar`.
|
||||
- `ext-phar`'s default mechanism is extremely wasteful (generating a separate cache file per thread), and doesn't clean up after itself.
|
||||
- The new cache mechanism is shared between all threads, and automatically cleans up stale caches.
|
||||
- The phar stub (`build/server-phar-stub.php`) now converts the phar contents into a `.tar`, and decompresses all the files into `$TMPDIR/PocketMine-MP-phar-cache.<random>/`.
|
||||
- `phar://` URIs still work with this system, but `new Phar(__FILE__)` must be replaced by `new PharData(__FILE__)` within PocketMine-MP core code.
|
||||
- Backtraces from a `phar`'d server will now point to a location in the extracted phar cache, rather than the phar itself.
|
||||
- `block_factory_consistency_check` test (actually for `RuntimeBlockStateRegistry`) now stores less data, and is no longer affected by changes to internal state ID construction.
|
||||
|
||||
# 5.14.1
|
||||
Released 5th April 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed incorrect `pmmpthread` version check in server bootstrap.
|
16
changelogs/5.15.md
Normal file
16
changelogs/5.15.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 5.15.0
|
||||
Released 25th April 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.80**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.80.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.20.80.
|
||||
- Removed support for earlier versions.
|
@ -22,7 +22,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pmmpthread": "^6.0.7",
|
||||
"ext-pmmpthread": "^6.1.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
@ -33,14 +33,14 @@
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.6.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-data": "~2.9.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.8.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-protocol": "~29.0.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~4.1.0+bedrock-1.20.80",
|
||||
"pocketmine/bedrock-data": "~2.10.0+bedrock-1.20.80",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.9.0+bedrock-1.20.80",
|
||||
"pocketmine/bedrock-protocol": "~30.0.0+bedrock-1.20.80",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/errorhandler": "^0.7.0",
|
||||
"pocketmine/locale-data": "~2.19.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/math": "~1.0.0",
|
||||
@ -52,7 +52,7 @@
|
||||
"symfony/filesystem": "~6.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.60",
|
||||
"phpstan/phpstan": "1.10.67",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
|
||||
@ -82,11 +82,14 @@
|
||||
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
|
||||
"@php -dphar.readonly=0 build/server-phar.php"
|
||||
],
|
||||
"update-registry-annotations": [
|
||||
"update-codegen": [
|
||||
"@php build/generate-bedrockdata-path-consts.php",
|
||||
"@php build/generate-biome-ids.php",
|
||||
"@php build/generate-block-serializer-consts.php vendor/pocketmine/bedrock-data/canonical_block_states.nbt",
|
||||
"@php build/generate-item-type-names.php vendor/pocketmine/bedrock-data/required_item_list.json",
|
||||
"@php build/generate-known-translation-apis.php",
|
||||
"@php build/generate-pocketmine-yml-property-consts.php",
|
||||
"@php build/generate-registry-annotations.php src"
|
||||
],
|
||||
"update-translation-apis": [
|
||||
"@php build/generate-known-translation-apis.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
188
composer.lock
generated
188
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": "f3dd41a25226b5ff1030d822b2c46d02",
|
||||
"content-hash": "99069514f4fb8ee6b03b0da07a77a486",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -122,16 +122,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "3.6.0",
|
||||
"version": "4.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d"
|
||||
"reference": "d6b10cb0a5e69fb1dfe3b7f493bf4f519723a9cb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1496e275db5148cb96bdaa998115e5e31a5c1e4d",
|
||||
"reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/d6b10cb0a5e69fb1dfe3b7f493bf4f519723a9cb",
|
||||
"reference": "d6b10cb0a5e69fb1dfe3b7f493bf4f519723a9cb",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -142,22 +142,22 @@
|
||||
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.6.0"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.1.0"
|
||||
},
|
||||
"time": "2024-02-28T19:25:25+00:00"
|
||||
"time": "2024-04-05T17:28:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "2.9.0+bedrock-1.20.70",
|
||||
"version": "2.10.0+bedrock-1.20.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "10b6696b662fd80a282eff7dca6c99d321c5b9e3"
|
||||
"reference": "d7d709fec3848f56ca77f5ff0e5d4741b59f9d69"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/10b6696b662fd80a282eff7dca6c99d321c5b9e3",
|
||||
"reference": "10b6696b662fd80a282eff7dca6c99d321c5b9e3",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d7d709fec3848f56ca77f5ff0e5d4741b59f9d69",
|
||||
"reference": "d7d709fec3848f56ca77f5ff0e5d4741b59f9d69",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -168,22 +168,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.20.70"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.80"
|
||||
},
|
||||
"time": "2024-03-13T13:55:05+00:00"
|
||||
"time": "2024-04-25T10:08:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||
"version": "1.8.0",
|
||||
"version": "1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||
"reference": "4c4dc3bbceb944c5de429b6e752ab7a15652078c"
|
||||
"reference": "24a89457c17c271b5378b195931e720873865a79"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/4c4dc3bbceb944c5de429b6e752ab7a15652078c",
|
||||
"reference": "4c4dc3bbceb944c5de429b6e752ab7a15652078c",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/24a89457c17c271b5378b195931e720873865a79",
|
||||
"reference": "24a89457c17c271b5378b195931e720873865a79",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -194,22 +194,22 @@
|
||||
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.8.0"
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.9.0"
|
||||
},
|
||||
"time": "2024-02-28T19:25:53+00:00"
|
||||
"time": "2024-04-05T18:46:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "29.0.0+bedrock-1.20.70",
|
||||
"version": "30.0.0+bedrock-1.20.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "8d63f39bb2cded3d3e578fd3cf7bc769b9674857"
|
||||
"reference": "dc7606a9f778eeeeccfae393bd58e0b62ec6f85a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/8d63f39bb2cded3d3e578fd3cf7bc769b9674857",
|
||||
"reference": "8d63f39bb2cded3d3e578fd3cf7bc769b9674857",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/dc7606a9f778eeeeccfae393bd58e0b62ec6f85a",
|
||||
"reference": "dc7606a9f778eeeeccfae393bd58e0b62ec6f85a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -240,9 +240,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/29.0.0+bedrock-1.20.70"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/30.0.0+bedrock-1.20.80"
|
||||
},
|
||||
"time": "2024-03-13T14:35:54+00:00"
|
||||
"time": "2024-04-05T17:53:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -376,25 +376,25 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/errorhandler",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/ErrorHandler.git",
|
||||
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878"
|
||||
"reference": "cae94884368a74ece5294b9ff7fef18732dcd921"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/dae214a04348b911e8219ebf125ff1c5589cc878",
|
||||
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878",
|
||||
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/cae94884368a74ece5294b9ff7fef18732dcd921",
|
||||
"reference": "cae94884368a74ece5294b9ff7fef18732dcd921",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"phpstan/phpstan": "~1.10.3",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -409,9 +409,9 @@
|
||||
"description": "Utilities to handle nasty PHP E_* errors in a usable way",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/ErrorHandler/issues",
|
||||
"source": "https://github.com/pmmp/ErrorHandler/tree/0.6.0"
|
||||
"source": "https://github.com/pmmp/ErrorHandler/tree/0.7.0"
|
||||
},
|
||||
"time": "2022-01-08T21:05:46+00:00"
|
||||
"time": "2024-04-02T18:29:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
@ -921,22 +921,23 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.4.3",
|
||||
"version": "v6.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb"
|
||||
"reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
|
||||
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/78dde75f8f6dbbca4ec436a4b0087f7af02076d4",
|
||||
"reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-mbstring": "~1.8"
|
||||
"symfony/polyfill-mbstring": "~1.8",
|
||||
"symfony/process": "^5.4|^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -964,7 +965,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.3"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -980,7 +981,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-23T14:51:35+00:00"
|
||||
"time": "2024-04-18T09:22:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@ -1140,6 +1141,67 @@
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v6.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "cdb1c81c145fd5aa9b0038bab694035020943381"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/cdb1c81c145fd5aa9b0038bab694035020943381",
|
||||
"reference": "cdb1c81c145fd5aa9b0038bab694035020943381",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v6.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-18T09:22:46+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@ -1380,16 +1442,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.60",
|
||||
"version": "1.10.67",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe"
|
||||
"reference": "16ddbe776f10da6a95ebd25de7c1dbed397dc493"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
|
||||
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/16ddbe776f10da6a95ebd25de7c1dbed397dc493",
|
||||
"reference": "16ddbe776f10da6a95ebd25de7c1dbed397dc493",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1432,13 +1494,9 @@
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-07T13:30:19+00:00"
|
||||
"time": "2024-04-16T07:22:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
@ -1494,21 +1552,21 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "1.5.2",
|
||||
"version": "1.5.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542"
|
||||
"reference": "2e193a07651a6f4be3baa44ddb21d822681f5918"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542",
|
||||
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/2e193a07651a6f4be3baa44ddb21d822681f5918",
|
||||
"reference": "2e193a07651a6f4be3baa44ddb21d822681f5918",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"phpstan/phpstan": "^1.10.34"
|
||||
"phpstan/phpstan": "^1.10.60"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
@ -1537,9 +1595,9 @@
|
||||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.5"
|
||||
},
|
||||
"time": "2023-10-30T14:35:06+00:00"
|
||||
"time": "2024-04-19T15:12:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
@ -2335,16 +2393,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "6.0.1",
|
||||
"version": "6.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951"
|
||||
"reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951",
|
||||
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
|
||||
"reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2359,7 +2417,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "6.0-dev"
|
||||
"dev-main": "6.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2387,7 +2445,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||
"security": "https://github.com/sebastianbergmann/environment/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/6.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2395,7 +2453,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-11T05:39:26+00:00"
|
||||
"time": "2024-03-23T08:47:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
@ -2953,7 +3011,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pmmpthread": "^6.0.7",
|
||||
"ext-pmmpthread": "^6.1.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
|
@ -47,4 +47,6 @@ final class BootstrapOptions{
|
||||
public const DATA = "data";
|
||||
/** Shows basic server version information and exits */
|
||||
public const VERSION = "version";
|
||||
/** Disables writing logs to server.log */
|
||||
public const NO_LOG_FILE = "no-log-file";
|
||||
}
|
||||
|
@ -124,8 +124,8 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
|
||||
if(version_compare($pmmpthread_version, "6.0.7") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
|
||||
$messages[] = "pmmpthread ^6.0.7 is required, while you have $pmmpthread_version.";
|
||||
if(version_compare($pmmpthread_version, "6.1.0") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
|
||||
$messages[] = "pmmpthread ^6.1.0 is required, while you have $pmmpthread_version.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,7 +317,7 @@ JIT_WARNING
|
||||
//Logger has a dependency on timezone
|
||||
Timezone::init();
|
||||
|
||||
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI]);
|
||||
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI, BootstrapOptions::NO_LOG_FILE]);
|
||||
if(isset($opts[BootstrapOptions::ENABLE_ANSI])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){
|
||||
@ -325,8 +325,13 @@ JIT_WARNING
|
||||
}else{
|
||||
Terminal::init();
|
||||
}
|
||||
$logFile = isset($opts[BootstrapOptions::NO_LOG_FILE]) ? null : Path::join($dataPath, "server.log");
|
||||
|
||||
$logger = new MainLogger($logFile, Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()), false, Path::join($dataPath, "log_archive"));
|
||||
if($logFile === null){
|
||||
$logger->notice("Logging to file disabled. Ensure logs are collected by other means (e.g. Docker logs).");
|
||||
}
|
||||
|
||||
$logger = new MainLogger(Path::join($dataPath, "server.log"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()));
|
||||
\GlobalLogger::set($logger);
|
||||
|
||||
emit_performance_warnings($logger);
|
||||
|
@ -1668,9 +1668,11 @@ class Server{
|
||||
$this->isRunning = false;
|
||||
|
||||
//Force minimum uptime to be >= 120 seconds, to reduce the impact of spammy crash loops
|
||||
$spacing = ((int) $this->startTime) - time() + 120;
|
||||
$uptime = time() - ((int) $this->startTime);
|
||||
$minUptime = 120;
|
||||
$spacing = $minUptime - $uptime;
|
||||
if($spacing > 0){
|
||||
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
echo "--- Uptime {$uptime}s - waiting {$spacing}s to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
sleep($spacing);
|
||||
}
|
||||
@Process::kill(Process::pid());
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.13.1";
|
||||
public const BASE_VERSION = "5.15.1";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
@ -63,7 +63,8 @@ final class VersionInfo{
|
||||
if(\Phar::running(true) === ""){
|
||||
$gitHash = Git::getRepositoryStatePretty(\pocketmine\PATH);
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$pharPath = \Phar::running(false);
|
||||
$phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath);
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
@ -82,7 +83,8 @@ final class VersionInfo{
|
||||
if(self::$buildNumber === null){
|
||||
self::$buildNumber = 0;
|
||||
if(\Phar::running(true) !== ""){
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$pharPath = \Phar::running(false);
|
||||
$phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath);
|
||||
$meta = $phar->getMetadata();
|
||||
if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){
|
||||
self::$buildNumber = $meta["build"];
|
||||
|
@ -34,6 +34,7 @@ final class NetherRoots extends Flowable{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$supportBlock->getTypeId() === BlockTypeIds::SOUL_SOIL;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\SweetBerriesPickSound;
|
||||
use function mt_rand;
|
||||
|
||||
class SweetBerryBush extends Flowable{
|
||||
@ -81,6 +82,7 @@ class SweetBerryBush extends Flowable{
|
||||
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
|
||||
$world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
|
||||
$world->dropItem($this->position, $this->asItem()->setCount($dropAmount));
|
||||
$world->addSound($this->position, new SweetBerriesPickSound());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -35,6 +35,7 @@ enum BannerPatternType{
|
||||
case DIAGONAL_UP_LEFT;
|
||||
case DIAGONAL_UP_RIGHT;
|
||||
case FLOWER;
|
||||
case GLOBE;
|
||||
case GRADIENT;
|
||||
case GRADIENT_UP;
|
||||
case HALF_HORIZONTAL;
|
||||
@ -42,6 +43,7 @@ enum BannerPatternType{
|
||||
case HALF_VERTICAL;
|
||||
case HALF_VERTICAL_RIGHT;
|
||||
case MOJANG;
|
||||
case PIGLIN;
|
||||
case RHOMBUS;
|
||||
case SKULL;
|
||||
case SMALL_STRIPES;
|
||||
|
@ -63,6 +63,12 @@ use function strpos;
|
||||
use function substr;
|
||||
use function zend_version;
|
||||
use function zlib_encode;
|
||||
use const E_COMPILE_ERROR;
|
||||
use const E_CORE_ERROR;
|
||||
use const E_ERROR;
|
||||
use const E_PARSE;
|
||||
use const E_RECOVERABLE_ERROR;
|
||||
use const E_USER_ERROR;
|
||||
use const FILE_IGNORE_NEW_LINES;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
@ -85,6 +91,9 @@ class CrashDump{
|
||||
public const PLUGIN_INVOLVEMENT_DIRECT = "direct";
|
||||
public const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
|
||||
|
||||
public const FATAL_ERROR_MASK =
|
||||
E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
|
||||
|
||||
private CrashDumpData $data;
|
||||
private string $encodedData;
|
||||
|
||||
@ -186,7 +195,7 @@ class CrashDump{
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = error_get_last();
|
||||
if($error === null){
|
||||
if($error === null || ($error["type"] & self::FATAL_ERROR_MASK) === 0){
|
||||
throw new \RuntimeException("Crash error information missing - did something use exit()?");
|
||||
}
|
||||
$error["trace"] = Utils::printableTrace(Utils::currentTrace(3)); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
|
@ -56,6 +56,7 @@ final class BannerPatternTypeIdMap{
|
||||
BannerPatternType::DIAGONAL_UP_LEFT => "ld",
|
||||
BannerPatternType::DIAGONAL_UP_RIGHT => "rud",
|
||||
BannerPatternType::FLOWER => "flo",
|
||||
BannerPatternType::GLOBE => "glb",
|
||||
BannerPatternType::GRADIENT => "gra",
|
||||
BannerPatternType::GRADIENT_UP => "gru",
|
||||
BannerPatternType::HALF_HORIZONTAL => "hh",
|
||||
@ -63,6 +64,7 @@ final class BannerPatternTypeIdMap{
|
||||
BannerPatternType::HALF_VERTICAL => "vh",
|
||||
BannerPatternType::HALF_VERTICAL_RIGHT => "vhr",
|
||||
BannerPatternType::MOJANG => "moj",
|
||||
BannerPatternType::PIGLIN => "pig",
|
||||
BannerPatternType::RHOMBUS => "mr",
|
||||
BannerPatternType::SKULL => "sku",
|
||||
BannerPatternType::SMALL_STRIPES => "ss",
|
||||
|
@ -42,8 +42,8 @@ final class BlockStateData{
|
||||
public const CURRENT_VERSION =
|
||||
(1 << 24) | //major
|
||||
(20 << 16) | //minor
|
||||
(70 << 8) | //patch
|
||||
(4); //revision
|
||||
(80 << 8) | //patch
|
||||
(3); //revision
|
||||
|
||||
public const TAG_NAME = "name";
|
||||
public const TAG_STATES = "states";
|
||||
|
@ -81,7 +81,6 @@ final class BlockStateNames{
|
||||
public const EXTINGUISHED = "extinguished";
|
||||
public const FACING_DIRECTION = "facing_direction";
|
||||
public const FILL_LEVEL = "fill_level";
|
||||
public const FLOWER_TYPE = "flower_type";
|
||||
public const GROUND_SIGN_DIRECTION = "ground_sign_direction";
|
||||
public const GROWING_PLANT_AGE = "growing_plant_age";
|
||||
public const GROWTH = "growth";
|
||||
@ -124,7 +123,6 @@ final class BlockStateNames{
|
||||
public const ROTATION = "rotation";
|
||||
public const SAND_STONE_TYPE = "sand_stone_type";
|
||||
public const SAND_TYPE = "sand_type";
|
||||
public const SAPLING_TYPE = "sapling_type";
|
||||
public const SCULK_SENSOR_PHASE = "sculk_sensor_phase";
|
||||
public const SEA_GRASS_TYPE = "sea_grass_type";
|
||||
public const SPONGE_TYPE = "sponge_type";
|
||||
|
@ -93,18 +93,6 @@ final class BlockStateStringValues{
|
||||
public const DRIPSTONE_THICKNESS_MIDDLE = "middle";
|
||||
public const DRIPSTONE_THICKNESS_TIP = "tip";
|
||||
|
||||
public const FLOWER_TYPE_ALLIUM = "allium";
|
||||
public const FLOWER_TYPE_CORNFLOWER = "cornflower";
|
||||
public const FLOWER_TYPE_HOUSTONIA = "houstonia";
|
||||
public const FLOWER_TYPE_LILY_OF_THE_VALLEY = "lily_of_the_valley";
|
||||
public const FLOWER_TYPE_ORCHID = "orchid";
|
||||
public const FLOWER_TYPE_OXEYE = "oxeye";
|
||||
public const FLOWER_TYPE_POPPY = "poppy";
|
||||
public const FLOWER_TYPE_TULIP_ORANGE = "tulip_orange";
|
||||
public const FLOWER_TYPE_TULIP_PINK = "tulip_pink";
|
||||
public const FLOWER_TYPE_TULIP_RED = "tulip_red";
|
||||
public const FLOWER_TYPE_TULIP_WHITE = "tulip_white";
|
||||
|
||||
public const LEVER_DIRECTION_DOWN_EAST_WEST = "down_east_west";
|
||||
public const LEVER_DIRECTION_DOWN_NORTH_SOUTH = "down_north_south";
|
||||
public const LEVER_DIRECTION_EAST = "east";
|
||||
@ -176,13 +164,6 @@ final class BlockStateStringValues{
|
||||
public const SAND_TYPE_NORMAL = "normal";
|
||||
public const SAND_TYPE_RED = "red";
|
||||
|
||||
public const SAPLING_TYPE_ACACIA = "acacia";
|
||||
public const SAPLING_TYPE_BIRCH = "birch";
|
||||
public const SAPLING_TYPE_DARK_OAK = "dark_oak";
|
||||
public const SAPLING_TYPE_JUNGLE = "jungle";
|
||||
public const SAPLING_TYPE_OAK = "oak";
|
||||
public const SAPLING_TYPE_SPRUCE = "spruce";
|
||||
|
||||
public const SEA_GRASS_TYPE_DEFAULT = "default";
|
||||
public const SEA_GRASS_TYPE_DOUBLE_BOT = "double_bot";
|
||||
public const SEA_GRASS_TYPE_DOUBLE_TOP = "double_top";
|
||||
|
@ -41,6 +41,7 @@ final class BlockTypeNames{
|
||||
public const ACACIA_LOG = "minecraft:acacia_log";
|
||||
public const ACACIA_PLANKS = "minecraft:acacia_planks";
|
||||
public const ACACIA_PRESSURE_PLATE = "minecraft:acacia_pressure_plate";
|
||||
public const ACACIA_SAPLING = "minecraft:acacia_sapling";
|
||||
public const ACACIA_SLAB = "minecraft:acacia_slab";
|
||||
public const ACACIA_STAIRS = "minecraft:acacia_stairs";
|
||||
public const ACACIA_STANDING_SIGN = "minecraft:acacia_standing_sign";
|
||||
@ -49,6 +50,7 @@ final class BlockTypeNames{
|
||||
public const ACACIA_WOOD = "minecraft:acacia_wood";
|
||||
public const ACTIVATOR_RAIL = "minecraft:activator_rail";
|
||||
public const AIR = "minecraft:air";
|
||||
public const ALLIUM = "minecraft:allium";
|
||||
public const ALLOW = "minecraft:allow";
|
||||
public const AMETHYST_BLOCK = "minecraft:amethyst_block";
|
||||
public const AMETHYST_CLUSTER = "minecraft:amethyst_cluster";
|
||||
@ -59,6 +61,7 @@ final class BlockTypeNames{
|
||||
public const AZALEA = "minecraft:azalea";
|
||||
public const AZALEA_LEAVES = "minecraft:azalea_leaves";
|
||||
public const AZALEA_LEAVES_FLOWERED = "minecraft:azalea_leaves_flowered";
|
||||
public const AZURE_BLUET = "minecraft:azure_bluet";
|
||||
public const BAMBOO = "minecraft:bamboo";
|
||||
public const BAMBOO_BLOCK = "minecraft:bamboo_block";
|
||||
public const BAMBOO_BUTTON = "minecraft:bamboo_button";
|
||||
@ -100,6 +103,7 @@ final class BlockTypeNames{
|
||||
public const BIRCH_LOG = "minecraft:birch_log";
|
||||
public const BIRCH_PLANKS = "minecraft:birch_planks";
|
||||
public const BIRCH_PRESSURE_PLATE = "minecraft:birch_pressure_plate";
|
||||
public const BIRCH_SAPLING = "minecraft:birch_sapling";
|
||||
public const BIRCH_SLAB = "minecraft:birch_slab";
|
||||
public const BIRCH_STAIRS = "minecraft:birch_stairs";
|
||||
public const BIRCH_STANDING_SIGN = "minecraft:birch_standing_sign";
|
||||
@ -130,6 +134,7 @@ final class BlockTypeNames{
|
||||
public const BLUE_CONCRETE_POWDER = "minecraft:blue_concrete_powder";
|
||||
public const BLUE_GLAZED_TERRACOTTA = "minecraft:blue_glazed_terracotta";
|
||||
public const BLUE_ICE = "minecraft:blue_ice";
|
||||
public const BLUE_ORCHID = "minecraft:blue_orchid";
|
||||
public const BLUE_SHULKER_BOX = "minecraft:blue_shulker_box";
|
||||
public const BLUE_STAINED_GLASS = "minecraft:blue_stained_glass";
|
||||
public const BLUE_STAINED_GLASS_PANE = "minecraft:blue_stained_glass_pane";
|
||||
@ -139,6 +144,7 @@ final class BlockTypeNames{
|
||||
public const BOOKSHELF = "minecraft:bookshelf";
|
||||
public const BORDER_BLOCK = "minecraft:border_block";
|
||||
public const BRAIN_CORAL = "minecraft:brain_coral";
|
||||
public const BRAIN_CORAL_FAN = "minecraft:brain_coral_fan";
|
||||
public const BREWING_STAND = "minecraft:brewing_stand";
|
||||
public const BRICK_BLOCK = "minecraft:brick_block";
|
||||
public const BRICK_STAIRS = "minecraft:brick_stairs";
|
||||
@ -157,6 +163,7 @@ final class BlockTypeNames{
|
||||
public const BROWN_WOOL = "minecraft:brown_wool";
|
||||
public const BUBBLE_COLUMN = "minecraft:bubble_column";
|
||||
public const BUBBLE_CORAL = "minecraft:bubble_coral";
|
||||
public const BUBBLE_CORAL_FAN = "minecraft:bubble_coral_fan";
|
||||
public const BUDDING_AMETHYST = "minecraft:budding_amethyst";
|
||||
public const CACTUS = "minecraft:cactus";
|
||||
public const CAKE = "minecraft:cake";
|
||||
@ -228,11 +235,10 @@ final class BlockTypeNames{
|
||||
public const COPPER_ORE = "minecraft:copper_ore";
|
||||
public const COPPER_TRAPDOOR = "minecraft:copper_trapdoor";
|
||||
public const CORAL_BLOCK = "minecraft:coral_block";
|
||||
public const CORAL_FAN = "minecraft:coral_fan";
|
||||
public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead";
|
||||
public const CORAL_FAN_HANG = "minecraft:coral_fan_hang";
|
||||
public const CORAL_FAN_HANG2 = "minecraft:coral_fan_hang2";
|
||||
public const CORAL_FAN_HANG3 = "minecraft:coral_fan_hang3";
|
||||
public const CORNFLOWER = "minecraft:cornflower";
|
||||
public const CRACKED_DEEPSLATE_BRICKS = "minecraft:cracked_deepslate_bricks";
|
||||
public const CRACKED_DEEPSLATE_TILES = "minecraft:cracked_deepslate_tiles";
|
||||
public const CRACKED_NETHER_BRICKS = "minecraft:cracked_nether_bricks";
|
||||
@ -282,6 +288,7 @@ final class BlockTypeNames{
|
||||
public const DARK_OAK_LOG = "minecraft:dark_oak_log";
|
||||
public const DARK_OAK_PLANKS = "minecraft:dark_oak_planks";
|
||||
public const DARK_OAK_PRESSURE_PLATE = "minecraft:dark_oak_pressure_plate";
|
||||
public const DARK_OAK_SAPLING = "minecraft:dark_oak_sapling";
|
||||
public const DARK_OAK_SLAB = "minecraft:dark_oak_slab";
|
||||
public const DARK_OAK_STAIRS = "minecraft:dark_oak_stairs";
|
||||
public const DARK_OAK_TRAPDOOR = "minecraft:dark_oak_trapdoor";
|
||||
@ -292,10 +299,15 @@ final class BlockTypeNames{
|
||||
public const DAYLIGHT_DETECTOR = "minecraft:daylight_detector";
|
||||
public const DAYLIGHT_DETECTOR_INVERTED = "minecraft:daylight_detector_inverted";
|
||||
public const DEAD_BRAIN_CORAL = "minecraft:dead_brain_coral";
|
||||
public const DEAD_BRAIN_CORAL_FAN = "minecraft:dead_brain_coral_fan";
|
||||
public const DEAD_BUBBLE_CORAL = "minecraft:dead_bubble_coral";
|
||||
public const DEAD_BUBBLE_CORAL_FAN = "minecraft:dead_bubble_coral_fan";
|
||||
public const DEAD_FIRE_CORAL = "minecraft:dead_fire_coral";
|
||||
public const DEAD_FIRE_CORAL_FAN = "minecraft:dead_fire_coral_fan";
|
||||
public const DEAD_HORN_CORAL = "minecraft:dead_horn_coral";
|
||||
public const DEAD_HORN_CORAL_FAN = "minecraft:dead_horn_coral_fan";
|
||||
public const DEAD_TUBE_CORAL = "minecraft:dead_tube_coral";
|
||||
public const DEAD_TUBE_CORAL_FAN = "minecraft:dead_tube_coral_fan";
|
||||
public const DEADBUSH = "minecraft:deadbush";
|
||||
public const DECORATED_POT = "minecraft:decorated_pot";
|
||||
public const DEEPSLATE = "minecraft:deepslate";
|
||||
@ -480,6 +492,7 @@ final class BlockTypeNames{
|
||||
public const FENCE_GATE = "minecraft:fence_gate";
|
||||
public const FIRE = "minecraft:fire";
|
||||
public const FIRE_CORAL = "minecraft:fire_coral";
|
||||
public const FIRE_CORAL_FAN = "minecraft:fire_coral_fan";
|
||||
public const FLETCHING_TABLE = "minecraft:fletching_table";
|
||||
public const FLOWER_POT = "minecraft:flower_pot";
|
||||
public const FLOWERING_AZALEA = "minecraft:flowering_azalea";
|
||||
@ -564,11 +577,13 @@ final class BlockTypeNames{
|
||||
public const HARD_YELLOW_STAINED_GLASS_PANE = "minecraft:hard_yellow_stained_glass_pane";
|
||||
public const HARDENED_CLAY = "minecraft:hardened_clay";
|
||||
public const HAY_BLOCK = "minecraft:hay_block";
|
||||
public const HEAVY_CORE = "minecraft:heavy_core";
|
||||
public const HEAVY_WEIGHTED_PRESSURE_PLATE = "minecraft:heavy_weighted_pressure_plate";
|
||||
public const HONEY_BLOCK = "minecraft:honey_block";
|
||||
public const HONEYCOMB_BLOCK = "minecraft:honeycomb_block";
|
||||
public const HOPPER = "minecraft:hopper";
|
||||
public const HORN_CORAL = "minecraft:horn_coral";
|
||||
public const HORN_CORAL_FAN = "minecraft:horn_coral_fan";
|
||||
public const ICE = "minecraft:ice";
|
||||
public const INFESTED_DEEPSLATE = "minecraft:infested_deepslate";
|
||||
public const INFO_UPDATE = "minecraft:info_update";
|
||||
@ -591,6 +606,7 @@ final class BlockTypeNames{
|
||||
public const JUNGLE_LOG = "minecraft:jungle_log";
|
||||
public const JUNGLE_PLANKS = "minecraft:jungle_planks";
|
||||
public const JUNGLE_PRESSURE_PLATE = "minecraft:jungle_pressure_plate";
|
||||
public const JUNGLE_SAPLING = "minecraft:jungle_sapling";
|
||||
public const JUNGLE_SLAB = "minecraft:jungle_slab";
|
||||
public const JUNGLE_STAIRS = "minecraft:jungle_stairs";
|
||||
public const JUNGLE_STANDING_SIGN = "minecraft:jungle_standing_sign";
|
||||
@ -630,6 +646,7 @@ final class BlockTypeNames{
|
||||
public const LIGHT_GRAY_WOOL = "minecraft:light_gray_wool";
|
||||
public const LIGHT_WEIGHTED_PRESSURE_PLATE = "minecraft:light_weighted_pressure_plate";
|
||||
public const LIGHTNING_ROD = "minecraft:lightning_rod";
|
||||
public const LILY_OF_THE_VALLEY = "minecraft:lily_of_the_valley";
|
||||
public const LIME_CANDLE = "minecraft:lime_candle";
|
||||
public const LIME_CANDLE_CAKE = "minecraft:lime_candle_cake";
|
||||
public const LIME_CARPET = "minecraft:lime_carpet";
|
||||
@ -717,6 +734,7 @@ final class BlockTypeNames{
|
||||
public const OAK_LEAVES = "minecraft:oak_leaves";
|
||||
public const OAK_LOG = "minecraft:oak_log";
|
||||
public const OAK_PLANKS = "minecraft:oak_planks";
|
||||
public const OAK_SAPLING = "minecraft:oak_sapling";
|
||||
public const OAK_SLAB = "minecraft:oak_slab";
|
||||
public const OAK_STAIRS = "minecraft:oak_stairs";
|
||||
public const OAK_WOOD = "minecraft:oak_wood";
|
||||
@ -733,7 +751,9 @@ final class BlockTypeNames{
|
||||
public const ORANGE_STAINED_GLASS = "minecraft:orange_stained_glass";
|
||||
public const ORANGE_STAINED_GLASS_PANE = "minecraft:orange_stained_glass_pane";
|
||||
public const ORANGE_TERRACOTTA = "minecraft:orange_terracotta";
|
||||
public const ORANGE_TULIP = "minecraft:orange_tulip";
|
||||
public const ORANGE_WOOL = "minecraft:orange_wool";
|
||||
public const OXEYE_DAISY = "minecraft:oxeye_daisy";
|
||||
public const OXIDIZED_CHISELED_COPPER = "minecraft:oxidized_chiseled_copper";
|
||||
public const OXIDIZED_COPPER = "minecraft:oxidized_copper";
|
||||
public const OXIDIZED_COPPER_BULB = "minecraft:oxidized_copper_bulb";
|
||||
@ -758,6 +778,7 @@ final class BlockTypeNames{
|
||||
public const PINK_STAINED_GLASS = "minecraft:pink_stained_glass";
|
||||
public const PINK_STAINED_GLASS_PANE = "minecraft:pink_stained_glass_pane";
|
||||
public const PINK_TERRACOTTA = "minecraft:pink_terracotta";
|
||||
public const PINK_TULIP = "minecraft:pink_tulip";
|
||||
public const PINK_WOOL = "minecraft:pink_wool";
|
||||
public const PISTON = "minecraft:piston";
|
||||
public const PISTON_ARM_COLLISION = "minecraft:piston_arm_collision";
|
||||
@ -794,6 +815,7 @@ final class BlockTypeNames{
|
||||
public const POLISHED_TUFF_SLAB = "minecraft:polished_tuff_slab";
|
||||
public const POLISHED_TUFF_STAIRS = "minecraft:polished_tuff_stairs";
|
||||
public const POLISHED_TUFF_WALL = "minecraft:polished_tuff_wall";
|
||||
public const POPPY = "minecraft:poppy";
|
||||
public const PORTAL = "minecraft:portal";
|
||||
public const POTATOES = "minecraft:potatoes";
|
||||
public const POWDER_SNOW = "minecraft:powder_snow";
|
||||
@ -830,7 +852,6 @@ final class BlockTypeNames{
|
||||
public const RED_CARPET = "minecraft:red_carpet";
|
||||
public const RED_CONCRETE = "minecraft:red_concrete";
|
||||
public const RED_CONCRETE_POWDER = "minecraft:red_concrete_powder";
|
||||
public const RED_FLOWER = "minecraft:red_flower";
|
||||
public const RED_GLAZED_TERRACOTTA = "minecraft:red_glazed_terracotta";
|
||||
public const RED_MUSHROOM = "minecraft:red_mushroom";
|
||||
public const RED_MUSHROOM_BLOCK = "minecraft:red_mushroom_block";
|
||||
@ -842,6 +863,7 @@ final class BlockTypeNames{
|
||||
public const RED_STAINED_GLASS = "minecraft:red_stained_glass";
|
||||
public const RED_STAINED_GLASS_PANE = "minecraft:red_stained_glass_pane";
|
||||
public const RED_TERRACOTTA = "minecraft:red_terracotta";
|
||||
public const RED_TULIP = "minecraft:red_tulip";
|
||||
public const RED_WOOL = "minecraft:red_wool";
|
||||
public const REDSTONE_BLOCK = "minecraft:redstone_block";
|
||||
public const REDSTONE_LAMP = "minecraft:redstone_lamp";
|
||||
@ -856,7 +878,6 @@ final class BlockTypeNames{
|
||||
public const SAND = "minecraft:sand";
|
||||
public const SANDSTONE = "minecraft:sandstone";
|
||||
public const SANDSTONE_STAIRS = "minecraft:sandstone_stairs";
|
||||
public const SAPLING = "minecraft:sapling";
|
||||
public const SCAFFOLDING = "minecraft:scaffolding";
|
||||
public const SCULK = "minecraft:sculk";
|
||||
public const SCULK_CATALYST = "minecraft:sculk_catalyst";
|
||||
@ -900,6 +921,7 @@ final class BlockTypeNames{
|
||||
public const SPRUCE_LOG = "minecraft:spruce_log";
|
||||
public const SPRUCE_PLANKS = "minecraft:spruce_planks";
|
||||
public const SPRUCE_PRESSURE_PLATE = "minecraft:spruce_pressure_plate";
|
||||
public const SPRUCE_SAPLING = "minecraft:spruce_sapling";
|
||||
public const SPRUCE_SLAB = "minecraft:spruce_slab";
|
||||
public const SPRUCE_STAIRS = "minecraft:spruce_stairs";
|
||||
public const SPRUCE_STANDING_SIGN = "minecraft:spruce_standing_sign";
|
||||
@ -961,6 +983,7 @@ final class BlockTypeNames{
|
||||
public const TRIP_WIRE = "minecraft:trip_wire";
|
||||
public const TRIPWIRE_HOOK = "minecraft:tripwire_hook";
|
||||
public const TUBE_CORAL = "minecraft:tube_coral";
|
||||
public const TUBE_CORAL_FAN = "minecraft:tube_coral_fan";
|
||||
public const TUFF = "minecraft:tuff";
|
||||
public const TUFF_BRICK_DOUBLE_SLAB = "minecraft:tuff_brick_double_slab";
|
||||
public const TUFF_BRICK_SLAB = "minecraft:tuff_brick_slab";
|
||||
@ -1068,6 +1091,7 @@ final class BlockTypeNames{
|
||||
public const WHITE_STAINED_GLASS = "minecraft:white_stained_glass";
|
||||
public const WHITE_STAINED_GLASS_PANE = "minecraft:white_stained_glass_pane";
|
||||
public const WHITE_TERRACOTTA = "minecraft:white_terracotta";
|
||||
public const WHITE_TULIP = "minecraft:white_tulip";
|
||||
public const WHITE_WOOL = "minecraft:white_wool";
|
||||
public const WITHER_ROSE = "minecraft:wither_rose";
|
||||
public const WOODEN_BUTTON = "minecraft:wooden_button";
|
||||
|
@ -203,6 +203,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->registerCauldronSerializers();
|
||||
$this->registerFlatWoodBlockSerializers();
|
||||
$this->registerLeavesSerializers();
|
||||
$this->registerSaplingSerializers();
|
||||
$this->registerSimpleSerializers();
|
||||
$this->registerSerializers();
|
||||
}
|
||||
@ -536,6 +537,20 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL,
|
||||
}
|
||||
));
|
||||
|
||||
$this->map(Blocks::CORAL_FAN(), fn(FloorCoralFan $block) => Writer::create(
|
||||
match($block->getCoralType()){
|
||||
CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_FAN : Ids::BRAIN_CORAL_FAN,
|
||||
CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_FAN : Ids::BUBBLE_CORAL_FAN,
|
||||
CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_FAN : Ids::FIRE_CORAL_FAN,
|
||||
CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_FAN : Ids::HORN_CORAL_FAN,
|
||||
CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_FAN : Ids::TUBE_CORAL_FAN,
|
||||
})
|
||||
->writeInt(StateNames::CORAL_FAN_DIRECTION, match($axis = $block->getAxis()){
|
||||
Axis::X => 0,
|
||||
Axis::Z => 1,
|
||||
default => throw new BlockStateSerializeException("Invalid axis {$axis}"),
|
||||
}));
|
||||
}
|
||||
|
||||
private function registerCauldronSerializers() : void{
|
||||
@ -725,6 +740,19 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::SPRUCE_LEAVES)));
|
||||
}
|
||||
|
||||
private function registerSaplingSerializers() : void{
|
||||
foreach([
|
||||
Ids::ACACIA_SAPLING => Blocks::ACACIA_SAPLING(),
|
||||
Ids::BIRCH_SAPLING => Blocks::BIRCH_SAPLING(),
|
||||
Ids::DARK_OAK_SAPLING => Blocks::DARK_OAK_SAPLING(),
|
||||
Ids::JUNGLE_SAPLING => Blocks::JUNGLE_SAPLING(),
|
||||
Ids::OAK_SAPLING => Blocks::OAK_SAPLING(),
|
||||
Ids::SPRUCE_SAPLING => Blocks::SPRUCE_SAPLING(),
|
||||
] as $id => $block){
|
||||
$this->map($block, fn(Sapling $block) => Helper::encodeSapling($block, new Writer($id)));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerSimpleSerializers() : void{
|
||||
$this->mapSimple(Blocks::AIR(), Ids::AIR);
|
||||
$this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK);
|
||||
@ -983,16 +1011,26 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK);
|
||||
$this->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS);
|
||||
$this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE);
|
||||
|
||||
$this->mapSimple(Blocks::ALLIUM(), Ids::ALLIUM);
|
||||
$this->mapSimple(Blocks::CORNFLOWER(), Ids::CORNFLOWER);
|
||||
$this->mapSimple(Blocks::AZURE_BLUET(), Ids::AZURE_BLUET);
|
||||
$this->mapSimple(Blocks::LILY_OF_THE_VALLEY(), Ids::LILY_OF_THE_VALLEY);
|
||||
$this->mapSimple(Blocks::BLUE_ORCHID(), Ids::BLUE_ORCHID);
|
||||
$this->mapSimple(Blocks::OXEYE_DAISY(), Ids::OXEYE_DAISY);
|
||||
$this->mapSimple(Blocks::POPPY(), Ids::POPPY);
|
||||
$this->mapSimple(Blocks::ORANGE_TULIP(), Ids::ORANGE_TULIP);
|
||||
$this->mapSimple(Blocks::PINK_TULIP(), Ids::PINK_TULIP);
|
||||
$this->mapSimple(Blocks::RED_TULIP(), Ids::RED_TULIP);
|
||||
$this->mapSimple(Blocks::WHITE_TULIP(), Ids::WHITE_TULIP);
|
||||
}
|
||||
|
||||
private function registerSerializers() : void{
|
||||
$this->map(Blocks::ACACIA_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_ACACIA));
|
||||
$this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{
|
||||
return Writer::create(Ids::ACTIVATOR_RAIL)
|
||||
->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered())
|
||||
->writeInt(StateNames::RAIL_DIRECTION, $block->getShape());
|
||||
});
|
||||
$this->map(Blocks::ALLIUM(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_ALLIUM));
|
||||
$this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
|
||||
->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM));
|
||||
$this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create(
|
||||
@ -1018,7 +1056,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"),
|
||||
});
|
||||
});
|
||||
$this->map(Blocks::AZURE_BLUET(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_HOUSTONIA));
|
||||
$this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{
|
||||
return Writer::create(Ids::BAMBOO)
|
||||
->writeBool(StateNames::AGE_BIT, $block->isReady())
|
||||
@ -1032,10 +1069,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
});
|
||||
$this->map(Blocks::BAMBOO_SAPLING(), function(BambooSapling $block) : Writer{
|
||||
return Writer::create(Ids::BAMBOO_SAPLING)
|
||||
->writeBool(StateNames::AGE_BIT, $block->isReady())
|
||||
|
||||
//TODO: bug in MCPE
|
||||
->writeString(StateNames::SAPLING_TYPE, StringValues::SAPLING_TYPE_OAK);
|
||||
->writeBool(StateNames::AGE_BIT, $block->isReady());
|
||||
});
|
||||
$this->map(Blocks::BANNER(), function(FloorBanner $block) : Writer{
|
||||
return Writer::create(Ids::STANDING_BANNER)
|
||||
@ -1085,12 +1119,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE)
|
||||
->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false);
|
||||
});
|
||||
$this->map(Blocks::BIRCH_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_BIRCH));
|
||||
$this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS);
|
||||
$this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL)));
|
||||
$this->map(Blocks::BLAST_FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::BLAST_FURNACE, Ids::LIT_BLAST_FURNACE));
|
||||
$this->map(Blocks::BLUE_ORCHID(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_ORCHID));
|
||||
$this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_BP)));
|
||||
$this->map(Blocks::BONE_BLOCK(), function(BoneBlock $block) : Writer{
|
||||
return Writer::create(Ids::BONE_BLOCK)
|
||||
@ -1247,22 +1279,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeBool(StateNames::DEAD_BIT, $block->isDead())
|
||||
->writeCoralType($block->getCoralType());
|
||||
});
|
||||
$this->map(Blocks::CORAL_FAN(), function(FloorCoralFan $block) : Writer{
|
||||
return Writer::create($block->isDead() ? Ids::CORAL_FAN_DEAD : Ids::CORAL_FAN)
|
||||
->writeCoralType($block->getCoralType())
|
||||
->writeInt(StateNames::CORAL_FAN_DIRECTION, match($axis = $block->getAxis()){
|
||||
Axis::X => 0,
|
||||
Axis::Z => 1,
|
||||
default => throw new BlockStateSerializeException("Invalid axis {$axis}"),
|
||||
});
|
||||
});
|
||||
$this->map(Blocks::CORNFLOWER(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_CORNFLOWER));
|
||||
$this->map(Blocks::CRACKED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CRACKED));
|
||||
$this->map(Blocks::CUT_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
|
||||
$this->map(Blocks::CUT_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE));
|
||||
$this->map(Blocks::CUT_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
|
||||
$this->map(Blocks::CUT_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE));
|
||||
$this->map(Blocks::DARK_OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_DARK_OAK));
|
||||
$this->map(Blocks::DARK_PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE)
|
||||
->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DARK));
|
||||
$this->map(Blocks::DARK_PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK));
|
||||
@ -1382,7 +1403,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR)));
|
||||
$this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR)));
|
||||
$this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME));
|
||||
$this->map(Blocks::JUNGLE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_JUNGLE));
|
||||
$this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE, new Writer(Ids::CHEMISTRY_TABLE)));
|
||||
$this->map(Blocks::LADDER(), function(Ladder $block) : Writer{
|
||||
return Writer::create(Ids::LADDER)
|
||||
@ -1422,7 +1442,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeFacingDirection($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::LILAC(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_SYRINGA, Writer::create(Ids::DOUBLE_PLANT)));
|
||||
$this->map(Blocks::LILY_OF_THE_VALLEY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_LILY_OF_THE_VALLEY));
|
||||
$this->map(Blocks::LIT_PUMPKIN(), function(LitPumpkin $block) : Writer{
|
||||
return Writer::create(Ids::LIT_PUMPKIN)
|
||||
->writeCardinalHorizontalFacing($block->getFacing());
|
||||
@ -1466,16 +1485,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::NETHER_WART)
|
||||
->writeInt(StateNames::AGE, $block->getAge());
|
||||
});
|
||||
$this->map(Blocks::OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_OAK));
|
||||
$this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE));
|
||||
$this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE));
|
||||
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT)));
|
||||
$this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{
|
||||
return Writer::create(Ids::PINK_PETALS)
|
||||
->writeCardinalHorizontalFacing($block->getFacing())
|
||||
->writeInt(StateNames::GROWTH, $block->getCount() - 1);
|
||||
});
|
||||
$this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK));
|
||||
$this->map(Blocks::PITCHER_PLANT(), function(DoublePlant $block) : Writer{
|
||||
return Writer::create(Ids::PITCHER_PLANT)
|
||||
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
|
||||
@ -1511,7 +1526,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS);
|
||||
$this->map(Blocks::POLISHED_GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_GRANITE));
|
||||
$this->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS);
|
||||
$this->map(Blocks::POPPY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_POPPY));
|
||||
$this->map(Blocks::POTATOES(), fn(Potato $block) => Helper::encodeCrops($block, new Writer(Ids::POTATOES)));
|
||||
$this->map(Blocks::POWERED_RAIL(), function(PoweredRail $block) : Writer{
|
||||
return Writer::create(Ids::GOLDEN_RAIL)
|
||||
@ -1585,7 +1599,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS);
|
||||
$this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE));
|
||||
$this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_RG)));
|
||||
$this->map(Blocks::RED_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_RED));
|
||||
$this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_ROSE, Writer::create(Ids::DOUBLE_PLANT)));
|
||||
$this->map(Blocks::SAND(), fn() => Writer::create(Ids::SAND)
|
||||
->writeString(StateNames::SAND_TYPE, StringValues::SAND_TYPE_NORMAL));
|
||||
@ -1635,7 +1648,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::SPONGE)
|
||||
->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY);
|
||||
});
|
||||
$this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE));
|
||||
$this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK)
|
||||
->writeCardinalHorizontalFacing($block->getFacing()));
|
||||
$this->map(Blocks::STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_DEFAULT));
|
||||
@ -1728,6 +1740,5 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength());
|
||||
});
|
||||
$this->map(Blocks::WHEAT(), fn(Wheat $block) => Helper::encodeCrops($block, new Writer(Ids::WHEAT)));
|
||||
$this->map(Blocks::WHITE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_WHITE));
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ use pocketmine\block\Liquid;
|
||||
use pocketmine\block\RedMushroomBlock;
|
||||
use pocketmine\block\RedstoneComparator;
|
||||
use pocketmine\block\RedstoneRepeater;
|
||||
use pocketmine\block\Sapling;
|
||||
use pocketmine\block\SimplePressurePlate;
|
||||
use pocketmine\block\Slab;
|
||||
use pocketmine\block\Stair;
|
||||
@ -149,7 +150,6 @@ final class BlockStateDeserializerHelper{
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{
|
||||
return $block
|
||||
->setCoralType($in->readCoralType())
|
||||
->setAxis(match($in->readBoundedInt(BlockStateNames::CORAL_FAN_DIRECTION, 0, 1)){
|
||||
0 => Axis::X,
|
||||
1 => Axis::Z,
|
||||
@ -220,6 +220,12 @@ final class BlockStateDeserializerHelper{
|
||||
->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1);
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{
|
||||
return $block
|
||||
->setReady($in->readBool(BlockStateNames::AGE_BIT));
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public static function decodeSimplePressurePlate(SimplePressurePlate $block, BlockStateReader $in) : SimplePressurePlate{
|
||||
//TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation?
|
||||
|
@ -38,20 +38,24 @@ use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
|
||||
final class BlockStateReader{
|
||||
|
||||
/**
|
||||
* @var true[]
|
||||
* @phpstan-var array<string, true>
|
||||
* @var Tag[]
|
||||
* @phpstan-var array<string, Tag>
|
||||
*/
|
||||
private array $usedStates = [];
|
||||
private array $unusedStates;
|
||||
|
||||
public function __construct(
|
||||
private BlockStateData $data
|
||||
){}
|
||||
){
|
||||
$this->unusedStates = $this->data->getStates();
|
||||
}
|
||||
|
||||
public function missingOrWrongTypeException(string $name, ?Tag $tag) : BlockStateDeserializeException{
|
||||
return new BlockStateDeserializeException("Property \"$name\" " . ($tag !== null ? "has unexpected type " . get_class($tag) : "is missing"));
|
||||
@ -66,7 +70,7 @@ final class BlockStateReader{
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readBool(string $name) : bool{
|
||||
$this->usedStates[$name] = true;
|
||||
unset($this->unusedStates[$name]);
|
||||
$tag = $this->data->getState($name);
|
||||
if($tag instanceof ByteTag){
|
||||
switch($tag->getValue()){
|
||||
@ -80,7 +84,7 @@ final class BlockStateReader{
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readInt(string $name) : int{
|
||||
$this->usedStates[$name] = true;
|
||||
unset($this->unusedStates[$name]);
|
||||
$tag = $this->data->getState($name);
|
||||
if($tag instanceof IntTag){
|
||||
return $tag->getValue();
|
||||
@ -99,7 +103,7 @@ final class BlockStateReader{
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readString(string $name) : string{
|
||||
$this->usedStates[$name] = true;
|
||||
unset($this->unusedStates[$name]);
|
||||
//TODO: only allow a specific set of values (strings are primarily used for enums)
|
||||
$tag = $this->data->getState($name);
|
||||
if($tag instanceof StringTag){
|
||||
@ -346,7 +350,7 @@ final class BlockStateReader{
|
||||
*/
|
||||
public function ignored(string $name) : void{
|
||||
if($this->data->getState($name) !== null){
|
||||
$this->usedStates[$name] = true;
|
||||
unset($this->unusedStates[$name]);
|
||||
}else{
|
||||
throw $this->missingOrWrongTypeException($name, null);
|
||||
}
|
||||
@ -363,10 +367,8 @@ final class BlockStateReader{
|
||||
* @throws BlockStateDeserializeException
|
||||
*/
|
||||
public function checkUnreadProperties() : void{
|
||||
foreach(Utils::stringifyKeys($this->data->getStates()) as $name => $tag){
|
||||
if(!isset($this->usedStates[$name])){
|
||||
throw new BlockStateDeserializeException("Unread property \"$name\"");
|
||||
}
|
||||
if(count($this->unusedStates) > 0){
|
||||
throw new BlockStateDeserializeException("Unread properties: " . implode(", ", array_keys($this->unusedStates)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,18 +167,13 @@ final class BlockStateSerializerHelper{
|
||||
->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway
|
||||
}
|
||||
|
||||
public static function encodeRedFlower(string $type) : Writer{
|
||||
return Writer::create(Ids::RED_FLOWER)->writeString(BlockStateNames::FLOWER_TYPE, $type);
|
||||
}
|
||||
|
||||
public static function encodeSandstone(string $id, string $type) : Writer{
|
||||
return Writer::create($id)->writeString(BlockStateNames::SAND_STONE_TYPE, $type);
|
||||
}
|
||||
|
||||
public static function encodeSapling(Sapling $block, string $type) : Writer{
|
||||
return Writer::create(Ids::SAPLING)
|
||||
->writeBool(BlockStateNames::AGE_BIT, $block->isReady())
|
||||
->writeString(BlockStateNames::SAPLING_TYPE, $type);
|
||||
public static function encodeSapling(Sapling $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::AGE_BIT, $block->isReady());
|
||||
}
|
||||
|
||||
public static function encodeSimplePressurePlate(SimplePressurePlate $block, Writer $out) : Writer{
|
||||
|
@ -83,6 +83,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->registerCauldronDeserializers();
|
||||
$this->registerFlatWoodBlockDeserializers();
|
||||
$this->registerLeavesDeserializers();
|
||||
$this->registerSaplingDeserializers();
|
||||
$this->registerSimpleDeserializers();
|
||||
$this->registerDeserializers();
|
||||
}
|
||||
@ -438,6 +439,17 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
] as $id => $coralType){
|
||||
$this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(true));
|
||||
}
|
||||
|
||||
foreach([
|
||||
[CoralType::BRAIN, Ids::BRAIN_CORAL_FAN, Ids::DEAD_BRAIN_CORAL_FAN],
|
||||
[CoralType::BUBBLE, Ids::BUBBLE_CORAL_FAN, Ids::DEAD_BUBBLE_CORAL_FAN],
|
||||
[CoralType::FIRE, Ids::FIRE_CORAL_FAN, Ids::DEAD_FIRE_CORAL_FAN],
|
||||
[CoralType::HORN, Ids::HORN_CORAL_FAN, Ids::DEAD_HORN_CORAL_FAN],
|
||||
[CoralType::TUBE, Ids::TUBE_CORAL_FAN, Ids::DEAD_TUBE_CORAL_FAN],
|
||||
] as [$coralType, $aliveId, $deadId]){
|
||||
$this->map($aliveId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(false), $in));
|
||||
$this->map($deadId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(true), $in));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerCauldronDeserializers() : void{
|
||||
@ -622,6 +634,19 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in));
|
||||
}
|
||||
|
||||
private function registerSaplingDeserializers() : void{
|
||||
foreach([
|
||||
Ids::ACACIA_SAPLING => fn() => Blocks::ACACIA_SAPLING(),
|
||||
Ids::BIRCH_SAPLING => fn() => Blocks::BIRCH_SAPLING(),
|
||||
Ids::DARK_OAK_SAPLING => fn() => Blocks::DARK_OAK_SAPLING(),
|
||||
Ids::JUNGLE_SAPLING => fn() => Blocks::JUNGLE_SAPLING(),
|
||||
Ids::OAK_SAPLING => fn() => Blocks::OAK_SAPLING(),
|
||||
Ids::SPRUCE_SAPLING => fn() => Blocks::SPRUCE_SAPLING(),
|
||||
] as $id => $getBlock){
|
||||
$this->map($id, fn(Reader $in) => Helper::decodeSapling($getBlock(), $in));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerSimpleDeserializers() : void{
|
||||
$this->mapSimple(Ids::AIR, fn() => Blocks::AIR());
|
||||
$this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST());
|
||||
@ -880,6 +905,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
|
||||
$this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE());
|
||||
$this->mapSimple(Ids::YELLOW_FLOWER, fn() => Blocks::DANDELION());
|
||||
|
||||
$this->mapSimple(Ids::ALLIUM, fn() => Blocks::ALLIUM());
|
||||
$this->mapSimple(Ids::CORNFLOWER, fn() => Blocks::CORNFLOWER());
|
||||
$this->mapSimple(Ids::AZURE_BLUET, fn() => Blocks::AZURE_BLUET());
|
||||
$this->mapSimple(Ids::LILY_OF_THE_VALLEY, fn() => Blocks::LILY_OF_THE_VALLEY());
|
||||
$this->mapSimple(Ids::BLUE_ORCHID, fn() => Blocks::BLUE_ORCHID());
|
||||
$this->mapSimple(Ids::OXEYE_DAISY, fn() => Blocks::OXEYE_DAISY());
|
||||
$this->mapSimple(Ids::POPPY, fn() => Blocks::POPPY());
|
||||
$this->mapSimple(Ids::ORANGE_TULIP, fn() => Blocks::ORANGE_TULIP());
|
||||
$this->mapSimple(Ids::PINK_TULIP, fn() => Blocks::PINK_TULIP());
|
||||
$this->mapSimple(Ids::RED_TULIP, fn() => Blocks::RED_TULIP());
|
||||
$this->mapSimple(Ids::WHITE_TULIP, fn() => Blocks::WHITE_TULIP());
|
||||
}
|
||||
|
||||
private function registerDeserializers() : void{
|
||||
@ -921,7 +958,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
});
|
||||
});
|
||||
$this->map(Ids::BAMBOO_SAPLING, function(Reader $in) : Block{
|
||||
$in->ignored(StateNames::SAPLING_TYPE); //bug in MCPE
|
||||
return Blocks::BAMBOO_SAPLING()->setReady($in->readBool(StateNames::AGE_BIT));
|
||||
});
|
||||
$this->map(Ids::BARREL, function(Reader $in) : Block{
|
||||
@ -1078,10 +1114,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setCoralType($in->readCoralType())
|
||||
->setDead($in->readBool(StateNames::DEAD_BIT));
|
||||
});
|
||||
$this->map(Ids::CORAL_FAN, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN(), $in)
|
||||
->setDead(false));
|
||||
$this->map(Ids::CORAL_FAN_DEAD, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN(), $in)
|
||||
->setDead(true));
|
||||
$this->map(Ids::CORAL_FAN_HANG, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
|
||||
->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::BRAIN : CoralType::TUBE));
|
||||
$this->map(Ids::CORAL_FAN_HANG2, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
|
||||
@ -1413,22 +1445,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::RAIL()
|
||||
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 9));
|
||||
});
|
||||
$this->map(Ids::RED_FLOWER, function(Reader $in) : Block{
|
||||
return match($type = $in->readString(StateNames::FLOWER_TYPE)){
|
||||
StringValues::FLOWER_TYPE_ALLIUM => Blocks::ALLIUM(),
|
||||
StringValues::FLOWER_TYPE_CORNFLOWER => Blocks::CORNFLOWER(),
|
||||
StringValues::FLOWER_TYPE_HOUSTONIA => Blocks::AZURE_BLUET(), //wtf ???
|
||||
StringValues::FLOWER_TYPE_LILY_OF_THE_VALLEY => Blocks::LILY_OF_THE_VALLEY(),
|
||||
StringValues::FLOWER_TYPE_ORCHID => Blocks::BLUE_ORCHID(),
|
||||
StringValues::FLOWER_TYPE_OXEYE => Blocks::OXEYE_DAISY(),
|
||||
StringValues::FLOWER_TYPE_POPPY => Blocks::POPPY(),
|
||||
StringValues::FLOWER_TYPE_TULIP_ORANGE => Blocks::ORANGE_TULIP(),
|
||||
StringValues::FLOWER_TYPE_TULIP_PINK => Blocks::PINK_TULIP(),
|
||||
StringValues::FLOWER_TYPE_TULIP_RED => Blocks::RED_TULIP(),
|
||||
StringValues::FLOWER_TYPE_TULIP_WHITE => Blocks::WHITE_TULIP(),
|
||||
default => throw $in->badValueException(StateNames::FLOWER_TYPE, $type),
|
||||
};
|
||||
});
|
||||
$this->map(Ids::RED_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::RED_MUSHROOM_BLOCK(), $in));
|
||||
$this->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS());
|
||||
$this->map(Ids::RED_SANDSTONE, function(Reader $in) : Block{
|
||||
@ -1479,18 +1495,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
};
|
||||
});
|
||||
$this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS());
|
||||
$this->map(Ids::SAPLING, function(Reader $in) : Block{
|
||||
return (match($type = $in->readString(StateNames::SAPLING_TYPE)){
|
||||
StringValues::SAPLING_TYPE_ACACIA => Blocks::ACACIA_SAPLING(),
|
||||
StringValues::SAPLING_TYPE_BIRCH => Blocks::BIRCH_SAPLING(),
|
||||
StringValues::SAPLING_TYPE_DARK_OAK => Blocks::DARK_OAK_SAPLING(),
|
||||
StringValues::SAPLING_TYPE_JUNGLE => Blocks::JUNGLE_SAPLING(),
|
||||
StringValues::SAPLING_TYPE_OAK => Blocks::OAK_SAPLING(),
|
||||
StringValues::SAPLING_TYPE_SPRUCE => Blocks::SPRUCE_SAPLING(),
|
||||
default => throw $in->badValueException(StateNames::SAPLING_TYPE, $type),
|
||||
})
|
||||
->setReady($in->readBool(StateNames::AGE_BIT));
|
||||
});
|
||||
$this->map(Ids::SEA_PICKLE, function(Reader $in) : Block{
|
||||
return Blocks::SEA_PICKLE()
|
||||
->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1)
|
||||
|
@ -23,17 +23,28 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use function ksort;
|
||||
use const SORT_STRING;
|
||||
|
||||
final class BlockStateUpgradeSchemaFlattenedName{
|
||||
|
||||
/**
|
||||
* @param string[] $flattenedValueRemaps
|
||||
* @phpstan-param array<string, string> $flattenedValueRemaps
|
||||
*/
|
||||
public function __construct(
|
||||
public string $prefix,
|
||||
public string $flattenedProperty,
|
||||
public string $suffix
|
||||
){}
|
||||
public string $suffix,
|
||||
public array $flattenedValueRemaps
|
||||
){
|
||||
ksort($this->flattenedValueRemaps, SORT_STRING);
|
||||
}
|
||||
|
||||
public function equals(self $that) : bool{
|
||||
return $this->prefix === $that->prefix &&
|
||||
$this->flattenedProperty === $that->flattenedProperty &&
|
||||
$this->suffix === $that->suffix;
|
||||
$this->suffix === $that->suffix &&
|
||||
$this->flattenedValueRemaps === $that->flattenedValueRemaps;
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +166,8 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
$remap->newName ?? new BlockStateUpgradeSchemaFlattenedName(
|
||||
$remap->newFlattenedName->prefix,
|
||||
$remap->newFlattenedName->flattenedProperty,
|
||||
$remap->newFlattenedName->suffix
|
||||
$remap->newFlattenedName->suffix,
|
||||
$remap->newFlattenedName->flattenedValueRemaps ?? [],
|
||||
),
|
||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
|
||||
$remap->copiedState ?? []
|
||||
@ -301,7 +302,8 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
new BlockStateUpgradeSchemaModelFlattenedName(
|
||||
$remap->newName->prefix,
|
||||
$remap->newName->flattenedProperty,
|
||||
$remap->newName->suffix
|
||||
$remap->newName->suffix,
|
||||
$remap->newName->flattenedValueRemaps
|
||||
),
|
||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
||||
$remap->copiedState
|
||||
|
@ -142,7 +142,8 @@ final class BlockStateUpgrader{
|
||||
}else{
|
||||
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
|
||||
if($flattenedValue instanceof StringTag){
|
||||
$newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix);
|
||||
$embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue();
|
||||
$newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix);
|
||||
unset($oldState[$remap->newName->flattenedProperty]);
|
||||
}else{
|
||||
//flattened property is not a TAG_String, so this transformation is not applicable
|
||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade\model;
|
||||
|
||||
final class BlockStateUpgradeSchemaModelFlattenedName{
|
||||
use function count;
|
||||
|
||||
final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{
|
||||
|
||||
/** @required */
|
||||
public string $prefix;
|
||||
@ -31,10 +33,31 @@ final class BlockStateUpgradeSchemaModelFlattenedName{
|
||||
public string $flattenedProperty;
|
||||
/** @required */
|
||||
public string $suffix;
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array<string, string>
|
||||
*/
|
||||
public array $flattenedValueRemaps;
|
||||
|
||||
public function __construct(string $prefix, string $flattenedProperty, string $suffix){
|
||||
/**
|
||||
* @param string[] $flattenedValueRemaps
|
||||
* @phpstan-param array<string, string> $flattenedValueRemaps
|
||||
*/
|
||||
public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps){
|
||||
$this->prefix = $prefix;
|
||||
$this->flattenedProperty = $flattenedProperty;
|
||||
$this->suffix = $suffix;
|
||||
$this->flattenedValueRemaps = $flattenedValueRemaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function jsonSerialize() : array{
|
||||
$result = (array) $this;
|
||||
if(count($this->flattenedValueRemaps) === 0){
|
||||
unset($result["flattenedValueRemaps"]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ final class ItemTypeNames{
|
||||
public const BLUE_DYE = "minecraft:blue_dye";
|
||||
public const BOAT = "minecraft:boat";
|
||||
public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg";
|
||||
public const BOLT_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:bolt_armor_trim_smithing_template";
|
||||
public const BONE = "minecraft:bone";
|
||||
public const BONE_MEAL = "minecraft:bone_meal";
|
||||
public const BOOK = "minecraft:book";
|
||||
@ -82,6 +83,7 @@ final class ItemTypeNames{
|
||||
public const BOW = "minecraft:bow";
|
||||
public const BOWL = "minecraft:bowl";
|
||||
public const BREAD = "minecraft:bread";
|
||||
public const BREEZE_ROD = "minecraft:breeze_rod";
|
||||
public const BREEZE_SPAWN_EGG = "minecraft:breeze_spawn_egg";
|
||||
public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd";
|
||||
public const BREWING_STAND = "minecraft:brewing_stand";
|
||||
@ -141,6 +143,8 @@ final class ItemTypeNames{
|
||||
public const COPPER_DOOR = "minecraft:copper_door";
|
||||
public const COPPER_INGOT = "minecraft:copper_ingot";
|
||||
public const CORAL = "minecraft:coral";
|
||||
public const CORAL_FAN = "minecraft:coral_fan";
|
||||
public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead";
|
||||
public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg";
|
||||
public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern";
|
||||
public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg";
|
||||
@ -155,7 +159,6 @@ final class ItemTypeNames{
|
||||
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
|
||||
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
|
||||
public const DARK_OAK_SIGN = "minecraft:dark_oak_sign";
|
||||
public const DEBUG_STICK = "minecraft:debug_stick";
|
||||
public const DIAMOND = "minecraft:diamond";
|
||||
public const DIAMOND_AXE = "minecraft:diamond_axe";
|
||||
public const DIAMOND_BOOTS = "minecraft:diamond_boots";
|
||||
@ -205,6 +208,9 @@ final class ItemTypeNames{
|
||||
public const FISHING_ROD = "minecraft:fishing_rod";
|
||||
public const FLINT = "minecraft:flint";
|
||||
public const FLINT_AND_STEEL = "minecraft:flint_and_steel";
|
||||
public const FLOW_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:flow_armor_trim_smithing_template";
|
||||
public const FLOW_BANNER_PATTERN = "minecraft:flow_banner_pattern";
|
||||
public const FLOW_POTTERY_SHERD = "minecraft:flow_pottery_sherd";
|
||||
public const FLOWER_BANNER_PATTERN = "minecraft:flower_banner_pattern";
|
||||
public const FLOWER_POT = "minecraft:flower_pot";
|
||||
public const FOX_SPAWN_EGG = "minecraft:fox_spawn_egg";
|
||||
@ -242,6 +248,8 @@ final class ItemTypeNames{
|
||||
public const GREEN_DYE = "minecraft:green_dye";
|
||||
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
|
||||
public const GUNPOWDER = "minecraft:gunpowder";
|
||||
public const GUSTER_BANNER_PATTERN = "minecraft:guster_banner_pattern";
|
||||
public const GUSTER_POTTERY_SHERD = "minecraft:guster_pottery_sherd";
|
||||
public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass";
|
||||
public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane";
|
||||
public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea";
|
||||
@ -297,6 +305,7 @@ final class ItemTypeNames{
|
||||
public const LODESTONE_COMPASS = "minecraft:lodestone_compass";
|
||||
public const LOG = "minecraft:log";
|
||||
public const LOG2 = "minecraft:log2";
|
||||
public const MACE = "minecraft:mace";
|
||||
public const MAGENTA_DYE = "minecraft:magenta_dye";
|
||||
public const MAGMA_CREAM = "minecraft:magma_cream";
|
||||
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
|
||||
@ -404,6 +413,7 @@ final class ItemTypeNames{
|
||||
public const RAW_IRON = "minecraft:raw_iron";
|
||||
public const RECOVERY_COMPASS = "minecraft:recovery_compass";
|
||||
public const RED_DYE = "minecraft:red_dye";
|
||||
public const RED_FLOWER = "minecraft:red_flower";
|
||||
public const REDSTONE = "minecraft:redstone";
|
||||
public const REPEATER = "minecraft:repeater";
|
||||
public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:rib_armor_trim_smithing_template";
|
||||
@ -412,6 +422,8 @@ final class ItemTypeNames{
|
||||
public const SALMON = "minecraft:salmon";
|
||||
public const SALMON_BUCKET = "minecraft:salmon_bucket";
|
||||
public const SALMON_SPAWN_EGG = "minecraft:salmon_spawn_egg";
|
||||
public const SAPLING = "minecraft:sapling";
|
||||
public const SCRAPE_POTTERY_SHERD = "minecraft:scrape_pottery_sherd";
|
||||
public const SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:sentry_armor_trim_smithing_template";
|
||||
public const SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:shaper_armor_trim_smithing_template";
|
||||
public const SHEAF_POTTERY_SHERD = "minecraft:sheaf_pottery_sherd";
|
||||
|
@ -555,26 +555,33 @@ abstract class Living extends Entity{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attackTime = $source->getAttackCooldown();
|
||||
if($this->attackTime <= 0){
|
||||
//this logic only applies if the entity was cold attacked
|
||||
|
||||
if($source instanceof EntityDamageByChildEntityEvent){
|
||||
$e = $source->getChild();
|
||||
if($e !== null){
|
||||
$motion = $e->getMotion();
|
||||
$this->knockBack($motion->x, $motion->z, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
|
||||
$this->attackTime = $source->getAttackCooldown();
|
||||
|
||||
if($source instanceof EntityDamageByChildEntityEvent){
|
||||
$e = $source->getChild();
|
||||
if($e !== null){
|
||||
$motion = $e->getMotion();
|
||||
$this->knockBack($motion->x, $motion->z, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
|
||||
}
|
||||
}elseif($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
if($e !== null){
|
||||
$deltaX = $this->location->x - $e->location->x;
|
||||
$deltaZ = $this->location->z - $e->location->z;
|
||||
$this->knockBack($deltaX, $deltaZ, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
|
||||
}
|
||||
}
|
||||
}elseif($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
if($e !== null){
|
||||
$deltaX = $this->location->x - $e->location->x;
|
||||
$deltaZ = $this->location->z - $e->location->z;
|
||||
$this->knockBack($deltaX, $deltaZ, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
|
||||
|
||||
if($this->isAlive()){
|
||||
$this->doHitAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
if($this->isAlive()){
|
||||
$this->applyPostDamageEffects($source);
|
||||
$this->doHitAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,10 @@ class Armor extends Durable{
|
||||
$new = $thisCopy->pop();
|
||||
$player->getArmorInventory()->setItem($this->getArmorSlot(), $new);
|
||||
$player->getInventory()->setItemInHand($existing);
|
||||
$sound = $new->getMaterial()->getEquipSound();
|
||||
if($sound !== null){
|
||||
$player->broadcastSound($sound);
|
||||
}
|
||||
if(!$thisCopy->isNull()){
|
||||
//if the stack size was bigger than 1 (usually won't happen, but might be caused by plugins)
|
||||
$returnedItems[] = $thisCopy;
|
||||
|
@ -23,10 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\world\sound\Sound;
|
||||
|
||||
class ArmorMaterial{
|
||||
|
||||
public function __construct(
|
||||
private readonly int $enchantability
|
||||
private readonly int $enchantability,
|
||||
private readonly ?Sound $equipSound = null
|
||||
){
|
||||
}
|
||||
|
||||
@ -39,4 +42,11 @@ class ArmorMaterial{
|
||||
public function getEnchantability() : int{
|
||||
return $this->enchantability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sound that plays when equipping the armor
|
||||
*/
|
||||
public function getEquipSound() : ?Sound{
|
||||
return $this->equipSound;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\utils\RegistryTrait;
|
||||
use pocketmine\world\sound\ArmorEquipChainSound;
|
||||
use pocketmine\world\sound\ArmorEquipDiamondSound;
|
||||
use pocketmine\world\sound\ArmorEquipGenericSound;
|
||||
use pocketmine\world\sound\ArmorEquipGoldSound;
|
||||
use pocketmine\world\sound\ArmorEquipIronSound;
|
||||
use pocketmine\world\sound\ArmorEquipLeatherSound;
|
||||
use pocketmine\world\sound\ArmorEquipNetheriteSound;
|
||||
|
||||
/**
|
||||
* This doc-block is generated automatically, do not modify it manually.
|
||||
@ -62,12 +69,12 @@ final class VanillaArmorMaterials{
|
||||
}
|
||||
|
||||
protected static function setup() : void{
|
||||
self::register("leather", new ArmorMaterial(15));
|
||||
self::register("chainmail", new ArmorMaterial(12));
|
||||
self::register("iron", new ArmorMaterial(9));
|
||||
self::register("turtle", new ArmorMaterial(9));
|
||||
self::register("gold", new ArmorMaterial(25));
|
||||
self::register("diamond", new ArmorMaterial(10));
|
||||
self::register("netherite", new ArmorMaterial(15));
|
||||
self::register("leather", new ArmorMaterial(15, new ArmorEquipLeatherSound()));
|
||||
self::register("chainmail", new ArmorMaterial(12, new ArmorEquipChainSound()));
|
||||
self::register("iron", new ArmorMaterial(9, new ArmorEquipIronSound()));
|
||||
self::register("turtle", new ArmorMaterial(9, new ArmorEquipGenericSound()));
|
||||
self::register("gold", new ArmorMaterial(25, new ArmorEquipGoldSound()));
|
||||
self::register("diamond", new ArmorMaterial(10, new ArmorEquipDiamondSound()));
|
||||
self::register("netherite", new ArmorMaterial(15, new ArmorEquipNetheriteSound()));
|
||||
}
|
||||
}
|
||||
|
3
src/network/mcpe/cache/CraftingDataCache.php
vendored
3
src/network/mcpe/cache/CraftingDataCache.php
vendored
@ -113,7 +113,8 @@ final class CraftingDataCache{
|
||||
$nullUUID,
|
||||
CraftingRecipeBlockName::CRAFTING_TABLE,
|
||||
50,
|
||||
$index
|
||||
true,
|
||||
$index,
|
||||
);
|
||||
}else{
|
||||
//TODO: probably special recipe types
|
||||
|
@ -177,7 +177,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
//we don't force here, because it doesn't have user-facing effects
|
||||
//but it does have an annoying side-effect when true: it makes
|
||||
//the client remove its own non-server-supplied resource packs.
|
||||
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, ProtocolInfo::MINECRAFT_VERSION_NETWORK, new Experiments([], false)));
|
||||
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, ProtocolInfo::MINECRAFT_VERSION_NETWORK, new Experiments([], false), false));
|
||||
$this->session->getLogger()->debug("Applying resource pack stack");
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\thread;
|
||||
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\crash\CrashDump;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\Server;
|
||||
use function error_get_last;
|
||||
@ -150,7 +151,7 @@ trait CommonThreadPartsTrait{
|
||||
$this->synchronized(function() : void{
|
||||
if($this->isTerminated() && $this->crashInfo === null){
|
||||
$last = error_get_last();
|
||||
if($last !== null){
|
||||
if($last !== null && ($last["type"] & CrashDump::FATAL_ERROR_MASK) !== 0){
|
||||
//fatal error
|
||||
$crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName());
|
||||
}else{
|
||||
|
@ -39,12 +39,12 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
|
||||
private bool $useFormattingCodes = false;
|
||||
private string $mainThreadName;
|
||||
private string $timezone;
|
||||
private MainLoggerThread $logWriterThread;
|
||||
private ?MainLoggerThread $logWriterThread = null;
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function __construct(string $logFile, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){
|
||||
public function __construct(?string $logFile, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false, ?string $logArchiveDir = null){
|
||||
parent::__construct();
|
||||
$this->logDebug = $logDebug;
|
||||
|
||||
@ -52,8 +52,10 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
|
||||
$this->mainThreadName = $mainThreadName;
|
||||
$this->timezone = $timezone->getName();
|
||||
|
||||
$this->logWriterThread = new MainLoggerThread($logFile);
|
||||
$this->logWriterThread->start(NativeThread::INHERIT_NONE);
|
||||
if($logFile !== null){
|
||||
$this->logWriterThread = new MainLoggerThread($logFile, $logArchiveDir);
|
||||
$this->logWriterThread->start(NativeThread::INHERIT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,10 +168,12 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
|
||||
}
|
||||
|
||||
public function shutdownLogWriterThread() : void{
|
||||
if(NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
|
||||
$this->logWriterThread->shutdown();
|
||||
}else{
|
||||
throw new \LogicException("Only the creator thread can shutdown the logger thread");
|
||||
if($this->logWriterThread !== null){
|
||||
if(NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
|
||||
$this->logWriterThread->shutdown();
|
||||
}else{
|
||||
throw new \LogicException("Only the creator thread can shutdown the logger thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +197,9 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
|
||||
|
||||
$this->synchronized(function() use ($message, $level, $time) : void{
|
||||
Terminal::writeLine($message);
|
||||
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
|
||||
if($this->logWriterThread !== null){
|
||||
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var ThreadSafeLoggerAttachment $attachment
|
||||
@ -205,11 +211,11 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
|
||||
}
|
||||
|
||||
public function syncFlushBuffer() : void{
|
||||
$this->logWriterThread->syncFlushBuffer();
|
||||
$this->logWriterThread?->syncFlushBuffer();
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
if(!$this->logWriterThread->isJoined() && NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
|
||||
if($this->logWriterThread !== null && !$this->logWriterThread->isJoined() && NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){
|
||||
$this->shutdownLogWriterThread();
|
||||
}
|
||||
}
|
||||
|
@ -25,23 +25,42 @@ namespace pocketmine\utils;
|
||||
|
||||
use pmmp\thread\Thread;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use function clearstatcache;
|
||||
use function date;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
use function fstat;
|
||||
use function fwrite;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function is_resource;
|
||||
use function mkdir;
|
||||
use function pathinfo;
|
||||
use function rename;
|
||||
use function strlen;
|
||||
use function touch;
|
||||
use const PATHINFO_EXTENSION;
|
||||
use const PATHINFO_FILENAME;
|
||||
|
||||
final class MainLoggerThread extends Thread{
|
||||
|
||||
/** @phpstan-var ThreadSafeArray<int, string> */
|
||||
private ThreadSafeArray $buffer;
|
||||
private bool $syncFlush = false;
|
||||
private bool $shutdown = false;
|
||||
|
||||
public function __construct(
|
||||
private string $logFile
|
||||
private string $logFile,
|
||||
private ?string $archiveDir,
|
||||
private readonly int $maxFileSize = 32 * 1024 * 1024 //32 MB
|
||||
){
|
||||
$this->buffer = new ThreadSafeArray();
|
||||
touch($this->logFile);
|
||||
if($this->archiveDir !== null && !@mkdir($this->archiveDir) && !is_dir($this->archiveDir)){
|
||||
throw new \RuntimeException("Unable to create archive directory: " . (
|
||||
is_file($this->archiveDir) ? "it already exists and is not a directory" : "permission denied"));
|
||||
}
|
||||
}
|
||||
|
||||
public function write(string $line) : void{
|
||||
@ -71,12 +90,64 @@ final class MainLoggerThread extends Thread{
|
||||
$this->join();
|
||||
}
|
||||
|
||||
/** @return resource */
|
||||
private function openLogFile(string $file, int &$size){
|
||||
$logResource = fopen($file, "ab");
|
||||
if(!is_resource($logResource)){
|
||||
throw new \RuntimeException("Couldn't open log file");
|
||||
}
|
||||
$stat = fstat($logResource);
|
||||
if($stat === false){
|
||||
throw new AssumptionFailedError("fstat() should not fail here");
|
||||
}
|
||||
$size = $stat['size'];
|
||||
return $logResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $logResource
|
||||
* @return resource
|
||||
*/
|
||||
private function archiveLogFile($logResource, int &$size, string $archiveDir){
|
||||
fclose($logResource);
|
||||
|
||||
clearstatcache();
|
||||
|
||||
$i = 0;
|
||||
$date = date("Y-m-d\TH.i.s");
|
||||
$baseName = pathinfo($this->logFile, PATHINFO_FILENAME);
|
||||
$extension = pathinfo($this->logFile, PATHINFO_EXTENSION);
|
||||
do{
|
||||
//this shouldn't be necessary, but in case the user messes with the system time for some reason ...
|
||||
$fileName = "$baseName.$date.$i.$extension";
|
||||
$out = $this->archiveDir . "/" . $fileName;
|
||||
$i++;
|
||||
}while(file_exists($out));
|
||||
|
||||
//the user may have externally deleted the whole directory - make sure it exists before we do anything
|
||||
@mkdir($archiveDir);
|
||||
rename($this->logFile, $out);
|
||||
|
||||
$logResource = $this->openLogFile($this->logFile, $size);
|
||||
fwrite($logResource, "--- Starting new log file - old log file archived as $fileName ---\n");
|
||||
|
||||
return $logResource;
|
||||
}
|
||||
|
||||
private function logFileReadyToArchive(int $size) : bool{
|
||||
return $size >= $this->maxFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $logResource
|
||||
*/
|
||||
private function writeLogStream($logResource) : void{
|
||||
private function writeLogStream(&$logResource, int &$size, ?string $archiveDir) : void{
|
||||
while(($chunk = $this->buffer->shift()) !== null){
|
||||
fwrite($logResource, $chunk);
|
||||
$size += strlen($chunk);
|
||||
if($archiveDir !== null && $this->logFileReadyToArchive($size)){
|
||||
$logResource = $this->archiveLogFile($logResource, $size, $archiveDir);
|
||||
}
|
||||
}
|
||||
|
||||
$this->synchronized(function() : void{
|
||||
@ -88,13 +159,15 @@ final class MainLoggerThread extends Thread{
|
||||
}
|
||||
|
||||
public function run() : void{
|
||||
$logResource = fopen($this->logFile, "ab");
|
||||
if(!is_resource($logResource)){
|
||||
throw new \RuntimeException("Couldn't open log file");
|
||||
$size = 0;
|
||||
$logResource = $this->openLogFile($this->logFile, $size);
|
||||
$archiveDir = $this->archiveDir;
|
||||
if($archiveDir !== null && $this->logFileReadyToArchive($size)){
|
||||
$logResource = $this->archiveLogFile($logResource, $size, $archiveDir);
|
||||
}
|
||||
|
||||
while(!$this->shutdown){
|
||||
$this->writeLogStream($logResource);
|
||||
$this->writeLogStream($logResource, $size, $archiveDir);
|
||||
$this->synchronized(function() : void{
|
||||
if(!$this->shutdown && !$this->syncFlush){
|
||||
$this->wait();
|
||||
@ -102,7 +175,7 @@ final class MainLoggerThread extends Thread{
|
||||
});
|
||||
}
|
||||
|
||||
$this->writeLogStream($logResource);
|
||||
$this->writeLogStream($logResource, $size, $archiveDir);
|
||||
|
||||
fclose($logResource);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use pocketmine\thread\ThreadManager;
|
||||
use pocketmine\thread\Thread;
|
||||
use function count;
|
||||
use function exec;
|
||||
use function fclose;
|
||||
@ -122,7 +122,7 @@ final class Process{
|
||||
|
||||
//TODO: more OS
|
||||
|
||||
return count(ThreadManager::getInstance()->getAll()) + 2; //MainLogger + Main Thread
|
||||
return Thread::getRunningCount() + 1; //pmmpthread doesn't count the main thread
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,12 +51,12 @@ use function time;
|
||||
class BedrockWorldData extends BaseNbtWorldData{
|
||||
|
||||
public const CURRENT_STORAGE_VERSION = 10;
|
||||
public const CURRENT_STORAGE_NETWORK_VERSION = 662;
|
||||
public const CURRENT_STORAGE_NETWORK_VERSION = 671;
|
||||
public const CURRENT_CLIENT_VERSION_TARGET = [
|
||||
1, //major
|
||||
20, //minor
|
||||
71, //patch
|
||||
1, //revision
|
||||
80, //patch
|
||||
5, //revision
|
||||
0 //is beta
|
||||
];
|
||||
|
||||
|
35
src/world/sound/ArmorEquipChainSound.php
Normal file
35
src/world/sound/ArmorEquipChainSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipChainSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_CHAIN, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/ArmorEquipDiamondSound.php
Normal file
35
src/world/sound/ArmorEquipDiamondSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipDiamondSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_DIAMOND, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/ArmorEquipGenericSound.php
Normal file
35
src/world/sound/ArmorEquipGenericSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipGenericSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_GENERIC, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/ArmorEquipGoldSound.php
Normal file
35
src/world/sound/ArmorEquipGoldSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipGoldSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_GOLD, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/ArmorEquipIronSound.php
Normal file
35
src/world/sound/ArmorEquipIronSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipIronSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_IRON, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/ArmorEquipLeatherSound.php
Normal file
35
src/world/sound/ArmorEquipLeatherSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipLeatherSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_LEATHER, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/ArmorEquipNetheriteSound.php
Normal file
35
src/world/sound/ArmorEquipNetheriteSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class ArmorEquipNetheriteSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ARMOR_EQUIP_NETHERITE, $pos, false)];
|
||||
}
|
||||
}
|
35
src/world/sound/SweetBerriesPickSound.php
Normal file
35
src/world/sound/SweetBerriesPickSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class SweetBerriesPickSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::BLOCK_SWEET_BERRY_BUSH_PICK, $pos, false)];
|
||||
}
|
||||
}
|
@ -24,13 +24,16 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function asort;
|
||||
use function file_get_contents;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function log;
|
||||
use function print_r;
|
||||
use const SORT_STRING;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
|
||||
class BlockTest extends TestCase{
|
||||
|
||||
@ -91,34 +94,71 @@ class BlockTest extends TestCase{
|
||||
}
|
||||
}
|
||||
|
||||
public function testConsistency() : void{
|
||||
$list = json_decode(file_get_contents(__DIR__ . '/block_factory_consistency_check.json'), true);
|
||||
if(!is_array($list)){
|
||||
throw new \pocketmine\utils\AssumptionFailedError("Old table should be array{knownStates: array<string, string>, stateDataBits: int}");
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<string, int>
|
||||
*/
|
||||
public static function computeConsistencyCheckTable(RuntimeBlockStateRegistry $blockStateRegistry) : array{
|
||||
$newTable = [];
|
||||
|
||||
$idNameLookup = [];
|
||||
//if we ever split up block registration into multiple registries (e.g. separating chemistry blocks),
|
||||
//we'll need to ensure those additional registries are also included here
|
||||
foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $blockType){
|
||||
$id = $blockType->getTypeId();
|
||||
if(isset($idNameLookup[$id])){
|
||||
throw new AssumptionFailedError("TypeID $name collides with " . $idNameLookup[$id]);
|
||||
}
|
||||
$idNameLookup[$id] = $name;
|
||||
}
|
||||
$knownStates = [];
|
||||
/**
|
||||
* @var string $name
|
||||
* @var int[] $stateIds
|
||||
*/
|
||||
foreach($list["knownStates"] as $name => $stateIds){
|
||||
foreach($stateIds as $stateId){
|
||||
$knownStates[$stateId] = $name;
|
||||
|
||||
foreach($blockStateRegistry->getAllKnownStates() as $index => $block){
|
||||
if($index !== $block->getStateId()){
|
||||
throw new AssumptionFailedError("State index should always match state ID");
|
||||
}
|
||||
$idName = $idNameLookup[$block->getTypeId()];
|
||||
$newTable[$idName] = ($newTable[$idName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return $newTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param array<string, int> $actual
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function computeConsistencyCheckDiff(string $expectedFile, array $actual) : array{
|
||||
$expected = json_decode(Filesystem::fileGetContents($expectedFile), true, 2, JSON_THROW_ON_ERROR);
|
||||
if(!is_array($expected)){
|
||||
throw new AssumptionFailedError("Old table should be array<string, int>");
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
foreach($expected as $typeName => $numStates){
|
||||
if(!is_string($typeName) || !is_int($numStates)){
|
||||
throw new AssumptionFailedError("Old table should be array<string, int>");
|
||||
}
|
||||
if(!isset($actual[$typeName])){
|
||||
$errors[] = "Removed block type $typeName ($numStates permutations)";
|
||||
}elseif($actual[$typeName] !== $numStates){
|
||||
$errors[] = "Block type $typeName permutation count changed: $numStates -> " . $actual[$typeName];
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($actual) as $typeName => $numStates){
|
||||
if(!isset($expected[$typeName])){
|
||||
$errors[] = "Added block type $typeName (" . $actual[$typeName] . " permutations)";
|
||||
}
|
||||
}
|
||||
$oldStateDataSize = $list["stateDataBits"];
|
||||
self::assertSame($oldStateDataSize, Block::INTERNAL_STATE_DATA_BITS, "Changed number of state data bits - consistency check probably need regenerating");
|
||||
|
||||
$states = $this->blockFactory->getAllKnownStates();
|
||||
foreach($states as $stateId => $state){
|
||||
self::assertArrayHasKey($stateId, $knownStates, "New block state $stateId (" . print_r($state, true) . ") - consistency check may need regenerating");
|
||||
self::assertSame($knownStates[$stateId], $state->getName());
|
||||
}
|
||||
asort($knownStates, SORT_STRING);
|
||||
foreach($knownStates as $k => $name){
|
||||
self::assertArrayHasKey($k, $states, "Missing previously-known block state $k " . ($k >> Block::INTERNAL_STATE_DATA_BITS) . ":" . ($k & Block::INTERNAL_STATE_DATA_MASK) . " ($name)");
|
||||
self::assertSame($name, $states[$k]->getName());
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function testConsistency() : void{
|
||||
$newTable = self::computeConsistencyCheckTable($this->blockFactory);
|
||||
$errors = self::computeConsistencyCheckDiff(__DIR__ . '/block_factory_consistency_check.json', $newTable);
|
||||
|
||||
self::assertEmpty($errors, "Block factory consistency check failed:\n" . implode("\n", $errors));
|
||||
}
|
||||
|
||||
public function testEmptyStateId() : void{
|
||||
|
File diff suppressed because one or more lines are too long
@ -21,86 +21,31 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockTest;
|
||||
use pocketmine\block\RuntimeBlockStateRegistry;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
require dirname(__DIR__, 3) . '/vendor/autoload.php';
|
||||
|
||||
/* This script needs to be re-run after any intentional blockfactory change (adding or removing a block state). */
|
||||
|
||||
$factory = new RuntimeBlockStateRegistry();
|
||||
$remaps = [];
|
||||
$new = [];
|
||||
foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $index => $block){
|
||||
if($index !== $block->getStateId()){
|
||||
throw new AssumptionFailedError("State index should always match state ID");
|
||||
}
|
||||
$new[$index] = $block->getName();
|
||||
}
|
||||
$newTable = BlockTest::computeConsistencyCheckTable(RuntimeBlockStateRegistry::getInstance());
|
||||
|
||||
$oldTablePath = __DIR__ . '/block_factory_consistency_check.json';
|
||||
if(file_exists($oldTablePath)){
|
||||
$oldTable = json_decode(file_get_contents($oldTablePath), true);
|
||||
if(!is_array($oldTable)){
|
||||
throw new AssumptionFailedError("Old table should be array{knownStates: array<string, string>, stateDataBits: int}");
|
||||
}
|
||||
$old = [];
|
||||
/**
|
||||
* @var string $name
|
||||
* @var int[] $stateIds
|
||||
*/
|
||||
foreach($oldTable["knownStates"] as $name => $stateIds){
|
||||
foreach($stateIds as $stateId){
|
||||
$old[$stateId] = $name;
|
||||
}
|
||||
}
|
||||
$oldStateDataSize = $oldTable["stateDataBits"];
|
||||
$oldStateDataMask = ~(~0 << $oldStateDataSize);
|
||||
$errors = BlockTest::computeConsistencyCheckDiff($oldTablePath, $newTable);
|
||||
|
||||
if($oldStateDataSize !== Block::INTERNAL_STATE_DATA_BITS){
|
||||
echo "State data bits changed from $oldStateDataSize to " . Block::INTERNAL_STATE_DATA_BITS . "\n";
|
||||
}
|
||||
|
||||
foreach($old as $k => $name){
|
||||
[$oldId, $oldStateData] = [$k >> $oldStateDataSize, $k & $oldStateDataMask];
|
||||
$reconstructedK = ($oldId << Block::INTERNAL_STATE_DATA_BITS) | $oldStateData;
|
||||
if(!isset($new[$reconstructedK])){
|
||||
echo "Removed state for $name ($oldId:$oldStateData)\n";
|
||||
}
|
||||
}
|
||||
foreach($new as $k => $name){
|
||||
[$newId, $newStateData] = [$k >> Block::INTERNAL_STATE_DATA_BITS, $k & Block::INTERNAL_STATE_DATA_MASK];
|
||||
if($newStateData > $oldStateDataMask){
|
||||
echo "Added state for $name ($newId, $newStateData)\n";
|
||||
}else{
|
||||
$reconstructedK = ($newId << $oldStateDataSize) | $newStateData;
|
||||
if(!isset($old[$reconstructedK])){
|
||||
echo "Added state for $name ($newId:$newStateData)\n";
|
||||
}elseif($old[$reconstructedK] !== $name){
|
||||
echo "Name changed ($newId:$newStateData) " . $old[$reconstructedK] . " -> " . $name . "\n";
|
||||
}
|
||||
if(count($errors) > 0){
|
||||
echo count($errors) . " changes detected:\n";
|
||||
foreach($errors as $error){
|
||||
echo $error . "\n";
|
||||
}
|
||||
}else{
|
||||
echo "No changes detected\n";
|
||||
}
|
||||
}else{
|
||||
echo "WARNING: Unable to calculate diff, no previous consistency check file found\n";
|
||||
}
|
||||
|
||||
$newTable = [];
|
||||
foreach($new as $stateId => $name){
|
||||
$newTable[$name][] = $stateId;
|
||||
}
|
||||
ksort($newTable, SORT_STRING);
|
||||
foreach(Utils::stringifyKeys($newTable) as $name => $stateIds){
|
||||
sort($stateIds, SORT_NUMERIC);
|
||||
$newTable[$name] = $stateIds;
|
||||
}
|
||||
|
||||
file_put_contents(__DIR__ . '/block_factory_consistency_check.json', json_encode(
|
||||
[
|
||||
"knownStates" => $newTable,
|
||||
"stateDataBits" => Block::INTERNAL_STATE_DATA_BITS
|
||||
],
|
||||
JSON_THROW_ON_ERROR
|
||||
));
|
||||
file_put_contents($oldTablePath, json_encode($newTable, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
|
||||
|
@ -32,8 +32,6 @@ use pocketmine\utils\MainLogger;
|
||||
use function define;
|
||||
use function dirname;
|
||||
use function microtime;
|
||||
use function sys_get_temp_dir;
|
||||
use function tempnam;
|
||||
use function usleep;
|
||||
|
||||
class AsyncPoolTest extends TestCase{
|
||||
@ -45,13 +43,12 @@ class AsyncPoolTest extends TestCase{
|
||||
|
||||
public function setUp() : void{
|
||||
@define('pocketmine\\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 3) . '/vendor/autoload.php');
|
||||
$this->mainLogger = new MainLogger(tempnam(sys_get_temp_dir(), "pmlog"), false, "Main", new \DateTimeZone('UTC'));
|
||||
$this->mainLogger = new MainLogger(null, false, "Main", new \DateTimeZone('UTC'));
|
||||
$this->pool = new AsyncPool(2, 1024, new ThreadSafeClassLoader(), $this->mainLogger, new SleeperHandler());
|
||||
}
|
||||
|
||||
public function tearDown() : void{
|
||||
$this->pool->shutdown();
|
||||
$this->mainLogger->shutdownLogWriterThread();
|
||||
}
|
||||
|
||||
public function testTaskLeak() : void{
|
||||
|
@ -407,7 +407,13 @@ class ParserPacketHandler extends PacketHandler{
|
||||
$mappedType = $typeMap[$entry->getTypeId()];
|
||||
|
||||
if($entry instanceof ShapedRecipe){
|
||||
$recipes[$mappedType][] = $this->shapedRecipeToJson($entry);
|
||||
//all known recipes are currently symmetric and I don't feel like attaching a `symmetric` field to
|
||||
//every shaped recipe for this - split it into a separate category instead
|
||||
if(!$entry->isSymmetric()){
|
||||
$recipes[$mappedType . "_asymmetric"][] = $this->shapedRecipeToJson($entry);
|
||||
}else{
|
||||
$recipes[$mappedType][] = $this->shapedRecipeToJson($entry);
|
||||
}
|
||||
}elseif($entry instanceof ShapelessRecipe){
|
||||
$recipes[$mappedType][] = $this->shapelessRecipeToJson($entry);
|
||||
}elseif($entry instanceof MultiRecipe){
|
||||
|
@ -38,18 +38,23 @@ use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_key_first;
|
||||
use function array_key_last;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function implode;
|
||||
use function json_encode;
|
||||
use function ksort;
|
||||
use function min;
|
||||
use function sort;
|
||||
use function strlen;
|
||||
use function strrev;
|
||||
use function substr;
|
||||
use function usort;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const SORT_STRING;
|
||||
@ -275,6 +280,77 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $strings
|
||||
*/
|
||||
function findCommonPrefix(array $strings) : string{
|
||||
sort($strings, SORT_STRING);
|
||||
|
||||
$first = $strings[array_key_first($strings)];
|
||||
$last = $strings[array_key_last($strings)];
|
||||
|
||||
$maxLength = min(strlen($first), strlen($last));
|
||||
for($i = 0; $i < $maxLength; ++$i){
|
||||
if($first[$i] !== $last[$i]){
|
||||
return substr($first, 0, $i);
|
||||
}
|
||||
}
|
||||
|
||||
return substr($first, 0, $maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $strings
|
||||
*/
|
||||
function findCommonSuffix(array $strings) : string{
|
||||
$reversed = array_map(strrev(...), $strings);
|
||||
|
||||
return strrev(findCommonPrefix($reversed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[][][] $candidateFlattenedValues
|
||||
* @phpstan-param array<string, array<string, array<string, string>>> $candidateFlattenedValues
|
||||
*
|
||||
* @return BlockStateUpgradeSchemaFlattenedName[][]
|
||||
* @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenedName>>
|
||||
*/
|
||||
function buildFlattenPropertyRules(array $candidateFlattenedValues) : array{
|
||||
$flattenPropertyRules = [];
|
||||
foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){
|
||||
foreach(Utils::stringifyKeys($filters) as $filter => $valueToId){
|
||||
$ids = array_values($valueToId);
|
||||
|
||||
//TODO: this is a bit too enthusiastic. For example, when flattening the old "stone", it will see that
|
||||
//"granite", "andesite", "stone" etc all have "e" as a common suffix, which works, but looks a bit daft.
|
||||
//This also causes more remaps to be generated than necessary, since some of the values are already
|
||||
//contained in the new ID.
|
||||
$idPrefix = findCommonPrefix($ids);
|
||||
$idSuffix = findCommonSuffix($ids);
|
||||
if(strlen($idSuffix) < 2){
|
||||
$idSuffix = "";
|
||||
}
|
||||
|
||||
$valueMap = [];
|
||||
foreach(Utils::stringifyKeys($valueToId) as $value => $newId){
|
||||
$newValue = substr($newId, strlen($idPrefix), $idSuffix !== "" ? -strlen($idSuffix) : null);
|
||||
if($newValue !== $value){
|
||||
$valueMap[$value] = $newValue;
|
||||
}
|
||||
}
|
||||
|
||||
$flattenPropertyRules[$propertyName][$filter] = new BlockStateUpgradeSchemaFlattenedName(
|
||||
$idPrefix,
|
||||
$propertyName,
|
||||
$idSuffix,
|
||||
$valueMap
|
||||
);
|
||||
}
|
||||
}
|
||||
ksort($flattenPropertyRules, SORT_STRING);
|
||||
return $flattenPropertyRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to compress a list of remapped states by looking at which state properties were consistently unchanged.
|
||||
* This significantly reduces the output size during flattening when the flattened block has many permutations
|
||||
@ -327,9 +403,9 @@ function processRemappedStates(array $upgradeTable) : array{
|
||||
$unchangedStatesByNewName[$newName] = $unchangedStates;
|
||||
}
|
||||
|
||||
$flattenedProperties = [];
|
||||
$notFlattenedProperties = [];
|
||||
$notFlattenedPropertyValues = [];
|
||||
|
||||
$candidateFlattenedValues = [];
|
||||
foreach($upgradeTable as $pair){
|
||||
foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){
|
||||
if(isset($notFlattenedProperties[$propertyName])){
|
||||
@ -344,37 +420,41 @@ function processRemappedStates(array $upgradeTable) : array{
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
$parts = explode($rawValue, $pair->new->getName(), 2);
|
||||
if(count($parts) !== 2){
|
||||
//the new name does not contain the property value, but it may still be able to be flattened in other cases
|
||||
$notFlattenedPropertyValues[$propertyName][$rawValue] = $rawValue;
|
||||
continue;
|
||||
}
|
||||
[$prefix, $suffix] = $parts;
|
||||
|
||||
$filter = $pair->old->getStates();
|
||||
foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){
|
||||
unset($filter[$unchangedPropertyName]);
|
||||
}
|
||||
unset($filter[$propertyName]);
|
||||
|
||||
$rawFilter = encodeOrderedProperties($filter);
|
||||
$flattenRule = new BlockStateUpgradeSchemaFlattenedName(
|
||||
prefix: $prefix,
|
||||
flattenedProperty: $propertyName,
|
||||
suffix: $suffix
|
||||
);
|
||||
if(!isset($flattenedProperties[$propertyName][$rawFilter])){
|
||||
$flattenedProperties[$propertyName][$rawFilter] = $flattenRule;
|
||||
}elseif(!$flattenRule->equals($flattenedProperties[$propertyName][$rawFilter])){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
if(isset($candidateFlattenedValues[$propertyName][$rawFilter])){
|
||||
$valuesToIds = $candidateFlattenedValues[$propertyName][$rawFilter];
|
||||
$existingNewId = $valuesToIds[$rawValue] ?? null;
|
||||
if($existingNewId !== null && $existingNewId !== $pair->new->getName()){
|
||||
//this old value is associated with multiple new IDs - bad candidate for flattening
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
foreach(Utils::stringifyKeys($valuesToIds) as $otherRawValue => $otherNewId){
|
||||
if($otherRawValue === $rawValue){
|
||||
continue;
|
||||
}
|
||||
if($otherNewId === $pair->new->getName()){
|
||||
//this old value maps to the same new ID as another old value - bad candidate for flattening
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$candidateFlattenedValues[$propertyName][$rawFilter][$rawValue] = $pair->new->getName();
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($notFlattenedProperties) as $propertyName => $_){
|
||||
unset($flattenedProperties[$propertyName]);
|
||||
unset($candidateFlattenedValues[$propertyName]);
|
||||
}
|
||||
|
||||
ksort($flattenedProperties, SORT_STRING);
|
||||
$flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues);
|
||||
$flattenProperty = array_key_first($flattenedProperties);
|
||||
|
||||
$list = [];
|
||||
@ -393,19 +473,15 @@ function processRemappedStates(array $upgradeTable) : array{
|
||||
}
|
||||
ksort($cleanedOldState);
|
||||
ksort($cleanedNewState);
|
||||
$flattened = false;
|
||||
if($flattenProperty !== null){
|
||||
$flattenedValue = $cleanedOldState[$flattenProperty] ?? null;
|
||||
if(!$flattenedValue instanceof StringTag){
|
||||
throw new AssumptionFailedError("This should always be a TAG_String");
|
||||
}
|
||||
if(!isset($notFlattenedPropertyValues[$flattenProperty][$flattenedValue->getValue()])){
|
||||
unset($cleanedOldState[$flattenProperty]);
|
||||
$flattened = true;
|
||||
throw new AssumptionFailedError("This should always be a TAG_String ($newName $flattenProperty)");
|
||||
}
|
||||
unset($cleanedOldState[$flattenProperty]);
|
||||
}
|
||||
$rawOldState = encodeOrderedProperties($cleanedOldState);
|
||||
$newNameRule = $flattenProperty !== null && $flattened ?
|
||||
$newNameRule = $flattenProperty !== null ?
|
||||
$flattenedProperties[$flattenProperty][$rawOldState] ?? throw new AssumptionFailedError("This should always be set") :
|
||||
$newName;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user