Compare commits

..

167 Commits

Author SHA1 Message Date
a37d740111 Release 3.11.2 2019-12-29 13:11:30 +00:00
2de0ec02ba phpstan 0.12.3 2019-12-28 17:22:08 +00:00
d83820477f TimingsHandler: throw on attempt to stop timer that is not running
I do not think that this bug is in effect in the core code, but I can't be sure.
2019-12-21 13:17:40 +00:00
8726604899 Merge #3251: Remove usages of empty() 2019-12-19 11:08:08 +00:00
9cbe378e8c Timezone: fix possible crash on CentOS 2019-12-18 11:23:24 +00:00
494660102e Replace empty() usages with count() 2019-12-18 11:23:24 +00:00
216138a37e PlayerAuthInputPacket: Fix assigning variable in static context 2019-12-17 22:04:24 +08:00
b08c38f8f9 build/make-release: remove redundant require_once
this is included by vendor/autoload.php since 3.7.x.
2019-12-17 11:47:07 +00:00
911b6feaf9 NetworkInventoryAction: remove dead code
the trading & beacon types need to be checked as well, but I don't have the tools to check that right now.
2019-12-17 11:17:40 +00:00
2cb6990698 Enchantment: don't throw exceptions on out of range IDs
this function is used for data deserialization, and data may have bad enchantment IDs in it.
2019-12-14 10:04:51 +00:00
f7d66613df CompletedUsingItemPacket: added missing #include, closes #3219 2019-12-13 19:50:40 +00:00
95c32d26df Explosion: fixed ray trace getting stuck on a block when it encounters an empty subchunk
this might have caused unexpected behaviour in large caves.
2019-12-13 18:07:59 +00:00
9e1f6a2486 protocol: updated particle IDs 2019-12-13 10:52:09 +00:00
76994f15ac phpstan: green on level 5 2019-12-12 18:27:26 +00:00
cf73d74bd0 format/anvil: fixed possible type violation on saving chunk 2019-12-12 18:24:21 +00:00
37a8d95464 world IO: fixed crashes when garbage data found in tile/entity NBT data 2019-12-12 18:21:23 +00:00
9a4b72add5 PlayerInventory: fix type violation when calling equipItem() for non-Player holder 2019-12-12 16:31:22 +00:00
919534d978 EnderChest: fixed crash when plugins overwrite tile classes with incompatible ones
relates to 47a959dace
2019-12-12 16:28:30 +00:00
cb598155a4 Server: add @return annotation to crashDump() to make phpstan happy
this is technically a bug in PHPStan, but it's easier to do this than report a bug.
2019-12-12 16:19:57 +00:00
00888fdc55 TranslationContainer::__construct() accepts float and int too (they can be casted to string) 2019-12-12 16:19:04 +00:00
77795ae3bc BaseLang::translateString() accepts float and int too (they can be casted to string) 2019-12-12 16:18:11 +00:00
f39fc7e525 CompressBatchedTask::__construct() accepts Player[] not string[] 2019-12-12 16:16:48 +00:00
77f7595e0e Location::__construct() accepts floats for x,y,z 2019-12-12 16:16:16 +00:00
e8d3a25028 Position::__construct() accepts floats for x,y,z 2019-12-12 16:15:49 +00:00
1370930ea9 Entity: remove redundant defaults from lastX lastY lastZ, remove nullability
these fields are never null because they are initialized in the constructor, and they are never written to with null nor ever expected to be null.
2019-12-12 16:14:30 +00:00
70c3008b7b phpstan: green on level 4 2019-12-12 13:00:57 +00:00
46930b98b7 Timings: add a dedicated field for checking initialization 2019-12-12 12:59:01 +00:00
92be8c8ec0 PopulationTask: don't assume anything about TLS return values
while these SHOULD be what we say they are, it's possible for something else to overwrite them with junk and make the server catch fire.
2019-12-12 12:19:54 +00:00
62069bc7af Level: fix return type content doc comment for getAdjacentChunks() 2019-12-12 12:18:35 +00:00
26230c1f9b Player: don't report never-played for a disconnected player
this should catch fire like everything else does.
2019-12-12 12:17:52 +00:00
a9fafbc5eb BanEntry: remove nullable return from parseDate()
this function never returns null
2019-12-12 11:59:41 +00:00
b8778cb791 LevelProvider: fix doc comment of ::generate()
this raised false positives on phpstan level 4
2019-12-12 11:59:12 +00:00
73c5fe5cf9 BaseInventory: correctly annotate content type of slots field
fixes warnings on phpstan level 4
2019-12-12 11:58:11 +00:00
b3cfa5a3a0 TimingsHandler: use weak comparison for stopTiming start check
this could be an int or float, and 0 !== 0.0.
2019-12-12 11:48:34 +00:00
6127a02a8b phpstan 0.12.2 2019-12-12 10:43:45 +00:00
dbca36e5e2 SkinImage: throw on wrong data length 2019-12-11 22:45:14 +00:00
1171bae691 login: read the correct key SkinAnimationData 2019-12-11 22:40:05 +00:00
494f8e8251 login: fixed missing base64_decode() for animation images 2019-12-11 22:39:40 +00:00
3c9af56e06 3.11.2 is next 2019-12-11 20:20:26 +00:00
40a2211a5a Release 3.11.1 2019-12-11 20:20:26 +00:00
0196aa21d7 updated BedrockData submodule, close #3222, close #3223 2019-12-11 20:19:17 +00:00
833f3e574b 3.11.1 is next 2019-12-11 18:35:03 +00:00
a386ff8ce7 Release 3.11.0 2019-12-11 18:35:03 +00:00
e6c3b0fc0d Changes for 1.14
there are no protocol changes in 1.14 that matter; the reason the protocol version has been bumped is because of new blocks, items, mobs and recipes.
2019-12-11 18:32:22 +00:00
9568364277 fix minor formatting issue in SkinAdapterSingleton 2019-12-09 21:19:15 +00:00
73d4ff6b52 more missed function imports 2019-12-09 21:16:35 +00:00
7e98aa1497 leveldb: remove usage of multi-import statement
this doesn't conform to the code style.
2019-12-09 20:28:02 +00:00
f00c69c513 added missing function imports 2019-12-09 20:13:02 +00:00
50a4c42f3f NetworkBinaryStream: fix misplaced import 2019-12-09 19:53:32 +00:00
6b61efcfc8 remove unused imports 2019-12-09 19:49:21 +00:00
1a99938e4b LegacySkinAdapter: clean up nonsensical code ordering 2019-12-09 18:26:20 +00:00
a4d68fb32b LegacySkinAdapter: fix some wacky cape handling logic
I added checks for length validity locally and ran into some interesting behaviour because of this code.
2019-12-09 18:16:46 +00:00
5682cc8d53 3.10.2 is next 2019-12-07 16:41:41 +00:00
4eb59c0372 Release 3.10.1 2019-12-07 16:41:41 +00:00
3486db3f1b update RakLib to 0.12.6 2019-12-07 14:20:35 +00:00
733d530ed0 fixing EOF newlines on new files 2019-12-07 10:08:29 +00:00
c10ce84035 fix invisible legacy skins which have empty geometry names 2019-12-07 10:02:22 +00:00
82d9e481d2 3.10.1 is next 2019-12-06 20:53:15 +00:00
d27c7f7141 Release 3.10.0 2019-12-06 20:53:15 +00:00
394c999710 Merge branch 'drew-1.13' into stable 2019-12-06 20:38:49 +00:00
b6bbf655d7 InventoryTransactionPacket: fixed crafting flags being set on useless empty transactions
this fixes some misleading debug noise when getting stuff from creative inventory and triggers some different spam instead.
2019-12-06 20:08:09 +00:00
3d2c018442 BiomeDefinitionListPacket: load pregenerated data from file 2019-12-06 11:32:33 +00:00
da8c54cf8b AvailableEntityIdentifiersPacket: load pregenerated data from file 2019-12-06 11:31:55 +00:00
8e984a1bc3 RuntimeBlockMapping: use new data, item frames & doors fixed
floor & ceiling item frames not supported though
2019-12-06 11:31:18 +00:00
124e60301a updated submodule to pmmp/BedrockData@a38b427888 2019-12-06 11:26:58 +00:00
e3cffca34b StartGamePacket: fixed possible type violation on decoding block table 2019-12-05 11:05:34 +00:00
d20d9fb689 Merge branch 'stable' into drew-1.13 2019-12-05 10:59:53 +00:00
740f0a2314 crafting now works on 1.13, but it's not good 2019-12-02 21:50:52 +00:00
7fdfe947b0 inventory: fix some transactions being rejected for no good reason
since 1.13, transactions such as interacting with creative inventory cause a spoof windowID 124 slot 50 action to appear which changes air -> air. This currently gets rejected because only cursor is mapped to ID 124, and it only has a single slot.

