Compare commits

...

356 Commits

Author SHA1 Message Date
2751c59979 Release 3.14.1 2020-07-08 10:32:07 +01:00
d99ffbd66c Attribute: register lava_movement attribute
this is purely to fix crashes when decoding net packets
2020-07-08 10:21:20 +01:00
a34f3261cb event: harden APIs that accept arrays
plugin devs can't be relied on to pass the proper types to these APIs, and when the wrong types get passed it makes type errors appear from inside the internals.
2020-07-04 21:55:23 +01:00
8ce0022de6 protocol: added UUInventorySlotOffset constants 2020-07-04 21:37:37 +01:00
fb6491ddeb BanListCommand: sort output into lexical order 2020-07-03 11:23:00 +01:00
3b961d0e5f WhitelistCommand: sort output of /whitelist list into lexical order 2020-07-03 11:19:23 +01:00
a60fc4cc28 ListCommand: sort output into lexical order 2020-07-03 11:15:31 +01:00
b747899fdd PluginsCommand: sort plugins list into lexical order 2020-07-03 11:13:32 +01:00
57b6451e16 Fix projectile motion being changed by the ladder, close #3602 (#3631) 2020-06-27 21:18:39 +01:00
8cf025a2df Default isVerified to true (#3644) 2020-06-27 21:17:34 +01:00
8480ee82ea Player: track hardcoded window state, fixes crashes opening inventory on high-latency connections 2020-06-27 18:34:39 +01:00
a6c1b7bf9c InventoryTransactionPacket: added missing field for encode 2020-06-26 20:57:48 +01:00
c267137fde 3.14.1 is next 2020-06-26 14:19:02 +01:00
461bc94236 Release 3.14.0 2020-06-26 14:19:02 +01:00
4fed08bcd4 ProtocolInfo: fixed version number 2020-06-26 14:15:58 +01:00
e990c5a0a5 Protocol changes for 1.16.0 2020-06-26 14:06:41 +01:00
c616d9bb7c 3.13.2 is next 2020-06-26 12:31:56 +01:00
81051441ba Release 3.13.1 2020-06-26 12:31:56 +01:00
3ecae0db19 WindowTypes: fill in a couple of blanks 2020-06-26 12:04:11 +01:00
c5bbb2bcbc Move crafting action detection from InventoryTransactionPacket to Player 2020-06-26 11:59:30 +01:00
24a2889758 NetworkInventoryAction does not require an InventoryTransactionPacket 2020-06-26 11:51:06 +01:00
60b26a7ea8 NetworkBinaryStream: unknown byte preceding NBT is a version, not a count 2020-06-26 11:47:08 +01:00
22b52f03d1 Player: fixed formatting error in InteractPacket debug message 2020-06-26 11:44:41 +01:00
df76c02e7a Explicitly release server.lock file when shutdown the server. (#3619)
Previously, this relied on PHP itself to release locks during the resource destructor phase during process exit, but sometimes it doesn't for god knows what reason. This change makes the lock file get explicitly released before the process dies.
2020-06-26 11:41:39 +01:00
d343187e58 phpstan: drop 2 obsolete level 8 error patterns fixed by 763c8ebfe3 2020-06-23 12:57:25 +01:00
c5ad127854 BaseInventory: mark eventProcessor as nullable, fixes a phpstan level 8 error 2020-06-23 12:55:06 +01:00
0f6dc9082a lock phpstan at 0.12.29
0.12.30 starts reporting non-ignorable errors for overriding Thread::start() due to outdated stubs for pthreads that I can't replace
2020-06-22 20:57:12 +01:00
2b6dcbc2e2 BaseLang: fixed passing onlyPrefix to str_replace()'s count reference parameter 2020-06-22 20:34:23 +01:00
763c8ebfe3 Thread/Worker: drop nullability flag from start() (fixed in pthreads 3.2.0, which we require as a minimum) 2020-06-21 19:07:01 +01:00
c572e9bb6a phpstan: regenerate l7 baseline 2020-06-21 18:58:41 +01:00
89521f166d Explosion: account for multi-block structures, fixes #2767 2020-06-21 17:28:38 +01:00
49d3a42120 phpstan: make EntityEvent generic, fix a bunch of 'actual-problems' ignored errors 2020-06-21 00:27:32 +01:00
c523595e85 Rewrite TeleportCommand (sadly I can't make this commit any smaller)
this pile of shit was overdue a rewrite. The new version is much easier to understand.
2020-06-21 00:04:18 +01:00
7c7e4f2093 WhitelistCommand: fixed silence on unknown subcommand 2020-06-20 23:05:39 +01:00
88c1014f03 TimingsCommand: fixed silence when using a nonexisting subcommand 2020-06-20 23:04:08 +01:00
e32180ce93 phpstan: drop an obsolete phpstan-bugs ignoreError 2020-06-19 10:41:41 +01:00
e105578be0 LegacySkinAdapter: an extra check for resourcePatch (fixes 3 phpstan explicitMixed errors) 2020-06-18 20:08:38 +01:00
a9d98bdf73 phpstan: baseline our way into checkExplicitMixed territory 2020-06-18 18:07:21 +01:00
c601352777 Fixed wrong meta value when pick cake block. (#3593) 2020-06-18 11:14:57 +01:00
77c71e22b2 SkinAdapter::fromSkinData() may now throw InvalidSkinException
fixes a rogue TODO in LegacySkinAdapter and invalid skins maybe showing up as Steve instead of getting kicked off the server
2020-06-17 21:44:22 +01:00
1c13ba5656 Avoid parameter ordering bugs during packet decoding
A PhpStorm refactor could have side effects on code that directly reads stuff from the packet input stream in the arguments block, because those calls will get moved into a different order if the constructor gets refactored. This would, obviously, break packet decoding, so that's something we should avoid and really should not encourage.
2020-06-17 21:01:01 +01:00
f970be0e4d SkinImage: 128x64 is not a valid classic skin size
MC itself doesn't accept classic skins of this size.
2020-06-17 20:31:28 +01:00
11a3f9f1b9 VerifyLoginTask: fast-fail by checking header x5u before verifying signature
this is less costly, although it doesn't make any difference except in invalid cases.
2020-06-17 17:52:19 +01:00
09771849ae VerifyLoginTask optimisation: do not copy the entire LoginPacket into the worker thread
this is especially bad considering the fact that the cached buffer is copied. That said, it's only a few kilobytes so it's not a huge problem, but nonetheless...
2020-06-17 17:46:22 +01:00
57a310230a ScoreboardIdentityPacketEntry: added missing field default 2020-06-15 23:59:53 +01:00
130c55d9f1 EntityLink: remove rogue default ctor parameters 2020-06-15 23:58:11 +01:00
2712befa82 SkinData: fixed capeImage type violation (doesn't accept null) 2020-06-15 23:51:48 +01:00
a4e250a3e6 TextFormat: improved exception messages for PCRE failures 2020-06-15 23:43:01 +01:00
23b97d8e2d TextFormat: wrap all preg_replace() usages in a type-safe exception-throwing version
fixes 3 phpstan level 8 errors
2020-06-15 23:31:46 +01:00
1fb5043eb1 build/server-phar: fixed a phpstan level 8 error 2020-06-15 23:16:40 +01:00
b0b1b29de4 Chunk: specify list<int> for heightMap 2020-06-15 23:07:35 +01:00
1c3b641e37 ChunkUtils: be more specific in extension stub too 2020-06-15 23:07:05 +01:00
f3063e797f ChunkUtils: provide a more explicit parameter type 2020-06-15 23:05:52 +01:00
8dcc88712c ChunkUtils: fixed phpstan level 7 type inference error 2020-06-15 23:04:12 +01:00
04191ec44a Rail: specify type for local static variable
fixes a phpstan level 7 error
phpstan doesn't make any assumptions about local static variable types because analysing them would require too much work, apparently.
2020-06-15 22:47:20 +01:00
62ea7c93a9 added a dedicated InvalidSkinException 2020-06-15 21:48:24 +01:00
cf06b5b8cf Player: explicitly check for false return of dataPacket()
fixes a phpstan error on l7
this won't ever actually be a problem, but this isn't obvious from the type system.
2020-06-15 21:24:19 +01:00
a8ec51daac Player: do not assign maybe-false result of json_encode() to ModalFormRequestPacket
fixes a phpstan error on l7
2020-06-15 21:22:33 +01:00
6a7b77fee2 first look at making region writes reuse old space 2020-06-15 18:36:54 +01:00
da42c8d020 Bump phpunit/phpunit from 9.2.2 to 9.2.3 (#3588) 2020-06-15 16:30:50 +00:00
b902f9ded0 RegionLoader: fixed 2 phpstan level 7 errors (eof reading region header) 2020-06-15 15:09:37 +01:00
9bb8a8f761 RegionLoader: added utility function getProportionUnusedSpace() 2020-06-15 13:49:03 +01:00
63b14a083c RegionLoader: added utility function generateSectorMap()
this proved very useful while debugging some internal issues.
2020-06-15 13:48:17 +01:00
627a7c951a RegionLoader: added missing const import 2020-06-15 13:46:03 +01:00
bb2685ca65 RegionLocationTableEntry: cap firstSector at 16777216
this is the biggest sector start that the location table can represent, due to the binary format. Larger values than this will overflow and cause corruption.
This provides an effective limit of 64 GB on region files.
2020-06-15 13:23:08 +01:00
d38709a7ae RegionLoader: remove unused variable 2020-06-15 12:26:20 +01:00
b559a65346 RegionLoader: account for possible corrupted header pointing to itself 2020-06-15 12:13:42 +01:00
b92a2ded8a RegionLoader: check for zero sector count when loading location table
implementations shouldn't be writing location entries that have an offset but zero sectors, but just in case they do, we need to be aware of it.
2020-06-15 12:08:55 +01:00
22f25dfbdb RegionLocationTableEntry: require sector count to be at least 1 2020-06-15 12:05:48 +01:00
6bf840c72e RegionLoader: use actual null instead of zeroed entry for non-allocated chunks
this forces the code to be properly aware of non-allocated chunks, because it'll crash with NPE if it isn't.
2020-06-15 12:02:03 +01:00
745be19a56 RegionLoader: fixed regions ballooning when writing chunks to the end of file
we already have a region growth problem due to the lack of garbage collection, but this bug was making it worse. If the region already contained 1024 allocated chunks, 4MB of file space would get wasted before the next chunk would be appended to the file.
2020-06-14 23:40:33 +01:00
e05bee5ffb RegionLoader: do a full check for chunk overlaps during initial load 2020-06-14 22:39:01 +01:00
087ba0cc1d phpstan: update config to use new 0.12.26+ options 2020-06-14 19:57:36 +01:00
d8d994351b phpstan 0.12.29 2020-06-14 16:25:55 +01:00
0029efa370 Server: add getPlayerDataPath(), reduce logic duplication 2020-06-14 12:40:24 +01:00
df13e967fd imports cleanup 2020-06-14 10:27:15 +01:00
097c260dbb Eradicate all usages of strtoupper()
strtoupper() is an evil function whose behaviour depends on the system locale. mb_strtoupper() has more consistent behaviour.
2020-06-13 19:47:00 +01:00
a219b727f2 updated build/php submodule to pmmp/php-build-scripts@cec63c3093 2020-06-13 11:14:21 +01:00
710c162604 QueryRegenerateEvent: fixed possible type violation on listPlugins 2020-06-10 12:11:39 +01:00
409c8c1703 TimingsCommand: workaround a PHPStan type specifying bug 2020-06-10 12:11:10 +01:00
376926c700 TimingsCommand: fix missing type information for async task local storage 2020-06-10 12:10:33 +01:00
c3fabe833e FileWriteTask: mark as deprecated 2020-06-10 11:22:18 +01:00
3e09ff5350 EnchantTable: fix formatting issue [ci skip] 2020-06-10 10:54:22 +01:00
7255065106 LevelDB: stop passing false to places where it's not expected 2020-06-10 10:45:54 +01:00
a7f10d8ccf phpstan: ignore a FP (fixed in 0.12.26, but we can't upgrade yet) 2020-06-10 10:45:28 +01:00
76f1add3b3 Timezone: return false if date_parse() fails
I hate this, but I don't want to change it to throw right now because it'll create a bunch of extra work.
2020-06-10 10:34:34 +01:00
fcc9e62c65 CommandEnum: specify enumValues type
phpstan 0.12.26 starts reporting errors about the result of array_search() being given to some constructor or another because of the lack of key type specification.
2020-06-10 10:33:35 +01:00
42613618a5 phpstan: add LevelDB::get() stub to fix return type 2020-06-10 10:31:54 +01:00
1bbeb62457 Bump phpunit/phpunit from 9.2.1 to 9.2.2 (#3566) 2020-06-08 17:32:39 +00:00
64893426fa Bump phpunit/phpunit from 8.5.5 to 9.2.1 (#3557) 2020-06-06 10:13:46 +00:00
3d50aafcc4 ShapedRecipe: remove a curly-brace array-access that nothing noticed 2020-06-06 11:12:45 +01:00
50fed41642 README: we're now on travis-ci.com 2020-06-05 13:02:31 +01:00
3f971a0c65 phpstan: fixed analyze failure caused by a27b29897c 2020-06-05 12:58:18 +01:00
a27b29897c TimingsCommand: tell operators what's going on for audit purposes
previously nobody except the person who was managing timings would know that timings was running, being pasted or whatever else. Since timings can impact performance (and, for example, block the main thread when writing timings to a file), access to it should be logged so that server owners know what's going on.
2020-06-05 11:12:36 +01:00
a90132a30e added missing 3.12.6 changelog 2020-06-04 20:38:06 +01:00
dfbd857771 3.13.1 is next 2020-06-04 20:01:28 +01:00
323d96d5c1 Release 3.13.0 2020-06-04 20:01:27 +01:00
f495ba1d0b Merge branch 'next-minor' into stable 2020-06-04 19:59:46 +01:00
643cf0ebf8 3.12.7 is next 2020-06-04 19:55:09 +01:00
1614206a6d Release 3.12.6 2020-06-04 19:55:09 +01:00
0ae2c6302a Merge branch 'stable' into next-minor 2020-06-04 13:59:30 +01:00
4f59c1b26c .gitignore: ignore some extra crap 2020-06-04 13:58:46 +01:00
00916ade0c update build/php submodule to pmmp/php-build-scripts@0aa88d2765 2020-06-04 13:55:03 +01:00
f4ee2912db Server: tell me what is wrong when crash archive submission fails 2020-06-04 13:48:40 +01:00
a0de9b0d46 Player: use real time to update rate limit, fixes #3554 (except in very extreme cases) 2020-06-04 13:39:26 +01:00
03e8cd3ed4 Player: fixing ground state handling in spectator mode, closes #3552, closes #3553 2020-06-04 11:10:21 +01:00
7af4e70f64 Entity: fixed movement updates not firing after teleport
this became obvious by teleporting non-moving entities into the air and observing that they didn't fall.
2020-06-04 10:52:04 +01:00
c864647cd1 Merge branch 'stable' into next-minor 2020-06-03 13:04:08 +01:00
92ed9e6125 3.12.6 is next 2020-06-03 10:59:33 +01:00
c32026333f Release 3.12.5 2020-06-03 10:59:33 +01:00
915224c8e5 Living: fix being unable to die in the void 2020-06-02 23:18:40 +01:00
734bc6c4a7 3.12.5 is next 2020-06-02 18:40:33 +01:00
d36b24c518 Release 3.12.4 2020-06-02 18:40:33 +01:00
d554d8060b fixed absorption hearts not being consumed, closes #3546
this was caused by a bad fix for switching. we can't consider zero-damage attacks as cancelled because zero-damage might have been the result of things like consuming absorption hearts, so the aftereffects need to be processed even when the net damage is zero.
2020-06-02 10:09:18 +01:00
b48243fd09 Bump phpstan/phpstan-phpunit from 0.12.10 to 0.12.11 (#3547) 2020-06-01 18:05:51 +00:00
5c63e06b0f piece together a changelog for release 2020-06-01 14:57:27 +01:00
3be83e09f2 Revert BC-breaking backport from b38c81c96
this can't be applied to a minor version because it places additional requirements on the Inventory contract.
2020-06-01 13:51:36 +01:00
f24be2b055 Merge branch 'stable' into next-minor
# Conflicts:
#	src/pocketmine/Player.php
#	src/pocketmine/block/SnowLayer.php
2020-06-01 13:42:59 +01:00
92cffc00d0 3.12.4 is next 2020-06-01 13:18:28 +01:00
e87e974323 Release 3.12.3 2020-06-01 13:18:28 +01:00
a3f6338626 Player: fixed internal server error when using insert-before on a client-sided book page 2020-06-01 12:59:18 +01:00
21aef97ba7 Player: fixed swapping book pages that only exist client-side, closes #3322 2020-06-01 12:50:39 +01:00
ed0d1978aa WritableBook: fixed thrown exception when asking for a page that doesn't exist (it's allowed to return null for a reason ...) 2020-06-01 12:46:41 +01:00
d64561b0b1 Fixed internal server error when deleting book pages that only exist client side 2020-06-01 12:39:17 +01:00
d234d3e45e ParticleCommand: added mobflame, closes #3102 2020-05-31 20:28:57 +01:00
5056754cea NetworkBinaryStream: do not round vectors directly on reading
fixes #3199
2020-05-31 20:17:42 +01:00
2dc3cf8162 InventoryTransaction: sync inventories before throwing validation exception to caller
fixes #3226

really the transaction shouldn't be handling inventory sync at all, but that's a job for another commit.
2020-05-31 20:10:29 +01:00
8c5a81cf5c Living: improved projectile knockback
this isn't pretty, but it works. I have a nicer idea how to do this on PM4, but it's going to involve BC breaks (as usual).
closes #3382
2020-05-31 20:04:12 +01:00
2b58f2bafd FallingBlock: remove superfluous transparent check, closes #3339 2020-05-31 19:54:47 +01:00
5dadf12374 Living: fixed cooldown damage logic, closes #2939 2020-05-31 19:46:51 +01:00
0d4e473bdd Reduce ResourcePackChunkData chunk size to 128 KB
while this is slightly less bandwidth efficient (1 in 92 datagrams not full vs 1 in 733), this is significantly less memory-hard.
I made this decision looking at the memory pressures that 1MB chunks exert - especially on RakNet. Client-side, these resource pack chunks all hang around in RakNet memory until the whole thing is received, and it's a lot more costly to receive 733 datagrams than it is to receive 92, especially since it's much more likely that some of the 733 will disappear along the way.
If, for example, the first couple of hundred KB split parts arrived out of 1MB, and then one of the parts got lost, all the already-received parts would hang around in memory not getting processed. With smaller chunks this is much less of a problem.
I explored taking the chunk size all the way down to 1KB to reduce the bandwidth waste caused by split packets (split headers), but this made resource pack downloading unbearably slow, so it wasn't acceptable.
2020-05-31 19:29:26 +01:00
11cedc4011 Player: added a constant for resource pack chunk sending size 2020-05-31 19:23:21 +01:00
3f2455f090 SnowLayer: fixed layer stacking, closes #2775 2020-05-31 17:38:24 +01:00
9d26a224a2 DoublePlant: add flammability info, closes #2465 2020-05-31 17:04:50 +01:00
c4ad390463 pocketmine.yml: raise default population-queue-size to 32
the old limit was made in the php5 days when performance was far worse and it was much more costly to generate chunks that weren't needed. Now it's significantly less and having a higher limit allows terrain to be sent more quickly in new worlds and to fast-moving players.
This limit really ought to go away completely but considering the technical barriers in the way it'll have to stay for now.
2020-05-31 16:58:53 +01:00
42e14f749e Do not blanket-ban all inventory transactions in spectator mode, fixes #2627
instead, we cancel the appropriate events before they are called, so that plugins can uncancel them if they choose.
2020-05-31 16:32:06 +01:00
484557935e Level: remove dead block placement code (player movements are now always processed immediately, just not immediately broadcasted) 2020-05-31 16:06:48 +01:00
485f573955 Player: remove move buffering, implement simple rate limited movement… (#3167)
Introduction
This PR is a second attempt at improving movement processing to fix #1215 , #2730 and more.

This is significantly less complex than the previous attempt #2646 -- it gets rid of the movement buffering system entirely and instead relies on a simple rate limit counter to restrict on-the-fly movement processing.

Movement is rate limited to a max average of 2 per tick. It allows up to 5 seconds' backlog to accommodate network lag. The rate limit counter is increased by 2 per tick and decreased once for every movement processed. This prevents movement processing being abused for denial of service attacks.

Changes
API changes
This PR, while obviously highly beneficial for most current users, poses some BC-breaking issues because of changes to the internal Player API.

Player->processMovement() (protected) has been removed. This is a BC concern for custom player classes which overrode it and called it as a parent. In addition, child implementations won't be called every tick any more, which could break some custom movement processing systems.
Player->newPosition (protected) has been removed. This internal field may have been accessed by custom movement implementations.
Player->isTeleporting (protected) has been removed. BC concern is same as previous point.
Player->getNextPosition() (public) has been @deprecated.
Added the following protected Player class members:
int $moveRateLimit
?Vector3 $forceMoveSync
handleMovement()
processMostRecentMovements()
revertMovement()
Behavioural changes
Player movement is now subject to less rubberbanding and has more reliable behaviour.
2020-05-31 15:51:30 +01:00
71e0521286 Merge branch 'stable' into next-minor
# Conflicts:
#	composer.lock
2020-05-31 14:43:53 +01:00
3f07f06874 updated composer lockfile 2020-05-31 14:42:31 +01:00
10279e11ed updated build/php submodule to pmmp/php-build-scripts@d475b694e4 2020-05-31 14:33:26 +01:00
673e444456 phpstan: fix build failure 2020-05-31 13:02:32 +01:00
89c49d77c6 ditch irstea/phpunit-shim, more trouble than it's worth 2020-05-31 12:03:38 +01:00
c3a795e876 Fix walk sounds (#3492)
Co-authored-by: Govdim <govdim.govorek@gmail.com>
2020-05-24 14:03:14 +01:00
4199c3796f Water: Remove duplicate call to Entity->resetFallDistance() (#3524)
It's not clear what the intended goal of this code was, but the duplicate call is obviously useless.
2020-05-24 14:00:19 +01:00
ecbf21acea Utils: added OS constants, remove hardcoded OS strings everywhere 2020-05-23 11:05:58 +01:00
45c89d084c TimeCommand: add time aliases "noon", "sunset", "midnight", "sunrise"
closes #3508
2020-05-20 20:42:21 +01:00
56f90a2901 Merge branch 'stable' into next-minor 2020-05-20 20:05:00 +01:00
bf6af269c8 StartGamePacket: use PUBLIC visibility by default 2020-05-20 20:04:39 +01:00
73d1f84072 Merge branch 'stable' into next-minor 2020-05-20 19:36:20 +01:00
a29424f5b3 UIProfile: name constants as they appear in the MC user-facing settings
I think these names are stupid, but it'll be easier for people to figure out what they refer to if they match the client-sided naming.
2020-05-20 15:06:50 +01:00
ff3af492f8 Random: remove multiline formatting in nextSignedInt()
I'm sick to death of formatting tools messing with this...
2020-05-20 14:34:29 +01:00
80b804f7aa automated imports cleanup 2020-05-20 14:31:31 +01:00
3a78735982 added UIProfile protocol constants
these are only used in the LoginPacket, but plugin devs will probably find them useful anyway
2020-05-20 14:27:50 +01:00
ab32784c74 UpdateBlockSyncedPacket: remove default values for fields 2020-05-20 13:58:36 +01:00
816234a379 UpdateBlockSyncedPacket: populate missing information for second field 2020-05-20 13:57:08 +01:00
b7bf92a5e9 SimpleEventPacket: added missing type constant 2020-05-20 13:50:43 +01:00
dcca000ead PlayerActionPacket: added missing constant 2020-05-20 13:44:52 +01:00
c4ea51f985 NpcRequestPacket: added request type constants 2020-05-20 13:42:40 +01:00
8202bb1cd8 MoveActorAbsolutePacket: added missing flag 2020-05-20 13:39:56 +01:00
b75758e35e MoveActorDeltaPacket: added missing flags 2020-05-20 13:39:37 +01:00
38a06f76f8 LoginPacket: remove unused constant 2020-05-20 13:15:43 +01:00
84f99ed418 CommandOriginData: Rename previously unknown field 2020-05-20 13:00:03 +01:00
fd63f19199 make use of new GameRuleType constants 2020-05-20 12:35:29 +01:00
66d44aa814 added GameRuleType protocol constants 2020-05-20 12:35:07 +01:00
f3089f577e StartGamePacket: use new protocol constants for multiplayer visibility 2020-05-20 12:22:33 +01:00
9516ef1632 added GamePublishSetting protocol constants 2020-05-20 12:22:12 +01:00
e982a57cb5 StartGamePacket: use EducationEditionOffer constants 2020-05-20 12:08:56 +01:00
f57fa2252b added EducationEditionOffer protocol constants 2020-05-20 12:08:36 +01:00
02cc370855 StartGamePacket: use GeneratorType constants 2020-05-20 12:05:41 +01:00
0a5d14a840 added GeneratorType protocol constants 2020-05-20 12:05:41 +01:00
3ec2994d7f added protocol GameMode constants 2020-05-20 11:53:35 +01:00
da4a2d8552 Fixed a bunch of missed Position->getLevel() usages
these were not in the usage search because PhpStorm decided to refer to ChunkLoader->getLevel() for any Player references, which caused them to only show when that was searched.

There's also an undetected LSP violation with ChunkLoader because it requires returning Level and Position->getLevel() returns Level|null. I don't know why PHPStan doesn't complain about that.
2020-05-19 21:01:18 +01:00
dc9351b024 Merge remote-tracking branch 'origin/stable' into next-minor 2020-05-19 11:26:25 +01:00
786f416f2e Improved support for paths with backslashes on Unix filesystems (#3501)
Fixes #3497
2020-05-19 10:34:51 +01:00
a67d2ae978 parity: burning players no longer shoot burning arrows (#3509)
this behaviour doesn't exist in vanilla.
2020-05-19 10:31:39 +01:00
089180fef4 Players no longer burn when hit by other players, closes #3170 (#3470) 2020-05-18 21:13:56 +01:00
15baf09339 update l7 and l8 baselines for next-minor branch 2020-05-18 20:16:03 +01:00
083dde8395 Merge branch 'stable' into next-minor 2020-05-18 20:10:47 +01:00
fffa4b9501 phpstan: baseline our way to level 8
we really need the level 8 checks on new code now, and waiting until old code is clean is not going to benefit us much because the issues that are there already exist.
2020-05-18 20:09:25 +01:00
22b5de09b4 added colours to EnchantParticle and InstantEnchantParticle, closes #3368 2020-05-18 20:00:51 +01:00
71a8b0340c BUILDING.md: EOF newline [ci skip] 2020-05-18 19:23:18 +01:00
e544055bbc BUILDING.md: change build/server.phar to build/server-phar.php
closes #3507
2020-05-18 18:54:14 +01:00
8f5db7c297 3.12.2 changelog typo (#3505) 2020-05-18 12:26:07 +01:00
a5edfa368e Merge branch 'stable' into next-minor 2020-05-18 10:25:25 +01:00
27f55e4c96 3.12.3 is next 2020-05-18 10:17:08 +01:00
9b6b3f50a1 Release 3.12.2 2020-05-18 10:17:02 +01:00
1fb0ba6fc0 travis: update PHP extension versions 2020-05-18 09:47:56 +01:00
f1d378468e updated build/php submodule to pmmp/php-build-scripts@a6ab41089e 2020-05-18 09:44:43 +01:00
9ebd6d6b0f Timezone: remove rogue newline 2020-05-17 14:03:22 +01:00
58e32086c0 Level: Updated TIME_* constants (#3385)
- Added Level::TIME_NOON
- Added Level::TIME_MIDNIGHT
- Changed values of Level::TIME_DAY and Level::TIME_NIGHT
2020-05-17 10:10:35 +01:00
8c0d441a13 Merge branch 'stable' into next-minor 2020-05-17 10:03:17 +01:00
25fb5140a2 Merge #3456: Added protocol DeviceOS constants
close #3456

since this is a protocol addition and not API, this belongs on stable so
that protocol changes can consistently use it without worrying about
branch compatibility.
2020-05-17 09:57:39 +01:00
f5a49b6d55 LightUpdate: improve quality of property type info 2020-05-17 09:33:18 +01:00
189f12a644 PermissionManager: fix timings not being stopped for default calculation (#3502) 2020-05-17 08:55:11 +01:00
5a8917f6f2 TaskScheduler: queue contains TaskHandler, not Task 2020-05-16 15:27:20 +01:00
f3e436592a ZippedResourcePack: detect location of manifest.json if it's not in the root
this isn't quite as bulletproof as I'd like it to be, but it should work...
2020-05-15 19:41:45 +01:00
35747874f6 Cocoa: fix drops, closes #3326
this doesn't really do a whole lot, but I want this issue off my tracker, it's worthless...
2020-05-14 00:56:39 +01:00
59445902b8 Entity: do not spawn to players who haven't received the target chunk yet, closes #3355
this might cause some delays on chunk resend as documented on master, but entities all get respawned on a resend, so this doesn't matter much. It's better than whatever side effects might come with spawning an entity in a chunk that the player doesn't have yet.
2020-05-14 00:46:54 +01:00
2eb62c85f6 Server: cast network.compression-level to int, closes #3393 2020-05-14 00:37:54 +01:00
ad2a39bf13 Merge #3489: fix spawn protection check for placement checking the wrong block, close #3488, close #3489 2020-05-14 00:30:11 +01:00
0847358070 SkinImage: fix inverted auto-detect height/width, closes #3490
it seems like the client doesn't care if the h/w are inverted for skins anyway, but this is still wrong anyway...
2020-05-14 00:22:12 +01:00
8766d4050c Merge branch 'stable' into next-minor 2020-05-13 13:18:55 +01:00
54f41dc145 3.12.2 is next 2020-05-13 13:10:25 +01:00
ded45bddfe Release 3.12.1 2020-05-13 13:10:10 +01:00
b044550475 Player: duct tape for yet another login sequence bug in the shitty net architecture 2020-05-13 12:59:43 +01:00
a70fa15690 phpstan 0.12.25, drop some bug-filtering error patterns 2020-05-13 12:51:52 +01:00
bd1d7b8d75 asserts 👏 are 👏 not 👏 error 👏 checking 2020-05-13 12:39:55 +01:00
1513a0e092 VerifyLoginTask: beware wrong number of parts when splitting JWT 2020-05-13 12:39:53 +01:00
c4150d4520 Bump phpstan/phpstan from 0.12.23 to 0.12.25 (#3486) 2020-05-11 20:19:30 +00:00
2f47597d75 BaseLevelProvider: stop crashing the server on invalid level.dat
broken userdata isn't a core bug, so it shouldn't be flooding our crash archives.
2020-05-11 15:34:53 +01:00
56883f9ff9 MemoryManager: explicitly assume return type of ini_get() 2020-05-10 11:35:53 +01:00
7cdd26add5 Player: make handleLogin() less unreadable 2020-05-10 11:23:11 +01:00
717b866605 Merge branch 'stable' into next-minor 2020-05-06 20:25:11 +01:00
ef97c8f99e Utils: fix parsing of single-line doc comments, closes #3388 (#3469)
* Utils: fix parsing of single-line doc comments, closes #3388

* correctly handle the empty doc-comment case, add another test case

* ignore an extra phpstan bug
2020-05-06 14:17:08 +01:00
84932ce908 Bump phpstan/phpstan from 0.12.22 to 0.12.23 (#3468) 2020-05-05 15:59:22 +00:00
6bfc309a0a phpstan 0.12.22 2020-05-04 22:26:44 +01:00
06e8c6a3ad Entity: new data flags 2020-05-04 21:58:44 +01:00
71271a0e03 Use env to locate bash (#3439)
This change uses env to locate bash, instead of hard-coding it. This is necessary on FreeBSD. Tested on Linux as well.
2020-04-30 17:34:13 +01:00
f87e96026c Merge branch 'stable' into next-minor 2020-04-26 00:28:09 +01:00
b63ad032a9 phpstan 0.12.19 2020-04-26 00:08:41 +01:00
d9b0e373bb Populator: fix return type of populate() 2020-04-26 00:04:17 +01:00
8e1b3edd2c Merge branch 'stable' into next-minor 2020-04-25 12:06:52 +01:00
32262d9bb5 Merge branch 'stable' of https://github.com/pmmp/pocketmine-mp into stable 2020-04-25 11:59:35 +01:00
4c1b10b24b restructure ignoreErrors to allow regenerating them file by file 2020-04-25 11:59:06 +01:00
61dc9d7f6b phpstan: split up phpstan-bugs and phpstan-bugs-generated configs 2020-04-25 11:26:54 +01:00
da9731ef59 phpstan: add stub defines to improve analysis 2020-04-25 11:07:05 +01:00
e6f64c609e bootstrap: avoid making COMPOSER_AUTOLOADER_PATH having a possible false type 2020-04-25 11:04:09 +01:00
8c7fbf379b Bump irstea/phpunit-shim from 8.5.3 to 8.5.4 (#3435) 2020-04-24 20:45:43 +00:00
3d2ca457f8 protocol: Added missing Window Types (#3420) 2020-04-19 15:43:11 +01:00
1579f41056 Added missing Enchantment IDs (#3419) 2020-04-19 15:42:27 +01:00
34a3e0d8b1 Level: fix sneaking use-item logic (thanks @95CivicSi)
inspired by, but closes #3403
fixes #3401
fixes #2539
fixes #1904
2020-04-19 15:38:12 +01:00
d42217ff57 Bump phpstan/phpstan-phpunit from 0.12.6 to 0.12.8 (#3421) 2020-04-19 14:26:22 +00:00
70a4f73d73 Drop PHP 7.2 support 2020-04-19 12:49:25 +01:00
7d43dffac4 updated phpstan/phpstan-phpunit 2020-04-19 12:44:26 +01:00
804a062c3a CrashDump: report the versions of all Composer libraries installed 2020-04-19 12:11:34 +01:00
22a4639162 Merge branch 'stable' into next-minor 2020-04-18 13:32:52 +01:00
39d02a67d2 3.12.1 is next 2020-04-17 18:39:20 +01:00
77d45bf116 Release 3.12.0 2020-04-17 18:39:15 +01:00
f79182852b Fixed typo in login handler (wrong variable to store persona pieces) (#3422) 2020-04-17 14:36:51 +01:00
a107ad7404 1.14.60 support (#3407) 2020-04-17 09:18:00 +01:00
7a072931df Merge branch 'stable' of https://github.com/pmmp/pocketmine-mp into stable 2020-04-17 01:58:26 +01:00
f428a9bf52 protocol: rename some useless fields 2020-04-17 01:58:06 +01:00
2e720b48d9 AnimatePacket: added a new constant 2020-04-17 01:57:20 +01:00
a6e79bedf5 ActorEventPacket: plug some gaps 2020-04-17 01:56:56 +01:00
a5ba570fdf StructureBlockUpdatePacket: Added encode/decode (#3148) 2020-04-17 01:11:16 +01:00
0d5164af02 LabTablePacket: updated type field and added constants 2020-04-16 22:03:36 +01:00
534af770f8 InteractPacket: new constant 2020-04-16 22:03:34 +01:00
619a9892e5 RCON: properly handle potential errors during socket setup 2020-04-16 01:29:28 +01:00
63b109f23e RCONInstance: fixed incorrect doc comment for field 2020-04-15 21:16:47 +01:00
79ed377c7a Bump pocketmine/nbt from 0.2.13 to 0.2.14 (#3406)
Bumps [pocketmine/nbt](https://github.com/pmmp/NBT) from 0.2.13 to 0.2.14.
- [Release notes](https://github.com/pmmp/NBT/releases)
- [Commits](https://github.com/pmmp/NBT/compare/0.2.13...0.2.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-15 13:45:52 +01:00
2da8ce7a20 PluginBase: account for fopen() maybe returning false
again, the error handler will normally take care of this, but we can't assume that the error handler is always set.
2020-04-15 13:05:41 +01:00
959dd4cbf1 PluginManager: explicitly assume the result of FilesystemIterator arrayification 2020-04-15 12:59:15 +01:00
0a3788f9ac ScriptPluginLoader: properly handle the case where the script itself is somehow missing
this won't ever happen during PocketMine runtime, but it might happen if something else tries to use it.
2020-04-15 12:54:45 +01:00
cdda74ef93 PluginDescription: use result of phpversion() to check if extension is loaded
technically phpstan should account for this with the extension_loaded() check, but it currently doesn't and it's not worth fighting with it when the fix is so simple anyway.
2020-04-15 12:50:53 +01:00
bbe428a874 Spawnable: explicitly assume that NBTStream->write() will not return false
it will never return false under these circumstances
2020-04-15 12:43:44 +01:00
755919c496 SendUsageTask: explicitly assume that json_encode() will not return false 2020-04-15 12:43:05 +01:00
88b216a17b AsyncTask: fix another phpstan level 7 error about wrong types
this should really be a dedicated type, but everything done with pthreads sucks.
2020-04-15 12:42:18 +01:00
8020912448 AsyncPool: add phpstan array type information to all fields 2020-04-15 12:40:54 +01:00
5571ae05b5 AsyncPool: silence a warning about wrong key type
getTaskId() returns int|null, although it won't happen after the ID has been set.
2020-04-15 12:40:23 +01:00
bc985198a0 Config: do not expect string keys on parseProperties(), because of key casting (PHP sucks) 2020-04-15 12:20:23 +01:00
27b2710c56 Config: make phpstan happy for load()
currently this will never be reached if the regular exception handler has been set, but it might not be set if the class is used on its own.
2020-04-15 12:18:22 +01:00
1755b25808 Utils: make explicit assumption about result of scandir() in recursiveUnlink()
TODO: this assumption might be flawed in the case of threading...
2020-04-15 12:13:26 +01:00
a78133d0e3 Utils: provide phpstan type information for testValidInstance() 2020-04-15 12:12:18 +01:00
a51a16a55c Utils: silence PHPStan warning about array_combine() result
phpstan doesn't account for having 2 arrays of the same size, and even if it did, the size cannot be inferred easily here.
2020-04-15 12:11:13 +01:00
099562d582 Utils: assume explicitly that ob_get_contents() will not return false in getReferenceCount() based on context 2020-04-15 12:10:21 +01:00
ae76e8f08f Utils: fix some implicit casts to boolean on result of preg_match_all() 2020-04-15 12:09:10 +01:00
42a08e7e4a Utils: don't assume that callable is only array|string implicitly
this should really support closures too, but since it's not used anywhere, I feel OK with this change.
2020-04-15 12:08:33 +01:00
b193d9f919 Process: shut up PHPStan about possible float returns on getMemoryUsage and friends
this can never actually happen because the given data sources will never have such large numbers, but PHPStan doesn't know this.
2020-04-15 12:07:24 +01:00
24d64eedab Process: make some assumptions about I/O explicit for type safety 2020-04-15 12:05:13 +01:00
0c9d16f1ef Internet: explicitly assume return of curl_exec() is string after error checking
this is documented as string|bool, but it's actually string|false if CURLOPT_RETURNTRANSFER is set, and bool if not.
2020-04-15 12:02:38 +01:00
d246933e3e TextFormat: account for failure to encode JSON in toJSON() 2020-04-15 11:29:50 +01:00
41d7b8c0e4 TextFormat: properly handle pcre errors in some cases
these would previously just hit type errors.
2020-04-15 11:29:04 +01:00
2622c34542 Terminal: explicitly assume that fopen(stdin) will not fail 2020-04-15 11:12:03 +01:00
5c9419b55c Timezone: use false checks instead of file existing for static analysis 2020-04-15 10:59:36 +01:00
83c40f4502 Timezone: properly account for failure to read timezone file 2020-04-15 10:23:07 +01:00
372202b3dc Utils: use type-safe checks to ensure file validity
this gives the same results while keeping phpstan happy.
2020-04-15 10:19:51 +01:00
917c744266 Properly handle error conditions in Utils::decodeJWT() 2020-04-15 10:18:02 +01:00
2281fe4e67 Account for reflection filename being false (in the case of classes/functions defined by builtins) 2020-04-15 10:15:38 +01:00
cf538d83bf Timezone: shut phpstan up about impossible ini_get() errors 2020-04-15 09:48:20 +01:00
7e9c38a9d9 Timezone::parseOffset() returns string|false, not string|bool 2020-04-15 09:47:52 +01:00
ccad97727f UUID: properly account for garbage inputs which aren't valid hexadecimal
this would previously throw a TypeError and crash.
2020-04-15 09:44:14 +01:00
e3ebf8bb61 Internet::getIP() returns string|false, not string|bool 2020-04-15 09:39:38 +01:00
cb6b59a52a Internet: curl_init() may return false on error (unclear on reasons) 2020-04-15 09:38:39 +01:00
53dbbd5f97 Internet: account for socket_create() maybe failing in getInternalIP() 2020-04-15 09:37:17 +01:00
51908ec45a Player: allow provision of a custom cooldown duration for items
this would be more useful to plugins, so that it's not necessary to extend any item classes for this trivial purpose.
2020-04-15 09:32:48 +01:00
a2543ff80d Position: add getLevelNonNull()
this allows assuming that a position has a valid world in places where it's never expected to not be valid. Since this is the vast majority of usages, it eliminates a lot of possible null-pointer warnings given by static analysers.
TODO: Consider whether we can make Position->getLevel/World use this behaviour out of the box in the next major version.
2020-04-14 11:08:37 +01:00
20f3030709 3.11.8 is next 2020-04-14 02:54:41 +01:00
6e08b622b3 Merge branch 'stable' into next-minor 2020-04-14 01:43:23 +01:00
604900d4c5 Merge branch 'stable' into next-minor 2020-04-14 01:38:01 +01:00
6422ed7722 Added RakLibInterface::setPacketLimit() (#3398) 2020-04-11 23:27:17 +01:00
06a9c98ded MemoryManager: fix strict-rules error on phpstan level 7 2020-03-15 15:49:04 +00:00
5c7b05c2ba CrashDump: do not assign possibly-false return value of fopen() directly to non-union field
this would become a problem with typed properties, and also phpstan level 7 doesn't like it.
2020-03-15 15:48:09 +00:00
9c86763322 CrashDump: do not assume that error_get_last() always returns array
this returns NULL if there was no error before the shutdown handler was triggered (usually caused by a plugin calling exit() prematurely).
2020-03-15 15:47:07 +00:00
35490ca41c CrashDump: do not assume that file() always returns array
phpstan level 7 prep
2020-03-15 15:46:03 +00:00
47c7872c88 Merge branch 'stable' into next-minor 2020-03-14 13:42:39 +00:00
f8ce01e2fd ItemFactory: extract fromStringSingle() from fromString()
on PM4, the multiple functionality is removed, but on PM3 this is a problem for phpstan.
2020-03-13 17:54:25 +00:00
3907a2b6ba Process: split getMemoryUsage() into 2 functions
this isn't released yet so it's OK to change.
phpstan level 7 doesn't like these kinds of ambiguous return types because there's no way for it to tell which type is returned without a return type specifying extension, and it's easier to just change the API than to make PHPStan understand it.
2020-03-13 17:32:17 +00:00
1171cd2493 Merge branch 'stable' into next-minor 2020-03-13 15:16:42 +00:00
e2579e0a2a Merge branch 'stable' into next-minor 2020-03-12 11:47:33 +00:00
5a9a576bfa AddActorPacket: remove unused import 2020-03-10 12:45:52 +00:00
b8caf34e62 Merge branch 'stable' into next-minor 2020-03-10 12:45:00 +00:00
dc757c25c8 cleanup CS from 8ec0a4d0d6 2020-03-10 12:14:38 +00:00
73267ae077 Merge branch 'stable' into next-minor 2020-03-10 12:11:36 +00:00
8ec0a4d0d6 Allow specifying compatible OS in plugin manifest (#3192) 2020-03-10 10:12:56 +00:00
df65f1009c Merge branch 'stable' into next-minor 2020-03-09 14:33:37 +00:00
c19ab97610 AddActorPacket: move BC hack to higher level
we shouldn't hack the protocol impl for BC.
2020-02-27 17:37:45 +00:00
dbaf851be7 Merge branch 'stable' into next-minor 2020-02-27 16:51:06 +00:00
19bd283807 Process: drop a blank line 2020-02-10 12:23:11 +00:00
20d1a048dd fixup imports 2020-02-10 12:21:56 +00:00
15b76a24b7 scrub useless phpdoc 2020-02-10 12:21:07 +00:00
2d51971b84 Revert "ClosureTask: drop requirement for void return type"
This reverts commit 9e993aa83f.

apparently PHPStan isn't cool with this ...
2020-02-10 11:40:47 +00:00
f08e411cad Merge branch 'stable' into next-minor 2020-02-10 11:40:08 +00:00
870c66d1fe Merge branch 'stable' into next-minor 2019-12-12 18:29:04 +00:00
1a467420e3 Merge branch 'stable' into next-minor 2019-12-12 13:07:02 +00:00
02fcfcc383 Merge branch 'stable' into next-minor 2019-12-09 10:47:53 +00:00
09961b5cd0 Merge branch 'stable' into next-minor 2019-12-04 22:16:04 +00:00
71a472e0eb Merge branch 'stable' into next-minor 2019-12-04 19:52:01 +00:00
e65bc5c3ae Merge branch 'stable' into next-minor 2019-12-04 11:11:29 +00:00
2ae37cc1c5 Merge branch 'stable' into next-minor 2019-12-03 19:59:35 +00:00
9a67192f74 Merge branch 'stable' into next-minor 2019-12-03 10:45:51 +00:00
4cfceeeb8e bootstrap: fix merge error introduced by cb76f8a5df
this error is harmless since it points to a deprecated function, but it showed up as conflicted in master merge.
2019-11-29 12:03:07 +00:00
3e4e0d51df Merge branch 'stable' into next-minor 2019-11-29 11:55:12 +00:00
cb76f8a5df Merge branch 'stable' into next-minor 2019-11-21 23:21:16 +08:00
0591458ef6 Merge branch 'stable' into next-minor 2019-10-22 18:49:22 +01:00
eeddaced9f PluginManager: Remove useless deprecation warning message
this message just confuses end users and is of little use to a developer. It doesn't make any sense to make a special case for events when we have lots of other deprecated things to think about anyway which won't be shown warnings for.
2019-08-02 16:34:00 +01:00
c237ff538c Merge branch 'stable' into next-minor 2019-08-02 16:32:00 +01:00
23b00bea5b Merge branch 'stable' into next-minor 2019-07-29 17:31:06 +01:00
cde2c10c1d AsyncTask: partial backport of 6ac0c517f5 (simplify TLS)
- deprecated AsyncTask::peekLocal()
- AsyncTask::fetchLocal() no longer deletes stored data
2019-07-27 15:09:42 +01:00
87fb42cabd Merge branch 'stable' into next-minor 2019-07-27 14:53:13 +01:00
6566dd8c8f AsyncPool: Remove useless warning about complex data leftovers
this is automatically cleaned up anyway, so this warning is just redundant noise.
2019-07-27 14:44:40 +01:00
1e65ac0d85 Merge branch 'stable' into next-minor 2019-07-27 14:40:31 +01:00
cb247a5f28 AsyncTask: Deprecate methods removed in 2c4f2810d2 2019-07-26 19:52:26 +01:00
bb048fb361 Merge branch 'stable' into next-minor 2019-07-26 19:50:17 +01:00
9e993aa83f ClosureTask: drop requirement for void return type
this creates unnecessary boilerplate for many inline usages.
2019-07-01 17:19:19 +01:00
fab12707ae Merge branch 'stable' into next-minor 2019-07-01 17:18:20 +01:00
51f299f196 Merge branch 'stable' into next-minor 2019-06-22 19:46:23 +01:00
2bb52cf811 Merge branch 'stable' into next-minor 2019-06-22 16:03:28 +01:00
6afc689529 Add Level->getTimeOfDay(), closes #2908 (#2979) 2019-06-22 16:00:18 +01:00
5a17a0d1aa Merge branch 'stable' into next-minor 2019-06-16 16:37:23 +01:00
b38c81c96f backport f84a1729c: Inventory: added swap() function 2019-06-16 16:35:34 +01:00
0fabc0c199 backport b8d1eb20b: EntityDeathEvent: add XP amount API, closes #2690 2019-06-16 16:31:03 +01:00
ec5598dbb1 Deprecate things removed in acb794e72 and 97c836f19 2019-06-16 15:37:27 +01:00
7b98d203f4 Merge branch 'stable' into next-minor 2019-06-16 14:47:01 +01:00
4635b93f4d backport 23071d257 + deprecations: Extract process-related functions from Utils into a separate Process class 2019-06-16 14:40:51 +01:00
a8433697ad backport aaaddd1fd: Terminal: stick a RESET on the end when writing a newline 2019-06-16 14:25:16 +01:00
680cdb8e3e backport f5dbbea5f: Utils: added recursiveUnlink() 2019-06-16 14:20:05 +01:00
eaa78fe849 backport 205e13d88: Config: add getPath() (#2758)
Config->getPath() returns the path of the config i.e. the place where the config file is located.
2019-06-16 14:18:50 +01:00
eedea4998b Terminal: Added write() and writeLine() to allow easily emitting Minecraft-formatted text to the console 2019-06-16 14:16:14 +01:00
4e5a80c481 Recipe: deprecate interface 2019-06-16 14:12:49 +01:00
4d54dc30c1 crafting: deprecate some stuff that's been removed on bleeding edge 2019-06-16 14:11:08 +01:00
ac5339414a Merge branch 'stable' into next-minor
# Conflicts:
#	build/preprocessor
2019-06-16 14:08:06 +01:00
9fd922fe6a Merge branch 'stable' into next-minor 2019-05-02 17:25:29 +01:00
fdaf9dce73 Merge branch 'stable' into next-minor 2019-05-02 16:39:05 +01:00
732e27751c Merge branch 'stable' into next-minor 2019-05-02 15:04:23 +01:00
932c489de1 Rename addTitle/addSubTitle/addActionBarMessage prefixes to "send", deprecated old variants
closes #2896

these deprecated methods will be removed in 4.0.
2019-05-01 18:54:20 +01:00
329 changed files with 12187 additions and 1422 deletions

6
.gitignore vendored
View File

@ -41,3 +41,9 @@ test_data/*
# Doxygen
Documentation/*
# PHPUnit
/.phpunit.result.cache
# php-cs-fixer
/.php_cs.cache

View File

@ -1,16 +1,14 @@
language: php
php:
- 7.2
- 7.3
before_script:
- phpenv config-rm xdebug.ini
# - pecl install channel://pecl.php.net/pthreads-3.1.6
- echo | pecl install channel://pecl.php.net/yaml-2.0.4
- echo | pecl install channel://pecl.php.net/yaml-2.1.0
- git clone https://github.com/pmmp/pthreads.git
- cd pthreads
- git checkout 1b7da492b944146fa9680f6399bd9c6c6c6095e0
- git checkout 646dac62ae0d48c1ada7b007e15575fb84f7d71d
- phpize
- ./configure
- make

View File

@ -2,7 +2,7 @@
## Pre-requisites
- A bash shell (git bash is sufficient for Windows)
- [`git`](https://git-scm.com) available in your shell
- PHP 7.2 or newer available in your shell
- PHP 7.3 or newer available in your shell
- [`composer`](https://getcomposer.org) available in your shell
## Custom PHP binaries
@ -30,9 +30,9 @@ If you use a custom binary, you'll need to replace `composer` usages in this gui
Preprocessor requires that the `cpp` (c preprocessor) is available in your PATH.
## Building `PocketMine-MP.phar`
Run `build/server.phar` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
Run `build/server-phar.php` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
You can also use the `--out` option to change the output filename.
## Running PocketMine-MP from source code
Run `src/pocketmine/PocketMine.php` using your preferred PHP binary.
Run `src/pocketmine/PocketMine.php` using your preferred PHP binary.

View File

@ -3,7 +3,7 @@
<b>A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP</b>
</p>
[![Build Status](https://travis-ci.org/pmmp/PocketMine-MP.svg?branch=master)](https://travis-ci.org/pmmp/PocketMine-MP)
[![Build Status](https://travis-ci.com/pmmp/PocketMine-MP.svg?branch=master)](https://travis-ci.com/pmmp/PocketMine-MP)
## Getting started
- [Documentation](http://pmmp.readthedocs.org/)

View File

@ -49,7 +49,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
*
* @return string[]
*/
function preg_quote_array(array $strings, string $delim = null) : array{
function preg_quote_array(array $strings, string $delim) : array{
return array_map(function(string $str) use ($delim) : string{ return preg_quote($str, $delim); }, $strings);
}

58
changelogs/3.12.md Normal file
View File

@ -0,0 +1,58 @@
**For Minecraft: Bedrock Edition 1.14.60**
### 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.12.0
- Added support for Minecraft: Bedrock Edition 1.14.60
- Removed compatibility with 1.14.0-1.14.30
# 3.12.1
- Fixed parsing of single-line doc comments for event handlers, e.g. `/** @ignoreCancelled */` should now work correctly.
- The server will no longer crash on failure to load `level.dat` contents, but will gracefully shutdown instead without producing a crashdump.
- Fixed some bugs in login verification that could cause undefined behaviour.
- Fixed item-use behaviour when sneaking - sneaking and clicking a block with an empty hand, and sneaking and using an item, both now follow vanilla behaviour.
- `start.sh` will now work on platforms where `/bin/bash` is not available, as long as `/usr/bin/env` knows where bash is.
# 3.12.2
- Fixed permission default timings not being reported in timings reports (they were never stopped, only started).
- Resource packs with a directory tree like `pack.zip/MyPack/manifest.json` are now supported. Note that the manifest closest to the root will be used.
- Fixed `SkinImage` height and width being inverted at the protocol layer.
- Fixed blocks being able to be placed inside the spawn protection radius by clicking the side of a block outside the radius.
- Fixed server crash when `network.compression-level` is overridden by a CLI parameter.
- Fixed moving entities spawning themselves to players registered on chunks when the players haven't received the chunk yet.
- Cocoa pods now drop cocoa beans when broken instead of the block itself.
# 3.12.3
- Core code is now analyzed using PHPStan level 8 (using baselines). While not all the code is level 8 compliant, this does mean that new code will be held to a higher standard, ensuring quality going forwards.
- Players no longer burn when melee-attacked by other players. (vanilla parity)
- Arrows shot by burning players are no longer on fire. (vanilla parity)
- Fixed a crash that could occur with plugins on Unix filesystems that had backslashes in their names.
- Cleaned up a whole bunch of unknowns in the protocol layer. Many new constants have been added.
- Fixed player walking sounds.
- Default generation queue size has been raised to 32 (previously 8). The previous default was selected in a time when PHP was much less performant than it is today, and in today's world it just needlessly slows things down.
- Double plants are now burned away by fire.
- Snow layers can now be stacked. (vanilla parity)
- Resource pack sending chunk size has been reduced to 128 KB (previously 1 MB). This change was made after analyzing the effects that larger pack chunk sizes have on RakNet. Given the technical evidence, a smaller size, while slightly less bandwidth-efficient, should be more manageable for RakNet due to lower split reassembly overhead and reduced memory pressure.
- Fixed "switching" (an exploit often complained about by PvP players). Now, the previous damage is subtracted from current damage when an entity is attacked while on cooldown. This means that attacking with a wooden sword and then diamond sword while attack cooldown is active will only deal as much damage as the diamond sword would have, instead of the combined total. This can be controlled using the `EntityDamageEvent::MODIFIER_PREVIOUS_DAMAGE_COOLDOWN` modifier. (vanilla parity)
- Fixed projectiles knocking mobs back in unexpected directions on collision.
- Fixed inventories not being synchronized on failed inventory transactions.
- Vector3s decoded from packets are no longer rounded directly. Instead, the player movement handler takes responsibility for rounding the coordinates to prevent anti cheat doing something it's not supposed to.
- `mobflame` particle can now be spawned using the `/particle` command.
- Fixed several internal errors that could occur while modifying writable books.
- Fixed swapping writable book pages not working in some cases.
- `WritableBook->getPageText()` no longer throws an exception when the page doesn't exist, but returns null (as it was originally intended to).
# 3.12.4
- Fixed absorption hearts not being consumed.
# 3.12.5
- Fixed broken attack cooldowns.
# 3.12.6
- Fixed entities not getting movement updates after teleports.
- Fixed slow flight in spectator mode when starting from the ground and after teleportation.
- Errors communicating with the crash archive on automatic crash submission are now logged.

131
changelogs/3.13.md Normal file
View File

@ -0,0 +1,131 @@
**For Minecraft: Bedrock Edition 1.14.60**
This is a feature release, containing various minor API additions, deprecations and a few minor features.
### 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.13.0
## Core
- PHP 7.3.0 or newer is now required.
- Player movement processing has been revamped. It's now more tolerant of network lag and doesn't have as many problems with falling.
## User Interface
- `/time` now supports additional aliases `noon`, `sunset`, `midnight` and `sunrise`.
- Removed warnings when a plugin registers a handler for a deprecated event. Since this warning is developer-focused, and too specific to be useful, it just caused annoyance and confusion to users who didn't know what it meant.
## API
### General
- It's now possible to require a specific operating system using the `os` directive in `plugin.yml`. More information about this directive can be found in the [developer documentation](https://github.com/pmmp/DeveloperDocs).
### Player
- `Player->resetItemCooldown()` now accepts a second parameter, allowing plugins to provide a custom duration.
- The following methods have been deprecated and have recommended replacements:
- `Player->addTitle()` -> `Player->sendTitle()`
- `Player->addSubTitle()` -> `Player->sendSubTitle()`
- `Player->addActionBarMessage()` -> `Player->sendActionBarMessage()`
### Event
- The following methods have been deprecated:
- `EntityDespawnEvent->getType()`
- `EntityDespawnEvent->getPosition()`
- `EntityDespawnEvent->isCreature()`
- `EntityDespawnEvent->isHuman()`
- `EntityDespawnEvent->isProjectile()`
- `EntityDespawnEvent->isVehicle()`
- `EntityDespawnEvent->isItem()`
- `EntitySpawnEvent->getType()`
- `EntitySpawnEvent->getPosition()`
- `EntitySpawnEvent->isCreature()`
- `EntitySpawnEvent->isHuman()`
- `EntitySpawnEvent->isProjectile()`
- `EntitySpawnEvent->isVehicle()`
- `EntitySpawnEvent->isItem()`
- Added the following API methods:
- `EntityDeathEvent->getXpDropAmount()`
- `EntityDeathEvent->setXpDropAmount()`
- `PlayerDeathEvent::__construct()` now accepts a fourth (optional) parameter `int $xp`.
- `EntityDeathEvent::__construct()` now accepts a third (optional) parameter `int $xp`.
### Inventory
- The following classes have been deprecated:
- `Recipe`
- The following methods have been deprecated:
- `CraftingManager->registerRecipe()`
- `Recipe->registerToCraftingManager()` (and all its implementations)
### Item
- New `Enchantment` type ID constants have been added.
- `ItemFactory::fromStringSingle()` has been added. This works exactly the same as `ItemFactory::fromString()`, but it has a return type of `Item` instead of `Item|Item[]` (more static analysis friendly).
### Level
- Added the following API methods:
- `Position->getLevelNonNull()`: this is the same as `Position->getLevel()`, but throws an `AssumptionFailedError` if the level is null or invalid (more static analysis friendly).
- `Level->getTimeOfDay()`
- The following constants have been changed:
- `Level::TIME_DAY` now has a value of `1000`
- `Level::TIME_NIGHT` now has a value of `13000`
- Added the following constants:
- `Level::TIME_MIDNIGHT`
- `Level::TIME_NOON`
- The following types of particles now accept optional `Color` parameters in the constructor:
- `EnchantParticle`
- `InstantEnchantParticle`
### Network
- Added the following API methods:
- `RakLibInterface->setPacketLimit()`
### Scheduler
AsyncTask thread-local storage has been improved, making it simpler and easier to use.
- `AsyncTask->fetchLocal()` no longer deletes stored thread-local data. Instead, the storage behaves more like properties, and gets deleted when the AsyncTask object goes out of scope.
- `AsyncTask->peekLocal()` has been `@deprecated` (use `fetchLocal()` instead).
- Notices are no longer emitted if an async task doesn't fetch its locally stored data.
- The following methods have been deprecated:
- `AsyncTask->getFromThreadStore()` (use its worker's corresponding method)
- `AsyncTask->saveToThreadStore()` (use its worker's corresponding method)
- `AsyncTask->removeFromThreadStore()` (use its worker's corresponding method)
### Utils
- The following functions have been deprecated and have recommended replacements:
- `Utils::getMemoryUsage()` -> split into `Process::getMemoryUsage()` and `Process::getAdvancedMemoryUsage()` (not 1:1 replacement!!)
- `Utils::getRealMemoryUsage()` -> `Process::getRealMemoryUsage()`
- `Utils::getThreadCount()` -> `Process::getThreadCount()`
- `Utils::kill()` -> `Process::kill()`
- `Utils::execute()` -> `Process::execute()`
- Added the following constants:
- `Utils::OS_WINDOWS`
- `Utils::OS_IOS`
- `Utils::OS_MACOS`
- `Utils::OS_ANDROID`
- `Utils::OS_LINUX`
- `Utils::OS_BSD`
- `Utils::OS_UNKNOWN`
- Added the following API methods:
- `Config->getPath()`
- `Utils::recursiveUnlink()`
- `Terminal::write()`
- `Terminal::writeLine()`
# 3.13.1
- Fixed issues with `server.lock` not being unlocked on some platforms. Now, the server explicitly releases it before exiting.
- `/timings` now sends a usage message when using an unknown subcommand. Previously, it would just give no output.
- `/whitelist` now sends a usage message when using an unknown subcommand. Previously, it would just give no output.
- The output from `/timings` is now broadcasted on the `pocketmine.broadcast.admin` broadcast channel for auditability, similarly to other operator commands.
- Fixed `ShapedRecipe` deprecation warning on PHP 7.4.
- Fixed some potential crashes with Bedrock worlds when chunk data is corrupted or missing.
- Fixed a bug in region handling that caused region loaders to overestimate the amount of space used in the file. This resulted in an up to 4 MB growth of the file size every time the region was reloaded after writing a chunk.
- Region handlers now try to reuse free space in region files before putting the chunk at the end of the file. Previously, space was only reused if the new version of the chunk was <= the size of the old. This fixes endless growth of region files.
- Regions now never directly overwrite old copies of chunks when saving; instead they try to find an alternative location (preferring unused space within the file first). This avoids chunk corruption on power failure (the old copy of the chunk won't be damaged, so a rollback might occur instead), and as happy side effect, causes oversized regions to gradually shrink towards their most packed state over time, saving disk space.
- Regions now have a hard size cap at 64 GB. This is because the header pointers will overflow beyond 64 GB (besides, a normal region shouldn't be this big anyway).
- Fixed a crash that could occur when reading a too-short region header.
- `VerifyLoginTask` now only copies JWTs to verify instead of the entire login packet. This reduces the amount of data copied between threads, improving performance.
- Added a fast-fail check to `VerifyLoginTask` by checking the JWT header's `x5u` against the expected public key.
- `Skin->validate()` now throws `InvalidSkinException` instead of `\InvalidArgumentException`.
- A debug message is now logged when a player is kicked for having an invalid skin, giving a brief line of detail why.
- Fixed players not being kicked for having an invalid `resourcePatch`.
- Fixed block meta value of cake being preserved when using pick-block.
- Fixed explosions not fully destroying multi-block objects like beds and doors.

25
changelogs/3.14.md Normal file
View File

@ -0,0 +1,25 @@
**For Minecraft: Bedrock Edition 1.16.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.14.0
- Added support for Minecraft: Bedrock Edition 1.16.0.
- Removed compatibility with 1.14.60.
## Known issues (please don't open issues for these)
- Walls don't connect to each other
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
- New blocks, items & mobs aren't implemented
- Nether doesn't exist
# 3.14.1
- All skins are now trusted, bypassing the client-side trusted skin setting. Note that this means that NSFW skin filtering will **not** apply.
- Fixed projectile motion being altered by ladders.
- Fixed client-sided crashes when pressing E repeatedly very quickly on a high-latency connection.
- `/plugins`, `/whitelist list`, `/banlist` and `/list` now show output in alphabetical order.
- Some `pocketmine\event` APIs which accept arrays now have more robust type checking, fixing type errors caused by plugin input occurring in core code.
- `Attribute::getAttributeByName()` is now aware of the `minecraft:lava_movement` attribute.

View File

@ -5,7 +5,7 @@
"homepage": "https://pmmp.io",
"license": "LGPL-3.0",
"require": {
"php": ">=7.2.0",
"php": ">=7.3.0",
"php-64bit": "*",
"ext-bcmath": "*",
"ext-curl": "*",
@ -34,13 +34,14 @@
"pocketmine/log": "^0.2.0",
"pocketmine/log-pthreads": "^0.1.0",
"pocketmine/callback-validator": "^1.0.1",
"adhocore/json-comment": "^0.1.0"
"adhocore/json-comment": "^0.1.0",
"ocramius/package-versions": "^1.5"
},
"require-dev": {
"phpstan/phpstan": "^0.12.14",
"irstea/phpunit-shim": "^8.5",
"phpstan/phpstan": "0.12.29",
"phpstan/phpstan-phpunit": "^0.12.6",
"phpstan/phpstan-strict-rules": "^0.12.2"
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpunit/phpunit": "^9.2"
},
"autoload": {
"psr-4": {

1887
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,11 @@
includes:
- tests/phpstan/configs/actual-problems.neon
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
- tests/phpstan/configs/com-dotnet-magic.neon
- tests/phpstan/configs/custom-leveldb.neon
- tests/phpstan/configs/gc-hacks.neon
- tests/phpstan/configs/l7-baseline.neon
- tests/phpstan/configs/l8-baseline.neon
- tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon
- tests/phpstan/configs/phpunit-wiring-tests.neon
@ -12,13 +16,14 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
parameters:
level: 6
autoload_files:
level: 8
checkExplicitMixed: true
bootstrapFiles:
- tests/phpstan/bootstrap.php
scanFiles:
- src/pocketmine/PocketMine.php
- build/make-release.php
- build/server-phar.php
- vendor/irstea/phpunit-shim/phpunit
paths:
- src
- build/make-release.php
@ -30,161 +35,7 @@ parameters:
stubFiles:
- tests/phpstan/stubs/pthreads.stub
- tests/phpstan/stubs/chunkutils.stub
- tests/phpstan/stubs/leveldb.stub
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
staticReflectionClassNamePatterns:
- "#^COM$#"
ignoreErrors:
-
message: "#^Instanceof between pocketmine\\\\plugin\\\\PluginManager and pocketmine\\\\plugin\\\\PluginManager will always evaluate to true\\.$#"
count: 1
path: src/pocketmine/CrashDump.php
-
message: "#^pocketmine\\\\Player\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\entity\\\\Human\\.$#"
count: 1
path: src/pocketmine/Player.php
-
message: "#^Cannot instantiate interface pocketmine\\\\level\\\\format\\\\io\\\\LevelProvider\\.$#"
count: 1
path: src/pocketmine/Server.php
-
message: "#^Instanceof between pocketmine\\\\plugin\\\\PluginManager and pocketmine\\\\plugin\\\\PluginManager will always evaluate to true\\.$#"
count: 1
path: src/pocketmine/Server.php
-
message: "#^Instanceof between pocketmine\\\\scheduler\\\\AsyncPool and pocketmine\\\\scheduler\\\\AsyncPool will always evaluate to true\\.$#"
count: 1
path: src/pocketmine/Server.php
-
message: "#^Instanceof between pocketmine\\\\command\\\\CommandReader and pocketmine\\\\command\\\\CommandReader will always evaluate to true\\.$#"
count: 1
path: src/pocketmine/Server.php
-
message: "#^Instanceof between pocketmine\\\\network\\\\Network and pocketmine\\\\network\\\\Network will always evaluate to true\\.$#"
count: 1
path: src/pocketmine/Server.php
-
message: "#^pocketmine\\\\block\\\\[A-Za-z\\d]+\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\block\\\\Block\\.$#"
path: src/pocketmine/block
-
message: "#^pocketmine\\\\block\\\\Block\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\level\\\\Position\\.$#"
count: 1
path: src/pocketmine/block/Block.php
-
message: "#^Call to an undefined method pocketmine\\\\command\\\\CommandSender\\:\\:teleport\\(\\)\\.$#"
count: 1
path: src/pocketmine/command/defaults/TeleportCommand.php
# comment: "not actually possible, but high cost to fix warning"
-
message: "#^Method pocketmine\\\\event\\\\entity\\\\EntityDeathEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\Living but returns pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/EntityDeathEvent.php
-
message: "#^Method pocketmine\\\\event\\\\entity\\\\EntityShootBowEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\Living but returns pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/EntityShootBowEvent.php
-
message: "#^Property pocketmine\\\\event\\\\entity\\\\EntityShootBowEvent\\:\\:\\$projectile \\(pocketmine\\\\entity\\\\projectile\\\\Projectile\\) does not accept pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/EntityShootBowEvent.php
-
message: "#^Method pocketmine\\\\event\\\\entity\\\\ItemDespawnEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\object\\\\ItemEntity but returns pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/ItemDespawnEvent.php
-
message: "#^Method pocketmine\\\\event\\\\entity\\\\ItemSpawnEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\object\\\\ItemEntity but returns pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/ItemSpawnEvent.php
-
message: "#^Method pocketmine\\\\event\\\\entity\\\\ProjectileHitEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\projectile\\\\Projectile but returns pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/ProjectileHitEvent.php
-
message: "#^Method pocketmine\\\\event\\\\entity\\\\ProjectileLaunchEvent\\:\\:getEntity\\(\\) should return pocketmine\\\\entity\\\\projectile\\\\Projectile but returns pocketmine\\\\entity\\\\Entity\\.$#"
count: 1
path: src/pocketmine/event/entity/ProjectileLaunchEvent.php
-
message: "#^pocketmine\\\\inventory\\\\DoubleChestInventory\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\inventory\\\\ChestInventory\\.$#"
count: 1
path: src/pocketmine/inventory/DoubleChestInventory.php
-
message: "#^pocketmine\\\\inventory\\\\EnderChestInventory\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\inventory\\\\ChestInventory\\.$#"
count: 1
path: src/pocketmine/inventory/EnderChestInventory.php
-
message: "#^pocketmine\\\\item\\\\GoldenAppleEnchanted\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\item\\\\GoldenApple\\.$#"
count: 1
path: src/pocketmine/item/GoldenAppleEnchanted.php
-
message: "#^pocketmine\\\\item\\\\WrittenBook\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\item\\\\WritableBook\\.$#"
count: 1
path: src/pocketmine/item/WrittenBook.php
-
message: "#^Variable property access on \\$this\\(pocketmine\\\\level\\\\generator\\\\PopulationTask\\)\\.$#"
count: 4
path: src/pocketmine/level/generator/PopulationTask.php
-
message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\hell\\\\Nether has an unused parameter \\$options\\.$#"
count: 1
path: src/pocketmine/level/generator/hell/Nether.php
-
message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\normal\\\\Normal has an unused parameter \\$options\\.$#"
count: 1
path: src/pocketmine/level/generator/normal/Normal.php
-
message: "#^Variable method call on pocketmine\\\\event\\\\Listener\\.$#"
count: 1
path: src/pocketmine/plugin/MethodEventExecutor.php
-
message: "#^Constructor of class pocketmine\\\\scheduler\\\\TaskScheduler has an unused parameter \\$logger\\.$#"
count: 1
path: src/pocketmine/scheduler/TaskScheduler.php
-
message: "#^Constant pocketmine\\\\COMPOSER_AUTOLOADER_PATH not found\\.$#"
path: src
-
message: "#^Constant pocketmine\\\\DATA not found\\.$#"
path: src
-
message: "#^Constant pocketmine\\\\GIT_COMMIT not found\\.$#"
path: src
-
message: "#^Constant pocketmine\\\\PLUGIN_PATH not found\\.$#"
path: src
-
message: "#^Constant pocketmine\\\\START_TIME not found\\.$#"
path: src
-
message: "#^Constant pocketmine\\\\VERSION not found\\.$#"
path: src

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine;
use PackageVersions\Versions;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\plugin\PluginBase;
use pocketmine\plugin\PluginLoadOrder;
use pocketmine\plugin\PluginManager;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
use raklib\RakLib;
use function base64_encode;
use function date;
use function error_get_last;
@ -88,7 +88,7 @@ class CrashDump{
* having their content changed, version format changing, etc.
* It is not necessary to increase this when adding new fields.
*/
private const FORMAT_VERSION = 2;
private const FORMAT_VERSION = 3;
private const PLUGIN_INVOLVEMENT_NONE = "none";
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
@ -117,10 +117,11 @@ class CrashDump{
mkdir($this->server->getDataPath() . "crashdumps");
}
$this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log";
$this->fp = @fopen($this->path, "wb");
if(!is_resource($this->fp)){
$fp = @fopen($this->path, "wb");
if(!is_resource($fp)){
throw new \RuntimeException("Could not create Crash Dump");
}
$this->fp = $fp;
$this->data["format_version"] = self::FORMAT_VERSION;
$this->data["time"] = $this->time;
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time));
@ -228,7 +229,10 @@ class CrashDump{
if(isset($lastExceptionError)){
$error = $lastExceptionError;
}else{
$error = (array) error_get_last();
$error = error_get_last();
if($error === null){
throw new \RuntimeException("Crash error information missing - did something use exit()?");
}
$error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
$errorConversion = [
E_ERROR => "E_ERROR",
@ -288,9 +292,11 @@ class CrashDump{
if($this->server->getProperty("auto-report.send-code", true) !== false and file_exists($error["fullFile"])){
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
$this->data["code"][$l + 1] = $file[$l];
if($file !== false){
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
$this->data["code"][$l + 1] = $file[$l];
}
}
}
@ -339,18 +345,22 @@ class CrashDump{
$this->data["general"]["is_dev"] = \pocketmine\IS_DEVELOPMENT_BUILD;
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
$this->data["general"]["git"] = \pocketmine\GIT_COMMIT;
$this->data["general"]["raklib"] = RakLib::VERSION;
$this->data["general"]["uname"] = php_uname("a");
$this->data["general"]["php"] = phpversion();
$this->data["general"]["zend"] = zend_version();
$this->data["general"]["php_os"] = PHP_OS;
$this->data["general"]["os"] = Utils::getOS();
$this->data["general"]["composer_libraries"] = Versions::VERSIONS;
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
$this->addLine("uname -a: " . php_uname("a"));
$this->addLine("PHP Version: " . phpversion());
$this->addLine("Zend version: " . zend_version());
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
$this->addLine("Composer libraries: ");
foreach(Versions::VERSIONS as $library => $libraryVersion){
$this->addLine("- $library $libraryVersion");
}
}
/**

View File

@ -27,6 +27,8 @@ use pocketmine\event\server\LowMemoryEvent;
use pocketmine\scheduler\DumpWorkerMemoryTask;
use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Process;
use pocketmine\utils\Utils;
use function arsort;
use function count;
@ -48,6 +50,7 @@ use function is_object;
use function is_resource;
use function is_string;
use function json_encode;
use function mb_strtoupper;
use function min;
use function mkdir;
use function preg_match;
@ -56,7 +59,6 @@ use function round;
use function spl_object_hash;
use function sprintf;
use function strlen;
use function strtoupper;
use function substr;
use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_SLASHES;
@ -125,7 +127,7 @@ class MemoryManager{
if($m <= 0){
$defaultMemory = 0;
}else{
switch(strtoupper($matches[2])){
switch(mb_strtoupper($matches[2])){
case "K":
$defaultMemory = $m / 1024;
break;
@ -225,7 +227,7 @@ class MemoryManager{
if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){
$this->checkTicker = 0;
$memory = Utils::getMemoryUsage(true);
$memory = Process::getAdvancedMemoryUsage();
$trigger = false;
if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){
$trigger = 0;
@ -304,6 +306,7 @@ class MemoryManager{
*/
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){
$hardLimit = ini_get('memory_limit');
if($hardLimit === false) throw new AssumptionFailedError("memory_limit INI directive should always exist");
ini_set('memory_limit', '-1');
gc_disable();
@ -403,8 +406,8 @@ class MemoryManager{
"properties" => []
];
if($reflection->getParentClass()){
$info["parent"] = $reflection->getParentClass()->getName();
if(($parent = $reflection->getParentClass()) !== false){
$info["parent"] = $parent->getName();
}
if(count($reflection->getInterfaceNames()) > 0){

View File

@ -33,6 +33,7 @@ use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\InvalidSkinException;
use pocketmine\entity\object\ItemEntity;
use pocketmine\entity\projectile\Arrow;
use pocketmine\entity\Skin;
@ -112,6 +113,7 @@ use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
use pocketmine\network\mcpe\protocol\BookEditPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\DataPacket;
use pocketmine\network\mcpe\protocol\DisconnectPacket;
use pocketmine\network\mcpe\protocol\InteractPacket;
@ -146,11 +148,17 @@ use pocketmine\network\mcpe\protocol\types\CommandEnum;
use pocketmine\network\mcpe\protocol\types\CommandParameter;
use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor;
use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece;
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\types\SpawnSettings;
use pocketmine\network\mcpe\protocol\types\WindowTypes;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
use pocketmine\network\mcpe\VerifyLoginTask;
@ -168,7 +176,9 @@ use pocketmine\timings\Timings;
use pocketmine\utils\TextFormat;
use pocketmine\utils\UUID;
use function abs;
use function array_key_exists;
use function array_merge;
use function array_values;
use function assert;
use function base64_decode;
use function ceil;
@ -214,6 +224,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
public const SPECTATOR = 3;
public const VIEW = Player::SPECTATOR;
private const MOVES_PER_TICK = 2;
private const MOVE_BACKLOG_SIZE = 100 * self::MOVES_PER_TICK; //100 ticks backlog (5 seconds)
private const RESOURCE_PACK_CHUNK_SIZE = 128 * 1024; //128KB
//TODO: HACK!
//these IDs are used for 1.16 to restore 1.14ish crafting & inventory behaviour; since they don't seem to have any
//effect on the behaviour of inventory transactions I don't currently plan to integrate these into the main system.
private const RESERVED_WINDOW_ID_RANGE_START = ContainerIds::LAST - 10;
private const RESERVED_WINDOW_ID_RANGE_END = ContainerIds::LAST;
public const HARDCODED_CRAFTING_GRID_WINDOW_ID = self::RESERVED_WINDOW_ID_RANGE_START + 1;
public const HARDCODED_INVENTORY_WINDOW_ID = self::RESERVED_WINDOW_ID_RANGE_START + 2;
/**
* Validates the given username.
*/
@ -259,6 +282,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var bool */
public $loggedIn = false;
/** @var bool */
private $seenLoginPacket = false;
/** @var bool */
private $resourcePacksDone = false;
@ -291,6 +316,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var CraftingTransaction|null */
protected $craftingTransaction = null;
/**
* TODO: HACK! This tracks GUIs for inventories that the server considers "always open" so that the client can't
* open them twice. (1.16 hack)
* @var true[]
* @phpstan-var array<int, true>
* @internal
*/
public $openHardcodedWindows = [];
/** @var int */
protected $messageCounter = 2;
/** @var bool */
@ -324,10 +358,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var bool[] map: raw UUID (string) => bool */
protected $hiddenPlayers = [];
/** @var float */
protected $moveRateLimit = 10 * self::MOVES_PER_TICK;
/** @var float|null */
protected $lastMovementProcess = null;
/** @var Vector3|null */
protected $newPosition;
/** @var bool */
protected $isTeleporting = false;
protected $forceMoveSync = null;
/** @var int */
protected $inAirTicks = 0;
/** @var float */
@ -517,7 +554,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function spawnTo(Player $player) : void{
if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevel() === $this->level and $player->canSee($this) and !$this->isSpectator()){
if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevelNonNull() === $this->level and $player->canSee($this) and !$this->isSpectator()){
parent::spawnTo($player);
}
}
@ -729,7 +766,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
$data->aliases = new CommandEnum();
$data->aliases->enumName = ucfirst($command->getName()) . "Aliases";
$data->aliases->enumValues = $aliases;
$data->aliases->enumValues = array_values($aliases);
}
$pk->commandData[$command->getName()] = $data;
@ -860,8 +897,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->lastPingMeasure = $pingMS;
}
/**
* @deprecated
*/
public function getNextPosition() : Position{
return $this->newPosition !== null ? Position::fromObject($this->newPosition, $this->level) : $this->getPosition();
return $this->getPosition();
}
public function getInAirTicks() : int{
@ -902,8 +942,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* Resets the player's cooldown time for the given item back to the maximum.
*/
public function resetItemCooldown(Item $item) : void{
$ticks = $item->getCooldownTicks();
public function resetItemCooldown(Item $item, ?int $ticks = null) : void{
$ticks = $ticks ?? $item->getCooldownTicks();
if($ticks > 0){
$this->usedItemsCooldown[$item->getId()] = $this->server->getTick() + $ticks;
}
@ -1207,15 +1247,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!($pos instanceof Position)){
$level = $this->level;
}else{
$level = $pos->getLevel();
$level = $pos->getLevelNonNull();
}
$this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level);
$pk = new SetSpawnPositionPacket();
$pk->x = $this->spawnPosition->getFloorX();
$pk->y = $this->spawnPosition->getFloorY();
$pk->z = $this->spawnPosition->getFloorZ();
$pk->x = $pk->x2 = $this->spawnPosition->getFloorX();
$pk->y = $pk->y2 = $this->spawnPosition->getFloorY();
$pk->z = $pk->z2 = $this->spawnPosition->getFloorZ();
$pk->dimension = DimensionIds::OVERWORLD;
$pk->spawnType = SetSpawnPositionPacket::TYPE_PLAYER_SPAWN;
$pk->spawnForced = false;
$this->dataPacket($pk);
}
@ -1329,12 +1370,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* TODO: remove this when Spectator Mode gets added properly to MCPE
*/
public static function getClientFriendlyGamemode(int $gamemode) : int{
$gamemode &= 0x03;
if($gamemode === Player::SPECTATOR){
return Player::CREATIVE;
}
return $gamemode;
static $map = [
self::SURVIVAL => GameMode::SURVIVAL,
self::CREATIVE => GameMode::CREATIVE,
self::ADVENTURE => GameMode::ADVENTURE,
self::SPECTATOR => GameMode::CREATIVE
];
return $map[$gamemode & 0x3];
}
/**
@ -1362,9 +1404,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($this->isSpectator()){
$this->setFlying(true);
$this->keepMovement = true;
$this->onGround = false;
//TODO: HACK! this syncs the onground flag with the client so that flying works properly
//this is a yucky hack but we don't have any other options :(
$this->sendPosition($this, null, null, MovePlayerPacket::MODE_TELEPORT);
$this->despawnFromAll();
}else{
$this->keepMovement = $this->allowMovementCheats;
$this->checkGroundState(0, 0, 0, 0, 0, 0);
if($this->isSurvival()){
$this->setFlying(false);
}
@ -1485,11 +1534,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
if($this->isSpectator()){
$this->onGround = false;
}else{
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
}
public function canBeMovedByCurrents() : bool{
@ -1511,23 +1564,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
/**
* @return void
*/
protected function processMovement(int $tickDiff){
if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->isSleeping()){
protected function handleMovement(Vector3 $newPos) : void{
$this->moveRateLimit--;
if($this->moveRateLimit < 0){
return;
}
assert($this->x !== null and $this->y !== null and $this->z !== null);
assert($this->newPosition->x !== null and $this->newPosition->y !== null and $this->newPosition->z !== null);
$newPos = $this->newPosition;
$distanceSquared = $newPos->distanceSquared($this);
$oldPos = $this->asLocation();
$distanceSquared = $newPos->distanceSquared($oldPos);
$revert = false;
if(($distanceSquared / ($tickDiff ** 2)) > 100){
if($distanceSquared > 100){
//TODO: this is probably too big if we process every movement
/* !!! BEWARE YE WHO ENTER HERE !!!
*
* This is NOT an anti-cheat check. It is a safety check.
@ -1539,7 +1588,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* asking for help if you suffer the consequences of messing with this.
*/
$this->server->getLogger()->debug($this->getName() . " moved too fast, reverting movement");
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition);
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos);
$revert = true;
}elseif(!$this->level->isInLoadedTerrain($newPos) or !$this->level->isChunkGenerated($newPos->getFloorX() >> 4, $newPos->getFloorZ() >> 4)){
$revert = true;
@ -1553,7 +1602,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->move($dx, $dy, $dz);
$diff = $this->distanceSquared($newPos) / $tickDiff ** 2;
$diff = $this->distanceSquared($newPos);
if($this->isSurvival() and $diff > 0.0625){
$ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ));
@ -1564,7 +1613,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$ev->isCancelled()){
$revert = true;
$this->server->getLogger()->debug($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()]));
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition);
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos);
}
}
@ -1573,13 +1622,28 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
if($revert){
$this->revertMovement($oldPos);
}
}
/**
* Fires movement events and synchronizes player movement, every tick.
*/
protected function processMostRecentMovements() : void{
$now = microtime(true);
$multiplier = $this->lastMovementProcess !== null ? ($now - $this->lastMovementProcess) * 20 : 1;
$exceededRateLimit = $this->moveRateLimit < 0;
$this->moveRateLimit = min(self::MOVE_BACKLOG_SIZE, max(0, $this->moveRateLimit) + self::MOVES_PER_TICK * $multiplier);
$this->lastMovementProcess = $now;
$from = new Location($this->lastX, $this->lastY, $this->lastZ, $this->lastYaw, $this->lastPitch, $this->level);
$to = $this->getLocation();
$delta = (($this->lastX - $to->x) ** 2) + (($this->lastY - $to->y) ** 2) + (($this->lastZ - $to->z) ** 2);
$deltaAngle = abs($this->lastYaw - $to->yaw) + abs($this->lastPitch - $to->pitch);
if(!$revert and ($delta > 0.0001 or $deltaAngle > 1.0)){
if($delta > 0.0001 or $deltaAngle > 1.0){
$this->lastX = $to->x;
$this->lastY = $to->y;
$this->lastZ = $to->z;
@ -1591,41 +1655,47 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$ev->call();
if(!($revert = $ev->isCancelled())){ //Yes, this is intended
if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination
$this->teleport($ev->getTo());
}else{
$this->broadcastMovement();
$distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
//TODO: check swimming (adds 0.015 exhaustion in MCPE)
if($this->isSprinting()){
$this->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING);
}else{
$this->exhaust(0.01 * $distance, PlayerExhaustEvent::CAUSE_WALKING);
}
}
if($ev->isCancelled()){
$this->revertMovement($from);
return;
}
}
if($revert){
if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination
$this->teleport($ev->getTo());
return;
}
$this->lastX = $from->x;
$this->lastY = $from->y;
$this->lastZ = $from->z;
$this->broadcastMovement();
$this->lastYaw = $from->yaw;
$this->lastPitch = $from->pitch;
$distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
//TODO: check swimming (adds 0.015 exhaustion in MCPE)
if($this->isSprinting()){
$this->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING);
}else{
$this->exhaust(0.01 * $distance, PlayerExhaustEvent::CAUSE_WALKING);
}
$this->setPosition($from);
$this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET);
}else{
if($distanceSquared != 0 and $this->nextChunkOrderRun > 20){
if($this->nextChunkOrderRun > 20){
$this->nextChunkOrderRun = 20;
}
}
$this->newPosition = null;
if($exceededRateLimit){ //client and server positions will be out of sync if this happens
$this->server->getLogger()->debug("Player " . $this->getName() . " exceeded movement rate limit, forcing to last accepted position");
$this->sendPosition($this, $this->yaw, $this->pitch, MovePlayerPacket::MODE_RESET);
}
}
protected function revertMovement(Location $from) : void{
$this->lastX = $from->x;
$this->lastY = $from->y;
$this->lastZ = $from->z;
$this->lastYaw = $from->yaw;
$this->lastPitch = $from->pitch;
$this->setPosition($from);
$this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET);
}
public function fall(float $fallDistance) : void{
@ -1697,7 +1767,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->timings->startTiming();
if($this->spawned){
$this->processMovement($tickDiff);
$this->processMostRecentMovements();
$this->motion->x = $this->motion->y = $this->motion->z = 0; //TODO: HACK! (Fixes player knockback being messed up)
if($this->onGround){
$this->inAirTicks = 0;
@ -1814,9 +1884,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handleLogin(LoginPacket $packet) : bool{
if($this->loggedIn){
if($this->seenLoginPacket){
return false;
}
$this->seenLoginPacket = true;
if($packet->protocol !== ProtocolInfo::CURRENT_PROTOCOL){
if($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL){
@ -1856,26 +1927,65 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$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"]);
$animations[] = new SkinAnimation(
new SkinImage(
$animation["ImageHeight"],
$animation["ImageWidth"],
base64_decode($animation["Image"], true)),
$animation["Type"],
$animation["Frames"]
);
}
$personaPieces = [];
foreach($packet->clientData["PersonaPieces"] as $piece){
$personaPieces[] = new PersonaSkinPiece(
$piece["PieceId"],
$piece["PieceType"],
$piece["PackId"],
$piece["IsDefault"],
$piece["ProductId"]
);
}
$pieceTintColors = [];
foreach($packet->clientData["PieceTintColors"] as $tintColor){
$pieceTintColors[] = new PersonaPieceTintColor($tintColor["PieceType"], $tintColor["Colors"]);
}
$skinData = new SkinData(
$packet->clientData["SkinId"],
base64_decode($packet->clientData["SkinResourcePatch"] ?? "", true),
new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"], true)),
new SkinImage(
$packet->clientData["SkinImageHeight"],
$packet->clientData["SkinImageWidth"],
base64_decode($packet->clientData["SkinData"], true)
),
$animations,
new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "", true)),
new SkinImage(
$packet->clientData["CapeImageHeight"],
$packet->clientData["CapeImageWidth"],
base64_decode($packet->clientData["CapeData"] ?? "", true)
),
base64_decode($packet->clientData["SkinGeometryData"] ?? "", true),
base64_decode($packet->clientData["SkinAnimationData"] ?? "", true),
$packet->clientData["PremiumSkin"] ?? false,
$packet->clientData["PersonaSkin"] ?? false,
$packet->clientData["CapeOnClassicSkin"] ?? false,
$packet->clientData["CapeId"] ?? ""
$packet->clientData["CapeId"] ?? "",
null,
$packet->clientData["ArmSize"] ?? SkinData::ARM_SIZE_WIDE,
$packet->clientData["SkinColor"] ?? "",
$personaPieces,
$pieceTintColors,
true
);
$skin = SkinAdapterSingleton::get()->fromSkinData($skinData);
if(!$skin->isValid()){
try{
$skin = SkinAdapterSingleton::get()->fromSkinData($skinData);
$skin->validate();
}catch(InvalidSkinException $e){
$this->server->getLogger()->debug("$this->username: Invalid skin: " . $e->getMessage());
$this->close("", "disconnectionScreen.invalidSkin");
return true;
@ -2047,7 +2157,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk = new ResourcePackDataInfoPacket();
$pk->packId = $pack->getPackId();
$pk->maxChunkSize = 1048576; //1MB
$pk->maxChunkSize = self::RESOURCE_PACK_CHUNK_SIZE;
$pk->chunkCount = (int) ceil($pack->getPackSize() / $pk->maxChunkSize);
$pk->compressedPackSize = $pack->getPackSize();
$pk->sha256 = $pack->getSha256();
@ -2114,7 +2224,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->pitch = $this->pitch;
$pk->yaw = $this->yaw;
$pk->seed = -1;
$pk->dimension = DimensionIds::OVERWORLD; //TODO: implement this properly
$pk->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly
$pk->worldGamemode = Player::getClientFriendlyGamemode($this->server->getGamemode());
$pk->difficulty = $this->level->getDifficulty();
$pk->spawnX = $spawnPosition->getFloorX();
@ -2213,10 +2323,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handleMovePlayer(MovePlayerPacket $packet) : bool{
$newPos = $packet->position->subtract(0, $this->baseOffset, 0);
$newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0);
if($this->isTeleporting and $newPos->distanceSquared($this) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET);
if($this->forceMoveSync !== null and $newPos->distanceSquared($this->forceMoveSync) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3());
//Still getting movements from before teleport, ignore them
}elseif((!$this->isAlive() or !$this->spawned) and $newPos->distanceSquared($this) > 0.01){
@ -2224,9 +2333,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->server->getLogger()->debug("Reverted movement of " . $this->getName() . " due to not alive or not spawned, received " . $newPos . ", locked at " . $this->asVector3());
}else{
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
if($this->isTeleporting){
$this->isTeleporting = false;
}
$this->forceMoveSync = null;
$packet->yaw = fmod($packet->yaw, 360);
$packet->pitch = fmod($packet->pitch, 360);
@ -2236,7 +2343,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
$this->setRotation($packet->yaw, $packet->pitch);
$this->newPosition = $newPos;
$this->handleMovement($newPos);
}
return true;
@ -2244,7 +2351,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
//TODO: add events so plugins can change this
$this->getLevel()->broadcastPacketToViewers($this, $packet);
$this->getLevelNonNull()->broadcastPacketToViewers($this, $packet);
return true;
}
@ -2282,14 +2389,22 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
if($this->isSpectator()){
$this->sendAllInventories();
return true;
}
/** @var InventoryAction[] $actions */
$actions = [];
$isCraftingPart = false;
$isFinalCraftingPart = false;
foreach($packet->actions as $networkInventoryAction){
if(
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
)
){
$isCraftingPart = true;
if($networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT){
$isFinalCraftingPart = true;
}
}
try{
$action = $networkInventoryAction->createInventoryAction($this);
if($action !== null){
@ -2302,7 +2417,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
if($packet->isCraftingPart){
if($isCraftingPart){
if($this->craftingTransaction === null){
$this->craftingTransaction = new CraftingTransaction($this, $actions);
}else{
@ -2311,7 +2426,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
if($packet->isFinalCraftingPart){
if($isFinalCraftingPart){
//we get the actions for this in several packets, so we need to wait until we have all the pieces before
//trying to execute it
@ -2380,7 +2495,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->setUsingItem(false);
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()){
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){
}elseif($this->isCreative()){
$item = $this->inventory->getItemInHand();
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){
@ -2486,7 +2601,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
if($this->hasItemCooldown($item)){
if($this->hasItemCooldown($item) or $this->isSpectator()){
$ev->setCancelled();
}
@ -2537,7 +2652,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$heldItem = $this->inventory->getItemInHand();
$oldItem = clone $heldItem;
if(!$this->canInteract($target, 8)){
if(!$this->canInteract($target, 8) or $this->isSpectator()){
$cancelled = true;
}elseif($target instanceof Player){
if(!$this->server->getConfigBool("pvp")){
@ -2689,8 +2804,23 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case InteractPacket::ACTION_LEAVE_VEHICLE:
case InteractPacket::ACTION_MOUSEOVER:
break; //TODO: handle these
case InteractPacket::ACTION_OPEN_INVENTORY:
if($target === $this && !array_key_exists($windowId = self::HARDCODED_INVENTORY_WINDOW_ID, $this->openHardcodedWindows)){
//TODO: HACK! this restores 1.14ish behaviour, but this should be able to be listened to and
//controlled by plugins. However, the player is always a subscriber to their own inventory so it
//doesn't integrate well with the regular container system right now.
$this->openHardcodedWindows[$windowId] = true;
$pk = new ContainerOpenPacket();
$pk->windowId = $windowId;
$pk->type = WindowTypes::INVENTORY;
$pk->x = $pk->y = $pk->z = 0;
$pk->entityUniqueId = $this->getId();
$this->sendDataPacket($pk);
break;
}
return false;
default:
$this->server->getLogger()->debug("Unhandled/unknown interaction type " . $packet->action . "received from " . $this->getName());
$this->server->getLogger()->debug("Unhandled/unknown interaction type " . $packet->action . " received from " . $this->getName());
return false;
}
@ -2706,7 +2836,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$item = $block->getPickedItem();
if($packet->addUserData){
$tile = $this->getLevel()->getTile($block);
$tile = $this->getLevelNonNull()->getTile($block);
if($tile instanceof Tile){
$nbt = $tile->getCleanedNBT();
if($nbt instanceof CompoundTag){
@ -2902,18 +3032,23 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handleContainerClose(ContainerClosePacket $packet) : bool{
if(!$this->spawned or $packet->windowId === 0){
if(!$this->spawned){
return true;
}
$this->doCloseInventory();
if(array_key_exists($packet->windowId, $this->openHardcodedWindows)){
unset($this->openHardcodedWindows[$packet->windowId]);
$pk = new ContainerClosePacket();
$pk->windowId = $packet->windowId;
$this->sendDataPacket($pk);
return true;
}
if(isset($this->windowIndex[$packet->windowId])){
(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call();
$this->removeWindow($this->windowIndex[$packet->windowId]);
return true;
}elseif($packet->windowId === 255){
//Closed a fake window
//removeWindow handles sending the appropriate
return true;
}
@ -3035,8 +3170,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk = new ResourcePackChunkDataPacket();
$pk->packId = $pack->getPackId();
$pk->chunkIndex = $packet->chunkIndex;
$pk->data = $pack->getPackChunk(1048576 * $packet->chunkIndex, 1048576);
$pk->progress = (1048576 * $packet->chunkIndex);
$pk->data = $pack->getPackChunk(self::RESOURCE_PACK_CHUNK_SIZE * $packet->chunkIndex, self::RESOURCE_PACK_CHUNK_SIZE);
$pk->progress = (self::RESOURCE_PACK_CHUNK_SIZE * $packet->chunkIndex);
$this->dataPacket($pk);
return true;
}
@ -3057,14 +3192,26 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_ADD_PAGE:
if(!$newBook->pageExists($packet->pageNumber)){
//this may only come before a page which already exists
//TODO: the client can send insert-before actions on trailing client-side pages which cause odd behaviour on the server
return false;
}
$newBook->insertPage($packet->pageNumber, $packet->text);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_DELETE_PAGE:
if(!$newBook->pageExists($packet->pageNumber)){
return false;
}
$newBook->deletePage($packet->pageNumber);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_SWAP_PAGES:
if(!$newBook->pageExists($packet->pageNumber) or !$newBook->pageExists($packet->secondaryPageNumber)){
//the client will create pages on its own without telling us until it tries to switch them
$newBook->addPage(max($packet->pageNumber, $packet->secondaryPageNumber));
}
$newBook->swapPages($packet->pageNumber, $packet->secondaryPageNumber);
$modifiedPages = [$packet->pageNumber, $packet->secondaryPageNumber];
break;
@ -3226,7 +3373,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
/**
* Adds a title text to the user's screen, with an optional subtitle.
* @deprecated
* @see Player::sendTitle()
*
* @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
* @param int $stay Duration in ticks to stay on screen for
@ -3235,28 +3383,55 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @return void
*/
public function addTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1){
$this->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
}
/**
* Adds a title text to the user's screen, with an optional subtitle.
*
* @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
* @param int $stay Duration in ticks to stay on screen for
* @param int $fadeOut Duration in ticks for fade-out.
*/
public function sendTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1) : void{
$this->setTitleDuration($fadeIn, $stay, $fadeOut);
if($subtitle !== ""){
$this->addSubTitle($subtitle);
$this->sendSubTitle($subtitle);
}
$this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE);
}
/**
* Sets the subtitle message, without sending a title.
* @deprecated
* @see Player::sendSubTitle()
*
* @return void
*/
public function addSubTitle(string $subtitle){
$this->sendSubTitle($subtitle);
}
/**
* Sets the subtitle message, without sending a title.
*/
public function sendSubTitle(string $subtitle) : void{
$this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE);
}
/**
* Adds small text to the user's screen.
* @deprecated
* @see Player::sendActionBarMessage()
*
* @return void
*/
public function addActionBarMessage(string $message){
$this->sendActionBarMessage($message);
}
/**
* Adds small text to the user's screen.
*/
public function sendActionBarMessage(string $message) : void{
$this->sendTitleText($message, SetTitlePacket::TYPE_SET_ACTIONBAR_MESSAGE);
}
@ -3399,14 +3574,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* Sends a Form to the player, or queue to send it if a form is already open.
*/
public function sendForm(Form $form) : void{
$formData = json_encode($form);
if($formData === false){
throw new \InvalidArgumentException("Failed to encode form JSON: " . json_last_error_msg());
}
$id = $this->formIdCounter++;
$pk = new ModalFormRequestPacket();
$pk->formId = $id;
$pk->formData = json_encode($form);
if($pk->formData === false){
throw new \InvalidArgumentException("Failed to encode form JSON: " . json_last_error_msg());
}
if($this->dataPacket($pk)){
$pk->formData = $formData;
if($this->dataPacket($pk) !== false){
$this->forms[$id] = $form;
}
}
@ -3555,7 +3731,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
if($this->hasValidSpawnPosition()){
$this->namedtag->setString("SpawnLevel", $this->spawnPosition->getLevel()->getFolderName());
$this->namedtag->setString("SpawnLevel", $this->spawnPosition->getLevelNonNull()->getFolderName());
$this->namedtag->setInt("SpawnX", $this->spawnPosition->getFloorX());
$this->namedtag->setInt("SpawnY", $this->spawnPosition->getFloorY());
$this->namedtag->setInt("SpawnZ", $this->spawnPosition->getFloorZ());
@ -3599,7 +3775,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//main inventory and drops the rest on the ground.
$this->doCloseInventory();
$ev = new PlayerDeathEvent($this, $this->getDrops());
$ev = new PlayerDeathEvent($this, $this->getDrops(), null, $this->getXpDropAmount());
$ev->call();
if(!$ev->getKeepInventory()){
@ -3616,8 +3792,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
//TODO: allow this number to be manipulated during PlayerDeathEvent
$this->level->dropExperience($this, $this->getXpDropAmount());
$this->level->dropExperience($this, $ev->getXpDropAmount());
$this->setXpAndProgress(0, 0);
if($ev->getDeathMessage() != ""){
@ -3639,7 +3814,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$ev = new PlayerRespawnEvent($this, $this->getSpawn());
$ev->call();
$realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getLevel());
$realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getLevelNonNull());
$this->teleport($realSpawn);
$this->setSprinting(false);
@ -3720,14 +3895,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->headYaw = $yaw;
$pk->yaw = $yaw;
$pk->mode = $mode;
$pk->onGround = $this->onGround;
if($targets !== null){
if(in_array($this, $targets, true)){
$this->forceMoveSync = $pos->asVector3();
}
$this->server->broadcastPacket($targets, $pk);
}else{
$this->forceMoveSync = $pos->asVector3();
$this->dataPacket($pk);
}
$this->newPosition = null;
}
/**
@ -3745,11 +3923,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->resetFallDistance();
$this->nextChunkOrderRun = 0;
$this->newPosition = null;
$this->stopSleep();
$this->isTeleporting = true;
//TODO: workaround for player last pos not getting updated
//Entity::updateMovement() normally handles this, but it's overridden with an empty function in Player
$this->resetLastMovements();
@ -3843,7 +4018,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($forceId === null){
$cnt = $this->windowCnt;
do{
$cnt = max(ContainerIds::FIRST, ($cnt + 1) % ContainerIds::LAST);
$cnt = max(ContainerIds::FIRST, ($cnt + 1) % self::RESERVED_WINDOW_ID_RANGE_START);
if($cnt === $this->windowCnt){ //wraparound, no free slots
throw new \InvalidStateException("No free window IDs found");
}
@ -3851,7 +4026,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->windowCnt = $cnt;
}else{
$cnt = $forceId;
if(isset($this->windowIndex[$cnt])){
if(isset($this->windowIndex[$cnt]) or ($cnt >= self::RESERVED_WINDOW_ID_RANGE_START && $cnt <= self::RESERVED_WINDOW_ID_RANGE_END)){
throw new \InvalidArgumentException("Requested force ID $forceId already in use");
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine {
use pocketmine\utils\Git;
use pocketmine\utils\MainLogger;
use pocketmine\utils\Process;
use pocketmine\utils\ServerKiller;
use pocketmine\utils\Terminal;
use pocketmine\utils\Timezone;
@ -34,7 +35,7 @@ namespace pocketmine {
require_once __DIR__ . '/VersionInfo.php';
const MIN_PHP_VERSION = "7.2.0";
const MIN_PHP_VERSION = "7.3.0";
/**
* @param string $message
@ -177,15 +178,14 @@ namespace pocketmine {
}else{
$bootstrap = dirname(__FILE__, 3) . '/vendor/autoload.php';
}
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{
if($bootstrap === false or !is_file($bootstrap)){
critical_error("Composer autoloader not found at " . $bootstrap);
critical_error("Please install/update Composer dependencies or use provided builds.");
exit(1);
}
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
set_error_handler([Utils::class, 'errorExceptionHandler']);
@ -280,7 +280,7 @@ namespace pocketmine {
if(ThreadManager::getInstance()->stopAll() > 0){
$logger->debug("Some threads could not be stopped, performing a force-kill");
Utils::kill(getmypid());
Process::kill(getmypid());
}
}while(false);
@ -289,6 +289,14 @@ namespace pocketmine {
echo Terminal::$FORMAT_RESET . PHP_EOL;
if(!flock(\pocketmine\LOCK_FILE, LOCK_UN)){
critical_error("Failed to release the server.lock file.");
}
if(!fclose(\pocketmine\LOCK_FILE)){
critical_error("Could not close server.lock resource.");
}
exit($exitCode);
}

View File

@ -102,6 +102,7 @@ use pocketmine\updater\AutoUpdater;
use pocketmine\utils\Config;
use pocketmine\utils\Internet;
use pocketmine\utils\MainLogger;
use pocketmine\utils\Process;
use pocketmine\utils\Terminal;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
@ -692,29 +693,32 @@ class Server{
return $result;
}
private function getPlayerDataPath(string $username) : string{
return $this->getDataPath() . '/players/' . strtolower($username) . '.dat';
}
/**
* Returns whether the server has stored any saved data for this player.
*/
public function hasOfflinePlayerData(string $name) : bool{
$name = strtolower($name);
return file_exists($this->getDataPath() . "players/$name.dat");
return file_exists($this->getPlayerDataPath($name));
}
public function getOfflinePlayerData(string $name) : CompoundTag{
$name = strtolower($name);
$path = $this->getDataPath() . "players/";
$path = $this->getPlayerDataPath($name);
if($this->shouldSavePlayerData()){
if(file_exists($path . "$name.dat")){
if(file_exists($path)){
try{
$nbt = new BigEndianNBTStream();
$compound = $nbt->readCompressed(file_get_contents($path . "$name.dat"));
$compound = $nbt->readCompressed(file_get_contents($path));
if(!($compound instanceof CompoundTag)){
throw new \RuntimeException("Invalid data found in \"$name.dat\", expected " . CompoundTag::class . ", got " . (is_object($compound) ? get_class($compound) : gettype($compound)));
}
return $compound;
}catch(\Throwable $e){ //zlib decode error / corrupt data
rename($path . "$name.dat", $path . "$name.dat.bak");
rename($path, $path . '.bak');
$this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerCorrupted", [$name]));
}
}else{
@ -775,7 +779,7 @@ class Server{
if(!$ev->isCancelled()){
$nbt = new BigEndianNBTStream();
try{
file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData()));
file_put_contents($this->getPlayerDataPath($name), $nbt->writeCompressed($ev->getSaveData()));
}catch(\Throwable $e){
$this->logger->critical($this->getLanguage()->translateString("pocketmine.data.saveError", [$name, $e->getMessage()]));
$this->logger->logException($e);
@ -1396,7 +1400,7 @@ class Server{
Network::$BATCH_THRESHOLD = -1;
}
$this->networkCompressionLevel = $this->getProperty("network.compression-level", 7);
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 7);
if($this->networkCompressionLevel < 1 or $this->networkCompressionLevel > 9){
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 7");
$this->networkCompressionLevel = 7;
@ -1665,7 +1669,7 @@ class Server{
}
foreach($recipients as $recipient){
$recipient->addTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
$recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
}
return count($recipients);
@ -1935,7 +1939,7 @@ class Server{
}catch(\Throwable $e){
$this->logger->logException($e);
$this->logger->emergency("Crashed while crashing, killing process");
@Utils::kill(getmypid());
@Process::kill(getmypid());
}
}
@ -2091,17 +2095,24 @@ class Server{
if($report){
$url = ((bool) $this->getProperty("auto-report.use-https", true) ? "https" : "http") . "://" . $this->getProperty("auto-report.host", "crash.pmmp.io") . "/submit/api";
$postUrlError = "Unknown error";
$reply = Internet::postURL($url, [
"report" => "yes",
"name" => $this->getName() . " " . $this->getPocketMineVersion(),
"email" => "crash@pocketmine.net",
"reportPaste" => base64_encode($dump->getEncodedData())
]);
], 10, [], $postUrlError);
if($reply !== false and ($data = json_decode($reply)) !== null and isset($data->crashId) and isset($data->crashUrl)){
$reportId = $data->crashId;
$reportUrl = $data->crashUrl;
$this->logger->emergency($this->getLanguage()->translateString("pocketmine.crash.archive", [$reportUrl, $reportId]));
if($reply !== false and ($data = json_decode($reply)) !== null){
if(isset($data->crashId) and isset($data->crashUrl)){
$reportId = $data->crashId;
$reportUrl = $data->crashUrl;
$this->logger->emergency($this->getLanguage()->translateString("pocketmine.crash.archive", [$reportUrl, $reportId]));
}elseif(isset($data->error)){
$this->logger->emergency("Automatic crash report submission failed: $data->error");
}
}else{
$this->logger->emergency("Failed to communicate with crash archive: $postUrlError");
}
}
}
@ -2121,7 +2132,7 @@ class Server{
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
sleep($spacing);
}
@Utils::kill(getmypid());
@Process::kill(getmypid());
exit(1);
}
@ -2322,10 +2333,10 @@ class Server{
private function titleTick() : void{
Timings::$titleTickTimer->startTiming();
$d = Utils::getRealMemoryUsage();
$d = Process::getRealMemoryUsage();
$u = Utils::getMemoryUsage(true);
$usage = sprintf("%g/%g/%g/%g MB @ %d threads", round(($u[0] / 1024) / 1024, 2), round(($d[0] / 1024) / 1024, 2), round(($u[1] / 1024) / 1024, 2), round(($u[2] / 1024) / 1024, 2), Utils::getThreadCount());
$u = Process::getAdvancedMemoryUsage();
$usage = sprintf("%g/%g/%g/%g MB @ %d threads", round(($u[0] / 1024) / 1024, 2), round(($d[0] / 1024) / 1024, 2), round(($u[1] / 1024) / 1024, 2), round(($u[2] / 1024) / 1024, 2), Process::getThreadCount());
echo "\x1b]0;" . $this->getName() . " " .
$this->getPocketMineVersion() .

View File

@ -76,11 +76,9 @@ abstract class Thread extends \Thread{
}
/**
* @param int|null $options TODO: pthreads bug
*
* @return bool
*/
public function start(?int $options = PTHREADS_INHERIT_ALL){
public function start(int $options = PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this);
if($this->getClassLoader() === null){

View File

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

View File

@ -76,11 +76,9 @@ abstract class Worker extends \Worker{
}
/**
* @param int|null $options TODO: pthreads bug
*
* @return bool
*/
public function start(?int $options = PTHREADS_INHERIT_ALL){
public function start(int $options = PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this);
if($this->getClassLoader() === null){

View File

@ -110,6 +110,6 @@ class Anvil extends Fallable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$direction = ($player !== null ? $player->getDirection() : 0) & 0x03;
$this->meta = $this->getVariant() | $direction;
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
}
}

View File

@ -91,7 +91,7 @@ abstract class BaseRail extends Flowable{
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
if(!$blockReplace->getSide(Vector3::SIDE_DOWN)->isTransparent() and $this->getLevel()->setBlock($blockReplace, $this, true, true)){
if(!$blockReplace->getSide(Vector3::SIDE_DOWN)->isTransparent() and $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true)){
$this->tryReconnect();
return true;
}
@ -279,7 +279,7 @@ abstract class BaseRail extends Flowable{
isset(self::ASCENDING_SIDES[$this->meta & 0x07]) and
$this->getSide(self::ASCENDING_SIDES[$this->meta & 0x07])->isTransparent()
)){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -83,7 +83,7 @@ class Bed extends Transparent{
$this->meta &= ~self::BITFLAG_OCCUPIED;
}
$this->getLevel()->setBlock($this, $this, false, false);
$this->getLevelNonNull()->setBlock($this, $this, false, false);
if(($other = $this->getOtherHalf()) !== null and $other->isOccupied() !== $occupied){
$other->setOccupied($occupied);
@ -137,7 +137,7 @@ class Bed extends Transparent{
return true;
}
$time = $this->getLevel()->getTime() % Level::TIME_FULL;
$time = $this->getLevelNonNull()->getTimeOfDay();
$isNight = ($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE);
@ -168,11 +168,11 @@ class Bed extends Transparent{
$meta = (($player instanceof Player ? $player->getDirection() : 0) - 1) & 0x03;
$next = $this->getSide(self::getOtherHalfSide($meta));
if($next->canBeReplaced() and !$next->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->id, $meta), true, true);
$this->getLevel()->setBlock($next, BlockFactory::get($this->id, $meta | self::BITFLAG_HEAD), true, true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->id, $meta), true, true);
$this->getLevelNonNull()->setBlock($next, BlockFactory::get($this->id, $meta | self::BITFLAG_HEAD), true, true);
Tile::createTile(Tile::BED, $this->getLevel(), TileBed::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::BED, $this->getLevel(), TileBed::createNBT($next, $face, $item, $player));
Tile::createTile(Tile::BED, $this->getLevelNonNull(), TileBed::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::BED, $this->getLevelNonNull(), TileBed::createNBT($next, $face, $item, $player));
return true;
}
@ -194,7 +194,7 @@ class Bed extends Transparent{
}
private function getItem() : Item{
$tile = $this->getLevel()->getTile($this);
$tile = $this->getLevelNonNull()->getTile($this);
if($tile instanceof TileBed){
return ItemFactory::get($this->getItemId(), $tile->getColor());
}

View File

@ -36,7 +36,7 @@ use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\metadata\Metadatable;
use pocketmine\metadata\MetadataValue;
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\Player;
use pocketmine\plugin\Plugin;
use function array_merge;
@ -154,7 +154,7 @@ class Block extends Position implements BlockIds, Metadatable{
* Places the Block, using block space and block target, and side. Returns if the block has been placed.
*/
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
return $this->getLevel()->setBlock($this, $this, true, true);
return $this->getLevelNonNull()->setBlock($this, $this, true, true);
}
/**
@ -204,7 +204,7 @@ class Block extends Position implements BlockIds, Metadatable{
* Do the actions needed so the block is broken with the Item
*/
public function onBreak(Item $item, Player $player = null) : bool{
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true, true);
return $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true, true);
}
/**
@ -479,7 +479,7 @@ class Block extends Position implements BlockIds, Metadatable{
*/
public function getSide(int $side, int $step = 1){
if($this->isValid()){
return $this->getLevel()->getBlock(Vector3::getSide($side, $step));
return $this->getLevelNonNull()->getBlock(Vector3::getSide($side, $step));
}
return BlockFactory::get(Block::AIR, 0, Position::fromObject(Vector3::getSide($side, $step)));

View File

@ -25,7 +25,7 @@ namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\level\Position;
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use function min;
/**

View File

@ -55,7 +55,7 @@ class BoneBlock extends Solid{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face);
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
}
public function getVariantBitmask() : int{

View File

@ -68,18 +68,18 @@ class BurningFurnace extends Solid{
3 => 3
];
$this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0];
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::FURNACE, $this->getLevelNonNull(), TileFurnace::createNBT($this, $face, $item, $player));
return true;
}
public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){
$furnace = $this->getLevel()->getTile($this);
$furnace = $this->getLevelNonNull()->getTile($this);
if(!($furnace instanceof TileFurnace)){
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this));
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevelNonNull(), TileFurnace::createNBT($this));
if(!($furnace instanceof TileFurnace)){
return true;
}

View File

@ -72,12 +72,12 @@ class Cactus extends Transparent{
public function onNearbyBlockChange() : void{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() !== self::SAND and $down->getId() !== self::CACTUS){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}else{
for($side = 2; $side <= 5; ++$side){
$b = $this->getSide($side);
if($b->isSolid()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
break;
}
}
@ -92,23 +92,23 @@ class Cactus extends Transparent{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::CACTUS){
if($this->meta === 0x0f){
for($y = 1; $y < 3; ++$y){
$b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z);
$b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z);
if($b->getId() === self::AIR){
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS));
$ev->call();
if($ev->isCancelled()){
break;
}
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
$this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true);
}else{
break;
}
}
$this->meta = 0;
$this->getLevel()->setBlock($this, $this);
$this->getLevelNonNull()->setBlock($this, $this);
}else{
++$this->meta;
$this->getLevel()->setBlock($this, $this);
$this->getLevelNonNull()->setBlock($this, $this);
}
}
}
@ -121,7 +121,7 @@ class Cactus extends Transparent{
$block2 = $this->getSide(Vector3::SIDE_WEST);
$block3 = $this->getSide(Vector3::SIDE_EAST);
if(!$block0->isSolid() and !$block1->isSolid() and !$block2->isSolid() and !$block3->isSolid()){
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
return true;
}

View File

@ -66,7 +66,7 @@ class Cake extends Transparent implements FoodSource{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() !== self::AIR){
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -76,7 +76,7 @@ class Cake extends Transparent implements FoodSource{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ //Replace with common break method
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true);
}
}
@ -109,6 +109,10 @@ class Cake extends Transparent implements FoodSource{
return true;
}
public function getVariantBitmask() : int{
return 0;
}
/**
* @return Block
*/

View File

@ -64,7 +64,7 @@ class Carpet extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() !== self::AIR){
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -74,7 +74,7 @@ class Carpet extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -81,7 +81,7 @@ class Chest extends Transparent{
}
$c = $this->getSide($side);
if($c->getId() === $this->id and $c->getDamage() === $this->meta){
$tile = $this->getLevel()->getTile($c);
$tile = $this->getLevelNonNull()->getTile($c);
if($tile instanceof TileChest and !$tile->isPaired()){
$chest = $tile;
break;
@ -89,8 +89,8 @@ class Chest extends Transparent{
}
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$tile = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this, $face, $item, $player));
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
$tile = Tile::createTile(Tile::CHEST, $this->getLevelNonNull(), TileChest::createNBT($this, $face, $item, $player));
if($chest instanceof TileChest and $tile instanceof TileChest){
$chest->pairWith($tile);
@ -103,12 +103,12 @@ class Chest extends Transparent{
public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){
$t = $this->getLevel()->getTile($this);
$t = $this->getLevelNonNull()->getTile($this);
$chest = null;
if($t instanceof TileChest){
$chest = $t;
}else{
$chest = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this));
$chest = Tile::createTile(Tile::CHEST, $this->getLevelNonNull(), TileChest::createNBT($this));
if(!($chest instanceof TileChest)){
return true;
}

View File

@ -23,6 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\ItemIds;
use function mt_rand;
class CocoaBlock extends Transparent{
protected $id = self::COCOA_BLOCK;
@ -48,4 +53,14 @@ class CocoaBlock extends Transparent{
public function isAffectedBySilkTouch() : bool{
return false;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
ItemFactory::get(ItemIds::DYE, 3, ($this->meta >> 2) === 2 ? mt_rand(2, 3) : 1)
];
}
public function getPickedItem() : Item{
return ItemFactory::get(ItemIds::DYE, 3);
}
}

View File

@ -25,7 +25,10 @@ namespace pocketmine\block;
use pocketmine\inventory\CraftingGrid;
use pocketmine\item\Item;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\types\WindowTypes;
use pocketmine\Player;
use function array_key_exists;
class CraftingTable extends Solid{
@ -50,6 +53,19 @@ class CraftingTable extends Solid{
public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){
$player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG));
if(!array_key_exists($windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID, $player->openHardcodedWindows)){
//TODO: HACK! crafting grid doesn't fit very well into the current PM container system, so this hack allows
//it to carry on working approximately the same way as it did in 1.14
$pk = new ContainerOpenPacket();
$pk->windowId = $windowId;
$pk->type = WindowTypes::WORKBENCH;
$pk->x = $this->getFloorX();
$pk->y = $this->getFloorY();
$pk->z = $this->getFloorZ();
$player->sendDataPacket($pk);
$player->openHardcodedWindows[$windowId] = true;
}
}
return true;

View File

@ -33,7 +33,7 @@ abstract class Crops extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
if($blockReplace->getSide(Vector3::SIDE_DOWN)->getId() === Block::FARMLAND){
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -52,7 +52,7 @@ abstract class Crops extends Flowable{
$ev = new BlockGrowEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true);
$this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true, true);
}
$item->pop();
@ -65,7 +65,7 @@ abstract class Crops extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== Block::FARMLAND){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
@ -81,7 +81,7 @@ abstract class Crops extends Flowable{
$ev = new BlockGrowEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true);
$this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true, true);
}
}
}

View File

@ -42,7 +42,7 @@ class Dandelion extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -52,7 +52,7 @@ class Dandelion extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -51,7 +51,7 @@ class DeadBush extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -54,9 +54,9 @@ class Dirt extends Solid{
if($item instanceof Hoe){
$item->applyDamage(1);
if($this->meta === 1){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::DIRT), true);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::DIRT), true);
}else{
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND), true);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FARMLAND), true);
}
return true;

View File

@ -201,9 +201,9 @@ abstract class Door extends Transparent{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ //Replace with common break method
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), false);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false);
if($this->getSide(Vector3::SIDE_UP) instanceof Door){
$this->getLevel()->setBlock($this->getSide(Vector3::SIDE_UP), BlockFactory::get(Block::AIR), false);
$this->getLevelNonNull()->setBlock($this->getSide(Vector3::SIDE_UP), BlockFactory::get(Block::AIR), false);
}
}
}
@ -230,8 +230,8 @@ abstract class Door extends Transparent{
}
$this->setDamage($player->getDirection() & 0x03);
$this->getLevel()->setBlock($blockReplace, $this, true, true); //Bottom
$this->getLevel()->setBlock($blockUp, BlockFactory::get($this->getId(), $metaUp), true); //Top
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); //Bottom
$this->getLevelNonNull()->setBlock($blockUp, BlockFactory::get($this->getId(), $metaUp), true); //Top
return true;
}

View File

@ -57,8 +57,8 @@ class DoublePlant extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$id = $blockReplace->getSide(Vector3::SIDE_DOWN)->getId();
if(($id === Block::GRASS or $id === Block::DIRT) and $blockReplace->getSide(Vector3::SIDE_UP)->canBeReplaced()){
$this->getLevel()->setBlock($blockReplace, $this, false, false);
$this->getLevel()->setBlock($blockReplace->getSide(Vector3::SIDE_UP), BlockFactory::get($this->id, $this->meta | self::BITFLAG_TOP), false, false);
$this->getLevelNonNull()->setBlock($blockReplace, $this, false, false);
$this->getLevelNonNull()->setBlock($blockReplace->getSide(Vector3::SIDE_UP), BlockFactory::get($this->id, $this->meta | self::BITFLAG_TOP), false, false);
return true;
}
@ -85,7 +85,7 @@ class DoublePlant extends Flowable{
public function onNearbyBlockChange() : void{
if(!$this->isValidHalfPlant() or (($this->meta & self::BITFLAG_TOP) === 0 and $this->getSide(Vector3::SIDE_DOWN)->isTransparent())){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
@ -124,4 +124,12 @@ class DoublePlant extends Flowable{
return parent::getAffectedBlocks();
}
public function getFlameEncouragement() : int{
return 60;
}
public function getFlammability() : int{
return 100;
}
}

View File

@ -40,9 +40,9 @@ class EnchantingTable extends Transparent{
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevel(), TileEnchantTable::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevelNonNull(), TileEnchantTable::createNBT($this, $face, $item, $player));
return true;
}

View File

@ -69,8 +69,8 @@ class EnderChest extends Chest{
$this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0];
$this->getLevel()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this, $face, $item, $player));
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::ENDER_CHEST, $this->getLevelNonNull(), TileEnderChest::createNBT($this, $face, $item, $player));
return true;
}
@ -78,12 +78,12 @@ class EnderChest extends Chest{
public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){
$t = $this->getLevel()->getTile($this);
$t = $this->getLevelNonNull()->getTile($this);
$enderChest = null;
if($t instanceof TileEnderChest){
$enderChest = $t;
}else{
$enderChest = Tile::createTile(Tile::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this));
$enderChest = Tile::createTile(Tile::ENDER_CHEST, $this->getLevelNonNull(), TileEnderChest::createNBT($this));
if(!($enderChest instanceof TileEnderChest)){
return true;
}

View File

@ -37,7 +37,7 @@ abstract class Fallable extends Solid{
$nbt->setInt("TileID", $this->getId());
$nbt->setByte("Data", $this->getDamage());
$fall = Entity::createEntity("FallingSand", $this->getLevel(), $nbt);
$fall = Entity::createEntity("FallingSand", $this->getLevelNonNull(), $nbt);
if($fall !== null){
$fall->spawnToAll();

View File

@ -69,7 +69,7 @@ class FenceGate extends Transparent{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->meta = ($player instanceof Player ? ($player->getDirection() - 1) & 0x03 : 0);
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -85,7 +85,7 @@ class FenceGate extends Transparent{
$this->meta |= (($player->getDirection() - 1) & 0x02);
}
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
$this->level->addSound(new DoorSound($this));
return true;
}

View File

@ -82,7 +82,7 @@ class Fire extends Flowable{
public function onNearbyBlockChange() : void{
if(!$this->getSide(Vector3::SIDE_DOWN)->isSolid() and !$this->hasAdjacentFlammableBlocks()){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true);
}else{
$this->level->scheduleDelayedBlockUpdate($this, mt_rand(30, 40));
}

View File

@ -62,7 +62,7 @@ class Flower extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){
$this->getLevel()->setBlock($blockReplace, $this, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
return true;
}
@ -72,7 +72,7 @@ class Flower extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -62,19 +62,19 @@ class FlowerPot extends Flowable{
return false;
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::FLOWER_POT, $this->getLevel(), TileFlowerPot::createNBT($this, $face, $item, $player));
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::FLOWER_POT, $this->getLevelNonNull(), TileFlowerPot::createNBT($this, $face, $item, $player));
return true;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
public function onActivate(Item $item, Player $player = null) : bool{
$pot = $this->getLevel()->getTile($this);
$pot = $this->getLevelNonNull()->getTile($this);
if(!($pot instanceof TileFlowerPot)){
return false;
}
@ -83,7 +83,7 @@ class FlowerPot extends Flowable{
}
$this->setDamage(self::STATE_FULL); //specific damage value is unnecessary, it just needs to be non-zero to show an item.
$this->getLevel()->setBlock($this, $this, true, false);
$this->getLevelNonNull()->setBlock($this, $this, true, false);
$pot->setItem($item->pop());
return true;
@ -96,7 +96,7 @@ class FlowerPot extends Flowable{
public function getDropsForCompatibleTool(Item $item) : array{
$items = parent::getDropsForCompatibleTool($item);
$tile = $this->getLevel()->getTile($this);
$tile = $this->getLevelNonNull()->getTile($this);
if($tile instanceof TileFlowerPot){
$item = $tile->getItem();
if($item->getId() !== Item::AIR){

View File

@ -53,7 +53,7 @@ class GlazedTerracotta extends Solid{
$this->meta = $faces[(~($player->getDirection() - 1)) & 0x03];
}
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
}
public function getVariantBitmask() : int{

View File

@ -53,6 +53,6 @@ class GlowingRedstoneOre extends RedstoneOre{
}
public function onRandomTick() : void{
$this->getLevel()->setBlock($this, BlockFactory::get(Block::REDSTONE_ORE, $this->meta), false, false);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::REDSTONE_ORE, $this->meta), false, false);
}
}

View File

@ -100,17 +100,17 @@ class Grass extends Solid{
public function onActivate(Item $item, Player $player = null) : bool{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){
$item->pop();
TallGrassObject::growGrass($this->getLevel(), $this, new Random(mt_rand()), 8, 2);
TallGrassObject::growGrass($this->getLevelNonNull(), $this, new Random(mt_rand()), 8, 2);
return true;
}elseif($item instanceof Hoe){
$item->applyDamage(1);
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND));
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FARMLAND));
return true;
}elseif($item instanceof Shovel and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){
$item->applyDamage(1);
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GRASS_PATH));
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GRASS_PATH));
return true;
}

View File

@ -46,7 +46,7 @@ class HayBale extends Solid{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face);
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -57,7 +57,7 @@ class Ice extends Transparent{
public function onBreak(Item $item, Player $player = null) : bool{
if(($player === null or $player->isSurvival()) and !$item->hasEnchantment(Enchantment::SILK_TOUCH)){
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::WATER), true);
return $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::WATER), true);
}
return parent::onBreak($item, $player);
}

View File

@ -46,7 +46,7 @@ class ItemFrame extends Flowable{
public function onActivate(Item $item, Player $player = null) : bool{
$tile = $this->level->getTile($this);
if(!($tile instanceof TileItemFrame)){
$tile = Tile::createTile(Tile::ITEM_FRAME, $this->getLevel(), TileItemFrame::createNBT($this));
$tile = Tile::createTile(Tile::ITEM_FRAME, $this->getLevelNonNull(), TileItemFrame::createNBT($this));
if(!($tile instanceof TileItemFrame)){
return true;
}
@ -88,7 +88,7 @@ class ItemFrame extends Flowable{
$this->meta = $faces[$face];
$this->level->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::ITEM_FRAME, $this->getLevel(), TileItemFrame::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::ITEM_FRAME, $this->getLevelNonNull(), TileItemFrame::createNBT($this, $face, $item, $player));
return true;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
@ -58,7 +59,7 @@ class Ladder extends Transparent{
}
public function onEntityCollide(Entity $entity) : void{
if($entity->asVector3()->floor()->distanceSquared($this) < 1){ //entity coordinates must be inside block
if($entity instanceof Living and $entity->asVector3()->floor()->distanceSquared($this) < 1){ //entity coordinates must be inside block
$entity->resetFallDistance();
$entity->onGround = true;
}
@ -100,7 +101,7 @@ class Ladder extends Transparent{
];
if(isset($faces[$face])){
$this->meta = $faces[$face];
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -115,8 +115,8 @@ class Lava extends Liquid{
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$ret = $this->getLevel()->setBlock($this, $this, true, false);
$this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate());
$ret = $this->getLevelNonNull()->setBlock($this, $this, true, false);
$this->getLevelNonNull()->scheduleDelayedBlockUpdate($this, $this->tickRate());
return $ret;
}

View File

@ -139,7 +139,7 @@ class Leaves extends Transparent{
public function onNearbyBlockChange() : void{
if(($this->meta & 0b00001100) === 0){
$this->meta |= 0x08;
$this->getLevel()->setBlock($this, $this, true, false);
$this->getLevelNonNull()->setBlock($this, $this, true, false);
}
}
@ -155,16 +155,16 @@ class Leaves extends Transparent{
$ev = new LeavesDecayEvent($this);
$ev->call();
if($ev->isCancelled() or $this->findLog($this, $visited, 0)){
$this->getLevel()->setBlock($this, $this, false, false);
$this->getLevelNonNull()->setBlock($this, $this, false, false);
}else{
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->meta |= 0x04;
return $this->getLevel()->setBlock($this, $this, true);
return $this->getLevelNonNull()->setBlock($this, $this, true);
}
public function getVariantBitmask() : int{

View File

@ -49,7 +49,7 @@ class MelonStem extends Crops{
$ev = new BlockGrowEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true);
$this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true);
}
}else{
for($side = 2; $side <= 5; ++$side){
@ -64,7 +64,7 @@ class MelonStem extends Crops{
$ev = new BlockGrowEvent($side, BlockFactory::get(Block::MELON_BLOCK));
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($side, $ev->getNewState(), true);
$this->getLevelNonNull()->setBlock($side, $ev->getNewState(), true);
}
}
}

View File

@ -64,13 +64,13 @@ class Mycelium extends Solid{
$x = mt_rand($this->x - 1, $this->x + 1);
$y = mt_rand($this->y - 2, $this->y + 2);
$z = mt_rand($this->z - 1, $this->z + 1);
$block = $this->getLevel()->getBlockAt($x, $y, $z);
$block = $this->getLevelNonNull()->getBlockAt($x, $y, $z);
if($block->getId() === Block::DIRT){
if($block->getSide(Vector3::SIDE_UP) instanceof Transparent){
$ev = new BlockSpreadEvent($block, $this, BlockFactory::get(Block::MYCELIUM));
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($block, $ev->getNewState());
$this->getLevelNonNull()->setBlock($block, $ev->getNewState());
}
}
}

View File

@ -46,7 +46,7 @@ class NetherWartPlant extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === Block::SOUL_SAND){
$this->getLevel()->setBlock($blockReplace, $this, false, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, false, true);
return true;
}
@ -56,7 +56,7 @@ class NetherWartPlant extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== Block::SOUL_SAND){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
@ -71,7 +71,7 @@ class NetherWartPlant extends Flowable{
$ev = new BlockGrowEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), false, true);
$this->getLevelNonNull()->setBlock($this, $ev->getNewState(), false, true);
}
}
}

View File

@ -51,7 +51,7 @@ class Pumpkin extends Solid{
if($player instanceof Player){
$this->meta = ((int) $player->getDirection() + 1) % 4;
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -49,7 +49,7 @@ class PumpkinStem extends Crops{
$ev = new BlockGrowEvent($this, $block);
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true);
$this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true);
}
}else{
for($side = 2; $side <= 5; ++$side){
@ -64,7 +64,7 @@ class PumpkinStem extends Crops{
$ev = new BlockGrowEvent($side, BlockFactory::get(Block::PUMPKIN));
$ev->call();
if(!$ev->isCancelled()){
$this->getLevel()->setBlock($side, $ev->getNewState(), true);
$this->getLevelNonNull()->setBlock($side, $ev->getNewState(), true);
}
}
}

View File

@ -58,7 +58,7 @@ class Quartz extends Solid{
if($this->getVariant() !== self::NORMAL){
$this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face);
}
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
}
public function getToolType() : int{

View File

@ -71,6 +71,7 @@ class Rail extends BaseRail{
}
protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{
/** @var int[] $horizontal */
static $horizontal = [
Vector3::SIDE_NORTH,
Vector3::SIDE_SOUTH,

View File

@ -45,14 +45,14 @@ class RedMushroom extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if(!$down->isTransparent()){
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -47,16 +47,16 @@ class RedstoneOre extends Solid{
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
return $this->getLevel()->setBlock($this, $this, true, false);
return $this->getLevelNonNull()->setBlock($this, $this, true, false);
}
public function onActivate(Item $item, Player $player = null) : bool{
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
return false; //this shouldn't prevent block placement
}
public function onNearbyBlockChange() : void{
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
}
public function getToolType() : int{

View File

@ -59,7 +59,7 @@ class Sapling extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::FARMLAND){
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -70,7 +70,7 @@ class Sapling extends Flowable{
public function onActivate(Item $item, Player $player = null) : bool{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
//TODO: change log type
Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
Tree::growTree($this->getLevelNonNull(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
$item->pop();
@ -82,7 +82,7 @@ class Sapling extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
@ -93,10 +93,10 @@ class Sapling extends Flowable{
public function onRandomTick() : void{
if($this->level->getFullLightAt($this->x, $this->y, $this->z) >= 8 and mt_rand(1, 7) === 1){
if(($this->meta & 0x08) === 0x08){
Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
Tree::growTree($this->getLevelNonNull(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
}else{
$this->meta |= 0x08;
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
}
}
}

View File

@ -62,13 +62,13 @@ class SignPost extends Transparent{
if($face === Vector3::SIDE_UP){
$this->meta = $player !== null ? (floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f) : 0;
$this->getLevel()->setBlock($blockReplace, $this, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
}else{
$this->meta = $face;
$this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::WALL_SIGN, $this->meta), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::WALL_SIGN, $this->meta), true);
}
Tile::createTile(Tile::SIGN, $this->getLevel(), TileSign::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::SIGN, $this->getLevelNonNull(), TileSign::createNBT($this, $face, $item, $player));
return true;
}
@ -78,7 +78,7 @@ class SignPost extends Transparent{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -65,8 +65,8 @@ class Skull extends Flowable{
}
$this->meta = $face;
$this->getLevel()->setBlock($blockReplace, $this, true);
Tile::createTile(Tile::SKULL, $this->getLevel(), TileSkull::createNBT($this, $face, $item, $player));
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
Tile::createTile(Tile::SKULL, $this->getLevelNonNull(), TileSkull::createNBT($this, $face, $item, $player));
return true;
}

View File

@ -56,11 +56,11 @@ abstract class Slab extends Transparent{
$this->meta &= 0x07;
if($face === Vector3::SIDE_DOWN){
if($blockClicked->getId() === $this->id and ($blockClicked->getDamage() & 0x08) === 0x08 and $blockClicked->getVariant() === $this->getVariant()){
$this->getLevel()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
$this->getLevelNonNull()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
return true;
}elseif($blockReplace->getId() === $this->id and $blockReplace->getVariant() === $this->getVariant()){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
return true;
}else{
@ -68,18 +68,18 @@ abstract class Slab extends Transparent{
}
}elseif($face === Vector3::SIDE_UP){
if($blockClicked->getId() === $this->id and ($blockClicked->getDamage() & 0x08) === 0 and $blockClicked->getVariant() === $this->getVariant()){
$this->getLevel()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
$this->getLevelNonNull()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
return true;
}elseif($blockReplace->getId() === $this->id and $blockReplace->getVariant() === $this->getVariant()){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
return true;
}
}else{ //TODO: collision
if($blockReplace->getId() === $this->id){
if($blockReplace->getVariant() === $this->getVariant()){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
return true;
}
@ -95,7 +95,7 @@ abstract class Slab extends Transparent{
if($blockReplace->getId() === $this->id and $blockClicked->getVariant() !== $this->getVariant()){
return false;
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -42,7 +42,7 @@ class SnowLayer extends Flowable{
}
public function canBeReplaced() : bool{
return true;
return $this->meta < 7; //8 snow layers
}
public function getHardness() : float{
@ -57,10 +57,16 @@ class SnowLayer extends Flowable{
return TieredTool::TIER_WOODEN;
}
private function canBeSupportedBy(Block $b) : bool{
return $b->isSolid() or ($b->getId() === $this->getId() and $b->getDamage() === 7);
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
if($blockReplace->getSide(Vector3::SIDE_DOWN)->isSolid()){
//TODO: fix placement
$this->getLevel()->setBlock($blockReplace, $this, true);
if($blockReplace->getId() === $this->getId() and $blockReplace->getDamage() < 7){
$this->setDamage($blockReplace->getDamage() + 1);
}
if($this->canBeSupportedBy($blockReplace->getSide(Vector3::SIDE_DOWN))){
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
return true;
}
@ -69,8 +75,8 @@ class SnowLayer extends Flowable{
}
public function onNearbyBlockChange() : void{
if(!$this->getSide(Vector3::SIDE_DOWN)->isSolid()){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), false, false);
if(!$this->canBeSupportedBy($this->getSide(Vector3::SIDE_DOWN))){
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false, false);
}
}
@ -80,7 +86,7 @@ class SnowLayer extends Flowable{
public function onRandomTick() : void{
if($this->level->getBlockLightAt($this->x, $this->y, $this->z) >= 12){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), false, false);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false, false);
}
}

View File

@ -93,7 +93,7 @@ abstract class Stair extends Transparent{
if(($clickVector->y > 0.5 and $face !== Vector3::SIDE_UP) or $face === Vector3::SIDE_DOWN){
$this->meta |= 0x04; //Upside-down stairs
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -62,13 +62,13 @@ class StandingBanner extends Transparent{
if($face !== Vector3::SIDE_DOWN){
if($face === Vector3::SIDE_UP and $player !== null){
$this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f;
$this->getLevel()->setBlock($blockReplace, $this, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
}else{
$this->meta = $face;
$this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::WALL_BANNER, $this->meta), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::WALL_BANNER, $this->meta), true);
}
Tile::createTile(Tile::BANNER, $this->getLevel(), TileBanner::createNBT($this, $face, $item, $player));
Tile::createTile(Tile::BANNER, $this->getLevelNonNull(), TileBanner::createNBT($this, $face, $item, $player));
return true;
}
@ -77,7 +77,7 @@ class StandingBanner extends Transparent{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -46,20 +46,20 @@ class Sugarcane extends Flowable{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){
for($y = 1; $y < 3; ++$y){
$b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z);
$b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z);
if($b->getId() === self::AIR){
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK));
$ev->call();
if($ev->isCancelled()){
break;
}
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
$this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true);
}else{
break;
}
}
$this->meta = 0;
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
}
$item->pop();
@ -73,7 +73,7 @@ class Sugarcane extends Flowable{
public function onNearbyBlockChange() : void{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->isTransparent() and $down->getId() !== self::SUGARCANE_BLOCK){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
@ -85,17 +85,17 @@ class Sugarcane extends Flowable{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){
if($this->meta === 0x0F){
for($y = 1; $y < 3; ++$y){
$b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z);
$b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z);
if($b->getId() === self::AIR){
$this->getLevel()->setBlock($b, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
$this->getLevelNonNull()->setBlock($b, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
break;
}
}
$this->meta = 0;
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
}else{
++$this->meta;
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
}
}
}
@ -103,7 +103,7 @@ class Sugarcane extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === self::SUGARCANE_BLOCK){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
return true;
}elseif($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::SAND){
@ -112,7 +112,7 @@ class Sugarcane extends Flowable{
$block2 = $down->getSide(Vector3::SIDE_WEST);
$block3 = $down->getSide(Vector3::SIDE_EAST);
if(($block0 instanceof Water) or ($block1 instanceof Water) or ($block2 instanceof Water) or ($block3 instanceof Water)){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
return true;
}

View File

@ -78,13 +78,13 @@ class TNT extends Solid{
* @return void
*/
public function ignite(int $fuse = 80){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true);
$mot = (new Random())->nextSignedFloat() * M_PI * 2;
$nbt = Entity::createBaseNBT($this->add(0.5, 0, 0.5), new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02));
$nbt->setShort("Fuse", $fuse);
$tnt = Entity::createEntity("PrimedTNT", $this->getLevel(), $nbt);
$tnt = Entity::createEntity("PrimedTNT", $this->getLevelNonNull(), $nbt);
if($tnt !== null){
$tnt->spawnToAll();

View File

@ -53,7 +53,7 @@ class TallGrass extends Flowable{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN)->getId();
if($down === self::GRASS or $down === self::DIRT){
$this->getLevel()->setBlock($blockReplace, $this, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
return true;
}
@ -63,7 +63,7 @@ class TallGrass extends Flowable{
public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ //Replace with common break method
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true, true);
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true, true);
}
}

View File

@ -57,7 +57,7 @@ class Torch extends Flowable{
$face = $faces[$meta] ?? Vector3::SIDE_DOWN;
if($this->getSide($face)->isTransparent() and !($face === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
@ -73,12 +73,12 @@ class Torch extends Flowable{
Vector3::SIDE_EAST => 1
];
$this->meta = $faces[$face];
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}elseif(!$below->isTransparent() or $below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL){
$this->meta = 0;
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -136,7 +136,7 @@ class Trapdoor extends Transparent{
if(($clickVector->y > 0.5 and $face !== self::SIDE_UP) or $face === self::SIDE_DOWN){
$this->meta |= self::MASK_UPPER; //top half of block
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}
@ -146,7 +146,7 @@ class Trapdoor extends Transparent{
public function onActivate(Item $item, Player $player = null) : bool{
$this->meta ^= self::MASK_OPENED;
$this->getLevel()->setBlock($this, $this, true);
$this->getLevelNonNull()->setBlock($this, $this, true);
$this->level->addSound(new DoorSound($this));
return true;
}

View File

@ -150,7 +150,7 @@ class Vine extends Flowable{
$this->meta |= $blockReplace->meta;
}
$this->getLevel()->setBlock($blockReplace, $this, true, true);
$this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true;
}

View File

@ -33,7 +33,7 @@ class WallBanner extends StandingBanner{
public function onNearbyBlockChange() : void{
if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
}

View File

@ -33,7 +33,7 @@ class WallSign extends SignPost{
public function onNearbyBlockChange() : void{
if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}
}

View File

@ -70,13 +70,11 @@ class Water extends Liquid{
if($entity->isOnFire()){
$entity->extinguish();
}
$entity->resetFallDistance();
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$ret = $this->getLevel()->setBlock($this, $this, true, false);
$this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate());
$ret = $this->getLevelNonNull()->setBlock($this, $this, true, false);
$this->getLevelNonNull()->scheduleDelayedBlockUpdate($this, $this->tickRate());
return $ret;
}

View File

@ -59,7 +59,7 @@ class WaterLily extends Flowable{
if($blockClicked instanceof Water){
$up = $blockClicked->getSide(Vector3::SIDE_UP);
if($up->getId() === Block::AIR){
$this->getLevel()->setBlock($up, $this, true, true);
$this->getLevelNonNull()->setBlock($up, $this, true, true);
return true;
}
}
@ -69,7 +69,7 @@ class WaterLily extends Flowable{
public function onNearbyBlockChange() : void{
if(!($this->getSide(Vector3::SIDE_DOWN) instanceof Water)){
$this->getLevel()->useBreakOn($this);
$this->getLevelNonNull()->useBreakOn($this);
}
}

View File

@ -56,7 +56,7 @@ class Wood extends Solid{
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face);
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
}
public function getVariantBitmask() : int{

View File

@ -68,7 +68,7 @@ class CommandReader extends Thread{
$opts = getopt("", ["disable-readline", "enable-readline"]);
if(extension_loaded("readline") and (Utils::getOS() === "win" ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){
if(extension_loaded("readline") and (Utils::getOS() === Utils::OS_WINDOWS ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){
$this->type = self::TYPE_READLINE;
}
}

View File

@ -30,7 +30,9 @@ use pocketmine\permission\BanEntry;
use function array_map;
use function count;
use function implode;
use function sort;
use function strtolower;
use const SORT_STRING;
class BanListCommand extends VanillaCommand{
@ -62,10 +64,11 @@ class BanListCommand extends VanillaCommand{
$args[0] = "players";
}
$list = $list->getEntries();
$message = implode(", ", array_map(function(BanEntry $entry) : string{
$list = array_map(function(BanEntry $entry) : string{
return $entry->getName();
}, $list));
}, $list->getEntries());
sort($list, SORT_STRING);
$message = implode(", ", $list);
if($args[0] === "ips"){
$sender->sendMessage(new TranslationContainer("commands.banlist.ips", [count($list)]));

View File

@ -62,7 +62,7 @@ class GiveCommand extends VanillaCommand{
}
try{
$item = ItemFactory::fromString($args[1]);
$item = ItemFactory::fromStringSingle($args[1]);
}catch(\InvalidArgumentException $e){
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]]));
return true;

View File

@ -30,6 +30,8 @@ use function array_filter;
use function array_map;
use function count;
use function implode;
use function sort;
use const SORT_STRING;
class ListCommand extends VanillaCommand{
@ -52,6 +54,7 @@ class ListCommand extends VanillaCommand{
}, array_filter($sender->getServer()->getOnlinePlayers(), function(Player $player) use ($sender) : bool{
return $player->isOnline() and (!($sender instanceof Player) or $sender->canSee($player));
}));
sort($playerNames, SORT_STRING);
$sender->sendMessage(new TranslationContainer("commands.players.list", [count($playerNames), $sender->getServer()->getMaxPlayers()]));
$sender->sendMessage(implode(", ", $playerNames));

View File

@ -37,6 +37,7 @@ use pocketmine\level\particle\CriticalParticle;
use pocketmine\level\particle\DustParticle;
use pocketmine\level\particle\EnchantmentTableParticle;
use pocketmine\level\particle\EnchantParticle;
use pocketmine\level\particle\EntityFlameParticle;
use pocketmine\level\particle\ExplodeParticle;
use pocketmine\level\particle\FlameParticle;
use pocketmine\level\particle\HappyVillagerParticle;
@ -91,7 +92,7 @@ class ParticleCommand extends VanillaCommand{
}
if($sender instanceof Player){
$level = $sender->getLevel();
$level = $sender->getLevelNonNull();
$pos = new Vector3(
$this->getRelativeDouble($sender->getX(), $sender, $args[1]),
$this->getRelativeDouble($sender->getY(), $sender, $args[2], 0, Level::Y_MAX),
@ -204,7 +205,8 @@ class ParticleCommand extends VanillaCommand{
return new AngryVillagerParticle($pos);
case "forcefield":
return new BlockForceFieldParticle($pos, $data ?? 0);
case "mobflame":
return new EntityFlameParticle($pos);
}
if(strpos($name, "iconcrack_") === 0){

View File

@ -30,6 +30,8 @@ use pocketmine\utils\TextFormat;
use function array_map;
use function count;
use function implode;
use function sort;
use const SORT_STRING;
class PluginsCommand extends VanillaCommand{
@ -51,6 +53,7 @@ class PluginsCommand extends VanillaCommand{
$list = array_map(function(Plugin $plugin) : string{
return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName();
}, $sender->getServer()->getPluginManager()->getPlugins());
sort($list, SORT_STRING);
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($list), implode(TextFormat::WHITE . ", ", $list)]));
return true;

View File

@ -44,7 +44,7 @@ class SeedCommand extends VanillaCommand{
}
if($sender instanceof Player){
$seed = $sender->getLevel()->getSeed();
$seed = $sender->getLevelNonNull()->getSeed();
}else{
$seed = $sender->getServer()->getDefaultLevel()->getSeed();
}

View File

@ -51,7 +51,7 @@ class SetWorldSpawnCommand extends VanillaCommand{
if(count($args) === 0){
if($sender instanceof Player){
$level = $sender->getLevel();
$level = $sender->getLevelNonNull();
$pos = (new Vector3($sender->x, $sender->y, $sender->z))->round();
}else{
$sender->sendMessage(TextFormat::RED . "You can only perform this command as a player");

View File

@ -71,7 +71,7 @@ class SpawnpointCommand extends VanillaCommand{
if(count($args) === 4){
if($target->isValid()){
$level = $target->getLevel();
$level = $target->getLevelNonNull();
$pos = $sender instanceof Player ? $sender->getPosition() : $level->getSpawnLocation();
$x = $this->getRelativeDouble($pos->x, $sender, $args[1]);
$y = $this->getRelativeDouble($pos->y, $sender, $args[2], 0, Level::Y_MAX);
@ -84,7 +84,7 @@ class SpawnpointCommand extends VanillaCommand{
}
}elseif(count($args) <= 1){
if($sender instanceof Player){
$pos = new Position($sender->getFloorX(), $sender->getFloorY(), $sender->getFloorZ(), $sender->getLevel());
$pos = new Position($sender->getFloorX(), $sender->getFloorY(), $sender->getFloorZ(), $sender->getLevelNonNull());
$target->setSpawn($pos);
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.spawnpoint.success", [$target->getName(), round($pos->x, 2), round($pos->y, 2), round($pos->z, 2)]));

View File

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\utils\Process;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function count;
use function floor;
use function microtime;
@ -48,8 +48,8 @@ class StatusCommand extends VanillaCommand{
return true;
}
$rUsage = Utils::getRealMemoryUsage();
$mUsage = Utils::getMemoryUsage(true);
$rUsage = Process::getRealMemoryUsage();
$mUsage = Process::getAdvancedMemoryUsage();
$server = $sender->getServer();
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
@ -94,7 +94,7 @@ class StatusCommand extends VanillaCommand{
$sender->sendMessage(TextFormat::GOLD . "Network upload: " . TextFormat::RED . round($server->getNetwork()->getUpload() / 1024, 2) . " kB/s");
$sender->sendMessage(TextFormat::GOLD . "Network download: " . TextFormat::RED . round($server->getNetwork()->getDownload() / 1024, 2) . " kB/s");
$sender->sendMessage(TextFormat::GOLD . "Thread count: " . TextFormat::RED . Utils::getThreadCount());
$sender->sendMessage(TextFormat::GOLD . "Thread count: " . TextFormat::RED . Process::getThreadCount());
$sender->sendMessage(TextFormat::GOLD . "Main thread memory: " . TextFormat::RED . number_format(round(($mUsage[0] / 1024) / 1024, 2), 2) . " MB.");
$sender->sendMessage(TextFormat::GOLD . "Total memory: " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2), 2) . " MB.");

View File

@ -27,11 +27,11 @@ use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\TranslationContainer;
use pocketmine\math\Vector3;
use pocketmine\level\Location;
use pocketmine\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use function array_filter;
use function array_values;
use function array_shift;
use function count;
use function round;
@ -47,84 +47,83 @@ class TeleportCommand extends VanillaCommand{
$this->setPermission("pocketmine.command.teleport");
}
private function findPlayer(CommandSender $sender, string $playerName) : ?Player{
$subject = $sender->getServer()->getPlayer($playerName);
if($subject === null){
$sender->sendMessage(TextFormat::RED . "Can't find player " . $playerName);
return null;
}
return $subject;
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
if(!$this->testPermission($sender)){
return true;
}
$args = array_values(array_filter($args, function(string $arg) : bool{
return $arg !== "";
}));
if(count($args) < 1 or count($args) > 6){
throw new InvalidCommandSyntaxException();
}
$target = null;
$origin = $sender;
if(count($args) === 1 or count($args) === 3){
if($sender instanceof Player){
$target = $sender;
}else{
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
return true;
}
if(count($args) === 1){
$target = $sender->getServer()->getPlayer($args[0]);
if($target === null){
$sender->sendMessage(TextFormat::RED . "Can't find player " . $args[0]);
switch(count($args)){
case 1: // /tp targetPlayer
case 3: // /tp x y z
case 5: // /tp x y z yaw pitch - TODO: 5 args could be target x y z yaw :(
if(!($sender instanceof Player)){
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
return true;
}
}
}else{
$target = $sender->getServer()->getPlayer($args[0]);
if($target === null){
$sender->sendMessage(TextFormat::RED . "Can't find player " . $args[0]);
return true;
}
if(count($args) === 2){
$origin = $target;
$target = $sender->getServer()->getPlayer($args[1]);
if($target === null){
$sender->sendMessage(TextFormat::RED . "Can't find player " . $args[1]);
$subject = $sender;
$targetArgs = $args;
break;
case 2: // /tp player1 player2
case 4: // /tp player1 x y z - TODO: 4 args could be x y z yaw :(
case 6: // /tp player1 x y z yaw pitch
$subject = $this->findPlayer($sender, $args[0]);
if($subject === null){
return true;
}
}
$targetArgs = $args;
array_shift($targetArgs);
break;
default:
throw new InvalidCommandSyntaxException();
}
if(count($args) < 3){
$origin->teleport($target);
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.tp.success", [$origin->getName(), $target->getName()]));
switch(count($targetArgs)){
case 1:
$targetPlayer = $this->findPlayer($sender, $targetArgs[0]);
if($targetPlayer === null){
return true;
}
return true;
}elseif($target->isValid()){
if(count($args) === 4 or count($args) === 6){
$pos = 1;
}else{
$pos = 0;
}
$subject->teleport($targetPlayer->getLocation());
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.tp.success", [$subject->getName(), $targetPlayer->getName()]));
$x = $this->getRelativeDouble($target->x, $sender, $args[$pos++]);
$y = $this->getRelativeDouble($target->y, $sender, $args[$pos++], 0, 256);
$z = $this->getRelativeDouble($target->z, $sender, $args[$pos++]);
$yaw = $target->getYaw();
$pitch = $target->getPitch();
return true;
case 3:
case 5:
$base = $subject->getLocation();
if(count($targetArgs) === 5){
$yaw = (float) $targetArgs[3];
$pitch = (float) $targetArgs[4];
}else{
$yaw = $base->yaw;
$pitch = $base->pitch;
}
if(count($args) === 6 or (count($args) === 5 and $pos === 3)){
$yaw = (float) $args[$pos++];
$pitch = (float) $args[$pos++];
}
$x = $this->getRelativeDouble($base->x, $sender, $targetArgs[0]);
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], 0, 256);
$z = $this->getRelativeDouble($base->z, $sender, $targetArgs[2]);
$targetLocation = new Location($x, $y, $z, $yaw, $pitch, $base->getLevelNonNull());
$target->teleport(new Vector3($x, $y, $z), $yaw, $pitch);
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.tp.success.coordinates", [$target->getName(), round($x, 2), round($y, 2), round($z, 2)]));
return true;
$subject->teleport($targetLocation);
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.tp.success.coordinates", [
$subject->getName(),
round($targetLocation->x, 2),
round($targetLocation->y, 2),
round($targetLocation->z, 2)
]));
return true;
default:
throw new AssumptionFailedError("This branch should be unreachable (for now)");
}
throw new InvalidCommandSyntaxException();
}
}

View File

@ -77,7 +77,7 @@ class TimeCommand extends VanillaCommand{
return true;
}
if($sender instanceof Player){
$level = $sender->getLevel();
$level = $sender->getLevelNonNull();
}else{
$level = $sender->getServer()->getDefaultLevel();
}
@ -96,12 +96,28 @@ class TimeCommand extends VanillaCommand{
return true;
}
if($args[1] === "day"){
$value = Level::TIME_DAY;
}elseif($args[1] === "night"){
$value = Level::TIME_NIGHT;
}else{
$value = $this->getInteger($sender, $args[1], 0);
switch($args[1]){
case "day":
$value = Level::TIME_DAY;
break;
case "noon":
$value = Level::TIME_NOON;
break;
case "sunset":
$value = Level::TIME_SUNSET;
break;
case "night":
$value = Level::TIME_NIGHT;
break;
case "midnight":
$value = Level::TIME_MIDNIGHT;
break;
case "sunrise":
$value = Level::TIME_SUNRISE;
break;
default:
$value = $this->getInteger($sender, $args[1], 0);
break;
}
foreach($sender->getServer()->getLevels() as $level){

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\TranslationContainer;
@ -72,12 +73,12 @@ class TimingsCommand extends VanillaCommand{
if($mode === "on"){
TimingsHandler::setEnabled();
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.enable"));
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.command.timings.enable"));
return true;
}elseif($mode === "off"){
TimingsHandler::setEnabled(false);
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.disable"));
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.command.timings.disable"));
return true;
}
@ -91,7 +92,7 @@ class TimingsCommand extends VanillaCommand{
if($mode === "reset"){
TimingsHandler::reload();
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.reset"));
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.command.timings.reset"));
}elseif($mode === "merged" or $mode === "report" or $paste){
$timings = "";
if($paste){
@ -150,6 +151,7 @@ class TimingsCommand extends VanillaCommand{
}
public function onCompletion(Server $server){
/** @var CommandSender $sender */
$sender = $this->fetchLocal();
if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
return;
@ -159,18 +161,21 @@ class TimingsCommand extends VanillaCommand{
$server->getLogger()->logException($result);
return;
}
if(isset($result[0]) && is_array($response = json_decode($result[0], true)) && isset($response["id"])){
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsRead",
$response = json_decode($result[0], true);
if(is_array($response) && isset($response["id"])){
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.command.timings.timingsRead",
["https://" . $this->host . "/?id=" . $response["id"]]));
}else{
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.pasteError"));
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.command.timings.pasteError"));
}
}
});
}else{
fclose($fileTimings);
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings]));
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings]));
}
}else{
throw new InvalidCommandSyntaxException();
}
return true;

View File

@ -68,21 +68,21 @@ class TitleCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player->addTitle(implode(" ", array_slice($args, 2)));
$player->sendTitle(implode(" ", array_slice($args, 2)));
break;
case "subtitle":
if(count($args) < 3){
throw new InvalidCommandSyntaxException();
}
$player->addSubTitle(implode(" ", array_slice($args, 2)));
$player->sendSubTitle(implode(" ", array_slice($args, 2)));
break;
case "actionbar":
if(count($args) < 3){
throw new InvalidCommandSyntaxException();
}
$player->addActionBarMessage(implode(" ", array_slice($args, 2)));
$player->sendActionBarMessage(implode(" ", array_slice($args, 2)));
break;
case "times":
if(count($args) < 5){

View File

@ -31,7 +31,9 @@ use pocketmine\Player;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
use function sort;
use function strtolower;
use const SORT_STRING;
class WhitelistCommand extends VanillaCommand{
@ -49,10 +51,6 @@ class WhitelistCommand extends VanillaCommand{
return true;
}
if(count($args) === 0 or count($args) > 2){
throw new InvalidCommandSyntaxException();
}
if(count($args) === 1){
if($this->badPerm($sender, strtolower($args[0]))){
return false;
@ -75,6 +73,7 @@ class WhitelistCommand extends VanillaCommand{
return true;
case "list":
$entries = $sender->getServer()->getWhitelisted()->getAll(true);
sort($entries, SORT_STRING);
$result = implode($entries, ", ");
$count = count($entries);
@ -112,7 +111,7 @@ class WhitelistCommand extends VanillaCommand{
}
}
return true;
throw new InvalidCommandSyntaxException();
}
private function badPerm(CommandSender $sender, string $subcommand) : bool{

View File

@ -45,6 +45,7 @@ class Attribute{
public const FALL_DAMAGE = 13;
public const HORSE_JUMP_STRENGTH = 14;
public const ZOMBIE_SPAWN_REINFORCEMENTS = 15;
public const LAVA_MOVEMENT = 16;
/** @var int */
private $id;
@ -84,6 +85,7 @@ class Attribute{
self::addAttribute(self::FALL_DAMAGE, "minecraft:fall_damage", 0.0, 340282346638528859811704183484516925440.0, 1.0);
self::addAttribute(self::HORSE_JUMP_STRENGTH, "minecraft:horse.jump_strength", 0.0, 2.0, 0.7);
self::addAttribute(self::ZOMBIE_SPAWN_REINFORCEMENTS, "minecraft:zombie.spawn_reinforcements", 0.0, 1.0, 0.0);
self::addAttribute(self::LAVA_MOVEMENT, "minecraft:lava_movement", 0.0, 340282346638528859811704183484516925440.0, 0.02);
}
/**

View File

@ -31,7 +31,7 @@ use pocketmine\event\player\PlayerExhaustEvent;
use pocketmine\utils\Color;
use function constant;
use function defined;
use function strtoupper;
use function mb_strtoupper;
class Effect{
public const SPEED = 1;
@ -102,7 +102,7 @@ class Effect{
}
public static function getEffectByName(string $name) : ?Effect{
$const = self::class . "::" . strtoupper($name);
$const = self::class . "::" . mb_strtoupper($name);
if(defined($const)){
return self::getEffect(constant($const));
}

View File

@ -297,11 +297,11 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_FLAG_OVER_SCAFFOLDING = 69;
public const DATA_FLAG_FALL_THROUGH_SCAFFOLDING = 70;
public const DATA_FLAG_BLOCKING = 71; //shield
public const DATA_FLAG_DISABLE_BLOCKING = 72;
//73 is set when a player is attacked while using shield, unclear on purpose
//74 related to shield usage, needs further investigation
public const DATA_FLAG_TRANSITION_BLOCKING = 72;
public const DATA_FLAG_BLOCKED_USING_SHIELD = 73;
public const DATA_FLAG_BLOCKED_USING_DAMAGED_SHIELD = 74;
public const DATA_FLAG_SLEEPING = 75;
//76 related to sleeping, unclear usage
public const DATA_FLAG_WANTS_TO_WAKE = 76;
public const DATA_FLAG_TRADE_INTEREST = 77;
public const DATA_FLAG_DOOR_BREAKER = 78; //...
public const DATA_FLAG_BREAKING_OBSTRUCTION = 79;
@ -311,8 +311,15 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_FLAG_ROARING = 83;
public const DATA_FLAG_DELAYED_ATTACKING = 84;
public const DATA_FLAG_AVOIDING_MOBS = 85;
//86 used by RangedAttackGoal
//87 used by NearestAttackableTargetGoal
public const DATA_FLAG_AVOIDING_BLOCK = 86;
public const DATA_FLAG_FACING_TARGET_TO_RANGE_ATTACK = 87;
public const DATA_FLAG_HIDDEN_WHEN_INVISIBLE = 88; //??????????????????
public const DATA_FLAG_IS_IN_UI = 89;
public const DATA_FLAG_STALKING = 90;
public const DATA_FLAG_EMOTING = 91;
public const DATA_FLAG_CELEBRATING = 92;
public const DATA_FLAG_ADMIRING = 93;
public const DATA_FLAG_CELEBRATING_SPECIAL = 94;
public const DATA_PLAYER_FLAG_SLEEP = 1;
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
@ -1164,6 +1171,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($teleport){
$pk->flags |= MoveActorAbsolutePacket::FLAG_TELEPORT;
}
if($this->onGround){
$pk->flags |= MoveActorAbsolutePacket::FLAG_GROUND;
}
$this->level->broadcastPacketToViewers($this, $pk);
}
@ -1743,7 +1753,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
if($pos instanceof Position and $pos->level !== null and $pos->level !== $this->level){
if(!$this->switchLevel($pos->getLevel())){
if(!$this->switchLevel($pos->getLevelNonNull())){
return false;
}
}
@ -1849,7 +1859,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$pitch = $pitch ?? $pos->pitch;
}
$from = Position::fromObject($this, $this->level);
$to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevel() : $this->level);
$to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevelNonNull() : $this->level);
$ev = new EntityTeleportEvent($this, $from, $to);
$ev->call();
if($ev->isCancelled()){
@ -1861,7 +1871,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->setMotion($this->temporalVector->setComponents(0, 0, 0));
if($this->setPositionAndRotation($pos, $yaw ?? $this->yaw, $pitch ?? $this->pitch)){
$this->resetFallDistance();
$this->onGround = true;
$this->setForceMovementUpdate();
$this->updateMovement(true);
@ -1914,7 +1924,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
protected function sendSpawnPacket(Player $player) : void{
$pk = new AddActorPacket();
$pk->entityRuntimeId = $this->getId();
$pk->type = static::NETWORK_ID;
$pk->type = AddActorPacket::LEGACY_ID_MAP_BC[static::NETWORK_ID];
$pk->position = $this->asVector3();
$pk->motion = $this->getMotion();
$pk->yaw = $this->yaw;
@ -1927,7 +1937,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
public function spawnTo(Player $player) : void{
if(!isset($this->hasSpawned[$player->getLoaderId()]) and $this->chunk !== null and isset($player->usedChunks[Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())])){
if(
!isset($this->hasSpawned[$player->getLoaderId()]) and
$this->chunk !== null and
isset($player->usedChunks[$chunkHash = Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())]) and
$player->usedChunks[$chunkHash] === true
){
$this->hasSpawned[$player->getLoaderId()] = $player;
$this->sendSpawnPacket($player);

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