Compare commits

...

35 Commits

Author SHA1 Message Date
9b01fb3d89 Release 3.16.1 2020-12-07 17:16:10 +00:00
f28405fcfb phpstan 0.12.59 2020-12-07 17:12:55 +00:00
9c07c206f6 Updated DevTools submodule to pmmp/DevTools@1606a4307b 2020-12-05 20:02:14 +00:00
d0d701f232 Updated build/php submodule to pmmp/php-build-scripts@a42c7df20a 2020-12-05 19:42:41 +00:00
07cae8a129 Updated composer dependencies 2020-12-05 19:40:30 +00:00
6869ee1c2d Clean up nonsensical code in NetworkBinaryStream->getSlot() 2020-12-05 01:24:41 +00:00
26155acff2 register HellBiome (#3950) 2020-12-04 11:58:49 +00:00
b550cf5163 phpunit 9.4.4 2020-12-01 17:32:27 +00:00
48fa19fdcd PermissionAttachmentInfo::__construct() never throws an exception 2020-12-01 17:30:35 +00:00
bac986d0b2 Fixed crash when executing command /version with multiple authors declared in 'author' (#3940)
closes #3902
2020-12-01 15:47:55 +00:00
215bac8dd7 phpstan 0.12.58 2020-11-30 22:11:43 +00:00
3709ba172b Sync composer dependencies 2020-11-30 22:02:01 +00:00
ef034f2d68 PermissionAttachment: better document the apparently nonsensical code responsible for making sure the entire permission system doesn't burn down 2020-11-28 16:05:16 +00:00
ab18332572 PermissionManager: fixed nonsensical PHPDoc type 2020-11-27 19:54:05 +00:00
48595630fc [ci skip] update crash issue template 2020-11-27 17:38:14 +00:00
4102205ba6 Enhance type information in PlayerCreationEvent 2020-11-24 16:28:36 +00:00
9e85ee4a7a Fixed missing field on Persona skin encode 2020-11-21 18:01:56 +00:00
e8e6b9304c phpstan 0.12.57 2020-11-21 17:46:32 +00:00
23849b7f63 3.16.1 is next 2020-11-21 01:25:06 +00:00
d2f68836c6 Release 3.16.0 2020-11-21 01:25:06 +00:00
d19db5d2e4 fix phpstan warnings 2020-11-21 01:16:04 +00:00
98cdc80d37 Protocol changes for 1.16.100 2020-11-21 01:07:25 +00:00
8273f789ee Backport SingletonTrait to PM3 2020-11-20 21:00:47 +00:00
29eccba5f0 Updated composer dependencies 2020-11-17 23:25:15 +00:00
9984b15de6 fix build #3 2020-11-16 19:25:13 +00:00
6ea01e0dd4 fix build #2 2020-11-16 19:21:44 +00:00
46331df7db fix build 2020-11-16 19:06:56 +00:00
691c49fb32 I don't know how to pass custom arguments to these scripts :< 2020-11-16 19:05:04 +00:00
db815360d1 [ci skip] BUILDING.md: tell users to use 'composer make-server' instead of the server-phar script directly
this takes care of the dev dependencies automatically.
2020-11-16 19:03:39 +00:00
6e297168c2 travis.sh: use new composer commands 2020-11-16 19:01:07 +00:00
95dbb00d4c Added custom composer commands: 'composer make-devtools' and 'composer make-server' 2020-11-16 18:55:23 +00:00
50e29a5ed8 build/make-server: bail when composer dev dependencies are installed
these mess up the phar and make it extremely bloated. Almost everyone building for themselves unintentionally includes dev dependencies.
2020-11-16 18:54:07 +00:00
9f3fb935b5 Update composer dependencies 2020-11-12 22:03:32 +00:00
e30b1ee2c7 Clean up entity and tile saveID handling
we only ever need the first entry, so there's no point storing all of them. In addition, the field is private, which guarantees that nothing else needs the array either.
This also fixes phpstan/phpstan@c50650c5dd.
2020-11-12 21:49:12 +00:00
574b7f6343 3.15.5 is next 2020-11-10 16:48:18 +00:00
58 changed files with 1427 additions and 367 deletions

View File

@ -9,6 +9,7 @@ assignees: ''
<!--- submit crashdump files to https://crash.pmmp.io -->
<!--- or, copy the data between ===BEGIN CRASH DUMP=== and ===END CRASH DUMP and paste it on a site like https://pastebin.com -->
<!--- DON'T JUST PASTE the crashdump into an issue -->
Link to crashdump:
<!--- write additional information about the crash to help us find the problem -->

View File

@ -30,7 +30,7 @@ If you use a custom binary, you'll need to replace `composer` usages in this gui
Preprocessor requires that the `cpp` (c preprocessor) is available in your PATH.
## Building `PocketMine-MP.phar`
Run `build/server-phar.php` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
Run `composer make-server` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
You can also use the `--out` option to change the output filename.

View File

@ -41,6 +41,7 @@ use function rtrim;
use function sprintf;
use function str_replace;
use function unlink;
use const PHP_EOL;
require dirname(__DIR__) . '/vendor/autoload.php';
@ -129,6 +130,10 @@ function main() : void{
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
exit(1);
}
if(file_exists(dirname(__DIR__) . '/vendor/phpunit')){
echo "Remove Composer dev dependencies before building (composer install --no-dev)" . PHP_EOL;
exit(1);
}
$opts = getopt("", ["out:", "git:"]);
if(isset($opts["git"])){

24
changelogs/3.16.md Normal file
View File

@ -0,0 +1,24 @@
**For Minecraft: Bedrock Edition 1.16.100**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 3.16.0
- Added support for Minecraft: Bedrock Edition 1.16.100.
- Removed compatibility with earlier versions.
- Added new custom composer commands `make-server` and `make-devtools` to ease setting up a development environment and building the server.
## Known issues (please don't open issues for these)
- Walls don't connect to each other
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
- New blocks, items & mobs aren't implemented
- Nether doesn't exist
# 3.16.1
- Fixed incorrect encoding of skins in the protocol.
- `/version` no longer crashes when a plugin provides `string[]` for the `author` field in `plugin.yml`.
- `author` in `plugin.yml` now accepts arrays, just like `authors`.
- Fixed `HellBiome` never being registered.

View File

@ -38,7 +38,7 @@
"composer-runtime-api": "^2.0"
},
"require-dev": {
"phpstan/phpstan": "0.12.54",
"phpstan/phpstan": "0.12.59",
"phpstan/phpstan-phpunit": "^0.12.6",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpunit/phpunit": "^9.2"
@ -62,5 +62,12 @@
"platform": {
"php": "7.3.0"
}
},
"scripts": {
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/DevTools/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
"make-server": [
"@composer install --no-dev --classmap-authoritative",
"@php -dphar.readonly=0 build/server-phar.php"
]
}
}

121
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f7b6b54d43e00372b7dea787d863d8b9",
"content-hash": "2c44138f2052c259a201871d4d423947",
"packages": [
{
"name": "adhocore/json-comment",
@ -477,36 +477,31 @@
"packages-dev": [
{
"name": "doctrine/instantiator",
"version": "1.3.1",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
"reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
"reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
"reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"doctrine/coding-standard": "^8.0",
"ext-pdo": "*",
"ext-phar": "*",
"phpbench/phpbench": "^0.13",
"phpstan/phpstan-phpunit": "^0.11",
"phpstan/phpstan-shim": "^0.11",
"phpunit/phpunit": "^7.0"
"phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
@ -520,7 +515,7 @@
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
"homepage": "https://ocramius.github.io/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
@ -531,7 +526,7 @@
],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.3.x"
"source": "https://github.com/doctrine/instantiator/tree/1.4.0"
},
"funding": [
{
@ -547,20 +542,20 @@
"type": "tidelift"
}
],
"time": "2020-05-29T17:27:14+00:00"
"time": "2020-11-10T18:47:58+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.10.1",
"version": "1.10.2",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
"reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
"reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
"shasum": ""
},
"require": {
@ -597,7 +592,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.x"
"source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
},
"funding": [
{
@ -605,20 +600,20 @@
"type": "tidelift"
}
],
"time": "2020-06-29T13:22:24+00:00"
"time": "2020-11-13T09:40:50+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.10.2",
"version": "v4.10.3",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "658f1be311a230e0907f5dfe0213742aff0596de"
"reference": "dbe56d23de8fcb157bbc0cfb3ad7c7de0cfb0984"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/658f1be311a230e0907f5dfe0213742aff0596de",
"reference": "658f1be311a230e0907f5dfe0213742aff0596de",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dbe56d23de8fcb157bbc0cfb3ad7c7de0cfb0984",
"reference": "dbe56d23de8fcb157bbc0cfb3ad7c7de0cfb0984",
"shasum": ""
},
"require": {
@ -659,9 +654,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.10.2"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.10.3"
},
"time": "2020-09-26T10:30:38+00:00"
"time": "2020-12-03T17:45:45+00:00"
},
{
"name": "phar-io/manifest",
@ -725,16 +720,16 @@
},
{
"name": "phar-io/version",
"version": "3.0.2",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/phar-io/version.git",
"reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0"
"reference": "726c026815142e4f8677b7cb7f2249c9ffb7ecae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0",
"reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0",
"url": "https://api.github.com/repos/phar-io/version/zipball/726c026815142e4f8677b7cb7f2249c9ffb7ecae",
"reference": "726c026815142e4f8677b7cb7f2249c9ffb7ecae",
"shasum": ""
},
"require": {
@ -770,9 +765,9 @@
"description": "Library for handling version information and constraints",
"support": {
"issues": "https://github.com/phar-io/version/issues",
"source": "https://github.com/phar-io/version/tree/master"
"source": "https://github.com/phar-io/version/tree/3.0.3"
},
"time": "2020-06-27T14:39:04+00:00"
"time": "2020-11-30T09:21:21+00:00"
},
{
"name": "phpdocumentor/reflection-common",
@ -1001,16 +996,16 @@
},
{
"name": "phpstan/phpstan",
"version": "0.12.54",
"version": "0.12.59",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "45c7b999a4b7dd9ac5558bdaaf23dcebbef88223"
"reference": "cf4107257c8ca2ad967efdd6a00f12b21acbb779"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/45c7b999a4b7dd9ac5558bdaaf23dcebbef88223",
"reference": "45c7b999a4b7dd9ac5558bdaaf23dcebbef88223",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cf4107257c8ca2ad967efdd6a00f12b21acbb779",
"reference": "cf4107257c8ca2ad967efdd6a00f12b21acbb779",
"shasum": ""
},
"require": {
@ -1041,7 +1036,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/0.12.54"
"source": "https://github.com/phpstan/phpstan/tree/0.12.59"
},
"funding": [
{
@ -1057,7 +1052,7 @@
"type": "tidelift"
}
],
"time": "2020-11-05T13:36:26+00:00"
"time": "2020-12-07T14:46:03+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
@ -1176,16 +1171,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.3",
"version": "9.2.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41"
"reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6b20e2055f7c29b56cb3870b3de7cc463d7add41",
"reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1",
"reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1",
"shasum": ""
},
"require": {
@ -1199,7 +1194,7 @@
"sebastian/code-unit-reverse-lookup": "^2.0.2",
"sebastian/complexity": "^2.0",
"sebastian/environment": "^5.1.2",
"sebastian/lines-of-code": "^1.0",
"sebastian/lines-of-code": "^1.0.3",
"sebastian/version": "^3.0.1",
"theseer/tokenizer": "^1.2.0"
},
@ -1241,7 +1236,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.3"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5"
},
"funding": [
{
@ -1249,7 +1244,7 @@
"type": "github"
}
],
"time": "2020-10-30T10:46:41+00:00"
"time": "2020-11-28T06:44:49+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1494,16 +1489,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.4.2",
"version": "9.5.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa"
"reference": "8e16c225d57c3d6808014df6b1dd7598d0a5bbbe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa",
"reference": "3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e16c225d57c3d6808014df6b1dd7598d0a5bbbe",
"reference": "8e16c225d57c3d6808014df6b1dd7598d0a5bbbe",
"shasum": ""
},
"require": {
@ -1519,7 +1514,7 @@
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpspec/prophecy": "^1.12.1",
"phpunit/php-code-coverage": "^9.2",
"phpunit/php-code-coverage": "^9.2.3",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@ -1550,7 +1545,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.4-dev"
"dev-master": "9.5-dev"
}
},
"autoload": {
@ -1581,7 +1576,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.4.2"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.0"
},
"funding": [
{
@ -1593,7 +1588,7 @@
"type": "github"
}
],
"time": "2020-10-19T09:23:29+00:00"
"time": "2020-12-04T05:05:53+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2165,16 +2160,16 @@
},
{
"name": "sebastian/lines-of-code",
"version": "1.0.2",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "acf76492a65401babcf5283296fa510782783a7a"
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/acf76492a65401babcf5283296fa510782783a7a",
"reference": "acf76492a65401babcf5283296fa510782783a7a",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
"shasum": ""
},
"require": {
@ -2210,7 +2205,7 @@
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
"support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.2"
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
},
"funding": [
{
@ -2218,7 +2213,7 @@
"type": "github"
}
],
"time": "2020-10-26T17:03:56+00:00"
"time": "2020-11-28T06:42:11+00:00"
},
{
"name": "sebastian/object-enumerator",

View File

@ -100,6 +100,7 @@ use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
@ -148,6 +149,7 @@ use pocketmine\network\mcpe\protocol\types\CommandEnum;
use pocketmine\network\mcpe\protocol\types\CommandParameter;
use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
@ -1939,7 +1941,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$animation["ImageWidth"],
base64_decode($animation["Image"], true)),
$animation["Type"],
$animation["Frames"]
$animation["Frames"],
$animation["AnimationExpression"]
);
}
@ -2179,6 +2182,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//but it does have an annoying side-effect when true: it makes
//the client remove its own non-server-supplied resource packs.
$pk->mustAccept = false;
$pk->experiments = new Experiments([], false);
$this->dataPacket($pk);
break;
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
@ -2244,6 +2248,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->commandsEnabled = true;
$pk->levelId = "";
$pk->worldName = $this->server->getMotd();
$pk->experiments = new Experiments([], false);
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
$this->dataPacket($pk);
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
@ -2870,7 +2876,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE_REQUEST)){
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN)){
return true;
}
@ -2957,7 +2963,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case PlayerActionPacket::ACTION_STOP_SWIMMING:
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
break;
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //ignored (for now)
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //TODO: ignored (for now)
break;
case PlayerActionPacket::ACTION_CREATIVE_PLAYER_DESTROY_BLOCK:
//TODO: do we need to handle this?
break;
default:
$this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName());
@ -3039,6 +3048,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
/** @var int|null */
private $closingWindowId = null;
/** @internal */
public function getClosingWindowId() : ?int{ return $this->closingWindowId; }
public function handleContainerClose(ContainerClosePacket $packet) : bool{
if(!$this->spawned){
return true;
@ -3050,12 +3065,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
unset($this->openHardcodedWindows[$packet->windowId]);
$pk = new ContainerClosePacket();
$pk->windowId = $packet->windowId;
$pk->server = false;
$this->sendDataPacket($pk);
return true;
}
if(isset($this->windowIndex[$packet->windowId])){
$this->closingWindowId = $packet->windowId;
(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call();
$this->removeWindow($this->windowIndex[$packet->windowId]);
$this->closingWindowId = null;
//removeWindow handles sending the appropriate
return true;
}

View File

@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.15.4";
const BASE_VERSION = "3.16.1";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;

View File

@ -77,7 +77,6 @@ use function abs;
use function assert;
use function cos;
use function count;
use function current;
use function deg2rad;
use function floor;
use function fmod;
@ -334,8 +333,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*/
private static $knownEntities = [];
/**
* @var string[][]
* @phpstan-var array<class-string<Entity>, list<string>>
* @var string[]
* @phpstan-var array<class-string<Entity>, string>
*/
private static $saveNames = [];
@ -414,7 +413,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
self::$knownEntities[$name] = $className;
}
self::$saveNames[$className] = $saveNames;
self::$saveNames[$className] = reset($saveNames);
return true;
}
@ -872,8 +871,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if(!isset(self::$saveNames[static::class])){
throw new \InvalidStateException("Entity " . static::class . " is not registered");
}
reset(self::$saveNames[static::class]);
return current(self::$saveNames[static::class]);
return self::$saveNames[static::class];
}
public function saveNBT() : void{

View File

@ -39,14 +39,22 @@ class PlayerCreationEvent extends Event{
/** @var int */
private $port;
/** @var string */
/**
* @var string
* @phpstan-var class-string<Player>
*/
private $baseClass;
/** @var string */
/**
* @var string
* @phpstan-var class-string<Player>
*/
private $playerClass;
/**
* @param string $baseClass
* @param string $playerClass
* @phpstan-param class-string<Player> $baseClass
* @phpstan-param class-string<Player> $playerClass
*/
public function __construct(SourceInterface $interface, $baseClass, $playerClass, string $address, int $port){
$this->interface = $interface;
@ -80,6 +88,7 @@ class PlayerCreationEvent extends Event{
/**
* @return string
* @phpstan-return class-string<Player>
*/
public function getBaseClass(){
return $this->baseClass;
@ -87,6 +96,7 @@ class PlayerCreationEvent extends Event{
/**
* @param string $class
* @phpstan-param class-string<Player> $class
*
* @return void
*/
@ -100,6 +110,7 @@ class PlayerCreationEvent extends Event{
/**
* @return string
* @phpstan-return class-string<Player>
*/
public function getPlayerClass(){
return $this->playerClass;
@ -107,6 +118,7 @@ class PlayerCreationEvent extends Event{
/**
* @param string $class
* @phpstan-param class-string<Player> $class
*
* @return void
*/

View File

@ -64,6 +64,7 @@ abstract class ContainerInventory extends BaseInventory{
public function onClose(Player $who) : void{
$pk = new ContainerClosePacket();
$pk->windowId = $who->getWindowId($this);
$pk->server = $who->getClosingWindowId() !== $pk->windowId;
$who->dataPacket($pk);
parent::onClose($who);
}

View File

@ -163,6 +163,7 @@ class CraftingTransaction extends InventoryTransaction{
*/
$pk = new ContainerClosePacket();
$pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID;
$pk->server = true;
$this->source->dataPacket($pk);
}

View File

@ -99,6 +99,8 @@ abstract class Biome{
self::register(self::SWAMP, new SwampBiome());
self::register(self::RIVER, new RiverBiome());
self::register(self::HELL, new HellBiome());
self::register(self::ICE_PLAINS, new IcePlainsBiome());
self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome());

View File

@ -35,6 +35,9 @@ use pocketmine\math\Vector3;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\NamedTag;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
use pocketmine\network\mcpe\protocol\types\EntityLink;
use pocketmine\network\mcpe\protocol\types\GameRuleType;
@ -47,6 +50,7 @@ use pocketmine\network\mcpe\protocol\types\StructureEditorData;
use pocketmine\network\mcpe\protocol\types\StructureSettings;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\UUID;
use function assert;
use function count;
use function strlen;
@ -91,7 +95,8 @@ class NetworkBinaryStream extends BinaryStream{
$skinImage = $this->getSkinImage();
$animationType = $this->getLInt();
$animationFrames = $this->getLFloat();
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames);
$expressionType = $this->getLInt();
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
}
$capeData = $this->getSkinImage();
$geometryData = $this->getString();
@ -143,6 +148,7 @@ class NetworkBinaryStream extends BinaryStream{
$this->putSkinImage($animation->getImage());
$this->putLInt($animation->getType());
$this->putLFloat($animation->getFrames());
$this->putLInt($animation->getExpressionType());
}
$this->putSkinImage($skin->getCapeImage());
$this->putString($skin->getGeometryData());
@ -186,15 +192,17 @@ class NetworkBinaryStream extends BinaryStream{
}
public function getSlot() : Item{
$id = $this->getVarInt();
if($id === 0){
$netId = $this->getVarInt();
if($netId === 0){
return ItemFactory::get(0, 0, 0);
}
$auxValue = $this->getVarInt();
$data = $auxValue >> 8;
$netData = $auxValue >> 8;
$cnt = $auxValue & 0xff;
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkId($netId, $netData);
$nbtLen = $this->getLShort();
/** @var CompoundTag|null $nbt */
@ -223,26 +231,23 @@ class NetworkBinaryStream extends BinaryStream{
$this->getString();
}
if($id === ItemIds::SHIELD){
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
$this->getVarLong(); //"blocking tick" (ffs mojang)
}
if($nbt !== null){
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
$data = $nbt->getInt(self::DAMAGE_TAG);
$meta = $nbt->getInt(self::DAMAGE_TAG);
$nbt->removeTag(self::DAMAGE_TAG);
if($nbt->count() === 0){
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
$conflicted->setName(self::DAMAGE_TAG);
$nbt->setTag($conflicted);
}elseif($nbt->count() === 0){
$nbt = null;
goto end;
}
}
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
$conflicted->setName(self::DAMAGE_TAG);
$nbt->setTag($conflicted);
}
}
end:
return ItemFactory::get($id, $data, $cnt, $nbt);
return ItemFactory::get($id, $meta, $cnt, $nbt);
}
public function putSlot(Item $item) : void{
@ -252,8 +257,10 @@ class NetworkBinaryStream extends BinaryStream{
return;
}
$this->putVarInt($item->getId());
$auxValue = (($item->getDamage() & 0x7fff) << 8) | $item->getCount();
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
$this->putVarInt($netId);
$auxValue = (($netData & 0x7fff) << 8) | $item->getCount();
$this->putVarInt($auxValue);
$nbt = null;
@ -284,20 +291,18 @@ class NetworkBinaryStream extends BinaryStream{
$this->putVarInt(0); //CanPlaceOn entry count (TODO)
$this->putVarInt(0); //CanDestroy entry count (TODO)
if($item->getId() === ItemIds::SHIELD){
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
$this->putVarLong(0); //"blocking tick" (ffs mojang)
}
}
public function getRecipeIngredient() : Item{
$id = $this->getVarInt();
if($id === 0){
$netId = $this->getVarInt();
if($netId === 0){
return ItemFactory::get(ItemIds::AIR, 0, 0);
}
$meta = $this->getVarInt();
if($meta === 0x7fff){
$meta = -1;
}
$netData = $this->getVarInt();
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($netId, $netData);
$count = $this->getVarInt();
return ItemFactory::get($id, $meta, $count);
}
@ -306,8 +311,14 @@ class NetworkBinaryStream extends BinaryStream{
if($item->isNull()){
$this->putVarInt(0);
}else{
$this->putVarInt($item->getId());
$this->putVarInt($item->getDamage() & 0x7fff);
if($item->hasAnyDamageValue()){
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($item->getId(), 0);
$netData = 0x7fff;
}else{
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
}
$this->putVarInt($netId);
$this->putVarInt($netData);
$this->putVarInt($item->getCount());
}
}
@ -750,6 +761,25 @@ class NetworkBinaryStream extends BinaryStream{
$this->putVarInt($structureEditorData->structureRedstoneSaveMove);
}
public function getNbtRoot() : NamedTag{
$offset = $this->getOffset();
try{
$result = (new NetworkLittleEndianNBTStream())->read($this->getBuffer(), false, $offset, 512);
assert($result instanceof NamedTag, "doMultiple is false so we should definitely have a NamedTag here");
return $result;
}finally{
$this->setOffset($offset);
}
}
public function getNbtCompoundRoot() : CompoundTag{
$root = $this->getNbtRoot();
if(!($root instanceof CompoundTag)){
throw new \UnexpectedValueException("Expected TAG_Compound root");
}
return $root;
}
public function readGenericTypeNetworkId() : int{
return $this->getVarInt();
}

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\ActorFallPacket;
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
use pocketmine\network\mcpe\protocol\AddActorPacket;
use pocketmine\network\mcpe\protocol\AddBehaviorTreePacket;
@ -33,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddItemActorPacket;
use pocketmine\network\mcpe\protocol\AddPaintingPacket;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimateEntityPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\AnvilDamagePacket;
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
@ -45,6 +45,7 @@ use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
use pocketmine\network\mcpe\protocol\BookEditPacket;
use pocketmine\network\mcpe\protocol\BossEventPacket;
use pocketmine\network\mcpe\protocol\CameraPacket;
use pocketmine\network\mcpe\protocol\CameraShakePacket;
use pocketmine\network\mcpe\protocol\ChangeDimensionPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket;
@ -60,6 +61,7 @@ use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\network\mcpe\protocol\CorrectPlayerMovePredictionPacket;
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
@ -77,6 +79,7 @@ use pocketmine\network\mcpe\protocol\InteractPacket;
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
use pocketmine\network\mcpe\protocol\ItemComponentPacket;
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
@ -96,6 +99,7 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
use pocketmine\network\mcpe\protocol\MotionPredictionHintsPacket;
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
@ -111,6 +115,7 @@ use pocketmine\network\mcpe\protocol\PlayerActionPacket;
use pocketmine\network\mcpe\protocol\PlayerArmorDamagePacket;
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket;
use pocketmine\network\mcpe\protocol\PlayerFogPacket;
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
@ -171,7 +176,6 @@ use pocketmine\network\mcpe\protocol\TickSyncPacket;
use pocketmine\network\mcpe\protocol\TransferPacket;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPropertiesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockSyncedPacket;
use pocketmine\network\mcpe\protocol\UpdateEquipPacket;
use pocketmine\network\mcpe\protocol\UpdatePlayerGameTypePacket;
@ -325,10 +329,6 @@ abstract class NetworkSession{
return false;
}
public function handleActorFall(ActorFallPacket $packet) : bool{
return false;
}
public function handleHurtArmor(HurtArmorPacket $packet) : bool{
return false;
}
@ -705,10 +705,6 @@ abstract class NetworkSession{
return false;
}
public function handleUpdateBlockProperties(UpdateBlockPropertiesPacket $packet) : bool{
return false;
}
public function handleClientCacheBlobStatus(ClientCacheBlobStatusPacket $packet) : bool{
return false;
}
@ -796,4 +792,28 @@ abstract class NetworkSession{
public function handlePacketViolationWarning(PacketViolationWarningPacket $packet) : bool{
return false;
}
public function handleMotionPredictionHints(MotionPredictionHintsPacket $packet) : bool{
return false;
}
public function handleAnimateEntity(AnimateEntityPacket $packet) : bool{
return false;
}
public function handleCameraShake(CameraShakePacket $packet) : bool{
return false;
}
public function handlePlayerFog(PlayerFogPacket $packet) : bool{
return false;
}
public function handleCorrectPlayerMovePrediction(CorrectPlayerMovePredictionPacket $packet) : bool{
return false;
}
public function handleItemComponent(ItemComponentPacket $packet) : bool{
return false;
}
}

View File

@ -25,7 +25,6 @@ namespace pocketmine\network\mcpe;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\ActorFallPacket;
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
@ -174,10 +173,6 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
return $this->player->handlePlayerAction($packet);
}
public function handleActorFall(ActorFallPacket $packet) : bool{
return true; //Not used
}
public function handleAnimate(AnimatePacket $packet) : bool{
return $this->player->handleAnimate($packet);
}

View File

@ -0,0 +1,183 @@
<?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\network\mcpe\convert;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use function array_key_exists;
use function file_get_contents;
use function is_array;
use function is_numeric;
use function is_string;
use function json_decode;
/**
* This class handles translation between network item ID+metadata to PocketMine-MP internal ID+metadata and vice versa.
*/
final class ItemTranslator{
use SingletonTrait;
/**
* @var int[]
* @phpstan-var array<int, int>
*/
private $simpleCoreToNetMapping = [];
/**
* @var int[]
* @phpstan-var array<int, int>
*/
private $simpleNetToCoreMapping = [];
/**
* runtimeId = array[internalId][metadata]
* @var int[][]
* @phpstan-var array<int, array<int, int>>
*/
private $complexCoreToNetMapping = [];
/**
* [internalId, metadata] = array[runtimeId]
* @var int[][]
* @phpstan-var array<int, array{int, int}>
*/
private $complexNetToCoreMapping = [];
private static function make() : self{
$data = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/r16_to_current_item_map.json');
if($data === false) throw new AssumptionFailedError("Missing required resource file");
$json = json_decode($data, true);
if(!is_array($json) or !isset($json["simple"], $json["complex"]) || !is_array($json["simple"]) || !is_array($json["complex"])){
throw new AssumptionFailedError("Invalid item table format");
}
$legacyStringToIntMapRaw = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/item_id_map.json');
if($legacyStringToIntMapRaw === false){
throw new AssumptionFailedError("Missing required resource file");
}
$legacyStringToIntMap = json_decode($legacyStringToIntMapRaw, true);
if(!is_array($legacyStringToIntMap)){
throw new AssumptionFailedError("Invalid mapping table format");
}
/** @phpstan-var array<string, int> $simpleMappings */
$simpleMappings = [];
foreach($json["simple"] as $oldId => $newId){
if(!is_string($oldId) || !is_string($newId)){
throw new AssumptionFailedError("Invalid item table format");
}
$simpleMappings[$newId] = $legacyStringToIntMap[$oldId];
}
foreach($legacyStringToIntMap as $stringId => $intId){
if(isset($simpleMappings[$stringId])){
throw new \UnexpectedValueException("Old ID $stringId collides with new ID");
}
$simpleMappings[$stringId] = $intId;
}
/** @phpstan-var array<string, array{int, int}> $complexMappings */
$complexMappings = [];
foreach($json["complex"] as $oldId => $map){
if(!is_string($oldId) || !is_array($map)){
throw new AssumptionFailedError("Invalid item table format");
}
foreach($map as $meta => $newId){
if(!is_numeric($meta) || !is_string($newId)){
throw new AssumptionFailedError("Invalid item table format");
}
$complexMappings[$newId] = [$legacyStringToIntMap[$oldId], (int) $meta];
}
}
return new self(ItemTypeDictionary::getInstance(), $simpleMappings, $complexMappings);
}
/**
* @param int[] $simpleMappings
* @param int[][] $complexMappings
* @phpstan-param array<string, int> $simpleMappings
* @phpstan-param array<string, array<int, int>> $complexMappings
*/
public function __construct(ItemTypeDictionary $dictionary, array $simpleMappings, array $complexMappings){
foreach($dictionary->getEntries() as $entry){
$stringId = $entry->getStringId();
$netId = $entry->getNumericId();
if(isset($complexMappings[$stringId])){
[$id, $meta] = $complexMappings[$stringId];
$this->complexCoreToNetMapping[$id][$meta] = $netId;
$this->complexNetToCoreMapping[$netId] = [$id, $meta];
}elseif(isset($simpleMappings[$stringId])){
$this->simpleCoreToNetMapping[$simpleMappings[$stringId]] = $netId;
$this->simpleNetToCoreMapping[$netId] = $simpleMappings[$stringId];
}elseif($stringId !== "minecraft:unknown"){
throw new \InvalidArgumentException("Unmapped entry " . $stringId);
}
}
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function toNetworkId(int $internalId, int $internalMeta) : array{
if(isset($this->complexCoreToNetMapping[$internalId][$internalMeta])){
return [$this->complexCoreToNetMapping[$internalId][$internalMeta], 0];
}
if(array_key_exists($internalId, $this->simpleCoreToNetMapping)){
return [$this->simpleCoreToNetMapping[$internalId], $internalMeta];
}
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function fromNetworkId(int $networkId, int $networkMeta, ?bool &$isComplexMapping = null) : array{
if(isset($this->complexNetToCoreMapping[$networkId])){
if($networkMeta !== 0){
throw new \UnexpectedValueException("Unexpected non-zero network meta on complex item mapping");
}
$isComplexMapping = true;
return $this->complexNetToCoreMapping[$networkId];
}
$isComplexMapping = false;
if(isset($this->simpleNetToCoreMapping[$networkId])){
return [$this->simpleNetToCoreMapping[$networkId], $networkMeta];
}
throw new \UnexpectedValueException("Unmapped network ID/metadata combination $networkId:$networkMeta");
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function fromNetworkIdWithWildcardHandling(int $networkId, int $networkMeta) : array{
$isComplexMapping = false;
if($networkMeta !== 0x7fff){
return $this->fromNetworkId($networkId, $networkMeta);
}
[$id, $meta] = $this->fromNetworkId($networkId, 0, $isComplexMapping);
return [$id, $isComplexMapping ? $meta : -1];
}
}

View 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\network\mcpe\convert;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use function array_key_exists;
use function file_get_contents;
use function is_array;
use function is_bool;
use function is_int;
use function is_string;
use function json_decode;
final class ItemTypeDictionary{
use SingletonTrait;
/**
* @var ItemTypeEntry[]
* @phpstan-var list<ItemTypeEntry>
*/
private $itemTypes;
/**
* @var string[]
* @phpstan-var array<int, string>
*/
private $intToStringIdMap = [];
/**
* @var int[]
* @phpstan-var array<string, int>
*/
private $stringToIntMap = [];
private static function make() : self{
$data = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/required_item_list.json');
if($data === false) throw new AssumptionFailedError("Missing required resource file");
$table = json_decode($data, true);
if(!is_array($table)){
throw new AssumptionFailedError("Invalid item list format");
}
$params = [];
foreach($table as $name => $entry){
if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){
throw new AssumptionFailedError("Invalid item list format");
}
$params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"]);
}
return new self($params);
}
/**
* @param ItemTypeEntry[] $itemTypes
*/
public function __construct(array $itemTypes){
$this->itemTypes = $itemTypes;
foreach($this->itemTypes as $type){
$this->stringToIntMap[$type->getStringId()] = $type->getNumericId();
$this->intToStringIdMap[$type->getNumericId()] = $type->getStringId();
}
}
/**
* @return ItemTypeEntry[]
* @phpstan-return list<ItemTypeEntry>
*/
public function getEntries() : array{
return $this->itemTypes;
}
public function fromStringId(string $stringId) : int{
if(!array_key_exists($stringId, $this->stringToIntMap)){
throw new \InvalidArgumentException("Unmapped string ID \"$stringId\"");
}
return $this->stringToIntMap[$stringId];
}
public function fromIntId(int $intId) : string{
if(!array_key_exists($intId, $this->intToStringIdMap)){
throw new \InvalidArgumentException("Unmapped int ID $intId");
}
return $this->intToStringIdMap[$intId];
}
}

View File

@ -27,14 +27,10 @@ use pocketmine\block\BlockIds;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\utils\AssumptionFailedError;
use function file_get_contents;
use function getmypid;
use function json_decode;
use function mt_rand;
use function mt_srand;
use function shuffle;
/**
* @internal
@ -53,14 +49,16 @@ final class RuntimeBlockMapping{
}
public static function init() : void{
$tag = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"));
if(!($tag instanceof ListTag) or $tag->getTagType() !== NBT::TAG_Compound){ //this is a little redundant currently, but good for auto complete and makes phpstan happy
throw new \RuntimeException("Invalid blockstates table, expected TAG_List<TAG_Compound> root");
$canonicalBlockStatesFile = file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/canonical_block_states.nbt");
if($canonicalBlockStatesFile === false){
throw new AssumptionFailedError("Missing required resource file");
}
/** @var CompoundTag[] $list */
$list = $tag->getValue();
self::$bedrockKnownStates = self::randomizeTable($list);
$stream = new NetworkBinaryStream($canonicalBlockStatesFile);
$list = [];
while(!$stream->feof()){
$list[] = $stream->getNbtCompoundRoot();
}
self::$bedrockKnownStates = $list;
self::setupLegacyMappings();
}
@ -90,7 +88,7 @@ final class RuntimeBlockMapping{
*/
$idToStatesMap = [];
foreach(self::$bedrockKnownStates as $k => $state){
$idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
$idToStatesMap[$state->getString("name")][] = $k;
}
foreach($legacyStateMap as $pair){
$id = $legacyIdMap[$pair->getId()] ?? null;
@ -105,14 +103,14 @@ final class RuntimeBlockMapping{
$mappedState = $pair->getBlockState();
//TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values
$mappedState->setName("block");
$mappedState->setName("");
$mappedName = $mappedState->getString("name");
if(!isset($idToStatesMap[$mappedName])){
throw new \RuntimeException("Mapped new state does not appear in network table");
}
foreach($idToStatesMap[$mappedName] as $k){
$networkState = self::$bedrockKnownStates[$k];
if($mappedState->equals($networkState->getCompoundTag("block"))){
if($mappedState->equals($networkState)){
self::registerMapping($k, $id, $data);
continue 2;
}
@ -127,23 +125,6 @@ final class RuntimeBlockMapping{
}
}
/**
* Randomizes the order of the runtimeID table to prevent plugins relying on them.
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
* aren't supposed to do, so we have to deliberately break it to make them stop.
*
* @param CompoundTag[] $table
*
* @return CompoundTag[]
*/
private static function randomizeTable(array $table) : array{
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
mt_srand(getmypid()); //Use a seed which is the same on all threads. This isn't a secure seed, but we don't care.
shuffle($table);
mt_srand($postSeed); //restore a good quality seed that isn't dependent on PID
return $table;
}
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
self::lazyInit();
/*

View File

@ -0,0 +1,108 @@
<?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\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use function count;
class AnimateEntityPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::ANIMATE_ENTITY_PACKET;
/** @var string */
private $animation;
/** @var string */
private $nextState;
/** @var string */
private $stopExpression;
/** @var string */
private $controller;
/** @var float */
private $blendOutTime;
/**
* @var int[]
* @phpstan-var list<int>
*/
private $actorRuntimeIds;
/**
* @param int[] $actorRuntimeIds
* @phpstan-param list<int> $actorRuntimeIds
*/
public static function create(string $animation, string $nextState, string $stopExpression, string $controller, float $blendOutTime, array $actorRuntimeIds) : self{
$result = new self;
$result->animation = $animation;
$result->nextState = $nextState;
$result->stopExpression = $stopExpression;
$result->controller = $controller;
$result->blendOutTime = $blendOutTime;
$result->actorRuntimeIds = $actorRuntimeIds;
return $result;
}
public function getAnimation() : string{ return $this->animation; }
public function getNextState() : string{ return $this->nextState; }
public function getStopExpression() : string{ return $this->stopExpression; }
public function getController() : string{ return $this->controller; }
public function getBlendOutTime() : float{ return $this->blendOutTime; }
/**
* @return int[]
* @phpstan-return list<int>
*/
public function getActorRuntimeIds() : array{ return $this->actorRuntimeIds; }
protected function decodePayload() : void{
$this->animation = $this->getString();
$this->nextState = $this->getString();
$this->stopExpression = $this->getString();
$this->controller = $this->getString();
$this->blendOutTime = $this->getLFloat();
$this->actorRuntimeIds = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$this->actorRuntimeIds[] = $this->getEntityRuntimeId();
}
}
protected function encodePayload() : void{
$this->putString($this->animation);
$this->putString($this->nextState);
$this->putString($this->stopExpression);
$this->putString($this->controller);
$this->putLFloat($this->blendOutTime);
$this->putUnsignedVarInt(count($this->actorRuntimeIds));
foreach($this->actorRuntimeIds as $id){
$this->putEntityRuntimeId($id);
}
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleAnimateEntity($this);
}
}

View File

@ -0,0 +1,72 @@
<?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\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
class CameraShakePacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::CAMERA_SHAKE_PACKET;
public const TYPE_POSITIONAL = 0;
public const TYPE_ROTATIONAL = 1;
/** @var float */
private $intensity;
/** @var float */
private $duration;
/** @var int */
private $shakeType;
public static function create(float $intensity, float $duration, int $shakeType) : self{
$result = new self;
$result->intensity = $intensity;
$result->duration = $duration;
$result->shakeType = $shakeType;
return $result;
}
public function getIntensity() : float{ return $this->intensity; }
public function getDuration() : float{ return $this->duration; }
public function getShakeType() : int{ return $this->shakeType; }
protected function decodePayload() : void{
$this->intensity = $this->getLFloat();
$this->duration = $this->getLFloat();
$this->shakeType = $this->getByte();
}
protected function encodePayload() : void{
$this->putLFloat($this->intensity);
$this->putLFloat($this->duration);
$this->putByte($this->shakeType);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleCameraShake($this);
}
}

View File

@ -32,13 +32,17 @@ class ContainerClosePacket extends DataPacket{
/** @var int */
public $windowId;
/** @var bool */
public $server = false;
protected function decodePayload(){
$this->windowId = $this->getByte();
$this->server = $this->getBool();
}
protected function encodePayload(){
$this->putByte($this->windowId);
$this->putBool($this->server);
}
public function handle(NetworkSession $session) : bool{

View File

@ -0,0 +1,77 @@
<?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\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\NetworkSession;
class CorrectPlayerMovePredictionPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::CORRECT_PLAYER_MOVE_PREDICTION_PACKET;
/** @var Vector3 */
private $position;
/** @var Vector3 */
private $delta;
/** @var bool */
private $onGround;
/** @var int */
private $tick;
public static function create(Vector3 $position, Vector3 $delta, bool $onGround, int $tick) : self{
$result = new self;
$result->position = $position;
$result->delta = $delta;
$result->onGround = $onGround;
$result->tick = $tick;
return $result;
}
public function getPosition() : Vector3{ return $this->position; }
public function getDelta() : Vector3{ return $this->delta; }
public function isOnGround() : bool{ return $this->onGround; }
public function getTick() : int{ return $this->tick; }
protected function decodePayload() : void{
$this->position = $this->getVector3();
$this->delta = $this->getVector3();
$this->onGround = $this->getBool();
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload() : void{
$this->putVector3($this->position);
$this->putVector3($this->delta);
$this->putBool($this->onGround);
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleCorrectPlayerMovePrediction($this);
}
}

View File

@ -30,6 +30,7 @@ use pocketmine\inventory\ShapedRecipe;
use pocketmine\inventory\ShapelessRecipe;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe;
@ -124,13 +125,12 @@ class CraftingDataPacket extends DataPacket{
break;
case self::ENTRY_FURNACE:
case self::ENTRY_FURNACE_DATA:
$inputId = $this->getVarInt();
$inputData = -1;
if($recipeType === self::ENTRY_FURNACE_DATA){
$inputData = $this->getVarInt();
if($inputData === 0x7fff){
$inputData = -1;
}
$inputIdNet = $this->getVarInt();
if($recipeType === self::ENTRY_FURNACE){
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, 0x7fff);
}else{
$inputMetaNet = $this->getVarInt();
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, $inputMetaNet);
}
$entry["input"] = ItemFactory::get($inputId, $inputData);
$entry["output"] = $out = $this->getSlot();
@ -150,18 +150,25 @@ class CraftingDataPacket extends DataPacket{
$this->decodedEntries[] = $entry;
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$input = $this->getVarInt();
$inputMeta = $this->getVarInt();
$ingredient = $this->getVarInt();
$ingredientMeta = $this->getVarInt();
$output = $this->getVarInt();
$outputMeta = $this->getVarInt();
$inputIdNet = $this->getVarInt();
$inputMetaNet = $this->getVarInt();
[$input, $inputMeta] = ItemTranslator::getInstance()->fromNetworkId($inputIdNet, $inputMetaNet);
$ingredientIdNet = $this->getVarInt();
$ingredientMetaNet = $this->getVarInt();
[$ingredient, $ingredientMeta] = ItemTranslator::getInstance()->fromNetworkId($ingredientIdNet, $ingredientMetaNet);
$outputIdNet = $this->getVarInt();
$outputMetaNet = $this->getVarInt();
[$output, $outputMeta] = ItemTranslator::getInstance()->fromNetworkId($outputIdNet, $outputMetaNet);
$this->potionTypeRecipes[] = new PotionTypeRecipe($input, $inputMeta, $ingredient, $ingredientMeta, $output, $outputMeta);
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$input = $this->getVarInt();
$ingredient = $this->getVarInt();
$output = $this->getVarInt();
//TODO: we discard inbound ID here, not safe because netID on its own might map to internalID+internalMeta for us
$inputIdNet = $this->getVarInt();
[$input, ] = ItemTranslator::getInstance()->fromNetworkId($inputIdNet, 0);
$ingredientIdNet = $this->getVarInt();
[$ingredient, ] = ItemTranslator::getInstance()->fromNetworkId($ingredientIdNet, 0);
$outputIdNet = $this->getVarInt();
[$output, ] = ItemTranslator::getInstance()->fromNetworkId($outputIdNet, 0);
$this->potionContainerRecipes[] = new PotionContainerChangeRecipe($input, $ingredient, $output);
}
$this->cleanRecipes = $this->getBool();
@ -230,15 +237,18 @@ class CraftingDataPacket extends DataPacket{
}
private static function writeFurnaceRecipe(FurnaceRecipe $recipe, NetworkBinaryStream $stream) : int{
$stream->putVarInt($recipe->getInput()->getId());
$result = CraftingDataPacket::ENTRY_FURNACE;
if(!$recipe->getInput()->hasAnyDamageValue()){ //Data recipe
$stream->putVarInt($recipe->getInput()->getDamage());
$result = CraftingDataPacket::ENTRY_FURNACE_DATA;
$input = $recipe->getInput();
if($input->hasAnyDamageValue()){
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($input->getId(), 0);
$netData = 0x7fff;
}else{
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($input->getId(), $input->getDamage());
}
$stream->putVarInt($netId);
$stream->putVarInt($netData);
$stream->putSlot($recipe->getResult());
$stream->putString("furnace"); //TODO: blocktype (no prefix) (this might require internal API breaks)
return $result;
return CraftingDataPacket::ENTRY_FURNACE_DATA;
}
/**

View File

@ -0,0 +1,78 @@
<?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\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\ItemComponentPacketEntry;
use function count;
class ItemComponentPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::ITEM_COMPONENT_PACKET;
/**
* @var ItemComponentPacketEntry[]
* @phpstan-var list<ItemComponentPacketEntry>
*/
private $entries;
/**
* @param ItemComponentPacketEntry[] $entries
* @phpstan-param list<ItemComponentPacketEntry> $entries
*/
public static function create(array $entries) : self{
$result = new self;
$result->entries = $entries;
return $result;
}
/**
* @return ItemComponentPacketEntry[]
* @phpstan-return list<ItemComponentPacketEntry>
*/
public function getEntries() : array{ return $this->entries; }
protected function decodePayload() : void{
$this->entries = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$name = $this->getString();
$nbt = $this->getNbtCompoundRoot();
$this->entries[] = new ItemComponentPacketEntry($name, $nbt);
}
}
protected function encodePayload() : void{
$this->putUnsignedVarInt(count($this->entries));
foreach($this->entries as $entry){
$this->putString($entry->getName());
$this->put((new NetworkLittleEndianNBTStream())->write($entry->getComponentNbt()));
}
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleItemComponent($this);
}
}

View File

@ -0,0 +1,70 @@
<?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\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\NetworkSession;
class MotionPredictionHintsPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::MOTION_PREDICTION_HINTS_PACKET;
/** @var int */
private $entityRuntimeId;
/** @var Vector3 */
private $motion;
/** @var bool */
private $onGround;
public static function create(int $entityRuntimeId, Vector3 $motion, bool $onGround) : self{
$result = new self;
$result->entityRuntimeId = $entityRuntimeId;
$result->motion = $motion;
$result->onGround = $onGround;
return $result;
}
public function getEntityRuntimeIdField() : int{ return $this->entityRuntimeId; } //TODO: rename this on PM4 (crap architecture, thanks shoghi)
public function getMotion() : Vector3{ return $this->motion; }
public function isOnGround() : bool{ return $this->onGround; }
protected function decodePayload() : void{
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->motion = $this->getVector3();
$this->onGround = $this->getBool();
}
protected function encodePayload() : void{
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putVector3($this->motion);
$this->putBool($this->onGround);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleMotionPredictionHints($this);
}
}

View File

@ -44,12 +44,12 @@ class MoveActorDeltaPacket extends DataPacket{
public $entityRuntimeId;
/** @var int */
public $flags;
/** @var int */
public $xDiff = 0;
/** @var int */
public $yDiff = 0;
/** @var int */
public $zDiff = 0;
/** @var float */
public $xPos = 0;
/** @var float */
public $yPos = 0;
/** @var float */
public $zPos = 0;
/** @var float */
public $xRot = 0.0;
/** @var float */
@ -57,9 +57,9 @@ class MoveActorDeltaPacket extends DataPacket{
/** @var float */
public $zRot = 0.0;
private function maybeReadCoord(int $flag) : int{
private function maybeReadCoord(int $flag) : float{
if(($this->flags & $flag) !== 0){
return $this->getVarInt();
return $this->getLFloat();
}
return 0;
}
@ -74,17 +74,17 @@ class MoveActorDeltaPacket extends DataPacket{
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->flags = $this->getLShort();
$this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X);
$this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y);
$this->zDiff = $this->maybeReadCoord(self::FLAG_HAS_Z);
$this->xPos = $this->maybeReadCoord(self::FLAG_HAS_X);
$this->yPos = $this->maybeReadCoord(self::FLAG_HAS_Y);
$this->zPos = $this->maybeReadCoord(self::FLAG_HAS_Z);
$this->xRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_X);
$this->yRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Y);
$this->zRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Z);
}
private function maybeWriteCoord(int $flag, int $val) : void{
private function maybeWriteCoord(int $flag, float $val) : void{
if(($this->flags & $flag) !== 0){
$this->putVarInt($val);
$this->putLFloat($val);
}
}
@ -97,9 +97,9 @@ class MoveActorDeltaPacket extends DataPacket{
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putLShort($this->flags);
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff);
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff);
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zDiff);
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xPos);
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yPos);
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zPos);
$this->maybeWriteRotation(self::FLAG_HAS_ROT_X, $this->xRot);
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Y, $this->yRot);
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Z, $this->zRot);

View File

@ -56,6 +56,8 @@ class MovePlayerPacket extends DataPacket{
public $teleportCause = 0;
/** @var int */
public $teleportItem = 0;
/** @var int */
public $tick = 0;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
@ -70,6 +72,7 @@ class MovePlayerPacket extends DataPacket{
$this->teleportCause = $this->getLInt();
$this->teleportItem = $this->getLInt();
}
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload(){
@ -85,6 +88,7 @@ class MovePlayerPacket extends DataPacket{
$this->putLInt($this->teleportCause);
$this->putLInt($this->teleportItem);
}
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $session) : bool{

View File

@ -71,7 +71,6 @@ class PacketPool{
static::registerPacket(new BlockPickRequestPacket());
static::registerPacket(new ActorPickRequestPacket());
static::registerPacket(new PlayerActionPacket());
static::registerPacket(new ActorFallPacket());
static::registerPacket(new HurtArmorPacket());
static::registerPacket(new SetActorDataPacket());
static::registerPacket(new SetActorMotionPacket());
@ -166,7 +165,6 @@ class PacketPool{
static::registerPacket(new MapCreateLockedCopyPacket());
static::registerPacket(new StructureTemplateDataRequestPacket());
static::registerPacket(new StructureTemplateDataResponsePacket());
static::registerPacket(new UpdateBlockPropertiesPacket());
static::registerPacket(new ClientCacheBlobStatusPacket());
static::registerPacket(new ClientCacheMissResponsePacket());
static::registerPacket(new EducationSettingsPacket());
@ -189,6 +187,12 @@ class PacketPool{
static::registerPacket(new PositionTrackingDBClientRequestPacket());
static::registerPacket(new DebugInfoPacket());
static::registerPacket(new PacketViolationWarningPacket());
static::registerPacket(new MotionPredictionHintsPacket());
static::registerPacket(new AnimateEntityPacket());
static::registerPacket(new CameraShakePacket());
static::registerPacket(new PlayerFogPacket());
static::registerPacket(new CorrectPlayerMovePredictionPacket());
static::registerPacket(new ItemComponentPacket());
}
/**

View File

@ -43,7 +43,7 @@ class PlayerActionPacket extends DataPacket{
public const ACTION_STOP_SPRINT = 10;
public const ACTION_START_SNEAK = 11;
public const ACTION_STOP_SNEAK = 12;
public const ACTION_DIMENSION_CHANGE_REQUEST = 13; //sent when dying in different dimension
public const ACTION_CREATIVE_PLAYER_DESTROY_BLOCK = 13;
public const ACTION_DIMENSION_CHANGE_ACK = 14; //sent when spawning in a different dimension to tell the server we spawned
public const ACTION_START_GLIDE = 15;
public const ACTION_STOP_GLIDE = 16;

View File

@ -54,13 +54,17 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
private $playMode;
/** @var Vector3|null */
private $vrGazeDirection = null;
/** @var int */
private $tick;
/** @var Vector3 */
private $delta;
/**
* @param int $inputMode @see InputMode
* @param int $playMode @see PlayMode
* @param Vector3|null $vrGazeDirection only used when PlayMode::VR
*/
public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection = null) : self{
public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection, int $tick, Vector3 $delta) : self{
if($playMode === PlayMode::VR and $vrGazeDirection === null){
//yuck, can we get a properly written packet just once? ...
throw new \InvalidArgumentException("Gaze direction must be provided for VR play mode");
@ -78,6 +82,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
if($vrGazeDirection !== null){
$result->vrGazeDirection = $vrGazeDirection->asVector3();
}
$result->tick = $tick;
$result->delta = $delta;
return $result;
}
@ -127,6 +133,10 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
return $this->vrGazeDirection;
}
public function getTick() : int{ return $this->tick; }
public function getDelta() : Vector3{ return $this->delta; }
protected function decodePayload() : void{
$this->pitch = $this->getLFloat();
$this->yaw = $this->getLFloat();
@ -140,6 +150,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
if($this->playMode === PlayMode::VR){
$this->vrGazeDirection = $this->getVector3();
}
$this->tick = $this->getUnsignedVarLong();
$this->delta = $this->getVector3();
}
protected function encodePayload() : void{
@ -156,6 +168,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
assert($this->vrGazeDirection !== null);
$this->putVector3($this->vrGazeDirection);
}
$this->putUnsignedVarLong($this->tick);
$this->putVector3($this->delta);
}
public function handle(NetworkSession $handler) : bool{

View File

@ -25,31 +25,49 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\NetworkSession;
use function count;
class UpdateBlockPropertiesPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::UPDATE_BLOCK_PROPERTIES_PACKET;
class PlayerFogPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::PLAYER_FOG_PACKET;
/** @var string */
private $nbt;
/**
* @var string[]
* @phpstan-var list<string>
*/
private $fogLayers;
public static function create(CompoundTag $data) : self{
/**
* @param string[] $fogLayers
* @phpstan-param list<string> $fogLayers
*/
public static function create(array $fogLayers) : self{
$result = new self;
$result->nbt = (new NetworkLittleEndianNBTStream())->write($data);
$result->fogLayers = $fogLayers;
return $result;
}
/**
* @return string[]
* @phpstan-return list<string>
*/
public function getFogLayers() : array{ return $this->fogLayers; }
protected function decodePayload() : void{
$this->nbt = $this->getRemaining();
$this->fogLayers = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$this->fogLayers[] = $this->getString();
}
}
protected function encodePayload() : void{
$this->put($this->nbt);
$this->putUnsignedVarInt(count($this->fogLayers));
foreach($this->fogLayers as $fogLayer){
$this->putString($fogLayer);
}
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleUpdateBlockProperties($this);
return $handler->handlePlayerFog($this);
}
}

View File

@ -37,11 +37,11 @@ interface ProtocolInfo{
*/
/** Actual Minecraft: PE protocol version */
public const CURRENT_PROTOCOL = 408;
public const CURRENT_PROTOCOL = 419;
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
public const MINECRAFT_VERSION = 'v1.16.20';
public const MINECRAFT_VERSION = 'v1.16.100';
/** Version number sent to clients in ping responses. */
public const MINECRAFT_VERSION_NETWORK = '1.16.20';
public const MINECRAFT_VERSION_NETWORK = '1.16.100';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;
@ -79,7 +79,7 @@ interface ProtocolInfo{
public const BLOCK_PICK_REQUEST_PACKET = 0x22;
public const ACTOR_PICK_REQUEST_PACKET = 0x23;
public const PLAYER_ACTION_PACKET = 0x24;
public const ACTOR_FALL_PACKET = 0x25;
public const HURT_ARMOR_PACKET = 0x26;
public const SET_ACTOR_DATA_PACKET = 0x27;
public const SET_ACTOR_MOTION_PACKET = 0x28;
@ -176,7 +176,7 @@ interface ProtocolInfo{
public const MAP_CREATE_LOCKED_COPY_PACKET = 0x83;
public const STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET = 0x84;
public const STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET = 0x85;
public const UPDATE_BLOCK_PROPERTIES_PACKET = 0x86;
public const CLIENT_CACHE_BLOB_STATUS_PACKET = 0x87;
public const CLIENT_CACHE_MISS_RESPONSE_PACKET = 0x88;
public const EDUCATION_SETTINGS_PACKET = 0x89;
@ -199,5 +199,11 @@ interface ProtocolInfo{
public const POSITION_TRACKING_D_B_CLIENT_REQUEST_PACKET = 0x9a;
public const DEBUG_INFO_PACKET = 0x9b;
public const PACKET_VIOLATION_WARNING_PACKET = 0x9c;
public const MOTION_PREDICTION_HINTS_PACKET = 0x9d;
public const ANIMATE_ENTITY_PACKET = 0x9e;
public const CAMERA_SHAKE_PACKET = 0x9f;
public const PLAYER_FOG_PACKET = 0xa0;
public const CORRECT_PLAYER_MOVE_PREDICTION_PACKET = 0xa1;
public const ITEM_COMPONENT_PACKET = 0xa2;
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\resourcepacks\ResourcePack;
use function count;
@ -40,11 +41,12 @@ class ResourcePackStackPacket extends DataPacket{
/** @var ResourcePack[] */
public $resourcePackStack = [];
/** @var bool */
public $isExperimental = false;
/** @var string */
public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
/** @var Experiments */
public $experiments;
protected function decodePayload(){
$this->mustAccept = $this->getBool();
$behaviorPackCount = $this->getUnsignedVarInt();
@ -61,8 +63,8 @@ class ResourcePackStackPacket extends DataPacket{
$this->getString();
}
$this->isExperimental = $this->getBool();
$this->baseGameVersion = $this->getString();
$this->experiments = Experiments::read($this);
}
protected function encodePayload(){
@ -82,8 +84,8 @@ class ResourcePackStackPacket extends DataPacket{
$this->putString(""); //TODO: subpack name
}
$this->putBool($this->isExperimental);
$this->putString($this->baseGameVersion);
$this->experiments->write($this);
}
public function handle(NetworkSession $session) : bool{

View File

@ -38,14 +38,19 @@ class SetActorDataPacket extends DataPacket{
*/
public $metadata;
/** @var int */
public $tick = 0;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->metadata = $this->getEntityMetadata();
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putEntityMetadata($this->metadata);
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $session) : bool{

View File

@ -27,29 +27,22 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\math\Vector3;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\BlockPaletteEntry;
use pocketmine\network\mcpe\protocol\types\EducationEditionOffer;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\GameRuleType;
use pocketmine\network\mcpe\protocol\types\GeneratorType;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\network\mcpe\protocol\types\MultiplayerGameVisibility;
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
use function count;
use function file_get_contents;
use function json_decode;
use const pocketmine\RESOURCE_PATH;
class StartGamePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::START_GAME_PACKET;
/** @var string|null */
private static $blockTableCache = null;
/** @var string|null */
private static $itemTableCache = null;
/** @var int */
public $entityUniqueId;
/** @var int */
@ -116,6 +109,8 @@ class StartGamePacket extends DataPacket{
public $gameRules = [ //TODO: implement this
"naturalregeneration" => [GameRuleType::BOOL, false] //Hack for client side regeneration
];
/** @var Experiments */
public $experiments;
/** @var bool */
public $hasBonusChestEnabled = false;
/** @var bool */
@ -159,8 +154,8 @@ class StartGamePacket extends DataPacket{
public $premiumWorldTemplateId = "";
/** @var bool */
public $isTrial = false;
/** @var bool */
public $isMovementServerAuthoritative = false;
/** @var int */
public $playerMovementType = PlayerMovementType::LEGACY;
/** @var int */
public $currentTick = 0; //only used if isTrial is true
/** @var int */
@ -168,13 +163,17 @@ class StartGamePacket extends DataPacket{
/** @var string */
public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort
/** @var ListTag|null */
public $blockTable = null;
/**
* @var int[]|null string (name) => int16 (legacyID)
* @phpstan-var array<string, int>|null
* @var BlockPaletteEntry[]
* @phpstan-var list<BlockPaletteEntry>
*/
public $itemTable = null;
public $blockPalette = [];
/**
* @var ItemTypeEntry[]
* @phpstan-var list<ItemTypeEntry>
*/
public $itemTable;
/** @var bool */
public $enableNewInventorySystem = false; //TODO
@ -210,6 +209,7 @@ class StartGamePacket extends DataPacket{
$this->commandsEnabled = $this->getBool();
$this->isTexturePacksRequired = $this->getBool();
$this->gameRules = $this->getGameRules();
$this->experiments = Experiments::read($this);
$this->hasBonusChestEnabled = $this->getBool();
$this->hasStartWithMapEnabled = $this->getBool();
$this->defaultPlayerPermission = $this->getVarInt();
@ -235,23 +235,25 @@ class StartGamePacket extends DataPacket{
$this->worldName = $this->getString();
$this->premiumWorldTemplateId = $this->getString();
$this->isTrial = $this->getBool();
$this->isMovementServerAuthoritative = $this->getBool();
$this->playerMovementType = $this->getVarInt();
$this->currentTick = $this->getLLong();
$this->enchantmentSeed = $this->getVarInt();
$blockTable = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
if(!($blockTable instanceof ListTag)){
throw new \UnexpectedValueException("Wrong block table root NBT tag type");
$this->blockPalette = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$blockName = $this->getString();
$state = $this->getNbtCompoundRoot();
$this->blockPalette[] = new BlockPaletteEntry($blockName, $state);
}
$this->blockTable = $blockTable;
$this->itemTable = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$id = $this->getString();
$legacyId = $this->getSignedLShort();
$stringId = $this->getString();
$numericId = $this->getSignedLShort();
$isComponentBased = $this->getBool();
$this->itemTable[$id] = $legacyId;
$this->itemTable[] = new ItemTypeEntry($stringId, $numericId, $isComponentBased);
}
$this->multiplayerCorrelationId = $this->getString();
@ -290,6 +292,7 @@ class StartGamePacket extends DataPacket{
$this->putBool($this->commandsEnabled);
$this->putBool($this->isTexturePacksRequired);
$this->putGameRules($this->gameRules);
$this->experiments->write($this);
$this->putBool($this->hasBonusChestEnabled);
$this->putBool($this->hasStartWithMapEnabled);
$this->putVarInt($this->defaultPlayerPermission);
@ -314,47 +317,28 @@ class StartGamePacket extends DataPacket{
$this->putString($this->worldName);
$this->putString($this->premiumWorldTemplateId);
$this->putBool($this->isTrial);
$this->putBool($this->isMovementServerAuthoritative);
$this->putVarInt($this->playerMovementType);
$this->putLLong($this->currentTick);
$this->putVarInt($this->enchantmentSeed);
if($this->blockTable === null){
if(self::$blockTableCache === null){
//this is a really nasty hack, but it'll do for now
self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(new ListTag("", RuntimeBlockMapping::getBedrockKnownStates()));
}
$this->put(self::$blockTableCache);
}else{
$this->put((new NetworkLittleEndianNBTStream())->write($this->blockTable));
$this->putUnsignedVarInt(count($this->blockPalette));
$nbtWriter = new NetworkLittleEndianNBTStream();
foreach($this->blockPalette as $entry){
$this->putString($entry->getName());
$this->put($nbtWriter->write($entry->getStates()));
}
if($this->itemTable === null){
if(self::$itemTableCache === null){
self::$itemTableCache = self::serializeItemTable(json_decode(file_get_contents(RESOURCE_PATH . '/vanilla/item_id_map.json'), true));
}
$this->put(self::$itemTableCache);
}else{
$this->put(self::serializeItemTable($this->itemTable));
$this->putUnsignedVarInt(count($this->itemTable));
foreach($this->itemTable as $entry){
$this->putString($entry->getStringId());
$this->putLShort($entry->getNumericId());
$this->putBool($entry->isComponentBased());
}
$this->putString($this->multiplayerCorrelationId);
$this->putBool($this->enableNewInventorySystem);
}
/**
* @param int[] $table
* @phpstan-param array<string, int> $table
*/
private static function serializeItemTable(array $table) : string{
$stream = new NetworkBinaryStream();
$stream->putUnsignedVarInt(count($table));
foreach($table as $name => $legacyId){
$stream->putString($name);
$stream->putLShort($legacyId);
}
return $stream->getBuffer();
}
public function handle(NetworkSession $session) : bool{
return $session->handleStartGame($this);
}

View File

@ -35,15 +35,19 @@ class UpdateAttributesPacket extends DataPacket{
public $entityRuntimeId;
/** @var Attribute[] */
public $entries = [];
/** @var int */
public $tick = 0;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->entries = $this->getAttributeList();
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putAttributeList(...$this->entries);
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $session) : bool{

View File

@ -0,0 +1,43 @@
<?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\network\mcpe\protocol\types;
use pocketmine\nbt\tag\CompoundTag;
final class BlockPaletteEntry{
/** @var string */
private $name;
/** @var CompoundTag */
private $states;
public function __construct(string $name, CompoundTag $states){
$this->name = $name;
$this->states = $states;
}
public function getName() : string{ return $this->name; }
public function getStates() : CompoundTag{ return $this->states; }
}

View File

@ -0,0 +1,72 @@
<?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\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
use function count;
final class Experiments{
/**
* @var bool[]
* @phpstan-var array<string, bool>
*/
private $experiments;
/** @var bool */
private $hasPreviouslyUsedExperiments;
/**
* @param bool[] $experiments
* @phpstan-param array<string, bool> $experiments
*/
public function __construct(array $experiments, bool $hasPreviouslyUsedExperiments){
$this->experiments = $experiments;
$this->hasPreviouslyUsedExperiments = $hasPreviouslyUsedExperiments;
}
/** @return bool[] */
public function getExperiments() : array{ return $this->experiments; }
public function hasPreviouslyUsedExperiments() : bool{ return $this->hasPreviouslyUsedExperiments; }
public static function read(NetworkBinaryStream $in) : self{
$experiments = [];
for($i = 0, $len = $in->getLInt(); $i < $len; ++$i){
$experimentName = $in->getString();
$enabled = $in->getBool();
$experiments[$experimentName] = $enabled;
}
$hasPreviouslyUsedExperiments = $in->getBool();
return new self($experiments, $hasPreviouslyUsedExperiments);
}
public function write(NetworkBinaryStream $out) : void{
$out->putLInt(count($this->experiments));
foreach($this->experiments as $experimentName => $enabled){
$out->putString($experimentName);
$out->putBool($enabled);
}
$out->putBool($this->hasPreviouslyUsedExperiments);
}
}

View File

@ -0,0 +1,43 @@
<?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\network\mcpe\protocol\types;
use pocketmine\nbt\tag\CompoundTag;
final class ItemComponentPacketEntry{
/** @var string */
private $name;
/** @var CompoundTag */
private $componentNbt;
public function __construct(string $name, CompoundTag $componentNbt){
$this->name = $name;
$this->componentNbt = $componentNbt;
}
public function getName() : string{ return $this->name; }
public function getComponentNbt() : CompoundTag{ return $this->componentNbt; }
}

View File

@ -21,35 +21,26 @@
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
namespace pocketmine\network\mcpe\protocol\types;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
class ActorFallPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::ACTOR_FALL_PACKET;
final class ItemTypeEntry{
/** @var string */
private $stringId;
/** @var int */
public $entityRuntimeId;
/** @var float */
public $fallDistance;
private $numericId;
/** @var bool */
public $isInVoid;
private $componentBased;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->fallDistance = $this->getLFloat();
$this->isInVoid = $this->getBool();
public function __construct(string $stringId, int $numericId, bool $componentBased){
$this->stringId = $stringId;
$this->numericId = $numericId;
$this->componentBased = $componentBased;
}
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putLFloat($this->fallDistance);
$this->putBool($this->isInVoid);
}
public function getStringId() : string{ return $this->stringId; }
public function handle(NetworkSession $session) : bool{
return $session->handleActorFall($this);
}
public function getNumericId() : int{ return $this->numericId; }
public function isComponentBased() : bool{ return $this->componentBased; }
}

View File

@ -0,0 +1,31 @@
<?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\network\mcpe\protocol\types;
final class PlayerMovementType{
public const LEGACY = 0; //MovePlayerPacket
public const SERVER_AUTHORITATIVE_V1 = 1; //PlayerAuthInputPacket
public const SERVER_AUTHORITATIVE_V2_REWIND = 2; //PlayerAuthInputPacket + a bunch of junk that solves a nonexisting problem
}

View File

@ -29,17 +29,23 @@ class SkinAnimation{
public const TYPE_BODY_32 = 2;
public const TYPE_BODY_64 = 3;
public const EXPRESSION_LINEAR = 0; //???
public const EXPRESSION_BLINKING = 1;
/** @var SkinImage */
private $image;
/** @var int */
private $type;
/** @var float */
private $frames;
/** @var int */
private $expressionType;
public function __construct(SkinImage $image, int $type, float $frames){
public function __construct(SkinImage $image, int $type, float $frames, int $expressionType){
$this->image = $image;
$this->type = $type;
$this->frames = $frames;
$this->expressionType = $expressionType;
}
/**
@ -62,4 +68,6 @@ class SkinAnimation{
public function getFrames() : float{
return $this->frames;
}
public function getExpressionType() : int{ return $this->expressionType; }
}

View File

@ -28,8 +28,13 @@ use function count;
final class ItemStackResponse{
/** @var bool */
private $ok;
public const RESULT_OK = 0;
public const RESULT_ERROR = 1;
//TODO: there are a ton more possible result types but we don't need them yet and they are wayyyyyy too many for me
//to waste my time on right now...
/** @var int */
private $result;
/** @var int */
private $requestId;
/** @var ItemStackResponseContainerInfo[] */
@ -38,13 +43,13 @@ final class ItemStackResponse{
/**
* @param ItemStackResponseContainerInfo[] $containerInfos
*/
public function __construct(bool $ok, int $requestId, array $containerInfos){
$this->ok = $ok;
public function __construct(int $result, int $requestId, array $containerInfos){
$this->result = $result;
$this->requestId = $requestId;
$this->containerInfos = $containerInfos;
}
public function isOk() : bool{ return $this->ok; }
public function getResult() : int{ return $this->result; }
public function getRequestId() : int{ return $this->requestId; }
@ -52,17 +57,17 @@ final class ItemStackResponse{
public function getContainerInfos() : array{ return $this->containerInfos; }
public static function read(NetworkBinaryStream $in) : self{
$ok = $in->getBool();
$result = $in->getByte();
$requestId = $in->readGenericTypeNetworkId();
$containerInfos = [];
for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){
$containerInfos[] = ItemStackResponseContainerInfo::read($in);
}
return new self($ok, $requestId, $containerInfos);
return new self($result, $requestId, $containerInfos);
}
public function write(NetworkBinaryStream $out) : void{
$out->putBool($this->ok);
$out->putByte($this->result);
$out->writeGenericTypeNetworkId($this->requestId);
$out->putUnsignedVarInt(count($this->containerInfos));
foreach($this->containerInfos as $containerInfo){

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\permission;
use pocketmine\utils\AssumptionFailedError;
use function array_shift;
use function count;
use function explode;
@ -145,7 +146,9 @@ class BanEntry{
private static function parseDate(string $date) : \DateTime{
$datetime = \DateTime::createFromFormat(self::$format, $date);
if(!($datetime instanceof \DateTime)){
throw new \RuntimeException("Error parsing date for BanEntry: " . implode(", ", \DateTime::getLastErrors()["errors"]));
$lastErrors = \DateTime::getLastErrors();
if($lastErrors === false) throw new AssumptionFailedError("DateTime::getLastErrors() should not be returning false in here");
throw new \RuntimeException("Error parsing date for BanEntry: " . implode(", ", $lastErrors["errors"]));
}
return $datetime;

View File

@ -123,7 +123,16 @@ class PermissionAttachment{
if($this->permissions[$name] === $value){
return;
}
unset($this->permissions[$name]); //Fixes children getting overwritten
/* Because of the way child permissions are calculated, permissions which were set later in time are
* preferred over earlier ones when conflicts in inherited permission values occur.
* Here's the kicker: This behaviour depends on PHP's internal array ordering, which maintains insertion
* order -- BUT -- assigning to an existing index replaces the old value WITHOUT changing the order.
* (what crazy person thought relying on this this was a good idea?!?!?!?!?!)
*
* This removes the old value so that the new value will be added at the end of the array's internal order
* instead of directly taking the place of the older value.
*/
unset($this->permissions[$name]);
}
$this->permissions[$name] = $value;
$this->permissible->recalculatePermissions();

View File

@ -36,9 +36,6 @@ class PermissionAttachmentInfo{
/** @var bool */
private $value;
/**
* @throws \InvalidStateException
*/
public function __construct(Permissible $permissible, string $permission, PermissionAttachment $attachment = null, bool $value){
$this->permissible = $permissible;
$this->permission = $permission;

View File

@ -157,7 +157,7 @@ class PermissionManager{
}
/**
* @return array|Permissible[]
* @return Permissible[]
*/
public function getPermissionSubscriptions(string $permission) : array{
return $this->permSubs[$permission] ?? [];

View File

@ -156,7 +156,11 @@ class PluginDescription{
}
$this->authors = [];
if(isset($plugin["author"])){
$this->authors[] = $plugin["author"];
if(is_array($plugin["author"])){
$this->authors = $plugin["author"];
}else{
$this->authors[] = $plugin["author"];
}
}
if(isset($plugin["authors"])){
foreach($plugin["authors"] as $author){

View File

@ -39,7 +39,6 @@ use pocketmine\Player;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use function current;
use function get_class;
use function in_array;
use function is_a;
@ -74,8 +73,8 @@ abstract class Tile extends Position{
*/
private static $knownTiles = [];
/**
* @var string[][]
* @phpstan-var array<class-string<Tile>, list<string>>
* @var string[]
* @phpstan-var array<class-string<Tile>, string>
*/
private static $saveNames = [];
@ -138,7 +137,7 @@ abstract class Tile extends Position{
self::$knownTiles[$name] = $className;
}
self::$saveNames[$className] = $saveNames;
self::$saveNames[$className] = reset($saveNames);
return true;
}
@ -154,8 +153,7 @@ abstract class Tile extends Position{
throw new \InvalidStateException("Tile is not registered");
}
reset(self::$saveNames[static::class]);
return current(self::$saveNames[static::class]);
return self::$saveNames[static::class];
}
public function __construct(Level $level, CompoundTag $nbt){

View File

@ -0,0 +1,48 @@
<?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\utils;
trait SingletonTrait{
/** @var self|null */
private static $instance = null;
private static function make() : self{
return new self;
}
public static function getInstance() : self{
if(self::$instance === null){
self::$instance = self::make();
}
return self::$instance;
}
public static function setInstance(self $instance) : void{
self::$instance = $instance;
}
public static function reset() : void{
self::$instance = null;
}
}

View File

@ -185,11 +185,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/entity/Effect.php
-
message: "#^Method pocketmine\\\\entity\\\\Entity\\:\\:getSaveId\\(\\) should return string but returns mixed\\.$#"
count: 1
path: ../../../src/pocketmine/entity/Entity.php
-
message: "#^Cannot cast mixed to int\\.$#"
count: 2
@ -560,11 +555,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/scheduler/AsyncTask.php
-
message: "#^Method pocketmine\\\\tile\\\\Tile\\:\\:getSaveId\\(\\) should return string but returns mixed\\.$#"
count: 1
path: ../../../src/pocketmine/tile/Tile.php
-
message: "#^Parameter \\#2 \\$timestamp of function date expects int, mixed given\\.$#"
count: 1

View File

@ -390,6 +390,11 @@ parameters:
count: 1
path: ../../../src/pocketmine/command/defaults/TimingsCommand.php
-
message: "#^Array \\(array\\<class\\-string\\<pocketmine\\\\entity\\\\Entity\\>, string\\>\\) does not accept string\\|false\\.$#"
count: 1
path: ../../../src/pocketmine/entity/Entity.php
-
message: "#^Parameter \\#1 \\$index of method pocketmine\\\\inventory\\\\BaseInventory\\:\\:setItem\\(\\) expects int, int\\|string given\\.$#"
count: 1
@ -820,11 +825,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/network/mcpe/VerifyLoginTask.php
-
message: "#^Parameter \\#1 \\$buffer of method pocketmine\\\\nbt\\\\NBTStream\\:\\:read\\(\\) expects string, string\\|false given\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php
-
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 1
@ -860,6 +860,11 @@ parameters:
count: 1
path: ../../../src/pocketmine/network/mcpe/protocol/BiomeDefinitionListPacket.php
-
message: "#^Parameter \\#1 \\$str of method pocketmine\\\\utils\\\\BinaryStream\\:\\:put\\(\\) expects string, string\\|false given\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/protocol/ItemComponentPacket.php
-
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\LevelEventGenericPacket\\:\\:\\$eventData \\(string\\) does not accept string\\|false\\.$#"
count: 1
@ -870,26 +875,11 @@ parameters:
count: 1
path: ../../../src/pocketmine/network/mcpe/protocol/PositionTrackingDBServerBroadcastPacket.php
-
message: "#^Static property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\StartGamePacket\\:\\:\\$blockTableCache \\(string\\|null\\) does not accept string\\|false\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/protocol/StartGamePacket.php
-
message: "#^Parameter \\#1 \\$str of method pocketmine\\\\utils\\\\BinaryStream\\:\\:put\\(\\) expects string, string\\|false given\\.$#"
count: 2
path: ../../../src/pocketmine/network/mcpe/protocol/StartGamePacket.php
-
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/protocol/StartGamePacket.php
-
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\UpdateBlockPropertiesPacket\\:\\:\\$nbt \\(string\\) does not accept string\\|false\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/protocol/UpdateBlockPropertiesPacket.php
-
message: "#^Parameter \\#2 \\$resourcePatch of class pocketmine\\\\network\\\\mcpe\\\\protocol\\\\types\\\\SkinData constructor expects string, string\\|false given\\.$#"
count: 1
@ -985,6 +975,11 @@ parameters:
count: 3
path: ../../../src/pocketmine/tile/Spawnable.php
-
message: "#^Array \\(array\\<class\\-string\\<pocketmine\\\\tile\\\\Tile\\>, string\\>\\) does not accept string\\|false\\.$#"
count: 1
path: ../../../src/pocketmine/tile/Tile.php
-
message: "#^Parameter \\#2 \\$value of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setInt\\(\\) expects int, float\\|int given\\.$#"
count: 3

View File

@ -1440,21 +1440,6 @@ parameters:
count: 1
path: ../../../src/pocketmine/level/light/SkyLightUpdate.php
-
message: "#^Cannot call method getTag\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/NetworkBinaryStream.php
-
message: "#^Cannot call method removeTag\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/NetworkBinaryStream.php
-
message: "#^Cannot call method setTag\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#"
count: 1
path: ../../../src/pocketmine/network/mcpe/NetworkBinaryStream.php
-
message: "#^Argument of an invalid type array\\<pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\>\\|null supplied for foreach, only iterables are supported\\.$#"
count: 1

View File

@ -11,17 +11,19 @@ while getopts "t:" OPTION 2> /dev/null; do
done
#Run-the-server tests
DATA_DIR="test_data"
DATA_DIR="$(pwd)/test_data"
PLUGINS_DIR="$DATA_DIR/plugins"
rm -rf "$DATA_DIR"
rm PocketMine-MP.phar 2> /dev/null
mkdir "$DATA_DIR"
mkdir "$PLUGINS_DIR"
cd tests/plugins/DevTools
php -dphar.readonly=0 ./src/DevTools/ConsoleScript.php --make ./ --relative ./ --out ../../../DevTools.phar
php -dphar.readonly=0 ./src/DevTools/ConsoleScript.php --make ./ --relative ./ --out "$PLUGINS_DIR/DevTools.phar"
cd ../../..
composer make-server
php -dphar.readonly=0 ./build/server-phar.php ./PocketMine-MP.phar
if [ -f PocketMine-MP.phar ]; then
echo Server phar created successfully.
else
@ -29,9 +31,6 @@ else
exit 1
fi
mkdir "$DATA_DIR"
mkdir "$PLUGINS_DIR"
mv DevTools.phar "$PLUGINS_DIR"
cp -r tests/plugins/TesterPlugin "$PLUGINS_DIR"
echo -e "stop\n" | php PocketMine-MP.phar --no-wizard --disable-ansi --disable-readline --debug.level=2 --data="$DATA_DIR" --plugins="$PLUGINS_DIR" --anonymous-statistics.enabled=0 --settings.async-workers="$PM_WORKERS" --settings.enable-dev-builds=1