mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
252 Commits
Author | SHA1 | Date | |
---|---|---|---|
40a2211a5a | |||
0196aa21d7 | |||
833f3e574b | |||
a386ff8ce7 | |||
e6c3b0fc0d | |||
9568364277 | |||
73d4ff6b52 | |||
7e98aa1497 | |||
f00c69c513 | |||
50a4c42f3f | |||
6b61efcfc8 | |||
1a99938e4b | |||
a4d68fb32b | |||
5682cc8d53 | |||
4eb59c0372 | |||
3486db3f1b | |||
733d530ed0 | |||
c10ce84035 | |||
82d9e481d2 | |||
d27c7f7141 | |||
394c999710 | |||
6399dacba7 | |||
b6bbf655d7 | |||
3d2c018442 | |||
da8c54cf8b | |||
8e984a1bc3 | |||
124e60301a | |||
4d13c48e5c | |||
9159e8f002 | |||
f5aa461945 | |||
16817ff301 | |||
18863b1098 | |||
e3cffca34b | |||
d20d9fb689 | |||
7b1ae2a822 | |||
8ecf5e02b9 | |||
39c607cbd5 | |||
d867ffc60d | |||
c57eb26fd5 | |||
c35d91a104 | |||
9fc260fb1a | |||
73d0f799c2 | |||
ecb2e6e3af | |||
7b75b6928d | |||
753a8a6937 | |||
bc76b1cafe | |||
00e415fc79 | |||
dbbe1f2d5c | |||
740f0a2314 | |||
7fdfe947b0 | |||
b7c4379700 | |||
20b7418916 | |||
85521f5e7a | |||
f37ea6a203 | |||
abf8081ebc | |||
8594cb3e74 | |||
d155de35ed | |||
e37c8e3a5d | |||
e38c0c0fe1 | |||
ce27c03774 | |||
c4a8781b5c | |||
dbab8b5733 | |||
2b08bbc7b1 | |||
17037f5e9c | |||
fee3c17148 | |||
25e6cb74b3 | |||
8d2e59222e | |||
cd778661c2 | |||
c2afc05e7c | |||
9be95bf263 | |||
4b65e1cbe1 | |||
5caae37768 | |||
92e1811b06 | |||
293c2710d0 | |||
8a7017fd6b | |||
15f8886958 | |||
3226a9dc6a | |||
1a1e3ff63b | |||
ea413d0882 | |||
0890b5fc99 | |||
163ed225f2 | |||
a4a6d3e094 | |||
ecbf3e9722 | |||
47a959dace | |||
3968f85c82 | |||
d8188b807a | |||
8e68655fc7 | |||
6d109bfc6f | |||
b7a5a53c9d | |||
76bd0f452c | |||
363556e9b6 | |||
6f08853b29 | |||
42d8357821 | |||
f2ac63d235 | |||
5d17405b92 | |||
3dd53ad998 | |||
a303c4b294 | |||
fa56290bb4 | |||
01d6cbe9c3 | |||
f682c16740 | |||
74c09dc202 | |||
0917b67573 | |||
5cb0eafcb2 | |||
221e6db47d | |||
8d06018d81 | |||
600e16d9f6 | |||
4340349db7 | |||
6105198313 | |||
c96ba13c23 | |||
c8d0cb315b | |||
be9c413a9e | |||
3e4366b30d | |||
7f3460190b | |||
10d44292e1 | |||
70f81334ae | |||
ead572fab9 | |||
ef8e286277 | |||
ba5a5981a0 | |||
c428596009 | |||
a81d8dd6d5 | |||
5bcbef90ea | |||
1c67f094e3 | |||
99d350914e | |||
49a9e0a880 | |||
7b152def7d | |||
3eb78fb0ff | |||
38c759c86e | |||
314ce1d012 | |||
7fcd40df15 | |||
ba39327b28 | |||
8d2e3894ef | |||
8ee0fbccc5 | |||
fe4354959b | |||
19377c86a4 | |||
fb23aade34 | |||
57e9fe78a3 | |||
6b97281c58 | |||
da67a085fc | |||
e2fc7cdf88 | |||
635bb08fb9 | |||
308d9ce3a8 | |||
5a76c38363 | |||
54530da6c1 | |||
dd55a0bccd | |||
f03bd982b5 | |||
025cb73bf5 | |||
2a6ffb5aa9 | |||
d53b84386f | |||
a7ed933b37 | |||
29cc9283f8 | |||
e60962c31f | |||
f6b5301e17 | |||
73b923e3a1 | |||
d2e4eb40b3 | |||
6a31628e78 | |||
bfb1ad1327 | |||
dfa603c335 | |||
eef979db4c | |||
6166c90bfd | |||
35d1d0080a | |||
3bacb1a9cb | |||
898af49e97 | |||
3061eb4157 | |||
b8d1d8f212 | |||
8c6189775b | |||
932418b951 | |||
95812252d6 | |||
7dec912d15 | |||
dbd36d66ae | |||
40b4166a6e | |||
51d18ffb89 | |||
af3c7b7c76 | |||
3511ac010d | |||
cac3c356a5 | |||
07f19dd4a1 | |||
17a17c31f3 | |||
75742b487f | |||
4e9a2b6d8c | |||
4ea907ae1a | |||
8b912c1363 | |||
080209c469 | |||
5b11ddee35 | |||
3b7ded0ba3 | |||
c5d3e9be76 | |||
714f4dc023 | |||
a86bcd5110 | |||
7ffc477d76 | |||
4cb0b319c0 | |||
5ebe9859e9 | |||
cd2b60a860 | |||
5edff79f5f | |||
0c91d568b4 | |||
35fabc7765 | |||
b5a98a993f | |||
0840ba8067 | |||
274cf58ccf | |||
1bce5d0bc2 | |||
0d5d5e21a8 | |||
a145e18c1e | |||
d1ca779c1a | |||
abbb8bbf55 | |||
86c7870427 | |||
48080b7f90 | |||
b216fb8910 | |||
d3171d6a8e | |||
c063a4da29 | |||
cc79dfa64c | |||
d6b9950901 | |||
1815fe5b46 | |||
3e993250d8 | |||
1163ac1d7a | |||
9a51ba697a | |||
32ad9d0c1a | |||
3d840e969d | |||
d1b70bd400 | |||
29f002b32c | |||
da17ade575 | |||
f0c36f3413 | |||
77d8f133f1 | |||
43ebb23085 | |||
e198c8fa8b | |||
cc3285c8fe | |||
305c63ba4d | |||
acaa0e33b0 | |||
f63857deed | |||
eda3d9b5e4 | |||
c3872619cd | |||
39cc590829 | |||
f347345bb3 | |||
bb05cfb36c | |||
7d5c3c9b46 | |||
cff2d37add | |||
447477c5fb | |||
abdbb2bf0e | |||
783c13926f | |||
45329ddf67 | |||
20af789963 | |||
93cb9390e0 | |||
0aed7f86f5 | |||
bf44bd016d | |||
04e4a36653 | |||
7439e1971d | |||
b961b4e003 | |||
d9eac8fc0a | |||
aeeee5eb53 | |||
13994055d9 | |||
247875e3d5 | |||
ee60a7bc36 | |||
348c2a599b | |||
562b47a1e5 | |||
4e060bc13f | |||
2807f14fcd |
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ server.properties
|
||||
memory_dumps/*
|
||||
resource_packs/
|
||||
server.lock
|
||||
/phpstan.neon
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
|
@ -9,7 +9,7 @@ before_script:
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.0.4
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout 6ca019c58b4fa09ee2ff490f2444e34bef0773d0
|
||||
- git checkout 1b7da492b944146fa9680f6399bd9c6c6c6095e0
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
|
@ -25,6 +25,7 @@
|
||||
## Donate
|
||||
- Bitcoin Cash (BCH): `qq3r46hn6ljnhnqnfwxt5pg3g447eq9jhvw5ddfear`
|
||||
- Bitcoin (BTC): `171u8K9e4FtU6j3e5sqNoxKUgEw9qWQdRV`
|
||||
- Stellar Lumens (XLM): `GAAC5WZ33HCTE3BFJFZJXONMEIBNHFLBXM2HJVAZHXXPYA3HP5XPPS7T`
|
||||
- [Patreon](https://www.patreon.com/pocketminemp)
|
||||
|
||||
## Licensing information
|
||||
|
@ -76,6 +76,6 @@ system('git tag ' . $currentVer->getBaseVersion());
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true);
|
||||
system('git add "' . $versionInfoPath . '"');
|
||||
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
|
||||
echo "pushing changes in 10 seconds\n";
|
||||
sleep(10);
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
sleep(5);
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
|
Submodule build/php updated: ffc465f4f8...8308571448
20
changelogs/3.10.md
Normal file
20
changelogs/3.10.md
Normal file
@ -0,0 +1,20 @@
|
||||
**For Minecraft: Bedrock Edition 1.13.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.10.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.13.0
|
||||
- Removed compatibility with 1.12.0
|
||||
|
||||
## Note about skins
|
||||
PocketMine-MP **does not support skins made in the Charactor Creator** (known as Persona skins), due to technical changes which would require premature backwards compatibility breaks. The dev team has decided not to support Persona yet.
|
||||
These skins will be **replaced with a random solid-colour skin. This is not a bug.**
|
||||
Skins chosen from the Classic tab (classic skins) will continue to work as normal.
|
||||
|
||||
# 3.10.1
|
||||
- Fixed custom plugin-created skins being invisible when no geometry name was specified.
|
||||
- Updated RakLib to 0.12.6 to fix security bugs.
|
14
changelogs/3.11.md
Normal file
14
changelogs/3.11.md
Normal file
@ -0,0 +1,14 @@
|
||||
**For Minecraft: Bedrock Edition 1.14.0**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.11.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.14.0
|
||||
- Removed compatibility with 1.13.0
|
||||
|
||||
# 3.11.1
|
||||
- Fixed blocks with incorrect properties when placed or interacted with.
|
@ -74,3 +74,56 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Fixed some issues with multiple consecutive commas inside quotes in form responses.
|
||||
- Fixed server crash when the manifest json does not contain a json object in a resource pack.
|
||||
- Ender pearls no longer collide with blocks that do not have any collision boxes.
|
||||
|
||||
# 3.9.6
|
||||
- Updated Composer dependencies to their latest versions.
|
||||
- Prevent clients repeating the resource pack sequence. This fixes error spam with bugged 1.12 clients.
|
||||
- `Internet::simpleCurl()` now includes the PocketMine-MP version in the user-agent string.
|
||||
- Spawn protection is now disabled by default in the setup wizard.
|
||||
- Default difficulty is now NORMAL(2) instead of EASY(1).
|
||||
- Fixed crashing on corrupted world manifest and unsupported world formats.
|
||||
- Fixed `/transferserver` being usable without appropriate permissions.
|
||||
- `RegionLoader->removeChunk()` now writes the region header as appropriate.
|
||||
- Fixed performance issue when loading large regions (bug in header validation).
|
||||
- Fixed skin geometry being removed when the JSON contained comments.
|
||||
- Added new constants to `EventPacket`.
|
||||
- Added encode/decode for `StructureTemplateDataExportRequestPacket` and `StructureTemplateDataExportResponsePacket`.
|
||||
- Fixed broken type asserts in `LevelChunkPacket::withCache()` and `ClientCacheMissResponsePacket::create()`.
|
||||
- `types\CommandParameter` field `byte1` has been renamed to `flags`.
|
||||
- Cleaned up public interface of `AvailableCommandsPacket`, removing fields which exposed details of the encoding scheme.
|
||||
- Improved documentation for the following API methods:
|
||||
- `pocketmine\item\Item`:
|
||||
- `addCreativeItem()`
|
||||
- `removeCreativeItem()`
|
||||
- `clearCreativeItems()`
|
||||
- `pocketmine\level\Explosion`:
|
||||
- `explodeA()`
|
||||
- `explodeB()`
|
||||
- Fixed various cosmetic documentation inconsistencies in the core and dependencies.
|
||||
|
||||
# 3.9.7
|
||||
- Fixed a crash that could occur during timezone detection.
|
||||
- Squid no longer spin around constantly in enclosed spaces. Their performance impact is reduced.
|
||||
- Cleaned up the bootstrap file.
|
||||
|
||||
# 3.9.8
|
||||
- Added [PHPStan](https://github.com/phpstan/phpstan) configuration. PHPStan is now used on CI for automated QA, which should improve stability and quality going forward.
|
||||
- The following constants are now autoloaded when loading the Composer autoloader:
|
||||
- `pocketmine\NAME`
|
||||
- `pocketmine\BASE_VERSION`
|
||||
- `pocketmine\IS_DEVELOPMENT_BUILD`
|
||||
- `pocketmine\BUILD_NUMBER`
|
||||
- `INT32_MIN`
|
||||
- `INT32_MAX`
|
||||
- `INT32_MASK`
|
||||
- Fixed memory leaks and crashes caused by plugin use of `Player->showPlayer()` and `Entity->spawnTo()`.
|
||||
- Fixed crashes that could occur when tile classes were overridden with classes incompatible with the originals.
|
||||
- Fixed improper handling of non-Compound root NBT tags on network itemstack decoding.
|
||||
- Fixed paintings dropping multiple items when destroyed by block updates.
|
||||
- Fixed `var_dump()` not showing private and protected properties of `DataPacket` subclasses.
|
||||
- Fixed overloads with zero arguments being missing when decoding `AvailableCommandsPacket`.
|
||||
- `CraftingDataPacket` now retains the `cleanRecipes` field when decoding.
|
||||
- Fixed `Block->getMetadata()` returning null (non-iterable).
|
||||
- `PlayerChatEvent` documentation has been updated to specify that `CommandSender` recipients are accepted. This behaviour was already present in previous versions, but incorrectly documented.
|
||||
- Fixed various issues with PHPDoc comments reported by PHPStan.
|
||||
- Fixed various minor code nits reported by PHPStan.
|
||||
|
@ -36,7 +36,11 @@
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": ["src"]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src/pocketmine/GlobalConstants.php",
|
||||
"src/pocketmine/VersionInfo.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
64
composer.lock
generated
64
composer.lock
generated
@ -92,16 +92,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "8b3b1160679398387cb896fd5d06018413437dfa"
|
||||
"reference": "435f2ee265bce75ef1aa9563f9b60ff36d705e80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/8b3b1160679398387cb896fd5d06018413437dfa",
|
||||
"reference": "8b3b1160679398387cb896fd5d06018413437dfa",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/435f2ee265bce75ef1aa9563f9b60ff36d705e80",
|
||||
"reference": "435f2ee265bce75ef1aa9563f9b60ff36d705e80",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -119,23 +119,23 @@
|
||||
],
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.9",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.10",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2019-07-22T13:15:53+00:00"
|
||||
"time": "2019-10-21T14:40:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c"
|
||||
"reference": "68be8a79fd0169043ef514797c304517cb8a6071"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c",
|
||||
"reference": "b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/68be8a79fd0169043ef514797c304517cb8a6071",
|
||||
"reference": "68be8a79fd0169043ef514797c304517cb8a6071",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -153,23 +153,23 @@
|
||||
],
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.2",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.3",
|
||||
"issues": "https://github.com/pmmp/Math/issues"
|
||||
},
|
||||
"time": "2019-01-04T15:42:36+00:00"
|
||||
"time": "2019-10-21T14:35:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.10",
|
||||
"version": "0.2.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "2db27aebe7dc89772aaa8df53361eef801f60063"
|
||||
"reference": "b5777265329753b74dd40bb105eedabeefb98724"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/2db27aebe7dc89772aaa8df53361eef801f60063",
|
||||
"reference": "2db27aebe7dc89772aaa8df53361eef801f60063",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/b5777265329753b74dd40bb105eedabeefb98724",
|
||||
"reference": "b5777265329753b74dd40bb105eedabeefb98724",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -194,23 +194,23 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.10",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2019-07-22T15:22:23+00:00"
|
||||
"time": "2019-12-01T08:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.12.5",
|
||||
"version": "0.12.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "874db2d3c24117db2221c1e4550380478aeea852"
|
||||
"reference": "18450e01185e6064790bda563ac672e7141c6992"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/874db2d3c24117db2221c1e4550380478aeea852",
|
||||
"reference": "874db2d3c24117db2221c1e4550380478aeea852",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/18450e01185e6064790bda563ac672e7141c6992",
|
||||
"reference": "18450e01185e6064790bda563ac672e7141c6992",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -235,10 +235,10 @@
|
||||
],
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.6",
|
||||
"issues": "https://github.com/pmmp/RakLib/issues"
|
||||
},
|
||||
"time": "2019-07-22T14:38:20+00:00"
|
||||
"time": "2019-12-07T13:43:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
@ -276,23 +276,20 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49"
|
||||
"reference": "94d4df142fe837ba836e9348dd00209e4bdcc307"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/94d4df142fe837ba836e9348dd00209e4bdcc307",
|
||||
"reference": "94d4df142fe837ba836e9348dd00209e4bdcc307",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"exclude-from-classmap": [
|
||||
"stubs"
|
||||
],
|
||||
"classmap": [
|
||||
"./"
|
||||
]
|
||||
@ -302,9 +299,10 @@
|
||||
],
|
||||
"description": "Standard library files required by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/SPL/tree/master"
|
||||
"source": "https://github.com/pmmp/SPL/tree/0.3.3",
|
||||
"issues": "https://github.com/pmmp/SPL/issues"
|
||||
},
|
||||
"time": "2018-08-12T15:17:39+00:00"
|
||||
"time": "2019-10-28T11:41:20+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
124
phpstan.neon.dist
Normal file
124
phpstan.neon.dist
Normal file
@ -0,0 +1,124 @@
|
||||
includes:
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/optional-com-dotnet.neon
|
||||
- tests/phpstan/configs/optional-leveldb.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
- tests/phpstan/configs/pthreads-bugs.neon
|
||||
|
||||
parameters:
|
||||
level: 3
|
||||
autoload_files:
|
||||
- tests/phpstan/bootstrap.php
|
||||
- src/pocketmine/PocketMine.php
|
||||
paths:
|
||||
- src
|
||||
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Cannot instantiate interface pocketmine\\\\level\\\\format\\\\io\\\\LevelProvider\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/Server.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method pocketmine\\\\command\\\\CommandSender\\:\\:teleport\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/command/defaults/TeleportCommand.php
|
||||
# comment: "not actually possible, but high cost to fix warning"
|
||||
|
||||
-
|
||||
message: "#^Array \\(array\\<string\\>\\) does not accept pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 2
|
||||
path: src/pocketmine/entity/Entity.php
|
||||
|
||||
-
|
||||
message: "#^Invalid array key type pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/entity/Entity.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\EntityDeathEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\Living but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/EntityDeathEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\EntityShootBowEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\Living but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/EntityShootBowEvent.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\event\\\\entity\\\\EntityShootBowEvent\\:\\:\\$projectile \\(pocketmine\\\\entity\\\\projectile\\\\Projectile\\) does not accept pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/EntityShootBowEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ItemDespawnEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\object\\\\ItemEntity but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ItemDespawnEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ItemSpawnEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\object\\\\ItemEntity but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ItemSpawnEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ProjectileHitEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\projectile\\\\Projectile but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ProjectileHitEvent.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\event\\\\entity\\\\ProjectileLaunchEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\projectile\\\\Projectile but returns pocketmine\\\\entity\\\\Entity\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/event/entity/ProjectileLaunchEvent.php
|
||||
|
||||
-
|
||||
message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\hell\\\\Nether has an unused parameter \\$options\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/level/generator/hell/Nether.php
|
||||
|
||||
-
|
||||
message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\normal\\\\Normal has an unused parameter \\$options\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/level/generator/normal/Normal.php
|
||||
|
||||
-
|
||||
message: "#^Used constant pocketmine\\\\RESOURCE_PATH not found\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/network/mcpe/protocol/StartGamePacket.php
|
||||
|
||||
-
|
||||
message: "#^Constructor of class pocketmine\\\\scheduler\\\\TaskScheduler has an unused parameter \\$logger\\.$#"
|
||||
count: 1
|
||||
path: src/pocketmine/scheduler/TaskScheduler.php
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\COMPOSER_AUTOLOADER_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\DATA not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\GIT_COMMIT not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\PLUGIN_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\RESOURCE_PATH not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\START_TIME not found\\.$#"
|
||||
path: src
|
||||
|
||||
-
|
||||
message: "#^Constant pocketmine\\\\VERSION not found\\.$#"
|
||||
path: src
|
||||
|
@ -333,7 +333,7 @@ class CrashDump{
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . GIT_COMMIT);
|
||||
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
$this->addLine("PHP Version: " . phpversion());
|
||||
$this->addLine("Zend version: " . zend_version());
|
||||
|
30
src/pocketmine/GlobalConstants.php
Normal file
30
src/pocketmine/GlobalConstants.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// composer autoload doesn't use require_once and also pthreads can inherit things
|
||||
if(defined('pocketmine\_GLOBAL_CONSTANTS_INCLUDED')){
|
||||
return;
|
||||
}
|
||||
define('pocketmine\_GLOBAL_CONSTANTS_INCLUDED', true);
|
||||
|
||||
const INT32_MIN = -0x80000000;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
const INT32_MASK = 0xffffffff;
|
@ -146,6 +146,10 @@ 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\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinImage;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\network\mcpe\VerifyLoginTask;
|
||||
@ -261,6 +265,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var bool */
|
||||
public $loggedIn = false;
|
||||
|
||||
/** @var bool */
|
||||
private $resourcePacksDone = false;
|
||||
|
||||
/** @var bool */
|
||||
public $spawned = false;
|
||||
|
||||
@ -1109,9 +1116,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
protected function sendRespawnPacket(Vector3 $pos){
|
||||
protected function sendRespawnPacket(Vector3 $pos, int $respawnState = RespawnPacket::SEARCHING_FOR_SPAWN){
|
||||
$pk = new RespawnPacket();
|
||||
$pk->position = $pos->add(0, $this->baseOffset, 0);
|
||||
$pk->respawnState = $respawnState;
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
@ -1613,7 +1622,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$diff = $this->distanceSquared($newPos) / $tickDiff ** 2;
|
||||
|
||||
if($this->isSurvival() and !$revert and $diff > 0.0625){
|
||||
if($this->isSurvival() and $diff > 0.0625){
|
||||
$ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ));
|
||||
$ev->setCancelled($this->allowMovementCheats);
|
||||
|
||||
@ -1910,14 +1919,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->uuid = UUID::fromString($packet->clientUUID);
|
||||
$this->rawUUID = $this->uuid->toBinary();
|
||||
|
||||
$skin = new Skin(
|
||||
$animations = [];
|
||||
foreach($packet->clientData["AnimatedImageData"] as $animation){
|
||||
$animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], $animation["Image"]), $animation["Type"], $animation["Frames"]);
|
||||
}
|
||||
|
||||
$skinData = new SkinData(
|
||||
$packet->clientData["SkinId"],
|
||||
base64_decode($packet->clientData["SkinData"] ?? ""),
|
||||
base64_decode($packet->clientData["CapeData"] ?? ""),
|
||||
$packet->clientData["SkinGeometryName"] ?? "",
|
||||
base64_decode($packet->clientData["SkinGeometry"] ?? "")
|
||||
base64_decode($packet->clientData["SkinResourcePatch"] ?? ""),
|
||||
new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"])),
|
||||
$animations,
|
||||
new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")),
|
||||
base64_decode($packet->clientData["SkinGeometryData"] ?? ""),
|
||||
base64_decode($packet->clientData["AnimationData"] ?? ""),
|
||||
$packet->clientData["PremiumSkin"] ?? false,
|
||||
$packet->clientData["PersonaSkin"] ?? false,
|
||||
$packet->clientData["CapeOnClassicSkin"] ?? false,
|
||||
$packet->clientData["CapeId"] ?? ""
|
||||
);
|
||||
|
||||
$skin = SkinAdapterSingleton::get()->fromSkinData($skinData);
|
||||
|
||||
if(!$skin->isValid()){
|
||||
$this->close("", "disconnectionScreen.invalidSkin");
|
||||
|
||||
@ -2056,6 +2078,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{
|
||||
if($this->resourcePacksDone){
|
||||
return false;
|
||||
}
|
||||
switch($packet->status){
|
||||
case ResourcePackClientResponsePacket::STATUS_REFUSED:
|
||||
//TODO: add lang strings for this
|
||||
@ -2097,6 +2122,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
$this->resourcePacksDone = true;
|
||||
$this->completeLoginSequence();
|
||||
break;
|
||||
default:
|
||||
@ -2149,7 +2175,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->spawnZ = $spawnPosition->getFloorZ();
|
||||
$pk->hasAchievementsDisabled = true;
|
||||
$pk->time = $this->level->getTime();
|
||||
$pk->eduMode = false;
|
||||
$pk->eduEditionOffer = 0;
|
||||
$pk->rainLevel = 0; //TODO: implement these properly
|
||||
$pk->lightningLevel = 0;
|
||||
$pk->commandsEnabled = true;
|
||||
@ -2484,6 +2510,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
|
||||
if($this->isUsingItem()){
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
if($slot instanceof Consumable){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
$this->resetItemCooldown($slot);
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
}
|
||||
$directionVector = $this->getDirectionVector();
|
||||
|
||||
if($this->isCreative()){
|
||||
@ -2637,33 +2684,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME:
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
|
||||
if($slot instanceof Consumable){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->resetItemCooldown($slot);
|
||||
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2821,7 +2841,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->stopSleep();
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_RESPAWN:
|
||||
if(!$this->spawned or $this->isAlive() or !$this->isOnline()){
|
||||
if($this->isAlive()){
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2906,6 +2926,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRespawn(RespawnPacket $packet) : bool{
|
||||
if(!$this->isAlive() && $packet->respawnState === RespawnPacket::CLIENT_READY_TO_SPAWN){
|
||||
$this->sendRespawnPacket($this, RespawnPacket::READY_TO_SPAWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an item on the ground in front of the player. Returns if the item drop was successful.
|
||||
*
|
||||
@ -3049,6 +3078,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||
if($this->resourcePacksDone){
|
||||
return false;
|
||||
}
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pack = $manager->getPackById($packet->packId);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
@ -3795,7 +3827,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true);
|
||||
|
||||
$this->cursorInventory = new PlayerCursorInventory($this);
|
||||
$this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true);
|
||||
$this->addWindow($this->cursorInventory, ContainerIds::UI, true);
|
||||
|
||||
$this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL);
|
||||
|
||||
|
@ -21,11 +21,6 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {
|
||||
const INT32_MIN = -0x80000000;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
}
|
||||
|
||||
namespace pocketmine {
|
||||
|
||||
use pocketmine\utils\MainLogger;
|
||||
@ -57,18 +52,18 @@ namespace pocketmine {
|
||||
if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
|
||||
//If PHP version isn't high enough, anything below might break, so don't bother checking it.
|
||||
return [
|
||||
\pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . "."
|
||||
"PHP >= " . MIN_PHP_VERSION . " is required, but you have PHP " . PHP_VERSION . "."
|
||||
];
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
|
||||
if(PHP_INT_SIZE < 8){
|
||||
$messages[] = "Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
|
||||
$messages[] = "32-bit systems/PHP are no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
|
||||
}
|
||||
|
||||
if(php_sapi_name() !== "cli"){
|
||||
$messages[] = "You must run " . \pocketmine\NAME . " using the CLI.";
|
||||
$messages[] = "Only PHP CLI is supported.";
|
||||
}
|
||||
|
||||
$extensions = [
|
||||
@ -121,6 +116,29 @@ namespace pocketmine {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
function emit_performance_warnings(\Logger $logger){
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
|
||||
}
|
||||
if(!extension_loaded("pocketmine_chunkutils")){
|
||||
$logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance.");
|
||||
}
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
$logger->warning("Debugging assertions are enabled. This may degrade performance. To disable them, set `zend.assertions = -1` in php.ini.");
|
||||
}
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged installation detected. This will degrade autoloading speed and make startup times longer.");
|
||||
}
|
||||
}
|
||||
|
||||
function set_ini_entries(){
|
||||
ini_set("allow_url_fopen", '1');
|
||||
ini_set("display_errors", '1');
|
||||
ini_set("display_startup_errors", '1');
|
||||
ini_set("default_charset", "utf-8");
|
||||
ini_set('assert.exception', '1');
|
||||
}
|
||||
|
||||
function server(){
|
||||
if(!empty($messages = check_platform_dependencies())){
|
||||
echo PHP_EOL;
|
||||
@ -136,6 +154,7 @@ namespace pocketmine {
|
||||
unset($messages);
|
||||
|
||||
error_reporting(-1);
|
||||
set_ini_entries();
|
||||
|
||||
if(\Phar::running(true) !== ""){
|
||||
define('pocketmine\PATH', \Phar::running(true) . "/");
|
||||
@ -161,20 +180,27 @@ namespace pocketmine {
|
||||
|
||||
set_error_handler([Utils::class, 'errorExceptionHandler']);
|
||||
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader->register(false);
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
define('pocketmine\VERSION', $version->getFullVersion(true));
|
||||
|
||||
set_time_limit(0); //Who set it to 30 seconds?!?!
|
||||
$gitHash = str_repeat("00", 20);
|
||||
|
||||
ini_set("allow_url_fopen", '1');
|
||||
ini_set("display_errors", '1');
|
||||
ini_set("display_startup_errors", '1');
|
||||
ini_set("default_charset", "utf-8");
|
||||
if(\Phar::running(true) === ""){
|
||||
if(Utils::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){
|
||||
$gitHash = trim($out);
|
||||
if(Utils::execute("git diff --quiet") === 1 or Utils::execute("git diff --cached --quiet") === 1){ //Locally-modified
|
||||
$gitHash .= "-dirty";
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
}
|
||||
}
|
||||
|
||||
ini_set("memory_limit", '-1');
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
|
||||
define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
|
||||
|
||||
@ -187,8 +213,7 @@ namespace pocketmine {
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
}
|
||||
|
||||
define('pocketmine\LOCK_FILE_PATH', \pocketmine\DATA . 'server.lock');
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\LOCK_FILE_PATH, "a+b"));
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\DATA . 'server.lock', "a+b"));
|
||||
if(!flock(\pocketmine\LOCK_FILE, LOCK_EX | LOCK_NB)){
|
||||
//wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the
|
||||
//other server wrote its PID and released exclusive lock before we get our lock
|
||||
@ -222,42 +247,7 @@ namespace pocketmine {
|
||||
}
|
||||
unset($tzError);
|
||||
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning(PHP_EOL . PHP_EOL . PHP_EOL . "\tYou are running " . \pocketmine\NAME . " with xdebug enabled. This has a major impact on performance." . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
if(!extension_loaded("pocketmine_chunkutils")){
|
||||
$logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance.");
|
||||
}
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged " . \pocketmine\NAME . " installation detected. Consider using a phar in production for better performance.");
|
||||
}
|
||||
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
define('pocketmine\VERSION', $version->getFullVersion(true));
|
||||
|
||||
$gitHash = str_repeat("00", 20);
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
if(Utils::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){
|
||||
$gitHash = trim($out);
|
||||
if(Utils::execute("git diff --quiet") === 1 or Utils::execute("git diff --cached --quiet") === 1){ //Locally-modified
|
||||
$gitHash .= "-dirty";
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
}
|
||||
}
|
||||
|
||||
define('pocketmine\GIT_COMMIT', $gitHash);
|
||||
|
||||
|
||||
@define("INT32_MASK", is_int(0xffffffff) ? 0xffffffff : -1);
|
||||
@ini_set("opcache.mmap_base", bin2hex(random_bytes(8))); //Fix OPCache address errors
|
||||
emit_performance_warnings($logger);
|
||||
|
||||
$exitCode = 0;
|
||||
do{
|
||||
@ -272,6 +262,13 @@ namespace pocketmine {
|
||||
//TODO: move this to a Server field
|
||||
define('pocketmine\START_TIME', microtime(true));
|
||||
ThreadManager::init();
|
||||
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader->register(false);
|
||||
|
||||
new Server($autoloader, $logger, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
|
||||
|
||||
$logger->info("Stopping other threads");
|
||||
@ -281,9 +278,7 @@ namespace pocketmine {
|
||||
usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
echo "Some threads could not be stopped, performing a force-kill" . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
$logger->debug("Some threads could not be stopped, performing a force-kill");
|
||||
Utils::kill(getmypid());
|
||||
}
|
||||
}while(false);
|
||||
@ -296,5 +291,7 @@ namespace pocketmine {
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
\pocketmine\server();
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
\pocketmine\server();
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\RakLibInterface;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\query\QueryHandler;
|
||||
@ -113,6 +114,7 @@ use function asort;
|
||||
use function assert;
|
||||
use function base64_encode;
|
||||
use function class_exists;
|
||||
use function cli_set_process_title;
|
||||
use function count;
|
||||
use function define;
|
||||
use function explode;
|
||||
@ -127,7 +129,6 @@ use function getmypid;
|
||||
use function getopt;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
@ -227,7 +228,7 @@ class Server{
|
||||
* @var int
|
||||
*/
|
||||
private $tickCounter = 0;
|
||||
/** @var int */
|
||||
/** @var float */
|
||||
private $nextTick = 0;
|
||||
/** @var float[] */
|
||||
private $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
|
||||
@ -593,7 +594,7 @@ class Server{
|
||||
* @return int
|
||||
*/
|
||||
public function getDifficulty() : int{
|
||||
return $this->getConfigInt("difficulty", 1);
|
||||
return $this->getConfigInt("difficulty", Level::DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1101,11 +1102,17 @@ class Server{
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @var LevelProvider $provider
|
||||
* @see LevelProvider::__construct()
|
||||
*/
|
||||
$provider = new $providerClass($path);
|
||||
|
||||
try{
|
||||
/**
|
||||
* @var LevelProvider $provider
|
||||
* @see LevelProvider::__construct()
|
||||
*/
|
||||
$provider = new $providerClass($path);
|
||||
}catch(LevelException $e){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, $e->getMessage()]));
|
||||
return false;
|
||||
}
|
||||
try{
|
||||
GeneratorManager::getGenerator($provider->getGenerator(), true);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
@ -1438,7 +1445,7 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return string[][]
|
||||
*/
|
||||
public function getCommandAliases() : array{
|
||||
$section = $this->getProperty("aliases");
|
||||
@ -1529,7 +1536,7 @@ class Server{
|
||||
"force-gamemode" => false,
|
||||
"hardcore" => false,
|
||||
"pvp" => true,
|
||||
"difficulty" => 1,
|
||||
"difficulty" => Level::DIFFICULTY_NORMAL,
|
||||
"generator-settings" => "",
|
||||
"level-name" => "world",
|
||||
"level-seed" => "",
|
||||
@ -1559,12 +1566,6 @@ class Server{
|
||||
return;
|
||||
}
|
||||
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
$this->logger->warning("Debugging assertions are enabled, this may impact on performance. To disable them, set `zend.assertions = -1` in php.ini.");
|
||||
}
|
||||
|
||||
ini_set('assert.exception', '1');
|
||||
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->setLogDebug(\pocketmine\DEBUG > 1);
|
||||
}
|
||||
@ -1789,7 +1790,7 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param TextContainer|string $message
|
||||
* @param Player[] $recipients
|
||||
* @param CommandSender[] $recipients
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@ -1798,7 +1799,6 @@ class Server{
|
||||
return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS);
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendMessage($message);
|
||||
}
|
||||
@ -1823,7 +1823,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendTip($tip);
|
||||
}
|
||||
@ -1849,7 +1848,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendPopup($popup);
|
||||
}
|
||||
@ -1879,7 +1877,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Player[] $recipients */
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->addTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
|
||||
}
|
||||
@ -2399,7 +2396,7 @@ class Server{
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, $skin, $xboxUserId);
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, SkinAdapterSingleton::get()->toSkinData($skin), $xboxUserId);
|
||||
|
||||
$this->broadcastPacket($players ?? $this->playerList, $pk);
|
||||
}
|
||||
@ -2422,7 +2419,7 @@ class Server{
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
foreach($this->playerList as $player){
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($player->getSkin()), $player->getXuid());
|
||||
}
|
||||
|
||||
$p->dataPacket($pk);
|
||||
|
@ -21,7 +21,15 @@
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
// composer autoload doesn't use require_once and also pthreads can inherit things
|
||||
// TODO: drop this file and use a final class with constants
|
||||
if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
return;
|
||||
}
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.9.5";
|
||||
const BASE_VERSION = "3.11.1";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
@ -69,7 +69,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
/** @var int|null */
|
||||
protected $itemId;
|
||||
|
||||
/** @var AxisAlignedBB */
|
||||
/** @var AxisAlignedBB|null */
|
||||
protected $boundingBox = null;
|
||||
|
||||
|
||||
@ -763,7 +763,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
return $this->level->getBlockMetadata()->getMetadata($this, $metadataKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
public function hasMetadata(string $metadataKey) : bool{
|
||||
|
@ -80,6 +80,9 @@ class BurningFurnace extends Solid{
|
||||
$furnace = $this->getLevel()->getTile($this);
|
||||
if(!($furnace instanceof TileFurnace)){
|
||||
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this));
|
||||
if(!($furnace instanceof TileFurnace)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$furnace->canOpenWith($item->getCustomName())){
|
||||
|
@ -109,6 +109,9 @@ class Chest extends Transparent{
|
||||
$chest = $t;
|
||||
}else{
|
||||
$chest = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this));
|
||||
if(!($chest instanceof TileChest)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
|
@ -47,6 +47,9 @@ class ItemFrame extends Flowable{
|
||||
$tile = $this->level->getTile($this);
|
||||
if(!($tile instanceof TileItemFrame)){
|
||||
$tile = Tile::createTile(Tile::ITEM_FRAME, $this->getLevel(), TileItemFrame::createNBT($this));
|
||||
if(!($tile instanceof TileItemFrame)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if($tile->hasItem()){
|
||||
|
@ -55,7 +55,7 @@ abstract class Command{
|
||||
*/
|
||||
private $activeAliases = [];
|
||||
|
||||
/** @var CommandMap */
|
||||
/** @var CommandMap|null */
|
||||
private $commandMap = null;
|
||||
|
||||
/** @var string */
|
||||
|
@ -141,6 +141,7 @@ class CommandReader extends Thread{
|
||||
case self::TYPE_STREAM:
|
||||
//stream_select doesn't work on piped streams for some reason
|
||||
$r = [self::$stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
|
@ -60,7 +60,6 @@ class GamemodeCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $sender;
|
||||
if(isset($args[1])){
|
||||
$target = $sender->getServer()->getPlayer($args[1]);
|
||||
if($target === null){
|
||||
@ -68,7 +67,9 @@ class GamemodeCommand extends VanillaCommand{
|
||||
|
||||
return true;
|
||||
}
|
||||
}elseif(!($sender instanceof Player)){
|
||||
}elseif($sender instanceof Player){
|
||||
$target = $sender;
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,10 @@ class TransferServerCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(count($args) < 1){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}elseif(!($sender instanceof Player)){
|
||||
|
@ -318,7 +318,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
/** @var string[] */
|
||||
private static $knownEntities = [];
|
||||
/** @var string[][] */
|
||||
private static $saveNames = [];
|
||||
@ -460,8 +460,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
/** @var EntityDamageEvent|null */
|
||||
protected $lastDamageCause = null;
|
||||
|
||||
/** @var Block[] */
|
||||
protected $blocksAround = [];
|
||||
/** @var Block[]|null */
|
||||
protected $blocksAround = null;
|
||||
|
||||
/** @var float|null */
|
||||
public $lastX = null;
|
||||
@ -2048,7 +2048,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param Player $player
|
||||
*/
|
||||
public function spawnTo(Player $player) : void{
|
||||
if(!isset($this->hasSpawned[$player->getLoaderId()])){
|
||||
if(!isset($this->hasSpawned[$player->getLoaderId()]) and $this->chunk !== null and isset($player->usedChunks[Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())])){
|
||||
$this->hasSpawned[$player->getLoaderId()] = $player;
|
||||
|
||||
$this->sendSpawnPacket($player);
|
||||
|
@ -53,6 +53,7 @@ use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\UUID;
|
||||
use function array_filter;
|
||||
@ -182,7 +183,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
public function sendSkin(?array $targets = null) : void{
|
||||
$pk = new PlayerSkinPacket();
|
||||
$pk->uuid = $this->getUniqueId();
|
||||
$pk->skin = $this->skin;
|
||||
$pk->skin = SkinAdapterSingleton::get()->toSkinData($this->skin);
|
||||
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
|
||||
}
|
||||
|
||||
@ -587,6 +588,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
return (int) min(100, 7 * $this->getXpLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PlayerInventory
|
||||
*/
|
||||
public function getInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
@ -847,7 +851,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)];
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))];
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use Ahc\Json\Comment as CommentedJsonDecoder;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function strlen;
|
||||
|
||||
@ -129,7 +129,7 @@ class Skin{
|
||||
*/
|
||||
public function debloatGeometryData() : void{
|
||||
if($this->geometryData !== ""){
|
||||
$this->geometryData = (string) json_encode(json_decode($this->geometryData));
|
||||
$this->geometryData = (string) json_encode((new CommentedJsonDecoder())->decode($this->geometryData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class Squid extends WaterAnimal{
|
||||
public $width = 0.95;
|
||||
public $height = 0.95;
|
||||
|
||||
/** @var Vector3 */
|
||||
/** @var Vector3|null */
|
||||
public $swimDirection = null;
|
||||
public $swimSpeed = 0.1;
|
||||
|
||||
@ -82,7 +82,7 @@ class Squid extends WaterAnimal{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(++$this->switchDirectionTicker === 100 or $this->isCollided){
|
||||
if(++$this->switchDirectionTicker === 100){
|
||||
$this->switchDirectionTicker = 0;
|
||||
if(mt_rand(0, 100) < 50){
|
||||
$this->swimDirection = null;
|
||||
|
@ -34,7 +34,6 @@ use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use function abs;
|
||||
use function floor;
|
||||
use function get_class;
|
||||
|
||||
class FallingBlock extends Entity{
|
||||
|
@ -90,6 +90,9 @@ class Painting extends Entity{
|
||||
}
|
||||
|
||||
public function kill() : void{
|
||||
if(!$this->isAlive()){
|
||||
return;
|
||||
}
|
||||
parent::kill();
|
||||
|
||||
$drops = true;
|
||||
|
@ -56,7 +56,6 @@ abstract class Event{
|
||||
throw new \BadMethodCallException(get_class($this) . " is not Cancellable");
|
||||
}
|
||||
|
||||
/** @var Event $this */
|
||||
return $this->isCancelled;
|
||||
}
|
||||
|
||||
@ -70,7 +69,6 @@ abstract class Event{
|
||||
throw new \BadMethodCallException(get_class($this) . " is not Cancellable");
|
||||
}
|
||||
|
||||
/** @var Event $this */
|
||||
$this->isCancelled = $value;
|
||||
}
|
||||
|
||||
@ -78,8 +76,6 @@ abstract class Event{
|
||||
* Calls event handlers registered for this event.
|
||||
*
|
||||
* @throws \RuntimeException if event call recursion reaches the max depth limit
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function call() : void{
|
||||
if(self::$eventCallDepth >= self::MAX_EVENT_CALL_DEPTH){
|
||||
|
@ -23,10 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Called when a player chats something
|
||||
@ -39,15 +41,15 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
protected $format;
|
||||
|
||||
/**
|
||||
* @var Player[]
|
||||
* @var CommandSender[]
|
||||
*/
|
||||
protected $recipients = [];
|
||||
|
||||
/**
|
||||
* @param Player $player
|
||||
* @param string $message
|
||||
* @param string $format
|
||||
* @param Player[] $recipients
|
||||
* @param Player $player
|
||||
* @param string $message
|
||||
* @param string $format
|
||||
* @param CommandSender[] $recipients
|
||||
*/
|
||||
public function __construct(Player $player, string $message, string $format = "chat.type.text", array $recipients = null){
|
||||
$this->player = $player;
|
||||
@ -56,7 +58,11 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
$this->format = $format;
|
||||
|
||||
if($recipients === null){
|
||||
$this->recipients = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS);
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS) as $permissible){
|
||||
if($permissible instanceof CommandSender){
|
||||
$this->recipients[spl_object_id($permissible)] = $permissible;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->recipients = $recipients;
|
||||
}
|
||||
@ -100,14 +106,14 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player[]
|
||||
* @return CommandSender[]
|
||||
*/
|
||||
public function getRecipients() : array{
|
||||
return $this->recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[] $recipients
|
||||
* @param CommandSender[] $recipients
|
||||
*/
|
||||
public function setRecipients(array $recipients) : void{
|
||||
$this->recipients = $recipients;
|
||||
|
@ -39,15 +39,15 @@ class PlayerCreationEvent extends Event{
|
||||
/** @var int */
|
||||
private $port;
|
||||
|
||||
/** @var Player::class */
|
||||
/** @var string */
|
||||
private $baseClass;
|
||||
/** @var Player::class */
|
||||
/** @var string */
|
||||
private $playerClass;
|
||||
|
||||
/**
|
||||
* @param SourceInterface $interface
|
||||
* @param Player::class $baseClass
|
||||
* @param Player::class $playerClass
|
||||
* @param string $baseClass
|
||||
* @param string $playerClass
|
||||
* @param string $address
|
||||
* @param int $port
|
||||
*/
|
||||
@ -91,14 +91,14 @@ class PlayerCreationEvent extends Event{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player::class
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseClass(){
|
||||
return $this->baseClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player::class $class
|
||||
* @param string $class
|
||||
*/
|
||||
public function setBaseClass($class){
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
@ -109,14 +109,14 @@ class PlayerCreationEvent extends Event{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player::class
|
||||
* @return string
|
||||
*/
|
||||
public function getPlayerClass(){
|
||||
return $this->playerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player::class $class
|
||||
* @param string $class
|
||||
*/
|
||||
public function setPlayerClass($class){
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
|
@ -47,7 +47,7 @@ abstract class BaseInventory implements Inventory{
|
||||
/** @var string */
|
||||
protected $title;
|
||||
/** @var \SplFixedArray|Item[] */
|
||||
protected $slots = [];
|
||||
protected $slots;
|
||||
/** @var Player[] */
|
||||
protected $viewers = [];
|
||||
/** @var InventoryEventProcessor */
|
||||
|
@ -43,7 +43,7 @@ class CraftingManager{
|
||||
/** @var FurnaceRecipe[] */
|
||||
protected $furnaceRecipes = [];
|
||||
|
||||
/** @var BatchPacket */
|
||||
/** @var BatchPacket|null */
|
||||
private $craftingDataCache;
|
||||
|
||||
public function __construct(){
|
||||
|
@ -65,6 +65,7 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
* @param int $iterations
|
||||
*
|
||||
* @return int
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
protected function matchRecipeItems(array $txItems, array $recipeItems, bool $wildcards, int $iterations = 0) : int{
|
||||
if(empty($recipeItems)){
|
||||
|
@ -137,6 +137,10 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all previously added items from the creative menu.
|
||||
* Note: Players who are already online when this is called will not see this change.
|
||||
*/
|
||||
public static function clearCreativeItems(){
|
||||
Item::$creative = [];
|
||||
}
|
||||
@ -145,10 +149,22 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
return Item::$creative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the creative menu.
|
||||
* Note: Players who are already online when this is called will not see this change.
|
||||
*
|
||||
* @param Item $item
|
||||
*/
|
||||
public static function addCreativeItem(Item $item){
|
||||
Item::$creative[] = clone $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the creative menu.
|
||||
* Note: Players who are already online when this is called will not see this change.
|
||||
*
|
||||
* @param Item $item
|
||||
*/
|
||||
public static function removeCreativeItem(Item $item){
|
||||
$index = self::getCreativeItemIndex($item);
|
||||
if($index !== -1){
|
||||
|
@ -48,7 +48,7 @@ class Shears extends Tool{
|
||||
}
|
||||
|
||||
public function onDestroyBlock(Block $block) : bool{
|
||||
if($block->getHardness() === 0 or $block->isCompatibleWithTool($this)){
|
||||
if($block->getHardness() === 0.0 or $block->isCompatibleWithTool($this)){
|
||||
return $this->applyDamage(1);
|
||||
}
|
||||
return false;
|
||||
|
@ -186,16 +186,18 @@ class WritableBook extends Item{
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
public function getPages() : array{
|
||||
$pages = $this->getNamedTag()->getListTag(self::TAG_PAGES);
|
||||
if($pages === null){
|
||||
return [];
|
||||
}
|
||||
/** @var CompoundTag[] $pages */
|
||||
$pages = $this->getPagesTag()->getValue();
|
||||
|
||||
return $pages->getValue();
|
||||
return $pages;
|
||||
}
|
||||
|
||||
protected function getPagesTag() : ListTag{
|
||||
return $this->getNamedTag()->getListTag(self::TAG_PAGES) ?? new ListTag(self::TAG_PAGES, [], NBT::TAG_Compound);
|
||||
$pagesTag = $this->getNamedTag()->getListTag(self::TAG_PAGES);
|
||||
if($pagesTag !== null and $pagesTag->getTagType() === NBT::TAG_Compound){
|
||||
return $pagesTag;
|
||||
}
|
||||
return new ListTag(self::TAG_PAGES, [], NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +94,7 @@ class Enchantment{
|
||||
public const SLOT_ELYTRA = 0x4000;
|
||||
public const SLOT_TRIDENT = 0x8000;
|
||||
|
||||
/** @var Enchantment[] */
|
||||
/** @var \SplFixedArray|Enchantment[] */
|
||||
protected static $enchantments;
|
||||
|
||||
public static function init() : void{
|
||||
|
@ -38,7 +38,6 @@ use pocketmine\level\particle\HugeExplodeSeedParticle;
|
||||
use pocketmine\level\utils\SubChunkIteratorManager;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\ExplodePacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\tile\Chest;
|
||||
use pocketmine\tile\Container;
|
||||
@ -89,6 +88,9 @@ class Explosion{
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates which blocks will be destroyed by this explosion. If explodeB() is called without calling this, no blocks
|
||||
* will be destroyed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function explodeA() : bool{
|
||||
@ -148,6 +150,12 @@ class Explosion{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the explosion's effects on the world. This includes destroying blocks (if any), harming and knocking back entities,
|
||||
* and creating sounds and particles.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function explodeB() : bool{
|
||||
$send = [];
|
||||
$updateBlocks = [];
|
||||
@ -251,12 +259,6 @@ class Explosion{
|
||||
$send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z);
|
||||
}
|
||||
|
||||
$pk = new ExplodePacket();
|
||||
$pk->position = $this->source->asVector3();
|
||||
$pk->radius = $this->size;
|
||||
$pk->records = $send;
|
||||
$this->level->broadcastPacketToViewers($source, $pk);
|
||||
|
||||
$this->level->addParticle(new HugeExplodeSeedParticle($source));
|
||||
$this->level->broadcastLevelSoundEvent($source, LevelSoundEventPacket::SOUND_EXPLODE);
|
||||
|
||||
|
@ -265,7 +265,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelTimings */
|
||||
public $timings;
|
||||
|
||||
/** @var int */
|
||||
/** @var float */
|
||||
public $tickRateTime = 0;
|
||||
/**
|
||||
* @deprecated
|
||||
|
@ -29,7 +29,7 @@ use function assert;
|
||||
|
||||
class Position extends Vector3{
|
||||
|
||||
/** @var Level */
|
||||
/** @var Level|null */
|
||||
public $level = null;
|
||||
|
||||
/**
|
||||
|
@ -694,50 +694,48 @@ class Chunk{
|
||||
public function initChunk(Level $level){
|
||||
if(!$this->isInit){
|
||||
$changed = false;
|
||||
if($this->NBTentities !== null){
|
||||
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
try{
|
||||
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
|
||||
if(!($entity instanceof Entity)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}catch(\Throwable $t){
|
||||
$level->getServer()->getLogger()->logException($t);
|
||||
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
try{
|
||||
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
|
||||
if(!($entity instanceof Entity)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}catch(\Throwable $t){
|
||||
$level->getServer()->getLogger()->logException($t);
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$level->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag(Tile::TAG_ID, StringTag::class)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Tile::createTile($nbt->getString(Tile::TAG_ID), $level, $nbt) === null){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
|
||||
$this->NBTentities = null;
|
||||
$this->NBTtiles = null;
|
||||
}
|
||||
$this->NBTentities = [];
|
||||
$level->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!$nbt->hasTag(Tile::TAG_ID, StringTag::class)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Tile::createTile($nbt->getString(Tile::TAG_ID), $level, $nbt) === null){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->NBTtiles = [];
|
||||
$level->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
|
||||
$this->hasChanged = $changed;
|
||||
|
||||
|
@ -33,7 +33,12 @@ use pocketmine\level\generator\GeneratorManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\LevelException;
|
||||
use pocketmine\nbt\LittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\{ByteTag, CompoundTag, FloatTag, IntTag, LongTag, StringTag};
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\LongTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
@ -129,7 +134,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
$version = $this->levelData->getInt("StorageVersion", INT32_MAX, true);
|
||||
if($version > self::CURRENT_STORAGE_VERSION){
|
||||
throw new LevelException("Specified LevelDB world format version ($version) is not supported by " . \pocketmine\NAME);
|
||||
throw new LevelException("Specified LevelDB world format version ($version) is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,10 @@ use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function array_fill;
|
||||
use function ceil;
|
||||
use function chr;
|
||||
use function fclose;
|
||||
use function fgetc;
|
||||
use function feof;
|
||||
use function file_exists;
|
||||
use function filesize;
|
||||
use function fopen;
|
||||
@ -40,6 +39,7 @@ use function fseek;
|
||||
use function ftruncate;
|
||||
use function fwrite;
|
||||
use function is_resource;
|
||||
use function max;
|
||||
use function ord;
|
||||
use function pack;
|
||||
use function str_pad;
|
||||
@ -60,6 +60,8 @@ class RegionLoader{
|
||||
public const MAX_SECTOR_LENGTH = 255 << 12; //255 sectors (~0.996 MiB)
|
||||
public const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps
|
||||
|
||||
private const FIRST_SECTOR = 2; //location table occupies 0 and 1
|
||||
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
|
||||
/** @var int */
|
||||
@ -71,8 +73,8 @@ class RegionLoader{
|
||||
/** @var resource */
|
||||
protected $filePointer;
|
||||
/** @var int */
|
||||
protected $lastSector;
|
||||
/** @var int[][] [offset in sectors, chunk size in sectors, timestamp] */
|
||||
protected $nextSector = self::FIRST_SECTOR;
|
||||
/** @var RegionLocationTableEntry[] */
|
||||
protected $locationTable = [];
|
||||
/** @var int */
|
||||
public $lastUsed = 0;
|
||||
@ -83,6 +85,9 @@ class RegionLoader{
|
||||
$this->filePath = $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
public function open(){
|
||||
$exists = file_exists($this->filePath);
|
||||
if(!$exists){
|
||||
@ -111,7 +116,7 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
protected function isChunkGenerated(int $index) : bool{
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
return !$this->locationTable[$index]->isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,23 +136,25 @@ class RegionLoader{
|
||||
return null;
|
||||
}
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fseek($this->filePointer, $this->locationTable[$index]->getFirstSector() << 12);
|
||||
|
||||
$prefix = fread($this->filePointer, 4);
|
||||
if($prefix === false or strlen($prefix) !== 4){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (unexpected end of file reading length prefix)");
|
||||
}
|
||||
$length = Binary::readInt($prefix);
|
||||
|
||||
if($length <= 0 or $length > self::MAX_SECTOR_LENGTH){ //Not yet generated / corrupted
|
||||
if($length >= self::MAX_SECTOR_LENGTH){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (sector count $length larger than max " . self::MAX_SECTOR_LENGTH . ")");
|
||||
}
|
||||
if($length <= 0){ //TODO: if we reached here, the locationTable probably needs updating
|
||||
return null;
|
||||
}
|
||||
if($length > self::MAX_SECTOR_LENGTH){ //corrupted
|
||||
throw new CorruptedChunkException("Length for chunk x=$x,z=$z ($length) is larger than maximum " . self::MAX_SECTOR_LENGTH);
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index][1] << 12) . " sectors, got $length sectors)");
|
||||
$this->locationTable[$index][1] = $length >> 12;
|
||||
if($length > ($this->locationTable[$index]->getSectorCount() << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index]->getSectorCount() << 12) . " sectors, got $length sectors)");
|
||||
$old = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($old->getFirstSector(), $length >> 12, time());
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
@ -190,26 +197,22 @@ class RegionLoader{
|
||||
if($length + 4 > self::MAX_SECTOR_LENGTH){
|
||||
throw new ChunkException("Chunk is too big! " . ($length + 4) . " > " . self::MAX_SECTOR_LENGTH);
|
||||
}
|
||||
$sectors = (int) ceil(($length + 4) / 4096);
|
||||
|
||||
$newSize = (int) ceil(($length + 4) / 4096);
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$indexChanged = false;
|
||||
if($this->locationTable[$index][1] < $sectors){
|
||||
$this->locationTable[$index][0] = $this->lastSector + 1;
|
||||
$this->lastSector += $sectors; //The GC will clean this shift "later"
|
||||
$indexChanged = true;
|
||||
}elseif($this->locationTable[$index][1] != $sectors){
|
||||
$indexChanged = true;
|
||||
$offset = $this->locationTable[$index]->getFirstSector();
|
||||
|
||||
if($this->locationTable[$index]->getSectorCount() < $newSize){
|
||||
$offset = $this->nextSector;
|
||||
}
|
||||
|
||||
$this->locationTable[$index][1] = $sectors;
|
||||
$this->locationTable[$index][2] = time();
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($offset, $newSize, time());
|
||||
$this->bumpNextFreeSector($this->locationTable[$index]);
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
fseek($this->filePointer, $offset << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $newSize << 12, "\x00", STR_PAD_RIGHT));
|
||||
|
||||
if($indexChanged){
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,8 +223,8 @@ class RegionLoader{
|
||||
*/
|
||||
public function removeChunk(int $x, int $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry(0, 0, 0);
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,9 +266,11 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
protected function loadLocationTable(){
|
||||
fseek($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
|
||||
$headerRaw = fread($this->filePointer, self::REGION_HEADER_LENGTH);
|
||||
if(($len = strlen($headerRaw)) !== self::REGION_HEADER_LENGTH){
|
||||
@ -273,43 +278,64 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
$data = unpack("N*", $headerRaw);
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = $data[$i + 1];
|
||||
$offset = $index >> 8;
|
||||
if($offset !== 0){
|
||||
self::getChunkCoords($i, $x, $z);
|
||||
$fileOffset = $offset << 12;
|
||||
$timestamp = $data[$i + 1025];
|
||||
|
||||
fseek($this->filePointer, $fileOffset);
|
||||
if(fgetc($this->filePointer) === false){ //Try and read from the location
|
||||
throw new CorruptedRegionException("Region file location offset x=$x,z=$z points to invalid file location $fileOffset");
|
||||
}elseif(isset($usedOffsets[$offset])){
|
||||
self::getChunkCoords($usedOffsets[$offset], $existingX, $existingZ);
|
||||
throw new CorruptedRegionException("Found two chunk offsets (chunk1: x=$existingX,z=$existingZ, chunk2: x=$x,z=$z) pointing to the file location $fileOffset");
|
||||
}else{
|
||||
$usedOffsets[$offset] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
$this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]];
|
||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||
if($offset === 0){
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0);
|
||||
}else{
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry($offset, $index & 0xff, $timestamp);
|
||||
$this->bumpNextFreeSector($this->locationTable[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkLocationTableValidity();
|
||||
|
||||
fseek($this->filePointer, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
private function checkLocationTableValidity() : void{
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$entry = $this->locationTable[$i];
|
||||
if($entry->isNull()){
|
||||
continue;
|
||||
}
|
||||
|
||||
self::getChunkCoords($i, $x, $z);
|
||||
$offset = $entry->getFirstSector();
|
||||
$fileOffset = $offset << 12;
|
||||
|
||||
//TODO: more validity checks
|
||||
|
||||
fseek($this->filePointer, $fileOffset);
|
||||
if(feof($this->filePointer)){
|
||||
throw new CorruptedRegionException("Region file location offset x=$x,z=$z points to invalid file location $fileOffset");
|
||||
}
|
||||
if(isset($usedOffsets[$offset])){
|
||||
self::getChunkCoords($usedOffsets[$offset], $existingX, $existingZ);
|
||||
throw new CorruptedRegionException("Found two chunk offsets (chunk1: x=$existingX,z=$existingZ, chunk2: x=$x,z=$z) pointing to the file location $fileOffset");
|
||||
}
|
||||
$usedOffsets[$offset] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
private function writeLocationTable(){
|
||||
$write = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$write[] = (($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
|
||||
$write[] = (($this->locationTable[$i]->getFirstSector() << 8) | $this->locationTable[$i]->getSectorCount());
|
||||
}
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$write[] = $this->locationTable[$i][2];
|
||||
$write[] = $this->locationTable[$i]->getTimestamp();
|
||||
}
|
||||
fseek($this->filePointer, 0);
|
||||
fwrite($this->filePointer, pack("N*", ...$write), 4096 * 2);
|
||||
@ -317,16 +343,21 @@ class RegionLoader{
|
||||
|
||||
protected function writeLocationIndex($index){
|
||||
fseek($this->filePointer, $index << 2);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index]->getFirstSector() << 8) | $this->locationTable[$index]->getSectorCount()), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index]->getTimestamp()), 4);
|
||||
}
|
||||
|
||||
protected function createBlank(){
|
||||
fseek($this->filePointer, 0);
|
||||
ftruncate($this->filePointer, 8192); // this fills the file with the null byte
|
||||
$this->lastSector = 1;
|
||||
$this->locationTable = array_fill(0, 1024, [0, 0, 0]);
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private function bumpNextFreeSector(RegionLocationTableEntry $entry) : void{
|
||||
$this->nextSector = max($this->nextSector, $entry->getLastSector()) + 1;
|
||||
}
|
||||
|
||||
public function getX() : int{
|
||||
|
@ -0,0 +1,98 @@
|
||||
<?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\level\format\io\region;
|
||||
|
||||
use function range;
|
||||
|
||||
class RegionLocationTableEntry{
|
||||
|
||||
/** @var int */
|
||||
private $firstSector;
|
||||
/** @var int */
|
||||
private $sectorCount;
|
||||
/** @var int */
|
||||
private $timestamp;
|
||||
|
||||
/**
|
||||
* @param int $firstSector
|
||||
* @param int $sectorCount
|
||||
* @param int $timestamp
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(int $firstSector, int $sectorCount, int $timestamp){
|
||||
if($firstSector < 0){
|
||||
throw new \InvalidArgumentException("Start sector must be positive, got $firstSector");
|
||||
}
|
||||
$this->firstSector = $firstSector;
|
||||
if($sectorCount < 0 or $sectorCount > 255){
|
||||
throw new \InvalidArgumentException("Sector count must be in range 0...255, got $sectorCount");
|
||||
}
|
||||
$this->sectorCount = $sectorCount;
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getFirstSector() : int{
|
||||
return $this->firstSector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastSector() : int{
|
||||
return $this->firstSector + $this->sectorCount - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of sector offsets reserved by this chunk.
|
||||
* @return int[]
|
||||
*/
|
||||
public function getUsedSectors() : array{
|
||||
return range($this->getFirstSector(), $this->getLastSector());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSectorCount() : int{
|
||||
return $this->sectorCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimestamp() : int{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNull() : bool{
|
||||
return $this->firstSector === 0 or $this->sectorCount === 0;
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ abstract class LightUpdate{
|
||||
/** @var ChunkManager */
|
||||
protected $level;
|
||||
|
||||
/** @var int[] blockhash => new light level */
|
||||
/** @var int[][] blockhash => [x, y, z, new light level] */
|
||||
protected $updateNodes = [];
|
||||
|
||||
/** @var \SplQueue */
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\utils\UUID;
|
||||
use function str_repeat;
|
||||
|
||||
@ -96,7 +97,7 @@ class FloatingTextParticle extends Particle{
|
||||
|
||||
$add = new PlayerListPacket();
|
||||
$add->type = PlayerListPacket::TYPE_ADD;
|
||||
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))];
|
||||
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin("Standard_Custom", str_repeat("\x00", 8192))))];
|
||||
$p[] = $add;
|
||||
|
||||
$pk = new AddPlayerPacket();
|
||||
|
@ -29,7 +29,7 @@ namespace pocketmine\metadata;
|
||||
use pocketmine\plugin\Plugin;
|
||||
|
||||
abstract class MetadataStore{
|
||||
/** @var \SplObjectStorage[] */
|
||||
/** @var \SplObjectStorage[]|MetadataValue[][] */
|
||||
private $metadataMap;
|
||||
|
||||
/**
|
||||
@ -100,7 +100,7 @@ abstract class MetadataStore{
|
||||
* @param Plugin $owningPlugin
|
||||
*/
|
||||
public function invalidateAll(Plugin $owningPlugin){
|
||||
/** @var MetadataValue[] $values */
|
||||
/** @var \SplObjectStorage|MetadataValue[] $values */
|
||||
foreach($this->metadataMap as $values){
|
||||
if(isset($values[$owningPlugin])){
|
||||
$values[$owningPlugin]->invalidate();
|
||||
|
@ -27,6 +27,7 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
@ -37,6 +38,10 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinImage;
|
||||
use pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\UUID;
|
||||
use function count;
|
||||
@ -73,6 +78,66 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putLInt($uuid->getPart(2));
|
||||
}
|
||||
|
||||
public function getSkin() : SkinData{
|
||||
$skinId = $this->getString();
|
||||
$skinResourcePatch = $this->getString();
|
||||
$skinData = $this->getSkinImage();
|
||||
$animationCount = $this->getLInt();
|
||||
$animations = [];
|
||||
for($i = 0; $i < $animationCount; ++$i){
|
||||
$animations[] = new SkinAnimation(
|
||||
$skinImage = $this->getSkinImage(),
|
||||
$animationType = $this->getLInt(),
|
||||
$animationFrames = $this->getLFloat()
|
||||
);
|
||||
}
|
||||
$capeData = $this->getSkinImage();
|
||||
$geometryData = $this->getString();
|
||||
$animationData = $this->getString();
|
||||
$premium = $this->getBool();
|
||||
$persona = $this->getBool();
|
||||
$capeOnClassic = $this->getBool();
|
||||
$capeId = $this->getString();
|
||||
$fullSkinId = $this->getString();
|
||||
|
||||
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId);
|
||||
}
|
||||
|
||||
public function putSkin(SkinData $skin){
|
||||
$this->putString($skin->getSkinId());
|
||||
$this->putString($skin->getResourcePatch());
|
||||
$this->putSkinImage($skin->getSkinImage());
|
||||
$this->putLInt(count($skin->getAnimations()));
|
||||
foreach($skin->getAnimations() as $animation){
|
||||
$this->putSkinImage($animation->getImage());
|
||||
$this->putLInt($animation->getType());
|
||||
$this->putLFloat($animation->getFrames());
|
||||
}
|
||||
$this->putSkinImage($skin->getCapeImage());
|
||||
$this->putString($skin->getGeometryData());
|
||||
$this->putString($skin->getAnimationData());
|
||||
$this->putBool($skin->isPremium());
|
||||
$this->putBool($skin->isPersona());
|
||||
$this->putBool($skin->isPersonaCapeOnClassic());
|
||||
$this->putString($skin->getCapeId());
|
||||
|
||||
//this has to be unique or the client will do stupid things
|
||||
$this->putString(UUID::fromRandom()->toString()); //full skin ID
|
||||
}
|
||||
|
||||
private function getSkinImage() : SkinImage{
|
||||
$width = $this->getLInt();
|
||||
$height = $this->getLInt();
|
||||
$data = $this->getString();
|
||||
return new SkinImage($height, $width, $data);
|
||||
}
|
||||
|
||||
private function putSkinImage(SkinImage $image) : void{
|
||||
$this->putLInt($image->getWidth());
|
||||
$this->putLInt($image->getHeight());
|
||||
$this->putString($image->getData());
|
||||
}
|
||||
|
||||
public function getSlot() : Item{
|
||||
$id = $this->getVarInt();
|
||||
if($id === 0){
|
||||
@ -92,7 +157,11 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
if($c !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT count $c");
|
||||
}
|
||||
$nbt = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
$decodedNBT = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
if(!($decodedNBT instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Unexpected root tag type for itemstack");
|
||||
}
|
||||
$nbt = $decodedNBT;
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
@ -377,7 +446,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an EntityUniqueID
|
||||
* Writes an EntityRuntimeID
|
||||
*
|
||||
* @param int $eid
|
||||
*/
|
||||
@ -592,4 +661,40 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putVarLong($data->varlong1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getStructureSettings() : StructureSettings{
|
||||
$result = new StructureSettings();
|
||||
|
||||
$result->paletteName = $this->getString();
|
||||
|
||||
$result->ignoreEntities = $this->getBool();
|
||||
$result->ignoreBlocks = $this->getBool();
|
||||
|
||||
$this->getBlockPosition($result->structureSizeX, $result->structureSizeY, $result->structureSizeZ);
|
||||
$this->getBlockPosition($result->structureOffsetX, $result->structureOffsetY, $result->structureOffsetZ);
|
||||
|
||||
$result->lastTouchedByPlayerID = $this->getEntityUniqueId();
|
||||
$result->rotation = $this->getByte();
|
||||
$result->mirror = $this->getByte();
|
||||
$result->integrityValue = $this->getFloat();
|
||||
$result->integritySeed = $this->getInt();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function putStructureSettings(StructureSettings $structureSettings) : void{
|
||||
$this->putString($structureSettings->paletteName);
|
||||
|
||||
$this->putBool($structureSettings->ignoreEntities);
|
||||
$this->putBool($structureSettings->ignoreBlocks);
|
||||
|
||||
$this->putBlockPosition($structureSettings->structureSizeX, $structureSettings->structureSizeY, $structureSettings->structureSizeZ);
|
||||
$this->putBlockPosition($structureSettings->structureOffsetX, $structureSettings->structureOffsetY, $structureSettings->structureOffsetZ);
|
||||
|
||||
$this->putEntityUniqueId($structureSettings->lastTouchedByPlayerID);
|
||||
$this->putByte($structureSettings->rotation);
|
||||
$this->putByte($structureSettings->mirror);
|
||||
$this->putFloat($structureSettings->integrityValue);
|
||||
$this->putInt($structureSettings->integritySeed);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\network\mcpe\protocol\AddPaintingPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AnvilDamagePacket;
|
||||
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
@ -54,6 +55,7 @@ use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket;
|
||||
use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\CommandOutputPacket;
|
||||
use pocketmine\network\mcpe\protocol\CommandRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
|
||||
@ -61,8 +63,9 @@ use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\EducationSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmotePacket;
|
||||
use pocketmine\network\mcpe\protocol\EventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ExplodePacket;
|
||||
use pocketmine\network\mcpe\protocol\GameRulesChangedPacket;
|
||||
use pocketmine\network\mcpe\protocol\GuiDataPickItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\HurtArmorPacket;
|
||||
@ -90,12 +93,15 @@ use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\MultiplayerSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
|
||||
use pocketmine\network\mcpe\protocol\NpcRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\OnScreenTextureAnimationPacket;
|
||||
use pocketmine\network\mcpe\protocol\PhotoTransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
@ -134,6 +140,7 @@ use pocketmine\network\mcpe\protocol\SetScoreboardIdentityPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetScorePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTimePacket;
|
||||
use pocketmine\network\mcpe\protocol\SettingsCommandPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTitlePacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowProfilePacket;
|
||||
@ -144,11 +151,12 @@ use pocketmine\network\mcpe\protocol\SpawnParticleEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\StartGamePacket;
|
||||
use pocketmine\network\mcpe\protocol\StopSoundPacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureTemplateDataExportRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureTemplateDataExportResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureTemplateDataRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureTemplateDataResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\TickSyncPacket;
|
||||
use pocketmine\network\mcpe\protocol\TransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
@ -247,7 +255,7 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleExplode(ExplodePacket $packet) : bool{
|
||||
public function handleTickSync(TickSyncPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -679,11 +687,11 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleStructureTemplateDataExportRequest(StructureTemplateDataExportRequestPacket $packet) : bool{
|
||||
public function handleStructureTemplateDataRequest(StructureTemplateDataRequestPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleStructureTemplateDataExportResponse(StructureTemplateDataExportResponsePacket $packet) : bool{
|
||||
public function handleStructureTemplateDataResponse(StructureTemplateDataResponsePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -698,4 +706,36 @@ abstract class NetworkSession{
|
||||
public function handleClientCacheMissResponse(ClientCacheMissResponsePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleEducationSettings(EducationSettingsPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleEmote(EmotePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleMultiplayerSettings(MultiplayerSettingsPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleSettingsCommand(SettingsCommandPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleAnvilDamage(AnvilDamagePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleCompletedUsingItem(CompletedUsingItemPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleNetworkSettings(NetworkSettingsPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -59,12 +59,14 @@ use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\RespawnPacket;
|
||||
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
@ -181,6 +183,10 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
return $this->player->handleAnimate($packet);
|
||||
}
|
||||
|
||||
public function handleRespawn(RespawnPacket $packet) : bool{
|
||||
return $this->player->handleRespawn($packet);
|
||||
}
|
||||
|
||||
public function handleContainerClose(ContainerClosePacket $packet) : bool{
|
||||
return $this->player->handleContainerClose($packet);
|
||||
}
|
||||
@ -248,7 +254,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
}
|
||||
|
||||
public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{
|
||||
return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName);
|
||||
return $this->player->changeSkin(SkinAdapterSingleton::get()->fromSkinData($packet->skin), $packet->newSkinName, $packet->oldSkinName);
|
||||
}
|
||||
|
||||
public function handleBookEdit(BookEditPacket $packet) : bool{
|
||||
|
@ -37,6 +37,7 @@ use function openssl_verify;
|
||||
use function ord;
|
||||
use function str_split;
|
||||
use function strlen;
|
||||
use function strtr;
|
||||
use function time;
|
||||
use function wordwrap;
|
||||
use const OPENSSL_ALGO_SHA384;
|
||||
|
@ -74,6 +74,8 @@ class AddPlayerPacket extends DataPacket{
|
||||
|
||||
/** @var string */
|
||||
public $deviceId = ""; //TODO: fill player's device ID (???)
|
||||
/** @var int */
|
||||
public $buildPlatform = -1;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->uuid = $this->getUUID();
|
||||
@ -103,6 +105,7 @@ class AddPlayerPacket extends DataPacket{
|
||||
}
|
||||
|
||||
$this->deviceId = $this->getString();
|
||||
$this->buildPlatform = $this->getLInt();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -133,6 +136,7 @@ class AddPlayerPacket extends DataPacket{
|
||||
}
|
||||
|
||||
$this->putString($this->deviceId);
|
||||
$this->putLInt($this->buildPlatform);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
78
src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php
Normal file
78
src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php
Normal 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\network\mcpe\NetworkSession;
|
||||
|
||||
class AnvilDamagePacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::ANVIL_DAMAGE_PACKET;
|
||||
|
||||
/** @var int */
|
||||
private $x;
|
||||
/** @var int */
|
||||
private $y;
|
||||
/** @var int */
|
||||
private $z;
|
||||
/** @var int */
|
||||
private $damageAmount;
|
||||
|
||||
public static function create(int $x, int $y, int $z, int $damageAmount) : self{
|
||||
$result = new self;
|
||||
[$result->x, $result->y, $result->z] = [$x, $y, $z];
|
||||
$result->damageAmount = $damageAmount;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getDamageAmount() : int{
|
||||
return $this->damageAmount;
|
||||
}
|
||||
|
||||
public function getX() : int{
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getY() : int{
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
public function getZ() : int{
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->damageAmount = $this->getByte();
|
||||
$this->getBlockPosition($this->x, $this->y, $this->z);
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putByte($this->damageAmount);
|
||||
$this->putBlockPosition($this->x, $this->y, $this->z);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleAnvilDamage($this);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -28,12 +28,10 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandData;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandEnumConstraint;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandParameter;
|
||||
use function array_flip;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
use function array_search;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function dechex;
|
||||
|
||||
@ -60,17 +58,17 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
|
||||
public const ARG_TYPE_FILEPATH = 0x0e;
|
||||
|
||||
public const ARG_TYPE_STRING = 0x1b;
|
||||
public const ARG_TYPE_STRING = 0x1d;
|
||||
|
||||
public const ARG_TYPE_POSITION = 0x1d;
|
||||
public const ARG_TYPE_POSITION = 0x25;
|
||||
|
||||
public const ARG_TYPE_MESSAGE = 0x20;
|
||||
public const ARG_TYPE_MESSAGE = 0x29;
|
||||
|
||||
public const ARG_TYPE_RAWTEXT = 0x22;
|
||||
public const ARG_TYPE_RAWTEXT = 0x2b;
|
||||
|
||||
public const ARG_TYPE_JSON = 0x25;
|
||||
public const ARG_TYPE_JSON = 0x2f;
|
||||
|
||||
public const ARG_TYPE_COMMAND = 0x2c;
|
||||
public const ARG_TYPE_COMMAND = 0x36;
|
||||
|
||||
/**
|
||||
* Enums are a little different: they are composed as follows:
|
||||
@ -83,29 +81,9 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
*/
|
||||
public const ARG_FLAG_POSTFIX = 0x1000000;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* A list of every single enum value for every single command in the packet, including alias names.
|
||||
*/
|
||||
public $enumValues = [];
|
||||
/** @var int */
|
||||
private $enumValuesCount = 0;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* A list of argument postfixes. Used for the /xp command's <int>L.
|
||||
*/
|
||||
public $postfixes = [];
|
||||
|
||||
/**
|
||||
* @var CommandEnum[]
|
||||
* List of command enums, from command aliases to argument enums.
|
||||
*/
|
||||
public $enums = [];
|
||||
/**
|
||||
* @var int[] string => int map of enum name to index
|
||||
*/
|
||||
private $enumMap = [];
|
||||
public const HARDCODED_ENUM_NAMES = [
|
||||
"CommandName" => true
|
||||
];
|
||||
|
||||
/**
|
||||
* @var CommandData[]
|
||||
@ -113,6 +91,13 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
*/
|
||||
public $commandData = [];
|
||||
|
||||
/**
|
||||
* @var CommandEnum[]
|
||||
* List of enums which aren't directly referenced by any vanilla command.
|
||||
* This is used for the `CommandName` enum, which is a magic enum used by the `command` argument type.
|
||||
*/
|
||||
public $hardcodedEnums = [];
|
||||
|
||||
/**
|
||||
* @var CommandEnum[]
|
||||
* List of dynamic command enums, also referred to as "soft" enums. These can by dynamically updated mid-game
|
||||
@ -120,39 +105,67 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
*/
|
||||
public $softEnums = [];
|
||||
|
||||
/**
|
||||
* @var CommandEnumConstraint[]
|
||||
* List of constraints for enum members. Used to constrain gamerules that can bechanged in nocheats mode and more.
|
||||
*/
|
||||
public $enumConstraints = [];
|
||||
|
||||
protected function decodePayload(){
|
||||
for($i = 0, $this->enumValuesCount = $this->getUnsignedVarInt(); $i < $this->enumValuesCount; ++$i){
|
||||
$this->enumValues[] = $this->getString();
|
||||
/** @var string[] $enumValues */
|
||||
$enumValues = [];
|
||||
for($i = 0, $enumValuesCount = $this->getUnsignedVarInt(); $i < $enumValuesCount; ++$i){
|
||||
$enumValues[] = $this->getString();
|
||||
}
|
||||
|
||||
/** @var string[] $postfixes */
|
||||
$postfixes = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$postfixes[] = $this->getString();
|
||||
}
|
||||
|
||||
/** @var CommandEnum[] $enums */
|
||||
$enums = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$enums[] = $enum = $this->getEnum($enumValues);
|
||||
if(isset(self::HARDCODED_ENUM_NAMES[$enum->enumName])){
|
||||
$this->hardcodedEnums[] = $enum;
|
||||
}
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->postfixes[] = $this->getString();
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->enums[] = $this->getEnum();
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->commandData[] = $this->getCommandData();
|
||||
$this->commandData[] = $this->getCommandData($enums, $postfixes);
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->softEnums[] = $this->getSoftEnum();
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->enumConstraints[] = $this->getEnumConstraint($enums, $enumValues);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEnum() : CommandEnum{
|
||||
/**
|
||||
* @param string[] $enumValueList
|
||||
*
|
||||
* @return CommandEnum
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
protected function getEnum(array $enumValueList) : CommandEnum{
|
||||
$retval = new CommandEnum();
|
||||
$retval->enumName = $this->getString();
|
||||
|
||||
$listSize = count($enumValueList);
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$index = $this->getEnumValueIndex();
|
||||
if(!isset($this->enumValues[$index])){
|
||||
$index = $this->getEnumValueIndex($listSize);
|
||||
if(!isset($enumValueList[$index])){
|
||||
throw new \UnexpectedValueException("Invalid enum value index $index");
|
||||
}
|
||||
//Get the enum value from the initial pile of mess
|
||||
$retval->enumValues[] = $this->enumValues[$index];
|
||||
$retval->enumValues[] = $enumValueList[$index];
|
||||
}
|
||||
|
||||
return $retval;
|
||||
@ -170,17 +183,21 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
return $retval;
|
||||
}
|
||||
|
||||
protected function putEnum(CommandEnum $enum){
|
||||
/**
|
||||
* @param CommandEnum $enum
|
||||
* @param int[] $enumValueMap string enum name -> int index
|
||||
*/
|
||||
protected function putEnum(CommandEnum $enum, array $enumValueMap) : void{
|
||||
$this->putString($enum->enumName);
|
||||
|
||||
$this->putUnsignedVarInt(count($enum->enumValues));
|
||||
$listSize = count($enumValueMap);
|
||||
foreach($enum->enumValues as $value){
|
||||
//Dumb bruteforce search. I hate this packet.
|
||||
$index = array_search($value, $this->enumValues, true);
|
||||
if($index === false){
|
||||
$index = $enumValueMap[$value] ?? -1;
|
||||
if($index === -1){
|
||||
throw new \InvalidStateException("Enum value '$value' not found");
|
||||
}
|
||||
$this->putEnumValueIndex($index);
|
||||
$this->putEnumValueIndex($index, $listSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,51 +210,110 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEnumValueIndex() : int{
|
||||
if($this->enumValuesCount < 256){
|
||||
/**
|
||||
* @param int $valueCount
|
||||
*
|
||||
* @return int
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
protected function getEnumValueIndex(int $valueCount) : int{
|
||||
if($valueCount < 256){
|
||||
return $this->getByte();
|
||||
}elseif($this->enumValuesCount < 65536){
|
||||
}elseif($valueCount < 65536){
|
||||
return $this->getLShort();
|
||||
}else{
|
||||
return $this->getLInt();
|
||||
}
|
||||
}
|
||||
|
||||
protected function putEnumValueIndex(int $index){
|
||||
if($this->enumValuesCount < 256){
|
||||
protected function putEnumValueIndex(int $index, int $valueCount) : void{
|
||||
if($valueCount < 256){
|
||||
$this->putByte($index);
|
||||
}elseif($this->enumValuesCount < 65536){
|
||||
}elseif($valueCount < 65536){
|
||||
$this->putLShort($index);
|
||||
}else{
|
||||
$this->putLInt($index);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCommandData() : CommandData{
|
||||
/**
|
||||
* @param CommandEnum[] $enums
|
||||
* @param string[] $enumValues
|
||||
*
|
||||
* @return CommandEnumConstraint
|
||||
*/
|
||||
protected function getEnumConstraint(array $enums, array $enumValues) : CommandEnumConstraint{
|
||||
//wtf, what was wrong with an offset inside the enum? :(
|
||||
$valueIndex = $this->getLInt();
|
||||
if(!isset($enumValues[$valueIndex])){
|
||||
throw new \UnexpectedValueException("Enum constraint refers to unknown enum value index $valueIndex");
|
||||
}
|
||||
$enumIndex = $this->getLInt();
|
||||
if(!isset($enums[$enumIndex])){
|
||||
throw new \UnexpectedValueException("Enum constraint refers to unknown enum index $enumIndex");
|
||||
}
|
||||
$enum = $enums[$enumIndex];
|
||||
$valueOffset = array_search($enumValues[$valueIndex], $enum->enumValues, true);
|
||||
if($valueOffset === false){
|
||||
throw new \UnexpectedValueException("Value \"" . $enumValues[$valueIndex] . "\" does not belong to enum \"$enum->enumName\"");
|
||||
}
|
||||
|
||||
$constraintIds = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$constraintIds[] = $this->getByte();
|
||||
}
|
||||
|
||||
return new CommandEnumConstraint($enum, $valueOffset, $constraintIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommandEnumConstraint $constraint
|
||||
* @param int[] $enumIndexes string enum name -> int index
|
||||
* @param int[] $enumValueIndexes string value -> int index
|
||||
*/
|
||||
protected function putEnumConstraint(CommandEnumConstraint $constraint, array $enumIndexes, array $enumValueIndexes) : void{
|
||||
$this->putLInt($enumValueIndexes[$constraint->getAffectedValue()]);
|
||||
$this->putLInt($enumIndexes[$constraint->getEnum()->enumName]);
|
||||
$this->putUnsignedVarInt(count($constraint->getConstraints()));
|
||||
foreach($constraint->getConstraints() as $v){
|
||||
$this->putByte($v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommandEnum[] $enums
|
||||
* @param string[] $postfixes
|
||||
*
|
||||
* @return CommandData
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
protected function getCommandData(array $enums, array $postfixes) : CommandData{
|
||||
$retval = new CommandData();
|
||||
$retval->commandName = $this->getString();
|
||||
$retval->commandDescription = $this->getString();
|
||||
$retval->flags = $this->getByte();
|
||||
$retval->permission = $this->getByte();
|
||||
$retval->aliases = $this->enums[$this->getLInt()] ?? null;
|
||||
$retval->aliases = $enums[$this->getLInt()] ?? null;
|
||||
|
||||
for($overloadIndex = 0, $overloadCount = $this->getUnsignedVarInt(); $overloadIndex < $overloadCount; ++$overloadIndex){
|
||||
$retval->overloads[$overloadIndex] = [];
|
||||
for($paramIndex = 0, $paramCount = $this->getUnsignedVarInt(); $paramIndex < $paramCount; ++$paramIndex){
|
||||
$parameter = new CommandParameter();
|
||||
$parameter->paramName = $this->getString();
|
||||
$parameter->paramType = $this->getLInt();
|
||||
$parameter->isOptional = $this->getBool();
|
||||
$parameter->byte1 = $this->getByte();
|
||||
$parameter->flags = $this->getByte();
|
||||
|
||||
if($parameter->paramType & self::ARG_FLAG_ENUM){
|
||||
$index = ($parameter->paramType & 0xffff);
|
||||
$parameter->enum = $this->enums[$index] ?? null;
|
||||
$parameter->enum = $enums[$index] ?? null;
|
||||
if($parameter->enum === null){
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: expected enum at $index, but got none");
|
||||
}
|
||||
}elseif($parameter->paramType & self::ARG_FLAG_POSTFIX){
|
||||
$index = ($parameter->paramType & 0xffff);
|
||||
$parameter->postfix = $this->postfixes[$index] ?? null;
|
||||
$parameter->postfix = $postfixes[$index] ?? null;
|
||||
if($parameter->postfix === null){
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: expected postfix at $index, but got none");
|
||||
}
|
||||
@ -252,14 +328,19 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
return $retval;
|
||||
}
|
||||
|
||||
protected function putCommandData(CommandData $data){
|
||||
/**
|
||||
* @param CommandData $data
|
||||
* @param int[] $enumIndexes string enum name -> int index
|
||||
* @param int[] $postfixIndexes
|
||||
*/
|
||||
protected function putCommandData(CommandData $data, array $enumIndexes, array $postfixIndexes) : void{
|
||||
$this->putString($data->commandName);
|
||||
$this->putString($data->commandDescription);
|
||||
$this->putByte($data->flags);
|
||||
$this->putByte($data->permission);
|
||||
|
||||
if($data->aliases !== null){
|
||||
$this->putLInt($this->enumMap[$data->aliases->enumName] ?? -1);
|
||||
$this->putLInt($enumIndexes[$data->aliases->enumName] ?? -1);
|
||||
}else{
|
||||
$this->putLInt(-1);
|
||||
}
|
||||
@ -272,10 +353,10 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
$this->putString($parameter->paramName);
|
||||
|
||||
if($parameter->enum !== null){
|
||||
$type = self::ARG_FLAG_ENUM | self::ARG_FLAG_VALID | ($this->enumMap[$parameter->enum->enumName] ?? -1);
|
||||
$type = self::ARG_FLAG_ENUM | self::ARG_FLAG_VALID | ($enumIndexes[$parameter->enum->enumName] ?? -1);
|
||||
}elseif($parameter->postfix !== null){
|
||||
$key = array_search($parameter->postfix, $this->postfixes, true);
|
||||
if($key === false){
|
||||
$key = $postfixIndexes[$parameter->postfix] ?? -1;
|
||||
if($key === -1){
|
||||
throw new \InvalidStateException("Postfix '$parameter->postfix' not in postfixes array");
|
||||
}
|
||||
$type = self::ARG_FLAG_POSTFIX | $key;
|
||||
@ -285,12 +366,12 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
|
||||
$this->putLInt($type);
|
||||
$this->putBool($parameter->isOptional);
|
||||
$this->putByte($parameter->byte1);
|
||||
$this->putByte($parameter->flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function argTypeToString(int $argtype) : string{
|
||||
private function argTypeToString(int $argtype, array $postfixes) : string{
|
||||
if($argtype & self::ARG_FLAG_VALID){
|
||||
if($argtype & self::ARG_FLAG_ENUM){
|
||||
return "stringenum (" . ($argtype & 0xffff) . ")";
|
||||
@ -319,7 +400,7 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
return "command";
|
||||
}
|
||||
}elseif($argtype & self::ARG_FLAG_POSTFIX){
|
||||
$postfix = $this->postfixes[$argtype & 0xffff];
|
||||
$postfix = $postfixes[$argtype & 0xffff];
|
||||
|
||||
return "int (postfix $postfix)";
|
||||
}else{
|
||||
@ -330,66 +411,74 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$enumValuesMap = [];
|
||||
$postfixesMap = [];
|
||||
$enumMap = [];
|
||||
/** @var int[] $enumValueIndexes */
|
||||
$enumValueIndexes = [];
|
||||
/** @var int[] $postfixIndexes */
|
||||
$postfixIndexes = [];
|
||||
/** @var int[] $enumIndexes */
|
||||
$enumIndexes = [];
|
||||
/** @var CommandEnum[] $enums */
|
||||
$enums = [];
|
||||
|
||||
$addEnumFn = static function(CommandEnum $enum) use (&$enums, &$enumIndexes, &$enumValueIndexes){
|
||||
if(!isset($enumIndexes[$enum->enumName])){
|
||||
$enums[$enumIndexes[$enum->enumName] = count($enumIndexes)] = $enum;
|
||||
}
|
||||
foreach($enum->enumValues as $str){
|
||||
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes);
|
||||
}
|
||||
};
|
||||
foreach($this->hardcodedEnums as $enum){
|
||||
$addEnumFn($enum);
|
||||
}
|
||||
foreach($this->commandData as $commandData){
|
||||
if($commandData->aliases !== null){
|
||||
$enumMap[$commandData->aliases->enumName] = $commandData->aliases;
|
||||
|
||||
foreach($commandData->aliases->enumValues as $str){
|
||||
$enumValuesMap[$str] = true;
|
||||
}
|
||||
$addEnumFn($commandData->aliases);
|
||||
}
|
||||
|
||||
/** @var CommandParameter[] $overload */
|
||||
foreach($commandData->overloads as $overload){
|
||||
/**
|
||||
* @var CommandParameter[] $overload
|
||||
* @var CommandParameter $parameter
|
||||
*/
|
||||
/** @var CommandParameter $parameter */
|
||||
foreach($overload as $parameter){
|
||||
if($parameter->enum !== null){
|
||||
$enumMap[$parameter->enum->enumName] = $parameter->enum;
|
||||
foreach($parameter->enum->enumValues as $str){
|
||||
$enumValuesMap[$str] = true;
|
||||
}
|
||||
$addEnumFn($parameter->enum);
|
||||
}
|
||||
|
||||
if($parameter->postfix !== null){
|
||||
$postfixesMap[$parameter->postfix] = true;
|
||||
$postfixIndexes[$parameter->postfix] = $postfixIndexes[$parameter->postfix] ?? count($postfixIndexes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->enumValues = array_map('\strval', array_keys($enumValuesMap)); //stupid PHP key casting D:
|
||||
$this->putUnsignedVarInt($this->enumValuesCount = count($this->enumValues));
|
||||
foreach($this->enumValues as $enumValue){
|
||||
$this->putString($enumValue);
|
||||
$this->putUnsignedVarInt(count($enumValueIndexes));
|
||||
foreach($enumValueIndexes as $enumValue => $index){
|
||||
$this->putString((string) $enumValue); //stupid PHP key casting D:
|
||||
}
|
||||
|
||||
$this->postfixes = array_map('\strval', array_keys($postfixesMap));
|
||||
$this->putUnsignedVarInt(count($this->postfixes));
|
||||
foreach($this->postfixes as $postfix){
|
||||
$this->putString($postfix);
|
||||
$this->putUnsignedVarInt(count($postfixIndexes));
|
||||
foreach($postfixIndexes as $postfix => $index){
|
||||
$this->putString((string) $postfix); //stupid PHP key casting D:
|
||||
}
|
||||
|
||||
$this->enums = array_values($enumMap);
|
||||
$this->enumMap = array_flip(array_keys($enumMap));
|
||||
$this->putUnsignedVarInt(count($this->enums));
|
||||
foreach($this->enums as $enum){
|
||||
$this->putEnum($enum);
|
||||
$this->putUnsignedVarInt(count($enums));
|
||||
foreach($enums as $enum){
|
||||
$this->putEnum($enum, $enumValueIndexes);
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt(count($this->commandData));
|
||||
foreach($this->commandData as $data){
|
||||
$this->putCommandData($data);
|
||||
$this->putCommandData($data, $enumIndexes, $postfixIndexes);
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt(count($this->softEnums));
|
||||
foreach($this->softEnums as $enum){
|
||||
$this->putSoftEnum($enum);
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt(count($this->enumConstraints));
|
||||
foreach($this->enumConstraints as $constraint){
|
||||
$this->putEnumConstraint($constraint, $enumIndexes, $enumValueIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -26,11 +26,14 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use function file_get_contents;
|
||||
|
||||
class BiomeDefinitionListPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::BIOME_DEFINITION_LIST_PACKET;
|
||||
|
||||
public const HARDCODED_NBT_BLOB = "CgAKDWJhbWJvb19qdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTYmFtYm9vX2p1bmdsZV9oaWxscwUIZG93bmZhbGxmZmY/BQt0ZW1wZXJhdHVyZTMzcz8ACgViZWFjaAUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACgxiaXJjaF9mb3Jlc3QFCGRvd25mYWxsmpkZPwULdGVtcGVyYXR1cmWamRk/AAoSYmlyY2hfZm9yZXN0X2hpbGxzBQhkb3duZmFsbJqZGT8FC3RlbXBlcmF0dXJlmpkZPwAKGmJpcmNoX2ZvcmVzdF9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFGJpcmNoX2ZvcmVzdF9tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKCmNvbGRfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw9AAoKY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgpjb2xkX3RhaWdhBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKEGNvbGRfdGFpZ2FfaGlsbHMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmUAAAC/AAoSY29sZF90YWlnYV9tdXRhdGVkBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKD2RlZXBfY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8AChFkZWVwX2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChNkZWVwX2x1a2V3YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCmRlZXBfb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAoPZGVlcF93YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKBmRlc2VydAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgxkZXNlcnRfaGlsbHMFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoOZGVzZXJ0X211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoNZXh0cmVtZV9oaWxscwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4AChJleHRyZW1lX2hpbGxzX2VkZ2UFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoVZXh0cmVtZV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbJqZmT4FC3RlbXBlcmF0dXJlzcxMPgAKGGV4dHJlbWVfaGlsbHNfcGx1c190cmVlcwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACiBleHRyZW1lX2hpbGxzX3BsdXNfdHJlZXNfbXV0YXRlZAUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACg1mbG93ZXJfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKBmZvcmVzdAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgxmb3Jlc3RfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzMzM/AAoMZnJvemVuX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAAAAKDGZyb3plbl9yaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACgRoZWxsBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDWljZV9tb3VudGFpbnMFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAAAAAoKaWNlX3BsYWlucwUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChFpY2VfcGxhaW5zX3NwaWtlcwUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZQAAAAAACgZqdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoLanVuZ2xlX2VkZ2UFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzM3M/AAoTanVuZ2xlX2VkZ2VfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzcz8ACgxqdW5nbGVfaGlsbHMFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoOanVuZ2xlX211dGF0ZWQFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTbGVnYWN5X2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACg5sdWtld2FybV9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgptZWdhX3RhaWdhBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKEG1lZ2FfdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmWamZk+AAoEbWVzYQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgptZXNhX2JyeWNlBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDG1lc2FfcGxhdGVhdQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChRtZXNhX3BsYXRlYXVfbXV0YXRlZAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChJtZXNhX3BsYXRlYXVfc3RvbmUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoabWVzYV9wbGF0ZWF1X3N0b25lX211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoPbXVzaHJvb21faXNsYW5kBQhkb3duZmFsbAAAgD8FC3RlbXBlcmF0dXJlZmZmPwAKFW11c2hyb29tX2lzbGFuZF9zaG9yZQUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZWZmZj8ACgVvY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgZwbGFpbnMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmXNzEw/AAobcmVkd29vZF90YWlnYV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKFXJlZHdvb2RfdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgVyaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACg1yb29mZWRfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFXJvb2ZlZF9mb3Jlc3RfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgdzYXZhbm5hBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlmpmZPwAKD3NhdmFubmFfbXV0YXRlZAUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZc3MjD8ACg9zYXZhbm5hX3BsYXRlYXUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAIA/AAoXc2F2YW5uYV9wbGF0ZWF1X211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAIA/AAoLc3RvbmVfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoQc3VuZmxvd2VyX3BsYWlucwUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACglzd2FtcGxhbmQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoRc3dhbXBsYW5kX211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoFdGFpZ2EFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoLdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoNdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgd0aGVfZW5kBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCndhcm1fb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAA=";
|
||||
/** @var string|null */
|
||||
private static $DEFAULT_NBT_CACHE = null;
|
||||
|
||||
/** @var string */
|
||||
public $namedtag;
|
||||
|
||||
@ -39,7 +42,11 @@ class BiomeDefinitionListPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->put($this->namedtag ?? self::HARDCODED_NBT_BLOB);
|
||||
$this->put(
|
||||
$this->namedtag ??
|
||||
self::$DEFAULT_NBT_CACHE ??
|
||||
(self::$DEFAULT_NBT_CACHE = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/biome_definitions.nbt'))
|
||||
);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -39,9 +39,9 @@ class BossEventPacket extends DataPacket{
|
||||
public const TYPE_HIDE = 2;
|
||||
/* C2S: Unregisters a player from a boss fight. */
|
||||
public const TYPE_UNREGISTER_PLAYER = 3;
|
||||
/* S2C: Appears not to be implemented. Currently bar percentage only appears to change in response to the target entity's health. */
|
||||
/* S2C: Sets the bar percentage. */
|
||||
public const TYPE_HEALTH_PERCENT = 4;
|
||||
/* S2C: Also appears to not be implemented. Title client-side sticks as the target entity's nametag, or their entity type name if not set. */
|
||||
/* S2C: Sets title of the bar. */
|
||||
public const TYPE_TITLE = 5;
|
||||
/* S2C: Not sure on this. Includes color and overlay fields, plus an unknown short. TODO: check this */
|
||||
public const TYPE_UNKNOWN_6 = 6;
|
||||
|
@ -42,7 +42,7 @@ class ClientCacheMissResponsePacket extends DataPacket/* implements ClientboundP
|
||||
*/
|
||||
public static function create(array $blobs) : self{
|
||||
//type check
|
||||
(static function(ChunkCacheBlob ...$blobs){})($blobs);
|
||||
(static function(ChunkCacheBlob ...$blobs){})(...$blobs);
|
||||
|
||||
$result = new self;
|
||||
$result->blobs = $blobs;
|
||||
|
@ -0,0 +1,66 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class CompletedUsingItemPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::COMPLETED_USING_ITEM_PACKET;
|
||||
|
||||
public const ACTION_UNKNOWN = -1;
|
||||
public const ACTION_EQUIP_ARMOR = 0;
|
||||
public const ACTION_EAT = 1;
|
||||
public const ACTION_ATTACK = 2;
|
||||
public const ACTION_CONSUME = 3;
|
||||
public const ACTION_THROW = 4;
|
||||
public const ACTION_SHOOT = 5;
|
||||
public const ACTION_PLACE = 6;
|
||||
public const ACTION_FILL_BOTTLE = 7;
|
||||
public const ACTION_FILL_BUCKET = 8;
|
||||
public const ACTION_POUR_BUCKET = 9;
|
||||
public const ACTION_USE_TOOL = 10;
|
||||
public const ACTION_INTERACT = 11;
|
||||
public const ACTION_RETRIEVED = 12;
|
||||
public const ACTION_DYED = 13;
|
||||
public const ACTION_TRADED = 14;
|
||||
|
||||
/** @var int */
|
||||
public $itemId;
|
||||
/** @var int */
|
||||
public $action;
|
||||
|
||||
public function decodePayload() : void{
|
||||
$this->itemId = $this->getShort();
|
||||
$this->action = $this->getLInt();
|
||||
}
|
||||
|
||||
public function encodePayload() : void{
|
||||
$this->putShort($this->itemId);
|
||||
$this->putLInt($this->action);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleCompletedUsingItem($this);
|
||||
}
|
||||
}
|
@ -33,6 +33,8 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe;
|
||||
use pocketmine\network\mcpe\protocol\types\PotionTypeRecipe;
|
||||
#ifndef COMPILE
|
||||
use pocketmine\utils\Binary;
|
||||
#endif
|
||||
@ -53,6 +55,10 @@ class CraftingDataPacket extends DataPacket{
|
||||
|
||||
/** @var object[] */
|
||||
public $entries = [];
|
||||
/** @var PotionTypeRecipe[] */
|
||||
public $potionTypeRecipes = [];
|
||||
/** @var PotionContainerChangeRecipe[] */
|
||||
public $potionContainerRecipes = [];
|
||||
/** @var bool */
|
||||
public $cleanRecipes = false;
|
||||
|
||||
@ -140,7 +146,19 @@ class CraftingDataPacket extends DataPacket{
|
||||
}
|
||||
$this->decodedEntries[] = $entry;
|
||||
}
|
||||
$this->getBool(); //cleanRecipes
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$input = $this->getVarInt();
|
||||
$ingredient = $this->getVarInt();
|
||||
$output = $this->getVarInt();
|
||||
$this->potionTypeRecipes[] = new PotionTypeRecipe($input, $ingredient, $output);
|
||||
}
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$input = $this->getVarInt();
|
||||
$ingredient = $this->getVarInt();
|
||||
$output = $this->getVarInt();
|
||||
$this->potionContainerRecipes[] = new PotionContainerChangeRecipe($input, $ingredient, $output);
|
||||
}
|
||||
$this->cleanRecipes = $this->getBool();
|
||||
}
|
||||
|
||||
private static function writeEntry($entry, NetworkBinaryStream $stream, int $pos){
|
||||
@ -240,6 +258,18 @@ class CraftingDataPacket extends DataPacket{
|
||||
|
||||
$writer->reset();
|
||||
}
|
||||
$this->putUnsignedVarInt(count($this->potionTypeRecipes));
|
||||
foreach($this->potionTypeRecipes as $recipe){
|
||||
$this->putVarInt($recipe->getInputPotionType());
|
||||
$this->putVarInt($recipe->getIngredientItemId());
|
||||
$this->putVarInt($recipe->getOutputPotionType());
|
||||
}
|
||||
$this->putUnsignedVarInt(count($this->potionContainerRecipes));
|
||||
foreach($this->potionContainerRecipes as $recipe){
|
||||
$this->putVarInt($recipe->getInputItemId());
|
||||
$this->putVarInt($recipe->getIngredientItemId());
|
||||
$this->putVarInt($recipe->getOutputItemId());
|
||||
}
|
||||
|
||||
$this->putBool($this->cleanRecipes);
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
abstract public function handle(NetworkSession $session) : bool;
|
||||
|
||||
public function clean(){
|
||||
$this->buffer = null;
|
||||
$this->buffer = "";
|
||||
$this->isEncoded = false;
|
||||
$this->offset = 0;
|
||||
return $this;
|
||||
@ -143,7 +143,7 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
|
||||
public function __debugInfo(){
|
||||
$data = [];
|
||||
foreach($this as $k => $v){
|
||||
foreach((array) $this as $k => $v){
|
||||
if($k === "buffer" and is_string($v)){
|
||||
$data[$k] = bin2hex($v);
|
||||
}elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){
|
||||
|
@ -0,0 +1,66 @@
|
||||
<?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 EducationSettingsPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::EDUCATION_SETTINGS_PACKET;
|
||||
|
||||
/** @var string */
|
||||
private $codeBuilderDefaultUri;
|
||||
/** @var bool */
|
||||
private $hasQuiz;
|
||||
|
||||
public static function create(string $codeBuilderDefaultUri, bool $hasQuiz) : self{
|
||||
$result = new self;
|
||||
$result->codeBuilderDefaultUri = $codeBuilderDefaultUri;
|
||||
$result->hasQuiz = $hasQuiz;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getCodeBuilderDefaultUri() : string{
|
||||
return $this->codeBuilderDefaultUri;
|
||||
}
|
||||
|
||||
public function getHasQuiz() : bool{
|
||||
return $this->hasQuiz;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->codeBuilderDefaultUri = $this->getString();
|
||||
$this->hasQuiz = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putString($this->codeBuilderDefaultUri);
|
||||
$this->putBool($this->hasQuiz);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleEducationSettings($this);
|
||||
}
|
||||
}
|
81
src/pocketmine/network/mcpe/protocol/EmotePacket.php
Normal file
81
src/pocketmine/network/mcpe/protocol/EmotePacket.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?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 EmotePacket extends DataPacket/* implements ClientboundPacket, ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::EMOTE_PACKET;
|
||||
|
||||
private const FLAG_SERVER = 1 << 0;
|
||||
|
||||
/** @var int */
|
||||
private $entityRuntimeId;
|
||||
/** @var string */
|
||||
private $emoteId;
|
||||
/** @var int */
|
||||
private $flags;
|
||||
|
||||
public static function create(int $entityRuntimeId, string $emoteId, int $flags) : self{
|
||||
$result = new self;
|
||||
$result->entityRuntimeId = $entityRuntimeId;
|
||||
$result->emoteId = $emoteId;
|
||||
$result->flags = $flags;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: we can't call this getEntityRuntimeId() because of base class collision (crap architecture, thanks Shoghi)
|
||||
* @return int
|
||||
*/
|
||||
public function getEntityRuntimeIdField() : int{
|
||||
return $this->entityRuntimeId;
|
||||
}
|
||||
|
||||
public function getEmoteId() : string{
|
||||
return $this->emoteId;
|
||||
}
|
||||
|
||||
public function getFlags() : int{
|
||||
return $this->flags;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->emoteId = $this->getString();
|
||||
$this->flags = $this->getByte();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putString($this->emoteId);
|
||||
$this->putByte($this->flags);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleEmote($this);
|
||||
}
|
||||
}
|
@ -43,6 +43,11 @@ class EventPacket extends DataPacket{
|
||||
public const TYPE_PATTERN_REMOVED = 10; //???
|
||||
public const TYPE_COMMANED_EXECUTED = 11;
|
||||
public const TYPE_FISH_BUCKETED = 12;
|
||||
public const TYPE_MOB_BORN = 13;
|
||||
public const TYPE_PET_DIED = 14;
|
||||
public const TYPE_CAULDRON_BLOCK_USED = 15;
|
||||
public const TYPE_COMPOSTER_BLOCK_USED = 16;
|
||||
public const TYPE_BELL_BLOCK_USED = 17;
|
||||
|
||||
/** @var int */
|
||||
public $playerRuntimeId;
|
||||
|
@ -1,73 +0,0 @@
|
||||
<?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;
|
||||
use function count;
|
||||
|
||||
class ExplodePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::EXPLODE_PACKET;
|
||||
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var float */
|
||||
public $radius;
|
||||
/** @var Vector3[] */
|
||||
public $records = [];
|
||||
|
||||
public function clean(){
|
||||
$this->records = [];
|
||||
return parent::clean();
|
||||
}
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->position = $this->getVector3();
|
||||
$this->radius = (float) ($this->getVarInt() / 32);
|
||||
$count = $this->getUnsignedVarInt();
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$x = $y = $z = null;
|
||||
$this->getSignedBlockPosition($x, $y, $z);
|
||||
$this->records[$i] = new Vector3($x, $y, $z);
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putVector3($this->position);
|
||||
$this->putVarInt((int) ($this->radius * 32));
|
||||
$this->putUnsignedVarInt(count($this->records));
|
||||
if(count($this->records) > 0){
|
||||
foreach($this->records as $record){
|
||||
$this->putSignedBlockPosition((int) $record->x, (int) $record->y, (int) $record->z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleExplode($this);
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
use function count;
|
||||
|
||||
@ -74,7 +75,26 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
$this->transactionType = $this->getUnsignedVarInt();
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->actions[] = (new NetworkInventoryAction())->read($this);
|
||||
$this->actions[] = $action = (new NetworkInventoryAction())->read($this);
|
||||
|
||||
if(
|
||||
$action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER and
|
||||
$action->windowId === ContainerIds::UI and
|
||||
$action->inventorySlot === 50 and
|
||||
!$action->oldItem->equalsExact($action->newItem)
|
||||
){
|
||||
$this->isCraftingPart = true;
|
||||
if(!$action->oldItem->isNull() and $action->newItem->isNull()){
|
||||
$this->isFinalCraftingPart = true;
|
||||
}
|
||||
}elseif(
|
||||
$action->sourceType === NetworkInventoryAction::SOURCE_TODO and (
|
||||
$action->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
|
||||
$action->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
|
||||
)
|
||||
){
|
||||
$this->isCraftingPart = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->trData = new \stdClass();
|
||||
|
@ -57,7 +57,7 @@ class LevelChunkPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
}
|
||||
|
||||
public static function withCache(int $chunkX, int $chunkZ, int $subChunkCount, array $usedBlobHashes, string $extraPayload) : self{
|
||||
(static function(int ...$hashes){})($usedBlobHashes);
|
||||
(static function(int ...$hashes){})(...$usedBlobHashes);
|
||||
$result = new self;
|
||||
$result->chunkX = $chunkX;
|
||||
$result->chunkZ = $chunkZ;
|
||||
|
@ -70,7 +70,7 @@ class MoveActorDeltaPacket extends DataPacket{
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->flags = $this->getByte();
|
||||
$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);
|
||||
@ -93,7 +93,7 @@ class MoveActorDeltaPacket extends DataPacket{
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putByte($this->flags);
|
||||
$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);
|
||||
|
@ -0,0 +1,61 @@
|
||||
<?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 MultiplayerSettingsPacket extends DataPacket/* implements ServerboundPacket*/{ //TODO: this might be clientbound too, but unsure
|
||||
public const NETWORK_ID = ProtocolInfo::MULTIPLAYER_SETTINGS_PACKET;
|
||||
|
||||
public const ACTION_ENABLE_MULTIPLAYER = 0;
|
||||
public const ACTION_DISABLE_MULTIPLAYER = 1;
|
||||
public const ACTION_REFRESH_JOIN_CODE = 2;
|
||||
|
||||
/** @var int */
|
||||
private $action;
|
||||
|
||||
public static function create(int $action) : self{
|
||||
$result = new self;
|
||||
$result->action = $action;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getAction() : int{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->action = $this->getVarInt();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putVarInt($this->action);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleMultiplayerSettings($this);
|
||||
}
|
||||
}
|
@ -27,18 +27,34 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class StructureTemplateDataExportResponsePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET;
|
||||
class NetworkSettingsPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::NETWORK_SETTINGS_PACKET;
|
||||
|
||||
public const COMPRESS_NOTHING = 0;
|
||||
public const COMPRESS_EVERYTHING = 1;
|
||||
|
||||
/** @var int */
|
||||
private $compressionThreshold;
|
||||
|
||||
public static function create(int $compressionThreshold) : self{
|
||||
$result = new self;
|
||||
$result->compressionThreshold = $compressionThreshold;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getCompressionThreshold() : int{
|
||||
return $this->compressionThreshold;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
//TODO
|
||||
$this->compressionThreshold = $this->getLShort();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
//TODO
|
||||
$this->putLShort($this->compressionThreshold);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleStructureTemplateDataExportResponse($this);
|
||||
return $handler->handleNetworkSettings($this);
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ class PacketPool{
|
||||
static::registerPacket(new RiderJumpPacket());
|
||||
static::registerPacket(new UpdateBlockPacket());
|
||||
static::registerPacket(new AddPaintingPacket());
|
||||
static::registerPacket(new ExplodePacket());
|
||||
static::registerPacket(new TickSyncPacket());
|
||||
static::registerPacket(new LevelSoundEventPacketV1());
|
||||
static::registerPacket(new LevelEventPacket());
|
||||
static::registerPacket(new BlockEventPacket());
|
||||
@ -162,11 +162,19 @@ class PacketPool{
|
||||
static::registerPacket(new ClientCacheStatusPacket());
|
||||
static::registerPacket(new OnScreenTextureAnimationPacket());
|
||||
static::registerPacket(new MapCreateLockedCopyPacket());
|
||||
static::registerPacket(new StructureTemplateDataExportRequestPacket());
|
||||
static::registerPacket(new StructureTemplateDataExportResponsePacket());
|
||||
static::registerPacket(new StructureTemplateDataRequestPacket());
|
||||
static::registerPacket(new StructureTemplateDataResponsePacket());
|
||||
static::registerPacket(new UpdateBlockPropertiesPacket());
|
||||
static::registerPacket(new ClientCacheBlobStatusPacket());
|
||||
static::registerPacket(new ClientCacheMissResponsePacket());
|
||||
static::registerPacket(new EducationSettingsPacket());
|
||||
static::registerPacket(new EmotePacket());
|
||||
static::registerPacket(new MultiplayerSettingsPacket());
|
||||
static::registerPacket(new SettingsCommandPacket());
|
||||
static::registerPacket(new AnvilDamagePacket());
|
||||
static::registerPacket(new CompletedUsingItemPacket());
|
||||
static::registerPacket(new NetworkSettingsPacket());
|
||||
static::registerPacket(new PlayerAuthInputPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
|
175
src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php
Normal file
175
src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?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;
|
||||
use pocketmine\network\mcpe\protocol\types\InputMode;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayMode;
|
||||
use function assert;
|
||||
|
||||
class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::PLAYER_AUTH_INPUT_PACKET;
|
||||
|
||||
/** @var Vector3 */
|
||||
private $position;
|
||||
/** @var float */
|
||||
private $pitch;
|
||||
/** @var float */
|
||||
private $yaw;
|
||||
/** @var float */
|
||||
private $headYaw;
|
||||
/** @var float */
|
||||
private $moveVecX;
|
||||
/** @var float */
|
||||
private $moveVecZ;
|
||||
/** @var int */
|
||||
private $inputFlags;
|
||||
/** @var int */
|
||||
private $inputMode;
|
||||
/** @var int */
|
||||
private $playMode;
|
||||
/** @var Vector3|null */
|
||||
private $vrGazeDirection = null;
|
||||
|
||||
/**
|
||||
* @param Vector3 $position
|
||||
* @param float $pitch
|
||||
* @param float $yaw
|
||||
* @param float $headYaw
|
||||
* @param float $moveVecX
|
||||
* @param float $moveVecZ
|
||||
* @param int $inputFlags
|
||||
* @param int $inputMode @see InputMode
|
||||
* @param int $playMode @see PlayMode
|
||||
* @param Vector3|null $vrGazeDirection only used when PlayMode::VR
|
||||
*
|
||||
* @return 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 = null) : 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");
|
||||
}
|
||||
$result = new self;
|
||||
$result->position = $position->asVector3();
|
||||
$result->pitch = $pitch;
|
||||
$result->yaw = $yaw;
|
||||
$result->headYaw = $headYaw;
|
||||
$result->moveVecX = $moveVecX;
|
||||
$result->moveVecZ = $moveVecZ;
|
||||
$result->inputFlags = $inputFlags;
|
||||
$result->inputMode = $inputMode;
|
||||
$result->playMode = $playMode;
|
||||
if($vrGazeDirection !== null){
|
||||
$this->vrGazeDirection = $vrGazeDirection->asVector3();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getPosition() : Vector3{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function getPitch() : float{
|
||||
return $this->pitch;
|
||||
}
|
||||
|
||||
public function getYaw() : float{
|
||||
return $this->yaw;
|
||||
}
|
||||
|
||||
public function getHeadYaw() : float{
|
||||
return $this->headYaw;
|
||||
}
|
||||
|
||||
public function getMoveVecX() : float{
|
||||
return $this->moveVecX;
|
||||
}
|
||||
|
||||
public function getMoveVecZ() : float{
|
||||
return $this->moveVecZ;
|
||||
}
|
||||
|
||||
public function getInputFlags() : int{
|
||||
return $this->inputFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see InputMode
|
||||
* @return int
|
||||
*/
|
||||
public function getInputMode() : int{
|
||||
return $this->inputMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PlayMode
|
||||
* @return int
|
||||
*/
|
||||
public function getPlayMode() : int{
|
||||
return $this->playMode;
|
||||
}
|
||||
|
||||
public function getVrGazeDirection() : ?Vector3{
|
||||
return $this->vrGazeDirection;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->yaw = $this->getLFloat();
|
||||
$this->pitch = $this->getLFloat();
|
||||
$this->position = $this->getVector3();
|
||||
$this->moveVecX = $this->getLFloat();
|
||||
$this->moveVecZ = $this->getLFloat();
|
||||
$this->headYaw = $this->getLFloat();
|
||||
$this->inputFlags = $this->getUnsignedVarLong();
|
||||
$this->inputMode = $this->getUnsignedVarInt();
|
||||
$this->playMode = $this->getUnsignedVarInt();
|
||||
if($this->playMode === PlayMode::VR){
|
||||
$this->vrGazeDirection = $this->getVector3();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putLFloat($this->yaw);
|
||||
$this->putLFloat($this->pitch);
|
||||
$this->putVector3($this->position);
|
||||
$this->putLFloat($this->moveVecX);
|
||||
$this->putLFloat($this->moveVecZ);
|
||||
$this->putLFloat($this->headYaw);
|
||||
$this->putUnsignedVarLong($this->inputFlags);
|
||||
$this->putUnsignedVarInt($this->inputMode);
|
||||
$this->putUnsignedVarInt($this->playMode);
|
||||
if($this->playMode === PlayMode::VR){
|
||||
assert($this->vrGazeDirection !== null);
|
||||
$this->putVector3($this->vrGazeDirection);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handlePlayerAuthInput($this);
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use function count;
|
||||
@ -57,22 +56,12 @@ class PlayerListPacket extends DataPacket{
|
||||
$entry->uuid = $this->getUUID();
|
||||
$entry->entityUniqueId = $this->getEntityUniqueId();
|
||||
$entry->username = $this->getString();
|
||||
|
||||
$skinId = $this->getString();
|
||||
$skinData = $this->getString();
|
||||
$capeData = $this->getString();
|
||||
$geometryName = $this->getString();
|
||||
$geometryData = $this->getString();
|
||||
|
||||
$entry->skin = new Skin(
|
||||
$skinId,
|
||||
$skinData,
|
||||
$capeData,
|
||||
$geometryName,
|
||||
$geometryData
|
||||
);
|
||||
$entry->xboxUserId = $this->getString();
|
||||
$entry->platformChatId = $this->getString();
|
||||
$entry->buildPlatform = $this->getLInt();
|
||||
$entry->skinData = $this->getSkin();
|
||||
$entry->isTeacher = $this->getBool();
|
||||
$entry->isHost = $this->getBool();
|
||||
}else{
|
||||
$entry->uuid = $this->getUUID();
|
||||
}
|
||||
@ -89,13 +78,12 @@ class PlayerListPacket extends DataPacket{
|
||||
$this->putUUID($entry->uuid);
|
||||
$this->putEntityUniqueId($entry->entityUniqueId);
|
||||
$this->putString($entry->username);
|
||||
$this->putString($entry->skin->getSkinId());
|
||||
$this->putString($entry->skin->getSkinData());
|
||||
$this->putString($entry->skin->getCapeData());
|
||||
$this->putString($entry->skin->getGeometryName());
|
||||
$this->putString($entry->skin->getGeometryData());
|
||||
$this->putString($entry->xboxUserId);
|
||||
$this->putString($entry->platformChatId);
|
||||
$this->putLInt($entry->buildPlatform);
|
||||
$this->putSkin($entry->skinData);
|
||||
$this->putBool($entry->isTeacher);
|
||||
$this->putBool($entry->isHost);
|
||||
}else{
|
||||
$this->putUUID($entry->uuid);
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\utils\UUID;
|
||||
|
||||
class PlayerSkinPacket extends DataPacket{
|
||||
@ -38,39 +38,21 @@ class PlayerSkinPacket extends DataPacket{
|
||||
public $oldSkinName = "";
|
||||
/** @var string */
|
||||
public $newSkinName = "";
|
||||
/** @var Skin */
|
||||
/** @var SkinData */
|
||||
public $skin;
|
||||
/** @var bool */
|
||||
public $premiumSkin = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->uuid = $this->getUUID();
|
||||
|
||||
$skinId = $this->getString();
|
||||
$this->skin = $this->getSkin();
|
||||
$this->newSkinName = $this->getString();
|
||||
$this->oldSkinName = $this->getString();
|
||||
$skinData = $this->getString();
|
||||
$capeData = $this->getString();
|
||||
$geometryModel = $this->getString();
|
||||
$geometryData = $this->getString();
|
||||
|
||||
$this->skin = new Skin($skinId, $skinData, $capeData, $geometryModel, $geometryData);
|
||||
|
||||
$this->premiumSkin = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putUUID($this->uuid);
|
||||
|
||||
$this->putString($this->skin->getSkinId());
|
||||
$this->putSkin($this->skin);
|
||||
$this->putString($this->newSkinName);
|
||||
$this->putString($this->oldSkinName);
|
||||
$this->putString($this->skin->getSkinData());
|
||||
$this->putString($this->skin->getCapeData());
|
||||
$this->putString($this->skin->getGeometryName());
|
||||
$this->putString($this->skin->getGeometryData());
|
||||
|
||||
$this->putBool($this->premiumSkin);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -39,15 +39,15 @@ interface ProtocolInfo{
|
||||
/**
|
||||
* Actual Minecraft: PE protocol version
|
||||
*/
|
||||
public const CURRENT_PROTOCOL = 361;
|
||||
public const CURRENT_PROTOCOL = 389;
|
||||
/**
|
||||
* Current Minecraft PE version reported by the server. This is usually the earliest currently supported version.
|
||||
*/
|
||||
public const MINECRAFT_VERSION = 'v1.12.0';
|
||||
public const MINECRAFT_VERSION = 'v1.14.0';
|
||||
/**
|
||||
* Version number sent to clients in ping responses.
|
||||
*/
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.12.0';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.14.0';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
@ -71,7 +71,7 @@ interface ProtocolInfo{
|
||||
public const RIDER_JUMP_PACKET = 0x14;
|
||||
public const UPDATE_BLOCK_PACKET = 0x15;
|
||||
public const ADD_PAINTING_PACKET = 0x16;
|
||||
public const EXPLODE_PACKET = 0x17;
|
||||
public const TICK_SYNC_PACKET = 0x17;
|
||||
public const LEVEL_SOUND_EVENT_PACKET_V1 = 0x18;
|
||||
public const LEVEL_EVENT_PACKET = 0x19;
|
||||
public const BLOCK_EVENT_PACKET = 0x1a;
|
||||
@ -180,10 +180,18 @@ interface ProtocolInfo{
|
||||
public const CLIENT_CACHE_STATUS_PACKET = 0x81;
|
||||
public const ON_SCREEN_TEXTURE_ANIMATION_PACKET = 0x82;
|
||||
public const MAP_CREATE_LOCKED_COPY_PACKET = 0x83;
|
||||
public const STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET = 0x84;
|
||||
public const STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET = 0x85;
|
||||
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;
|
||||
public const EMOTE_PACKET = 0x8a;
|
||||
public const MULTIPLAYER_SETTINGS_PACKET = 0x8b;
|
||||
public const SETTINGS_COMMAND_PACKET = 0x8c;
|
||||
public const ANVIL_DAMAGE_PACKET = 0x8d;
|
||||
public const COMPLETED_USING_ITEM_PACKET = 0x8e;
|
||||
public const NETWORK_SETTINGS_PACKET = 0x8f;
|
||||
public const PLAYER_AUTH_INPUT_PACKET = 0x90;
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use function strlen;
|
||||
|
||||
class ResourcePackChunkDataPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_CHUNK_DATA_PACKET;
|
||||
@ -46,15 +45,14 @@ class ResourcePackChunkDataPacket extends DataPacket{
|
||||
$this->packId = $this->getString();
|
||||
$this->chunkIndex = $this->getLInt();
|
||||
$this->progress = $this->getLLong();
|
||||
$this->data = $this->get($this->getLInt());
|
||||
$this->data = $this->getString();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putString($this->packId);
|
||||
$this->putLInt($this->chunkIndex);
|
||||
$this->putLLong($this->progress);
|
||||
$this->putLInt(strlen($this->data));
|
||||
$this->put($this->data);
|
||||
$this->putString($this->data);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -44,6 +44,8 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
|
||||
/** @var bool */
|
||||
public $isExperimental = false;
|
||||
/** @var string */
|
||||
public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->mustAccept = $this->getBool();
|
||||
@ -62,6 +64,7 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
}
|
||||
|
||||
$this->isExperimental = $this->getBool();
|
||||
$this->baseGameVersion = $this->getString();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -82,6 +85,7 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
}
|
||||
|
||||
$this->putBool($this->isExperimental);
|
||||
$this->putString($this->baseGameVersion);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -32,15 +32,27 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
class RespawnPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::RESPAWN_PACKET;
|
||||
|
||||
public const SEARCHING_FOR_SPAWN = 0;
|
||||
public const READY_TO_SPAWN = 1;
|
||||
public const CLIENT_READY_TO_SPAWN = 2;
|
||||
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var int */
|
||||
public $respawnState = self::SEARCHING_FOR_SPAWN;
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->position = $this->getVector3();
|
||||
$this->respawnState = $this->getByte();
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putVector3($this->position);
|
||||
$this->putByte($this->respawnState);
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -0,0 +1,66 @@
|
||||
<?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 SettingsCommandPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::SETTINGS_COMMAND_PACKET;
|
||||
|
||||
/** @var string */
|
||||
private $command;
|
||||
/** @var bool */
|
||||
private $suppressOutput;
|
||||
|
||||
public static function create(string $command, bool $suppressOutput) : self{
|
||||
$result = new self;
|
||||
$result->command = $command;
|
||||
$result->suppressOutput = $suppressOutput;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getCommand() : string{
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
public function getSuppressOutput() : bool{
|
||||
return $this->suppressOutput;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->command = $this->getString();
|
||||
$this->suppressOutput = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putString($this->command);
|
||||
$this->putBool($this->suppressOutput);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleSettingsCommand($this);
|
||||
}
|
||||
}
|
@ -27,6 +27,8 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
@ -79,8 +81,8 @@ class StartGamePacket extends DataPacket{
|
||||
public $hasAchievementsDisabled = true;
|
||||
/** @var int */
|
||||
public $time = -1;
|
||||
/** @var bool */
|
||||
public $eduMode = false;
|
||||
/** @var int */
|
||||
public $eduEditionOffer = 0;
|
||||
/** @var bool */
|
||||
public $hasEduFeaturesEnabled = false;
|
||||
/** @var float */
|
||||
@ -130,6 +132,8 @@ class StartGamePacket extends DataPacket{
|
||||
/** @var bool */
|
||||
public $onlySpawnV1Villagers = false;
|
||||
|
||||
/** @var string */
|
||||
public $vanillaVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
|
||||
/** @var string */
|
||||
public $levelId = ""; //base64 string, usually the same as world folder name in vanilla
|
||||
/** @var string */
|
||||
@ -138,6 +142,8 @@ class StartGamePacket extends DataPacket{
|
||||
public $premiumWorldTemplateId = "";
|
||||
/** @var bool */
|
||||
public $isTrial = false;
|
||||
/** @var bool */
|
||||
public $isMovementServerAuthoritative = false;
|
||||
/** @var int */
|
||||
public $currentTick = 0; //only used if isTrial is true
|
||||
/** @var int */
|
||||
@ -145,7 +151,7 @@ class StartGamePacket extends DataPacket{
|
||||
/** @var string */
|
||||
public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort
|
||||
|
||||
/** @var array|null ["name" (string), "data" (int16), "legacy_id" (int16)] */
|
||||
/** @var ListTag|null */
|
||||
public $blockTable = null;
|
||||
/** @var array|null string (name) => int16 (legacyID) */
|
||||
public $itemTable = null;
|
||||
@ -169,7 +175,7 @@ class StartGamePacket extends DataPacket{
|
||||
$this->getBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ);
|
||||
$this->hasAchievementsDisabled = $this->getBool();
|
||||
$this->time = $this->getVarInt();
|
||||
$this->eduMode = $this->getBool();
|
||||
$this->eduEditionOffer = $this->getVarInt();
|
||||
$this->hasEduFeaturesEnabled = $this->getBool();
|
||||
$this->rainLevel = $this->getLFloat();
|
||||
$this->lightningLevel = $this->getLFloat();
|
||||
@ -193,22 +199,22 @@ class StartGamePacket extends DataPacket{
|
||||
$this->isWorldTemplateOptionLocked = $this->getBool();
|
||||
$this->onlySpawnV1Villagers = $this->getBool();
|
||||
|
||||
$this->vanillaVersion = $this->getString();
|
||||
$this->levelId = $this->getString();
|
||||
$this->worldName = $this->getString();
|
||||
$this->premiumWorldTemplateId = $this->getString();
|
||||
$this->isTrial = $this->getBool();
|
||||
$this->isMovementServerAuthoritative = $this->getBool();
|
||||
$this->currentTick = $this->getLLong();
|
||||
|
||||
$this->enchantmentSeed = $this->getVarInt();
|
||||
|
||||
$this->blockTable = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$id = $this->getString();
|
||||
$data = $this->getSignedLShort();
|
||||
$unknown = $this->getSignedLShort();
|
||||
|
||||
$this->blockTable[$i] = ["name" => $id, "data" => $data, "legacy_id" => $unknown];
|
||||
$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->blockTable = $blockTable;
|
||||
|
||||
$this->itemTable = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$id = $this->getString();
|
||||
@ -239,7 +245,7 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ);
|
||||
$this->putBool($this->hasAchievementsDisabled);
|
||||
$this->putVarInt($this->time);
|
||||
$this->putBool($this->eduMode);
|
||||
$this->putVarInt($this->eduEditionOffer);
|
||||
$this->putBool($this->hasEduFeaturesEnabled);
|
||||
$this->putLFloat($this->rainLevel);
|
||||
$this->putLFloat($this->lightningLevel);
|
||||
@ -263,10 +269,12 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putBool($this->isWorldTemplateOptionLocked);
|
||||
$this->putBool($this->onlySpawnV1Villagers);
|
||||
|
||||
$this->putString($this->vanillaVersion);
|
||||
$this->putString($this->levelId);
|
||||
$this->putString($this->worldName);
|
||||
$this->putString($this->premiumWorldTemplateId);
|
||||
$this->putBool($this->isTrial);
|
||||
$this->putBool($this->isMovementServerAuthoritative);
|
||||
$this->putLLong($this->currentTick);
|
||||
|
||||
$this->putVarInt($this->enchantmentSeed);
|
||||
@ -274,11 +282,11 @@ class StartGamePacket extends DataPacket{
|
||||
if($this->blockTable === null){
|
||||
if(self::$blockTableCache === null){
|
||||
//this is a really nasty hack, but it'll do for now
|
||||
self::$blockTableCache = self::serializeBlockTable(RuntimeBlockMapping::getBedrockKnownStates());
|
||||
self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(new ListTag("", RuntimeBlockMapping::getBedrockKnownStates()));
|
||||
}
|
||||
$this->put(self::$blockTableCache);
|
||||
}else{
|
||||
$this->put(self::serializeBlockTable($this->blockTable));
|
||||
$this->put((new NetworkLittleEndianNBTStream())->write($this->blockTable));
|
||||
}
|
||||
if($this->itemTable === null){
|
||||
if(self::$itemTableCache === null){
|
||||
@ -292,17 +300,6 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putString($this->multiplayerCorrelationId);
|
||||
}
|
||||
|
||||
private static function serializeBlockTable(array $table) : string{
|
||||
$stream = new NetworkBinaryStream();
|
||||
$stream->putUnsignedVarInt(count($table));
|
||||
foreach($table as $v){
|
||||
$stream->putString($v["name"]);
|
||||
$stream->putLShort($v["data"]);
|
||||
$stream->putLShort($v["legacy_id"]);
|
||||
}
|
||||
return $stream->getBuffer();
|
||||
}
|
||||
|
||||
private static function serializeItemTable(array $table) : string{
|
||||
$stream = new NetworkBinaryStream();
|
||||
$stream->putUnsignedVarInt(count($table));
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?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 pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
|
||||
class StructureTemplateDataRequestPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET;
|
||||
|
||||
public const TYPE_ALWAYS_LOAD = 1;
|
||||
public const TYPE_CREATE_AND_LOAD = 2;
|
||||
|
||||
/** @var string */
|
||||
public $structureTemplateName;
|
||||
/** @var int */
|
||||
public $structureBlockX;
|
||||
/** @var int */
|
||||
public $structureBlockY;
|
||||
/** @var int */
|
||||
public $structureBlockZ;
|
||||
/** @var StructureSettings */
|
||||
public $structureSettings;
|
||||
/** @var int */
|
||||
public $structureTemplateResponseType;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->structureTemplateName = $this->getString();
|
||||
$this->getBlockPosition($this->structureBlockX, $this->structureBlockY, $this->structureBlockZ);
|
||||
$this->structureSettings = $this->getStructureSettings();
|
||||
$this->structureTemplateResponseType = $this->getByte();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putString($this->structureTemplateName);
|
||||
$this->putBlockPosition($this->structureBlockX, $this->structureBlockY, $this->structureBlockZ);
|
||||
$this->putStructureSettings($this->structureSettings);
|
||||
$this->putByte($this->structureTemplateResponseType);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleStructureTemplateDataRequest($this);
|
||||
}
|
||||
}
|
@ -27,18 +27,30 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class StructureTemplateDataExportRequestPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET;
|
||||
class StructureTemplateDataResponsePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET;
|
||||
|
||||
/** @var string */
|
||||
public $structureTemplateName;
|
||||
/** @var string|null */
|
||||
public $namedtag;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
//TODO
|
||||
$this->structureTemplateName = $this->getString();
|
||||
if($this->getBool()){
|
||||
$this->namedtag = $this->getRemaining();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
//TODO
|
||||
$this->putString($this->structureTemplateName);
|
||||
$this->putBool($this->namedtag !== null);
|
||||
if($this->namedtag !== null){
|
||||
$this->put($this->namedtag);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleStructureTemplateDataExportRequest($this);
|
||||
return $handler->handleStructureTemplateDataResponse($this);
|
||||
}
|
||||
}
|
73
src/pocketmine/network/mcpe/protocol/TickSyncPacket.php
Normal file
73
src/pocketmine/network/mcpe/protocol/TickSyncPacket.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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 TickSyncPacket extends DataPacket/* implements ClientboundPacket, ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::TICK_SYNC_PACKET;
|
||||
|
||||
/** @var int */
|
||||
private $clientSendTime;
|
||||
/** @var int */
|
||||
private $serverReceiveTime;
|
||||
|
||||
public static function request(int $clientTime) : self{
|
||||
$result = new self;
|
||||
$result->clientSendTime = $clientTime;
|
||||
$result->serverReceiveTime = 0; //useless
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function response(int $clientSendTime, int $serverReceiveTime) : self{
|
||||
$result = new self;
|
||||
$result->clientSendTime = $clientSendTime;
|
||||
$result->serverReceiveTime = $serverReceiveTime;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getClientSendTime() : int{
|
||||
return $this->clientSendTime;
|
||||
}
|
||||
|
||||
public function getServerReceiveTime() : int{
|
||||
return $this->serverReceiveTime;
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->clientSendTime = $this->getLLong();
|
||||
$this->serverReceiveTime = $this->getLLong();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putLLong($this->clientSendTime);
|
||||
$this->putLLong($this->serverReceiveTime);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleTickSync($this);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?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;
|
||||
|
||||
class CommandEnumConstraint{
|
||||
/** @var CommandEnum */
|
||||
private $enum;
|
||||
/** @var int */
|
||||
private $valueOffset;
|
||||
/** @var int[] */
|
||||
private $constraints; //TODO: find constants
|
||||
|
||||
/**
|
||||
* @param CommandEnum $enum
|
||||
* @param int $valueOffset
|
||||
* @param int[] $constraints
|
||||
*/
|
||||
public function __construct(CommandEnum $enum, int $valueOffset, array $constraints){
|
||||
(static function(int ...$_){})(...$constraints);
|
||||
if(!isset($enum->enumValues[$valueOffset])){
|
||||
throw new \InvalidArgumentException("Invalid enum value offset $valueOffset");
|
||||
}
|
||||
$this->enum = $enum;
|
||||
$this->valueOffset = $valueOffset;
|
||||
$this->constraints = $constraints;
|
||||
}
|
||||
|
||||
public function getEnum() : CommandEnum{
|
||||
return $this->enum;
|
||||
}
|
||||
|
||||
public function getValueOffset() : int{
|
||||
return $this->valueOffset;
|
||||
}
|
||||
|
||||
public function getAffectedValue() : string{
|
||||
return $this->enum->enumValues[$this->valueOffset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getConstraints() : array{
|
||||
return $this->constraints;
|
||||
}
|
||||
}
|
@ -24,6 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
class CommandParameter{
|
||||
public const FLAG_FORCE_COLLAPSE_ENUM = 0x1;
|
||||
public const FLAG_HAS_ENUM_CONSTRAINT = 0x2;
|
||||
|
||||
/** @var string */
|
||||
public $paramName;
|
||||
/** @var int */
|
||||
@ -31,7 +34,7 @@ class CommandParameter{
|
||||
/** @var bool */
|
||||
public $isOptional;
|
||||
/** @var int */
|
||||
public $byte1 = 0; //unknown, always zero except for in /gamerule command
|
||||
public $flags = 0; //shows enum name if 1, always zero except for in /gamerule command
|
||||
/** @var CommandEnum|null */
|
||||
public $enum;
|
||||
/** @var string|null */
|
||||
|
@ -34,6 +34,6 @@ interface ContainerIds{
|
||||
public const CREATIVE = 121;
|
||||
public const HOTBAR = 122;
|
||||
public const FIXED_INVENTORY = 123;
|
||||
public const CURSOR = 124;
|
||||
public const UI = 124;
|
||||
|
||||
}
|
||||
|
36
src/pocketmine/network/mcpe/protocol/types/InputMode.php
Normal file
36
src/pocketmine/network/mcpe/protocol/types/InputMode.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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 InputMode{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public const MOUSE_KEYBOARD = 1;
|
||||
public const TOUCHSCREEN = 2;
|
||||
public const GAME_PAD = 3;
|
||||
public const MOTION_CONTROLLER = 4;
|
||||
}
|
@ -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\types;
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function random_bytes;
|
||||
use function str_repeat;
|
||||
|
||||
class LegacySkinAdapter implements SkinAdapter{
|
||||
|
||||
public function toSkinData(Skin $skin) : SkinData{
|
||||
$capeData = $skin->getCapeData();
|
||||
$capeImage = $capeData === "" ? new SkinImage(0, 0, "") : new SkinImage(32, 64, $capeData);
|
||||
$geometryName = $skin->getGeometryName();
|
||||
if($geometryName === ""){
|
||||
$geometryName = "geometry.humanoid.custom";
|
||||
}
|
||||
return new SkinData(
|
||||
$skin->getSkinId(),
|
||||
json_encode(["geometry" => ["default" => $geometryName]]),
|
||||
SkinImage::fromLegacy($skin->getSkinData()), [],
|
||||
$capeImage,
|
||||
$skin->getGeometryData()
|
||||
);
|
||||
}
|
||||
|
||||
public function fromSkinData(SkinData $data) : Skin{
|
||||
if($data->isPersona()){
|
||||
return new Skin("Standard_Custom", str_repeat(random_bytes(3) . "\xff", 2048));
|
||||
}
|
||||
|
||||
$capeData = $data->isPersonaCapeOnClassic() ? "" : $data->getCapeImage()->getData();
|
||||
|
||||
$geometryName = "";
|
||||
$resourcePatch = json_decode($data->getResourcePatch(), true);
|
||||
if(is_array($resourcePatch["geometry"]) && is_string($resourcePatch["geometry"]["default"])){
|
||||
$geometryName = $resourcePatch["geometry"]["default"];
|
||||
}else{
|
||||
//TODO: Kick for invalid skin
|
||||
}
|
||||
|
||||
return new Skin($data->getSkinId(), $data->getSkinImage()->getData(), $capeData, $geometryName, $data->getGeometryData());
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
use pocketmine\inventory\CraftingGrid;
|
||||
use pocketmine\inventory\transaction\action\CreativeInventoryAction;
|
||||
use pocketmine\inventory\transaction\action\DropItemAction;
|
||||
use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
@ -36,7 +37,6 @@ class NetworkInventoryAction{
|
||||
|
||||
public const SOURCE_WORLD = 2; //drop/pickup item entity
|
||||
public const SOURCE_CREATIVE = 3;
|
||||
public const SOURCE_CRAFTING_GRID = 100;
|
||||
public const SOURCE_TODO = 99999;
|
||||
|
||||
/**
|
||||
@ -108,17 +108,8 @@ class NetworkInventoryAction{
|
||||
break;
|
||||
case self::SOURCE_CREATIVE:
|
||||
break;
|
||||
case self::SOURCE_CRAFTING_GRID:
|
||||
case self::SOURCE_TODO:
|
||||
$this->windowId = $packet->getVarInt();
|
||||
switch($this->windowId){
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case self::SOURCE_TYPE_CRAFTING_RESULT:
|
||||
$packet->isFinalCraftingPart = true;
|
||||
case self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT:
|
||||
$packet->isCraftingPart = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType");
|
||||
@ -146,7 +137,6 @@ class NetworkInventoryAction{
|
||||
break;
|
||||
case self::SOURCE_CREATIVE:
|
||||
break;
|
||||
case self::SOURCE_CRAFTING_GRID:
|
||||
case self::SOURCE_TODO:
|
||||
$packet->putVarInt($this->windowId);
|
||||
break;
|
||||
@ -167,11 +157,37 @@ class NetworkInventoryAction{
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function createInventoryAction(Player $player){
|
||||
if($this->oldItem->equalsExact($this->newItem)){
|
||||
//filter out useless noise in 1.13
|
||||
return null;
|
||||
}
|
||||
switch($this->sourceType){
|
||||
case self::SOURCE_CONTAINER:
|
||||
$window = $player->getWindow($this->windowId);
|
||||
if($this->windowId === ContainerIds::UI and $this->inventorySlot > 0){
|
||||
if($this->inventorySlot === 50){
|
||||
return null; //useless noise
|
||||
}
|
||||
if($this->inventorySlot >= 28 and $this->inventorySlot <= 31){
|
||||
$window = $player->getCraftingGrid();
|
||||
if($window->getGridWidth() !== CraftingGrid::SIZE_SMALL){
|
||||
throw new \UnexpectedValueException("Expected small crafting grid");
|
||||
}
|
||||
$slot = $this->inventorySlot - 28;
|
||||
}elseif($this->inventorySlot >= 32 and $this->inventorySlot <= 40){
|
||||
$window = $player->getCraftingGrid();
|
||||
if($window->getGridWidth() !== CraftingGrid::SIZE_BIG){
|
||||
throw new \UnexpectedValueException("Expected big crafting grid");
|
||||
}
|
||||
$slot = $this->inventorySlot - 32;
|
||||
}else{
|
||||
throw new \UnexpectedValueException("Unhandled magic UI slot offset $this->inventorySlot");
|
||||
}
|
||||
}else{
|
||||
$window = $player->getWindow($this->windowId);
|
||||
$slot = $this->inventorySlot;
|
||||
}
|
||||
if($window !== null){
|
||||
return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem);
|
||||
return new SlotChangeAction($window, $slot, $this->oldItem, $this->newItem);
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException("Player " . $player->getName() . " has no open container with window ID $this->windowId");
|
||||
@ -195,7 +211,6 @@ class NetworkInventoryAction{
|
||||
}
|
||||
|
||||
return new CreativeInventoryAction($this->oldItem, $this->newItem, $type);
|
||||
case self::SOURCE_CRAFTING_GRID:
|
||||
case self::SOURCE_TODO:
|
||||
//These types need special handling.
|
||||
switch($this->windowId){
|
||||
|
45
src/pocketmine/network/mcpe/protocol/types/PlayMode.php
Normal file
45
src/pocketmine/network/mcpe/protocol/types/PlayMode.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Enum used by PlayerAuthInputPacket. Most of these names don't make any sense, but that isn't surprising.
|
||||
*/
|
||||
final class PlayMode{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public const NORMAL = 0;
|
||||
public const TEASER = 1;
|
||||
public const SCREEN = 2;
|
||||
public const VIEWER = 3;
|
||||
public const VR = 4;
|
||||
public const PLACEMENT = 5;
|
||||
public const LIVING_ROOM = 6;
|
||||
public const EXIT_LEVEL = 7;
|
||||
public const EXIT_LEVEL_LIVING_ROOM = 8;
|
||||
|
||||
}
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\utils\UUID;
|
||||
|
||||
class PlayerListEntry{
|
||||
@ -34,12 +33,18 @@ class PlayerListEntry{
|
||||
public $entityUniqueId;
|
||||
/** @var string */
|
||||
public $username;
|
||||
/** @var Skin */
|
||||
public $skin;
|
||||
/** @var SkinData */
|
||||
public $skinData;
|
||||
/** @var string */
|
||||
public $xboxUserId;
|
||||
/** @var string */
|
||||
public $platformChatId = "";
|
||||
/** @var int */
|
||||
public $buildPlatform = -1;
|
||||
/** @var bool */
|
||||
public $isTeacher = false;
|
||||
/** @var bool */
|
||||
public $isHost = false;
|
||||
|
||||
public static function createRemovalEntry(UUID $uuid) : PlayerListEntry{
|
||||
$entry = new PlayerListEntry();
|
||||
@ -48,14 +53,17 @@ class PlayerListEntry{
|
||||
return $entry;
|
||||
}
|
||||
|
||||
public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "") : PlayerListEntry{
|
||||
public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, SkinData $skinData, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = -1, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{
|
||||
$entry = new PlayerListEntry();
|
||||
$entry->uuid = $uuid;
|
||||
$entry->entityUniqueId = $entityUniqueId;
|
||||
$entry->username = $username;
|
||||
$entry->skin = $skin;
|
||||
$entry->skinData = $skinData;
|
||||
$entry->xboxUserId = $xboxUserId;
|
||||
$entry->platformChatId = $platformChatId;
|
||||
$entry->buildPlatform = $buildPlatform;
|
||||
$entry->isTeacher = $isTeacher;
|
||||
$entry->isHost = $isHost;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
<?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;
|
||||
|
||||
class PotionContainerChangeRecipe{
|
||||
/** @var int */
|
||||
private $inputItemId;
|
||||
/** @var int */
|
||||
private $ingredientItemId;
|
||||
/** @var int */
|
||||
private $outputItemId;
|
||||
|
||||
public function __construct(int $inputItemId, int $ingredientItemId, int $outputItemId){
|
||||
$this->inputItemId = $inputItemId;
|
||||
$this->ingredientItemId = $ingredientItemId;
|
||||
$this->outputItemId = $outputItemId;
|
||||
}
|
||||
|
||||
public function getInputItemId() : int{
|
||||
return $this->inputItemId;
|
||||
}
|
||||
|
||||
public function getIngredientItemId() : int{
|
||||
return $this->ingredientItemId;
|
||||
}
|
||||
|
||||
public function getOutputItemId() : int{
|
||||
return $this->outputItemId;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?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;
|
||||
|
||||
class PotionTypeRecipe{
|
||||
/** @var int */
|
||||
private $inputPotionType;
|
||||
/** @var int */
|
||||
private $ingredientItemId;
|
||||
/** @var int */
|
||||
private $outputPotionType;
|
||||
|
||||
public function __construct(int $inputPotionType, int $ingredientItemId, int $outputPotionType){
|
||||
$this->inputPotionType = $inputPotionType;
|
||||
$this->ingredientItemId = $ingredientItemId;
|
||||
$this->outputPotionType = $outputPotionType;
|
||||
}
|
||||
|
||||
public function getInputPotionType() : int{
|
||||
return $this->inputPotionType;
|
||||
}
|
||||
|
||||
public function getIngredientItemId() : int{
|
||||
return $this->ingredientItemId;
|
||||
}
|
||||
|
||||
public function getOutputPotionType() : int{
|
||||
return $this->outputPotionType;
|
||||
}
|
||||
}
|
@ -30,9 +30,12 @@ final class ResourcePackType{
|
||||
}
|
||||
|
||||
public const INVALID = 0;
|
||||
public const RESOURCES = 1;
|
||||
public const BEHAVIORS = 2;
|
||||
public const WORLD_TEMPLATE = 3;
|
||||
public const ADDON = 4; //scripts?
|
||||
public const SKINS = 5;
|
||||
public const ADDON = 1;
|
||||
public const CACHED = 2;
|
||||
public const COPY_PROTECTED = 3;
|
||||
public const BEHAVIORS = 4;
|
||||
public const PERSONA_PIECE = 5;
|
||||
public const RESOURCES = 6;
|
||||
public const SKINS = 7;
|
||||
public const WORLD_TEMPLATE = 8;
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
use pocketmine\block\BlockIds;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use function file_get_contents;
|
||||
use function getmypid;
|
||||
use function json_decode;
|
||||
@ -40,40 +44,71 @@ final class RuntimeBlockMapping{
|
||||
private static $legacyToRuntimeMap = [];
|
||||
/** @var int[] */
|
||||
private static $runtimeToLegacyMap = [];
|
||||
/** @var mixed[] */
|
||||
private static $bedrockKnownStates;
|
||||
/** @var CompoundTag[]|null */
|
||||
private static $bedrockKnownStates = null;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public static function init() : void{
|
||||
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
|
||||
|
||||
$compressedTable = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.json"), true);
|
||||
$decompressed = [];
|
||||
|
||||
foreach($compressedTable as $prefix => $entries){
|
||||
foreach($entries as $shortStringId => $states){
|
||||
foreach($states as $state){
|
||||
$name = "$prefix:$shortStringId";
|
||||
$decompressed[] = [
|
||||
"name" => $name,
|
||||
"data" => $state,
|
||||
"legacy_id" => $legacyIdMap[$name]
|
||||
];
|
||||
}
|
||||
}
|
||||
$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");
|
||||
}
|
||||
self::$bedrockKnownStates = self::randomizeTable($decompressed);
|
||||
|
||||
foreach(self::$bedrockKnownStates as $k => $obj){
|
||||
if($obj["data"] > 15){
|
||||
//TODO: in 1.12 they started using data values bigger than 4 bits which we can't handle right now
|
||||
/** @var CompoundTag[] $list */
|
||||
$list = $tag->getValue();
|
||||
self::$bedrockKnownStates = self::randomizeTable($list);
|
||||
|
||||
self::setupLegacyMappings();
|
||||
}
|
||||
|
||||
private static function setupLegacyMappings() : void{
|
||||
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
|
||||
$legacyStateMap = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt"));
|
||||
if(!($legacyStateMap instanceof ListTag) or $legacyStateMap->getTagType() !== NBT::TAG_Compound){
|
||||
throw new \RuntimeException("Invalid legacy states mapping table, expected TAG_List<TAG_Compound> root");
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int[][] $idToStatesMap string id -> int[] list of candidate state indices
|
||||
*/
|
||||
$idToStatesMap = [];
|
||||
foreach(self::$bedrockKnownStates as $k => $state){
|
||||
$idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
|
||||
}
|
||||
/** @var CompoundTag $pair */
|
||||
foreach($legacyStateMap as $pair){
|
||||
$oldState = $pair->getCompoundTag("old");
|
||||
$id = $legacyIdMap[$oldState->getString("name")];
|
||||
$data = $oldState->getShort("val");
|
||||
if($data > 15){
|
||||
//we can't handle metadata with more than 4 bits
|
||||
continue;
|
||||
}
|
||||
//this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries
|
||||
self::registerMapping($k, $obj["legacy_id"], $obj["data"]);
|
||||
$mappedState = $pair->getCompoundTag("new");
|
||||
|
||||
//TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values
|
||||
$mappedState->setName("block");
|
||||
$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"))){
|
||||
self::registerMapping($k, $id, $data);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException("Mapped new state does not appear in network table");
|
||||
}
|
||||
}
|
||||
|
||||
private static function lazyInit() : void{
|
||||
if(self::$bedrockKnownStates === null){
|
||||
self::init();
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,9 +117,9 @@ final class RuntimeBlockMapping{
|
||||
* 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 array $table
|
||||
* @param CompoundTag[] $table
|
||||
*
|
||||
* @return array
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
private static function randomizeTable(array $table) : array{
|
||||
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
|
||||
@ -101,6 +136,7 @@ final class RuntimeBlockMapping{
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
self::lazyInit();
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
@ -115,6 +151,7 @@ final class RuntimeBlockMapping{
|
||||
* @return int[] [id, meta]
|
||||
*/
|
||||
public static function fromStaticRuntimeId(int $runtimeId) : array{
|
||||
self::lazyInit();
|
||||
$v = self::$runtimeToLegacyMap[$runtimeId];
|
||||
return [$v >> 4, $v & 0xf];
|
||||
}
|
||||
@ -125,10 +162,10 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
public static function getBedrockKnownStates() : array{
|
||||
self::lazyInit();
|
||||
return self::$bedrockKnownStates;
|
||||
}
|
||||
}
|
||||
RuntimeBlockMapping::init();
|
||||
|
48
src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php
Normal file
48
src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php
Normal 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\network\mcpe\protocol\types;
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
|
||||
/**
|
||||
* Used to convert new skin data to the skin entity or old skin entity to skin data.
|
||||
*/
|
||||
interface SkinAdapter{
|
||||
|
||||
/**
|
||||
* Allows you to convert a skin entity to skin data.
|
||||
*
|
||||
* @param Skin $skin
|
||||
* @return SkinData
|
||||
*/
|
||||
public function toSkinData(Skin $skin) : SkinData;
|
||||
|
||||
/**
|
||||
* Allows you to convert skin data to a skin entity.
|
||||
*
|
||||
* @param SkinData $data
|
||||
* @return Skin
|
||||
*/
|
||||
public function fromSkinData(SkinData $data) : Skin;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user