It is not clear what the purpose of 124:50 is, but this fix filters out any actions which do not change anything, since they won't affect transaction balance anyway.
2019-12-02 21:41:08 +00:00
b7c4379700 StartGamePacket: fixed asymmetry in block table handling 2019-12-02 19:35:55 +00:00
20b7418916 PlayerAuthInputPacket: added encode & decode, and some aux classes 2019-12-02 15:14:59 +00:00
85521f5e7a EducationSettingsPacket: added encode & decode 2019-12-02 13:57:19 +00:00
f37ea6a203 AvailableCommandsPacket: fix nonsensical placement of HARDCODED_ENUMS 2019-12-02 12:57:05 +00:00
abf8081ebc RuntimeBlockMapping: add a type check for decoded NBT root type
this is kinda redundant since this function can blow up in so many other ways anyway, but it makes PHPStan happy, so it'll do.
2019-12-02 08:21:42 +00:00
8594cb3e74 AvailableCommandsPacket: fixed doc comment for putEnumConstraint() 2019-12-02 08:17:00 +00:00
d155de35ed Merge branch 'dktapps-drew-1.13' into stable 2019-12-01 22:03:03 -05:00
e37c8e3a5d Merge branch 'drew-1.13' of https://github.com/dktapps/PocketMine-MP into dktapps-drew-1.13 2019-12-01 22:02:40 -05:00
e38c0c0fe1 Merge remote-tracking branch 'upstream/stable' into stable 2019-12-01 21:55:50 -05:00
d8188b807a CraftingDataPacket: read & write potion recipes 2019-11-30 21:18:54 +00:00
8e68655fc7 Merge branch 'stable' into drew-1.13 2019-11-30 21:15:50 +00:00
b7a5a53c9d MoveActorDeltaPacket: flags is now a short 2019-11-30 12:56:16 +00:00
76bd0f452c AvailableCommandsPacket: add special handling for enums which aren't referenced by any command directly
the CommandName enum is a magic enum used by the  argtype.

TODO: It's possible that not sending the CommandName enum might be causing client sided crashes. Investigate.
2019-11-30 12:41:44 +00:00
363556e9b6 AvailableCommandsPacket: encode & decode for enum value constraints
This is a peculiarly overengineered system that is used for restricting access to enum members under certain conditions, e.g. to disallow changing specific gamerules in survival.
2019-11-30 12:31:31 +00:00
6f08853b29 Merge branch 'stable' into drew-1.13 2019-11-30 12:04:46 +00:00
5d17405b92 Better checks on resource patch 2019-11-28 19:48:34 -05:00
3dd53ad998 Remove offset for crafting grid 2019-11-28 19:42:34 -05:00
a303c4b294 Add some isset checks to prevent server from crashing on an invalid resourcepatch 2019-11-21 17:58:33 -05:00
fa56290bb4 Reflect StartGamePacket changes in Player 2019-11-21 15:05:09 -05:00
01d6cbe9c3 Added : void typehint to SkinAdapterSingleton & provide documentation 2019-11-21 15:01:34 -05:00
f682c16740 Changed eduMode to eduEditionOffer and added an unsignedVarInt in availablecommandspacket 2019-11-21 14:55:29 -05:00
74c09dc202 Remove inline assignment 2019-11-21 14:49:01 -05:00
0917b67573 Generate param docs for constructor 2019-11-21 14:46:10 -05:00
5cb0eafcb2 Should be checking the string, not an object 2019-11-21 14:24:47 -05:00
221e6db47d Remove persona capes off of classic skins, add checks for empty cape data when converting 2019-11-21 14:21:08 -05:00
8d06018d81 make toSkinData return statement multi lined 2019-11-21 13:54:22 -05:00
600e16d9f6 Fixed floatingtextparticle yet again 2019-11-21 13:54:00 -05:00
4340349db7 Generate getters for SkinData, applied suggested change, and fixed an underfined variable in PlayerListPacket 2019-11-21 13:52:27 -05:00
6105198313 Update AvailableCommandsPacket constants 2019-11-20 22:47:48 -05:00
c96ba13c23 Extract geometry name from resource patch 2019-11-20 22:42:56 -05:00
c8d0cb315b Move persona skin hack to legacyskinadapter 2019-11-20 21:52:49 -05:00
be9c413a9e Added network components for skins, to collect instead of throw out data 2019-11-20 21:43:41 -05:00
7f3460190b Merge pull request #5 from dktapps/drew-1.13
more 1.13
2019-11-12 14:19:31 -05:00
10d44292e1 fix classic capes 2019-11-12 12:27:50 -05:00
70f81334ae MultiplayerSettingsPacket 2019-11-12 11:58:40 -05:00
ead572fab9 fix skin sharing stupidity 2019-11-12 07:08:48 -05:00
ef8e286277 Merge branch 'stable' of https://github.com/drew-mcbe/pocketmine-mp into drew-1.13 2019-11-12 06:57:57 -05:00
ba5a5981a0 Update Player.php 2019-11-12 08:19:05 +00:00
c428596009 AddPlayerPacket: missing field 2019-11-11 16:36:55 -05:00
a81d8dd6d5 Return false in unexpected condition 2019-11-11 16:25:53 -05:00
5bcbef90ea Added variables for getSkin's animation for easier readability 2019-11-11 15:32:48 -05:00
1c67f094e3 Change get and put SkinImage visibility to private 2019-11-11 15:29:57 -05:00
99d350914e Remove convertToLegacyName 2019-11-11 15:29:42 -05:00
49a9e0a880 Remove changes to FloatingTextParticle 2019-11-11 15:26:36 -05:00
7b152def7d Move SkinAnimation and changed SerializedImage to SkinImage
Also change putImage and getImage to getSkinImage and putSkinImage
2019-11-11 15:23:48 -05:00
3eb78fb0ff Merge pull request #4 from dktapps/drew-1.13
moar 1.13 changes
2019-11-11 15:04:33 -05:00
38c759c86e ResourcePackStack: missing field 2019-11-11 14:58:45 -05:00
7fcd40df15 add encode/decode for some new packets 2019-11-11 11:20:37 -05:00
ba39327b28 Merge remote-tracking branch 'origin/stable' into drew-1.13 2019-11-11 10:42:25 -05:00
8ee0fbccc5 Merge branch 'stable' of https://github.com/drew-mcbe/pocketmine-mp into drew-1.13 2019-11-11 05:48:17 -05:00
fe4354959b SkinAnimation: fix typo 2019-11-11 10:07:45 +00:00
19377c86a4 PlayerSkinPacket: reduce diff pollution 2019-11-11 09:47:04 +00:00
fb23aade34 Clean up unused imports 2019-11-10 21:49:58 -05:00
57e9fe78a3 change buildPlatform parameter to -1 2019-11-10 21:45:33 -05:00
6b97281c58 Update SkinAnimation constants and docs 2019-11-10 21:41:00 -05:00
da67a085fc Remove unused imports and unused todo 2019-11-10 21:37:55 -05:00
e2fc7cdf88 Friendly BC skins (persona not supported) 2019-11-10 21:04:38 -05:00
635bb08fb9 Update playerskinpacket to use the strings 2019-11-10 19:39:07 -05:00
308d9ce3a8 auto generated baseline 2019-11-10 17:40:04 -05:00
5a76c38363 Merge remote-tracking branch 'origin/stable' into drew-1.13 2019-11-10 17:02:46 -05:00
54530da6c1 reduce visibility of offset to private 2019-11-10 00:09:29 -05:00
dd55a0bccd Use unknown os for PlayerListEntry 2019-11-10 00:01:12 -05:00
f03bd982b5 Empty fields in PlayerSkinPacket, thanks @Minejong 2019-11-09 23:56:18 -05:00
d53b84386f Merge pull request #3 from dktapps/drew-1.13
clean up 1.13 a little
2019-11-08 16:46:01 +05:00
a7ed933b37 superfluous import 2019-11-08 11:32:09 +00:00
29cc9283f8 reverse unnecessary formatting changes 2019-11-08 11:29:00 +00:00
e60962c31f reverse BC-breaking change to Player->changeSkin()
this change would cause LSP violations for subclasses
2019-11-08 11:22:55 +00:00
f6b5301e17 Player: remove unused import 2019-11-08 11:10:20 +00:00
73b923e3a1 cleanup eating clusterfuck 2019-11-08 11:08:22 +00:00
d2e4eb40b3 CompletedUsingItemPacket is superfluous 2019-11-08 08:29:25 +00:00
6a31628e78 Clean up whitespace disaster and add getIngredient back 2019-11-07 23:43:26 -05:00
bfb1ad1327 Removed PlayerUIInventory and BaseUIInventory 2019-11-07 23:17:01 -05:00
dfa603c335 Simplify if null statement 2019-11-07 20:06:46 -05:00
eef979db4c Switch to isUsingItem()
I don't plan on using this, as you informed me on discord that I didn't need the extra step, and it's a BC break anyway
2019-11-07 19:49:04 -05:00
6166c90bfd No need for a try catch statement around reading a file 2019-11-07 19:45:47 -05:00
35d1d0080a Removed count const and fixed BC break 2019-11-07 19:45:33 -05:00
3bacb1a9cb Moved skin back
Not too sure what I planned on doing with that
2019-11-07 19:45:20 -05:00
898af49e97 Add typehints to completedusingitempacket 2019-11-07 19:45:06 -05:00
3061eb4157 Fixed convertToLegacyName not using variables, add typehints to it too 2019-11-07 19:44:49 -05:00
b8d1d8f212 Clean up unused imports 2019-11-07 19:43:53 -05:00
8c6189775b Removed root directory runtime states 2019-11-07 19:33:53 -05:00
7dec912d15 Fixed FloatingTextParticles 2019-11-06 23:22:41 -05:00
dbd36d66ae Cape data can be null 2019-11-06 23:01:06 -05:00
40b4166a6e created method sendCompletedUsingItemPacket for less duplication 2019-11-06 22:30:05 -05:00
51d18ffb89 Merge branch 'stable' of https://github.com/pmmp/PocketMine-MP into stable 2019-11-06 22:10:41 -05:00
af3c7b7c76 Updated ResourcePackType 2019-11-06 21:57:09 -05:00
3511ac010d Fixed crafting 2019-11-06 20:42:53 -05:00
07f19dd4a1 Fixed respawning 2019-11-05 22:33:12 -05:00
17a17c31f3 Fixed items, but I'm not so sure how well this'll work out, it'll need improved in the future 2019-11-05 22:25:11 -05:00
75742b487f Fixed stupid mistake 2019-11-05 21:56:20 -05:00
4e9a2b6d8c Added CompletedUsingItemPacket 2019-11-05 21:19:49 -05:00
4ea907ae1a Start saving new skin data 2019-11-05 21:01:01 -05:00
8b912c1363 Removed some useless casts 2019-11-05 20:59:48 -05:00
080209c469 Fixed persona skins being messed up on join 2019-11-05 20:35:33 -05:00
5b11ddee35 Clean up duplicated skin entries 2019-11-05 20:30:41 -05:00
3b7ded0ba3 Fixed changing skins crashing the server 2019-11-05 20:25:45 -05:00
5ebe9859e9 Moved runtime_block_states to vanilla 2019-11-04 21:47:44 -05:00
cd2b60a860 Initial update to 1.13 2019-10-30 21:36:08 -04:00
105 changed files with 2343 additions and 469 deletions

