mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 11:16:57 +00:00
Compare commits
114 Commits
Author | SHA1 | Date | |
---|---|---|---|
d273ccf87c | |||
737f5066a0 | |||
10238d7934 | |||
6077748caa | |||
50e2c469a5 | |||
fa87602661 | |||
d3c9c137ad | |||
37322e0d50 | |||
55cf24aa02 | |||
3590d84d03 | |||
68f8fa8caf | |||
1ad190024a | |||
769a149057 | |||
ea339355bb | |||
b9288c238b | |||
16f29c775e | |||
e30e27dd57 | |||
cd6634d34b | |||
f013079ff6 | |||
c4abac4606 | |||
11fbc8db6f | |||
022362a01a | |||
98380e46bf | |||
dad9a7e6cd | |||
de6a91dabc | |||
0615afa766 | |||
d5919dc094 | |||
09904dc519 | |||
f799cfaba6 | |||
11f119551d | |||
2584314202 | |||
337e462c8f | |||
b680a1693c | |||
0e5395c59b | |||
94e0bf954b | |||
556b00d11f | |||
981f49ff56 | |||
f527a4c8fe | |||
7148c7a222 | |||
e31fd122d9 | |||
a835069564 | |||
b77193b987 | |||
11ca208d93 | |||
8d7f1a8557 | |||
7ff0ae19d6 | |||
1de66cb0de | |||
9f3533d870 | |||
2d24fac067 | |||
f193a990b0 | |||
c11c0679e3 | |||
ba48f258f3 | |||
e105c9bd76 | |||
23f4632409 | |||
264ce06cbf | |||
a6202d0442 | |||
8ec304e66e | |||
cbffbd23f9 | |||
9d7aec5891 | |||
ac8dbf8640 | |||
dbc7105e5b | |||
3b97d067a3 | |||
b0390a39fd | |||
5cb69e00d0 | |||
781e3643dd | |||
2ca50ecd36 | |||
5ad63f27bb | |||
f13eaaab05 | |||
72f3c0b4b9 | |||
b9a1ef1357 | |||
4abc36275c | |||
4b5ac53276 | |||
90409b50d1 | |||
bc2abf4b15 | |||
b2c97cf2f1 | |||
a35c3406a8 | |||
54ea404d80 | |||
98042f844f | |||
a0cca53f52 | |||
6872118355 | |||
efd113bdc8 | |||
34a5f91aa9 | |||
920341668f | |||
4fab518384 | |||
2616d8c5ad | |||
61d0181bfd | |||
d211392b67 | |||
6bb84bc46c | |||
20837c9894 | |||
f207d1bbf2 | |||
c7c20d4d79 | |||
c6a09e5ed8 | |||
a459e3c1a9 | |||
288bd4018b | |||
9b03b082ab | |||
db3bb55a2b | |||
8372c9efc2 | |||
5386e86079 | |||
1b0ef468f3 | |||
b69843a8bd | |||
03619ebca9 | |||
fd1bc1b845 | |||
c05116849a | |||
bf99917f2a | |||
57f3a04bc5 | |||
c51b1b2812 | |||
80125f9b19 | |||
8dc28b7ea8 | |||
58ce746ae1 | |||
74cb0be868 | |||
4d9b97d2bb | |||
c8da9dea95 | |||
e1f4fd3048 | |||
d3d7f24015 | |||
944dd7d3e4 |
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.1.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.1.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.1.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.1.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.29.0
|
||||
uses: shivammathur/setup-php@2.30.2
|
||||
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.29.0
|
||||
uses: shivammathur/setup-php@2.30.2
|
||||
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.29.0
|
||||
uses: shivammathur/setup-php@2.30.2
|
||||
with:
|
||||
php-version: 8.2
|
||||
tools: php-cs-fixer:3.49
|
||||
|
133
build/generate-biome-ids.php
Normal file
133
build/generate-biome-ids.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?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\build\generate_biome_ids;
|
||||
|
||||
use pocketmine\data\bedrock\BedrockDataFiles;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use function asort;
|
||||
use function dirname;
|
||||
use function fclose;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function str_replace;
|
||||
use function strtoupper;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
const HEADER = <<<'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);
|
||||
|
||||
|
||||
HEADER;
|
||||
|
||||
/** @return resource */
|
||||
function safe_fopen(string $file, string $flags){
|
||||
$result = fopen($file, $flags);
|
||||
if($result === false){
|
||||
throw new \RuntimeException("Failed to open file");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function make_const_name(string $name) : string{
|
||||
return strtoupper(str_replace(['.', 'minecraft:'], ['_', ''], $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $map
|
||||
* @phpstan-param array<string, int> $map
|
||||
*/
|
||||
function generate(array $map, string $outputFile) : void{
|
||||
$file = safe_fopen($outputFile, 'wb');
|
||||
fwrite($file, HEADER);
|
||||
fwrite($file, <<<'CLASSHEADER'
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
final class BiomeIds{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
CLASSHEADER
|
||||
);
|
||||
$list = $map;
|
||||
asort($list, SORT_NUMERIC);
|
||||
$lastId = -1;
|
||||
foreach(Utils::stringifyKeys($list) as $name => $id){
|
||||
if($name === ""){
|
||||
continue;
|
||||
}
|
||||
if($id !== $lastId + 1){
|
||||
fwrite($file, "\n");
|
||||
}
|
||||
$lastId = $id;
|
||||
fwrite($file, "\tpublic const " . make_const_name($name) . ' = ' . $id . ';' . "\n");
|
||||
}
|
||||
fwrite($file, "}\n");
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
$ids = json_decode(Filesystem::fileGetContents(BedrockDataFiles::BIOME_ID_MAP_JSON), true);
|
||||
if(!is_array($ids)){
|
||||
throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object");
|
||||
}
|
||||
$cleanedIds = [];
|
||||
foreach($ids as $name => $id){
|
||||
if(!is_string($name) || !is_int($id)){
|
||||
throw new \RuntimeException("Invalid biome ID map, expected string => int map");
|
||||
}
|
||||
$cleanedIds[$name] = $id;
|
||||
}
|
||||
generate($cleanedIds, dirname(__DIR__) . '/src/data/bedrock/BiomeIds.php');
|
||||
|
||||
echo "Done. Don't forget to run CS fixup after generating code.\n";
|
@ -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;
|
||||
|
Submodule build/php updated: 6f619bf7a0...084822aa9e
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){
|
||||
|
63
changelogs/5.12.md
Normal file
63
changelogs/5.12.md
Normal file
@ -0,0 +1,63 @@
|
||||
# 5.12.0
|
||||
Released 28th February 2024
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.60**
|
||||
|
||||
This is a minor feature release, with a few new features and 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 a `--version` command-line option to display the server version and exit.
|
||||
|
||||
## Tools
|
||||
- Added `tools/generate-biome-ids.php` to generate `pocketmine\data\bedrock\BiomeIds`.
|
||||
- Fixed ordering of property values generated by `tools/generate-block-palette-spec.php`.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- The following new classes have been added:
|
||||
- `utils\LightableTrait` - used by blocks with `getLit()` and `setLit()` methods
|
||||
- The following methods have been deprecated:
|
||||
- `Block->isSolid()` - this method returns confusing results which don't match expectations and no one really knows what it actually means
|
||||
- `CocoaBlock` now extends `Flowable` to match vanilla Minecraft behaviour.
|
||||
|
||||
### `pocketmine\plugin`
|
||||
- `PluginManager->registerEvent()` now throws an exception when given a generator function for the event handler.
|
||||
- `PluginManager->registerEvents()` now throws an exception if any of the detected event handlers are generator functions. Use `@notHandler` to have the function ignored if intended.
|
||||
|
||||
### `pocketmine\promise`
|
||||
- The following methods have been added:
|
||||
- `public static Promise::all(list<Promise> $promises) : Promise` - returns a promise that is resolved once all given promises are resolved, or is rejected if any of the promises are rejected.
|
||||
|
||||
### `pocketmine\scheduler`
|
||||
- The following methods have been deprecated:
|
||||
- `AsyncWorker->getFromThreadStore()` - use class static properties for thread-local storage
|
||||
- `AsyncWorker->removeFromThreadStore()`
|
||||
- `AsyncWorker->saveToThreadStore()`
|
||||
|
||||
## Documentation
|
||||
- Improved documentation of various methods in `Block`.
|
||||
|
||||
## Gameplay
|
||||
- The following new items have been added:
|
||||
- Name Tag
|
||||
|
||||
## Internals
|
||||
- Removed specialization of shutdown logic for `Thread` vs `Worker` (no specialization is required).
|
||||
- Authentication system no longer accepts logins signed with the old Mojang root public key.
|
||||
- ID to enum mappings in `pocketmine\data` now use a new `match` convention to allow static analysis to ensure that all enum cases are handled.
|
||||
- Updated version of `pocketmine/bedrock-protocol` allows avoiding decoding of some itemstack data from the client in most cases, improving performance.
|
||||
|
||||
# 5.12.1
|
||||
Released 13th March 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed `Player Network Receive - Decompression` timings not being stopped correctly when receiving an uncompressed packet.
|
||||
|
||||
## Internals
|
||||
- Removed hardcoded batch packet size limit. This was already covered by other limits anyway.
|
16
changelogs/5.13.md
Normal file
16
changelogs/5.13.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 5.13.0
|
||||
Released 13th March 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.70**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.70.
|
||||
|
||||
**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.70.
|
||||
- Removed support for earlier versions.
|
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,26 +33,26 @@
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.5.0+bedrock-1.20.60",
|
||||
"pocketmine/bedrock-data": "~2.8.0+bedrock-1.20.60",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.7.0+bedrock-1.20.60",
|
||||
"pocketmine/bedrock-protocol": "~27.0.0+bedrock-1.20.60",
|
||||
"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",
|
||||
"pocketmine/nbt": "~1.0.0",
|
||||
"pocketmine/raklib": "^0.15.0",
|
||||
"pocketmine/raklib-ipc": "^0.2.0",
|
||||
"pocketmine/raklib": "~1.1.0",
|
||||
"pocketmine/raklib-ipc": "~1.0.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"symfony/filesystem": "~6.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.58",
|
||||
"phpstan/phpstan": "1.10.66",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
|
||||
@ -83,11 +83,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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
320
composer.lock
generated
320
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": "14848cb7b70d0fa63ed46b30128c2320",
|
||||
"content-hash": "7cddeede03046e04932d117c76898baf",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -122,16 +122,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "3.5.0",
|
||||
"version": "4.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3"
|
||||
"reference": "d6b10cb0a5e69fb1dfe3b7f493bf4f519723a9cb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
|
||||
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
|
||||
"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.5.0"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.1.0"
|
||||
},
|
||||
"time": "2024-02-07T11:46:50+00:00"
|
||||
"time": "2024-04-05T17:28:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "2.8.0+bedrock-1.20.60",
|
||||
"version": "2.10.0+bedrock-1.20.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2"
|
||||
"reference": "d7d709fec3848f56ca77f5ff0e5d4741b59f9d69"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d8ea0355b7c835564af9fe6e273e650ac62c84a2",
|
||||
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2",
|
||||
"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.60"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.80"
|
||||
},
|
||||
"time": "2024-02-07T11:23:46+00:00"
|
||||
"time": "2024-04-25T10:08:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||
"version": "1.7.0",
|
||||
"version": "1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c"
|
||||
"reference": "24a89457c17c271b5378b195931e720873865a79"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/69772dd58e2b2c7b7513fa2bcdc46e782228641c",
|
||||
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c",
|
||||
"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.7.0"
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.9.0"
|
||||
},
|
||||
"time": "2024-02-07T11:58:05+00:00"
|
||||
"time": "2024-04-05T18:46:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "27.0.2+bedrock-1.20.60",
|
||||
"version": "30.0.0+bedrock-1.20.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "6905865133b69da8c95a13c563d349e1993c06b8"
|
||||
"reference": "dc7606a9f778eeeeccfae393bd58e0b62ec6f85a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6905865133b69da8c95a13c563d349e1993c06b8",
|
||||
"reference": "6905865133b69da8c95a13c563d349e1993c06b8",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/dc7606a9f778eeeeccfae393bd58e0b62ec6f85a",
|
||||
"reference": "dc7606a9f778eeeeccfae393bd58e0b62ec6f85a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -222,7 +222,7 @@
|
||||
"ramsey/uuid": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.39",
|
||||
"phpstan/phpstan": "1.10.59",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5 || ^10.0"
|
||||
@ -240,22 +240,22 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/27.0.2+bedrock-1.20.60"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/30.0.0+bedrock-1.20.80"
|
||||
},
|
||||
"time": "2024-02-23T13:43:39+00:00"
|
||||
"time": "2024-04-05T17:53:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a"
|
||||
"reference": "ccfc1899b859d45814ea3592e20ebec4cb731c84"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
|
||||
"reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/ccfc1899b859d45814ea3592e20ebec4cb731c84",
|
||||
"reference": "ccfc1899b859d45814ea3592e20ebec4cb731c84",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -264,10 +264,10 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "1.3.0",
|
||||
"phpstan/phpstan": "~1.10.3",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -282,9 +282,9 @@
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.4"
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.6"
|
||||
},
|
||||
"time": "2022-01-12T18:06:33+00:00"
|
||||
"time": "2024-03-04T15:04:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/callback-validator",
|
||||
@ -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",
|
||||
@ -616,28 +616,28 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.15.1",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca"
|
||||
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/79b7b4d1d7516dc6e322514453645ad9452b20ca",
|
||||
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97",
|
||||
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-sockets": "*",
|
||||
"php": "^8.0",
|
||||
"php": "^8.1",
|
||||
"php-64bit": "*",
|
||||
"php-ipv6": "*",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/log": "^0.3.0 || ^0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.9.17",
|
||||
"phpstan/phpstan": "1.10.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
@ -653,32 +653,32 @@
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.15.1"
|
||||
"source": "https://github.com/pmmp/RakLib/tree/1.1.1"
|
||||
},
|
||||
"time": "2023-03-07T15:10:34+00:00"
|
||||
"time": "2024-03-04T14:02:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib-ipc",
|
||||
"version": "0.2.0",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLibIpc.git",
|
||||
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c"
|
||||
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
|
||||
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/ce632ef2c6743e71eddb5dc329c49af6555f90bc",
|
||||
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php-64bit": "*",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/raklib": "^0.15.0"
|
||||
"pocketmine/raklib": "^0.15.0 || ^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.9.17",
|
||||
"phpstan/phpstan": "1.10.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
@ -694,9 +694,9 @@
|
||||
"description": "Channel-based protocols for inter-thread/inter-process communication with RakLib",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLibIpc/issues",
|
||||
"source": "https://github.com/pmmp/RakLibIpc/tree/0.2.0"
|
||||
"source": "https://github.com/pmmp/RakLibIpc/tree/1.0.1"
|
||||
},
|
||||
"time": "2023-02-13T13:40:40+00:00"
|
||||
"time": "2024-03-01T15:55:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
@ -921,16 +921,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.4.0",
|
||||
"version": "v6.4.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59"
|
||||
"reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59",
|
||||
"reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/9919b5509ada52cc7f66f9a35c86a4a29955c9d3",
|
||||
"reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -964,7 +964,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.0"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -980,20 +980,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-26T17:27:13+00:00"
|
||||
"time": "2024-03-21T19:36:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.28.0",
|
||||
"version": "v1.29.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
|
||||
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
|
||||
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
|
||||
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1007,9 +1007,6 @@
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
@ -1046,7 +1043,7 @@
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1062,20 +1059,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-26T09:26:14+00:00"
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.28.0",
|
||||
"version": "v1.29.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "42292d99c55abe617799667f454222c54c60e229"
|
||||
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
|
||||
"reference": "42292d99c55abe617799667f454222c54c60e229",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
|
||||
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1089,9 +1086,6 @@
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
@ -1129,7 +1123,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1145,7 +1139,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-28T09:04:16+00:00"
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@ -1210,16 +1204,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.0.0",
|
||||
"version": "v5.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
|
||||
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
|
||||
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1262,26 +1256,27 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
|
||||
},
|
||||
"time": "2024-01-07T17:17:35+00:00"
|
||||
"time": "2024-03-05T20:51:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/manifest.git",
|
||||
"reference": "97803eca37d319dfa7826cc2437fc020857acb53"
|
||||
"reference": "54750ef60c58e43759730615a392c31c80e23176"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
|
||||
"reference": "97803eca37d319dfa7826cc2437fc020857acb53",
|
||||
"url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
|
||||
"reference": "54750ef60c58e43759730615a392c31c80e23176",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"phar-io/version": "^3.0.1",
|
||||
@ -1322,9 +1317,15 @@
|
||||
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
|
||||
"support": {
|
||||
"issues": "https://github.com/phar-io/manifest/issues",
|
||||
"source": "https://github.com/phar-io/manifest/tree/2.0.3"
|
||||
"source": "https://github.com/phar-io/manifest/tree/2.0.4"
|
||||
},
|
||||
"time": "2021-07-20T11:28:43+00:00"
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/theseer",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-03T12:33:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/version",
|
||||
@ -1379,16 +1380,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.58",
|
||||
"version": "1.10.66",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "a23518379ec4defd9e47cbf81019526861623ec2"
|
||||
"reference": "94779c987e4ebd620025d9e5fdd23323903950bd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a23518379ec4defd9e47cbf81019526861623ec2",
|
||||
"reference": "a23518379ec4defd9e47cbf81019526861623ec2",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/94779c987e4ebd620025d9e5fdd23323903950bd",
|
||||
"reference": "94779c987e4ebd620025d9e5fdd23323903950bd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1437,20 +1438,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-02-12T20:02:57+00:00"
|
||||
"time": "2024-03-28T16:17:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
"version": "1.3.15",
|
||||
"version": "1.3.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a"
|
||||
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
|
||||
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d5242a59d035e46774f2e634b374bc39ff62cb95",
|
||||
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1487,27 +1488,27 @@
|
||||
"description": "PHPUnit extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15"
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.16"
|
||||
},
|
||||
"time": "2023-10-09T18:58:39+00:00"
|
||||
"time": "2024-02-23T09:51:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "1.5.2",
|
||||
"version": "1.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542"
|
||||
"reference": "568210bd301f94a0d4b1e5a0808c374c1b9cf11b"
|
||||
},
|
||||
"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/568210bd301f94a0d4b1e5a0808c374c1b9cf11b",
|
||||
"reference": "568210bd301f94a0d4b1e5a0808c374c1b9cf11b",
|
||||
"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",
|
||||
@ -1536,22 +1537,22 @@
|
||||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.3"
|
||||
},
|
||||
"time": "2023-10-30T14:35:06+00:00"
|
||||
"time": "2024-04-06T07:43:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "10.1.11",
|
||||
"version": "10.1.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "78c3b7625965c2513ee96569a4dbb62601784145"
|
||||
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
|
||||
"reference": "78c3b7625965c2513ee96569a4dbb62601784145",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
|
||||
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1608,7 +1609,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1616,7 +1617,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-21T15:38:30+00:00"
|
||||
"time": "2024-03-12T15:33:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -1964,16 +1965,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/cli-parser.git",
|
||||
"reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae"
|
||||
"reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae",
|
||||
"reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
|
||||
"reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2008,7 +2009,8 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/cli-parser",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
|
||||
"source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0"
|
||||
"security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2016,7 +2018,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-03T06:58:15+00:00"
|
||||
"time": "2024-03-02T07:12:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit",
|
||||
@ -2266,16 +2268,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "5.1.0",
|
||||
"version": "5.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
|
||||
"reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
|
||||
"reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2283,7 +2285,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"symfony/process": "^4.2 || ^5"
|
||||
"symfony/process": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -2321,7 +2323,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2329,20 +2331,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-22T10:55:06+00:00"
|
||||
"time": "2024-03-02T07:15:17+00:00"
|
||||
},
|
||||
{
|
||||
"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": {
|
||||
@ -2357,7 +2359,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "6.0-dev"
|
||||
"dev-main": "6.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2385,7 +2387,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": [
|
||||
{
|
||||
@ -2393,20 +2395,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-11T05:39:26+00:00"
|
||||
"time": "2024-03-23T08:47:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "5.1.1",
|
||||
"version": "5.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
|
||||
"reference": "955288482d97c19a372d3f31006ab3f37da47adf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
|
||||
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
|
||||
"reference": "955288482d97c19a372d3f31006ab3f37da47adf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2463,7 +2465,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
||||
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2471,20 +2473,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-09-24T13:22:09+00:00"
|
||||
"time": "2024-03-02T07:17:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
"version": "6.0.1",
|
||||
"version": "6.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/global-state.git",
|
||||
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4"
|
||||
"reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4",
|
||||
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
|
||||
"reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2518,14 +2520,14 @@
|
||||
}
|
||||
],
|
||||
"description": "Snapshotting of global state",
|
||||
"homepage": "http://www.github.com/sebastianbergmann/global-state",
|
||||
"homepage": "https://www.github.com/sebastianbergmann/global-state",
|
||||
"keywords": [
|
||||
"global state"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/global-state/issues",
|
||||
"security": "https://github.com/sebastianbergmann/global-state/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2533,7 +2535,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-19T07:19:23+00:00"
|
||||
"time": "2024-03-02T07:19:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
@ -2879,16 +2881,16 @@
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/theseer/tokenizer.git",
|
||||
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
|
||||
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
|
||||
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
|
||||
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2917,7 +2919,7 @@
|
||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
"support": {
|
||||
"issues": "https://github.com/theseer/tokenizer/issues",
|
||||
"source": "https://github.com/theseer/tokenizer/tree/1.2.2"
|
||||
"source": "https://github.com/theseer/tokenizer/tree/1.2.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2925,7 +2927,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-11-20T00:12:19+00:00"
|
||||
"time": "2024-03-03T12:36:25+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
@ -2951,7 +2953,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pmmpthread": "^6.0.7",
|
||||
"ext-pmmpthread": "^6.1.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
|
@ -45,4 +45,8 @@ final class BootstrapOptions{
|
||||
public const PLUGINS = "plugins";
|
||||
/** Path to store and load server data */
|
||||
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";
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine {
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\thread\ThreadManager;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\utils\Filesystem;
|
||||
@ -40,14 +41,17 @@ namespace pocketmine {
|
||||
use function extension_loaded;
|
||||
use function function_exists;
|
||||
use function getcwd;
|
||||
use function getopt;
|
||||
use function is_dir;
|
||||
use function mkdir;
|
||||
use function phpversion;
|
||||
use function preg_match;
|
||||
use function preg_quote;
|
||||
use function printf;
|
||||
use function realpath;
|
||||
use function version_compare;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_EOL;
|
||||
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
|
||||
@ -120,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.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +170,7 @@ namespace pocketmine {
|
||||
* @return void
|
||||
*/
|
||||
function emit_performance_warnings(\Logger $logger){
|
||||
if(PHP_DEBUG !== 0){
|
||||
if(ZEND_DEBUG_BUILD){
|
||||
$logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
|
||||
}
|
||||
if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){
|
||||
@ -273,6 +277,11 @@ JIT_WARNING
|
||||
|
||||
ErrorToExceptionHandler::set();
|
||||
|
||||
if(count(getopt("", [BootstrapOptions::VERSION])) > 0){
|
||||
printf("%s %s (git hash %s) for Minecraft: Bedrock Edition %s\n", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true), VersionInfo::GIT_HASH(), ProtocolInfo::MINECRAFT_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
|
||||
$dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd;
|
||||
$pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
|
||||
@ -308,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])){
|
||||
@ -316,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);
|
||||
|
@ -59,7 +59,6 @@ use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\PacketBroadcaster;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
|
||||
use pocketmine\network\mcpe\raklib\RakLibInterface;
|
||||
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
|
||||
@ -1187,12 +1186,11 @@ class Server{
|
||||
bool $useQuery,
|
||||
PacketBroadcaster $packetBroadcaster,
|
||||
EntityEventBroadcaster $entityEventBroadcaster,
|
||||
PacketSerializerContext $packetSerializerContext,
|
||||
TypeConverter $typeConverter
|
||||
) : bool{
|
||||
$prettyIp = $ipV6 ? "[$ip]" : $ip;
|
||||
try{
|
||||
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter));
|
||||
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $typeConverter));
|
||||
}catch(NetworkInterfaceStartException $e){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
|
||||
$ip,
|
||||
@ -1219,15 +1217,14 @@ class Server{
|
||||
$useQuery = $this->configGroup->getConfigBool(ServerProperties::ENABLE_QUERY, true);
|
||||
|
||||
$typeConverter = TypeConverter::getInstance();
|
||||
$packetSerializerContext = new PacketSerializerContext($typeConverter->getItemTypeDictionary());
|
||||
$packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext);
|
||||
$packetBroadcaster = new StandardPacketBroadcaster($this);
|
||||
$entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster, $typeConverter);
|
||||
|
||||
if(
|
||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) ||
|
||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter) ||
|
||||
(
|
||||
$this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) &&
|
||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter)
|
||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter)
|
||||
)
|
||||
){
|
||||
return false;
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.11.2";
|
||||
public const BASE_VERSION = "5.15.0";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
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,7 +34,6 @@ use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function array_filter;
|
||||
use function assert;
|
||||
use function count;
|
||||
|
||||
@ -89,11 +88,12 @@ abstract class BaseBanner extends Transparent{
|
||||
* @return $this
|
||||
*/
|
||||
public function setPatterns(array $patterns) : self{
|
||||
$checked = array_filter($patterns, fn($v) => $v instanceof BannerPatternLayer);
|
||||
if(count($checked) !== count($patterns)){
|
||||
throw new \TypeError("Deque must only contain " . BannerPatternLayer::class . " objects");
|
||||
foreach($patterns as $pattern){
|
||||
if(!$pattern instanceof BannerPatternLayer){
|
||||
throw new \TypeError("Array must only contain " . BannerPatternLayer::class . " objects");
|
||||
}
|
||||
}
|
||||
$this->patterns = $checked;
|
||||
$this->patterns = $patterns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -402,7 +402,7 @@ class Block{
|
||||
}
|
||||
|
||||
/**
|
||||
* AKA: Block->isPlaceable
|
||||
* Returns whether this block can be placed when obtained as an item.
|
||||
*/
|
||||
public function canBePlaced() : bool{
|
||||
return true;
|
||||
@ -572,16 +572,28 @@ class Block{
|
||||
return $this->getLightFilter() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this block allows any light to pass through it.
|
||||
*/
|
||||
public function isTransparent() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TL;DR: Don't use this function. Its results are confusing and inconsistent.
|
||||
*
|
||||
* No one is sure what the meaning of this property actually is. It's borrowed from Minecraft Java Edition, and is
|
||||
* used by various blocks for support checks.
|
||||
*
|
||||
* Things like signs and banners are considered "solid" despite having no collision box, and things like skulls and
|
||||
* flower pots are considered non-solid despite obviously being "solid" in the conventional, real-world sense.
|
||||
*/
|
||||
public function isSolid() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* AKA: Block->isFlowable
|
||||
* Returns whether this block can be destroyed by liquid flowing into its cell.
|
||||
*/
|
||||
public function canBeFlowedInto() : bool{
|
||||
return false;
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Fertilizer;
|
||||
@ -40,7 +39,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
class CocoaBlock extends Transparent{
|
||||
class CocoaBlock extends Flowable{
|
||||
use HorizontalFacingTrait;
|
||||
use AgeableTrait;
|
||||
|
||||
@ -65,10 +64,6 @@ class CocoaBlock extends Transparent{
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function canAttachTo(Block $block) : bool{
|
||||
return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Furnace as TileFurnace;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -34,11 +35,10 @@ use function mt_rand;
|
||||
|
||||
class Furnace extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use LightableTrait;
|
||||
|
||||
protected FurnaceType $furnaceType;
|
||||
|
||||
protected bool $lit = false;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, FurnaceType $furnaceType){
|
||||
$this->furnaceType = $furnaceType;
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
@ -57,18 +57,6 @@ class Furnace extends Opaque{
|
||||
return $this->lit ? 13 : 0;
|
||||
}
|
||||
|
||||
public function isLit() : bool{
|
||||
return $this->lit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLit(bool $lit = true) : self{
|
||||
$this->lit = $lit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
$furnace = $this->position->getWorld()->getTile($this->position);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -32,23 +32,7 @@ use pocketmine\player\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class RedstoneOre extends Opaque{
|
||||
protected bool $lit = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->bool($this->lit);
|
||||
}
|
||||
|
||||
public function isLit() : bool{
|
||||
return $this->lit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLit(bool $lit = true) : self{
|
||||
$this->lit = $lit;
|
||||
return $this;
|
||||
}
|
||||
use LightableTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 9 : 0;
|
||||
|
@ -23,28 +23,22 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
class RedstoneTorch extends Torch{
|
||||
protected bool $lit = true;
|
||||
use LightableTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->lit = true;
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
parent::describeBlockOnlyState($w);
|
||||
$w->bool($this->lit);
|
||||
}
|
||||
|
||||
public function isLit() : bool{
|
||||
return $this->lit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLit(bool $lit = true) : self{
|
||||
$this->lit = $lit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 7 : 0;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -82,6 +82,7 @@ enum BannerPatternType{
|
||||
case DIAGONAL_UP_LEFT;
|
||||
case DIAGONAL_UP_RIGHT;
|
||||
case FLOWER;
|
||||
case GLOBE;
|
||||
case GRADIENT;
|
||||
case GRADIENT_UP;
|
||||
case HALF_HORIZONTAL;
|
||||
@ -89,6 +90,7 @@ enum BannerPatternType{
|
||||
case HALF_VERTICAL;
|
||||
case HALF_VERTICAL_RIGHT;
|
||||
case MOJANG;
|
||||
case PIGLIN;
|
||||
case RHOMBUS;
|
||||
case SKULL;
|
||||
case SMALL_STRIPES;
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
@ -38,24 +37,12 @@ use pocketmine\world\sound\FireExtinguishSound;
|
||||
use pocketmine\world\sound\FlintSteelSound;
|
||||
|
||||
trait CandleTrait{
|
||||
private bool $lit = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->bool($this->lit);
|
||||
}
|
||||
use LightableTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 3 : 0;
|
||||
}
|
||||
|
||||
public function isLit() : bool{ return $this->lit; }
|
||||
|
||||
/** @return $this */
|
||||
public function setLit(bool $lit) : self{
|
||||
$this->lit = $lit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @see Block::onInteract() */
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
|
||||
|
46
src/block/utils/LightableTrait.php
Normal file
46
src/block/utils/LightableTrait.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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\block\utils;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
trait LightableTrait{
|
||||
protected bool $lit = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->bool($this->lit);
|
||||
}
|
||||
|
||||
public function isLit() : bool{
|
||||
return $this->lit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLit(bool $lit = true) : self{
|
||||
$this->lit = $lit;
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -43,44 +43,50 @@ final class BannerPatternTypeIdMap{
|
||||
private array $enumToId = [];
|
||||
|
||||
public function __construct(){
|
||||
$this->register("bo", BannerPatternType::BORDER);
|
||||
$this->register("bri", BannerPatternType::BRICKS);
|
||||
$this->register("mc", BannerPatternType::CIRCLE);
|
||||
$this->register("cre", BannerPatternType::CREEPER);
|
||||
$this->register("cr", BannerPatternType::CROSS);
|
||||
$this->register("cbo", BannerPatternType::CURLY_BORDER);
|
||||
$this->register("lud", BannerPatternType::DIAGONAL_LEFT);
|
||||
$this->register("rd", BannerPatternType::DIAGONAL_RIGHT);
|
||||
$this->register("ld", BannerPatternType::DIAGONAL_UP_LEFT);
|
||||
$this->register("rud", BannerPatternType::DIAGONAL_UP_RIGHT);
|
||||
$this->register("flo", BannerPatternType::FLOWER);
|
||||
$this->register("gra", BannerPatternType::GRADIENT);
|
||||
$this->register("gru", BannerPatternType::GRADIENT_UP);
|
||||
$this->register("hh", BannerPatternType::HALF_HORIZONTAL);
|
||||
$this->register("hhb", BannerPatternType::HALF_HORIZONTAL_BOTTOM);
|
||||
$this->register("vh", BannerPatternType::HALF_VERTICAL);
|
||||
$this->register("vhr", BannerPatternType::HALF_VERTICAL_RIGHT);
|
||||
$this->register("moj", BannerPatternType::MOJANG);
|
||||
$this->register("mr", BannerPatternType::RHOMBUS);
|
||||
$this->register("sku", BannerPatternType::SKULL);
|
||||
$this->register("ss", BannerPatternType::SMALL_STRIPES);
|
||||
$this->register("bl", BannerPatternType::SQUARE_BOTTOM_LEFT);
|
||||
$this->register("br", BannerPatternType::SQUARE_BOTTOM_RIGHT);
|
||||
$this->register("tl", BannerPatternType::SQUARE_TOP_LEFT);
|
||||
$this->register("tr", BannerPatternType::SQUARE_TOP_RIGHT);
|
||||
$this->register("sc", BannerPatternType::STRAIGHT_CROSS);
|
||||
$this->register("bs", BannerPatternType::STRIPE_BOTTOM);
|
||||
$this->register("cs", BannerPatternType::STRIPE_CENTER);
|
||||
$this->register("dls", BannerPatternType::STRIPE_DOWNLEFT);
|
||||
$this->register("drs", BannerPatternType::STRIPE_DOWNRIGHT);
|
||||
$this->register("ls", BannerPatternType::STRIPE_LEFT);
|
||||
$this->register("ms", BannerPatternType::STRIPE_MIDDLE);
|
||||
$this->register("rs", BannerPatternType::STRIPE_RIGHT);
|
||||
$this->register("ts", BannerPatternType::STRIPE_TOP);
|
||||
$this->register("bt", BannerPatternType::TRIANGLE_BOTTOM);
|
||||
$this->register("tt", BannerPatternType::TRIANGLE_TOP);
|
||||
$this->register("bts", BannerPatternType::TRIANGLES_BOTTOM);
|
||||
$this->register("tts", BannerPatternType::TRIANGLES_TOP);
|
||||
foreach(BannerPatternType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
BannerPatternType::BORDER => "bo",
|
||||
BannerPatternType::BRICKS => "bri",
|
||||
BannerPatternType::CIRCLE => "mc",
|
||||
BannerPatternType::CREEPER => "cre",
|
||||
BannerPatternType::CROSS => "cr",
|
||||
BannerPatternType::CURLY_BORDER => "cbo",
|
||||
BannerPatternType::DIAGONAL_LEFT => "lud",
|
||||
BannerPatternType::DIAGONAL_RIGHT => "rd",
|
||||
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",
|
||||
BannerPatternType::HALF_HORIZONTAL_BOTTOM => "hhb",
|
||||
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",
|
||||
BannerPatternType::SQUARE_BOTTOM_LEFT => "bl",
|
||||
BannerPatternType::SQUARE_BOTTOM_RIGHT => "br",
|
||||
BannerPatternType::SQUARE_TOP_LEFT => "tl",
|
||||
BannerPatternType::SQUARE_TOP_RIGHT => "tr",
|
||||
BannerPatternType::STRAIGHT_CROSS => "sc",
|
||||
BannerPatternType::STRIPE_BOTTOM => "bs",
|
||||
BannerPatternType::STRIPE_CENTER => "cs",
|
||||
BannerPatternType::STRIPE_DOWNLEFT => "dls",
|
||||
BannerPatternType::STRIPE_DOWNRIGHT => "drs",
|
||||
BannerPatternType::STRIPE_LEFT => "ls",
|
||||
BannerPatternType::STRIPE_MIDDLE => "ms",
|
||||
BannerPatternType::STRIPE_RIGHT => "rs",
|
||||
BannerPatternType::STRIPE_TOP => "ts",
|
||||
BannerPatternType::TRIANGLE_BOTTOM => "bt",
|
||||
BannerPatternType::TRIANGLE_TOP => "tt",
|
||||
BannerPatternType::TRIANGLES_BOTTOM => "bts",
|
||||
BannerPatternType::TRIANGLES_TOP => "tts",
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
|
||||
public function register(string $stringId, BannerPatternType $type) : void{
|
||||
|
@ -111,4 +111,15 @@ final class BiomeIds{
|
||||
public const CRIMSON_FOREST = 179;
|
||||
public const WARPED_FOREST = 180;
|
||||
public const BASALT_DELTAS = 181;
|
||||
public const JAGGED_PEAKS = 182;
|
||||
public const FROZEN_PEAKS = 183;
|
||||
public const SNOWY_SLOPES = 184;
|
||||
public const GROVE = 185;
|
||||
public const MEADOW = 186;
|
||||
public const LUSH_CAVES = 187;
|
||||
public const DRIPSTONE_CAVES = 188;
|
||||
public const STONY_PEAKS = 189;
|
||||
public const DEEP_DARK = 190;
|
||||
public const MANGROVE_SWAMP = 191;
|
||||
public const CHERRY_GROVE = 192;
|
||||
}
|
||||
|
@ -48,22 +48,28 @@ final class DyeColorIdMap{
|
||||
private array $enumToItemId = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(0, ItemTypeNames::WHITE_DYE, DyeColor::WHITE);
|
||||
$this->register(1, ItemTypeNames::ORANGE_DYE, DyeColor::ORANGE);
|
||||
$this->register(2, ItemTypeNames::MAGENTA_DYE, DyeColor::MAGENTA);
|
||||
$this->register(3, ItemTypeNames::LIGHT_BLUE_DYE, DyeColor::LIGHT_BLUE);
|
||||
$this->register(4, ItemTypeNames::YELLOW_DYE, DyeColor::YELLOW);
|
||||
$this->register(5, ItemTypeNames::LIME_DYE, DyeColor::LIME);
|
||||
$this->register(6, ItemTypeNames::PINK_DYE, DyeColor::PINK);
|
||||
$this->register(7, ItemTypeNames::GRAY_DYE, DyeColor::GRAY);
|
||||
$this->register(8, ItemTypeNames::LIGHT_GRAY_DYE, DyeColor::LIGHT_GRAY);
|
||||
$this->register(9, ItemTypeNames::CYAN_DYE, DyeColor::CYAN);
|
||||
$this->register(10, ItemTypeNames::PURPLE_DYE, DyeColor::PURPLE);
|
||||
$this->register(11, ItemTypeNames::BLUE_DYE, DyeColor::BLUE);
|
||||
$this->register(12, ItemTypeNames::BROWN_DYE, DyeColor::BROWN);
|
||||
$this->register(13, ItemTypeNames::GREEN_DYE, DyeColor::GREEN);
|
||||
$this->register(14, ItemTypeNames::RED_DYE, DyeColor::RED);
|
||||
$this->register(15, ItemTypeNames::BLACK_DYE, DyeColor::BLACK);
|
||||
foreach(DyeColor::cases() as $case){
|
||||
[$colorId, $dyeItemId] = match($case){
|
||||
DyeColor::WHITE => [0, ItemTypeNames::WHITE_DYE],
|
||||
DyeColor::ORANGE => [1, ItemTypeNames::ORANGE_DYE],
|
||||
DyeColor::MAGENTA => [2, ItemTypeNames::MAGENTA_DYE],
|
||||
DyeColor::LIGHT_BLUE => [3, ItemTypeNames::LIGHT_BLUE_DYE],
|
||||
DyeColor::YELLOW => [4, ItemTypeNames::YELLOW_DYE],
|
||||
DyeColor::LIME => [5, ItemTypeNames::LIME_DYE],
|
||||
DyeColor::PINK => [6, ItemTypeNames::PINK_DYE],
|
||||
DyeColor::GRAY => [7, ItemTypeNames::GRAY_DYE],
|
||||
DyeColor::LIGHT_GRAY => [8, ItemTypeNames::LIGHT_GRAY_DYE],
|
||||
DyeColor::CYAN => [9, ItemTypeNames::CYAN_DYE],
|
||||
DyeColor::PURPLE => [10, ItemTypeNames::PURPLE_DYE],
|
||||
DyeColor::BLUE => [11, ItemTypeNames::BLUE_DYE],
|
||||
DyeColor::BROWN => [12, ItemTypeNames::BROWN_DYE],
|
||||
DyeColor::GREEN => [13, ItemTypeNames::GREEN_DYE],
|
||||
DyeColor::RED => [14, ItemTypeNames::RED_DYE],
|
||||
DyeColor::BLACK => [15, ItemTypeNames::BLACK_DYE],
|
||||
};
|
||||
|
||||
$this->register($colorId, $dyeItemId, $case);
|
||||
}
|
||||
}
|
||||
|
||||
private function register(int $id, string $itemId, DyeColor $color) : void{
|
||||
|
@ -32,9 +32,13 @@ final class MedicineTypeIdMap{
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
private function __construct(){
|
||||
$this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE);
|
||||
$this->register(MedicineTypeIds::ELIXIR, MedicineType::ELIXIR);
|
||||
$this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS);
|
||||
$this->register(MedicineTypeIds::TONIC, MedicineType::TONIC);
|
||||
foreach(MedicineType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
MedicineType::ANTIDOTE => MedicineTypeIds::ANTIDOTE,
|
||||
MedicineType::ELIXIR => MedicineTypeIds::ELIXIR,
|
||||
MedicineType::EYE_DROPS => MedicineTypeIds::EYE_DROPS,
|
||||
MedicineType::TONIC => MedicineTypeIds::TONIC,
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,12 +32,16 @@ final class MobHeadTypeIdMap{
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
private function __construct(){
|
||||
$this->register(0, MobHeadType::SKELETON);
|
||||
$this->register(1, MobHeadType::WITHER_SKELETON);
|
||||
$this->register(2, MobHeadType::ZOMBIE);
|
||||
$this->register(3, MobHeadType::PLAYER);
|
||||
$this->register(4, MobHeadType::CREEPER);
|
||||
$this->register(5, MobHeadType::DRAGON);
|
||||
$this->register(6, MobHeadType::PIGLIN);
|
||||
foreach(MobHeadType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
MobHeadType::SKELETON => 0,
|
||||
MobHeadType::WITHER_SKELETON => 1,
|
||||
MobHeadType::ZOMBIE => 2,
|
||||
MobHeadType::PLAYER => 3,
|
||||
MobHeadType::CREEPER => 4,
|
||||
MobHeadType::DRAGON => 5,
|
||||
MobHeadType::PIGLIN => 6,
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,16 +33,20 @@ final class MushroomBlockTypeIdMap{
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
public function __construct(){
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, MushroomBlockType::CAP_NORTHWEST);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, MushroomBlockType::CAP_NORTH);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, MushroomBlockType::CAP_NORTHEAST);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, MushroomBlockType::CAP_WEST);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, MushroomBlockType::CAP_MIDDLE);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, MushroomBlockType::CAP_EAST);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, MushroomBlockType::CAP_SOUTHWEST);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, MushroomBlockType::CAP_SOUTH);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST);
|
||||
$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP);
|
||||
foreach(MushroomBlockType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES,
|
||||
MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER,
|
||||
MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE,
|
||||
MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER,
|
||||
MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE,
|
||||
MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY,
|
||||
MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE,
|
||||
MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER,
|
||||
MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE,
|
||||
MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER,
|
||||
MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP,
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,21 +32,25 @@ final class NoteInstrumentIdMap{
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
private function __construct(){
|
||||
$this->register(0, NoteInstrument::PIANO);
|
||||
$this->register(1, NoteInstrument::BASS_DRUM);
|
||||
$this->register(2, NoteInstrument::SNARE);
|
||||
$this->register(3, NoteInstrument::CLICKS_AND_STICKS);
|
||||
$this->register(4, NoteInstrument::DOUBLE_BASS);
|
||||
$this->register(5, NoteInstrument::BELL);
|
||||
$this->register(6, NoteInstrument::FLUTE);
|
||||
$this->register(7, NoteInstrument::CHIME);
|
||||
$this->register(8, NoteInstrument::GUITAR);
|
||||
$this->register(9, NoteInstrument::XYLOPHONE);
|
||||
$this->register(10, NoteInstrument::IRON_XYLOPHONE);
|
||||
$this->register(11, NoteInstrument::COW_BELL);
|
||||
$this->register(12, NoteInstrument::DIDGERIDOO);
|
||||
$this->register(13, NoteInstrument::BIT);
|
||||
$this->register(14, NoteInstrument::BANJO);
|
||||
$this->register(15, NoteInstrument::PLING);
|
||||
foreach(NoteInstrument::cases() as $case){
|
||||
$this->register(match($case){
|
||||
NoteInstrument::PIANO => 0,
|
||||
NoteInstrument::BASS_DRUM => 1,
|
||||
NoteInstrument::SNARE => 2,
|
||||
NoteInstrument::CLICKS_AND_STICKS => 3,
|
||||
NoteInstrument::DOUBLE_BASS => 4,
|
||||
NoteInstrument::BELL => 5,
|
||||
NoteInstrument::FLUTE => 6,
|
||||
NoteInstrument::CHIME => 7,
|
||||
NoteInstrument::GUITAR => 8,
|
||||
NoteInstrument::XYLOPHONE => 9,
|
||||
NoteInstrument::IRON_XYLOPHONE => 10,
|
||||
NoteInstrument::COW_BELL => 11,
|
||||
NoteInstrument::DIDGERIDOO => 12,
|
||||
NoteInstrument::BIT => 13,
|
||||
NoteInstrument::BANJO => 14,
|
||||
NoteInstrument::PLING => 15,
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,48 +32,52 @@ final class PotionTypeIdMap{
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
private function __construct(){
|
||||
$this->register(PotionTypeIds::WATER, PotionType::WATER);
|
||||
$this->register(PotionTypeIds::MUNDANE, PotionType::MUNDANE);
|
||||
$this->register(PotionTypeIds::LONG_MUNDANE, PotionType::LONG_MUNDANE);
|
||||
$this->register(PotionTypeIds::THICK, PotionType::THICK);
|
||||
$this->register(PotionTypeIds::AWKWARD, PotionType::AWKWARD);
|
||||
$this->register(PotionTypeIds::NIGHT_VISION, PotionType::NIGHT_VISION);
|
||||
$this->register(PotionTypeIds::LONG_NIGHT_VISION, PotionType::LONG_NIGHT_VISION);
|
||||
$this->register(PotionTypeIds::INVISIBILITY, PotionType::INVISIBILITY);
|
||||
$this->register(PotionTypeIds::LONG_INVISIBILITY, PotionType::LONG_INVISIBILITY);
|
||||
$this->register(PotionTypeIds::LEAPING, PotionType::LEAPING);
|
||||
$this->register(PotionTypeIds::LONG_LEAPING, PotionType::LONG_LEAPING);
|
||||
$this->register(PotionTypeIds::STRONG_LEAPING, PotionType::STRONG_LEAPING);
|
||||
$this->register(PotionTypeIds::FIRE_RESISTANCE, PotionType::FIRE_RESISTANCE);
|
||||
$this->register(PotionTypeIds::LONG_FIRE_RESISTANCE, PotionType::LONG_FIRE_RESISTANCE);
|
||||
$this->register(PotionTypeIds::SWIFTNESS, PotionType::SWIFTNESS);
|
||||
$this->register(PotionTypeIds::LONG_SWIFTNESS, PotionType::LONG_SWIFTNESS);
|
||||
$this->register(PotionTypeIds::STRONG_SWIFTNESS, PotionType::STRONG_SWIFTNESS);
|
||||
$this->register(PotionTypeIds::SLOWNESS, PotionType::SLOWNESS);
|
||||
$this->register(PotionTypeIds::LONG_SLOWNESS, PotionType::LONG_SLOWNESS);
|
||||
$this->register(PotionTypeIds::WATER_BREATHING, PotionType::WATER_BREATHING);
|
||||
$this->register(PotionTypeIds::LONG_WATER_BREATHING, PotionType::LONG_WATER_BREATHING);
|
||||
$this->register(PotionTypeIds::HEALING, PotionType::HEALING);
|
||||
$this->register(PotionTypeIds::STRONG_HEALING, PotionType::STRONG_HEALING);
|
||||
$this->register(PotionTypeIds::HARMING, PotionType::HARMING);
|
||||
$this->register(PotionTypeIds::STRONG_HARMING, PotionType::STRONG_HARMING);
|
||||
$this->register(PotionTypeIds::POISON, PotionType::POISON);
|
||||
$this->register(PotionTypeIds::LONG_POISON, PotionType::LONG_POISON);
|
||||
$this->register(PotionTypeIds::STRONG_POISON, PotionType::STRONG_POISON);
|
||||
$this->register(PotionTypeIds::REGENERATION, PotionType::REGENERATION);
|
||||
$this->register(PotionTypeIds::LONG_REGENERATION, PotionType::LONG_REGENERATION);
|
||||
$this->register(PotionTypeIds::STRONG_REGENERATION, PotionType::STRONG_REGENERATION);
|
||||
$this->register(PotionTypeIds::STRENGTH, PotionType::STRENGTH);
|
||||
$this->register(PotionTypeIds::LONG_STRENGTH, PotionType::LONG_STRENGTH);
|
||||
$this->register(PotionTypeIds::STRONG_STRENGTH, PotionType::STRONG_STRENGTH);
|
||||
$this->register(PotionTypeIds::WEAKNESS, PotionType::WEAKNESS);
|
||||
$this->register(PotionTypeIds::LONG_WEAKNESS, PotionType::LONG_WEAKNESS);
|
||||
$this->register(PotionTypeIds::WITHER, PotionType::WITHER);
|
||||
$this->register(PotionTypeIds::TURTLE_MASTER, PotionType::TURTLE_MASTER);
|
||||
$this->register(PotionTypeIds::LONG_TURTLE_MASTER, PotionType::LONG_TURTLE_MASTER);
|
||||
$this->register(PotionTypeIds::STRONG_TURTLE_MASTER, PotionType::STRONG_TURTLE_MASTER);
|
||||
$this->register(PotionTypeIds::SLOW_FALLING, PotionType::SLOW_FALLING);
|
||||
$this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING);
|
||||
$this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS);
|
||||
foreach(PotionType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
PotionType::WATER => PotionTypeIds::WATER,
|
||||
PotionType::MUNDANE => PotionTypeIds::MUNDANE,
|
||||
PotionType::LONG_MUNDANE => PotionTypeIds::LONG_MUNDANE,
|
||||
PotionType::THICK => PotionTypeIds::THICK,
|
||||
PotionType::AWKWARD => PotionTypeIds::AWKWARD,
|
||||
PotionType::NIGHT_VISION => PotionTypeIds::NIGHT_VISION,
|
||||
PotionType::LONG_NIGHT_VISION => PotionTypeIds::LONG_NIGHT_VISION,
|
||||
PotionType::INVISIBILITY => PotionTypeIds::INVISIBILITY,
|
||||
PotionType::LONG_INVISIBILITY => PotionTypeIds::LONG_INVISIBILITY,
|
||||
PotionType::LEAPING => PotionTypeIds::LEAPING,
|
||||
PotionType::LONG_LEAPING => PotionTypeIds::LONG_LEAPING,
|
||||
PotionType::STRONG_LEAPING => PotionTypeIds::STRONG_LEAPING,
|
||||
PotionType::FIRE_RESISTANCE => PotionTypeIds::FIRE_RESISTANCE,
|
||||
PotionType::LONG_FIRE_RESISTANCE => PotionTypeIds::LONG_FIRE_RESISTANCE,
|
||||
PotionType::SWIFTNESS => PotionTypeIds::SWIFTNESS,
|
||||
PotionType::LONG_SWIFTNESS => PotionTypeIds::LONG_SWIFTNESS,
|
||||
PotionType::STRONG_SWIFTNESS => PotionTypeIds::STRONG_SWIFTNESS,
|
||||
PotionType::SLOWNESS => PotionTypeIds::SLOWNESS,
|
||||
PotionType::LONG_SLOWNESS => PotionTypeIds::LONG_SLOWNESS,
|
||||
PotionType::WATER_BREATHING => PotionTypeIds::WATER_BREATHING,
|
||||
PotionType::LONG_WATER_BREATHING => PotionTypeIds::LONG_WATER_BREATHING,
|
||||
PotionType::HEALING => PotionTypeIds::HEALING,
|
||||
PotionType::STRONG_HEALING => PotionTypeIds::STRONG_HEALING,
|
||||
PotionType::HARMING => PotionTypeIds::HARMING,
|
||||
PotionType::STRONG_HARMING => PotionTypeIds::STRONG_HARMING,
|
||||
PotionType::POISON => PotionTypeIds::POISON,
|
||||
PotionType::LONG_POISON => PotionTypeIds::LONG_POISON,
|
||||
PotionType::STRONG_POISON => PotionTypeIds::STRONG_POISON,
|
||||
PotionType::REGENERATION => PotionTypeIds::REGENERATION,
|
||||
PotionType::LONG_REGENERATION => PotionTypeIds::LONG_REGENERATION,
|
||||
PotionType::STRONG_REGENERATION => PotionTypeIds::STRONG_REGENERATION,
|
||||
PotionType::STRENGTH => PotionTypeIds::STRENGTH,
|
||||
PotionType::LONG_STRENGTH => PotionTypeIds::LONG_STRENGTH,
|
||||
PotionType::STRONG_STRENGTH => PotionTypeIds::STRONG_STRENGTH,
|
||||
PotionType::WEAKNESS => PotionTypeIds::WEAKNESS,
|
||||
PotionType::LONG_WEAKNESS => PotionTypeIds::LONG_WEAKNESS,
|
||||
PotionType::WITHER => PotionTypeIds::WITHER,
|
||||
PotionType::TURTLE_MASTER => PotionTypeIds::TURTLE_MASTER,
|
||||
PotionType::LONG_TURTLE_MASTER => PotionTypeIds::LONG_TURTLE_MASTER,
|
||||
PotionType::STRONG_TURTLE_MASTER => PotionTypeIds::STRONG_TURTLE_MASTER,
|
||||
PotionType::SLOW_FALLING => PotionTypeIds::SLOW_FALLING,
|
||||
PotionType::LONG_SLOW_FALLING => PotionTypeIds::LONG_SLOW_FALLING,
|
||||
PotionType::STRONG_SLOWNESS => PotionTypeIds::STRONG_SLOWNESS,
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,15 +32,20 @@ final class SuspiciousStewTypeIdMap{
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
private function __construct(){
|
||||
$this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY);
|
||||
$this->register(SuspiciousStewTypeIds::CORNFLOWER, SuspiciousStewType::CORNFLOWER);
|
||||
$this->register(SuspiciousStewTypeIds::TULIP, SuspiciousStewType::TULIP);
|
||||
$this->register(SuspiciousStewTypeIds::AZURE_BLUET, SuspiciousStewType::AZURE_BLUET);
|
||||
$this->register(SuspiciousStewTypeIds::LILY_OF_THE_VALLEY, SuspiciousStewType::LILY_OF_THE_VALLEY);
|
||||
$this->register(SuspiciousStewTypeIds::DANDELION, SuspiciousStewType::DANDELION);
|
||||
$this->register(SuspiciousStewTypeIds::BLUE_ORCHID, SuspiciousStewType::BLUE_ORCHID);
|
||||
$this->register(SuspiciousStewTypeIds::ALLIUM, SuspiciousStewType::ALLIUM);
|
||||
$this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY);
|
||||
$this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE);
|
||||
foreach(SuspiciousStewType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
SuspiciousStewType::POPPY => SuspiciousStewTypeIds::POPPY,
|
||||
SuspiciousStewType::CORNFLOWER => SuspiciousStewTypeIds::CORNFLOWER,
|
||||
SuspiciousStewType::TULIP => SuspiciousStewTypeIds::TULIP,
|
||||
SuspiciousStewType::AZURE_BLUET => SuspiciousStewTypeIds::AZURE_BLUET,
|
||||
SuspiciousStewType::LILY_OF_THE_VALLEY => SuspiciousStewTypeIds::LILY_OF_THE_VALLEY,
|
||||
SuspiciousStewType::DANDELION => SuspiciousStewTypeIds::DANDELION,
|
||||
SuspiciousStewType::BLUE_ORCHID => SuspiciousStewTypeIds::BLUE_ORCHID,
|
||||
SuspiciousStewType::ALLIUM => SuspiciousStewTypeIds::ALLIUM,
|
||||
SuspiciousStewType::OXEYE_DAISY => SuspiciousStewTypeIds::OXEYE_DAISY,
|
||||
SuspiciousStewType::WITHER_ROSE => SuspiciousStewTypeIds::WITHER_ROSE,
|
||||
}, $case);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ final class BlockStateData{
|
||||
public const CURRENT_VERSION =
|
||||
(1 << 24) | //major
|
||||
(20 << 16) | //minor
|
||||
(60 << 8) | //patch
|
||||
(1); //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";
|
||||
@ -105,9 +104,7 @@ final class BlockStateNames{
|
||||
public const MOISTURIZED_AMOUNT = "moisturized_amount";
|
||||
public const MONSTER_EGG_STONE_TYPE = "monster_egg_stone_type";
|
||||
public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits";
|
||||
public const NEW_LEAF_TYPE = "new_leaf_type";
|
||||
public const OCCUPIED_BIT = "occupied_bit";
|
||||
public const OLD_LEAF_TYPE = "old_leaf_type";
|
||||
public const OPEN_BIT = "open_bit";
|
||||
public const ORIENTATION = "orientation";
|
||||
public const OUTPUT_LIT_BIT = "output_lit_bit";
|
||||
@ -126,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";
|
||||
@ -151,6 +147,7 @@ final class BlockStateNames{
|
||||
public const UPDATE_BIT = "update_bit";
|
||||
public const UPPER_BLOCK_BIT = "upper_block_bit";
|
||||
public const UPSIDE_DOWN_BIT = "upside_down_bit";
|
||||
public const VAULT_STATE = "vault_state";
|
||||
public const VINE_DIRECTION_BITS = "vine_direction_bits";
|
||||
public const WALL_BLOCK_TYPE = "wall_block_type";
|
||||
public const WALL_CONNECTION_TYPE_EAST = "wall_connection_type_east";
|
||||
@ -160,5 +157,4 @@ final class BlockStateNames{
|
||||
public const WALL_POST_BIT = "wall_post_bit";
|
||||
public const WEEPING_VINES_AGE = "weeping_vines_age";
|
||||
public const WEIRDO_DIRECTION = "weirdo_direction";
|
||||
public const WOOD_TYPE = "wood_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";
|
||||
@ -143,14 +131,6 @@ final class BlockStateStringValues{
|
||||
public const MONSTER_EGG_STONE_TYPE_STONE = "stone";
|
||||
public const MONSTER_EGG_STONE_TYPE_STONE_BRICK = "stone_brick";
|
||||
|
||||
public const NEW_LEAF_TYPE_ACACIA = "acacia";
|
||||
public const NEW_LEAF_TYPE_DARK_OAK = "dark_oak";
|
||||
|
||||
public const OLD_LEAF_TYPE_BIRCH = "birch";
|
||||
public const OLD_LEAF_TYPE_JUNGLE = "jungle";
|
||||
public const OLD_LEAF_TYPE_OAK = "oak";
|
||||
public const OLD_LEAF_TYPE_SPRUCE = "spruce";
|
||||
|
||||
public const ORIENTATION_DOWN_EAST = "down_east";
|
||||
public const ORIENTATION_DOWN_NORTH = "down_north";
|
||||
public const ORIENTATION_DOWN_SOUTH = "down_south";
|
||||
@ -184,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";
|
||||
@ -264,6 +237,11 @@ final class BlockStateStringValues{
|
||||
public const TURTLE_EGG_COUNT_THREE_EGG = "three_egg";
|
||||
public const TURTLE_EGG_COUNT_TWO_EGG = "two_egg";
|
||||
|
||||
public const VAULT_STATE_ACTIVE = "active";
|
||||
public const VAULT_STATE_EJECTING = "ejecting";
|
||||
public const VAULT_STATE_INACTIVE = "inactive";
|
||||
public const VAULT_STATE_UNLOCKING = "unlocking";
|
||||
|
||||
public const WALL_BLOCK_TYPE_ANDESITE = "andesite";
|
||||
public const WALL_BLOCK_TYPE_BRICK = "brick";
|
||||
public const WALL_BLOCK_TYPE_COBBLESTONE = "cobblestone";
|
||||
@ -295,11 +273,4 @@ final class BlockStateStringValues{
|
||||
public const WALL_CONNECTION_TYPE_WEST_SHORT = "short";
|
||||
public const WALL_CONNECTION_TYPE_WEST_TALL = "tall";
|
||||
|
||||
public const WOOD_TYPE_ACACIA = "acacia";
|
||||
public const WOOD_TYPE_BIRCH = "birch";
|
||||
public const WOOD_TYPE_DARK_OAK = "dark_oak";
|
||||
public const WOOD_TYPE_JUNGLE = "jungle";
|
||||
public const WOOD_TYPE_OAK = "oak";
|
||||
public const WOOD_TYPE_SPRUCE = "spruce";
|
||||
|
||||
}
|
||||
|
@ -33,18 +33,24 @@ final class BlockTypeNames{
|
||||
|
||||
public const ACACIA_BUTTON = "minecraft:acacia_button";
|
||||
public const ACACIA_DOOR = "minecraft:acacia_door";
|
||||
public const ACACIA_DOUBLE_SLAB = "minecraft:acacia_double_slab";
|
||||
public const ACACIA_FENCE = "minecraft:acacia_fence";
|
||||
public const ACACIA_FENCE_GATE = "minecraft:acacia_fence_gate";
|
||||
public const ACACIA_HANGING_SIGN = "minecraft:acacia_hanging_sign";
|
||||
public const ACACIA_LEAVES = "minecraft:acacia_leaves";
|
||||
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";
|
||||
public const ACACIA_TRAPDOOR = "minecraft:acacia_trapdoor";
|
||||
public const ACACIA_WALL_SIGN = "minecraft:acacia_wall_sign";
|
||||
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";
|
||||
@ -55,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";
|
||||
@ -88,16 +95,21 @@ final class BlockTypeNames{
|
||||
public const BIG_DRIPLEAF = "minecraft:big_dripleaf";
|
||||
public const BIRCH_BUTTON = "minecraft:birch_button";
|
||||
public const BIRCH_DOOR = "minecraft:birch_door";
|
||||
public const BIRCH_DOUBLE_SLAB = "minecraft:birch_double_slab";
|
||||
public const BIRCH_FENCE = "minecraft:birch_fence";
|
||||
public const BIRCH_FENCE_GATE = "minecraft:birch_fence_gate";
|
||||
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
|
||||
public const BIRCH_LEAVES = "minecraft:birch_leaves";
|
||||
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";
|
||||
public const BIRCH_TRAPDOOR = "minecraft:birch_trapdoor";
|
||||
public const BIRCH_WALL_SIGN = "minecraft:birch_wall_sign";
|
||||
public const BIRCH_WOOD = "minecraft:birch_wood";
|
||||
public const BLACK_CANDLE = "minecraft:black_candle";
|
||||
public const BLACK_CANDLE_CAKE = "minecraft:black_candle_cake";
|
||||
public const BLACK_CARPET = "minecraft:black_carpet";
|
||||
@ -122,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";
|
||||
@ -131,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";
|
||||
@ -149,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";
|
||||
@ -220,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";
|
||||
@ -266,24 +280,34 @@ final class BlockTypeNames{
|
||||
public const CYAN_WOOL = "minecraft:cyan_wool";
|
||||
public const DARK_OAK_BUTTON = "minecraft:dark_oak_button";
|
||||
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
|
||||
public const DARK_OAK_DOUBLE_SLAB = "minecraft:dark_oak_double_slab";
|
||||
public const DARK_OAK_FENCE = "minecraft:dark_oak_fence";
|
||||
public const DARK_OAK_FENCE_GATE = "minecraft:dark_oak_fence_gate";
|
||||
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
|
||||
public const DARK_OAK_LEAVES = "minecraft:dark_oak_leaves";
|
||||
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";
|
||||
public const DARK_OAK_WOOD = "minecraft:dark_oak_wood";
|
||||
public const DARK_PRISMARINE_STAIRS = "minecraft:dark_prismarine_stairs";
|
||||
public const DARKOAK_STANDING_SIGN = "minecraft:darkoak_standing_sign";
|
||||
public const DARKOAK_WALL_SIGN = "minecraft:darkoak_wall_sign";
|
||||
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";
|
||||
@ -320,7 +344,6 @@ final class BlockTypeNames{
|
||||
public const DOUBLE_STONE_BLOCK_SLAB2 = "minecraft:double_stone_block_slab2";
|
||||
public const DOUBLE_STONE_BLOCK_SLAB3 = "minecraft:double_stone_block_slab3";
|
||||
public const DOUBLE_STONE_BLOCK_SLAB4 = "minecraft:double_stone_block_slab4";
|
||||
public const DOUBLE_WOODEN_SLAB = "minecraft:double_wooden_slab";
|
||||
public const DRAGON_EGG = "minecraft:dragon_egg";
|
||||
public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block";
|
||||
public const DRIPSTONE_BLOCK = "minecraft:dripstone_block";
|
||||
@ -469,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";
|
||||
@ -490,7 +514,7 @@ final class BlockTypeNames{
|
||||
public const GOLDEN_RAIL = "minecraft:golden_rail";
|
||||
public const GRANITE = "minecraft:granite";
|
||||
public const GRANITE_STAIRS = "minecraft:granite_stairs";
|
||||
public const GRASS = "minecraft:grass";
|
||||
public const GRASS_BLOCK = "minecraft:grass_block";
|
||||
public const GRASS_PATH = "minecraft:grass_path";
|
||||
public const GRAVEL = "minecraft:gravel";
|
||||
public const GRAY_CANDLE = "minecraft:gray_candle";
|
||||
@ -553,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";
|
||||
@ -572,16 +598,21 @@ final class BlockTypeNames{
|
||||
public const JUKEBOX = "minecraft:jukebox";
|
||||
public const JUNGLE_BUTTON = "minecraft:jungle_button";
|
||||
public const JUNGLE_DOOR = "minecraft:jungle_door";
|
||||
public const JUNGLE_DOUBLE_SLAB = "minecraft:jungle_double_slab";
|
||||
public const JUNGLE_FENCE = "minecraft:jungle_fence";
|
||||
public const JUNGLE_FENCE_GATE = "minecraft:jungle_fence_gate";
|
||||
public const JUNGLE_HANGING_SIGN = "minecraft:jungle_hanging_sign";
|
||||
public const JUNGLE_LEAVES = "minecraft:jungle_leaves";
|
||||
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";
|
||||
public const JUNGLE_TRAPDOOR = "minecraft:jungle_trapdoor";
|
||||
public const JUNGLE_WALL_SIGN = "minecraft:jungle_wall_sign";
|
||||
public const JUNGLE_WOOD = "minecraft:jungle_wood";
|
||||
public const KELP = "minecraft:kelp";
|
||||
public const LADDER = "minecraft:ladder";
|
||||
public const LANTERN = "minecraft:lantern";
|
||||
@ -589,8 +620,6 @@ final class BlockTypeNames{
|
||||
public const LAPIS_ORE = "minecraft:lapis_ore";
|
||||
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
|
||||
public const LAVA = "minecraft:lava";
|
||||
public const LEAVES = "minecraft:leaves";
|
||||
public const LEAVES2 = "minecraft:leaves2";
|
||||
public const LECTERN = "minecraft:lectern";
|
||||
public const LEVER = "minecraft:lever";
|
||||
public const LIGHT_BLOCK = "minecraft:light_block";
|
||||
@ -617,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";
|
||||
@ -698,11 +728,16 @@ final class BlockTypeNames{
|
||||
public const NETHERREACTOR = "minecraft:netherreactor";
|
||||
public const NORMAL_STONE_STAIRS = "minecraft:normal_stone_stairs";
|
||||
public const NOTEBLOCK = "minecraft:noteblock";
|
||||
public const OAK_DOUBLE_SLAB = "minecraft:oak_double_slab";
|
||||
public const OAK_FENCE = "minecraft:oak_fence";
|
||||
public const OAK_HANGING_SIGN = "minecraft:oak_hanging_sign";
|
||||
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";
|
||||
public const OBSERVER = "minecraft:observer";
|
||||
public const OBSIDIAN = "minecraft:obsidian";
|
||||
public const OCHRE_FROGLIGHT = "minecraft:ochre_froglight";
|
||||
@ -716,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";
|
||||
@ -741,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";
|
||||
@ -777,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";
|
||||
@ -813,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";
|
||||
@ -825,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";
|
||||
@ -839,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";
|
||||
@ -875,16 +913,21 @@ final class BlockTypeNames{
|
||||
public const SPORE_BLOSSOM = "minecraft:spore_blossom";
|
||||
public const SPRUCE_BUTTON = "minecraft:spruce_button";
|
||||
public const SPRUCE_DOOR = "minecraft:spruce_door";
|
||||
public const SPRUCE_DOUBLE_SLAB = "minecraft:spruce_double_slab";
|
||||
public const SPRUCE_FENCE = "minecraft:spruce_fence";
|
||||
public const SPRUCE_FENCE_GATE = "minecraft:spruce_fence_gate";
|
||||
public const SPRUCE_HANGING_SIGN = "minecraft:spruce_hanging_sign";
|
||||
public const SPRUCE_LEAVES = "minecraft:spruce_leaves";
|
||||
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";
|
||||
public const SPRUCE_TRAPDOOR = "minecraft:spruce_trapdoor";
|
||||
public const SPRUCE_WALL_SIGN = "minecraft:spruce_wall_sign";
|
||||
public const SPRUCE_WOOD = "minecraft:spruce_wood";
|
||||
public const STANDING_BANNER = "minecraft:standing_banner";
|
||||
public const STANDING_SIGN = "minecraft:standing_sign";
|
||||
public const STICKY_PISTON = "minecraft:sticky_piston";
|
||||
@ -902,18 +945,24 @@ final class BlockTypeNames{
|
||||
public const STONECUTTER = "minecraft:stonecutter";
|
||||
public const STONECUTTER_BLOCK = "minecraft:stonecutter_block";
|
||||
public const STRIPPED_ACACIA_LOG = "minecraft:stripped_acacia_log";
|
||||
public const STRIPPED_ACACIA_WOOD = "minecraft:stripped_acacia_wood";
|
||||
public const STRIPPED_BAMBOO_BLOCK = "minecraft:stripped_bamboo_block";
|
||||
public const STRIPPED_BIRCH_LOG = "minecraft:stripped_birch_log";
|
||||
public const STRIPPED_BIRCH_WOOD = "minecraft:stripped_birch_wood";
|
||||
public const STRIPPED_CHERRY_LOG = "minecraft:stripped_cherry_log";
|
||||
public const STRIPPED_CHERRY_WOOD = "minecraft:stripped_cherry_wood";
|
||||
public const STRIPPED_CRIMSON_HYPHAE = "minecraft:stripped_crimson_hyphae";
|
||||
public const STRIPPED_CRIMSON_STEM = "minecraft:stripped_crimson_stem";
|
||||
public const STRIPPED_DARK_OAK_LOG = "minecraft:stripped_dark_oak_log";
|
||||
public const STRIPPED_DARK_OAK_WOOD = "minecraft:stripped_dark_oak_wood";
|
||||
public const STRIPPED_JUNGLE_LOG = "minecraft:stripped_jungle_log";
|
||||
public const STRIPPED_JUNGLE_WOOD = "minecraft:stripped_jungle_wood";
|
||||
public const STRIPPED_MANGROVE_LOG = "minecraft:stripped_mangrove_log";
|
||||
public const STRIPPED_MANGROVE_WOOD = "minecraft:stripped_mangrove_wood";
|
||||
public const STRIPPED_OAK_LOG = "minecraft:stripped_oak_log";
|
||||
public const STRIPPED_OAK_WOOD = "minecraft:stripped_oak_wood";
|
||||
public const STRIPPED_SPRUCE_LOG = "minecraft:stripped_spruce_log";
|
||||
public const STRIPPED_SPRUCE_WOOD = "minecraft:stripped_spruce_wood";
|
||||
public const STRIPPED_WARPED_HYPHAE = "minecraft:stripped_warped_hyphae";
|
||||
public const STRIPPED_WARPED_STEM = "minecraft:stripped_warped_stem";
|
||||
public const STRUCTURE_BLOCK = "minecraft:structure_block";
|
||||
@ -934,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";
|
||||
@ -952,6 +1002,7 @@ final class BlockTypeNames{
|
||||
public const UNLIT_REDSTONE_TORCH = "minecraft:unlit_redstone_torch";
|
||||
public const UNPOWERED_COMPARATOR = "minecraft:unpowered_comparator";
|
||||
public const UNPOWERED_REPEATER = "minecraft:unpowered_repeater";
|
||||
public const VAULT = "minecraft:vault";
|
||||
public const VERDANT_FROGLIGHT = "minecraft:verdant_froglight";
|
||||
public const VINE = "minecraft:vine";
|
||||
public const WALL_BANNER = "minecraft:wall_banner";
|
||||
@ -1040,13 +1091,12 @@ 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 WOOD = "minecraft:wood";
|
||||
public const WOODEN_BUTTON = "minecraft:wooden_button";
|
||||
public const WOODEN_DOOR = "minecraft:wooden_door";
|
||||
public const WOODEN_PRESSURE_PLATE = "minecraft:wooden_pressure_plate";
|
||||
public const WOODEN_SLAB = "minecraft:wooden_slab";
|
||||
public const YELLOW_CANDLE = "minecraft:yellow_candle";
|
||||
public const YELLOW_CANDLE_CAKE = "minecraft:yellow_candle_cake";
|
||||
public const YELLOW_CARPET = "minecraft:yellow_carpet";
|
||||
|
@ -203,8 +203,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->registerFlatCoralSerializers();
|
||||
$this->registerCauldronSerializers();
|
||||
$this->registerFlatWoodBlockSerializers();
|
||||
$this->registerLegacyWoodBlockSerializers();
|
||||
$this->registerLeavesSerializers();
|
||||
$this->registerSaplingSerializers();
|
||||
$this->registerSimpleSerializers();
|
||||
$this->registerSerializers();
|
||||
}
|
||||
@ -538,6 +538,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{
|
||||
@ -558,9 +572,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
|
||||
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG);
|
||||
$this->mapLog(Blocks::ACACIA_WOOD(), Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD);
|
||||
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE);
|
||||
$this->mapSimple(Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS);
|
||||
//wood and slabs still use the old way of storing wood type
|
||||
$this->mapSlab(Blocks::ACACIA_SLAB(), Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB);
|
||||
|
||||
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
|
||||
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
|
||||
@ -570,10 +585,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
|
||||
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG);
|
||||
$this->mapLog(Blocks::BIRCH_WOOD(), Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD);
|
||||
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE);
|
||||
$this->mapSimple(Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS);
|
||||
$this->mapSlab(Blocks::BIRCH_SLAB(), Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
|
||||
//wood and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::CHERRY_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CHERRY_BUTTON)));
|
||||
$this->map(Blocks::CHERRY_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CHERRY_DOOR)));
|
||||
@ -621,10 +637,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
|
||||
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG);
|
||||
$this->mapLog(Blocks::DARK_OAK_WOOD(), Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD);
|
||||
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE);
|
||||
$this->mapSimple(Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS);
|
||||
$this->mapSlab(Blocks::DARK_OAK_SLAB(), Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
|
||||
//wood and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
|
||||
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
|
||||
@ -634,10 +651,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
|
||||
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG);
|
||||
$this->mapLog(Blocks::JUNGLE_WOOD(), Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD);
|
||||
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE);
|
||||
$this->mapSimple(Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS);
|
||||
$this->mapSlab(Blocks::JUNGLE_SLAB(), Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
|
||||
//wood and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON)));
|
||||
$this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR)));
|
||||
@ -671,10 +689,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
|
||||
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
|
||||
$this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG);
|
||||
$this->mapLog(Blocks::OAK_WOOD(), Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD);
|
||||
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE);
|
||||
$this->mapSimple(Blocks::OAK_PLANKS(), Ids::OAK_PLANKS);
|
||||
$this->mapSlab(Blocks::OAK_SLAB(), Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
|
||||
//wood and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON)));
|
||||
$this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR)));
|
||||
@ -684,8 +703,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
|
||||
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG);
|
||||
$this->mapLog(Blocks::SPRUCE_WOOD(), Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD);
|
||||
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE);
|
||||
$this->mapSimple(Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS);
|
||||
$this->mapSlab(Blocks::SPRUCE_SLAB(), Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
|
||||
//wood and slabs still use the old way of storing wood type
|
||||
|
||||
@ -704,30 +725,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
|
||||
}
|
||||
|
||||
private function registerLegacyWoodBlockSerializers() : void{
|
||||
foreach([
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_SLAB(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_SLAB(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_SLAB(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_SLAB(),
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_SLAB(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_SLAB(),
|
||||
] as $woodType => $block){
|
||||
$this->map($block, fn(Slab $block) => Helper::encodeWoodenSlab($block, $woodType));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Blocks::ACACIA_WOOD(),
|
||||
Blocks::BIRCH_WOOD(),
|
||||
Blocks::DARK_OAK_WOOD(),
|
||||
Blocks::JUNGLE_WOOD(),
|
||||
Blocks::OAK_WOOD(),
|
||||
Blocks::SPRUCE_WOOD(),
|
||||
] as $block){
|
||||
$this->map($block, fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerLeavesSerializers() : void{
|
||||
//flattened IDs
|
||||
$this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES)));
|
||||
@ -736,12 +733,25 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES)));
|
||||
|
||||
//legacy mess
|
||||
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_ACACIA));
|
||||
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_BIRCH));
|
||||
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_DARK_OAK));
|
||||
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_JUNGLE));
|
||||
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_OAK));
|
||||
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_SPRUCE));
|
||||
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::ACACIA_LEAVES)));
|
||||
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::BIRCH_LEAVES)));
|
||||
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::DARK_OAK_LEAVES)));
|
||||
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::JUNGLE_LEAVES)));
|
||||
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::OAK_LEAVES)));
|
||||
$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{
|
||||
@ -927,7 +937,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK);
|
||||
$this->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE);
|
||||
$this->mapSimple(Blocks::GRANITE(), Ids::GRANITE);
|
||||
$this->mapSimple(Blocks::GRASS(), Ids::GRASS);
|
||||
$this->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK);
|
||||
$this->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH);
|
||||
$this->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL);
|
||||
$this->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS);
|
||||
@ -1002,16 +1012,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(
|
||||
@ -1037,7 +1057,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())
|
||||
@ -1051,10 +1070,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)
|
||||
@ -1104,12 +1120,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)
|
||||
@ -1266,22 +1280,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));
|
||||
@ -1401,7 +1404,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)
|
||||
@ -1441,7 +1443,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());
|
||||
@ -1485,16 +1486,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());
|
||||
@ -1530,7 +1527,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)
|
||||
@ -1604,7 +1600,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));
|
||||
@ -1654,7 +1649,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));
|
||||
@ -1747,6 +1741,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?
|
||||
@ -362,18 +368,4 @@ final class BlockStateDeserializerHelper{
|
||||
default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_4, $type),
|
||||
};
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public static function mapWoodenSlabType(BlockStateReader $in) : Slab{
|
||||
// * wood_type (StringTag) = acacia, birch, dark_oak, jungle, oak, spruce
|
||||
return match($type = $in->readString(BlockStateNames::WOOD_TYPE)){
|
||||
StringValues::WOOD_TYPE_ACACIA => VanillaBlocks::ACACIA_SLAB(),
|
||||
StringValues::WOOD_TYPE_BIRCH => VanillaBlocks::BIRCH_SLAB(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => VanillaBlocks::DARK_OAK_SLAB(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => VanillaBlocks::JUNGLE_SLAB(),
|
||||
StringValues::WOOD_TYPE_OAK => VanillaBlocks::OAK_SLAB(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => VanillaBlocks::SPRUCE_SLAB(),
|
||||
default => throw $in->badValueException(BlockStateNames::WOOD_TYPE, $type),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,14 +56,6 @@ use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
final class BlockStateSerializerHelper{
|
||||
|
||||
public static function encodeAllSidedLog(Wood $block) : Writer{
|
||||
return Writer::create(Ids::WOOD)
|
||||
->writeBool(BlockStateNames::STRIPPED_BIT, $block->isStripped())
|
||||
->writePillarAxis($block->getAxis())
|
||||
->writeLegacyWoodType($block->getWoodType());
|
||||
}
|
||||
|
||||
public static function encodeButton(Button $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeFacingDirection($block->getFacing())
|
||||
@ -151,16 +143,6 @@ final class BlockStateSerializerHelper{
|
||||
->writeBool(BlockStateNames::UPDATE_BIT, $block->isCheckDecay());
|
||||
}
|
||||
|
||||
public static function encodeLeaves1(Leaves $block, string $type) : Writer{
|
||||
return self::encodeLeaves($block, Writer::create(Ids::LEAVES)
|
||||
->writeString(BlockStateNames::OLD_LEAF_TYPE, $type));
|
||||
}
|
||||
|
||||
public static function encodeLeaves2(Leaves $block, string $type) : Writer{
|
||||
return self::encodeLeaves($block, Writer::create(Ids::LEAVES2)
|
||||
->writeString(BlockStateNames::NEW_LEAF_TYPE, $type));
|
||||
}
|
||||
|
||||
public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : Writer{
|
||||
return Writer::create($block->isStill() ? $stillId : $flowingId)
|
||||
->writeInt(BlockStateNames::LIQUID_DEPTH, $block->getDecay() | ($block->isFalling() ? 0x8 : 0));
|
||||
@ -185,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{
|
||||
@ -279,9 +256,4 @@ final class BlockStateSerializerHelper{
|
||||
return $out
|
||||
->writeHorizontalFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeWoodenSlab(Slab $block, string $typeValue) : Writer{
|
||||
return self::encodeSlab($block, Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB)
|
||||
->writeString(BlockStateNames::WOOD_TYPE, $typeValue);
|
||||
}
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->registerFlatCoralDeserializers();
|
||||
$this->registerCauldronDeserializers();
|
||||
$this->registerFlatWoodBlockDeserializers();
|
||||
$this->registerLegacyWoodBlockDeserializers();
|
||||
$this->registerLeavesDeserializers();
|
||||
$this->registerSaplingDeserializers();
|
||||
$this->registerSimpleDeserializers();
|
||||
$this->registerDeserializers();
|
||||
}
|
||||
@ -439,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{
|
||||
@ -468,10 +479,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
|
||||
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG());
|
||||
$this->mapLog(Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD, fn() => Blocks::ACACIA_WOOD());
|
||||
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE());
|
||||
$this->mapSimple(Ids::ACACIA_PLANKS, fn() => Blocks::ACACIA_PLANKS());
|
||||
$this->mapSlab(Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB, fn() => Blocks::ACACIA_SLAB());
|
||||
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in));
|
||||
$this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in));
|
||||
@ -481,10 +493,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
|
||||
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG());
|
||||
$this->mapLog(Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD, fn() => Blocks::BIRCH_WOOD());
|
||||
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE());
|
||||
$this->mapSimple(Ids::BIRCH_PLANKS, fn() => Blocks::BIRCH_PLANKS());
|
||||
$this->mapSlab(Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB, fn() => Blocks::BIRCH_SLAB());
|
||||
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::CHERRY_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CHERRY_BUTTON(), $in));
|
||||
$this->map(Ids::CHERRY_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CHERRY_DOOR(), $in));
|
||||
@ -526,10 +539,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
|
||||
$this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG());
|
||||
$this->mapLog(Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD, fn() => Blocks::DARK_OAK_WOOD());
|
||||
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE());
|
||||
$this->mapSimple(Ids::DARK_OAK_PLANKS, fn() => Blocks::DARK_OAK_PLANKS());
|
||||
$this->mapSlab(Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB, fn() => Blocks::DARK_OAK_SLAB());
|
||||
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in));
|
||||
$this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in));
|
||||
@ -539,10 +553,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG());
|
||||
$this->mapLog(Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD, fn() => Blocks::JUNGLE_WOOD());
|
||||
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE());
|
||||
$this->mapSimple(Ids::JUNGLE_PLANKS, fn() => Blocks::JUNGLE_PLANKS());
|
||||
$this->mapSlab(Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB, fn() => Blocks::JUNGLE_SLAB());
|
||||
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in));
|
||||
$this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in));
|
||||
@ -571,10 +586,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
|
||||
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG());
|
||||
$this->mapLog(Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD, fn() => Blocks::OAK_WOOD());
|
||||
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE());
|
||||
$this->mapSimple(Ids::OAK_PLANKS, fn() => Blocks::OAK_PLANKS());
|
||||
$this->mapSlab(Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB, fn() => Blocks::OAK_SLAB());
|
||||
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in));
|
||||
$this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in));
|
||||
@ -584,10 +600,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG());
|
||||
$this->mapLog(Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD, fn() => Blocks::SPRUCE_WOOD());
|
||||
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE());
|
||||
$this->mapSimple(Ids::SPRUCE_PLANKS, fn() => Blocks::SPRUCE_PLANKS());
|
||||
$this->mapSlab(Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB, fn() => Blocks::SPRUCE_SLAB());
|
||||
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in));
|
||||
$this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in));
|
||||
@ -604,40 +621,30 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
|
||||
}
|
||||
|
||||
private function registerLegacyWoodBlockDeserializers() : void{
|
||||
$this->mapSlab(Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB, fn(Reader $in) => Helper::mapWoodenSlabType($in));
|
||||
|
||||
$this->map(Ids::WOOD, fn(Reader $in) : Block => Helper::decodeLog(match($woodType = $in->readString(StateNames::WOOD_TYPE)){
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_WOOD(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_WOOD(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_WOOD(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_WOOD(),
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_WOOD(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_WOOD(),
|
||||
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodType),
|
||||
}, $in->readBool(StateNames::STRIPPED_BIT), $in));
|
||||
}
|
||||
|
||||
private function registerLeavesDeserializers() : void{
|
||||
//flattened IDs
|
||||
$this->map(Ids::ACACIA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::ACACIA_LEAVES(), $in));
|
||||
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
|
||||
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
|
||||
$this->map(Ids::BIRCH_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::BIRCH_LEAVES(), $in));
|
||||
$this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in));
|
||||
$this->map(Ids::DARK_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::DARK_OAK_LEAVES(), $in));
|
||||
$this->map(Ids::JUNGLE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::JUNGLE_LEAVES(), $in));
|
||||
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
|
||||
$this->map(Ids::OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::OAK_LEAVES(), $in));
|
||||
$this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in));
|
||||
}
|
||||
|
||||
//legacy mess
|
||||
$this->map(Ids::LEAVES, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::OLD_LEAF_TYPE)){
|
||||
StringValues::OLD_LEAF_TYPE_BIRCH => Blocks::BIRCH_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_JUNGLE => Blocks::JUNGLE_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_OAK => Blocks::OAK_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_SPRUCE => Blocks::SPRUCE_LEAVES(),
|
||||
default => throw $in->badValueException(StateNames::OLD_LEAF_TYPE, $type),
|
||||
}, $in));
|
||||
$this->map(Ids::LEAVES2, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::NEW_LEAF_TYPE)){
|
||||
StringValues::NEW_LEAF_TYPE_ACACIA => Blocks::ACACIA_LEAVES(),
|
||||
StringValues::NEW_LEAF_TYPE_DARK_OAK => Blocks::DARK_OAK_LEAVES(),
|
||||
default => throw $in->badValueException(StateNames::NEW_LEAF_TYPE, $type),
|
||||
}, $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{
|
||||
@ -821,7 +828,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::GOLD_BLOCK, fn() => Blocks::GOLD());
|
||||
$this->mapSimple(Ids::GOLD_ORE, fn() => Blocks::GOLD_ORE());
|
||||
$this->mapSimple(Ids::GRANITE, fn() => Blocks::GRANITE());
|
||||
$this->mapSimple(Ids::GRASS, fn() => Blocks::GRASS());
|
||||
$this->mapSimple(Ids::GRASS_BLOCK, fn() => Blocks::GRASS());
|
||||
$this->mapSimple(Ids::GRASS_PATH, fn() => Blocks::GRASS_PATH());
|
||||
$this->mapSimple(Ids::GRAVEL, fn() => Blocks::GRAVEL());
|
||||
$this->mapSimple(Ids::HANGING_ROOTS, fn() => Blocks::HANGING_ROOTS());
|
||||
@ -898,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{
|
||||
@ -939,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{
|
||||
@ -1096,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)
|
||||
@ -1431,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{
|
||||
@ -1497,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)
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\utils\WallConnectionType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\block\BlockStateNames;
|
||||
@ -257,20 +256,6 @@ final class BlockStateWriter{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function writeLegacyWoodType(WoodType $treeType) : self{
|
||||
$this->writeString(BlockStateNames::WOOD_TYPE, match($treeType){
|
||||
WoodType::OAK => StringValues::WOOD_TYPE_OAK,
|
||||
WoodType::SPRUCE => StringValues::WOOD_TYPE_SPRUCE,
|
||||
WoodType::BIRCH => StringValues::WOOD_TYPE_BIRCH,
|
||||
WoodType::JUNGLE => StringValues::WOOD_TYPE_JUNGLE,
|
||||
WoodType::ACACIA => StringValues::WOOD_TYPE_ACACIA,
|
||||
WoodType::DARK_OAK => StringValues::WOOD_TYPE_DARK_OAK,
|
||||
default => throw new BlockStateSerializeException("Invalid legacy wood type " . $treeType->name)
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function writeCoralType(CoralType $coralType) : self{
|
||||
$this->writeString(BlockStateNames::CORAL_COLOR, match($coralType){
|
||||
|
@ -64,20 +64,24 @@ final class BlockStateUpgradeSchema{
|
||||
*/
|
||||
public array $remappedStates = [];
|
||||
|
||||
public readonly int $versionId;
|
||||
|
||||
public function __construct(
|
||||
public int $maxVersionMajor,
|
||||
public int $maxVersionMinor,
|
||||
public int $maxVersionPatch,
|
||||
public int $maxVersionRevision,
|
||||
public readonly int $maxVersionMajor,
|
||||
public readonly int $maxVersionMinor,
|
||||
public readonly int $maxVersionPatch,
|
||||
public readonly int $maxVersionRevision,
|
||||
private int $schemaId
|
||||
){}
|
||||
){
|
||||
$this->versionId = ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This is defined by Mojang, and therefore cannot be relied on. Use getSchemaId() instead for
|
||||
* internal version management.
|
||||
*/
|
||||
public function getVersionId() : int{
|
||||
return ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision;
|
||||
return $this->versionId;
|
||||
}
|
||||
|
||||
public function getSchemaId() : int{ return $this->schemaId; }
|
||||
|
@ -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
|
||||
|
@ -35,9 +35,14 @@ use function sprintf;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
final class BlockStateUpgrader{
|
||||
/** @var BlockStateUpgradeSchema[] */
|
||||
/**
|
||||
* @var BlockStateUpgradeSchema[][] versionId => [schemaId => schema]
|
||||
* @phpstan-var array<int, array<int, BlockStateUpgradeSchema>>
|
||||
*/
|
||||
private array $upgradeSchemas = [];
|
||||
|
||||
private int $outputVersion = 0;
|
||||
|
||||
/**
|
||||
* @param BlockStateUpgradeSchema[] $upgradeSchemas
|
||||
* @phpstan-param array<int, BlockStateUpgradeSchema> $upgradeSchemas
|
||||
@ -50,87 +55,116 @@ final class BlockStateUpgrader{
|
||||
|
||||
public function addSchema(BlockStateUpgradeSchema $schema) : void{
|
||||
$schemaId = $schema->getSchemaId();
|
||||
if(isset($this->upgradeSchemas[$schemaId])){
|
||||
throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID");
|
||||
$versionId = $schema->getVersionId();
|
||||
if(isset($this->upgradeSchemas[$versionId][$schemaId])){
|
||||
throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID and version ID");
|
||||
}
|
||||
$this->upgradeSchemas[$schemaId] = $schema;
|
||||
|
||||
//schema ID tells us the order when multiple schemas use the same version ID
|
||||
$this->upgradeSchemas[$versionId][$schemaId] = $schema;
|
||||
|
||||
ksort($this->upgradeSchemas, SORT_NUMERIC);
|
||||
ksort($this->upgradeSchemas[$versionId], SORT_NUMERIC);
|
||||
|
||||
$this->outputVersion = max($this->outputVersion, $schema->getVersionId());
|
||||
}
|
||||
|
||||
public function upgrade(BlockStateData $blockStateData) : BlockStateData{
|
||||
$version = $blockStateData->getVersion();
|
||||
$highestVersion = $version;
|
||||
foreach($this->upgradeSchemas as $schema){
|
||||
$resultVersion = $schema->getVersionId();
|
||||
$highestVersion = max($highestVersion, $resultVersion);
|
||||
if($version > $resultVersion){
|
||||
//even if this is actually the same version, we have to apply it anyway because mojang are dumb and
|
||||
//didn't always bump the blockstate version when changing it :(
|
||||
foreach($this->upgradeSchemas as $resultVersion => $schemaList){
|
||||
/*
|
||||
* Sometimes Mojang made changes without bumping the version ID.
|
||||
* A notable example is 0131_1.18.20.27_beta_to_1.18.30.json, which renamed a bunch of blockIDs.
|
||||
* When this happens, all the schemas must be applied even if the version is the same, because the input
|
||||
* version doesn't tell us which of the schemas have already been applied.
|
||||
* If there's only one schema for a version (the norm), we can safely assume it's already been applied if
|
||||
* the version is the same, and skip over it.
|
||||
*/
|
||||
if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
|
||||
continue;
|
||||
}
|
||||
$oldName = $blockStateData->getName();
|
||||
$oldState = $blockStateData->getStates();
|
||||
if(isset($schema->remappedStates[$oldName])){
|
||||
foreach($schema->remappedStates[$oldName] as $remap){
|
||||
if(count($remap->oldState) > count($oldState)){
|
||||
//match criteria has more requirements than we have state properties
|
||||
continue; //try next state
|
||||
}
|
||||
foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
|
||||
if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){
|
||||
continue 2; //try next state
|
||||
}
|
||||
}
|
||||
|
||||
if(is_string($remap->newName)){
|
||||
$newName = $remap->newName;
|
||||
}else{
|
||||
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
|
||||
if($flattenedValue instanceof StringTag){
|
||||
$newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix);
|
||||
unset($oldState[$remap->newName->flattenedProperty]);
|
||||
}else{
|
||||
//flattened property is not a TAG_String, so this transformation is not applicable
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$newState = $remap->newState;
|
||||
foreach($remap->copiedState as $stateName){
|
||||
if(isset($oldState[$stateName])){
|
||||
$newState[$stateName] = $oldState[$stateName];
|
||||
}
|
||||
}
|
||||
|
||||
$blockStateData = new BlockStateData($newName, $newState, $resultVersion);
|
||||
continue 2; //try next schema
|
||||
}
|
||||
}
|
||||
$newName = $schema->renamedIds[$oldName] ?? null;
|
||||
|
||||
$stateChanges = 0;
|
||||
$states = $blockStateData->getStates();
|
||||
|
||||
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
|
||||
|
||||
if($newName !== null || $stateChanges > 0){
|
||||
$blockStateData = new BlockStateData($newName ?? $oldName, $states, $resultVersion);
|
||||
//don't break out; we may need to further upgrade the state
|
||||
foreach($schemaList as $schema){
|
||||
$blockStateData = $this->applySchema($schema, $blockStateData);
|
||||
}
|
||||
}
|
||||
|
||||
if($highestVersion > $version){
|
||||
if($this->outputVersion > $version){
|
||||
//always update the version number of the blockstate, even if it didn't change - this is needed for
|
||||
//external tools
|
||||
$blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $highestVersion);
|
||||
$blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion);
|
||||
}
|
||||
return $blockStateData;
|
||||
}
|
||||
|
||||
private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{
|
||||
$newStateData = $this->applyStateRemapped($schema, $blockStateData);
|
||||
if($newStateData !== null){
|
||||
return $newStateData;
|
||||
}
|
||||
|
||||
$oldName = $blockStateData->getName();
|
||||
$newName = $schema->renamedIds[$oldName] ?? null;
|
||||
|
||||
$stateChanges = 0;
|
||||
$states = $blockStateData->getStates();
|
||||
|
||||
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
|
||||
|
||||
if($newName !== null || $stateChanges > 0){
|
||||
return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId());
|
||||
}
|
||||
|
||||
return $blockStateData;
|
||||
}
|
||||
|
||||
private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{
|
||||
$oldName = $blockStateData->getName();
|
||||
$oldState = $blockStateData->getStates();
|
||||
|
||||
if(isset($schema->remappedStates[$oldName])){
|
||||
foreach($schema->remappedStates[$oldName] as $remap){
|
||||
if(count($remap->oldState) > count($oldState)){
|
||||
//match criteria has more requirements than we have state properties
|
||||
continue; //try next state
|
||||
}
|
||||
foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
|
||||
if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){
|
||||
continue 2; //try next state
|
||||
}
|
||||
}
|
||||
|
||||
if(is_string($remap->newName)){
|
||||
$newName = $remap->newName;
|
||||
}else{
|
||||
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
|
||||
if($flattenedValue instanceof StringTag){
|
||||
$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
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$newState = $remap->newState;
|
||||
foreach($remap->copiedState as $stateName){
|
||||
if(isset($oldState[$stateName])){
|
||||
$newState[$stateName] = $oldState[$stateName];
|
||||
}
|
||||
}
|
||||
|
||||
return new BlockStateData($newName, $newState, $schema->getVersionId());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag[] $states
|
||||
* @phpstan-param array<string, Tag> $states
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -305,6 +305,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::MUSIC_DISC_WAIT, Items::RECORD_WAIT());
|
||||
$this->map1to1Item(Ids::MUSIC_DISC_WARD, Items::RECORD_WARD());
|
||||
$this->map1to1Item(Ids::MUTTON, Items::RAW_MUTTON());
|
||||
$this->map1to1Item(Ids::NAME_TAG, Items::NAME_TAG());
|
||||
$this->map1to1Item(Ids::NAUTILUS_SHELL, Items::NAUTILUS_SHELL());
|
||||
$this->map1to1Item(Ids::NETHER_STAR, Items::NETHER_STAR());
|
||||
$this->map1to1Item(Ids::NETHERBRICK, Items::NETHER_BRICK());
|
||||
|
@ -74,6 +74,8 @@ final class ItemTypeNames{
|
||||
public const BLEACH = "minecraft:bleach";
|
||||
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";
|
||||
@ -81,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";
|
||||
@ -140,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";
|
||||
@ -154,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";
|
||||
@ -204,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";
|
||||
@ -241,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";
|
||||
@ -286,6 +295,8 @@ final class ItemTypeNames{
|
||||
public const LEATHER_HELMET = "minecraft:leather_helmet";
|
||||
public const LEATHER_HORSE_ARMOR = "minecraft:leather_horse_armor";
|
||||
public const LEATHER_LEGGINGS = "minecraft:leather_leggings";
|
||||
public const LEAVES = "minecraft:leaves";
|
||||
public const LEAVES2 = "minecraft:leaves2";
|
||||
public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye";
|
||||
public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye";
|
||||
public const LIME_DYE = "minecraft:lime_dye";
|
||||
@ -294,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";
|
||||
@ -401,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";
|
||||
@ -409,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";
|
||||
@ -500,16 +515,19 @@ final class ItemTypeNames{
|
||||
public const WHEAT_SEEDS = "minecraft:wheat_seeds";
|
||||
public const WHITE_DYE = "minecraft:white_dye";
|
||||
public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template";
|
||||
public const WIND_CHARGE = "minecraft:wind_charge";
|
||||
public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg";
|
||||
public const WITHER_SKELETON_SPAWN_EGG = "minecraft:wither_skeleton_spawn_egg";
|
||||
public const WITHER_SPAWN_EGG = "minecraft:wither_spawn_egg";
|
||||
public const WOLF_ARMOR = "minecraft:wolf_armor";
|
||||
public const WOLF_SPAWN_EGG = "minecraft:wolf_spawn_egg";
|
||||
public const WOOD = "minecraft:wood";
|
||||
public const WOODEN_AXE = "minecraft:wooden_axe";
|
||||
public const WOODEN_DOOR = "minecraft:wooden_door";
|
||||
public const WOODEN_HOE = "minecraft:wooden_hoe";
|
||||
public const WOODEN_PICKAXE = "minecraft:wooden_pickaxe";
|
||||
public const WOODEN_SHOVEL = "minecraft:wooden_shovel";
|
||||
public const WOODEN_SLAB = "minecraft:wooden_slab";
|
||||
public const WOODEN_SWORD = "minecraft:wooden_sword";
|
||||
public const WOOL = "minecraft:wool";
|
||||
public const WRITABLE_BOOK = "minecraft:writable_book";
|
||||
|
@ -44,10 +44,14 @@ final class GameModeIdMap{
|
||||
private array $enumToId = [];
|
||||
|
||||
public function __construct(){
|
||||
$this->register(0, GameMode::SURVIVAL);
|
||||
$this->register(1, GameMode::CREATIVE);
|
||||
$this->register(2, GameMode::ADVENTURE);
|
||||
$this->register(3, GameMode::SPECTATOR);
|
||||
foreach(GameMode::cases() as $case){
|
||||
$this->register(match($case){
|
||||
GameMode::SURVIVAL => 0,
|
||||
GameMode::CREATIVE => 1,
|
||||
GameMode::ADVENTURE => 2,
|
||||
GameMode::SPECTATOR => 3,
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
|
||||
private function register(int $id, GameMode $type) : void{
|
||||
|
@ -29,11 +29,6 @@ namespace pocketmine\data\runtime;
|
||||
* @deprecated
|
||||
*/
|
||||
trait LegacyRuntimeEnumDescriberTrait{
|
||||
|
||||
/**
|
||||
* @phpstan-template T of \UnitEnum
|
||||
* @phpstan-param T $case
|
||||
*/
|
||||
abstract protected function enum(\UnitEnum &$case) : void;
|
||||
|
||||
public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{
|
||||
|
@ -89,10 +89,6 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{
|
||||
|
||||
public function straightOnlyRailShape(int &$railShape) : void;
|
||||
|
||||
/**
|
||||
* @phpstan-template T of \UnitEnum
|
||||
* @phpstan-param T $case
|
||||
*/
|
||||
public function enum(\UnitEnum &$case) : void;
|
||||
|
||||
/**
|
||||
|
@ -252,6 +252,14 @@ abstract class Entity{
|
||||
return $this->alwaysShowNameTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether players can rename this entity using a name tag.
|
||||
* Note that plugins can still name entities using setNameTag().
|
||||
*/
|
||||
public function canBeRenamed() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setNameTag(string $name) : void{
|
||||
$this->nameTag = $name;
|
||||
$this->networkPropertiesDirty = true;
|
||||
@ -792,7 +800,7 @@ abstract class Entity{
|
||||
}
|
||||
|
||||
protected function broadcastMotion() : void{
|
||||
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
|
||||
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion(), tick: 0)]);
|
||||
}
|
||||
|
||||
public function getGravity() : float{
|
||||
|
@ -132,6 +132,10 @@ abstract class Living extends Entity{
|
||||
|
||||
abstract public function getName() : string;
|
||||
|
||||
public function canBeRenamed() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
|
@ -37,8 +37,7 @@ class HandlerList{
|
||||
private array $affectedHandlerCaches = [];
|
||||
|
||||
/**
|
||||
* @phpstan-template TEvent of Event
|
||||
* @phpstan-param class-string<TEvent> $class
|
||||
* @phpstan-param class-string<covariant Event> $class
|
||||
*/
|
||||
public function __construct(
|
||||
private string $class,
|
||||
|
@ -86,8 +86,7 @@ class HandlerListManager{
|
||||
*
|
||||
* Calling this method also lazily initializes the $classMap inheritance tree of handler lists.
|
||||
*
|
||||
* @phpstan-template TEvent of Event
|
||||
* @phpstan-param class-string<TEvent> $event
|
||||
* @phpstan-param class-string<covariant Event> $event
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
* @throws \InvalidArgumentException
|
||||
@ -113,8 +112,7 @@ class HandlerListManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TEvent of Event
|
||||
* @phpstan-param class-string<TEvent> $event
|
||||
* @phpstan-param class-string<covariant Event> $event
|
||||
*
|
||||
* @return RegisteredListener[]
|
||||
*/
|
||||
|
106
src/event/player/PlayerResourcePackOfferEvent.php
Normal file
106
src/event/player/PlayerResourcePackOfferEvent.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?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\event\player;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\player\PlayerInfo;
|
||||
use pocketmine\resourcepacks\ResourcePack;
|
||||
use function array_unshift;
|
||||
|
||||
/**
|
||||
* Called after a player authenticates and is being offered resource packs to download.
|
||||
*
|
||||
* This event should be used to decide which resource packs to offer the player and whether to require the player to
|
||||
* download the packs before they can join the server.
|
||||
*/
|
||||
class PlayerResourcePackOfferEvent extends Event{
|
||||
/**
|
||||
* @param ResourcePack[] $resourcePacks
|
||||
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
|
||||
*
|
||||
* @phpstan-param list<ResourcePack> $resourcePacks
|
||||
* @phpstan-param array<string, string> $encryptionKeys
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly PlayerInfo $playerInfo,
|
||||
private array $resourcePacks,
|
||||
private array $encryptionKeys,
|
||||
private bool $mustAccept
|
||||
){}
|
||||
|
||||
public function getPlayerInfo() : PlayerInfo{
|
||||
return $this->playerInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a resource pack to the top of the stack.
|
||||
* The resources in this pack will be applied over the top of any existing packs.
|
||||
*/
|
||||
public function addResourcePack(ResourcePack $entry, ?string $encryptionKey = null) : void{
|
||||
array_unshift($this->resourcePacks, $entry);
|
||||
if($encryptionKey !== null){
|
||||
$this->encryptionKeys[$entry->getPackId()] = $encryptionKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource packs to offer. Packs are applied from the highest key to the lowest, with each pack
|
||||
* overwriting any resources from the previous pack. This means that the pack at index 0 gets the final say on which
|
||||
* resources are used.
|
||||
*
|
||||
* @param ResourcePack[] $resourcePacks
|
||||
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
|
||||
*
|
||||
* @phpstan-param list<ResourcePack> $resourcePacks
|
||||
* @phpstan-param array<string, string> $encryptionKeys
|
||||
*/
|
||||
public function setResourcePacks(array $resourcePacks, array $encryptionKeys) : void{
|
||||
$this->resourcePacks = $resourcePacks;
|
||||
$this->encryptionKeys = $encryptionKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResourcePack[]
|
||||
* @phpstan-return list<ResourcePack>
|
||||
*/
|
||||
public function getResourcePacks() : array{
|
||||
return $this->resourcePacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return array<string, string>
|
||||
*/
|
||||
public function getEncryptionKeys() : array{
|
||||
return $this->encryptionKeys;
|
||||
}
|
||||
|
||||
public function setMustAccept(bool $mustAccept) : void{
|
||||
$this->mustAccept = $mustAccept;
|
||||
}
|
||||
|
||||
public function mustAccept() : bool{
|
||||
return $this->mustAccept;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -323,8 +323,9 @@ final class ItemTypeIds{
|
||||
public const EYE_ARMOR_TRIM_SMITHING_TEMPLATE = 20284;
|
||||
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285;
|
||||
public const PITCHER_POD = 20286;
|
||||
public const NAME_TAG = 20287;
|
||||
|
||||
public const FIRST_UNUSED_ITEM_ID = 20287;
|
||||
public const FIRST_UNUSED_ITEM_ID = 20288;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;
|
||||
|
||||
|
40
src/item/NameTag.php
Normal file
40
src/item/NameTag.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?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\item;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class NameTag extends Item{
|
||||
|
||||
public function onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool{
|
||||
if($entity->canBeRenamed() && $this->hasCustomName()){
|
||||
$entity->setNameTag($this->getCustomName());
|
||||
$this->pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1384,6 +1384,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("mutton_raw", fn() => Items::RAW_MUTTON());
|
||||
$result->register("muttoncooked", fn() => Items::COOKED_MUTTON());
|
||||
$result->register("muttonraw", fn() => Items::RAW_MUTTON());
|
||||
$result->register("name_tag", fn() => Items::NAME_TAG());
|
||||
$result->register("nautilus_shell", fn() => Items::NAUTILUS_SHELL());
|
||||
$result->register("nether_brick", fn() => Items::NETHER_BRICK());
|
||||
$result->register("nether_quartz", fn() => Items::NETHER_QUARTZ());
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
@ -219,6 +219,7 @@ use function strtolower;
|
||||
* @method static MilkBucket MILK_BUCKET()
|
||||
* @method static Minecart MINECART()
|
||||
* @method static MushroomStew MUSHROOM_STEW()
|
||||
* @method static NameTag NAME_TAG()
|
||||
* @method static Item NAUTILUS_SHELL()
|
||||
* @method static Axe NETHERITE_AXE()
|
||||
* @method static Armor NETHERITE_BOOTS()
|
||||
@ -490,6 +491,7 @@ final class VanillaItems{
|
||||
self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket"));
|
||||
self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart"));
|
||||
self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew"));
|
||||
self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag"));
|
||||
self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell"));
|
||||
self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick"));
|
||||
self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz"));
|
||||
|
@ -28,7 +28,6 @@ use pocketmine\network\mcpe\compression\Compressor;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\LevelChunkPacket;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\types\ChunkPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\serializer\ChunkSerializer;
|
||||
@ -72,11 +71,10 @@ class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk, $dimensionId);
|
||||
$converter = TypeConverter::getInstance();
|
||||
$encoderContext = new PacketSerializerContext($converter->getItemTypeDictionary());
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $encoderContext, $this->tiles);
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $this->tiles);
|
||||
|
||||
$stream = new BinaryStream();
|
||||
PacketBatch::encodePackets($stream, $encoderContext, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]);
|
||||
PacketBatch::encodePackets($stream, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]);
|
||||
|
||||
$compressor = $this->compressor->deserialize();
|
||||
$this->setResult(chr($compressor->getNetworkId()) . $compressor->compress($stream->getBuffer()));
|
||||
|
@ -423,6 +423,41 @@ class InventoryManager{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares itemstack extra data for equality. This is used to verify legacy InventoryTransaction slot predictions.
|
||||
*
|
||||
* TODO: It would be preferable if we didn't have to deserialize this, to improve performance and reduce attack
|
||||
* surface. However, the raw data may not match due to differences in ordering. Investigate whether the
|
||||
* client-provided NBT is consistently sorted.
|
||||
*/
|
||||
private function itemStackExtraDataEqual(ItemStack $left, ItemStack $right) : bool{
|
||||
if($left->getRawExtraData() === $right->getRawExtraData()){
|
||||
return true;
|
||||
}
|
||||
|
||||
$typeConverter = $this->session->getTypeConverter();
|
||||
$leftExtraData = $typeConverter->deserializeItemStackExtraData($left->getRawExtraData(), $left->getId());
|
||||
$rightExtraData = $typeConverter->deserializeItemStackExtraData($right->getRawExtraData(), $right->getId());
|
||||
|
||||
$leftNbt = $leftExtraData->getNbt();
|
||||
$rightNbt = $rightExtraData->getNbt();
|
||||
return
|
||||
$leftExtraData->getCanPlaceOn() === $rightExtraData->getCanPlaceOn() &&
|
||||
$leftExtraData->getCanDestroy() === $rightExtraData->getCanDestroy() && (
|
||||
$leftNbt === $rightNbt || //this covers null === null and fast object identity
|
||||
($leftNbt !== null && $rightNbt !== null && $leftNbt->equals($rightNbt))
|
||||
);
|
||||
}
|
||||
|
||||
private function itemStacksEqual(ItemStack $left, ItemStack $right) : bool{
|
||||
return
|
||||
$left->getId() === $right->getId() &&
|
||||
$left->getMeta() === $right->getMeta() &&
|
||||
$left->getBlockRuntimeId() === $right->getBlockRuntimeId() &&
|
||||
$left->getCount() === $right->getCount() &&
|
||||
$this->itemStackExtraDataEqual($left, $right);
|
||||
}
|
||||
|
||||
public function onSlotChange(Inventory $inventory, int $slot) : void{
|
||||
$inventoryEntry = $this->inventories[spl_object_id($inventory)] ?? null;
|
||||
if($inventoryEntry === null){
|
||||
@ -432,7 +467,7 @@ class InventoryManager{
|
||||
}
|
||||
$currentItem = $this->session->getTypeConverter()->coreItemStackToNet($inventory->getItem($slot));
|
||||
$clientSideItem = $inventoryEntry->predictions[$slot] ?? null;
|
||||
if($clientSideItem === null || !$clientSideItem->equals($currentItem)){
|
||||
if($clientSideItem === null || !$this->itemStacksEqual($currentItem, $clientSideItem)){
|
||||
//no prediction or incorrect - do not associate this with the currently active itemstack request
|
||||
$this->trackItemStack($inventoryEntry, $slot, $currentItem, null);
|
||||
$inventoryEntry->pendingSyncs[$slot] = $currentItem;
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\event\player\PlayerDuplicateLoginEvent;
|
||||
use pocketmine\event\player\PlayerResourcePackOfferEvent;
|
||||
use pocketmine\event\server\DataPacketDecodeEvent;
|
||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||
use pocketmine\event\server\DataPacketSendEvent;
|
||||
@ -67,7 +68,6 @@ use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\ServerboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetDifficultyPacket;
|
||||
@ -101,6 +101,8 @@ use pocketmine\player\Player;
|
||||
use pocketmine\player\PlayerInfo;
|
||||
use pocketmine\player\UsedChunkStatus;
|
||||
use pocketmine\player\XboxLivePlayerInfo;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
@ -159,15 +161,24 @@ class NetworkSession{
|
||||
|
||||
/** @var string[] */
|
||||
private array $sendBuffer = [];
|
||||
|
||||
/**
|
||||
* @var \SplQueue|CompressBatchPromise[]|string[]
|
||||
* @phpstan-var \SplQueue<CompressBatchPromise|string>
|
||||
* @var PromiseResolver[]
|
||||
* @phpstan-var list<PromiseResolver<true>>
|
||||
*/
|
||||
private array $sendBufferAckPromises = [];
|
||||
|
||||
/** @phpstan-var \SplQueue<array{CompressBatchPromise|string, list<PromiseResolver<true>>}> */
|
||||
private \SplQueue $compressedQueue;
|
||||
private bool $forceAsyncCompression = true;
|
||||
private bool $enableCompression = false; //disabled until handshake completed
|
||||
|
||||
private int $nextAckReceiptId = 0;
|
||||
/**
|
||||
* @var PromiseResolver[][]
|
||||
* @phpstan-var array<int, list<PromiseResolver<true>>>
|
||||
*/
|
||||
private array $ackPromisesByReceiptId = [];
|
||||
|
||||
private ?InventoryManager $invManager = null;
|
||||
|
||||
/**
|
||||
@ -180,7 +191,6 @@ class NetworkSession{
|
||||
private Server $server,
|
||||
private NetworkSessionManager $manager,
|
||||
private PacketPool $packetPool,
|
||||
private PacketSerializerContext $packetSerializerContext,
|
||||
private PacketSender $sender,
|
||||
private PacketBroadcaster $broadcaster,
|
||||
private EntityEventBroadcaster $entityEventBroadcaster,
|
||||
@ -362,12 +372,12 @@ class NetworkSession{
|
||||
}
|
||||
|
||||
if($this->enableCompression){
|
||||
Timings::$playerNetworkReceiveDecompress->startTiming();
|
||||
$compressionType = ord($payload[0]);
|
||||
$compressed = substr($payload, 1);
|
||||
if($compressionType === CompressionAlgorithm::NONE){
|
||||
$decompressed = $compressed;
|
||||
}elseif($compressionType === $this->compressor->getNetworkId()){
|
||||
Timings::$playerNetworkReceiveDecompress->startTiming();
|
||||
try{
|
||||
$decompressed = $this->compressor->decompress($compressed);
|
||||
}catch(DecompressionException $e){
|
||||
@ -385,12 +395,8 @@ class NetworkSession{
|
||||
|
||||
try{
|
||||
$stream = new BinaryStream($decompressed);
|
||||
$count = 0;
|
||||
foreach(PacketBatch::decodeRaw($stream) as $buffer){
|
||||
$this->gamePacketLimiter->decrement();
|
||||
if(++$count > 100){
|
||||
throw new PacketHandlingException("Too many packets in batch");
|
||||
}
|
||||
$packet = $this->packetPool->getPacket($buffer);
|
||||
if($packet === null){
|
||||
$this->logger->debug("Unknown packet: " . base64_encode($buffer));
|
||||
@ -435,7 +441,7 @@ class NetworkSession{
|
||||
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
|
||||
$decodeTimings->startTiming();
|
||||
try{
|
||||
$stream = PacketSerializer::decoder($buffer, 0, $this->packetSerializerContext);
|
||||
$stream = PacketSerializer::decoder($buffer, 0);
|
||||
try{
|
||||
$packet->decode($stream);
|
||||
}catch(PacketDecodeException $e){
|
||||
@ -470,7 +476,23 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{
|
||||
public function handleAckReceipt(int $receiptId) : void{
|
||||
if(!$this->connected){
|
||||
return;
|
||||
}
|
||||
if(isset($this->ackPromisesByReceiptId[$receiptId])){
|
||||
$promises = $this->ackPromisesByReceiptId[$receiptId];
|
||||
unset($this->ackPromisesByReceiptId[$receiptId]);
|
||||
foreach($promises as $promise){
|
||||
$promise->resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param PromiseResolver<true>|null $ackReceiptResolver
|
||||
*/
|
||||
private function sendDataPacketInternal(ClientboundPacket $packet, bool $immediate, ?PromiseResolver $ackReceiptResolver) : bool{
|
||||
if(!$this->connected){
|
||||
return false;
|
||||
}
|
||||
@ -493,8 +515,11 @@ class NetworkSession{
|
||||
$packets = [$packet];
|
||||
}
|
||||
|
||||
if($ackReceiptResolver !== null){
|
||||
$this->sendBufferAckPromises[] = $ackReceiptResolver;
|
||||
}
|
||||
foreach($packets as $evPacket){
|
||||
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket));
|
||||
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder(), $evPacket));
|
||||
}
|
||||
if($immediate){
|
||||
$this->flushSendBuffer(true);
|
||||
@ -506,6 +531,23 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{
|
||||
return $this->sendDataPacketInternal($packet, $immediate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<true>
|
||||
*/
|
||||
public function sendDataPacketWithReceipt(ClientboundPacket $packet, bool $immediate = false) : Promise{
|
||||
$resolver = new PromiseResolver();
|
||||
|
||||
if(!$this->sendDataPacketInternal($packet, $immediate, $resolver)){
|
||||
$resolver->reject();
|
||||
}
|
||||
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -547,15 +589,15 @@ class NetworkSession{
|
||||
$batch = $stream->getBuffer();
|
||||
}
|
||||
$this->sendBuffer = [];
|
||||
$this->queueCompressedNoBufferFlush($batch, $immediate);
|
||||
$ackPromises = $this->sendBufferAckPromises;
|
||||
$this->sendBufferAckPromises = [];
|
||||
$this->queueCompressedNoBufferFlush($batch, $immediate, $ackPromises);
|
||||
}finally{
|
||||
Timings::$playerNetworkSend->stopTiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getPacketSerializerContext() : PacketSerializerContext{ return $this->packetSerializerContext; }
|
||||
|
||||
public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; }
|
||||
|
||||
public function getEntityEventBroadcaster() : EntityEventBroadcaster{ return $this->entityEventBroadcaster; }
|
||||
@ -576,22 +618,27 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false) : void{
|
||||
/**
|
||||
* @param PromiseResolver[] $ackPromises
|
||||
*
|
||||
* @phpstan-param list<PromiseResolver<true>> $ackPromises
|
||||
*/
|
||||
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false, array $ackPromises = []) : void{
|
||||
Timings::$playerNetworkSend->startTiming();
|
||||
try{
|
||||
if(is_string($batch)){
|
||||
if($immediate){
|
||||
//Skips all queues
|
||||
$this->sendEncoded($batch, true);
|
||||
$this->sendEncoded($batch, true, $ackPromises);
|
||||
}else{
|
||||
$this->compressedQueue->enqueue($batch);
|
||||
$this->compressedQueue->enqueue([$batch, $ackPromises]);
|
||||
$this->flushCompressedQueue();
|
||||
}
|
||||
}elseif($immediate){
|
||||
//Skips all queues
|
||||
$this->sendEncoded($batch->getResult(), true);
|
||||
$this->sendEncoded($batch->getResult(), true, $ackPromises);
|
||||
}else{
|
||||
$this->compressedQueue->enqueue($batch);
|
||||
$this->compressedQueue->enqueue([$batch, $ackPromises]);
|
||||
$batch->onResolve(function() : void{
|
||||
if($this->connected){
|
||||
$this->flushCompressedQueue();
|
||||
@ -608,14 +655,14 @@ class NetworkSession{
|
||||
try{
|
||||
while(!$this->compressedQueue->isEmpty()){
|
||||
/** @var CompressBatchPromise|string $current */
|
||||
$current = $this->compressedQueue->bottom();
|
||||
[$current, $ackPromises] = $this->compressedQueue->bottom();
|
||||
if(is_string($current)){
|
||||
$this->compressedQueue->dequeue();
|
||||
$this->sendEncoded($current);
|
||||
$this->sendEncoded($current, false, $ackPromises);
|
||||
|
||||
}elseif($current->hasResult()){
|
||||
$this->compressedQueue->dequeue();
|
||||
$this->sendEncoded($current->getResult());
|
||||
$this->sendEncoded($current->getResult(), false, $ackPromises);
|
||||
|
||||
}else{
|
||||
//can't send any more queued until this one is ready
|
||||
@ -627,13 +674,24 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
private function sendEncoded(string $payload, bool $immediate = false) : void{
|
||||
/**
|
||||
* @param PromiseResolver[] $ackPromises
|
||||
* @phpstan-param list<PromiseResolver<true>> $ackPromises
|
||||
*/
|
||||
private function sendEncoded(string $payload, bool $immediate, array $ackPromises) : void{
|
||||
if($this->cipher !== null){
|
||||
Timings::$playerNetworkSendEncrypt->startTiming();
|
||||
$payload = $this->cipher->encrypt($payload);
|
||||
Timings::$playerNetworkSendEncrypt->stopTiming();
|
||||
}
|
||||
$this->sender->send($payload, $immediate);
|
||||
|
||||
if(count($ackPromises) > 0){
|
||||
$ackReceiptId = $this->nextAckReceiptId++;
|
||||
$this->ackPromisesByReceiptId[$ackReceiptId] = $ackPromises;
|
||||
}else{
|
||||
$ackReceiptId = null;
|
||||
}
|
||||
$this->sender->send($payload, $immediate, $ackReceiptId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -653,6 +711,19 @@ class NetworkSession{
|
||||
$this->setHandler(null);
|
||||
$this->connected = false;
|
||||
|
||||
$ackPromisesByReceiptId = $this->ackPromisesByReceiptId;
|
||||
$this->ackPromisesByReceiptId = [];
|
||||
foreach($ackPromisesByReceiptId as $resolvers){
|
||||
foreach($resolvers as $resolver){
|
||||
$resolver->reject();
|
||||
}
|
||||
}
|
||||
$sendBufferAckPromises = $this->sendBufferAckPromises;
|
||||
$this->sendBufferAckPromises = [];
|
||||
foreach($sendBufferAckPromises as $resolver){
|
||||
$resolver->reject();
|
||||
}
|
||||
|
||||
$this->logger->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_network_session_close($reason)));
|
||||
}
|
||||
}
|
||||
@ -848,7 +919,19 @@ class NetworkSession{
|
||||
$this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_SUCCESS));
|
||||
|
||||
$this->logger->debug("Initiating resource packs phase");
|
||||
$this->setHandler(new ResourcePacksPacketHandler($this, $this->server->getResourcePackManager(), function() : void{
|
||||
|
||||
$packManager = $this->server->getResourcePackManager();
|
||||
$resourcePacks = $packManager->getResourceStack();
|
||||
$keys = [];
|
||||
foreach($resourcePacks as $resourcePack){
|
||||
$key = $packManager->getPackEncryptionKey($resourcePack->getPackId());
|
||||
if($key !== null){
|
||||
$keys[$resourcePack->getPackId()] = $key;
|
||||
}
|
||||
}
|
||||
$event = new PlayerResourcePackOfferEvent($this->info, $resourcePacks, $keys, $packManager->resourcePacksRequired());
|
||||
$event->call();
|
||||
$this->setHandler(new ResourcePacksPacketHandler($this, $event->getResourcePacks(), $event->getEncryptionKeys(), $event->mustAccept(), function() : void{
|
||||
$this->createPlayer();
|
||||
}));
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ interface PacketSender{
|
||||
/**
|
||||
* Pushes a packet into the channel to be processed.
|
||||
*/
|
||||
public function send(string $payload, bool $immediate) : void;
|
||||
public function send(string $payload, bool $immediate, ?int $receiptId) : void;
|
||||
|
||||
/**
|
||||
* Closes the channel, terminating the connection.
|
||||
|
@ -87,12 +87,13 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
|
||||
EffectIdMap::getInstance()->toId($effect->getType()),
|
||||
$effect->getAmplifier(),
|
||||
$effect->isVisible(),
|
||||
$effect->getDuration()
|
||||
$effect->getDuration(),
|
||||
tick: 0
|
||||
));
|
||||
}
|
||||
|
||||
public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void{
|
||||
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType())));
|
||||
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType()), tick: 0));
|
||||
}
|
||||
|
||||
public function onEntityRemoved(array $recipients, Entity $entity) : void{
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\network\mcpe;
|
||||
use pocketmine\event\server\DataPacketSendEvent;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
@ -37,8 +36,7 @@ use function strlen;
|
||||
|
||||
final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
public function __construct(
|
||||
private Server $server,
|
||||
private PacketSerializerContext $protocolContext
|
||||
private Server $server
|
||||
){}
|
||||
|
||||
public function broadcastPackets(array $recipients, array $packets) : void{
|
||||
@ -58,10 +56,6 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
/** @var NetworkSession[][] $targetsByCompressor */
|
||||
$targetsByCompressor = [];
|
||||
foreach($recipients as $recipient){
|
||||
if($recipient->getPacketSerializerContext() !== $this->protocolContext){
|
||||
throw new \InvalidArgumentException("Only recipients with the same protocol context as the broadcaster can be broadcast to by this broadcaster");
|
||||
}
|
||||
|
||||
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
|
||||
$compressor = $recipient->getCompressor();
|
||||
$compressors[spl_object_id($compressor)] = $compressor;
|
||||
@ -72,7 +66,7 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
$totalLength = 0;
|
||||
$packetBuffers = [];
|
||||
foreach($packets as $packet){
|
||||
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($this->protocolContext), $packet);
|
||||
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder(), $packet);
|
||||
//varint length prefix + packet buffer
|
||||
$totalLength += (((int) log(strlen($buffer), 128)) + 1) + strlen($buffer);
|
||||
$packetBuffers[] = $buffer;
|
||||
|
@ -39,16 +39,6 @@ use function time;
|
||||
class ProcessLoginTask extends AsyncTask{
|
||||
private const TLS_KEY_ON_COMPLETION = "completion";
|
||||
|
||||
/**
|
||||
* Old Mojang root auth key. This was used since the introduction of Xbox Live authentication in 0.15.0.
|
||||
* This key is expected to be replaced by the key below in the future, but this has not yet happened as of
|
||||
* 2023-07-01.
|
||||
* Ideally we would place a time expiry on this key, but since Mojang have not given a hard date for the key change,
|
||||
* and one bad guess has already caused a major outage, we can't do this.
|
||||
* TODO: This needs to be removed as soon as the new key is deployed by Mojang's authentication servers.
|
||||
*/
|
||||
public const MOJANG_OLD_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V";
|
||||
|
||||
/**
|
||||
* New Mojang root auth key. Mojang notified third-party developers of this change prior to the release of 1.20.0.
|
||||
* Expectations were that this would be used starting a "couple of weeks" after the release, but as of 2023-07-01,
|
||||
@ -128,7 +118,6 @@ class ProcessLoginTask extends AsyncTask{
|
||||
try{
|
||||
[$headersArray, $claimsArray, ] = JwtUtils::parse($jwt);
|
||||
}catch(JwtException $e){
|
||||
//TODO: we shouldn't be showing internal information like this to the client
|
||||
throw new VerifyLoginException("Failed to parse JWT: " . $e->getMessage(), null, 0, $e);
|
||||
}
|
||||
|
||||
@ -142,13 +131,11 @@ class ProcessLoginTask extends AsyncTask{
|
||||
/** @var JwtHeader $headers */
|
||||
$headers = $mapper->map($headersArray, new JwtHeader());
|
||||
}catch(\JsonMapper_Exception $e){
|
||||
//TODO: we shouldn't be showing internal information like this to the client
|
||||
throw new VerifyLoginException("Invalid JWT header: " . $e->getMessage(), null, 0, $e);
|
||||
}
|
||||
|
||||
$headerDerKey = base64_decode($headers->x5u, true);
|
||||
if($headerDerKey === false){
|
||||
//TODO: we shouldn't be showing internal information like this to the client
|
||||
throw new VerifyLoginException("Invalid JWT public key: base64 decoding error decoding x5u");
|
||||
}
|
||||
|
||||
@ -164,7 +151,6 @@ class ProcessLoginTask extends AsyncTask{
|
||||
try{
|
||||
$signingKeyOpenSSL = JwtUtils::parseDerPublicKey($headerDerKey);
|
||||
}catch(JwtException $e){
|
||||
//TODO: we shouldn't be showing this internal information to the client
|
||||
throw new VerifyLoginException("Invalid JWT public key: " . $e->getMessage(), null, 0, $e);
|
||||
}
|
||||
try{
|
||||
@ -175,7 +161,7 @@ class ProcessLoginTask extends AsyncTask{
|
||||
throw new VerifyLoginException($e->getMessage(), null, 0, $e);
|
||||
}
|
||||
|
||||
if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY || $headers->x5u === self::MOJANG_OLD_ROOT_PUBLIC_KEY){
|
||||
if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY){
|
||||
$this->authenticated = true; //we're signed into xbox live
|
||||
}
|
||||
|
||||
|
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
|
||||
|
@ -38,7 +38,7 @@ final class ZlibCompressor implements Compressor{
|
||||
|
||||
public const DEFAULT_LEVEL = 7;
|
||||
public const DEFAULT_THRESHOLD = 256;
|
||||
public const DEFAULT_MAX_DECOMPRESSION_SIZE = 2 * 1024 * 1024;
|
||||
public const DEFAULT_MAX_DECOMPRESSION_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* @see SingletonTrait::make()
|
||||
|
@ -30,13 +30,17 @@ use pocketmine\crafting\RecipeIngredient;
|
||||
use pocketmine\crafting\TagWildcardRecipeIngredient;
|
||||
use pocketmine\data\bedrock\BedrockDataFiles;
|
||||
use pocketmine\data\bedrock\item\BlockItemIdMap;
|
||||
use pocketmine\data\bedrock\item\ItemTypeNames;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\types\GameMode as ProtocolGameMode;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient as ProtocolRecipeIngredient;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\StringIdMetaItemDescriptor;
|
||||
@ -76,7 +80,7 @@ class TypeConverter{
|
||||
);
|
||||
|
||||
$this->itemTypeDictionary = ItemTypeDictionaryFromDataHelper::loadFromString(Filesystem::fileGetContents(BedrockDataFiles::REQUIRED_ITEM_LIST_JSON));
|
||||
$this->shieldRuntimeId = $this->itemTypeDictionary->fromStringId("minecraft:shield");
|
||||
$this->shieldRuntimeId = $this->itemTypeDictionary->fromStringId(ItemTypeNames::SHIELD);
|
||||
|
||||
$this->itemTranslator = new ItemTranslator(
|
||||
$this->itemTypeDictionary,
|
||||
@ -217,26 +221,36 @@ class TypeConverter{
|
||||
[$id, $meta, $blockRuntimeId] = $idMeta;
|
||||
}
|
||||
|
||||
$extraData = $id === $this->shieldRuntimeId ?
|
||||
new ItemStackExtraDataShield($nbt, canPlaceOn: [], canDestroy: [], blockingTick: 0) :
|
||||
new ItemStackExtraData($nbt, canPlaceOn: [], canDestroy: []);
|
||||
$extraDataSerializer = PacketSerializer::encoder();
|
||||
$extraData->write($extraDataSerializer);
|
||||
|
||||
return new ItemStack(
|
||||
$id,
|
||||
$meta,
|
||||
$itemStack->getCount(),
|
||||
$blockRuntimeId ?? ItemTranslator::NO_BLOCK_RUNTIME_ID,
|
||||
$nbt,
|
||||
[],
|
||||
[],
|
||||
$id === $this->shieldRuntimeId ? 0 : null
|
||||
$extraDataSerializer->getBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: Avoid this in server-side code. If you need to compare ItemStacks provided by the client to the
|
||||
* server, prefer encoding the server's itemstack and comparing the serialized ItemStack, instead of converting
|
||||
* the client's ItemStack to a core Item.
|
||||
* This method will fully decode the item's extra data, which can be very costly if the item has a lot of NBT data.
|
||||
*
|
||||
* @throws TypeConversionException
|
||||
*/
|
||||
public function netItemStackToCore(ItemStack $itemStack) : Item{
|
||||
if($itemStack->getId() === 0){
|
||||
return VanillaItems::AIR();
|
||||
}
|
||||
$compound = $itemStack->getNbt();
|
||||
$extraData = $this->deserializeItemStackExtraData($itemStack->getRawExtraData(), $itemStack->getId());
|
||||
|
||||
$compound = $extraData->getNbt();
|
||||
|
||||
$itemResult = $this->itemTranslator->fromNetworkId($itemStack->getId(), $itemStack->getMeta(), $itemStack->getBlockRuntimeId());
|
||||
|
||||
@ -255,4 +269,11 @@ class TypeConverter{
|
||||
|
||||
return $itemResult;
|
||||
}
|
||||
|
||||
public function deserializeItemStackExtraData(string $extraData, int $id) : ItemStackExtraData{
|
||||
$extraDataDeserializer = PacketSerializer::decoder($extraData, 0);
|
||||
return $id === $this->shieldRuntimeId ?
|
||||
ItemStackExtraDataShield::read($extraDataDeserializer) :
|
||||
ItemStackExtraData::read($extraDataDeserializer);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe\handler;
|
||||
|
||||
use pocketmine\block\BaseSign;
|
||||
use pocketmine\block\ItemFrame;
|
||||
use pocketmine\block\Lectern;
|
||||
use pocketmine\block\tile\Sign;
|
||||
use pocketmine\block\utils\SignText;
|
||||
@ -60,7 +59,6 @@ use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\EmotePacket;
|
||||
use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\LabTablePacket;
|
||||
@ -444,9 +442,18 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return false;
|
||||
}
|
||||
$serverItemStack = $this->session->getTypeConverter()->coreItemStackToNet($sourceSlotItem);
|
||||
//because the client doesn't tell us the expected itemstack ID, we have to deep-compare our known
|
||||
//itemstack info with the one the client sent. This is costly, but we don't have any other option :(
|
||||
if(!$serverItemStack->equals($clientItemStack)){
|
||||
//Sadly we don't have itemstack IDs here, so we have to compare the basic item properties to ensure that we're
|
||||
//dropping the item the client expects (inventory might be out of sync with the client).
|
||||
if(
|
||||
$serverItemStack->getId() !== $clientItemStack->getId() ||
|
||||
$serverItemStack->getMeta() !== $clientItemStack->getMeta() ||
|
||||
$serverItemStack->getCount() !== $clientItemStack->getCount() ||
|
||||
$serverItemStack->getBlockRuntimeId() !== $clientItemStack->getBlockRuntimeId()
|
||||
//Raw extraData may not match because of TAG_Compound key ordering differences, and decoding it to compare
|
||||
//is costly. Assume that we're in sync if id+meta+count+runtimeId match.
|
||||
//NB: Make sure $clientItemStack isn't used to create the dropped item, as that would allow the client
|
||||
//to change the item NBT since we're not validating it.
|
||||
){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -799,15 +806,6 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{
|
||||
$blockPosition = $packet->blockPosition;
|
||||
$block = $this->player->getWorld()->getBlockAt($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ());
|
||||
if($block instanceof ItemFrame && $block->getFramedItem() !== null){
|
||||
return $this->player->attackBlock(new Vector3($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ()), $block->getFacing());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleBossEvent(BossEventPacket $packet) : bool{
|
||||
return false; //TODO
|
||||
}
|
||||
@ -989,11 +987,6 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleLecternUpdate(LecternUpdatePacket $packet) : bool{
|
||||
if($packet->dropBook){
|
||||
//Drop book is handled with an interact event on use item transaction
|
||||
return true;
|
||||
}
|
||||
|
||||
$pos = $packet->blockPosition;
|
||||
$chunkX = $pos->getX() >> Chunk::COORD_BIT_SIZE;
|
||||
$chunkZ = $pos->getZ() >> Chunk::COORD_BIT_SIZE;
|
||||
|
@ -37,12 +37,13 @@ use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
|
||||
use pocketmine\resourcepacks\ResourcePack;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function ceil;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
@ -50,37 +51,77 @@ use function substr;
|
||||
* packs to the client.
|
||||
*/
|
||||
class ResourcePacksPacketHandler extends PacketHandler{
|
||||
private const PACK_CHUNK_SIZE = 128 * 1024; //128KB
|
||||
private const PACK_CHUNK_SIZE = 256 * 1024; //256KB
|
||||
|
||||
/**
|
||||
* Larger values allow downloading more chunks at the same time, increasing download speed, but the client may choke
|
||||
* and cause the download speed to drop (due to ACKs taking too long to arrive).
|
||||
*/
|
||||
private const MAX_CONCURRENT_CHUNK_REQUESTS = 1;
|
||||
|
||||
/**
|
||||
* @var ResourcePack[]
|
||||
* @phpstan-var array<string, ResourcePack>
|
||||
*/
|
||||
private array $resourcePacksById = [];
|
||||
|
||||
/** @var bool[][] uuid => [chunk index => hasSent] */
|
||||
private array $downloadedChunks = [];
|
||||
|
||||
/** @phpstan-var \SplQueue<array{ResourcePack, int}> */
|
||||
private \SplQueue $requestQueue;
|
||||
|
||||
private int $activeRequests = 0;
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure() : void $completionCallback
|
||||
* @param ResourcePack[] $resourcePackStack
|
||||
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
|
||||
*
|
||||
* @phpstan-param list<ResourcePack> $resourcePackStack
|
||||
* @phpstan-param array<string, string> $encryptionKeys
|
||||
* @phpstan-param \Closure() : void $completionCallback
|
||||
*/
|
||||
public function __construct(
|
||||
private NetworkSession $session,
|
||||
private ResourcePackManager $resourcePackManager,
|
||||
private array $resourcePackStack,
|
||||
private array $encryptionKeys,
|
||||
private bool $mustAccept,
|
||||
private \Closure $completionCallback
|
||||
){}
|
||||
){
|
||||
$this->requestQueue = new \SplQueue();
|
||||
foreach($resourcePackStack as $pack){
|
||||
$this->resourcePacksById[$pack->getPackId()] = $pack;
|
||||
}
|
||||
}
|
||||
|
||||
private function getPackById(string $id) : ?ResourcePack{
|
||||
return $this->resourcePacksById[strtolower($id)] ?? null;
|
||||
}
|
||||
|
||||
public function setUp() : void{
|
||||
$resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{
|
||||
//TODO: more stuff
|
||||
$encryptionKey = $this->resourcePackManager->getPackEncryptionKey($pack->getPackId());
|
||||
|
||||
return new ResourcePackInfoEntry(
|
||||
$pack->getPackId(),
|
||||
$pack->getPackVersion(),
|
||||
$pack->getPackSize(),
|
||||
$encryptionKey ?? "",
|
||||
$this->encryptionKeys[$pack->getPackId()] ?? "",
|
||||
"",
|
||||
$pack->getPackId(),
|
||||
false
|
||||
);
|
||||
}, $this->resourcePackManager->getResourceStack());
|
||||
}, $this->resourcePackStack);
|
||||
//TODO: support forcing server packs
|
||||
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false, []));
|
||||
$this->session->sendDataPacket(ResourcePacksInfoPacket::create(
|
||||
resourcePackEntries: $resourcePackEntries,
|
||||
behaviorPackEntries: [],
|
||||
mustAccept: $this->mustAccept,
|
||||
hasAddons: false,
|
||||
hasScripts: false,
|
||||
forceServerPacks: false,
|
||||
cdnUrls: []
|
||||
));
|
||||
$this->session->getLogger()->debug("Waiting for client to accept resource packs");
|
||||
}
|
||||
|
||||
@ -104,11 +145,11 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
if($splitPos !== false){
|
||||
$uuid = substr($uuid, 0, $splitPos);
|
||||
}
|
||||
$pack = $this->resourcePackManager->getPackById($uuid);
|
||||
$pack = $this->getPackById($uuid);
|
||||
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
//Client requested a resource pack but we don't have it available on the server
|
||||
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
|
||||
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -128,7 +169,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
|
||||
$stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{
|
||||
return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks
|
||||
}, $this->resourcePackManager->getResourceStack());
|
||||
}, $this->resourcePackStack);
|
||||
|
||||
//we support chemistry blocks by default, the client should already have this installed
|
||||
$stack[] = new ResourcePackStackEntry("0fba4063-dba1-4281-9b89-ff9390653530", "1.0.0", "");
|
||||
@ -136,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:
|
||||
@ -151,9 +192,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||
$pack = $this->resourcePackManager->getPackById($packet->packId);
|
||||
$pack = $this->getPackById($packet->packId);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
|
||||
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -176,8 +217,37 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
$this->downloadedChunks[$packId][$packet->chunkIndex] = true;
|
||||
}
|
||||
|
||||
$this->session->sendDataPacket(ResourcePackChunkDataPacket::create($packId, $packet->chunkIndex, $offset, $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE)));
|
||||
$this->requestQueue->enqueue([$pack, $packet->chunkIndex]);
|
||||
$this->processChunkRequestQueue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processChunkRequestQueue() : void{
|
||||
if($this->activeRequests >= self::MAX_CONCURRENT_CHUNK_REQUESTS || $this->requestQueue->isEmpty()){
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @var ResourcePack $pack
|
||||
* @var int $chunkIndex
|
||||
*/
|
||||
[$pack, $chunkIndex] = $this->requestQueue->dequeue();
|
||||
|
||||
$packId = $pack->getPackId();
|
||||
$offset = $chunkIndex * self::PACK_CHUNK_SIZE;
|
||||
$chunkData = $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE);
|
||||
$this->activeRequests++;
|
||||
$this->session
|
||||
->sendDataPacketWithReceipt(ResourcePackChunkDataPacket::create($packId, $chunkIndex, $offset, $chunkData))
|
||||
->onCompletion(
|
||||
function() : void{
|
||||
$this->activeRequests--;
|
||||
$this->processChunkRequestQueue();
|
||||
},
|
||||
function() : void{
|
||||
//this may have been rejected because of a disconnection - this will do nothing in that case
|
||||
$this->disconnectWithError("Plugin interrupted sending of resource packs");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\PacketBroadcaster;
|
||||
use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\NetworkInterfaceStartException;
|
||||
use pocketmine\network\PacketHandlingException;
|
||||
@ -84,7 +83,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
|
||||
private PacketBroadcaster $packetBroadcaster;
|
||||
private EntityEventBroadcaster $entityEventBroadcaster;
|
||||
private PacketSerializerContext $packetSerializerContext;
|
||||
private TypeConverter $typeConverter;
|
||||
|
||||
public function __construct(
|
||||
@ -94,12 +92,10 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
bool $ipV6,
|
||||
PacketBroadcaster $packetBroadcaster,
|
||||
EntityEventBroadcaster $entityEventBroadcaster,
|
||||
PacketSerializerContext $packetSerializerContext,
|
||||
TypeConverter $typeConverter
|
||||
){
|
||||
$this->server = $server;
|
||||
$this->packetBroadcaster = $packetBroadcaster;
|
||||
$this->packetSerializerContext = $packetSerializerContext;
|
||||
$this->entityEventBroadcaster = $entityEventBroadcaster;
|
||||
$this->typeConverter = $typeConverter;
|
||||
|
||||
@ -192,7 +188,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$this->server,
|
||||
$this->network->getSessionManager(),
|
||||
PacketPool::getInstance(),
|
||||
$this->packetSerializerContext,
|
||||
new RakLibPacketSender($sessionId, $this),
|
||||
$this->packetBroadcaster,
|
||||
$this->entityEventBroadcaster,
|
||||
@ -257,7 +252,9 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
}
|
||||
|
||||
public function onPacketAck(int $sessionId, int $identifierACK) : void{
|
||||
|
||||
if(isset($this->sessions[$sessionId])){
|
||||
$this->sessions[$sessionId]->handleAckReceipt($identifierACK);
|
||||
}
|
||||
}
|
||||
|
||||
public function setName(string $name) : void{
|
||||
@ -294,12 +291,13 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$this->network->getBandwidthTracker()->add($bytesSentDiff, $bytesReceivedDiff);
|
||||
}
|
||||
|
||||
public function putPacket(int $sessionId, string $payload, bool $immediate = true) : void{
|
||||
public function putPacket(int $sessionId, string $payload, bool $immediate = true, ?int $receiptId = null) : void{
|
||||
if(isset($this->sessions[$sessionId])){
|
||||
$pk = new EncapsulatedPacket();
|
||||
$pk->buffer = self::MCPE_RAKNET_PACKET_ID . $payload;
|
||||
$pk->reliability = PacketReliability::RELIABLE_ORDERED;
|
||||
$pk->orderChannel = 0;
|
||||
$pk->identifierACK = $receiptId;
|
||||
|
||||
$this->interface->sendEncapsulated($sessionId, $pk, $immediate);
|
||||
}
|
||||
|
@ -33,9 +33,9 @@ class RakLibPacketSender implements PacketSender{
|
||||
private RakLibInterface $handler
|
||||
){}
|
||||
|
||||
public function send(string $payload, bool $immediate) : void{
|
||||
public function send(string $payload, bool $immediate, ?int $receiptId) : void{
|
||||
if(!$this->closed){
|
||||
$this->handler->putPacket($this->sessionId, $payload, $immediate);
|
||||
$this->handler->putPacket($this->sessionId, $payload, $immediate, $receiptId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,8 @@ class RakLibServer extends Thread{
|
||||
new SimpleProtocolAcceptor($this->protocolVersion),
|
||||
new UserToRakLibThreadMessageReceiver(new PthreadsChannelReader($this->mainToThreadBuffer)),
|
||||
new RakLibToUserThreadMessageSender(new SnoozeAwarePthreadsChannelWriter($this->threadToMainBuffer, $this->sleeperEntry->createNotifier())),
|
||||
new ExceptionTraceCleaner($this->mainPath)
|
||||
new ExceptionTraceCleaner($this->mainPath),
|
||||
recvMaxSplitParts: 512
|
||||
);
|
||||
$this->synchronized(function() : void{
|
||||
$this->ready = true;
|
||||
|
@ -30,7 +30,6 @@ use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\convert\BlockTranslator;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
@ -84,8 +83,8 @@ final class ChunkSerializer{
|
||||
/**
|
||||
* @phpstan-param DimensionIds::* $dimensionId
|
||||
*/
|
||||
public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{
|
||||
$stream = PacketSerializer::encoder($encoderContext);
|
||||
public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, ?string $tiles = null) : string{
|
||||
$stream = PacketSerializer::encoder();
|
||||
|
||||
$subChunkCount = self::getSubChunkCount($chunk, $dimensionId);
|
||||
$writtenCount = 0;
|
||||
|
@ -630,6 +630,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->displayName = $ev->getNewName();
|
||||
}
|
||||
|
||||
public function canBeRenamed() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player's locale, e.g. en_US.
|
||||
*/
|
||||
@ -1389,7 +1393,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
public function setMotion(Vector3 $motion) : bool{
|
||||
if(parent::setMotion($motion)){
|
||||
$this->broadcastMotion();
|
||||
$this->getNetworkSession()->sendDataPacket(SetActorMotionPacket::create($this->id, $motion));
|
||||
$this->getNetworkSession()->sendDataPacket(SetActorMotionPacket::create($this->id, $motion, tick: 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -650,6 +650,11 @@ class PluginManager{
|
||||
|
||||
$handlerName = Utils::getNiceClosureName($handler);
|
||||
|
||||
$reflect = new \ReflectionFunction($handler);
|
||||
if($reflect->isGenerator()){
|
||||
throw new PluginException("Generator function $handlerName cannot be used as an event handler");
|
||||
}
|
||||
|
||||
if(!$plugin->isEnabled()){
|
||||
throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled");
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\promise;
|
||||
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
@ -57,4 +58,53 @@ final class Promise{
|
||||
//rejected or just hasn't been resolved yet
|
||||
return $this->shared->state === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve only once all the Promises in
|
||||
* `$promises` have resolved. The resolution value of the returned promise
|
||||
* will be an array containing the resolution values of each Promises in
|
||||
* `$promises` indexed by the respective Promises' array keys.
|
||||
*
|
||||
* @param Promise[] $promises
|
||||
*
|
||||
* @phpstan-template TPromiseValue
|
||||
* @phpstan-template TKey of array-key
|
||||
* @phpstan-param non-empty-array<TKey, Promise<TPromiseValue>> $promises
|
||||
*
|
||||
* @phpstan-return Promise<array<TKey, TPromiseValue>>
|
||||
*/
|
||||
public static function all(array $promises) : Promise{
|
||||
if(count($promises) === 0){
|
||||
throw new \InvalidArgumentException("At least one promise must be provided");
|
||||
}
|
||||
/** @phpstan-var PromiseResolver<array<TKey, TPromiseValue>> $resolver */
|
||||
$resolver = new PromiseResolver();
|
||||
$values = [];
|
||||
$toResolve = count($promises);
|
||||
$continue = true;
|
||||
|
||||
foreach($promises as $key => $promise){
|
||||
$promise->onCompletion(
|
||||
function(mixed $value) use ($resolver, $key, $toResolve, &$values) : void{
|
||||
$values[$key] = $value;
|
||||
|
||||
if(count($values) === $toResolve){
|
||||
$resolver->resolve($values);
|
||||
}
|
||||
},
|
||||
function() use ($resolver, &$continue) : void{
|
||||
if($continue){
|
||||
$continue = false;
|
||||
$resolver->reject();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if(!$continue){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\scheduler;
|
||||
|
||||
use pmmp\thread\Runnable;
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pmmp\thread\ThreadSafe;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\thread\NonThreadSafeValue;
|
||||
use function array_key_exists;
|
||||
use function assert;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function is_null;
|
||||
@ -83,9 +81,7 @@ abstract class AsyncTask extends Runnable{
|
||||
$this->onRun();
|
||||
|
||||
$this->finished = true;
|
||||
$worker = NativeThread::getCurrentThread();
|
||||
assert($worker instanceof AsyncWorker);
|
||||
$worker->getNotifier()->wakeupSleeper();
|
||||
AsyncWorker::getNotifier()->wakeupSleeper();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +36,7 @@ class AsyncWorker extends Worker{
|
||||
/** @var mixed[] */
|
||||
private static array $store = [];
|
||||
|
||||
private const TLS_KEY_NOTIFIER = self::class . "::notifier";
|
||||
private static ?SleeperNotifier $notifier = null;
|
||||
|
||||
public function __construct(
|
||||
private ThreadSafeLogger $logger,
|
||||
@ -45,12 +45,11 @@ class AsyncWorker extends Worker{
|
||||
private SleeperHandlerEntry $sleeperEntry
|
||||
){}
|
||||
|
||||
public function getNotifier() : SleeperNotifier{
|
||||
$notifier = $this->getFromThreadStore(self::TLS_KEY_NOTIFIER);
|
||||
if(!$notifier instanceof SleeperNotifier){
|
||||
throw new AssumptionFailedError("SleeperNotifier not found in thread-local storage");
|
||||
public static function getNotifier() : SleeperNotifier{
|
||||
if(self::$notifier !== null){
|
||||
return self::$notifier;
|
||||
}
|
||||
return $notifier;
|
||||
throw new AssumptionFailedError("SleeperNotifier not found in thread-local storage");
|
||||
}
|
||||
|
||||
protected function onRun() : void{
|
||||
@ -66,7 +65,7 @@ class AsyncWorker extends Worker{
|
||||
$this->logger->debug("No memory limit set");
|
||||
}
|
||||
|
||||
$this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier());
|
||||
self::$notifier = $this->sleeperEntry->createNotifier();
|
||||
}
|
||||
|
||||
public function getLogger() : ThreadSafeLogger{
|
||||
@ -84,6 +83,8 @@ class AsyncWorker extends Worker{
|
||||
/**
|
||||
* Saves mixed data into the worker's thread-local object store. This can be used to store objects which you
|
||||
* want to use on this worker thread from multiple AsyncTasks.
|
||||
*
|
||||
* @deprecated Use static class properties instead.
|
||||
*/
|
||||
public function saveToThreadStore(string $identifier, mixed $value) : void{
|
||||
if(NativeThread::getCurrentThread() !== $this){
|
||||
@ -99,6 +100,8 @@ class AsyncWorker extends Worker{
|
||||
* account for the possibility that what you're trying to retrieve might not exist.
|
||||
*
|
||||
* Objects stored in this storage may ONLY be retrieved while the task is running.
|
||||
*
|
||||
* @deprecated Use static class properties instead.
|
||||
*/
|
||||
public function getFromThreadStore(string $identifier) : mixed{
|
||||
if(NativeThread::getCurrentThread() !== $this){
|
||||
@ -109,6 +112,8 @@ class AsyncWorker extends Worker{
|
||||
|
||||
/**
|
||||
* Removes previously-stored mixed data from the worker's thread-local object store.
|
||||
*
|
||||
* @deprecated Use static class properties instead.
|
||||
*/
|
||||
public function removeFromThreadStore(string $identifier) : void{
|
||||
if(NativeThread::getCurrentThread() !== $this){
|
||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
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;
|
||||
@ -77,9 +79,7 @@ trait CommonThreadPartsTrait{
|
||||
/**
|
||||
* Registers the class loaders for this thread.
|
||||
*
|
||||
* WARNING: This method MUST be called from any descendent threads' run() method to make autoloading usable.
|
||||
* If you do not do this, you will not be able to use new classes that were not loaded when the thread was started
|
||||
* (unless you are using a custom autoloader).
|
||||
* @internal
|
||||
*/
|
||||
public function registerClassLoaders() : void{
|
||||
if($this->composerAutoloaderPath !== null){
|
||||
@ -96,6 +96,15 @@ trait CommonThreadPartsTrait{
|
||||
|
||||
public function getCrashInfo() : ?ThreadCrashInfo{ return $this->crashInfo; }
|
||||
|
||||
public function start(int $options = NativeThread::INHERIT_NONE) : bool{
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if($this->getClassLoaders() === null){
|
||||
$this->setClassLoaders();
|
||||
}
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
final public function run() : void{
|
||||
error_reporting(-1);
|
||||
$this->registerClassLoaders();
|
||||
@ -110,6 +119,20 @@ trait CommonThreadPartsTrait{
|
||||
$this->isKilled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the thread using the best way possible. Try to stop it yourself before calling this.
|
||||
*/
|
||||
public function quit() : void{
|
||||
$this->isKilled = true;
|
||||
|
||||
if(!$this->isJoined()){
|
||||
$this->notify();
|
||||
$this->join();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by set_exception_handler() when an uncaught exception is thrown.
|
||||
*/
|
||||
@ -128,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{
|
||||
|
@ -37,28 +37,4 @@ use pocketmine\scheduler\AsyncTask;
|
||||
*/
|
||||
abstract class Thread extends NativeThread{
|
||||
use CommonThreadPartsTrait;
|
||||
|
||||
public function start(int $options = NativeThread::INHERIT_NONE) : bool{
|
||||
//this is intentionally not traitified
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if($this->getClassLoaders() === null){
|
||||
$this->setClassLoaders();
|
||||
}
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the thread using the best way possible. Try to stop it yourself before calling this.
|
||||
*/
|
||||
public function quit() : void{
|
||||
$this->isKilled = true;
|
||||
|
||||
if(!$this->isJoined()){
|
||||
$this->notify();
|
||||
$this->join();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ class ThreadSafeClassLoader extends ThreadSafe{
|
||||
* @var ThreadSafeArray|string[]
|
||||
* @phpstan-var ThreadSafeArray<int, string>
|
||||
*/
|
||||
private $fallbackLookup;
|
||||
private ThreadSafeArray $fallbackLookup;
|
||||
/**
|
||||
* @var ThreadSafeArray|string[][]
|
||||
* @phpstan-var ThreadSafeArray<string, ThreadSafeArray<int, string>>
|
||||
*/
|
||||
private $psr4Lookup;
|
||||
private ThreadSafeArray $psr4Lookup;
|
||||
|
||||
public function __construct(){
|
||||
$this->fallbackLookup = new ThreadSafeArray();
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\thread;
|
||||
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pmmp\thread\Worker as NativeWorker;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
|
||||
@ -39,31 +38,4 @@ use pocketmine\scheduler\AsyncTask;
|
||||
*/
|
||||
abstract class Worker extends NativeWorker{
|
||||
use CommonThreadPartsTrait;
|
||||
|
||||
public function start(int $options = NativeThread::INHERIT_NONE) : bool{
|
||||
//this is intentionally not traitified
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if($this->getClassLoaders() === null){
|
||||
$this->setClassLoaders();
|
||||
}
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the thread using the best way possible. Try to stop it yourself before calling this.
|
||||
*/
|
||||
public function quit() : void{
|
||||
$this->isKilled = true;
|
||||
|
||||
if(!$this->isShutdown()){
|
||||
$this->synchronized(function() : void{
|
||||
while($this->unstack() !== null);
|
||||
});
|
||||
$this->notify();
|
||||
$this->shutdown();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
}
|
||||
}
|
||||
|
@ -210,8 +210,7 @@ abstract class Timings{
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template T of object
|
||||
* @phpstan-param class-string<T> $class
|
||||
* @phpstan-param class-string<covariant object> $class
|
||||
*/
|
||||
private static function shortenCoreClassName(string $class, string $prefix) : string{
|
||||
if(str_starts_with($class, $prefix)){
|
||||
@ -302,8 +301,7 @@ abstract class Timings{
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TEvent of Event
|
||||
* @phpstan-param class-string<TEvent> $event
|
||||
* @phpstan-param class-string<covariant Event> $event
|
||||
*/
|
||||
public static function getEventHandlerTimings(string $event, string $handlerName, string $group) : TimingsHandler{
|
||||
if(!isset(self::$eventHandlers[$event][$handlerName])){
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user