View File

@ -36,7 +36,6 @@ use function system;
use const pocketmine\BASE_VERSION;
use const STDIN;
require_once dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
require_once dirname(__DIR__) . '/vendor/autoload.php';
if(isset($argv[1])){

20
changelogs/3.10.md Normal file
View 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.

35
changelogs/3.11.md Normal file
View File

@ -0,0 +1,35 @@
**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.
# 3.11.2
## Core
- PHPStan 0.12.3 with level 5 is now used for automated static analysis.
- Fixed a possible crash when plugins override the `EnderChest` tile class with something incompatible.
- Fixed disconnected players being considered as never played.
- Fixed enchantments with IDs outside the range 0-255 in item NBT crashing the server.
- Fixed particles rendering incorrectly.
- Timings handlers are no longer able to underflow; they now throw exceptions when attempting to be stopped more times than they were started.
- Fixed explosion rays getting stuck in empty subchunks (possible incorrect behaviour in large caves).
- Fixed bad tile/entity NBT data being propagated from world providers in some cases.
- Fixed a possible crash when detecting timezone on CentOS.
- Fixed many cases of incorrectly documented types in the API found by PHPStan.
- Generation tasks no longer assume that generator instances stored in TLS are always valid, fixing a possible crash.
## Protocol
- Fixed skin animation image corruption in LoginPacket handling caused by incorrect data handling.
- Fixed skin animation extra data not being decoded from LoginPacket.
- `SkinImage` now throws `InvalidArgumentException` if it receives an unexpected amount of bytes for the given image heigh/width.
- Fixed broken code in `PlayerAuthInputPacket::create()`.
- Removed some dead constants from `NetworkInventoryAction`.

12
composer.lock generated
View File

@ -201,16 +201,16 @@
},
{
"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",

View File

@ -1,12 +1,14 @@
includes:
- tests/phpstan/configs/debug-const-checks.neon
- 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
- tests/phpstan/configs/runtime-type-checks.neon
parameters:
level: 3
level: 5
autoload_files:
- tests/phpstan/bootstrap.php
- src/pocketmine/PocketMine.php

View File

@ -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;
@ -460,11 +464,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function getFirstPlayed(){
return $this->namedtag instanceof CompoundTag ? $this->namedtag->getLong("firstPlayed", 0, true) : null;
return $this->namedtag->getLong("firstPlayed", 0, true);
}
public function getLastPlayed(){
return $this->namedtag instanceof CompoundTag ? $this->namedtag->getLong("lastPlayed", 0, true) : null;
return $this->namedtag->getLong("lastPlayed", 0, true);
}
public function hasPlayedBefore() : bool{
@ -737,7 +741,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$data->overloads[0][0] = $parameter;
$aliases = $command->getAliases();
if(!empty($aliases)){
if(count($aliases) > 0){
if(!in_array($data->commandName, $aliases, true)){
//work around a client bug which makes the original name not show when aliases are used
$aliases[] = $data->commandName;
@ -1112,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);
}
@ -1202,7 +1208,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
$this->loadQueue = $newOrder;
if(!empty($this->loadQueue) or !empty($unloadChunks)){
if(count($this->loadQueue) > 0 or count($unloadChunks) > 0){
$pk = new NetworkChunkPublisherUpdatePacket();
$pk->x = $this->getFloorX();
$pk->y = $this->getFloorY();
@ -1913,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"], base64_decode($animation["Image"], true)), $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["SkinAnimationData"] ?? ""),
$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");
@ -2156,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;
@ -2491,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()){
@ -2644,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;
}
@ -2913,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.
*
@ -3805,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);

View File

@ -140,7 +140,7 @@ namespace pocketmine {
}
function server(){
if(!empty($messages = check_platform_dependencies())){
if(count($messages = check_platform_dependencies()) > 0){
echo PHP_EOL;
$binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
critical_error("Selected PHP binary ($binary) does not satisfy some requirements.");

View File

@ -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;
@ -1211,9 +1212,9 @@ class Server{
}
$path = $this->getDataPath() . "worlds/" . $name . "/";
if(!($this->getLevelByName($name) instanceof Level)){
return is_dir($path) and !empty(array_filter(scandir($path, SCANDIR_SORT_NONE), function($v){
return is_dir($path) and count(array_filter(scandir($path, SCANDIR_SORT_NONE), function($v){
return $v !== ".." and $v !== ".";
}));
})) > 0;
}
return true;
@ -1927,14 +1928,14 @@ class Server{
* @param bool $immediate
*/
public function batchPackets(array $players, array $packets, bool $forceSync = false, bool $immediate = false){
if(empty($packets)){
if(count($packets) === 0){
throw new \InvalidArgumentException("Cannot send empty batch");
}
Timings::$playerNetworkTimer->startTiming();
$targets = array_filter($players, function(Player $player) : bool{ return $player->isConnected(); });
if(!empty($targets)){
if(count($targets) > 0){
$pk = new BatchPacket();
foreach($packets as $p){
@ -2245,6 +2246,9 @@ class Server{
$this->crashDump();
}
/**
* @return void
*/
public function crashDump(){
while(@ob_end_flush()){}
if(!$this->isRunning){
@ -2395,7 +2399,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);
}
@ -2418,7 +2422,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);

View File

@ -30,6 +30,6 @@ const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.9.8";
const BASE_VERSION = "3.11.2";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;

View File

@ -40,6 +40,7 @@ use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
use pocketmine\Player;
use pocketmine\plugin\Plugin;
use function array_merge;
use function count;
use function get_class;
use const PHP_INT_MAX;
@ -727,7 +728,7 @@ class Block extends Position implements BlockIds, Metadatable{
*/
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{
$bbs = $this->getCollisionBoxes();
if(empty($bbs)){
if(count($bbs) === 0){
return null;
}

View File

@ -84,6 +84,9 @@ class EnderChest extends Chest{
$enderChest = $t;
}else{
$enderChest = Tile::createTile(Tile::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this));
if(!($enderChest instanceof TileEnderChest)){
return true;
}
}
if(!$this->getSide(Vector3::SIDE_UP)->isTransparent()){

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use function count;
abstract class Fence extends Transparent{
@ -85,7 +86,7 @@ abstract class Fence extends Transparent{
);
}
if(empty($bbs)){
if(count($bbs) === 0){
//centre post AABB (only needed if not connected on any axis - other BBs overlapping will do this if any connections are made)
return [
new AxisAlignedBB(

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use function count;
abstract class Thin extends Transparent{
@ -77,7 +78,7 @@ abstract class Thin extends Transparent{
);
}
if(empty($bbs)){
if(count($bbs) === 0){
//centre post AABB (only needed if not connected on any axis - other BBs overlapping will do this if any connections are made)
return [
new AxisAlignedBB(

View File

@ -328,12 +328,12 @@ class SimpleCommandMap implements CommandMap{
}
}
if(!empty($recursive)){
if(count($recursive) > 0){
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, implode(", ", $recursive)]));
continue;
}
if(!empty($bad)){
if(count($bad) > 0){
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, implode(", ", $bad)]));
continue;
}

View File

@ -463,12 +463,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/** @var Block[]|null */
protected $blocksAround = null;
/** @var float|null */
public $lastX = null;
/** @var float|null */
public $lastY = null;
/** @var float|null */
public $lastZ = null;
/** @var float */
public $lastX;
/** @var float */
public $lastY;
/** @var float */
public $lastZ;
/** @var Vector3 */
protected $motion;
@ -1102,7 +1102,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->justCreated = false;
$changedProperties = $this->propertyManager->getDirty();
if(!empty($changedProperties)){
if(count($changedProperties) > 0){
$this->sendData($this->hasSpawned, $changedProperties);
$this->propertyManager->clearDirtyProperties();
}

View File

@ -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;
@ -60,6 +61,7 @@ use function array_merge;
use function array_rand;
use function array_values;
use function ceil;
use function count;
use function in_array;
use function max;
use function min;
@ -182,7 +184,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);
}
@ -553,7 +555,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
}
if(!empty($equipment)){
if(count($equipment) > 0){
$repairItem = $equipment[$k = array_rand($equipment)];
if($repairItem->getDamage() > 0){
$repairAmount = min($repairItem->getDamage(), $xpValue * 2);
@ -850,7 +852,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);
}

View File

@ -263,7 +263,7 @@ abstract class Living extends Entity implements Damageable{
* @return bool
*/
public function hasEffects() : bool{
return !empty($this->effects);
return count($this->effects) > 0;
}
/**
@ -332,7 +332,7 @@ abstract class Living extends Entity implements Damageable{
}
}
if(!empty($colors)){
if(count($colors) > 0){
$this->propertyManager->setInt(Entity::DATA_POTION_COLOR, Color::mix(...$colors)->toARGB());
$this->propertyManager->setByte(Entity::DATA_POTION_AMBIENT, $ambient ? 1 : 0);
}else{
@ -713,7 +713,7 @@ abstract class Living extends Entity implements Damageable{
}
}
return !empty($this->effects);
return count($this->effects) > 0;
}
/**
@ -893,7 +893,7 @@ abstract class Living extends Entity implements Damageable{
*/
public function getTargetBlock(int $maxDistance, array $transparent = []) : ?Block{
$line = $this->getLineOfSight($maxDistance, 1, $transparent);
if(!empty($line)){
if(count($line) > 0){
return array_shift($line);
}

View File

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

View File

@ -34,6 +34,7 @@ use pocketmine\item\Potion;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\utils\Color;
use function count;
use function round;
use function sqrt;
@ -63,7 +64,7 @@ class SplashPotion extends Throwable{
$effects = $this->getPotionEffects();
$hasEffects = true;
if(empty($effects)){
if(count($effects) === 0){
$colors = [
new Color(0x38, 0x5d, 0xc6) //Default colour for splash water bottle and similar with no effects.
];

View File

@ -46,7 +46,7 @@ abstract class BaseInventory implements Inventory{
protected $name;
/** @var string */
protected $title;
/** @var \SplFixedArray|Item[] */
/** @var \SplFixedArray|(Item|null)[] */
protected $slots;
/** @var Player[] */
protected $viewers = [];

View File

@ -66,19 +66,23 @@ class PlayerInventory extends BaseInventory{
* @return bool if the equipment change was successful, false if not.
*/
public function equipItem(int $hotbarSlot) : bool{
$holder = $this->getHolder();
if(!$this->isHotbarSlot($hotbarSlot)){
$this->sendContents($this->getHolder());
if($holder instanceof Player){
$this->sendContents($holder);
}
return false;
}
$ev = new PlayerItemHeldEvent($this->getHolder(), $this->getItem($hotbarSlot), $hotbarSlot);
$ev->call();
if($holder instanceof Player){
$ev = new PlayerItemHeldEvent($holder, $this->getItem($hotbarSlot), $hotbarSlot);
$ev->call();
if($ev->isCancelled()){
$this->sendHeldItem($this->getHolder());
return false;
if($ev->isCancelled()){
$this->sendHeldItem($holder);
return false;
}
}
$this->setHeldItemIndex($hotbarSlot, false);
return true;

View File

@ -135,6 +135,6 @@ class ShapelessRecipe implements CraftingRecipe{
return false; //failed to match the needed item to a given item
}
return empty($input); //crafting grid should be empty apart from the given ingredient stacks
return count($input) === 0; //crafting grid should be empty apart from the given ingredient stacks
}
}

View File

@ -68,14 +68,14 @@ class CraftingTransaction extends InventoryTransaction{
* @throws TransactionValidationException
*/
protected function matchRecipeItems(array $txItems, array $recipeItems, bool $wildcards, int $iterations = 0) : int{
if(empty($recipeItems)){
if(count($recipeItems) === 0){
throw new TransactionValidationException("No recipe items given");
}
if(empty($txItems)){
if(count($txItems) === 0){
throw new TransactionValidationException("No transaction items given");
}
while(!empty($recipeItems)){
while(count($recipeItems) > 0){
/** @var Item $recipeItem */
$recipeItem = array_pop($recipeItems);
$needCount = $recipeItem->getCount();
@ -114,7 +114,7 @@ class CraftingTransaction extends InventoryTransaction{
if($iterations < 1){
throw new TransactionValidationException("Tried to craft zero times");
}
if(!empty($txItems)){
if(count($txItems) > 0){
//all items should be destroyed in this process
throw new TransactionValidationException("Expected 0 ingredients left over, have " . count($txItems));
}

View File

@ -237,13 +237,13 @@ class InventoryTransaction{
* @return null|Item
*/
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
assert(!empty($possibleActions));
assert(count($possibleActions) > 0);
foreach($possibleActions as $i => $action){
if($action->getSourceItem()->equalsExact($needOrigin)){
$newList = $possibleActions;
unset($newList[$i]);
if(empty($newList)){
if(count($newList) === 0){
return $action->getTargetItem();
}
$result = $this->findResultItem($action->getTargetItem(), $newList);

View File

@ -31,6 +31,7 @@ use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
use pocketmine\Player;
use function array_rand;
use function count;
class PaintingItem extends Item{
public function __construct(int $meta = 0){
@ -66,7 +67,7 @@ class PaintingItem extends Item{
}
}
if(empty($motives)){ //No space available
if(count($motives) === 0){ //No space available
return false;
}

View File

@ -155,6 +155,9 @@ class Enchantment{
* @return Enchantment|null
*/
public static function getEnchantment(int $id) : ?Enchantment{
if($id < 0 or $id >= self::$enchantments->getSize()){
return null;
}
return self::$enchantments[$id] ?? null;
}

View File

@ -115,9 +115,9 @@ class BaseLang{
}
/**
* @param string $str
* @param string[] $params
* @param string|null $onlyPrefix
* @param string $str
* @param (float|int|string)[] $params
* @param string|null $onlyPrefix
*
* @return string
*/

View File

@ -31,8 +31,8 @@ class TranslationContainer extends TextContainer{
protected $params = [];
/**
* @param string $text
* @param string[] $params
* @param string $text
* @param (float|int|string)[] $params
*/
public function __construct(string $text, array $params = []){
parent::__construct($text);

View File

@ -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;
@ -124,6 +123,10 @@ class Explosion{
$vBlock->y = $pointerY >= $y ? $y : $y - 1;
$vBlock->z = $pointerZ >= $z ? $z : $z - 1;
$pointerX += $vector->x;
$pointerY += $vector->y;
$pointerZ += $vector->z;
if(!$this->subChunkHandler->moveTo($vBlock->x, $vBlock->y, $vBlock->z)){
continue;
}
@ -138,10 +141,6 @@ class Explosion{
}
}
}
$pointerX += $vector->x;
$pointerY += $vector->y;
$pointerZ += $vector->z;
}
}
}
@ -260,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);

View File

@ -503,7 +503,7 @@ class Level implements ChunkManager, Metadatable{
if(!is_array($pk)){
$pk = [$pk];
}
if(!empty($pk)){
if(count($pk) > 0){
if($players === null){
foreach($pk as $e){
$this->broadcastPacketToViewers($sound, $e);
@ -519,7 +519,7 @@ class Level implements ChunkManager, Metadatable{
if(!is_array($pk)){
$pk = [$pk];
}
if(!empty($pk)){
if(count($pk) > 0){
if($players === null){
foreach($pk as $e){
$this->broadcastPacketToViewers($particle, $e);
@ -881,7 +881,7 @@ class Level implements ChunkManager, Metadatable{
if(count($this->changedBlocks) > 0){
if(count($this->players) > 0){
foreach($this->changedBlocks as $index => $blocks){
if(empty($blocks)){ //blocks can be set normally and then later re-set with direct send
if(count($blocks) === 0){ //blocks can be set normally and then later re-set with direct send
continue;
}
unset($this->chunkCache[$index]);
@ -909,8 +909,8 @@ class Level implements ChunkManager, Metadatable{
$this->checkSleep();
}
if(!empty($this->globalPackets)){
if(!empty($this->players)){
if(count($this->globalPackets) > 0){
if(count($this->players) > 0){
$this->server->batchPackets($this->players, $this->globalPackets);
}
$this->globalPackets = [];
@ -1845,7 +1845,7 @@ class Level implements ChunkManager, Metadatable{
$item->onDestroyBlock($target);
if(!empty($drops)){
if(count($drops) > 0){
$dropPos = $target->add(0.5, 0.5, 0.5);
foreach($drops as $drop){
if(!$drop->isNull()){
@ -1949,7 +1949,7 @@ class Level implements ChunkManager, Metadatable{
if($hand->isSolid()){
foreach($hand->getCollisionBoxes() as $collisionBox){
if(!empty($this->getCollidingEntities($collisionBox))){
if(count($this->getCollidingEntities($collisionBox)) > 0){
return false; //Entity in block
}
@ -2442,7 +2442,7 @@ class Level implements ChunkManager, Metadatable{
* @param int $x
* @param int $z
*
* @return Chunk[]
* @return (Chunk|null)[]
*/
public function getAdjacentChunks(int $x, int $z) : array{
$result = [];

View File

@ -33,12 +33,12 @@ class Location extends Position{
public $pitch;
/**
* @param int $x
* @param int $y
* @param int $z
* @param float $yaw
* @param float $pitch
* @param Level $level
* @param float|int $x
* @param float|int $y
* @param float|int $z
* @param float $yaw
* @param float $pitch
* @param Level $level
*/
public function __construct($x = 0, $y = 0, $z = 0, $yaw = 0.0, $pitch = 0.0, Level $level = null){
$this->yaw = $yaw;

View File

@ -33,10 +33,10 @@ class Position extends Vector3{
public $level = null;
/**
* @param int $x
* @param int $y
* @param int $z
* @param Level $level
* @param float|int $x
* @param float|int $y
* @param float|int $z
* @param Level $level
*/
public function __construct($x = 0, $y = 0, $z = 0, Level $level = null){
parent::__construct($x, $y, $z);

View File

@ -71,7 +71,7 @@ interface LevelProvider{
* @param string $name
* @param int $seed
* @param string $generator
* @param array[] $options
* @param array $options
*/
public static function generate(string $path, string $name, int $seed, string $generator, array $options = []);

View File

@ -26,6 +26,7 @@ namespace pocketmine\level\format\io\leveldb;
use pocketmine\level\format\Chunk;
use pocketmine\level\format\io\BaseLevelProvider;
use pocketmine\level\format\io\ChunkUtils;
use pocketmine\level\format\io\exception\CorruptedChunkException;
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
use pocketmine\level\format\SubChunk;
use pocketmine\level\generator\Flat;
@ -33,12 +34,18 @@ 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;
use function array_values;
use function chr;
use function count;
use function defined;
use function explode;
use function extension_loaded;
@ -409,24 +416,27 @@ class LevelDB extends BaseLevelProvider{
/** @var CompoundTag[] $entities */
$entities = [];
if(($entityData = $this->db->get($index . self::TAG_ENTITY)) !== false and $entityData !== ""){
$entities = $nbt->read($entityData, true);
if(!is_array($entities)){
$entities = [$entities];
}
}
/** @var CompoundTag $entityNBT */
foreach($entities as $entityNBT){
if($entityNBT->hasTag("id", IntTag::class)){
$entityNBT->setInt("id", $entityNBT->getInt("id") & 0xff); //remove type flags - TODO: use these instead of removing them)
$entityTags = $nbt->read($entityData, true);
foreach((is_array($entityTags) ? $entityTags : [$entityTags]) as $entityTag){
if(!($entityTag instanceof CompoundTag)){
throw new CorruptedChunkException("Entity root tag should be TAG_Compound");
}
if($entityTag->hasTag("id", IntTag::class)){
$entityTag->setInt("id", $entityTag->getInt("id") & 0xff); //remove type flags - TODO: use these instead of removing them)
}
$entities[] = $entityTag;
}
}
/** @var CompoundTag[] $tiles */
$tiles = [];
if(($tileData = $this->db->get($index . self::TAG_BLOCK_ENTITY)) !== false and $tileData !== ""){
$tiles = $nbt->read($tileData, true);
if(!is_array($tiles)){
$tiles = [$tiles];
$tileTags = $nbt->read($tileData, true);
foreach((is_array($tileTags) ? $tileTags : [$tileTags]) as $tileTag){
if(!($tileTag instanceof CompoundTag)){
throw new CorruptedChunkException("Tile root tag should be TAG_Compound");
}
$tiles[] = $tileTag;
}
}
@ -510,7 +520,7 @@ class LevelDB extends BaseLevelProvider{
* @param string $index
*/
private function writeTags(array $targets, string $index){
if(!empty($targets)){
if(count($targets) > 0){
$nbt = new LittleEndianNBTStream();
$this->db->put($index, $nbt->write($targets));
}else{

View File

@ -51,7 +51,7 @@ class Anvil extends McRegion{
$subChunks = [];
foreach($chunk->getSubChunks() as $y => $subChunk){
if($subChunk->isEmpty()){
if(!($subChunk instanceof SubChunk) or $subChunk->isEmpty()){
continue;
}
@ -122,8 +122,8 @@ class Anvil extends McRegion{
$chunk->getInt("xPos"),
$chunk->getInt("zPos"),
$subChunks,
$chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
$chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$chunk->hasTag("Entities", ListTag::class) ? self::getCompoundList("Entities", $chunk->getListTag("Entities")) : [],
$chunk->hasTag("TileEntities", ListTag::class) ? self::getCompoundList("TileEntities", $chunk->getListTag("TileEntities")) : [],
$biomeIds,
$chunk->getIntArray("HeightMap", [])
);

View File

@ -194,8 +194,8 @@ class McRegion extends BaseLevelProvider{
$chunk->getInt("xPos"),
$chunk->getInt("zPos"),
$subChunks,
$chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
$chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$chunk->hasTag("Entities", ListTag::class) ? self::getCompoundList("Entities", $chunk->getListTag("Entities")) : [],
$chunk->hasTag("TileEntities", ListTag::class) ? self::getCompoundList("TileEntities", $chunk->getListTag("TileEntities")) : [],
$biomeIds,
$heightMap
);
@ -205,6 +205,31 @@ class McRegion extends BaseLevelProvider{
return $result;
}
/**
* @param string $context
* @param ListTag $list
*
* @return CompoundTag[]
* @throws CorruptedChunkException
*/
protected static function getCompoundList(string $context, ListTag $list) : array{
if($list->count() === 0){ //empty lists might have wrong types, we don't care
return [];
}
if($list->getTagType() !== NBT::TAG_Compound){
throw new CorruptedChunkException("Expected TAG_List<TAG_Compound> for '$context'");
}
$result = [];
foreach($list as $tag){
if(!($tag instanceof CompoundTag)){
//this should never happen, but it's still possible due to lack of native type safety
throw new CorruptedChunkException("Expected TAG_List<TAG_Compound> for '$context'");
}
$result[] = $tag;
}
return $result;
}
public static function getProviderName() : string{
return "mcregion";
}

View File

@ -56,11 +56,9 @@ class PopulationTask extends AsyncTask{
}
public function onRun(){
/** @var SimpleChunkManager $manager */
$manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager");
/** @var Generator $generator */
$generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator");
if($manager === null or $generator === null){
if(!($manager instanceof SimpleChunkManager) or !($generator instanceof Generator)){
$this->state = false;
return;
}

View File

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

View File

@ -29,7 +29,7 @@ use pocketmine\network\mcpe\protocol\DataPacket;
abstract class Particle extends Vector3{
public const TYPE_BUBBLE = 1;
//2 same as 1
public const TYPE_BUBBLE_MANUAL = 2;
public const TYPE_CRITICAL = 3;
public const TYPE_BLOCK_FORCE_FIELD = 4;
public const TYPE_SMOKE = 5;
@ -40,56 +40,61 @@ abstract class Particle extends Vector3{
public const TYPE_LARGE_SMOKE = 10;
public const TYPE_REDSTONE = 11;
public const TYPE_RISING_RED_DUST = 12;
//62 same as 12
public const TYPE_ITEM_BREAK = 13;
public const TYPE_SNOWBALL_POOF = 14;
public const TYPE_HUGE_EXPLODE = 15;
//60 same as 15
public const TYPE_HUGE_EXPLODE_SEED = 16;
public const TYPE_MOB_FLAME = 17;
public const TYPE_HEART = 18;
public const TYPE_TERRAIN = 19;
public const TYPE_SUSPENDED_TOWN = 20, TYPE_TOWN_AURA = 20;
//61 same as 20
public const TYPE_PORTAL = 21;
//22 same as 21
public const TYPE_SPLASH = 23, TYPE_WATER_SPLASH = 23;
//24 same as 23
public const TYPE_WATER_SPLASH_MANUAL = 24;
public const TYPE_WATER_WAKE = 25;
public const TYPE_DRIP_WATER = 26;
public const TYPE_DRIP_LAVA = 27;
public const TYPE_FALLING_DUST = 28, TYPE_DUST = 28;
public const TYPE_MOB_SPELL = 29;
public const TYPE_MOB_SPELL_AMBIENT = 30;
public const TYPE_MOB_SPELL_INSTANTANEOUS = 31;
public const TYPE_INK = 32;
public const TYPE_SLIME = 33;
public const TYPE_RAIN_SPLASH = 34;
public const TYPE_VILLAGER_ANGRY = 35;
//59 same as 35
public const TYPE_VILLAGER_HAPPY = 36;
public const TYPE_ENCHANTMENT_TABLE = 37;
public const TYPE_TRACKING_EMITTER = 38;
public const TYPE_NOTE = 39;
public const TYPE_WITCH_SPELL = 40;
public const TYPE_CARROT = 41;
//42 unknown
public const TYPE_END_ROD = 43;
//58 same as 43
public const TYPE_DRAGONS_BREATH = 44;
public const TYPE_SPIT = 45;
public const TYPE_TOTEM = 46;
public const TYPE_FOOD = 47;
public const TYPE_FIREWORKS_STARTER = 48;
public const TYPE_FIREWORKS_SPARK = 49;
public const TYPE_FIREWORKS_OVERLAY = 50;
public const TYPE_BALLOON_GAS = 51;
public const TYPE_COLORED_FLAME = 52;
public const TYPE_SPARKLER = 53;
public const TYPE_CONDUIT = 54;
public const TYPE_BUBBLE_COLUMN_UP = 55;
public const TYPE_BUBBLE_COLUMN_DOWN = 56;
public const TYPE_SNEEZE = 57;
public const TYPE_DRIP_HONEY = 28;
public const TYPE_FALLING_DUST = 29, TYPE_DUST = 29;
public const TYPE_MOB_SPELL = 30;
public const TYPE_MOB_SPELL_AMBIENT = 31;
public const TYPE_MOB_SPELL_INSTANTANEOUS = 32;
public const TYPE_INK = 33;
public const TYPE_SLIME = 34;
public const TYPE_RAIN_SPLASH = 35;
public const TYPE_VILLAGER_ANGRY = 36;
public const TYPE_VILLAGER_HAPPY = 37;
public const TYPE_ENCHANTMENT_TABLE = 38;
public const TYPE_TRACKING_EMITTER = 39;
public const TYPE_NOTE = 40;
public const TYPE_WITCH_SPELL = 41;
public const TYPE_CARROT = 42;
public const TYPE_MOB_APPEARANCE = 43;
public const TYPE_END_ROD = 44;
public const TYPE_DRAGONS_BREATH = 45;
public const TYPE_SPIT = 46;
public const TYPE_TOTEM = 47;
public const TYPE_FOOD = 48;
public const TYPE_FIREWORKS_STARTER = 49;
public const TYPE_FIREWORKS_SPARK = 50;
public const TYPE_FIREWORKS_OVERLAY = 51;
public const TYPE_BALLOON_GAS = 52;
public const TYPE_COLORED_FLAME = 53;
public const TYPE_SPARKLER = 54;
public const TYPE_CONDUIT = 55;
public const TYPE_BUBBLE_COLUMN_UP = 56;
public const TYPE_BUBBLE_COLUMN_DOWN = 57;
public const TYPE_SNEEZE = 58;
public const TYPE_SHULKER_BULLET = 59;
public const TYPE_BLEACH = 60;
public const TYPE_DRAGON_DESTROY_BLOCK = 61;
public const TYPE_MYCELIUM_DUST = 62;
public const TYPE_FALLING_RED_DUST = 63;
public const TYPE_CAMPFIRE_SMOKE = 64;
public const TYPE_TALL_CAMPFIRE_SMOKE = 65;
public const TYPE_DRAGON_BREATH_FIRE = 66;
public const TYPE_DRAGON_BREATH_TRAIL = 67;
/**
* @return DataPacket|DataPacket[]

View File

@ -35,7 +35,7 @@ class CompressBatchedTask extends AsyncTask{
/**
* @param BatchPacket $batch
* @param string[] $targets
* @param Player[] $targets
*/
public function __construct(BatchPacket $batch, array $targets){
$this->data = $batch->payload;

View File

@ -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,9 @@ 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;
@ -74,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){

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,78 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\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

View File

@ -28,8 +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 pocketmine\utils\BinaryDataException;
use function array_search;
use function count;
use function dechex;
@ -56,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:
@ -79,12 +81,23 @@ class AvailableCommandsPacket extends DataPacket{
*/
public const ARG_FLAG_POSTFIX = 0x1000000;
public const HARDCODED_ENUM_NAMES = [
"CommandName" => true
];
/**
* @var CommandData[]
* List of command data, including name, description, alias indexes and parameters.
*/
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
@ -92,6 +105,12 @@ 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(){
/** @var string[] $enumValues */
$enumValues = [];
@ -108,7 +127,10 @@ class AvailableCommandsPacket extends DataPacket{
/** @var CommandEnum[] $enums */
$enums = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$enums[] = $this->getEnum($enumValues);
$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){
@ -118,6 +140,10 @@ class AvailableCommandsPacket extends DataPacket{
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);
}
}
/**
@ -210,6 +236,50 @@ class AvailableCommandsPacket extends DataPacket{
}
}
/**
* @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
@ -349,27 +419,28 @@ class AvailableCommandsPacket extends DataPacket{
$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){
if(!isset($enumIndexes[$commandData->aliases->enumName])){
$enums[$enumIndexes[$commandData->aliases->enumName] = count($enumIndexes)] = $commandData->aliases;
}
foreach($commandData->aliases->enumValues as $str){
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); //latest index
}
$addEnumFn($commandData->aliases);
}
/** @var CommandParameter[] $overload */
foreach($commandData->overloads as $overload){
/** @var CommandParameter $parameter */
foreach($overload as $parameter){
if($parameter->enum !== null){
if(!isset($enumIndexes[$parameter->enum->enumName])){
$enums[$enumIndexes[$parameter->enum->enumName] = count($enumIndexes)] = $parameter->enum;
}
foreach($parameter->enum->enumValues as $str){
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes);
}
$addEnumFn($parameter->enum);
}
if($parameter->postfix !== null){
@ -403,6 +474,11 @@ class AvailableCommandsPacket extends DataPacket{
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{

View File

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

View File

@ -0,0 +1,68 @@
<?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 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);
}
}

View File

@ -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,6 +146,18 @@ class CraftingDataPacket extends DataPacket{
}
$this->decodedEntries[] = $entry;
}
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();
}
@ -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);
}

View File

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

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

View File

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

View File

@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\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();

View File

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

View File

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

View File

@ -0,0 +1,60 @@
<?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 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{
$this->compressionThreshold = $this->getLShort();
}
protected function encodePayload() : void{
$this->putLShort($this->compressionThreshold);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleNetworkSettings($this);
}
}

View File

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

View 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){
$result->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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,8 +28,8 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\StructureSettings;
class StructureTemplateDataExportRequestPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET;
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;
@ -62,6 +62,6 @@ class StructureTemplateDataExportRequestPacket extends DataPacket{
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleStructureTemplateDataExportRequest($this);
return $handler->handleStructureTemplateDataRequest($this);
}
}

View File

@ -27,8 +27,8 @@ 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 StructureTemplateDataResponsePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET;
/** @var string */
public $structureTemplateName;
@ -51,6 +51,6 @@ class StructureTemplateDataExportResponsePacket extends DataPacket{
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleStructureTemplateDataExportResponse($this);
return $handler->handleStructureTemplateDataResponse($this);
}
}

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

View File

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

View File

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

View File

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

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

View File

@ -0,0 +1,70 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\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());
}
}

View File

@ -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;
/**
@ -48,18 +48,12 @@ class NetworkInventoryAction{
*
* Expect these to change in the future.
*/
public const SOURCE_TYPE_CRAFTING_ADD_INGREDIENT = -2;
public const SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT = -3;
public const SOURCE_TYPE_CRAFTING_RESULT = -4;
public const SOURCE_TYPE_CRAFTING_USE_INGREDIENT = -5;
public const SOURCE_TYPE_ANVIL_INPUT = -10;
public const SOURCE_TYPE_ANVIL_MATERIAL = -11;
public const SOURCE_TYPE_ANVIL_RESULT = -12;
public const SOURCE_TYPE_ANVIL_OUTPUT = -13;
public const SOURCE_TYPE_ENCHANT_INPUT = -15;
public const SOURCE_TYPE_ENCHANT_MATERIAL = -16;
public const SOURCE_TYPE_ENCHANT_OUTPUT = -17;
public const SOURCE_TYPE_TRADING_INPUT_1 = -20;
@ -69,9 +63,6 @@ class NetworkInventoryAction{
public const SOURCE_TYPE_BEACON = -24;
/** Any client-side window dropping its contents when the player closes it */
public const SOURCE_TYPE_CONTAINER_DROP_CONTENTS = -100;
public const ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM = 0;
public const ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM = 1;
@ -108,17 +99,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 +128,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 +148,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,14 +202,9 @@ 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){
case self::SOURCE_TYPE_CRAFTING_ADD_INGREDIENT:
case self::SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT:
case self::SOURCE_TYPE_CONTAINER_DROP_CONTENTS: //TODO: this type applies to all fake windows, not just crafting
return new SlotChangeAction($player->getCraftingGrid(), $this->inventorySlot, $this->oldItem, $this->newItem);
case self::SOURCE_TYPE_CRAFTING_RESULT:
case self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT:
return null;

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,7 +44,7 @@ final class RuntimeBlockMapping{
private static $legacyToRuntimeMap = [];
/** @var int[] */
private static $runtimeToLegacyMap = [];
/** @var mixed[]|null */
/** @var CompoundTag[]|null */
private static $bedrockKnownStates = null;
private function __construct(){
@ -48,32 +52,57 @@ final class RuntimeBlockMapping{
}
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");
}
}
@ -88,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
@ -133,7 +162,7 @@ final class RuntimeBlockMapping{
}
/**
* @return array
* @return CompoundTag[]
*/
public static function getBedrockKnownStates() : array{
self::lazyInit();

View File

@ -0,0 +1,48 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\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;
}

View File

@ -0,0 +1,43 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
/**
* Accessor for SkinAdapter
*/
class SkinAdapterSingleton{
/** @var SkinAdapter|null */
private static $skinAdapter = null;
public static function get() : SkinAdapter{
if(self::$skinAdapter === null){
self::$skinAdapter = new LegacySkinAdapter();
}
return self::$skinAdapter;
}
public static function set(SkinAdapter $adapter) : void{
self::$skinAdapter = $adapter;
}
}

View File

@ -0,0 +1,71 @@
<?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 SkinAnimation{
public const TYPE_HEAD = 1;
public const TYPE_BODY_32 = 2;
public const TYPE_BODY_64 = 3;
/** @var SkinImage */
private $image;
/** @var int */
private $type;
/** @var float */
private $frames;
public function __construct(SkinImage $image, int $type, float $frames){
$this->image = $image;
$this->type = $type;
$this->frames = $frames;
}
/**
* Image of the animation.
*
* @return SkinImage
*/
public function getImage() : SkinImage{
return $this->image;
}
/**
* The type of animation you are applying.
*
* @return int
*/
public function getType() : int{
return $this->type;
}
/**
* The total amount of frames in an animation.
*
* @return float
*/
public function getFrames() : float{
return $this->frames;
}
}

View File

@ -0,0 +1,155 @@
<?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 SkinData{
/** @var string */
private $skinId;
/** @var string */
private $resourcePatch;
/** @var SkinImage */
private $skinImage;
/** @var SkinAnimation[] */
private $animations;
/** @var SkinImage */
private $capeImage;
/** @var string */
private $geometryData;
/** @var string */
private $animationData;
/** @var bool */
private $persona;
/** @var bool */
private $premium;
/** @var bool */
private $personaCapeOnClassic;
/** @var string */
private $capeId;
/**
* @param string $skinId
* @param string $resourcePatch
* @param SkinImage $skinImage
* @param SkinAnimation[] $animations
* @param SkinImage|null $capeImage
* @param string $geometryData
* @param string $animationData
* @param bool $premium
* @param bool $persona
* @param bool $personaCapeOnClassic
* @param string $capeId
*/
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = ""){
$this->skinId = $skinId;
$this->resourcePatch = $resourcePatch;
$this->skinImage = $skinImage;
$this->animations = $animations;
$this->capeImage = $capeImage;
$this->geometryData = $geometryData;
$this->animationData = $animationData;
$this->premium = $premium;
$this->persona = $persona;
$this->personaCapeOnClassic = $personaCapeOnClassic;
$this->capeId = $capeId;
}
/**
* @return string
*/
public function getSkinId() : string{
return $this->skinId;
}
/**
* @return string
*/
public function getResourcePatch() : string{
return $this->resourcePatch;
}
/**
* @return SkinImage
*/
public function getSkinImage() : SkinImage{
return $this->skinImage;
}
/**
* @return SkinAnimation[]
*/
public function getAnimations() : array{
return $this->animations;
}
/**
* @return SkinImage
*/
public function getCapeImage() : SkinImage{
return $this->capeImage;
}
/**
* @return string
*/
public function getGeometryData() : string{
return $this->geometryData;
}
/**
* @return string
*/
public function getAnimationData() : string{
return $this->animationData;
}
/**
* @return bool
*/
public function isPersona() : bool{
return $this->persona;
}
/**
* @return bool
*/
public function isPremium() : bool{
return $this->premium;
}
/**
* @return bool
*/
public function isPersonaCapeOnClassic() : bool{
return $this->personaCapeOnClassic;
}
/**
* @return string
*/
public function getCapeId() : string{
return $this->capeId;
}
}

View File

@ -0,0 +1,71 @@
<?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 function strlen;
class SkinImage{
/** @var int */
private $height;
/** @var int */
private $width;
/** @var string */
private $data;
public function __construct(int $height, int $width, string $data){
if(($expected = $height * $width * 4) !== ($actual = strlen($data))){
throw new \InvalidArgumentException("Data should be exactly $expected bytes, got $actual bytes");
}
$this->height = $height;
$this->width = $width;
$this->data = $data;
}
public static function fromLegacy(string $data) : SkinImage{
switch(strlen($data)){
case 64 * 32 * 4:
return new self(64, 32, $data);
case 64 * 64 * 4:
return new self(64, 64, $data);
case 128 * 64 * 4:
return new self(128, 64, $data);
case 128 * 128 * 4:
return new self(128, 128, $data);
}
throw new \InvalidArgumentException("Unknown size");
}
public function getHeight() : int{
return $this->height;
}
public function getWidth() : int{
return $this->width;
}
public function getData() : string{
return $this->data;
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\permission;
use function array_shift;
use function count;
use function explode;
use function implode;
use function strlen;
@ -136,10 +137,10 @@ class BanEntry{
/**
* @param string $date
*
* @return \DateTime|null
* @return \DateTime
* @throws \RuntimeException
*/
private static function parseDate(string $date) : ?\DateTime{
private static function parseDate(string $date) : \DateTime{
$datetime = \DateTime::createFromFormat(self::$format, $date);
if(!($datetime instanceof \DateTime)){
throw new \RuntimeException("Error parsing date for BanEntry: " . implode(", ", \DateTime::getLastErrors()["errors"]));
@ -161,17 +162,17 @@ class BanEntry{
$str = explode("|", trim($str));
$entry = new BanEntry(trim(array_shift($str)));
do{
if(empty($str)){
if(count($str) === 0){
break;
}
$entry->setCreated(self::parseDate(array_shift($str)));
if(empty($str)){
if(count($str) === 0){
break;
}
$entry->setSource(trim(array_shift($str)));
if(empty($str)){
if(count($str) === 0){
break;
}
@ -179,7 +180,7 @@ class BanEntry{
if($expire !== "" and strtolower($expire) !== "forever"){
$entry->setExpires(self::parseDate($expire));
}
if(empty($str)){
if(count($str) === 0){
break;
}

View File

@ -168,7 +168,7 @@ class PermissionManager{
public function unsubscribeFromAllPermissions(Permissible $permissible) : void{
foreach($this->permSubs as $permission => &$subs){
unset($subs[spl_object_hash($permissible)]);
if(empty($subs)){
if(count($subs) === 0){
unset($this->permSubs[$permission]);
}
}

View File

@ -38,6 +38,7 @@ use function strlen;
use function strtoupper;
use function substr;
use function version_compare;
use function yaml_parse;
class PluginDescription{
private $map;

View File

@ -40,6 +40,7 @@ use pocketmine\timings\TimingsHandler;
use pocketmine\utils\Utils;
use function array_intersect;
use function array_map;
use function array_merge;
use function array_pad;
use function class_exists;
use function count;

View File

@ -34,6 +34,7 @@ use function filesize;
use function fopen;
use function fread;
use function fseek;
use function gettype;
use function hash_file;
use function implode;

View File

@ -38,6 +38,7 @@ use pocketmine\level\Level;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use function ceil;
use function count;
use function max;
class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
@ -232,7 +233,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
$packets[] = $pk;
}
if(!empty($packets)){
if(count($packets) > 0){
foreach($this->getInventory()->getViewers() as $player){
$windowId = $player->getWindowId($this->getInventory());
if($windowId > 0){

View File

@ -31,6 +31,8 @@ use pocketmine\tile\Tile;
use function dechex;
abstract class Timings{
/** @var bool */
private static $initialized = false;
/** @var TimingsHandler */
public static $fullTickTimer;
@ -104,9 +106,10 @@ abstract class Timings{
public static $pluginTaskTimingMap = [];
public static function init(){
if(self::$serverTickTimer instanceof TimingsHandler){
if(self::$initialized){
return;
}
self::$initialized = true;
self::$fullTickTimer = new TimingsHandler("Full Server Tick");
self::$serverTickTimer = new TimingsHandler("** Full Server Tick", self::$fullTickTimer);

View File

@ -168,7 +168,10 @@ class TimingsHandler{
public function stopTiming(){
if(self::$enabled){
if(--$this->timingDepth !== 0 or $this->start === 0){
if($this->timingDepth === 0){
throw new \InvalidStateException("Cannot stop a timer that is not running");
}
if(--$this->timingDepth !== 0 or $this->start == 0){
return;
}

View File

@ -420,7 +420,7 @@ class Config{
while(count($vars) > 0){
$nodeName = array_shift($vars);
if(isset($currentNode[$nodeName])){
if(empty($vars)){ //final node
if(count($vars) === 0){ //final node
unset($currentNode[$nodeName]);
}elseif(is_array($currentNode[$nodeName])){
$currentNode =& $currentNode[$nodeName];

View File

@ -33,6 +33,7 @@ use function implode;
use function ini_get;
use function ini_set;
use function is_link;
use function is_string;
use function json_decode;
use function parse_ini_file;
use function preg_match;
@ -149,7 +150,7 @@ abstract class Timezone{
// RHEL / CentOS
if(file_exists('/etc/sysconfig/clock')){
$data = parse_ini_file('/etc/sysconfig/clock');
if(!empty($data['ZONE'])){
if(isset($data['ZONE']) and is_string($data['ZONE'])){
return trim($data['ZONE']);
}
}

View File

@ -58,6 +58,7 @@ use function is_object;
use function is_readable;
use function is_string;
use function json_decode;
use function ltrim;
use function memory_get_usage;
use function ob_end_clean;
use function ob_get_contents;
@ -72,6 +73,7 @@ use function preg_match_all;
use function preg_replace;
use function proc_close;
use function proc_open;
use function rtrim;
use function sha1;
use function spl_object_hash;
use function str_pad;
@ -82,6 +84,7 @@ use function stripos;
use function strlen;
use function strpos;
use function strtolower;
use function strtr;
use function substr;
use function sys_get_temp_dir;
use function trim;

Some files were not shown because too many files have changed in this diff Show More