Compare commits

...

401 Commits

Author SHA1 Message Date
1ff6f8846e disable dev flag 2018-08-04 16:30:23 +01:00
87f458f9bd AsyncPool: remove now-unnecessary isTerminated() call 2018-08-03 20:07:37 +01:00
5a7e575c3a AsyncPool: isCrashed() now returns true when a fatal error occurred
the fix for chunks earlier didn't fix...
2018-08-03 20:06:41 +01:00
d6d98183ea MainLogger: Log messages and exception traces in a synchronized block
this ensures that stack traces are emitted coherently without messages from other threads landing in the middle.
2018-08-03 18:50:06 +01:00
9ff5c65fb6 Level: Make async chunk sending aware of faults
Previously any random error could occur during an AsyncTask preparing a chunk, and the Level would never know about it and thus never send the chunk.

I don't know how many invisible-chunk bug cases this fixes, but I expect it's quite a lot.
2018-08-03 18:23:32 +01:00
1532b0ef6d Level: Remove chunks from chunk send queue on unload
When a chunk request task crashes, these can get stuck and never get removed. This allows using /gc to collect the bad chunk in order to fix the bug.
2018-08-03 18:04:56 +01:00
9ece971a2b Server: remove useless check from exceptionHandler()
this cannot be null... @shoghicp y u litter the code with these useless checks ???
2018-08-02 14:41:28 +01:00
5546c88f88 Server: Fixed parse errors getting reported to CA
this changed to throwing errors as of PHP 7
2018-08-02 14:40:36 +01:00
e8c7ae595d back to dev 2018-07-30 15:08:32 +01:00
0d9f40873f disable dev flag 2018-07-30 14:57:51 +01:00
a4aee98cba TimingsCommand: some code cleanup 2018-07-30 14:53:10 +01:00
a97c7d3132 Fix for timings 2018-07-30 14:42:16 +01:00
4a1ed21e52 PluginManager: Fix patch level check to allow loading the plugin when the server's minor level is higher than the plugins declared minor level. 2018-07-27 11:46:24 +01:00
1b053c7928 Clean up pointless checks in Thread/Worker 2018-07-26 14:20:55 +01:00
c684f99cc4 Clean up Thread/Worker quit() 2018-07-26 14:17:01 +01:00
695793795e PluginManager: Remove dead $pluginParentTimer left over from 9e4d88a852 2018-07-26 10:25:01 +01:00
a4965842d6 Remove $handlerList from PlayerExperienceChangeEvent 2018-07-25 15:30:01 +01:00
1ef6f5d166 ZippedResourcePack: Make manifest parse errors less useless 2018-07-21 09:53:16 +01:00
eccc249009 KillCommand: clean up old shitcode 2018-07-20 19:44:41 +01:00
dbaf7287bc back to dev 2018-07-20 12:20:24 +01:00
b3b240e25b disable dev flag 2018-07-20 12:05:14 +01:00
2b30ef1671 Revert "Living: fix knockback condition, take 2"
This reverts commit 0081e30a89.

The logic introduced by this commit is correct in MC JAVA 1.9+. Unfortunately, nobody likes 1.9+ for combat.
Some testing in MCPE vanilla made it apparent that this logic isn't correct for MCPE. The old logic is correct for pre-1.9 knockback.
2018-07-20 11:55:10 +01:00
124ebf69c5 PlayStatusPacket: default to current protocol if not specified 2018-07-20 11:29:40 +01:00
4274640845 Player: fixed on-ground state not being updated when walking horizontally
it's possible to walk off a tower while flying without moving vertically, and this code previously wouldn't detect that, leaving a gaping hole in the anti-cheat.
2018-07-18 15:14:18 +01:00
58b665985e back to dev 2018-07-17 18:09:24 +01:00
0f5c48e342 Disable dev flag for release 2018-07-17 16:59:00 +01:00
08ad5db05b Config: remove useless switch cases
CNF is the same type as PROPERTIES (it's an alias) so these cases are useless.
2018-07-17 16:56:47 +01:00
94e8623c75 Server: account for default provider being missing 2018-07-17 12:14:26 +01:00
921f7e8f6a Level: remove useless check from populateChunk()
this is already checked at the top of the function.
2018-07-16 17:36:36 +01:00
710e1d014d Entity: fixed 0-length motion vectors being passed to move()
this was an interesting bug.

This was discovered by making a projectile's drag 0, making its gravity a factor of its throw force (such that force / gravity = integer value), and then throwing it directly up. At the apex, an error would occur due to trying to do a ray trace with a zero vector.

This also led me to realize that there's an edge case in the current movement system - if an entity's motion reaches 0, it will stop getting movement updates. This can be undesirable when things such as gravity cause motion to become zero when throwing a projectile directly upwards. This will need to be fixed separately.
2018-07-16 12:08:13 +01:00
7fc22d3227 Entity: fixed setNameTagAlwaysVisible()
mojang >.<

this doesn't fix the problem of invisibility making nametags hidden though.
2018-07-14 16:05:46 +01:00
7bfe487ee5 ConcretePowder: fixed a missed usage of Block::get() 2018-07-14 10:35:05 +01:00
d8cf835f92 BlockFactory: better handling for dodgy IDs
I thought I'd already dealt with this, but it seems not.
2018-07-13 12:31:22 +01:00
65e44364e5 Added some debug for raw packets and Query handling 2018-07-13 10:07:11 +01:00
ebbbc581ca Player: clean up cursor inventory when closing main inventory 2018-07-12 17:52:22 +01:00
8aa8280a63 Level: Make spawn protection always active regardless of op count (#2290)
I don't care if this matches PC behaviour or not. bugs.mojang.com is full of bug reports about this. Just search for "minecraft spawn protection not working" and you'll see what I mean.

If you want to disable spawn protection, actually disable it. This behaviour is something that most users are not aware of and find astonishing when they discover it.

This behaviour was copied from Minecraft PC, and it's nearly as unexpected there as it is here.

This commit reverses the stupidity done in eb0525e892.
2018-07-12 17:25:05 +01:00
6a637d9099 update pthreads version for travis 2018-07-12 17:23:52 +01:00
06b80a9536 Level: Make getSafeSpawn() account for non-generated chunks
fixes #2295

There is still an issue in that the spawn point will not be offset if the chunk is not generated, but this is better than the spawn point being down at y=0. The other issue is a job for another time.
2018-07-11 10:17:59 +01:00
b3ffce9729 back to dev 2018-07-11 09:14:38 +01:00
ce9f18c6b4 disable dev flag 2018-07-10 17:38:40 +01:00
9610c55b19 PluginManager: Skip methods not declared by instanceof Listener when registering handlers (#2293)
This is quite an interesting bug. If you have
```php
class A{
    public function onMove(PlayerMoveEvent $event){} //shouldn't be a handler because this class isn't a Listener
}

class B extends A implements Listener{}
```
then
```php
registerEvents(new B, $plugin);
```

then `A::onMove()` will be registered as an event handler even though `A` is not an instanceof `Listener`.

This was observed by noting that plugins which do something like `extends PluginBase implements Listener` causes `registerEvents()` to try and register `PluginBase` methods as event handlers, which could lead to astonishing behaviour.


then A::onMove() will be registered as an event handler even though A is not an instanceof Listener.

This was observed by noting that plugins which do something like "extends PluginBase implements Listener" causes registerEvents() to try and register PluginBase methods as event handlers, which could lead to astonishing behaviour.
2018-07-10 16:59:33 +01:00
b01b477a2a Properly fixed newline issues when parsing doc comments
fixes #2110 properly

fixed @notHandler and such not being detected when CRLF is used
2018-07-10 12:46:20 +01:00
2d454ae56f PluginManager: fixed bug in YML commands permission type checking 2018-07-08 16:19:46 +01:00
066c9d4fd4 PluginManager: simplify isPluginEnabled() 2018-07-08 16:16:39 +01:00
23829952c3 PermissibleBase: removed nonsensical code
it's not possible for this to be null, unless a child class doesn't call the constructor, and anything could break in that case anyway.
2018-07-08 13:04:51 +01:00
7ee98ff139 Config: fixed whitespace between key and = being invalid
it tolerates whitespace everywhere except here already ^.^
2018-07-08 11:54:06 +01:00
f1cab91ac9 Config: fixed interpreting invalid keys as empty strings
these should just be ignored completely.
2018-07-08 11:50:17 +01:00
e0bc9c5e96 back to dev 2018-07-07 19:20:55 +01:00
70caa00266 disable dev flag for release 2018-07-06 12:59:02 +01:00
ee7c838040 LoginPacket: barf on finding extraData multiple times
this fixes a potential exploit where clients could append JWTs signed with their own keys to the end of the chain containing fake XUID/UUID/username which would then overwrite the legitimate ones in earlier links.
This stems from the fact that the final link of the vanilla chain contains the client's own pubkey, so the client is able to append its own data to the end of the chain.
2018-07-06 12:54:43 +01:00
34e9e93210 PluginBase: fixed crashing on getConfig() when data dir doesn't exist
I considered making this instead save the default config instead of creating an empty config file, but that would be (albeit minor) a behavioural change which therefore belongs in 3.1.
2018-07-05 19:59:08 +01:00
5dbb0d177e Fixed double chest inventory desync issues, closes #2261 (#2279)
chest pairing really needs rewriting... this code really sucks
2018-07-05 17:42:30 +01:00
58f0ad3e3e Command: remove unnecessary getPermission() calls 2018-07-05 10:38:31 +01:00
0df3585c81 TellCommand: remove useless strtolower() and temp variable 2018-07-05 09:12:21 +01:00
697723b551 DoubleChestInventory: remove redundant clear() override
this calls setItem() which deals with the necessary logic anyway.
2018-07-04 20:06:42 +01:00
5926d80525 DoubleChestInventory: fixed wrong logic for setting items into the right-hand side 2018-07-04 20:04:40 +01:00
a57ec1b1ba Living: fixed death animation not being played when kill() is used
this fixes players having a random delayed despawn when using /kill on themselves
2018-06-29 16:49:40 +01:00
905259a4e1 Fixed not being able to place blocks inside dead players
closes #2265
2018-06-29 16:38:35 +01:00
ca6930006c back to dev 2018-06-29 12:30:08 +01:00
33eeeb856e disable dev flag 2018-06-29 12:21:56 +01:00
c43ce5c8fa RCONInstance: apply stfu operator 2018-06-29 12:16:17 +01:00
57cfe9fd43 Level: fixed logic for sending changed blocks to players
If there is an empty list of blocks in the changedBlocks array for a chunk, that means that blocks changed the normal way and then were later set the direct way in the same tick. This means that no action needs to be taken on these chunks.
2018-06-29 11:10:31 +01:00
d8824e7ee1 Level: discard changed blocks on chunk replace
this could cause issues when plugins replace chunks when blocks in the chunk have been changed on the same tick.
2018-06-29 11:06:33 +01:00
3455d0f3b9 Level: cleaned up some nonsensical code in setChunk() 2018-06-29 10:58:31 +01:00
6b2250cbce RCONInstance: terminate session on ECONNRESET errors 2018-06-24 17:32:51 +01:00
8dae497610 back to dev 2018-06-24 17:32:51 +01:00
cade15e2dd disable dev flag for release 2018-06-24 16:34:19 +01:00
d3e54db146 ExperienceOrb: stop tracking targets if they die while being tracked 2018-06-23 16:41:21 +01:00
0081e30a89 Living: fix knockback condition, take 2
onGround doesn't necessarily reflect 0 motion, because something else could change the motion prior to the onGround flag getting updated - for example 2 knockbacks in a row.
2018-06-23 14:30:26 +01:00
76174f1920 Explosion: avoid leaving arrows stuck in nonexistent blocks 2018-06-23 13:03:46 +01:00
dd6b5902a6 EmeraldOre: fixed not dropping XP on break 2018-06-22 21:35:58 +01:00
87852f2fe1 EmeraldOre: remove excess indentation 2018-06-22 21:31:22 +01:00
056d24c67d Add MUTTON as an ID constant
fixes crashdump #518862 - Unable to resolve "minecraft:mutton" to a valid item

PC refers to these as just mutton, but PE calls them muttonraw
2018-06-22 19:39:18 +01:00
484d34fe04 Living: Reset attack cooldown before applying post damage effects
this fixes things causing damage during post-damage calls coming back and being able to do even more damage
2018-06-22 17:47:11 +01:00
6c6630d845 Player: avoid doing some post-melee attack actions if attacking killed the attacker
This can happen when an attacker attacks a victim wearing thorns armour while having low health, which prior to this commit would cause the tool to be duplicated.
2018-06-22 17:17:40 +01:00
a5a236084f Living: don't applyPostDamageEffects() for dead mobs
this has already been seen to cause duplication bugs when thorns is used. Anything else that modifies inventory during applyPostDamageEffects() when the mob is possibly dead will also cause duplication issues.
2018-06-22 14:31:48 +01:00
641a5a5e23 fixed damaged anvils dropping the wrong items
they changed this in 1.2.13 to use regular masks instead of bitshifts. The item was fixed, but not the block.
2018-06-22 09:57:39 +01:00
ebacb8525f SignPost: fixed possible field read on null 2018-06-22 09:12:48 +01:00
579ab5866b Versions again 2018-06-22 09:12:48 +01:00
56b04fa0bb disable dev flag 2018-06-21 12:55:36 +01:00
95787c2be9 TaskScheduler: fixed tasks not getting destroyed when all tasks in scheduler are cancelled before next heartbeat
they got removed from the tasks array, but not from the queue (for performance reasons). The queue gets cleaned up by the heartbeat, but it was checking if there were things in the main array, not in the queue.

There are a couple of other bugs with cancelling tasks that this doesn't fix that are rather more complicated to deal with.
2018-06-21 12:49:08 +01:00
7b7be9618c PluginBase: fixed plugin task timings showing "Unknown" for plugin name 2018-06-21 12:05:30 +01:00
4a8232d591 MemoryManager: Fixed parent private properties not being visible in memory dumps 2018-06-21 12:03:05 +01:00
40e5a1aacb versions again 2018-06-21 12:03:05 +01:00
dbda044229 bump version 2018-06-19 18:34:19 +01:00
d2a037de71 InventoryTransaction: Avoid crashing when trying to compact slot changes with invalid slot numbers
fixes #2250
2018-06-19 18:31:13 +01:00
03510333dc Disable dev flag for 3.0.0 release 2018-06-18 19:46:23 +01:00
064e9464bc Removed --enable-profiler CLI option
krakjoe's profiler hasn't been updated since 2013. I doubt it's going to get updated in the near future. These days we use xdebug, which is either always on or always off.
2018-06-18 12:27:51 +01:00
2d3ce9e8b0 Remove some fully qualified function calls
PhpStorm can't see these or understand how they are being called, which is very annoying for bug hunting. Additionally, we already have the CodeOptimizer for this.
2018-06-18 12:23:19 +01:00
49f80830a7 Clean up unused imports 2018-06-18 12:10:27 +01:00
80daaf09b2 SendUsageTask: fixed oops x2 (thanks PhpStorm)
looks like this file was somehow missed during the refactor.
2018-06-18 12:08:55 +01:00
30ad3a1705 RCONInstance: inherit runtime-defined INI entries 2018-06-18 10:32:17 +01:00
4e7d1a7947 fixed doxyfile 2018-06-17 19:59:45 +01:00
da6439e3f4 SendUsageTask: fixed oops 2018-06-17 16:13:05 +01:00
5f2d4c36c0 Updated Language submodule 2018-06-17 13:04:34 +01:00
1f9bed275a Merged server and API version 2018-06-17 12:54:18 +01:00
77f3ca4d47 PluginManager: make isCompatibleApi() a bit less sub optimal 2018-06-17 11:13:48 +01:00
d88368ceb6 BlockBreakEvent: rename inconsistently-named function 2018-06-16 19:17:13 +01:00
f77a829a52 LevelDB: remove redundant check
Assume that entities on a chunk are not closed... none of the other providers check this
2018-06-16 18:00:26 +01:00
f315aca4c3 Tile: make saveNBT() return a CompoundTag
I don't know why I made it take a parameter for this...
2018-06-16 17:59:41 +01:00
9f7f62e9e5 Don't use Config for builtin JSON resources
it doesn't make sense to use a config in these cases, and also it just hides problems when the files are missing.
2018-06-16 16:35:47 +01:00
cc97f76ec9 Added plugin_data folder to .gitignore (#2243) 2018-06-15 12:49:08 +01:00
3b0aad38cf PocketMine.php: Stop telling noobs to use an installer that doesn't exist 2018-06-15 09:57:08 +01:00
a9b7cd1699 VersionString: Always consider dev builds less than release builds 2018-06-14 13:12:18 +01:00
37b65aac91 AutoUpdater: Use update info for build number instead of trying to parse the version number
the version number might not include the build number, particularly for releases.
2018-06-13 18:19:17 +01:00
ad7787e13b PluginLoader: fixed access protocol, updated devtools 2018-06-13 17:22:29 +01:00
7b0ce16b12 Fixed buckets being able to delete any block 2018-06-13 17:05:51 +01:00
0ff6b7b572 PluginManager: Track enabled plugins in a separate array 2018-06-13 16:54:04 +01:00
763e20ba4e Server: prevent stupidity with settings.async-workers 2018-06-13 16:44:40 +01:00
4b99285fd6 PocketMine.php: remove unused function 2018-06-13 16:33:48 +01:00
03a55d5e9d PocketMine.php: fixed pthreads version 2018-06-13 16:27:02 +01:00
fe29b89fd1 Store plugin data in <data path>/plugin_data in new installations
This will preserve the old behaviour for existing installations.
2018-06-13 12:57:41 +01:00
b0780c4d1d VersionString: Always show the patch version 2018-06-13 12:17:11 +01:00
c835c97aba Fixed phar plugins not reading resources correctly 2018-06-13 12:07:27 +01:00
78eae28a3e Updated dependencies to release versions 2018-06-13 11:17:07 +01:00
31c187f366 Bed: fixed recursion when setting state to something it already is
fixes #2236
2018-06-13 04:16:52 -04:00
2e6afa54c2 [ci skip] typo 2018-06-12 21:20:00 +01:00
e36a6dc8cc Stop RakLib spamming the console when php.ini has a bad timezone 2018-06-12 21:18:33 +01:00
2e9e44ba05 Moved JSON blobs to submodule 2018-06-12 18:49:18 +01:00
c9ec6f0a63 Remove JSON vanilla blobs in preparation for move to submodule 2018-06-12 18:45:37 +01:00
60836ebec9 Updated resources json to use base64 instead of hex 2018-06-12 18:32:26 +01:00
3def3cd502 Item: use base64 instead of hex for json encoding NBT 2018-06-12 18:31:35 +01:00
b5da6b1591 Added a pocketmine.yml kill switch for development builds 2018-06-12 17:11:01 +01:00
8a9af7bf2f Update and sync extension dependencies 2018-06-12 16:27:52 +01:00
8cfd5604cf PocketMine.php: remove 1.3 legacy things 2018-06-12 14:35:11 +01:00
f51743765d Start console reader in a more sensible place 2018-06-12 11:26:34 +01:00
0b9ce8a0d4 Remove server codename
This has long been a waste of time and creativity, and that's only going to continue to be the case now that we're going to be properly versioning. New codenames every couple of months is not worth the bother.
2018-06-12 10:51:49 +01:00
c3c360f589 Bump API to 3.0.0
This may raise a few eyebrows.

Does this mean that all the things that were planned for API 3.0.0 are done?
Not at all. The plans laid out in December 2016 for API 3.0.0 were far too ambitious, and as a result the ALPHA series dragged out forever (18 months now). This is a break away from those plans, to bring development and release flow back to some sort of sanity.

Does this mean that my plugins will stop breaking all the time now?
No, it does not! Development isn't going to stop, although breaking changes will be confined only to major API releases. It's anticipated that the major API version will be bumped as nearly as often as the ALPHA version was during the last 18 months. The reason for this return to 3.0.0 is to allow us to fine-tune our release flow so that plugin developers can get advantage of newer, non-breaking API features without having to bump the API in a breaking way.

What are the criteria for the API versions now?
- Major: This will be bumped for breaking changes, changes which will break plugins. it's expected that we'll roll out new major versions regularly.
- Minor: This will be bumped when non-breaking feature additions are made, such that API 3.0.0 plugins would still load on 3.1.0. However, plugins requiring 3.1.0 will not run on 3.0.0.
- Patch: This will be bumped for bugfix updates. Plugins requiring 3.0.0 will work on 3.0.1, but not vice versa.

TL;DR: This insanity has gone on far too long.
2018-06-12 10:39:49 +01:00
5a55d434ab Nuke plugin loaders from orbit
This features a near-total rewrite of PluginLoaders and some code associated with them.

Highlights:
- PluginManager->registerInterface() does not return anything, and now accepts a PluginLoader instance instead of a string.
- PluginLoader itself is drastically simplified. getPluginFilters(), enablePlugin() and disablePlugin() are now removed. loadPlugin() responsibilities are now solely confined to doing whatever is necessary to make the plugin's classes visible by the server, and does not emit log messages or check for data directories.
- PluginBase->init() and PluginBase->isInitialized() have been removed.
- Plugin interface now declares a signature for the constructor which implementations must comply with.
- Plugin interface now declares setEnabled().
2018-06-12 10:23:49 +01:00
19d2d6b91c PluginManager: use null coalesce in getPermission() 2018-06-11 16:38:27 +01:00
ff2e99ecdc McRegion: nbtDeserialize() cannot return null 2018-06-11 15:05:38 +01:00
07a156f5c4 BaseLevelProvider: remove leftover from b54197904d 2018-06-11 14:32:42 +01:00
13fe8ee96d PluginDescription: fix precedence issue with ?? and cast
this would cause issues if the api field was null or not found.
2018-06-11 13:53:49 +01:00
eb0276d459 Apply nullable and void typehints to events namespace 2018-06-11 13:49:16 +01:00
cfb10360ff Furnace: remove trailing whitespace 2018-06-11 13:31:05 +01:00
05af87e1d4 Strip empty lines at the end of classes 2018-06-11 13:19:23 +01:00
b3ea9606c7 Server: fixed doc comment for shutdown() 2018-06-11 12:32:38 +01:00
a080a9b75c Player: remove redundant parameter from checkNearEntities() 2018-06-11 12:25:01 +01:00
137a05c418 Player: remove useless return value from orderChunks() 2018-06-11 12:22:46 +01:00
245f5c6bef Player: remove obsolete TODO 2018-06-11 12:20:11 +01:00
3be6665e3d AsyncWorker IDs now start from 0 instead of 1
this now matches their IDs in the AsyncWorker pool, as used by submitTask().
2018-06-11 11:58:24 +01:00
8704d378d4 PluginBase::getResources() returns associative array (#2193)
Now, it has string keys, which is the path of the resource file relative to the resources folder.
2018-06-11 11:06:54 +01:00
b9718f9e87 Remove redundant second parameters for BlockFactory::get() 2018-06-11 11:03:23 +01:00
1d1e6966a2 Biome: remove useless abstract class 2018-06-11 11:00:33 +01:00
610b7bd8b0 RiverBiome: don't generate grass underwater 2018-06-11 10:58:30 +01:00
17607b8116 AsyncPool: added some void typehints 2018-06-11 10:41:40 +01:00
4c98d9d3ad AsyncPool: add a type doc to $size 2018-06-11 10:40:01 +01:00
7d5b3079bc AsyncPool: more documentation 2018-06-11 10:38:51 +01:00
88d83e0fca AsyncPool: Lazy-start AsyncWorkers when they are needed only
This changes how the AsyncPool works so that it does not immediately always start all of the workers in the pool.
Instead, workers will be started only when an idle worker was not found.
This allows for significant memory footprint reductions while idle.

In effect the async-workers setting in pocketmine.yml now dictates a _maximum_ pool size, not a fixed pool size.
2018-06-11 10:23:46 +01:00
4b221c0601 OceanBiome: don't inherit from GrassyBiome, change ground cover to gravel 2018-06-11 09:58:48 +01:00
e867427f71 Leaves: remove useless $check parameter
this is only written to and never read from.
2018-06-10 17:20:34 +01:00
c4c6c58615 Added some missing typehints 2018-06-10 17:18:55 +01:00
89643ff9af MainLogger: Added getFormat() and setFormat()
this allows plugins to easily alter the output of the console logger.
2018-06-10 10:49:12 +01:00
9657d50aeb MainLogger: workaround --disable-ansi not being respected on other threads 2018-06-10 10:24:49 +01:00
3725bea3e5 AsyncPool: Slightly reduce worker memory usage with more conservative start options
this results in a memory footprint reduction of maybe 4MB for a total of 8 workers. Not much, but it's something.
2018-06-10 10:18:07 +01:00
f3a84b332b Updated RakLib and SPL dependencies, allow any 0.3.x SPL 2018-06-09 20:13:27 +01:00
f6481eab8f Implemented an InventoryEventProcessor, fixes #1986 (#2176)
* Implemented InventoryEventProcessor, fixes #1986
Event processors can now be registered and unregistered at will. Entity inventory/armor change events are now handled by event processors instead of the inventories themselves, which allows enabling/disabling the calling of these events at will.
This now avoids stupid things happening when initializing inventory contents, since the callers for those events are now registered _after_ the contents are initialized.
2018-06-09 17:37:10 +01:00
8e5aca70b4 Entity: Avoid using close() during initEntity()
this simply conceals bugs and will cause astonishing behaviour.
2018-06-09 17:06:15 +01:00
85136b7b4a Entity: Attach to level & chunk after initEntity() call, not before
this avoids leaving garbage entities attached to chunks when exceptions are thrown during initEntity().
2018-06-09 16:59:22 +01:00
47742d74c8 Player: Drop load queue on level switch
This ensures that the player doesn't request bad chunks if it was teleported from an invalid level.
2018-06-09 16:21:41 +01:00
ca54c8d78e Merge branch 'master' of https://github.com/pmmp/pocketmine-mp 2018-06-09 16:21:01 +01:00
601811f0f8 Player: Account for the possibility of Level being null in switchLevel()
the Entity base also accounts for this, and assuming that this is automatically valid is causing lots of crashes. I am not sure of the circumstances under which this is suddenly becoming null, but this shouldn't assume that the level is valid nonetheless.
2018-06-09 16:20:51 +01:00
aeb551b317 Remove double use of spl_object_hash (#2226) 2018-06-09 14:15:50 +01:00
37b445f210 Updated Math dependency 2018-06-09 13:05:25 +01:00
d04991feb6 Level: Avoid chunk sending bugs caused by duplicate chunks with wrong coordinates
If the same chunk is set into multiple different places in the world, the chunk's position is no longer able to be relied on, because it will have the position of the last place it was set. This results in chunks not getting sent correctly when the same chunk is set in multiple places.

This avoids the bug by using known valid coordinates (using chunk hashes) to establish the real coordinates, and also adds an assert to notify developers should they unintentionally set a duplicate chunk by mistake.
2018-06-09 11:25:45 +01:00
c327b3d2c4 AsyncPool: be less dependent on Server in the code
The goal is to remove the Server things from here completely.
2018-06-08 20:08:23 +01:00
af69418a55 PlainBiome: reduce maximum elevation 2018-06-08 13:04:45 +01:00
8cd311bcb4 GroundCover: don't replace liquid with can-be-flowed-into blocks
fixes snow layers generating underwater
2018-06-08 11:15:19 +01:00
78ec3937bf BiomeSelector: drastically simplified implementation and made more robust
- Doesn't need to be pre-populated with biomes prior to calculating the heatmap - now population of biomes is entirely dependent on the lookup function, improving consistency
- Uses an abstract class method for lookup instead of callback (use anonymous class instead)
- Faster because the heatmap is directly populated with biomes instead of biome IDs, removing an unnecessary lookup.
2018-06-08 10:19:08 +01:00
4e3e807741 Biome: Added UnknownBiome class to avoid astonishing behaviour on unknown biomes 2018-06-08 10:19:08 +01:00
8c6161a4f2 Biome: make biomes list use an SplFixedArray 2018-06-08 10:19:08 +01:00
c8a87b14d5 PopulationTask: actually fix locks not getting released in generation errors 2018-06-07 23:53:59 +01:00
8fca7cc68d Apply some typehints to generators 2018-06-07 20:22:35 +01:00
45f940681a Make Normal->pickBiome() private 2018-06-07 20:21:26 +01:00
e3c97d7d5e Flat: clean up more garbage 2018-06-07 20:18:04 +01:00
172abef2a7 Flat: clean up generation of base chunk 2018-06-07 20:11:29 +01:00
709abb02e6 Generator: remove more dead incomplete code
again, this can be added when and if it's actually implemented.
2018-06-07 19:54:51 +01:00
428ca29e4b Remove dead bedrockDepth fields from generator
yes, this is not implemented yet. fields for this can be added when proper bedrock generation is implemented. For now, it's just dead code.
2018-06-07 19:52:56 +01:00
f61ad20f6b Remove a whole bunch of dead copy-pasted code from Nether generator 2018-06-07 19:51:27 +01:00
3c9af5cd6d Generator: small reduction of code duplication 2018-06-07 19:49:51 +01:00
996935e9b2 Cleaned up level seed handling 2018-06-07 19:39:24 +01:00
3707a41b67 Server: small cleanup to seed handling 2018-06-07 18:25:44 +01:00
354b2dc5d1 and an extra note just in case 2018-06-07 15:20:51 +01:00
0c70b83d81 LevelProvider: workaround bug fixed in b54197904d for previously-affected worlds 2018-06-07 15:18:42 +01:00
083a1e1ff6 GeneratorManager: Make addGenerator() throw exceptions instead of returning false 2018-06-07 14:34:26 +01:00
17b58357fb GeneratorManager: add documentation for functions 2018-06-07 14:30:19 +01:00
96a4dbb7d8 GeneratorManager: add typehints 2018-06-07 14:24:01 +01:00
5eec683110 Generator: Moved generator registering things to a separate GeneratorManager class
this isolates the concerns of the Generator class, and also removes cyclic dependencies between the Generator class and its descendents.
2018-06-07 13:48:01 +01:00
0bca3cd481 Generator: Move static noise functions to Noise instance methods 2018-06-07 13:40:10 +01:00
b54197904d Fixed imported worlds getting PM classpaths written into their level.dat for generator
This will not fix existing worlds affected by this bug.
2018-06-07 13:20:50 +01:00
fb484087a8 Entity: Remove unnecessary scheduleUpdate() call from initEntity()
this is already done by the constructor
2018-06-07 12:53:59 +01:00
14914781fc Added exception throws when a closed tile or entity attempts to schedule itself for updating
I can't believe it took so long to find what was causing these stdClass bugs.
2018-06-07 12:37:26 +01:00
fdd5b7b9c9 Entity: Fixed despawn flagging not kicking in until scheduling updates on entities 2018-06-07 12:34:23 +01:00
c83c0eb935 Entity: Move responsibility of checking for despawn to Level
this ensures that flagging for despawn will always work as intended, including when onUpdate() is overridden.
2018-06-07 12:31:21 +01:00
b331f8e1c9 AsyncWorker: added removeFromThreadStore()
and use it instead of overwriting with null things, which still occupies memory
2018-06-07 10:12:50 +01:00
ee787974f2 AsyncWorker: Use statics for thread-local worker storage instead of globals 2018-06-07 09:59:32 +01:00
73e56c8a36 Utils: Make kill() use static MainLogger instead of global variable 2018-06-07 09:29:53 +01:00
5f7c884255 Reduce AsyncWorker default memory limit to 256MB
a worker shouldn't be using as much memory as the main server thread would, so 1024MB is extremely excessive.
2018-06-06 18:49:48 +01:00
2b5e6b790f BanList: rename poorly-named parameter 2018-06-06 18:18:06 +01:00
1a21041d00 AsyncPool: Parameterize worker memory limit instead of calling back into Server 2018-06-06 18:00:56 +01:00
7b17a83227 Level: fix chunk locks not getting released on generation errors 2018-06-06 11:42:29 +01:00
edd150971e CommandReader: Use statics for thread-local storage instead of globals 2018-06-06 10:06:52 +01:00
38f4afb17c Make sure that test failures kill the build 2018-06-05 19:49:22 +01:00
9d16863b1a Convert some TesterPlugin tests into PHPUnit tests, add PHPUnit configuration 2018-06-05 19:33:21 +01:00
41a179e6e1 BlockFactory: fix bug in light filters which could result in negative light levels (and therefore wraparounds) 2018-06-05 18:12:45 +01:00
3a31c531af Level: remove unused import 2018-06-05 13:04:24 +01:00
e081b7dffa PermissibleBase: fix typehints of calculateChildPermissions() 2018-06-05 11:14:36 +01:00
0233ae1eb6 Updated TesterPlugin submodule 2018-06-04 21:17:48 +01:00
dce8ed9dd1 Eliminate more hard dependencies on MainLogger 2018-06-04 16:52:03 +01:00
35eaf38ca1 MemoryManager: parameterize an abstract \Logger to remove dependency on MainLogger 2018-06-04 13:30:48 +01:00
1d71f0cf43 AsyncWorker: added getLogger() 2018-06-04 13:29:38 +01:00
9644766df3 Merge pull request #2213: Scheduler API refactor, plugins now have their own schedulers
- Removed `Server->getScheduler()`. All plugins now have their own scheduler which is accessible using `Plugin->getScheduler()`. Aside from being syntactically more concise and pleasant, this also allows much more effective management of tasks when plugins are disabled.
- Removed `PluginTask` class. Before this PR it was necessary for plugin tasks to descend from `PluginTask` to ensure that the server could clean them up correctly on plugin disable. This is no longer necessary, so the `PluginTask` class has been removed. Plugins may now utilize the `Task` class as a base if they like.
- Added `Server->getAsyncPool()`. Since the global scheduler does not exist any more, it does not manage the server's `AsyncPool` any more. Additionally, `ServerScheduler` was previously bloated by a lot of `AsyncTask` related methods, which are now not necessary because direct access to `AsyncPool` is granted instead.

- `ServerScheduler`:
  - `ServerScheduler` has been renamed to `TaskScheduler` since it is now a general-purpose task scheduler which is non-dependent on the user. This allows much greater flexibility and also makes it possible to unit-test.
  - All `AsyncTask`/`AsyncPool` related methods have been removed - the task scheduler does not manage the async pool anymore.
    - Calls to `Server->getScheduler()->scheduleAsyncTask()` should be replaced with `Server->getAsyncPool()->submitTask()`.
    - Calls to `Server->getScheduler()->scheduleAsyncTaskToWorker()` should be replaced with and `Server->getAsyncPool()->submitTaskToWorker()`.

## Backwards compatibility
This poses significant backwards compatibility breaks for any plugins utilizing Tasks or AsyncTasks. These breaks are described above, along with basic upgrade steps. The upgrade process is quite straightforward.

## Follow-up
A large part of the goal with this pull request is to modularize these parts of the code so that they can be reused and also unit-tested. I would like to remove the existing test set from TesterPlugin at some stage when the AsyncPool can operate without a Server.

Because of the above, I am considering making further backwards incompatible changes directly to `AsyncTask` to remove the `Server` parameters from `onCompletion()` and `onProgressUpdate()`. These shouldn't be too difficult to upgrade from and can be prepared for in advance.
2018-06-04 11:54:43 +01:00
857f6dd5df Chunk: Share EmptySubChunk instance with other chunks on the same thread 2018-06-04 11:44:18 +01:00
0d177d5219 Entity: Clear dirty properties at the end of the constructor
this prevents sending duplicate properties immediately after entity spawn (due to properties getting set in the constructor).
2018-06-04 11:18:42 +01:00
fe21f0e916 Spawnable: change visibility of addAdditionalSpawnData() 2018-06-03 19:58:48 +01:00
4c1d29cdf7 Tile: remove unnecessary removeTag() calls
these CompoundTags are now ephemeral, so it's not necessary to remove potential garbage from them anymore.
2018-06-03 18:31:23 +01:00
fa21cd96c5 Tiles no longer store their NBT at runtime
This is a significant breaking change for anything utilizing Tiles as they now do not store their NBT at runtime anymore.
This is another step in the process of ridding PocketMine-MP of runtime NBT.

It can be noticed that all tiles are now using class fields to store properties instead of NBT, which is much faster, uses less memory and is also more concise when written in code.

Highlights:
- Tile->namedtag has been removed.
- Tile->saveNBT() now accepts a CompoundTag() parameter. Typically it's expected that this will be fed a newly-created CompoundTag (this part may be improved in the future).
- New internal methods Tile->readSaveData() and Tile->writeSaveData() have been added. Instead of overriding __construct() and saveNBT() to load and save properties from NBT, you should now implement these methods instead.

This is not final and will see further changes before it's done.
2018-06-03 18:29:08 +01:00
a22e5616f6 Fixed tile and furnace custom names not being visible
This is caused by the Spawnable constructor calling spawnToAll() before the tile is fully initialized. I really really really hate constructors that _DO_ things by themselves.
2018-06-03 17:02:00 +01:00
b6317fa7ce Sign: remove non-standard broken bad solution for editing
this never really worked properly and is a bad idea anyway. This was the cause of many sign-going-blank bugs before it was broken (by me), and since it's broken it's now useless. I don't think there's any practical way to make this work properly, so I leave it up to plugins to decide whether or not they want to cancel sign editing in protected areas. PocketMine-MP built-in spawn protection will already block this by default anyway.
2018-06-03 16:45:26 +01:00
b1cb63ebd6 Tile: make ContainerTrait and NameableTrait non-dependent on context-retained NBT 2018-06-03 16:32:05 +01:00
7b7917939a Cleaned up Container lock handling 2018-06-03 13:42:04 +01:00
6aaaaefd2f Make tiles less dependent on runtime NBT, use properties instead
This will ultimately culminate in the complete removal of runtime NBT, so plugins should also follow these steps if they have custom data.
2018-06-03 12:50:16 +01:00
1bb0337420 Tile: Improved Nameable and NameableTrait to cut down code duplication 2018-06-02 15:17:32 +01:00
b6b0bbde18 Level: remove nonsensical code from generateChunkCallback()
it's impossible for this to be null when we just set it to a Chunk object. It's assigned directly to the chunk index.
2018-06-01 19:47:47 +01:00
5d07f66d86 Travis: Limit worker count to 4
By default it starts 30 workers on Travis because there are 32 logical cores available. This is ridiculously excessive and pollutes the log with debug spam.
2018-06-01 18:58:30 +01:00
ec28612a12 BlockFactory: partial revert of 515e4aabc4
it's necessary to register these to fill the static property arrays. Someday these won't be necessary I hope...
2018-06-01 17:08:58 +01:00
6047810113 Level: remove useless null checks for fastDeserialize() returns 2018-06-01 17:02:29 +01:00
d535fe20a3 BlockFactory: split up static ID mapping registration from other things
this is actually HUGELY WASTEFUL on memory. An average of 3 MB is wasted per AsyncWorker on this.
2018-06-01 10:28:53 +01:00
515e4aabc4 BlockFactory: minor reduction in memory usage
removing useless array and don't pre-populate with UnknownBlock objects
2018-06-01 10:15:20 +01:00
f27c6fcf70 ResourcePack: slightly better handling of bad configs 2018-05-31 18:30:21 +01:00
7864a315f6 ResourcePackManager: cleaned up pack loading error handling 2018-05-31 17:58:47 +01:00
02b4eeeb9b TaskScheduler: Remove repeating tasks which throw exceptions 2018-05-31 14:10:59 +01:00
6b4b4e4bb1 TaskScheduler: remove redundant else branch 2018-05-31 13:01:06 +01:00
f2b8d6879f TaskScheduler: remove leftover code that makes no sense
this is impossible now that we have typehints
2018-05-31 12:52:29 +01:00
60212cef2f TaskScheduler: adjust disabled scheduler exception 2018-05-31 12:38:36 +01:00
15270f8329 Fixed plugin schedulers crashing after disable/reenable 2018-05-31 12:34:22 +01:00
05ef13b23a Merge branch 'master' into scheduler-nuke
# Conflicts:
#	src/pocketmine/level/Level.php
2018-05-31 10:40:10 +01:00
1b4723d816 AsyncWorker: don't require an attachable logger 2018-05-31 10:34:42 +01:00
c493d0e6ac Level: Stop unnecessarily initializing the generator on the main thread
this is just slowing down startup times for no good reason.
2018-05-31 10:22:44 +01:00
b3043f9552 Task: remove obsolete doc comment
not anymore sunshine
2018-05-30 21:30:43 +01:00
18fdbc2834 TaskScheduler: fix typo in isReady() parameter 2018-05-30 16:45:37 +01:00
a8c766be88 Remove TaskHandler dependency on MainLogger
This instead allows the exception to be caught by the scheduler and reported using its logger.
2018-05-30 16:00:25 +01:00
e20be3eeba Move task timings responsibility from scheduler to handler 2018-05-30 15:38:39 +01:00
51f43fb375 Removed global ServerScheduler - plugins now get their own isolated schedulers
This change breaks pretty much all API pertaining to synchronous task scheduling.

Significant changes:
- Server->getScheduler() has been removed
- Plugin->getScheduler() has been added - every plugin now has its own scheduler
- Because schedulers are now per-plugin, it is now unnecessary for PluginTask to exist because stopping plugin tasks on plugin disable is as simple as destroying the plugin's scheduler. Therefore PluginTask has now been removed and it is expected for things to now use the base Task class instead.

For the most part, plugins will simply need to change Plugin->getServer()->getScheduler()->... to Plugin->getScheduler()->...
Another highlight is that plugin tasks now no longer have global IDs - they are unique to each scheduler.
2018-05-30 14:11:11 +01:00
132746aa3d ServerScheduler: Require a Logger instance as ctor param, now non-dependent on Server
yay for unit-testing and reusability!!!
2018-05-30 12:29:19 +01:00
d03f36ebee First look at splitting up AsyncPool and ServerScheduler
This commit contains quite a few breaking changes with respect to how AsyncTasks are handled. This is necessary to allow separation of the ServerScheduler and the AsyncPool, because in the future the ServerScheduler may be removed and instead there will be isolated per-plugin sync-task schedulers - but we cannot have every plugin with its own worker pool for memory usage reasons if nothing else.

The following things have changed:
- ServerScheduler: scheduleAsyncTask(), scheduleAsyncTaskToWorker(), getAsyncTaskPoolSize(), increaseAsyncTaskPoolSize() and similar methods have all been removed. Additionally the static \$WORKERS field has been removed.
- Server: added API method getAsyncPool(). This grants you direct access to the server's AsyncPool. Calls to getScheduler()->scheduleAsyncTask() and scheduleAsyncTaskToWorker() should be replaced with getAsyncPool()->submitTask() and submitTaskToWorker() respectively.
2018-05-30 12:20:10 +01:00
7fce48d38c AsyncPool: Unstack tasks from workers before entering the below loop 2018-05-30 11:17:16 +01:00
b7ca045c51 updated TesterPlugin submodule 2018-05-29 20:11:29 +01:00
81957d133d AsyncTask: Rewrite how thread-local storage works, now non-dependent on Server or ServerScheduler
this implementation was god-awful bad and it was entirely avoidable to make it this complicated.

This utilizes the fact that pthreads treats static properties as thread-local. AsyncTask local storage now utilizes a \SplObjectStorage stored in an AsyncTask private static field.
2018-05-29 19:41:00 +01:00
299e4c8a85 ServerScheduler: remove nonsensical condition in addTask()
now that return type declarations exist, it's not possible for this to return a non-Plugin instance.
2018-05-29 18:17:49 +01:00
0a50b8cb9b Removed imports left over from 4f8f334436 2018-05-29 11:42:12 +01:00
6d53350291 Level: Moved LightPopulationTask out of generator namespace 2018-05-29 11:20:27 +01:00
ad61d70eee Level: added getBiome()
this will be needed for weather impl, amongst other things.
2018-05-29 11:10:06 +01:00
353a1d69db Level: move getHeightMap() to be more consistent 2018-05-29 11:10:06 +01:00
1d8b77f16e README: remove unnecessary and outdated section
Code dependencies can be seen in the code and in the composer.json file. Besides, there are additional third-party libraries used by PocketMine-MP now which are not listed in this section, and I don't plan to maintain it...
2018-05-29 10:42:59 +01:00
e3d2fa10a5 Item: added count parameter to pop()
this allows popping an arbitrary number of items from the stack, instead of just 1.
2018-05-29 10:21:04 +01:00
ad15ab5b42 EnchantCommand: use Item->isNull() instead of maths on ID 2018-05-28 19:45:37 +01:00
b003295d01 Player: reduce duplicated code in PlayerActionPacket handler 2018-05-28 19:38:29 +01:00
4f8f334436 Replaced usages of Math::floorFloat() with (int) floor() and Math::ceilFloat() with (int) ceil()
Once upon a time, these userland functions were faster than calling builtins, but not anymore. According to my test the Math functions are twice slower in PHP 7.2 with typehints and 50% slower without typehints.

Inlining is slightly faster than using builtins, but the difference is very small - not worth making the code look any more ugly than it does already.
2018-05-28 18:54:35 +01:00
71fdd59c4c Level: fine-tuned getCollisionCubes() a little bit
since we're getting the BBs anyway, what's the point in delegating this job to the block?
2018-05-28 17:41:12 +01:00
0a9ed059d6 Level: fixed logic of getCollisionBlocks() to match getCollisionCubes() 2018-05-28 17:40:05 +01:00
74c0863905 Living: fixed preventing effect expiry using events, closes #2208 2018-05-28 17:14:18 +01:00
87ff1c0382 Player: don't catch Throwable on transaction creation failure 2018-05-28 17:00:59 +01:00
2eaba7c936 Timings: added timings for Player->checkNearEntities() 2018-05-28 16:27:03 +01:00
3ee6bfca2a Updated Math dependency 2018-05-28 14:04:17 +01:00
63ab27550a Level: Use bitshifts instead of division in some cases 2018-05-28 14:02:32 +01:00
d612988882 Level: fixed more off-by-one errors
these are the same crap as the previous commit, but with whole chunks instead of blocks.
2018-05-28 14:00:00 +01:00
c9a0c381b1 Level: fixed logical errors in getCollisionBlocks() and getCollisionCubes()
This is the same bug that Entity->getBlocksAround() had, except this actually checks for BB intersections. What this means is that as a result of the bug, one extra layer of blocks is unnecessarily checked on the max sides of the BB.

For example:
Assume you have a BB with maxY -5.5
You're definitely colliding with block -6 (because you're inside it) and you want to check an extra block in case you hit something weird like a fence.
So you want to check _at most_ up to block -5 (inclusive).

Following this maths:
-5.5 + 1 = -4.5
ceil(-4.5) = -4

This causes us to check block -4 unnecessarily. This may be a performance waste - depending on the BB size it could be proportionally a lot of blocks getting unnecessarily checked. This has not been benchmarked.
2018-05-28 12:37:17 +01:00
982444949c Fixed typo in AsyncPool
asychronous -> asynchronous
2018-05-26 16:30:49 +01:00
8cf0fc63d8 Player: spawn at safe-spawn on level not found, instead of real spawn 2018-05-26 12:20:58 +01:00
c18ba38b74 Entity: avoid astonishing behaviour with motion vector modifications 2018-05-26 10:40:04 +01:00
3a1df1d99e LevelSoundEventPacket: added STOP_RECORD constant
this isn't actually a sound and as such doesn't have a stringy ID, so the script didn't see it.
2018-05-25 17:56:05 +01:00
8ccd13319c LevelSoundEventPacket: update constants
now with whitespace gaps generated because apparently some are missing... ugh!
2018-05-25 17:27:31 +01:00
c513d355cb Updated Snooze dependency 2018-05-25 13:52:12 +01:00
02b53785be Entity: added some new ID constants 2018-05-24 12:39:44 +01:00
9dd0ee7f05 Entity: replaced motion and lastMotion fields with vectors 2018-05-24 12:11:41 +01:00
595f1f58da Living: added knockback resistance attribute checks 2018-05-24 11:49:18 +01:00
509e8c5f6d ItemFactory: register some easy items
these are items that do nothing and/or are only used for crafting. As such they are simple to add.

Others will be added later on, but others require extra work and/or reverse engineering which I don't have time for now.
2018-05-23 19:48:30 +01:00
263cd900a8 Enchantment: update item type flags 2018-05-23 17:39:52 +01:00
164ce76ff5 Enchantment: apply typehints to PHP 7.2 standards 2018-05-23 17:28:40 +01:00
fbf760bafe Enchantment: implemented Vanishing 2018-05-23 17:23:56 +01:00
2c1afe5f2c ItemFactory: generate some TODOs 2018-05-23 13:42:19 +01:00
b109b457dc Item: added some new constants 2018-05-23 13:35:15 +01:00
e12e2897bb Enchantment: Add new constants
these aren't registered yet because they aren't implemented.
2018-05-23 12:34:58 +01:00
8f41384923 Item: remove workaround for anvils
they removed this in the 1.2.13 release. now the metadata matches the block.
2018-05-23 11:43:55 +01:00
acf29711c2 Implemented Totems, close #2198
Totem usage can be detected using the MODIFIER_TOTEM constant of EntityDamageEvent.

This does not currently support using the totem in the offhand because offhand is not implemented yet.
2018-05-23 10:06:35 +01:00
1c4dd4f280 Player: broadcast entity event to self as well when sending to all viewers
this fixes several bugs with entity events and removes the need for a hit animation hack.
2018-05-23 09:28:46 +01:00
faa88a55e4 EntityDamageEvent: break API
The general purpose of this is to split up base damage from modifiers.

- Added methods getBaseDamage(), setBaseDamage(), getOriginalBaseDamage(), getModifiers(), getOriginalModifiers()
- setDamage() renamed to setModifier() and type is now mandatory
- getDamage() renamed to getModifier() and type is now mandatory
- getOriginalDamage() renamed to getOriginalModifier() and type is now mandatory
- Removed MODIFIER_BASE constant
- Constructors now accept: float baseDamage, float[] modifiers instead of just float[] modifiers
2018-05-22 19:05:25 +01:00
c9ed517063 Merge branch 'release/alpha12' 2018-05-22 18:50:24 +01:00
28b0f5f86a UpdateBlockSyncedPacket: rename field 2018-05-22 18:49:41 +01:00
e87e2d4e52 UpdateBlockSyncedPacket: fix field visibility 2018-05-22 18:49:41 +01:00
86c27953ec NetworkBinaryStream: bail on unknown entity data types 2018-05-22 16:44:03 +01:00
5552704922 PluginBase->getResources() should only return files
Directories should not be returned. Previously it even returns resources\.. according to my test on Windows.
2018-05-21 17:25:57 +08:00
c7ac5dfd4b Fixed the doc comment in Plugin::getResources()
It returns SplFileInfo[] not string[]
2018-05-21 17:24:12 +08:00
bd9b59f401 Contributing: be more clear about committing on GitHub 2018-05-21 09:59:56 +01:00
2f03f5f6d5 Fix a grammar error in Player (#2197) 2018-05-20 21:27:40 +01:00
f4a26ddfd9 update Composer dependencies to get Snooze asserts
warning: there are bugs in this code! this will crash and burn without warning!!!!!
2018-05-20 12:57:23 +01:00
adb9390b53 Entity: Rename isInsideOfWater() to isUnderwater() 2018-05-19 18:10:43 +01:00
6111ce7df1 Human: don't hardcode max food 2018-05-19 16:48:21 +01:00
1f73c08762 Human: fix possible bug with food ticking
it updates it but doesn't take note of the updated value, which could cause the code below to be erroneously triggered.
2018-05-19 16:47:04 +01:00
2900167ffa Human: removed redundant isSprinting() check from doFoodTick()
this is checked in setSprinting() anyway.
2018-05-19 16:33:13 +01:00
11cc9f19ad Human: replace hardcoded difficulty values with constants in doFoodTick() 2018-05-19 16:32:30 +01:00
807af2e6fb Human: change doFoodTick() visibility to protected 2018-05-19 16:30:41 +01:00
f2511983cf Level: use increment operation for updating time 2018-05-19 16:05:08 +01:00
bac649137b Level: fixed doc comment for getSafeSpawn() 2018-05-19 14:02:54 +01:00
71224f51d5 Level: cleaned up some nonsensical code in getSafeSpawn()
it's impossible for this function to return false because $spawn is guaranteed to be a Vector3 when it's checked in instanceof.
2018-05-19 13:18:20 +01:00
6c3fc4af46 Level: switch order of addEntity() and removeEntity()
now it's consistent with addTile() and removeTile()
2018-05-19 13:14:11 +01:00
75d13be38e EnderChestInventory: remove unused import 2018-05-19 11:37:22 +01:00
9bc860f7a8 LevelDB: fixup extra-data handling, don't delete it - just don't read it
we'll need this once multi-layer block storages are implemented.
2018-05-19 11:35:54 +01:00
66963fbf9a Nuke block extradata
this has been superseded by multi-layer blockstorages in 1.2.14+
2018-05-19 11:03:28 +01:00
172c6420c1 Server: add type documentation for fields
found a few bugs in the process too
2018-05-19 10:55:34 +01:00
e7fc9227bc Server: fixed bug in alwaysTickPlayers config 2018-05-19 10:53:31 +01:00
13cd0cdcfd Server: cast result of getProperty() for networkCompressionAsync 2018-05-19 10:52:09 +01:00
0bb5e88b5c Hinting up Entity API to PHP 7.2 standards 2018-05-19 10:46:47 +01:00
389990e0a8 Entity: remove useless functions
these may be TODOs but I have no idea why or where they need to be implemented, so until they do they are useless clutter.
2018-05-18 20:09:55 +01:00
067aad9546 PocketMine.php: Remove unused \pocketmine\ANSI constant
Terminal::hasFormattingCodes() should be used instead (this is also portable across threads even if constants aren't inherited by child threads, provided that the child thread has an autoloader available.
2018-05-18 18:51:18 +01:00
b1a7606e82 PocketMine.php: Take dependency versions for granted
Since this is managed by Composer now, it's expected that users should install dependencies appropriately when running from source code. We have a few more dependencies than just RakLib and SPL which are version-critical which are not checked here, and I don't have the taste for adding more version checks here.

A better way would be to automatically detect outdated Composer autoloader and warn that dependencies might need updating, but I'm not sure how to do that off the hop. Users should prefer using prebuilt phars anyway - only developers and/or people who know what they are doing should be running the server from source code.
2018-05-18 18:47:12 +01:00
febba6e3a6 Tile: call parent constructor instead of duping code 2018-05-18 16:49:14 +01:00
d8dc89e7c8 EnderChestInventory: Removed unnecessary owner parameter 2018-05-18 16:46:48 +01:00
b75413e3c4 CommandReader: Use stream_isatty() instead of posix_isatty() (new in 7.2) 2018-05-18 16:46:48 +01:00
f08537a1e0 BlockMetadataStore: fix case of import
found by PHPStan static analysis tool
2018-05-18 16:46:48 +01:00
6643fa5f09 README: it's not Pocket Edition anymore 2018-05-18 14:54:50 +01:00
210e108574 Level: add a null coalesce
meant to add this before push but wasn't thinking clearly...
2018-05-18 11:28:52 +01:00
813437e3ee Level: Avoid unnecessary use of getChunk()
this is completely pointless.
2018-05-18 11:27:53 +01:00
24295ce02f Level: Avoid creating useless throwaway objects in sensitive functions 2018-05-18 11:05:41 +01:00
29fd26627e Level: Change isInWorld signature to use ints instead of floats
this is only used in one place, where it's being given floats, and it's 10% faster to use int for this because it won't convert it.

It is also 25% faster to remove typehints and 60% faster to inline it. We really need a proper PHP preprocessor for inlining.
2018-05-18 11:01:13 +01:00
22b91aaa24 SubChunkIteratorManager: Added method invalidate() to allow destroying stale chunk refs conveniently
this could be necessary for reusable long-life iterators when chunks get replaced.
2018-05-18 09:52:27 +01:00
f757ba1851 Merge branch 'release/alpha12' 2018-05-17 19:27:26 -04:00
c285295037 Merge branch 'release/alpha12' 2018-05-17 18:40:24 +01:00
8312ad709e InventoryTransaction: Removed creationTime
this is no longer necessary because transactions now always arrive in a single packet.
2018-05-16 12:14:29 +01:00
63fc04b3dd thanks for being useless PhpStorm 2018-05-13 11:56:32 +01:00
34b8557094 Moved parseDocComment from PluginManager to Utils 2018-05-13 11:24:04 +01:00
edaef588ab CommandReader: remove unnecessary setClassLoader() call
this is already handled in start() anyway.
2018-05-12 12:47:28 +01:00
889222e9c5 MainLogger: Use PTHREADS_INHERIT_NONE
this thread doesn't need to inherit anything because its sole purpose is to write log messages to file.
2018-05-12 12:39:13 +01:00
8239c67b1a Enchantment: split up primary and secondary flags
closes #1911
2018-05-12 11:20:27 +01:00
ed65e91a3c Tree: avoid astonishing behaviour with dark-oak and acacia saplings
ref #1973, these should simply not grow at all since they are not implemented yet.
2018-05-12 10:03:20 +01:00
619390c5b7 Sapling: Account for light level when trying to grow on random ticks 2018-05-11 09:53:03 +01:00
7e70569ba2 Player: Send all open inventories instead of hardcoded selection
Players have a few associated inventories which might need sending nowadays, such as main, armour, offhand (not implemented yet), cursor, crafting (if it ever worked). Under these conditions we should be sending all open windows.
2018-05-11 09:28:59 +01:00
083ac8a770 Clean up Tool garbage leftovers
these were intended to be removed long ago, but other things came up and durability handling still depended on them.
2018-05-11 09:23:51 +01:00
b21572774a Tool: cleanup durability handling, closes #379
long overdue... this isn't quite as extensible as the original api3/blocks system was, but this is primarily intended to replace Item->useOn(). If plugins want to use it it can be extended later on.
2018-05-10 19:48:51 +01:00
b8523cb304 Merge branch 'remove-weak-position' 2018-05-10 13:53:07 +01:00
6ceb9af749 .-. 2018-05-10 12:51:39 +01:00
bcd197d7bb AsyncWorker: fixed __construct() signature 2018-05-10 12:50:09 +01:00
3148f692c1 AsyncWorker: No need to register static logger - it's inherited by default with pthreads 3.1.7 2018-05-10 12:49:03 +01:00
d8d22efc3b Server: avoid abusing Throwable in a couple of places 2018-05-10 12:46:13 +01:00
7b3653f75d SetupWizard: remove dead constant 2018-05-10 12:39:15 +01:00
9c5f7128a4 RCON: lots of cleanup, now notification-based instead of poll-based
This now utilizes Snooze in order to have the server wake up to process RCON commands ondemand, similar to how the CommandReader thread operates. This is better for performance and response times.

This also makes a few other changes:
- RCON thread will now waste less CPU since it uses a blocking select() with timeout to read
- Following from that, IPC sockets are used to allow interrupting select() from the RCON thread.
- Multiple threads for RCON has been removed (this is entirely unnecessary, reading data from sockets is not CPU-intensive, and a single thread is easier to work with)
2018-05-10 12:33:05 +01:00
1e4a97f921 Server: remove dead code from forceShutdown() 2018-05-10 11:30:50 +01:00
4d743ade45 CrashDump: resource hygiene 2018-05-10 11:29:16 +01:00
78b5cc993b Server: fixed signatures and type-checks for logger
this might not be a MainLogger instance, but it definitely has to be an \AttachableThreadedLogger instance.
2018-05-10 10:49:41 +01:00
5e91c05424 Server::getIp() now returns 0.0.0.0 if the IP string is empty 2018-05-10 10:25:44 +01:00
e7c5d14af3 Fix DoubleSlab name (#2177) 2018-05-09 21:17:35 +01:00
126a97b405 ServerKiller: fixed start/stop race condition
in some cases the main thread was trying to signal the server killer to stop before it was even started due to limited resources available for scheduling.
2018-05-09 20:59:56 +01:00
753ed3801d update RakLib version 2018-05-09 20:18:36 +01:00
68ef4b210d Allow RakLibServer to inherit constants (PATH const is needed for exception logging)
this is very annoying and needs a better fix.
2018-05-09 20:08:25 +01:00
c3822b795c CommandReader: use PTHREADS_INHERIT_NONE
probably not necessary to register autoloader, but just in case...
2018-05-09 20:02:51 +01:00
be0e85dfae CommandReader: fix notifier race condition crash, don't start self in constructor
self-start is extremely annoying!
2018-05-09 20:01:16 +01:00
72690ea7f5 RakLibInterface: start RakLibServer with PTHREADS_INHERIT_NONE
this is now OK since the logger colours will be initialized on the fly.
2018-05-09 20:00:09 +01:00
c9bd60123b Scheduler: shutdown async pool properly instead of relying on ThreadManager 2018-05-09 19:49:12 +01:00
05f4262e81 MainLogger: moved format to private variable 2018-05-09 18:14:46 +01:00
dd11bcaf11 Fixed #1979: logger colours don't show on other threads when classes are not inherited 2018-05-09 18:07:12 +01:00
b96adda14d MainLogger: Colorize at point of echo, not beforehand
this removes the need for a hack I had to do with ClientConsole to translate ANSI colour codes back into MC colour codes.
2018-05-09 18:04:16 +01:00
5c66c615bf Living: fix possible crash in getTargetBlock()
ArrayOutOfBoundsException is not thrown by SPL anymore since the exception handler throwing it was removed by @shoghicp. Regardless, it seems cleaner to to check it properly.
2018-05-09 17:11:37 +01:00
2ff2a2de08 FormattedCommandAlias: use multiple catch clauses 2018-05-09 16:58:55 +01:00
78f8d54f89 Merge branch 'release/alpha12' 2018-05-09 16:53:41 +01:00
2a0a2134d1 Server: Implemented an signal/sleep interrupt mechanism for ticking (#2171)
This allows other threads to notify the main thread to wake it up while it's sleeping between ticks, allowing reduction of processing latency.

Currently only RakLib and the CommandReader threads utilize this, but it's planned to extend it to more things in the near future.

CommandReader is now event-driven instead of poll-based - the server will not poll the CommandReader thread for messages each tick anymore.

RakLib utilizes this mechanism to get packets processed without delays to lower latency.

This now adds an extra dependency - `pocketmine/snooze` library contains the meat of the code used for this. See the Snooze repository for details.
2018-05-09 14:18:13 +01:00
e70af362d0 RCONInstance: fixup bad continues 2018-05-07 13:55:23 +01:00
24aab8365e RCONInstance: remove redundant size ref param from readPacket() 2018-05-07 13:53:34 +01:00
0e5504ed3f RCONInstance: add docs to fields, make 'stop' private 2018-05-07 13:52:33 +01:00
83008440c0 RCONInstance: replace hardcoded status values with constants
I thought I'd seen the worst of PM, but I haven't.........
2018-05-07 13:44:59 +01:00
b14dfa9f7e RCONInstance: Remove pointless code that makes pthreads segfault
pthreads doesn't play well with resources, and this code upsets it. Also, this code is utterly pointless.

The whole of RCON needs burning to the ground...
2018-05-07 10:05:05 +01:00
197102ca3d Level: fixed blocks not dropping when not broken by player
closes #2172
2018-05-05 21:50:28 +01:00
f497e43db3 SourceInterface: removed redundant return value from process() 2018-05-05 15:30:46 +01:00
38c3f00ef7 avoid crashes when XUID is null 2018-05-05 13:33:13 +01:00
396056c636 Sign: Pad exploded blob to appropriate size
I'm not sure how it's possible to get one of these blobs with less newlines than expected, but whatever I guess
closes #2152
2018-05-04 23:29:32 +01:00
dd1dfefd83 ItemEntity: remove unnecessary damage types restrictions
Invalid damage types are all restricted by other means anyway. This fixes items not getting killed by fire (close #2143) and cacti.
2018-05-04 22:57:28 +01:00
7565b786e7 Implemented @notHandler annotation for event handlers - skip registering any handlers with this annotation (#2164)
It is somewhat reasonable to have a function in an event handler which accepts an Event parameter, but is not a handler. For example, multiple event handlers can redirect to the same function to process an event, but this function may not want to receive called events.

There are other ways to get around this, such as making the event handler protected/private, or adding a dummy parameter, but this way is cleaner and more explicit.

Relevant old-repo PR: PocketMine/PocketMine-MP#2143
2018-05-04 21:36:58 +01:00
ae0c1c185f Fixed wrong doccomment for Permission::$children 2018-05-04 15:31:00 +08:00
723251e800 Fixed bug in commit hash detection when output ends with newline 2018-05-03 16:37:05 +01:00
295016cbc1 DisconnectPacket: fix decoding 2018-05-02 16:56:48 +01:00
d80c471ae1 Typehints on missed sound and particle APIs 2018-05-02 11:44:18 +01:00
b4068dfd2f remove unused import 2018-05-02 10:44:05 +01:00
3095eb544d Item: removed isTool()
this should be replaced with `instanceof Tool`.
2018-05-01 20:05:53 +01:00
0247dff909 Fixed mis-uses of Item->isTool()
this has been wrongly used to indicate a durable item, but not just tools are durable items.
2018-05-01 20:05:02 +01:00
5368120f13 Level: remove redundant getAutoSave() condition in close()
this is already checked by save() anyway.
2018-05-01 18:47:08 +01:00
2e7db552dc Level: __construct() now accepts a LevelProvider object instead of string, string
This is made possible by the removal of LevelProvider dependence on their Levels, and furthers the goal of #2024.
2018-05-01 18:43:11 +01:00
53c35aaa1d Server: remove unused variable from generateLevel() 2018-05-01 18:26:03 +01:00
3293074cfc Implement @softDepend annotation for event handlers - skip registering if the event class is undefined (#2162)
This allows plugins to soft-depend on other plugins without separating their listeners into a dedicated class for listening to that plugin.

This can be utilized by adding a `@softDepend PluginName` to the event handler's annotations.
If the plugin providing the event does not exist or is not loaded, then the handler will silently not be registered.
If it does exist and the event is not found, the original behaviour applies and an exception will be thrown.

This change should be fully backwards compatible.
2018-05-01 14:33:24 +01:00
d8f4dde5f3 PlayerHotbarPacket: remove unused import 2018-05-01 14:09:17 +01:00
dfa6cd2b7e Biome: Moved biome classes from level\generator\* to level\biome 2018-05-01 14:07:46 +01:00
e03d2b23f7 Sign: add some typehints where it wasn't previously possible 2018-05-01 14:00:23 +01:00
96a2fd7482 Server: fixed wonky doc comment for broadcastTitle() 2018-05-01 12:30:06 +01:00
88c56bcdc8 Server: move formattingCodes check to initial assignment of doTitleTick 2018-05-01 12:21:41 +01:00
2731fc3d0f Server: remove redundant return value from tick() 2018-05-01 12:17:11 +01:00
a02f178f80 PocketMine.php: add git hash length check "just in case" 2018-04-29 18:07:40 +01:00
96d26a77a1 Explosion: set block damage to zero on destruction 2018-04-16 14:30:54 +01:00
554fe4d14d Updated dependencies, require 64-bit PHP in composer.json 2018-04-16 10:19:17 +01:00
532269a484 Implemented block break XP drops 2018-04-15 19:03:18 +01:00
1e2122d854 avoid crashing in forceShutdown() if properties wasn't initialized yet 2018-04-14 12:45:03 +01:00
8d645b714f Position: Destroy references to Level in isValid() 2018-03-20 10:55:24 +00:00
403e996d2f Level: Removed WeakPosition
This is a bad fix for an issue found in one specific use-case. This is redundant because the only places this is used are places where it's guaranteed to be valid, or places where it is checked to be valid anyway.

The Level leak originally noted that led to the creation of this class is something I consider to be a non-issue, because all the heavy things will be cleaned up anyway.
2018-03-20 10:49:23 +00:00
417 changed files with 4343 additions and 32124 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
players/*
worlds/*
plugin_data/*
plugins/*
bin*/*
timings/*

3
.gitmodules vendored
View File

@ -10,3 +10,6 @@
[submodule "tests/plugins/PocketMine-TesterPlugin"]
path = tests/plugins/PocketMine-TesterPlugin
url = https://github.com/pmmp/PocketMine-TesterPlugin.git
[submodule "src/pocketmine/resources/vanilla"]
path = src/pocketmine/resources/vanilla
url = https://github.com/pmmp/BedrockData.git

View File

@ -6,9 +6,9 @@ php:
before_script:
# - pecl install channel://pecl.php.net/pthreads-3.1.6
- echo | pecl install channel://pecl.php.net/yaml-2.0.2
- git clone https://github.com/krakjoe/pthreads.git
- git clone https://github.com/pmmp/pthreads.git
- cd pthreads
- git checkout d32079fb4a88e6e008104d36dbbf0c2dd7deb403
- git checkout c8cfacda84f21032d6014b53e72bf345ac901dac
- phpize
- ./configure
- make
@ -18,7 +18,7 @@ before_script:
- composer install
script:
- ./tests/travis.sh
- ./tests/travis.sh -t4
notifications:
email: false

View File

@ -42,7 +42,7 @@ We try to ensure that our project's codebase is as clean as possible and ensure
- **Details should be provided of tests done.** Simply saying "Tested" or equivalent is not acceptable.
### Code contributions
- **Avoid using GitHub Web Editor**. The web editor lacks most useful GIT features and **should only be used for very minor changes**. It is immediately clear if the web editor has been used, and if so the PR is more likely to be rejected. If you want to make serious contributions, **please learn how to use [GIT version control](https://git-scm.com/)**.
- **Avoid committing changes directly on GitHub. This includes use of the web editor, and also uploading files.** The web editor lacks most useful GIT features and **should only be used for very minor changes**. It is immediately clear if the web editor has been used, and if so the PR is more likely to be rejected. If you want to make serious contributions, **please learn how to use [GIT version control](https://git-scm.com/)**.
- **Do not copy-paste code**. There are potential license issues implicit with copy-pasting, and copy-paste usually indicates a lack of understanding of the actual code. Copy-pasted code is obvious a mile off and **any PR like this is likely to be closed**. If you want to use somebody else's code from a Git repository, **use [GIT's cherry-pick feature](https://git-scm.com/docs/git-cherry-pick)** to cherry-pick the commit. **Cherry-picking is the politer way to copy somebody's changes** and retains all the original accreditation, so there is no need for copy-pasted commits with descriptions like `Some code, thanks @exampleperson`.
- **Make sure you can explain your changes**. If you can't provide a good explanation of changes, your PR may be rejected.
- **Code should use the same style as in PocketMine-MP.** See [below](#code-syntax) for an example.

View File

@ -1,6 +1,6 @@
# [![PocketMine-MP](http://cdn.pocketmine.net/img/PocketMine-MP-h.png)](https://pmmp.io)
__A highly customisable, open source server software for Minecraft: Pocket Edition written in PHP__
__A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP__
[![Build Status](https://travis-ci.org/pmmp/PocketMine-MP.svg?branch=master)](https://travis-ci.org/pmmp/PocketMine-MP)
@ -27,18 +27,6 @@ Yes you can! Contributions are welcomed provided that they comply with our [Cont
**Note: Please avoid development builds unless there is no other alternative for what you need.** Development builds are subject to changes at any time without notice, and it is likely that your server or plugins might break without warning.
## Third-party Libraries/Protocols Used
* __[PHP Sockets](http://php.net/manual/en/book.sockets.php)__
* __[PHP mbstring](http://php.net/manual/en/book.mbstring.php)__
* __[PHP BCMath](http://php.net/manual/en/book.bc.php)__
* __[PHP pthreads](http://pthreads.org/)__ by _[krakjoe](https://github.com/krakjoe)_: Threading for PHP - Share Nothing, Do Everything.
* __[PHP YAML](https://code.google.com/p/php-yaml/)__ by _Bryan Davis_: The Yaml PHP Extension provides a wrapper to the LibYAML library.
* __[LibYAML](http://pyyaml.org/wiki/LibYAML)__ by _Kirill Simonov_: A YAML 1.1 parser and emitter written in C.
* __[cURL](http://curl.haxx.se/)__: cURL is a command line tool for transferring data with URL syntax
* __[Zlib](http://www.zlib.net/)__: A Massively Spiffy Yet Delicately Unobtrusive Compression Library
* __[Source RCON Protocol](https://developer.valvesoftware.com/wiki/Source_RCON_Protocol)__
* __[UT3 Query Protocol](http://wiki.unrealadmin.org/UT3_query_protocol)__
## Licensing information
This program is free software: you can redistribute it and/or modify

View File

@ -6,8 +6,11 @@
"license": "LGPL-3.0",
"require": {
"php": ">=7.2.0",
"php-64bit": "*",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-ctype": "*",
"ext-date": "*",
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
@ -21,17 +24,23 @@
"ext-yaml": ">=2.0.0",
"ext-zip": "*",
"ext-zlib": ">=1.2.11",
"pocketmine/raklib": "0.11.0",
"pocketmine/spl": "0.3.0",
"pocketmine/binaryutils": "0.0.1",
"pocketmine/nbt": "0.1.0",
"pocketmine/math": "0.1.0"
"pocketmine/raklib": "^0.12.0",
"pocketmine/spl": "^0.3.0",
"pocketmine/binaryutils": "^0.1.0",
"pocketmine/nbt": "^0.2.0",
"pocketmine/math": "^0.2.0",
"pocketmine/snooze": "^0.1.0"
},
"autoload": {
"psr-4": {
"": ["src"]
}
},
"autoload-dev": {
"psr-4": {
"pocketmine\\": "tests/phpunit/"
}
},
"repositories": [
{
"type": "vcs",
@ -52,6 +61,10 @@
{
"type": "vcs",
"url": "https://github.com/pmmp/Math"
},
{
"type": "vcs",
"url": "https://github.com/pmmp/Snooze"
}
]
}

123
composer.lock generated
View File

@ -1,27 +1,28 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "401dbada37e501304f05b0f1fa818953",
"content-hash": "2670b9e2a730ff758909be8b9e9d609a",
"packages": [
{
"name": "pocketmine/binaryutils",
"version": "0.0.1",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BinaryUtils.git",
"reference": "03e6851f814aba96487ec64181a6ae948edd9f7a"
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/03e6851f814aba96487ec64181a6ae948edd9f7a",
"reference": "03e6851f814aba96487ec64181a6ae948edd9f7a",
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
"shasum": ""
},
"require": {
"php": ">=7.2"
"php": ">=7.2",
"php-64bit": "*"
},
"type": "library",
"autoload": {
@ -37,24 +38,25 @@
"source": "https://github.com/pmmp/BinaryUtils/tree/master",
"issues": "https://github.com/pmmp/BinaryUtils/issues"
},
"time": "2018-03-17T11:57:06+00:00"
"time": "2018-04-16T09:05:08+00:00"
},
{
"name": "pocketmine/math",
"version": "0.1.0",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Math.git",
"reference": "1df74f0352309a9c1e6728fa416a3f0493d07b16"
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Math/zipball/1df74f0352309a9c1e6728fa416a3f0493d07b16",
"reference": "1df74f0352309a9c1e6728fa416a3f0493d07b16",
"url": "https://api.github.com/repos/pmmp/Math/zipball/95ae5600328ed2add44c0bc830a68d3660e9e0ef",
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef",
"shasum": ""
},
"require": {
"php": ">=7.2.0"
"php": ">=7.2.0",
"php-64bit": "*"
},
"type": "library",
"autoload": {
@ -70,25 +72,26 @@
"source": "https://github.com/pmmp/Math/tree/master",
"issues": "https://github.com/pmmp/Math/issues"
},
"time": "2018-03-18T18:01:56+00:00"
"time": "2018-06-09T09:26:30+00:00"
},
{
"name": "pocketmine/nbt",
"version": "0.1.0",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "d79f8615442887bb45cfacdb52e1e6eb47c38fd7"
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/d79f8615442887bb45cfacdb52e1e6eb47c38fd7",
"reference": "d79f8615442887bb45cfacdb52e1e6eb47c38fd7",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
"shasum": ""
},
"require": {
"php": ">=7.2.0",
"pocketmine/binaryutils": "0.0.1"
"php-64bit": "*",
"pocketmine/binaryutils": "^0.1.0"
},
"type": "library",
"autoload": {
@ -106,61 +109,98 @@
],
"description": "PHP library for working with Named Binary Tags",
"support": {
"source": "https://github.com/pmmp/NBT/tree/0.1.0",
"source": "https://github.com/pmmp/NBT/tree/0.2.0",
"issues": "https://github.com/pmmp/NBT/issues"
},
"time": "2018-04-13T18:43:03+00:00"
"time": "2018-06-13T09:56:00+00:00"
},
{
"name": "pocketmine/raklib",
"version": "0.11.0",
"version": "0.12.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "1da1b4c6cc6bd5337ce5e468d22bbb013ae02b43"
"reference": "922da28efd828e2af6c19db1676ea9b6a267071c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/1da1b4c6cc6bd5337ce5e468d22bbb013ae02b43",
"reference": "1da1b4c6cc6bd5337ce5e468d22bbb013ae02b43",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/922da28efd828e2af6c19db1676ea9b6a267071c",
"reference": "922da28efd828e2af6c19db1676ea9b6a267071c",
"shasum": ""
},
"require": {
"ext-bcmath": "*",
"ext-pthreads": ">=3.1.7dev",
"ext-sockets": "*",
"php": ">=7.2.0RC3",
"pocketmine/binaryutils": "0.0.1",
"pocketmine/spl": "0.3.0"
"php": ">=7.2.0",
"php-64bit": "*",
"php-ipv6": "*",
"pocketmine/binaryutils": "^0.1.0",
"pocketmine/snooze": "^0.1.0",
"pocketmine/spl": "^0.3.0"
},
"type": "library",
"autoload": {
"classmap": [
"./"
]
"psr-4": {
"raklib\\": "src/"
}
},
"license": [
"GPL-3.0"
],
"description": "A RakNet server implementation written in PHP",
"support": {
"source": "https://github.com/pmmp/RakLib/tree/0.11.0",
"source": "https://github.com/pmmp/RakLib/tree/0.12.0",
"issues": "https://github.com/pmmp/RakLib/issues"
},
"time": "2018-04-13T19:05:24+00:00"
"time": "2018-06-13T10:06:14+00:00"
},
{
"name": "pocketmine/spl",
"version": "0.3.0",
"name": "pocketmine/snooze",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/SPL.git",
"reference": "ee32424c100fd11ae7f7b8df7604623fd475f0ec"
"url": "https://github.com/pmmp/Snooze.git",
"reference": "3cc9d0164230889acb08e22cc126133809e9d346"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/SPL/zipball/ee32424c100fd11ae7f7b8df7604623fd475f0ec",
"reference": "ee32424c100fd11ae7f7b8df7604623fd475f0ec",
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/3cc9d0164230889acb08e22cc126133809e9d346",
"reference": "3cc9d0164230889acb08e22cc126133809e9d346",
"shasum": ""
},
"require": {
"ext-pthreads": ">=3.1.7dev",
"php-64bit": ">=7.2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"pocketmine\\snooze\\": "src/"
}
},
"license": [
"LGPL-3.0"
],
"description": "Thread notification management library for code using the pthreads extension",
"support": {
"source": "https://github.com/pmmp/Snooze/tree/0.1.0",
"issues": "https://github.com/pmmp/Snooze/issues"
},
"time": "2018-06-13T09:36:11+00:00"
},
{
"name": "pocketmine/spl",
"version": "0.3.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/SPL.git",
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/SPL/zipball/ca3912099543ddc4b4b14f40e258d84ca547dfa5",
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5",
"shasum": ""
},
"type": "library",
@ -179,7 +219,7 @@
"support": {
"source": "https://github.com/pmmp/SPL/tree/master"
},
"time": "2018-03-17T11:56:20+00:00"
"time": "2018-06-09T17:30:36+00:00"
}
],
"packages-dev": [],
@ -192,8 +232,11 @@
"prefer-lowest": false,
"platform": {
"php": ">=7.2.0",
"php-64bit": "*",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-ctype": "*",
"ext-date": "*",
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",

View File

@ -38,7 +38,7 @@ PROJECT_NAME = PocketMine-MP
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "PM_VERSION - API PM_API"
PROJECT_NUMBER = "PM_VERSION"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@ -147,6 +147,4 @@ abstract class Achievement{
return false;
}
}

View File

@ -64,6 +64,8 @@ class CrashDump{
$this->extraData();
$this->encodeData();
fclose($this->fp);
}
public function getPath() : string{
@ -227,13 +229,13 @@ class CrashDump{
}
private function generalData(){
$version = new VersionString();
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
$this->data["general"] = [];
$this->data["general"]["name"] = $this->server->getName();
$this->data["general"]["version"] = $version->get(false);
$this->data["general"]["version"] = $version->getFullVersion(false);
$this->data["general"]["build"] = $version->getBuild();
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
$this->data["general"]["api"] = \pocketmine\API_VERSION;
$this->data["general"]["api"] = \pocketmine\BASE_VERSION;
$this->data["general"]["git"] = \pocketmine\GIT_COMMIT;
$this->data["general"]["raklib"] = RakLib::VERSION;
$this->data["general"]["uname"] = php_uname("a");
@ -241,7 +243,7 @@ class CrashDump{
$this->data["general"]["zend"] = zend_version();
$this->data["general"]["php_os"] = PHP_OS;
$this->data["general"]["os"] = Utils::getOS();
$this->addLine($this->server->getName() . " version: " . $version->get(false) . " #" . $version->getBuild() . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "; API " . API_VERSION . "]");
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
$this->addLine("Git commit: " . GIT_COMMIT);
$this->addLine("uname -a: " . php_uname("a"));
$this->addLine("PHP Version: " . phpversion());
@ -256,5 +258,4 @@ class CrashDump{
public function add($str){
fwrite($this->fp, $str);
}
}

View File

@ -27,7 +27,6 @@ use pocketmine\event\server\LowMemoryEvent;
use pocketmine\scheduler\DumpWorkerMemoryTask;
use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings;
use pocketmine\utils\MainLogger;
use pocketmine\utils\Utils;
class MemoryManager{
@ -243,9 +242,9 @@ class MemoryManager{
Timings::$garbageCollectorTimer->startTiming();
if($this->garbageCollectionAsync){
$size = $this->server->getScheduler()->getAsyncTaskPoolSize();
for($i = 0; $i < $size; ++$i){
$this->server->getScheduler()->scheduleAsyncTaskToWorker(new GarbageCollectionTask(), $i);
$pool = $this->server->getAsyncPool();
foreach($pool->getRunningWorkers() as $i){
$pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
}
}
@ -264,13 +263,13 @@ class MemoryManager{
* @param int $maxStringSize
*/
public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize){
MainLogger::getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize);
$this->server->getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $this->server->getLogger());
if($this->dumpWorkers){
$scheduler = $this->server->getScheduler();
for($i = 0, $size = $scheduler->getAsyncTaskPoolSize(); $i < $size; ++$i){
$scheduler->scheduleAsyncTaskToWorker(new DumpWorkerMemoryTask($outputFolder, $maxNesting, $maxStringSize), $i);
$pool = $this->server->getAsyncPool();
foreach($pool->getRunningWorkers() as $i){
$pool->submitTaskToWorker(new DumpWorkerMemoryTask($outputFolder, $maxNesting, $maxStringSize), $i);
}
}
}
@ -278,14 +277,15 @@ class MemoryManager{
/**
* Static memory dumper accessible from any thread.
*
* @param mixed $startingObject
* @param string $outputFolder
* @param int $maxNesting
* @param int $maxStringSize
* @param mixed $startingObject
* @param string $outputFolder
* @param int $maxNesting
* @param int $maxStringSize
* @param \Logger $logger
*
* @throws \ReflectionException
*/
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize){
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){
$hardLimit = ini_get('memory_limit');
ini_set('memory_limit', '-1');
gc_disable();
@ -329,7 +329,7 @@ class MemoryManager{
}
file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
MainLogger::getLogger()->info("[Dump] Wrote $staticCount static properties");
$logger->info("[Dump] Wrote $staticCount static properties");
if(isset($GLOBALS)){ //This might be null if we're on a different thread
$globalVariables = [];
@ -357,7 +357,7 @@ class MemoryManager{
}
file_put_contents($outputFolder . "/globalVariables.js", json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
MainLogger::getLogger()->info("[Dump] Wrote $globalCount global variables");
$logger->info("[Dump] Wrote $globalCount global variables");
}
self::continueDump($startingObject, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
@ -394,15 +394,22 @@ class MemoryManager{
$info["implements"] = implode(", ", $reflection->getInterfaceNames());
}
foreach($reflection->getProperties() as $property){
if($property->isStatic()){
continue;
}
for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
foreach($reflection->getProperties() as $property){
if($property->isStatic()){
continue;
}
if(!$property->isPublic()){
$property->setAccessible(true);
$name = $property->getName();
if($reflection !== $original and !$property->isPublic()){
$name = $reflection->getName() . ":" . $name;
}
if(!$property->isPublic()){
$property->setAccessible(true);
}
self::continueDump($property->getValue($object), $info["properties"][$name], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
self::continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
fwrite($obData, "$hash@$className: " . json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
@ -411,7 +418,7 @@ class MemoryManager{
}while($continue);
MainLogger::getLogger()->info("[Dump] Wrote " . count($objects) . " objects");
$logger->info("[Dump] Wrote " . count($objects) . " objects");
fclose($obData);
@ -421,7 +428,7 @@ class MemoryManager{
arsort($instanceCounts, SORT_NUMERIC);
file_put_contents($outputFolder . "/instanceCounts.js", json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
MainLogger::getLogger()->info("[Dump] Finished!");
$logger->info("[Dump] Finished!");
ini_set('memory_limit', $hardLimit);
gc_enable();

View File

@ -134,6 +134,4 @@ class OfflinePlayer implements IPlayer, Metadatable{
public function removeMetadata(string $metadataKey, Plugin $owningPlugin){
$this->server->getPlayerMetadata()->removeMetadata($this, $metadataKey, $owningPlugin);
}
}

View File

@ -76,6 +76,7 @@ use pocketmine\inventory\transaction\CraftingTransaction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Consumable;
use pocketmine\item\Durable;
use pocketmine\item\Item;
use pocketmine\item\WritableBook;
use pocketmine\item\WrittenBook;
@ -86,7 +87,6 @@ use pocketmine\level\format\Chunk;
use pocketmine\level\Level;
use pocketmine\level\Location;
use pocketmine\level\Position;
use pocketmine\level\WeakPosition;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\metadata\MetadataValue;
@ -298,7 +298,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var Vector3|null */
protected $sleeping = null;
/** @var WeakPosition|null */
/** @var Position|null */
private $spawnPosition = null;
//TODO: Abilities
@ -401,7 +401,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
*
* @return UUID|null
*/
public function getUniqueId(){
public function getUniqueId() : ?UUID{
return parent::getUniqueId();
}
@ -459,7 +459,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* @param Player $player
*/
public function spawnTo(Player $player){
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()){
parent::spawnTo($player);
}
@ -535,10 +535,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function canBeCollidedWith() : bool{
return !$this->isSpectator();
return !$this->isSpectator() and parent::canBeCollidedWith();
}
public function resetFallDistance(){
public function resetFallDistance() : void{
parent::resetFallDistance();
if($this->inAirTicks !== 0){
$this->startAirTicks = 5;
@ -666,8 +666,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function sendCommandData(){
//TODO: this needs fixing
$pk = new AvailableCommandsPacket();
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
if(isset($pk->commandData[$command->getName()]) or $command->getName() === "help"){
@ -688,7 +686,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$aliases = $command->getAliases();
if(!empty($aliases)){
if(!\in_array($data->commandName, $aliases, true)){
if(!in_array($data->commandName, $aliases, true)){
//work around a client bug which makes the original name not show when aliases are used
$aliases[] = $data->commandName;
}
@ -812,7 +810,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
*
* If null is given, will additionally send the skin to the player itself as well as its viewers.
*/
public function sendSkin(array $targets = null) : void{
public function sendSkin(?array $targets = null) : void{
parent::sendSkin($targets ?? $this->server->getOnlinePlayers());
}
@ -923,12 +921,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
protected function switchLevel(Level $targetLevel) : bool{
$oldLevel = $this->level;
if(parent::switchLevel($targetLevel)){
foreach($this->usedChunks as $index => $d){
Level::getXZ($index, $X, $Z);
$this->unloadChunk($X, $Z, $oldLevel);
if($oldLevel !== null){
foreach($this->usedChunks as $index => $d){
Level::getXZ($index, $X, $Z);
$this->unloadChunk($X, $Z, $oldLevel);
}
}
$this->usedChunks = [];
$this->loadQueue = [];
$this->level->sendTime($this);
$this->level->sendDifficulty($this);
@ -1061,9 +1062,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->dataPacket($pk);
}
protected function orderChunks(){
protected function orderChunks() : void{
if(!$this->isConnected() or $this->viewDistance === -1){
return false;
return;
}
Timings::$playerChunkOrderTimer->startTiming();
@ -1148,8 +1149,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->loadQueue = $newOrder;
Timings::$playerChunkOrderTimer->stopTiming();
return true;
}
/**
@ -1169,7 +1168,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @return bool
*/
public function hasValidSpawnPosition() : bool{
return $this->spawnPosition instanceof WeakPosition and $this->spawnPosition->isValid();
return $this->spawnPosition !== null and $this->spawnPosition->isValid();
}
/**
@ -1184,7 +1183,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}else{
$level = $pos->getLevel();
}
$this->spawnPosition = new WeakPosition($pos->x, $pos->y, $pos->z, $level);
$this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level);
$pk = new SetSpawnPositionPacket();
$pk->x = $this->spawnPosition->getFloorX();
$pk->y = $this->spawnPosition->getFloorY();
@ -1487,23 +1486,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return 0;
}
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz){
if(!$this->onGround or $movY != 0){
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
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;
$this->onGround = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
$this->isCollided = $this->onGround;
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
public function canBeMovedByCurrents() : bool{
return false; //currently has no server-side movement
}
protected function checkNearEntities(int $tickDiff){
foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){
protected function checkNearEntities(){
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(1, 0.5, 1), $this) as $entity){
$entity->scheduleUpdate();
if(!$entity->isAlive() or $entity->isFlaggedForDespawn()){
@ -1637,17 +1633,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->newPosition = null;
}
public function jump(){
public function jump() : void{
$this->server->getPluginManager()->callEvent(new PlayerJumpEvent($this));
parent::jump();
}
public function setMotion(Vector3 $mot){
if(parent::setMotion($mot)){
public function setMotion(Vector3 $motion) : bool{
if(parent::setMotion($motion)){
$this->broadcastMotion();
if($this->motionY > 0){
$this->startAirTicks = (-log($this->gravity / ($this->gravity + $this->drag * $this->motionY)) / $this->drag) * 2 + 5;
if($this->motion->y > 0){
$this->startAirTicks = (-log($this->gravity / ($this->gravity + $this->drag * $this->motion->y)) / $this->drag) * 2 + 5;
}
return true;
@ -1655,11 +1651,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
protected function updateMovement(bool $teleport = false){
protected function updateMovement(bool $teleport = false) : void{
}
protected function tryChangeMovement(){
protected function tryChangeMovement() : void{
}
@ -1702,14 +1698,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($this->spawned){
$this->processMovement($tickDiff);
$this->motionX = $this->motionY = $this->motionZ = 0; //TODO: HACK! (Fixes player knockback being messed up)
$this->motion->x = $this->motion->y = $this->motion->z = 0; //TODO: HACK! (Fixes player knockback being messed up)
Timings::$timerEntityBaseTick->startTiming();
$this->entityBaseTick($tickDiff);
Timings::$timerEntityBaseTick->stopTiming();
if(!$this->isSpectator() and $this->isAlive()){
$this->checkNearEntities($tickDiff);
Timings::$playerCheckNearEntitiesTimer->startTiming();
$this->checkNearEntities();
Timings::$playerCheckNearEntitiesTimer->stopTiming();
if($this->speed !== null){
if($this->onGround){
@ -1744,7 +1742,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
public function doFoodTick(int $tickDiff = 1){
protected function doFoodTick(int $tickDiff = 1) : void{
if($this->isSurvival()){
parent::doFoodTick($tickDiff);
}
@ -1827,11 +1825,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return ($targetDot - $eyeDot) >= -$maxDiff;
}
protected function initHumanData(){
protected function initHumanData() : void{
$this->setNameTag($this->username);
}
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->addDefaultWindows();
}
@ -1914,7 +1912,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
if(!$packet->skipVerification){
$this->server->getScheduler()->scheduleAsyncTask(new VerifyLoginTask($this, $packet));
$this->server->getAsyncPool()->submitTask(new VerifyLoginTask($this, $packet));
}else{
$this->onVerifyCompleted($packet, null, true);
}
@ -1946,7 +1944,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$xuid = "";
}
if($xuid === ""){
if($xuid === "" or !is_string($xuid)){
if($signedByMojang){
$this->server->getLogger()->error($this->getName() . " should have an XUID, but none found");
}
@ -1955,7 +1953,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return;
}
$this->server->getLogger()->debug($this->getName() . " is NOT logged into to Xbox Live");
$this->server->getLogger()->debug($this->getName() . " is NOT logged into Xbox Live");
}else{
$this->server->getLogger()->debug($this->getName() . " is logged into Xbox Live");
$this->xuid = $xuid;
@ -1994,7 +1992,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(($level = $this->server->getLevelByName($this->namedtag->getString("Level", "", true))) === null){
$this->setLevel($this->server->getDefaultLevel());
$this->namedtag->setString("Level", $this->level->getFolderName());
$spawnLocation = $this->level->getSpawnLocation();
$spawnLocation = $this->level->getSafeSpawn();
$this->namedtag->setTag(new ListTag("Pos", [
new DoubleTag("", $spawnLocation->x),
new DoubleTag("", $spawnLocation->y),
@ -2084,9 +2082,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->hasValidSpawnPosition()){
if(($level = $this->server->getLevelByName($this->namedtag->getString("SpawnLevel", ""))) instanceof Level){
$this->spawnPosition = new WeakPosition($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
$this->spawnPosition = new Position($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
}else{
$this->spawnPosition = WeakPosition::fromObject($this->level->getSafeSpawn());
$this->spawnPosition = $this->level->getSafeSpawn();
}
}
@ -2145,8 +2143,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendPotionEffects($this);
$this->sendData($this);
$this->inventory->sendContents($this);
$this->armorInventory->sendContents($this);
$this->sendAllInventories();
$this->inventory->sendCreativeContents();
$this->inventory->sendHeldItem($this);
$this->dataPacket($this->server->getCraftingManager()->getCraftingDataPacket());
@ -2168,7 +2165,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$message = TextFormat::clean($message, $this->removeFormat);
foreach(explode("\n", $message) as $messagePart){
@ -2243,7 +2240,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->spawned or !$this->isAlive()){
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
switch($packet->event){
case EntityEventPacket::EATING_ITEM:
@ -2285,7 +2282,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($action !== null){
$actions[] = $action;
}
}catch(\Throwable $e){
}catch(\Exception $e){
$this->server->getLogger()->debug("Unhandled inventory action from " . $this->getName() . ": " . $e->getMessage());
$this->sendAllInventories();
return false;
@ -2394,7 +2391,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
$this->resetCraftingGridType();
$this->doCloseInventory();
$item = $this->inventory->getItemInHand();
$oldItem = clone $item;
@ -2506,20 +2503,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$ev->setCancelled();
}
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isInsideOfWater()){
$ev->setDamage($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
}
$target->attack($ev);
if($ev->isCancelled()){
if($heldItem->isTool() and $this->isSurvival()){
if($heldItem instanceof Durable and $this->isSurvival()){
$this->inventory->sendContents($this);
}
return true;
}
if($ev->getDamage(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
$pk = new AnimatePacket();
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
$pk->entityRuntimeId = $target->getId();
@ -2529,8 +2526,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
if($this->isSurvival()){
if($heldItem->useOn($target)){
if($this->isAlive()){
//reactive damage like thorns might cause us to be killed by attacking another mob, which
//would mean we'd already have dropped the inventory by the time we reached here
if($heldItem->onAttackEntity($target) and $this->isSurvival()){ //always fire the hook, even if we are survival
$this->inventory->setItemInHand($heldItem);
}
@ -2633,7 +2632,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$target = $this->level->getEntity($packet->target);
if($target === null){
@ -2747,40 +2746,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->jump();
return true;
case PlayerActionPacket::ACTION_START_SPRINT:
$ev = new PlayerToggleSprintEvent($this, true);
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
$this->sendData($this);
}else{
$this->setSprinting(true);
}
$this->toggleSprint(true);
return true;
case PlayerActionPacket::ACTION_STOP_SPRINT:
$ev = new PlayerToggleSprintEvent($this, false);
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
$this->sendData($this);
}else{
$this->setSprinting(false);
}
$this->toggleSprint(false);
return true;
case PlayerActionPacket::ACTION_START_SNEAK:
$ev = new PlayerToggleSneakEvent($this, true);
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
$this->sendData($this);
}else{
$this->setSneaking(true);
}
$this->toggleSneak(true);
return true;
case PlayerActionPacket::ACTION_STOP_SNEAK:
$ev = new PlayerToggleSneakEvent($this, false);
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
$this->sendData($this);
}else{
$this->setSneaking(false);
}
$this->toggleSneak(false);
return true;
case PlayerActionPacket::ACTION_START_GLIDE:
case PlayerActionPacket::ACTION_STOP_GLIDE:
@ -2805,6 +2780,26 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
public function toggleSprint(bool $sprint) : void{
$ev = new PlayerToggleSprintEvent($this, $sprint);
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
$this->sendData($this);
}else{
$this->setSprinting($sprint);
}
}
public function toggleSneak(bool $sneak) : void{
$ev = new PlayerToggleSneakEvent($this, $sneak);
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
$this->sendData($this);
}else{
$this->setSneaking($sneak);
}
}
public function handleAnimate(AnimatePacket $packet) : bool{
if(!$this->spawned or !$this->isAlive()){
return true;
@ -2851,7 +2846,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
if(isset($this->windowIndex[$packet->windowId])){
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
@ -2901,7 +2896,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->spawned or !$this->isAlive()){
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$pos = new Vector3($packet->x, $packet->y, $packet->z);
if($pos->distanceSquared($this) > 10000 or $this->level->checkSpawnProtection($this, $pos)){
@ -3343,7 +3338,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @param string $reason Reason showed in console
* @param bool $notify
*/
final public function close($message = "", string $reason = "generic reason", bool $notify = true){
final public function close($message = "", string $reason = "generic reason", bool $notify = true) : void{
if($this->isConnected() and !$this->closed){
try{
@ -3494,7 +3489,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
public function kill(){
public function kill() : void{
if(!$this->spawned){
return;
}
@ -3504,7 +3499,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendRespawnPacket($this->getSpawn());
}
protected function onDeath(){
protected function onDeath() : void{
$message = "death.attack.generic";
$params = [
@ -3619,7 +3614,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//Crafting grid must always be evacuated even if keep-inventory is true. This dumps the contents into the
//main inventory and drops the rest on the ground.
$this->resetCraftingGridType();
$this->doCloseInventory();
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params)));
@ -3680,8 +3675,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendData($this->getViewers());
$this->sendSettings();
$this->inventory->sendContents($this);
$this->armorInventory->sendContents($this);
$this->sendAllInventories();
$this->spawnToAll();
$this->scheduleUpdate();
@ -3693,7 +3687,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_DAMAGE);
}
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
if(!$this->isAlive()){
return;
}
@ -3710,11 +3704,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
parent::attack($source);
}
protected function doHitAnimation() : void{
parent::doHitAnimation();
if($this->spawned){
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION, null, [$this]);
public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{
if($this->spawned and $players === null){
$players = $this->getViewers();
$players[] = $this;
}
parent::broadcastEntityEvent($eventId, $eventData, $players);
}
public function getOffsetPosition(Vector3 $vector3) : Vector3{
@ -3802,15 +3797,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->craftingGrid = $grid;
}
public function resetCraftingGridType() : void{
$contents = $this->craftingGrid->getContents();
if(count($contents) > 0){
$drops = $this->inventory->addItem(...$contents);
foreach($drops as $drop){
$this->dropItem($drop);
}
public function doCloseInventory() : void{
/** @var Inventory[] $inventories */
$inventories = [$this->craftingGrid, $this->cursorInventory];
foreach($inventories as $inventory){
$contents = $inventory->getContents();
if(count($contents) > 0){
$drops = $this->inventory->addItem(...$contents);
foreach($drops as $drop){
$this->dropItem($drop);
}
$this->craftingGrid->clearAll();
$inventory->clearAll();
}
}
if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){

View File

@ -24,49 +24,6 @@ declare(strict_types=1);
namespace {
const INT32_MIN = -0x80000000;
const INT32_MAX = 0x7fffffff;
function safe_var_dump(){
static $cnt = 0;
foreach(func_get_args() as $var){
switch(true){
case is_array($var):
echo str_repeat(" ", $cnt) . "array(" . count($var) . ") {" . PHP_EOL;
foreach($var as $key => $value){
echo str_repeat(" ", $cnt + 1) . "[" . (is_int($key) ? $key : '"' . $key . '"') . "]=>" . PHP_EOL;
++$cnt;
safe_var_dump($value);
--$cnt;
}
echo str_repeat(" ", $cnt) . "}" . PHP_EOL;
break;
case is_int($var):
echo str_repeat(" ", $cnt) . "int(" . $var . ")" . PHP_EOL;
break;
case is_float($var):
echo str_repeat(" ", $cnt) . "float(" . $var . ")" . PHP_EOL;
break;
case is_bool($var):
echo str_repeat(" ", $cnt) . "bool(" . ($var === true ? "true" : "false") . ")" . PHP_EOL;
break;
case is_string($var):
echo str_repeat(" ", $cnt) . "string(" . strlen($var) . ") \"$var\"" . PHP_EOL;
break;
case is_resource($var):
echo str_repeat(" ", $cnt) . "resource() of type (" . get_resource_type($var) . ")" . PHP_EOL;
break;
case is_object($var):
echo str_repeat(" ", $cnt) . "object(" . get_class($var) . ")" . PHP_EOL;
break;
case is_null($var):
echo str_repeat(" ", $cnt) . "NULL" . PHP_EOL;
break;
}
}
}
function dummy(){
}
}
namespace pocketmine {
@ -76,13 +33,13 @@ namespace pocketmine {
use pocketmine\utils\Terminal;
use pocketmine\utils\Timezone;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
use pocketmine\wizard\SetupWizard;
use raklib\RakLib;
const NAME = "PocketMine-MP";
const VERSION = "1.7dev";
const API_VERSION = "3.0.0-ALPHA12";
const CODENAME = "[REDACTED]";
const BASE_VERSION = "3.0.10";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;
const MIN_PHP_VERSION = "7.2.0";
@ -98,13 +55,14 @@ namespace pocketmine {
if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
critical_error(\pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . ".");
critical_error("Please use the installer provided on the homepage, or update to a newer PHP version.");
critical_error("Please refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
exit(1);
}
if(PHP_INT_SIZE < 8){
critical_error("Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported.");
critical_error("Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.");
critical_error("Please refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
exit(1);
}
@ -120,12 +78,18 @@ namespace pocketmine {
$extensions = [
"bcmath" => "BC Math",
"curl" => "cURL",
"ctype" => "ctype",
"date" => "Date",
"hash" => "Hash",
"json" => "JSON",
"mbstring" => "Multibyte String",
"openssl" => "OpenSSL",
"pcre" => "PCRE",
"phar" => "Phar",
"pthreads" => "pthreads",
"reflection" => "Reflection",
"sockets" => "Sockets",
"spl" => "SPL",
"yaml" => "YAML",
"zip" => "Zip",
"zlib" => "Zlib"
@ -143,8 +107,8 @@ namespace pocketmine {
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "3.1.7-dev") < 0){
critical_error("pthreads >= 3.1.7-dev is required, while you have $pthreads_version.");
if(version_compare($pthreads_version, "3.1.7dev") < 0){
critical_error("pthreads >= 3.1.7dev is required, while you have $pthreads_version.");
++$errors;
}
}
@ -163,22 +127,12 @@ namespace pocketmine {
}
if($errors > 0){
critical_error("Please use the installer provided on the homepage, or recompile PHP again.");
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
exit(1);
}
error_reporting(-1);
function error_handler($severity, $message, $file, $line){
if(error_reporting() & $severity){
throw new \ErrorException($message, 0, $severity, $file, $line);
}else{ //stfu operator
return true;
}
}
set_error_handler('\pocketmine\error_handler');
if(\Phar::running(true) !== ""){
define('pocketmine\PATH', \Phar::running(true) . "/");
}else{
@ -187,27 +141,15 @@ namespace pocketmine {
define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
function composer_error_die($message){
critical_error($message);
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{
critical_error("Composer autoloader not found.");
critical_error("Please install/update Composer dependencies or use provided builds.");
exit(1);
}
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{
composer_error_die("Composer autoloader not found.");
}
if(!class_exists(RakLib::class)){
composer_error_die("Unable to find the RakLib library.");
}
if(version_compare(RakLib::VERSION, "0.11.0") < 0){ //TODO: remove this check (it's managed by Composer now)
composer_error_die("RakLib version 0.11.0 is required, while you have version " . RakLib::VERSION . ".");
}
if(!class_exists(\BaseClassLoader::class)){
composer_error_die("Unable to find the PocketMine-SPL library.");
}
set_error_handler([Utils::class, 'errorExceptionHandler']);
/*
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
@ -227,14 +169,10 @@ namespace pocketmine {
define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-profiler"]);
$opts = getopt("", ["data:", "plugins:", "no-wizard"]);
define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : \realpath(\getcwd()) . DIRECTORY_SEPARATOR);
define('pocketmine\PLUGIN_PATH', isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : \realpath(\getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
Terminal::init();
define('pocketmine\ANSI', Terminal::hasFormattingCodes());
define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR);
define('pocketmine\PLUGIN_PATH', isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
if(!file_exists(\pocketmine\DATA)){
mkdir(\pocketmine\DATA, 0777, true);
@ -251,15 +189,6 @@ namespace pocketmine {
}
unset($tzError);
if(isset($opts["enable-profiler"])){
if(function_exists("profiler_enable")){
\profiler_enable();
$logger->notice("Execution is being profiled");
}else{
$logger->notice("No profiler found. Please install https://github.com/krakjoe/profiler");
}
}
if(extension_loaded("xdebug")){
$logger->warning(PHP_EOL . PHP_EOL . PHP_EOL . "\tYou are running " . \pocketmine\NAME . " with xdebug enabled. This has a major impact on performance." . PHP_EOL . PHP_EOL);
}
@ -268,10 +197,13 @@ namespace pocketmine {
$logger->warning("Non-packaged " . \pocketmine\NAME . " installation detected. Consider using a phar in production for better performance.");
}
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
define('pocketmine\VERSION', $version->getFullVersion(true));
$gitHash = str_repeat("00", 20);
if(\Phar::running(true) === ""){
if(Utils::execute("git rev-parse HEAD", $out) === 0){
if(Utils::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){
$gitHash = trim($out);
if(Utils::execute("git diff --quiet") === 1 or Utils::execute("git diff --cached --quiet") === 1){ //Locally-modified
$gitHash .= "-dirty";

View File

@ -47,10 +47,11 @@ use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\lang\BaseLang;
use pocketmine\lang\TextContainer;
use pocketmine\level\biome\Biome;
use pocketmine\level\format\io\LevelProvider;
use pocketmine\level\format\io\LevelProviderManager;
use pocketmine\level\generator\biome\Biome;
use pocketmine\level\generator\Generator;
use pocketmine\level\generator\GeneratorManager;
use pocketmine\level\Level;
use pocketmine\level\LevelException;
use pocketmine\metadata\EntityMetadataStore;
@ -87,9 +88,11 @@ use pocketmine\plugin\PluginLoadOrder;
use pocketmine\plugin\PluginManager;
use pocketmine\plugin\ScriptPluginLoader;
use pocketmine\resourcepacks\ResourcePackManager;
use pocketmine\scheduler\AsyncPool;
use pocketmine\scheduler\FileWriteTask;
use pocketmine\scheduler\SendUsageTask;
use pocketmine\scheduler\ServerScheduler;
use pocketmine\snooze\SleeperHandler;
use pocketmine\snooze\SleeperNotifier;
use pocketmine\tile\Tile;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
@ -101,7 +104,6 @@ use pocketmine\utils\Terminal;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\utils\UUID;
use pocketmine\utils\VersionString;
/**
* The class that manages everything
@ -116,6 +118,9 @@ class Server{
/** @var \Threaded */
private static $sleeper = null;
/** @var SleeperHandler */
private $tickSleeper;
/** @var BanList */
private $banByName = null;
@ -131,18 +136,20 @@ class Server{
/** @var bool */
private $isRunning = true;
/** @var bool */
private $hasStopped = false;
/** @var PluginManager */
private $pluginManager = null;
/** @var float */
private $profilingTickRate = 20;
/** @var AutoUpdater */
private $updater = null;
/** @var ServerScheduler */
private $scheduler = null;
/** @var AsyncPool */
private $asyncPool;
/**
* Counts the ticks since the server start
@ -150,17 +157,24 @@ class Server{
* @var int
*/
private $tickCounter = 0;
/** @var int */
private $nextTick = 0;
/** @var float[] */
private $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
/** @var float[] */
private $useAverage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/** @var float */
private $currentTPS = 20;
/** @var float */
private $currentUse = 0;
/** @var bool */
private $doTitleTick = true;
/** @var int */
private $sendUsageTicker = 0;
/** @var bool */
private $dispatchSignals = false;
/** @var \AttachableThreadedLogger */
@ -207,29 +221,41 @@ class Server{
/** @var Network */
private $network;
/** @var bool */
private $networkCompressionAsync = true;
/** @var int */
public $networkCompressionLevel = 7;
/** @var bool */
private $autoTickRate = true;
/** @var int */
private $autoTickRateLimit = 20;
/** @var bool */
private $alwaysTickPlayers = false;
/** @var int */
private $baseTickRate = 1;
/** @var int */
private $autoSaveTicker = 0;
/** @var int */
private $autoSaveTicks = 6000;
/** @var BaseLang */
private $baseLang;
/** @var bool */
private $forceLanguage = false;
/** @var UUID */
private $serverID;
/** @var \ClassLoader */
private $autoloader;
/** @var string */
private $dataPath;
/** @var string */
private $pluginPath;
/** @var string[] */
private $uniquePlayers = [];
/** @var QueryHandler */
@ -240,7 +266,7 @@ class Server{
/** @var Config */
private $properties;
/** @var mixed[] */
private $propertyCache = [];
/** @var Config */
@ -282,13 +308,6 @@ class Server{
return \pocketmine\VERSION;
}
/**
* @return string
*/
public function getCodename() : string{
return \pocketmine\CODENAME;
}
/**
* @return string
*/
@ -300,7 +319,7 @@ class Server{
* @return string
*/
public function getApiVersion() : string{
return \pocketmine\API_VERSION;
return \pocketmine\BASE_VERSION;
}
/**
@ -385,7 +404,8 @@ class Server{
* @return string
*/
public function getIp() : string{
return $this->getConfigString("server-ip", "0.0.0.0");
$str = $this->getConfigString("server-ip");
return $str !== "" ? $str : "0.0.0.0";
}
/**
@ -568,7 +588,7 @@ class Server{
}
/**
* @return MainLogger
* @return \AttachableThreadedLogger
*/
public function getLogger(){
return $this->logger;
@ -623,11 +643,8 @@ class Server{
return $this->resourceManager;
}
/**
* @return ServerScheduler
*/
public function getScheduler(){
return $this->scheduler;
public function getAsyncPool() : AsyncPool{
return $this->asyncPool;
}
/**
@ -797,7 +814,7 @@ class Server{
$nbt = new BigEndianNBTStream();
try{
if($async){
$this->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData())));
$this->asyncPool->submitTask(new FileWriteTask($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData())));
}else{
file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed($ev->getSaveData()));
}
@ -1000,16 +1017,16 @@ class Server{
$path = $this->getDataPath() . "worlds/" . $name . "/";
$provider = LevelProviderManager::getProvider($path);
$providerClass = LevelProviderManager::getProvider($path);
if($provider === null){
if($providerClass === null){
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, "Cannot identify format of world"]));
return false;
}
try{
$level = new Level($this, $name, $path, $provider);
$level = new Level($this, $name, new $providerClass($path));
}catch(\Throwable $e){
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, $e->getMessage()]));
@ -1019,8 +1036,6 @@ class Server{
$this->levels[$level->getId()] = $level;
$level->initLevel();
$this->getPluginManager()->callEvent(new LevelLoadEvent($level));
$level->setTickRate($this->baseTickRate);
@ -1050,23 +1065,24 @@ class Server{
}
if(!($generator !== null and class_exists($generator, true) and is_subclass_of($generator, Generator::class))){
$generator = Generator::getGenerator($this->getLevelType());
$generator = GeneratorManager::getGenerator($this->getLevelType());
}
if(($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "pmanvil"))) === null){
$provider = LevelProviderManager::getProviderByName($providerName = "pmanvil");
if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){
$providerClass = LevelProviderManager::getProviderByName("pmanvil");
if($providerClass === null){
throw new \InvalidStateException("Default level provider has not been registered");
}
}
try{
$path = $this->getDataPath() . "worlds/" . $name . "/";
/** @var LevelProvider $provider */
$provider::generate($path, $name, $seed, $generator, $options);
/** @var LevelProvider $providerClass */
$providerClass::generate($path, $name, $seed, $generator, $options);
$level = new Level($this, $name, $path, (string) $provider);
$level = new Level($this, $name, new $providerClass($path));
$this->levels[$level->getId()] = $level;
$level->initLevel();
$level->setTickRate($this->baseTickRate);
}catch(\Throwable $e){
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.generationError", [$name, $e->getMessage()]));
@ -1393,17 +1409,18 @@ class Server{
}
/**
* @param \ClassLoader $autoloader
* @param \ThreadedLogger $logger
* @param string $dataPath
* @param string $pluginPath
* @param \ClassLoader $autoloader
* @param \AttachableThreadedLogger $logger
* @param string $dataPath
* @param string $pluginPath
*/
public function __construct(\ClassLoader $autoloader, \ThreadedLogger $logger, string $dataPath, string $pluginPath){
public function __construct(\ClassLoader $autoloader, \AttachableThreadedLogger $logger, string $dataPath, string $pluginPath){
if(self::$instance !== null){
throw new \InvalidStateException("Only one server instance can exist at once");
}
self::$instance = $this;
self::$sleeper = new \Threaded;
$this->tickSleeper = new SleeperHandler();
$this->autoloader = $autoloader;
$this->logger = $logger;
@ -1423,14 +1440,10 @@ class Server{
$this->dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
$this->pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
$this->console = new CommandReader();
$version = new VersionString($this->getPocketMineVersion());
$this->logger->info("Loading pocketmine.yml...");
if(!file_exists($this->dataPath . "pocketmine.yml")){
$content = file_get_contents(\pocketmine\RESOURCE_PATH . "pocketmine.yml");
if($version->isDev()){
if(\pocketmine\IS_DEVELOPMENT_BUILD){
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
}
@file_put_contents($this->dataPath . "pocketmine.yml", $content);
@ -1439,6 +1452,19 @@ class Server{
define('pocketmine\DEBUG', (int) $this->getProperty("debug.level", 1));
$this->forceLanguage = (bool) $this->getProperty("settings.force-language", false);
$this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE));
$this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()]));
if(\pocketmine\IS_DEVELOPMENT_BUILD and !((bool) $this->getProperty("settings.enable-dev-builds", false))){
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error1", [\pocketmine\NAME]));
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error2"));
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error3"));
$this->logger->emergency($this->baseLang->translateString("pocketmine.server.devBuild.error4", ["settings.enable-dev-builds"]));
$this->forceShutdown();
return;
}
if(((int) ini_get('zend.assertions')) > 0 and ((bool) $this->getProperty("debug.assertions.warn-if-enabled", true)) !== false){
$this->logger->warning("Debugging assertions are enabled, this may impact on performance. To disable them, set `zend.assertions = -1` in php.ini.");
}
@ -1477,26 +1503,22 @@ class Server{
"xbox-auth" => true
]);
$this->forceLanguage = (bool) $this->getProperty("settings.force-language", false);
$this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE));
$this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()]));
$this->memoryManager = new MemoryManager($this);
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.start", [TextFormat::AQUA . $this->getVersion() . TextFormat::RESET]));
if(($poolSize = $this->getProperty("settings.async-workers", "auto")) === "auto"){
$poolSize = ServerScheduler::$WORKERS;
$poolSize = 2;
$processors = Utils::getCoreCount() - 2;
if($processors > 0){
$poolSize = max(1, $processors);
}
}else{
$poolSize = (int) $poolSize;
$poolSize = max(1, (int) $poolSize);
}
ServerScheduler::$WORKERS = $poolSize;
$this->asyncPool = new AsyncPool($this, $poolSize, (int) max(-1, (int) $this->getProperty("memory.async-worker-hard-limit", 256)), $this->autoloader, $this->logger);
if($this->getProperty("network.batch-threshold", 256) >= 0){
Network::$BATCH_THRESHOLD = (int) $this->getProperty("network.batch-threshold", 256);
@ -1509,16 +1531,21 @@ class Server{
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 7");
$this->networkCompressionLevel = 7;
}
$this->networkCompressionAsync = $this->getProperty("network.async-compression", true);
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
$this->autoTickRate = (bool) $this->getProperty("level-settings.auto-tick-rate", true);
$this->autoTickRateLimit = (int) $this->getProperty("level-settings.auto-tick-rate-limit", 20);
$this->alwaysTickPlayers = (int) $this->getProperty("level-settings.always-tick-players", false);
$this->alwaysTickPlayers = (bool) $this->getProperty("level-settings.always-tick-players", false);
$this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1);
$this->doTitleTick = (bool) $this->getProperty("console.title-tick", true);
$this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes();
$this->scheduler = new ServerScheduler();
$consoleNotifier = new SleeperNotifier();
$this->console = new CommandReader($consoleNotifier);
$this->tickSleeper->addNotifier($consoleNotifier, function() : void{
$this->checkConsole();
});
$this->console->start(PTHREADS_INHERIT_NONE);
if($this->getConfigBool("enable-rcon", false)){
try{
@ -1526,11 +1553,10 @@ class Server{
$this,
$this->getConfigString("rcon.password", ""),
$this->getConfigInt("rcon.port", $this->getPort()),
($ip = $this->getIp()) != "" ? $ip : "0.0.0.0",
$this->getConfigInt("rcon.threads", 1),
$this->getConfigInt("rcon.clients-per-thread", 50)
$this->getIp(),
$this->getConfigInt("rcon.max-clients", 50)
);
}catch(\Throwable $e){
}catch(\Exception $e){
$this->getLogger()->critical("RCON can't be started: " . $e->getMessage());
}
}
@ -1572,7 +1598,7 @@ class Server{
@cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion());
}
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp() === "" ? "*" : $this->getIp(), $this->getPort()]));
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp(), $this->getPort()]));
define("BOOTUP_RANDOM", random_bytes(16));
$this->serverID = Utils::getMachineUniqueId($this->getIp() . $this->getPort());
@ -1585,9 +1611,7 @@ class Server{
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.info", [
$this->getName(),
($version->isDev() ? TextFormat::YELLOW : "") . $version->get(true) . TextFormat::RESET,
$this->getCodename(),
$this->getApiVersion()
(\pocketmine\IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET
]));
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.license", [$this->getName()]));
@ -1601,6 +1625,7 @@ class Server{
Entity::init();
Tile::init();
BlockFactory::init();
BlockFactory::registerStaticRuntimeIdMappings();
Enchantment::init();
ItemFactory::init();
Item::initCreativeItems();
@ -1608,13 +1633,13 @@ class Server{
$this->craftingManager = new CraftingManager();
$this->resourceManager = new ResourcePackManager($this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR);
$this->resourceManager = new ResourcePackManager($this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR, $this->logger);
$this->pluginManager = new PluginManager($this, $this->commandMap);
$this->pluginManager = new PluginManager($this, $this->commandMap, ((bool) $this->getProperty("plugins.legacy-data-dir", true)) ? null : $this->getDataPath() . "plugin_data" . DIRECTORY_SEPARATOR);
$this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender);
$this->profilingTickRate = (float) $this->getProperty("settings.profile-report-trigger", 20);
$this->pluginManager->registerInterface(PharPluginLoader::class);
$this->pluginManager->registerInterface(ScriptPluginLoader::class);
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
$this->pluginManager->registerInterface(new ScriptPluginLoader());
register_shutdown_function([$this, "crashDump"]);
@ -1633,31 +1658,24 @@ class Server{
$this->logger->debug($this->getLanguage()->translateString("pocketmine.debug.enable"));
}
Generator::registerDefaultGenerators();
GeneratorManager::registerDefaultGenerators();
foreach((array) $this->getProperty("worlds", []) as $name => $options){
if(!is_array($options)){
continue;
}
if(!$this->loadLevel($name)){
$seed = $options["seed"] ?? time();
if(is_string($seed) and !is_numeric($seed)){
$seed = Utils::javaStringHash($seed);
}elseif(!is_int($seed)){
$seed = (int) $seed;
}
if(isset($options["generator"])){
$generatorOptions = explode(":", $options["generator"]);
$generator = Generator::getGenerator(array_shift($generatorOptions));
$generator = GeneratorManager::getGenerator(array_shift($generatorOptions));
if(count($options) > 0){
$options["preset"] = implode(":", $generatorOptions);
}
}else{
$generator = Generator::getGenerator("default");
$generator = GeneratorManager::getGenerator("default");
}
$this->generateLevel($name, $seed, $generator, $options);
$this->generateLevel($name, Generator::convertSeed((string) ($options["seed"] ?? "")), $generator, $options);
}
}
@ -1669,13 +1687,7 @@ class Server{
$this->setConfigString("level-name", "world");
}
if(!$this->loadLevel($default)){
$seed = getopt("", ["level-seed::"])["level-seed"] ?? $this->properties->get("level-seed", time());
if(!is_numeric($seed) or bccomp($seed, "9223372036854775807") > 0){
$seed = Utils::javaStringHash($seed);
}elseif(PHP_INT_SIZE === 8){
$seed = (int) $seed;
}
$this->generateLevel($default, $seed === 0 ? time() : $seed);
$this->generateLevel($default, Generator::convertSeed($this->getConfigString("level-seed")));
}
$this->setDefaultLevel($this->getLevelByName($default));
@ -1775,11 +1787,11 @@ class Server{
}
/**
* @param string $title
* @param string $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.
* @param string $title
* @param string $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.
* @param Player[]|null $recipients
*
* @return int
@ -1871,7 +1883,7 @@ class Server{
if(!$forceSync and !$immediate and $this->networkCompressionAsync){
$task = new CompressBatchedTask($pk, $targets);
$this->getScheduler()->scheduleAsyncTask($task);
$this->asyncPool->submitTask($task);
}else{
$this->broadcastPacketsCallback($pk, $targets, $immediate);
}
@ -1925,7 +1937,7 @@ class Server{
public function checkConsole(){
Timings::$serverCommandTimer->startTiming();
if(($line = $this->console->getLine()) !== null){
while(($line = $this->console->getLine()) !== null){
$this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line));
if(!$ev->isCancelled()){
$this->dispatchCommand($ev->getSender(), $ev->getCommand());
@ -1981,8 +1993,8 @@ class Server{
$this->getNetwork()->blockAddress($entry->getName(), -1);
}
$this->pluginManager->registerInterface(PharPluginLoader::class);
$this->pluginManager->registerInterface(ScriptPluginLoader::class);
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
$this->pluginManager->registerInterface(new ScriptPluginLoader());
$this->pluginManager->loadPlugins($this->pluginPath);
$this->enablePlugins(PluginLoadOrder::STARTUP);
$this->enablePlugins(PluginLoadOrder::POSTWORLD);
@ -1990,7 +2002,7 @@ class Server{
}
/**
* Shutdowns the server correctly
* Shuts the server down correctly
*/
public function shutdown(){
$this->isRunning = false;
@ -2035,20 +2047,21 @@ class Server{
$this->getLogger()->debug("Removing event handlers");
HandlerList::unregisterAll();
if($this->scheduler instanceof ServerScheduler){
$this->getLogger()->debug("Stopping all tasks");
$this->scheduler->cancelAllTasks();
$this->scheduler->mainThreadHeartbeat(PHP_INT_MAX);
if($this->asyncPool instanceof AsyncPool){
$this->getLogger()->debug("Shutting down async task worker pool");
$this->asyncPool->shutdown();
}
if($this->properties->hasChanged()){
if($this->properties !== null and $this->properties->hasChanged()){
$this->getLogger()->debug("Saving properties");
$this->properties->save();
}
$this->getLogger()->debug("Closing console");
$this->console->shutdown();
$this->console->notify();
if($this->console instanceof CommandReader){
$this->getLogger()->debug("Closing console");
$this->console->shutdown();
$this->console->notify();
}
if($this->network instanceof Network){
$this->getLogger()->debug("Stopping network interfaces");
@ -2098,7 +2111,7 @@ class Server{
$this->logger->info("[UPnP] Trying to port forward...");
try{
UPnP::PortForward($this->getPort());
}catch(\Throwable $e){
}catch(\Exception $e){
$this->logger->alert("UPnP portforward failed: " . $e->getMessage());
}
}
@ -2131,10 +2144,6 @@ class Server{
* @param array|null $trace
*/
public function exceptionHandler(\Throwable $e, $trace = null){
if($e === null){
return;
}
global $lastError;
if($trace === null){
@ -2152,7 +2161,7 @@ class Server{
$this->logger->logException($e, $trace);
$lastError = [
"type" => \get_class($e),
"type" => get_class($e),
"message" => $errstr,
"fullFile" => $e->getFile(),
"file" => $errfile,
@ -2192,7 +2201,7 @@ class Server{
}
}
if($dump->getData()["error"]["type"] === "E_PARSE" or $dump->getData()["error"]["type"] === "E_COMPILE_ERROR"){
if($dump->getData()["error"]["type"] === \ParseError::class){
$report = false;
}
@ -2224,9 +2233,6 @@ class Server{
}catch(\Throwable $e){}
}
//$this->checkMemory();
//$dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n";
$this->forceShutdown();
$this->isRunning = false;
@Utils::kill(getmypid());
@ -2237,18 +2243,18 @@ class Server{
return [];
}
public function getTickSleeper() : SleeperHandler{
return $this->tickSleeper;
}
private function tickProcessor(){
$this->nextTick = microtime(true);
while($this->isRunning){
$this->tick();
$next = $this->nextTick - 0.0001;
if($next > microtime(true)){
try{
@time_sleep_until($next);
}catch(\Throwable $e){
//Sometimes $next is less than the current time. High load?
}
}
//sleeps are self-correcting - if we undersleep 1ms on this tick, we'll sleep an extra ms on the next tick
$this->tickSleeper->sleepUntil($this->nextTick);
}
}
@ -2330,7 +2336,7 @@ class Server{
$p->dataPacket($pk);
}
private function checkTickUpdates($currentTick, $tickTime){
private function checkTickUpdates(int $currentTick, float $tickTime) : void{
foreach($this->players as $p){
if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){
$p->close("", "Login timeout");
@ -2399,7 +2405,7 @@ class Server{
public function sendUsage($type = SendUsageTask::TYPE_STATUS){
if((bool) $this->getProperty("anonymous-statistics.enabled", true)){
$this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers));
$this->asyncPool->submitTask(new SendUsageTask($this, $type, $this->uniquePlayers));
}
$this->uniquePlayers = [];
}
@ -2464,6 +2470,8 @@ class Server{
try{
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
$this->queryHandler->handle($interface, $address, $port, $payload);
}else{
$this->logger->debug("Unhandled raw packet from $address $port: " . bin2hex($payload));
}
}catch(\Throwable $e){
if(\pocketmine\DEBUG > 1){
@ -2479,31 +2487,28 @@ class Server{
/**
* Tries to execute a server tick
*/
private function tick() : bool{
private function tick() : void{
$tickTime = microtime(true);
if(($tickTime - $this->nextTick) < -0.025){ //Allow half a tick of diff
return false;
return;
}
Timings::$serverTickTimer->startTiming();
++$this->tickCounter;
$this->checkConsole();
Timings::$connectionTimer->startTiming();
$this->network->processInterfaces();
if($this->rcon !== null){
$this->rcon->check();
}
Timings::$connectionTimer->stopTiming();
Timings::$schedulerTimer->startTiming();
$this->scheduler->mainThreadHeartbeat($this->tickCounter);
$this->pluginManager->tickSchedulers($this->tickCounter);
Timings::$schedulerTimer->stopTiming();
Timings::$schedulerAsyncTimer->startTiming();
$this->asyncPool->collectTasks();
Timings::$schedulerAsyncTimer->stopTiming();
$this->checkTickUpdates($this->tickCounter, $tickTime);
foreach($this->players as $player){
@ -2511,7 +2516,7 @@ class Server{
}
if(($this->tickCounter % 20) === 0){
if($this->doTitleTick and Terminal::hasFormattingCodes()){
if($this->doTitleTick){
$this->titleTick();
}
$this->currentTPS = 20;
@ -2576,8 +2581,6 @@ class Server{
}else{
$this->nextTick += 0.05;
}
return true;
}
/**

View File

@ -67,14 +67,10 @@ abstract class Thread extends \Thread{
public function start(?int $options = \PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this);
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return parent::start($options);
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return false;
return parent::start($options);
}
/**
@ -83,12 +79,9 @@ abstract class Thread extends \Thread{
public function quit(){
$this->isKilled = true;
$this->notify();
if(!$this->isJoined()){
if(!$this->isTerminated()){
$this->join();
}
$this->notify();
$this->join();
}
ThreadManager::getInstance()->remove($this);

View File

@ -67,14 +67,10 @@ abstract class Worker extends \Worker{
public function start(?int $options = \PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this);
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return parent::start($options);
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return false;
return parent::start($options);
}
/**
@ -83,16 +79,10 @@ abstract class Worker extends \Worker{
public function quit(){
$this->isKilled = true;
$this->notify();
if($this->isRunning()){
$this->shutdown();
while($this->unstack() !== null);
$this->notify();
$this->unstack();
}elseif(!$this->isJoined()){
if(!$this->isTerminated()){
$this->join();
}
$this->shutdown();
}
ThreadManager::getInstance()->remove($this);

View File

@ -81,5 +81,4 @@ class Air extends Transparent{
public function getBlastResistance() : float{
return 0;
}
}

View File

@ -25,7 +25,6 @@ namespace pocketmine\block;
use pocketmine\inventory\AnvilInventory;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\TieredTool;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
@ -55,13 +54,17 @@ class Anvil extends Fallable{
return 6000;
}
public function getVariantBitmask() : int{
return 0x0c;
}
public function getName() : string{
static $names = [
self::TYPE_NORMAL => "Anvil",
self::TYPE_SLIGHTLY_DAMAGED => "Slightly Damaged Anvil",
self::TYPE_VERY_DAMAGED => "Very Damaged Anvil"
];
return $names[$this->meta & 0x0c] ?? "Anvil";
return $names[$this->getVariant()] ?? "Anvil";
}
public function getToolType() : int{
@ -106,13 +109,7 @@ 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->meta & 0x0c) | $direction;
$this->meta = $this->getVariant() | $direction;
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
ItemFactory::get($this->getItemId(), $this->getDamage() >> 2)
];
}
}

View File

@ -85,7 +85,7 @@ class Bed extends Transparent{
$this->getLevel()->setBlock($this, $this, false, false);
if(($other = $this->getOtherHalf()) !== null and !$other->isOccupied()){
if(($other = $this->getOtherHalf()) !== null and $other->isOccupied() !== $occupied){
$other->setOccupied($occupied);
}
}

View File

@ -48,5 +48,4 @@ class Bedrock extends Solid{
public function isBreakable(Item $item) : bool{
return false;
}
}

View File

@ -471,6 +471,30 @@ class Block extends Position implements BlockIds, Metadatable{
];
}
/**
* Returns how much XP will be dropped by breaking this block with the given item.
*
* @param Item $item
*
* @return int
*/
public function getXpDropForTool(Item $item) : int{
if($item->hasEnchantment(Enchantment::SILK_TOUCH) or !$this->isCompatibleWithTool($item)){
return 0;
}
return $this->getXpDropAmount();
}
/**
* Returns how much XP this block will drop when broken with an appropriate tool.
*
* @return int
*/
protected function getXpDropAmount() : int{
return 0;
}
/**
* Returns whether Silk Touch enchanted tools will cause this block to drop as itself. Since most blocks drop
* themselves anyway, this is implicitly true.

View File

@ -25,14 +25,11 @@ namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\level\Position;
use pocketmine\utils\MainLogger;
/**
* Manages block registration and instance creation
*/
class BlockFactory{
/** @var \SplFixedArray<Block> */
private static $list = null;
/** @var \SplFixedArray<Block> */
private static $fullList = null;
@ -65,7 +62,6 @@ class BlockFactory{
* this if you need to reset the block factory back to its original defaults for whatever reason.
*/
public static function init() : void{
self::$list = new \SplFixedArray(256);
self::$fullList = new \SplFixedArray(4096);
self::$light = new \SplFixedArray(256);
@ -327,17 +323,11 @@ class BlockFactory{
//TODO: RESERVED6
foreach(self::$list as $id => $block){
if($block === null){
for($id = 0, $size = self::$fullList->getSize() >> 4; $id < $size; ++$id){
if(self::$fullList[$id << 4] === null){
self::registerBlock(new UnknownBlock($id));
}
}
/** @var mixed[] $runtimeIdMap */
$runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
foreach($runtimeIdMap as $obj){
self::registerMapping($obj["runtimeID"], $obj["id"], $obj["data"]);
}
}
/**
@ -360,8 +350,6 @@ class BlockFactory{
throw new \RuntimeException("Trying to overwrite an already registered block");
}
self::$list[$id] = clone $block;
for($meta = 0; $meta < 16; ++$meta){
$variant = clone $block;
$variant->setDamage($meta);
@ -372,7 +360,7 @@ class BlockFactory{
self::$transparent[$id] = $block->isTransparent();
self::$hardness[$id] = $block->getHardness();
self::$light[$id] = $block->getLightLevel();
self::$lightFilter[$id] = $block->getLightFilter() + 1; //opacity plus 1 standard light filter
self::$lightFilter[$id] = min(15, $block->getLightFilter() + 1); //opacity plus 1 standard light filter
self::$diffusesSkyLight[$id] = $block->diffusesSkyLight();
self::$blastResistance[$id] = $block->getBlastResistance();
}
@ -426,10 +414,18 @@ class BlockFactory{
* @return bool
*/
public static function isRegistered(int $id) : bool{
$b = self::$list[$id];
$b = self::$fullList[$id << 4];
return $b !== null and !($b instanceof UnknownBlock);
}
public static function registerStaticRuntimeIdMappings() : void{
/** @var mixed[] $runtimeIdMap */
$runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
foreach($runtimeIdMap as $obj){
self::registerMapping($obj["runtimeID"], $obj["id"], $obj["data"]);
}
}
/**
* @internal
*
@ -439,19 +435,12 @@ class BlockFactory{
* @return int
*/
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
if($id === Block::AIR){
//TODO: HACK! (weird air blocks with non-zero damage values shouldn't turn into update! blocks)
$meta = 0;
}
$index = ($id << 4) | $meta;
if(!isset(self::$staticRuntimeIdMap[$index])){
self::registerMapping($rtId = ++self::$lastRuntimeId, $id, $meta);
MainLogger::getLogger()->error("ID $id meta $meta does not have a corresponding block static runtime ID, added a new unknown runtime ID ($rtId)");
return $rtId;
}
return self::$staticRuntimeIdMap[$index];
/*
* try id+meta first
* if not found, try id+0 (strip meta)
* if still not found, return update! block
*/
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
}
/**

View File

@ -52,5 +52,4 @@ class BrickStairs extends Stair{
public function getName() : string{
return "Brick Stairs";
}
}

View File

@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\item\TieredTool;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\tile\Furnace as TileFurnace;
use pocketmine\tile\Tile;
@ -83,7 +82,7 @@ class BurningFurnace extends Solid{
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this));
}
if($furnace->namedtag->hasTag("Lock", StringTag::class) and $furnace->namedtag->getString("Lock") !== $item->getCustomName()){
if(!$furnace->canOpenWith($item->getCustomName())){
return true;
}

View File

@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\tile\Chest as TileChest;
use pocketmine\tile\Tile;
@ -115,7 +114,7 @@ class Chest extends Transparent{
if(
!$this->getSide(Vector3::SIDE_UP)->isTransparent() or
($chest->isPaired() and !$chest->getPair()->getBlock()->getSide(Vector3::SIDE_UP)->isTransparent()) or
($chest->namedtag->hasTag("Lock", StringTag::class) and $chest->namedtag->getString("Lock") !== $item->getCustomName())
!$chest->canOpenWith($item->getCustomName())
){
return true;
}

View File

@ -57,4 +57,7 @@ class CoalOre extends Solid{
];
}
public function getXpDropForTool(Item $item) : int{
return mt_rand(0, 2);
}
}

View File

@ -48,5 +48,4 @@ class CobblestoneStairs extends Stair{
public function getName() : string{
return "Cobblestone Stairs";
}
}

View File

@ -90,5 +90,4 @@ class CobblestoneWall extends Transparent{
public function canConnect(Block $block){
return $block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent());
}
}

View File

@ -66,7 +66,7 @@ class ConcretePowder extends Fallable{
private function checkAdjacentWater() : ?Block{
for($i = 1; $i < 6; ++$i){ //Do not check underneath
if($this->getSide($i) instanceof Water){
return Block::get(Block::CONCRETE, $this->meta);
return BlockFactory::get(Block::CONCRETE, $this->meta);
}
}

View File

@ -56,4 +56,8 @@ class DiamondOre extends Solid{
ItemFactory::get(Item::DIAMOND)
];
}
protected function getXpDropAmount() : int{
return mt_rand(3, 7);
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Hoe;
use pocketmine\item\Item;
use pocketmine\Player;
@ -50,8 +51,8 @@ class Dirt extends Solid{
}
public function onActivate(Item $item, Player $player = null) : bool{
if($item->isHoe()){
$item->useOn($this);
if($item instanceof Hoe){
$item->applyDamage(1);
if($this->meta === 1){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::DIRT), true);
}else{

View File

@ -35,7 +35,7 @@ abstract class DoubleSlab extends Solid{
abstract public function getSlabId() : int;
public function getName() : string{
return "Double " . BlockFactory::get($this->getSlabId(), $this->getVariant())->getName() . " Slab";
return "Double " . BlockFactory::get($this->getSlabId(), $this->getVariant())->getName();
}
public function getDropsForCompatibleTool(Item $item) : array{

View File

@ -30,5 +30,4 @@ class DoubleStoneSlab2 extends DoubleStoneSlab{
public function getSlabId() : int{
return self::STONE_SLAB2;
}
}

View File

@ -53,7 +53,11 @@ class EmeraldOre extends Solid{
public function getDropsForCompatibleTool(Item $item) : array{
return [
ItemFactory::get(Item::EMERALD)
ItemFactory::get(Item::EMERALD)
];
}
protected function getXpDropAmount() : int{
return mt_rand(3, 7);
}
}

View File

@ -106,5 +106,4 @@ class EnderChest extends Chest{
public function getFuelTime() : int{
return 0;
}
}

View File

@ -105,5 +105,4 @@ abstract class Fence extends Transparent{
public function canConnect(Block $block){
return $block instanceof static or $block instanceof FenceGate or ($block->isSolid() and !$block->isTransparent());
}
}

View File

@ -24,8 +24,10 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Hoe;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\Shovel;
use pocketmine\level\generator\object\TallGrass as TallGrassObject;
use pocketmine\math\Vector3;
use pocketmine\Player;
@ -98,13 +100,13 @@ class Grass extends Solid{
TallGrassObject::growGrass($this->getLevel(), $this, new Random(mt_rand()), 8, 2);
return true;
}elseif($item->isHoe()){
$item->useOn($this);
}elseif($item instanceof Hoe){
$item->applyDamage(1);
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND));
return true;
}elseif($item->isShovel() and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){
$item->useOn($this);
}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));
return true;

View File

@ -55,5 +55,4 @@ class Gravel extends Fallable{
return parent::getDropsForCompatibleTool($item);
}
}

View File

@ -57,4 +57,7 @@ class LapisOre extends Solid{
];
}
protected function getXpDropAmount() : int{
return mt_rand(2, 5);
}
}

View File

@ -121,5 +121,4 @@ class Lava extends Liquid{
return $ret;
}
}

View File

@ -67,8 +67,7 @@ class Leaves extends Transparent{
}
protected function findLog(Block $pos, array $visited, $distance, &$check, $fromSide = null) : bool{
++$check;
protected function findLog(Block $pos, array $visited, int $distance, ?int $fromSide = null) : bool{
$index = $pos->x . "." . $pos->y . "." . $pos->z;
if(isset($visited[$index])){
return false;
@ -83,45 +82,45 @@ class Leaves extends Transparent{
}
if($fromSide === null){
for($side = 2; $side <= 5; ++$side){
if($this->findLog($pos->getSide($side), $visited, $distance + 1, $check, $side)){
if($this->findLog($pos->getSide($side), $visited, $distance + 1, $side)){
return true;
}
}
}else{ //No more loops
switch($fromSide){
case 2:
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $check, $fromSide)){
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $fromSide)){
return true;
}
break;
case 3:
if($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $check, $fromSide)){
if($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $fromSide)){
return true;
}
break;
case 4:
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $check, $fromSide)){
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_WEST), $visited, $distance + 1, $fromSide)){
return true;
}
break;
case 5:
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $check, $fromSide)){
if($this->findLog($pos->getSide(Vector3::SIDE_NORTH), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_SOUTH), $visited, $distance + 1, $fromSide)){
return true;
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $check, $fromSide)){
}elseif($this->findLog($pos->getSide(Vector3::SIDE_EAST), $visited, $distance + 1, $fromSide)){
return true;
}
break;
@ -147,11 +146,10 @@ class Leaves extends Transparent{
if(($this->meta & 0b00001100) === 0x08){
$this->meta &= 0x03;
$visited = [];
$check = 0;
$this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new LeavesDecayEvent($this));
if($ev->isCancelled() or $this->findLog($this, $visited, 0, $check)){
if($ev->isCancelled() or $this->findLog($this, $visited, 0)){
$this->getLevel()->setBlock($this, $this, false, false);
}else{
$this->getLevel()->useBreakOn($this);

View File

@ -57,4 +57,8 @@ class MonsterSpawner extends Transparent{
public function isAffectedBySilkTouch() : bool{
return false;
}
protected function getXpDropAmount() : int{
return mt_rand(15, 43);
}
}

View File

@ -48,5 +48,4 @@ class NetherBrickStairs extends Stair{
public function getToolHarvestLevel() : int{
return TieredTool::TIER_WOODEN;
}
}

View File

@ -57,4 +57,7 @@ class NetherQuartzOre extends Solid{
];
}
protected function getXpDropAmount() : int{
return mt_rand(2, 5);
}
}

View File

@ -61,5 +61,4 @@ class NetherReactor extends Solid{
ItemFactory::get(Item::DIAMOND, 0, 3)
];
}
}

View File

@ -46,5 +46,4 @@ class PackedIce extends Solid{
public function getToolType() : int{
return BlockToolType::TYPE_PICKAXE;
}
}

View File

@ -48,5 +48,4 @@ class QuartzStairs extends Stair{
public function getName() : string{
return "Quartz Stairs";
}
}

View File

@ -50,5 +50,4 @@ class RedMushroomBlock extends Solid{
Item::get(Item::RED_MUSHROOM, 0, mt_rand(0, 2))
];
}
}

View File

@ -30,5 +30,4 @@ class RedSandstoneStairs extends SandstoneStairs{
public function getName() : string{
return "Red " . parent::getName();
}
}

View File

@ -70,4 +70,8 @@ class RedstoneOre extends Solid{
ItemFactory::get(Item::REDSTONE_DUST, 0, mt_rand(4, 5))
];
}
protected function getXpDropAmount() : int{
return mt_rand(1, 5);
}
}

View File

@ -46,5 +46,4 @@ class Sand extends Fallable{
return "Sand";
}
}

View File

@ -48,5 +48,4 @@ class SandstoneStairs extends Stair{
public function getName() : string{
return "Sandstone Stairs";
}
}

View File

@ -90,7 +90,7 @@ class Sapling extends Flowable{
}
public function onRandomTick() : void{
if(mt_rand(1, 7) === 1){
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());
}else{

View File

@ -51,5 +51,4 @@ class SeaLantern extends Transparent{
ItemFactory::get(Item::PRISMARINE_CRYSTALS, 0, 3)
];
}
}

View File

@ -61,7 +61,7 @@ class SignPost extends Transparent{
if($face !== Vector3::SIDE_DOWN){
if($face === Vector3::SIDE_UP){
$this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f;
$this->meta = $player !== null ? (floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f) : 0;
$this->getLevel()->setBlock($blockReplace, $this, true);
}else{
$this->meta = $face;

View File

@ -56,5 +56,4 @@ class Snow extends Solid{
ItemFactory::get(Item::SNOWBALL, 0, 4)
];
}
}

View File

@ -56,5 +56,4 @@ class SoulSand extends Solid{
$this->z + 1
);
}
}

View File

@ -39,5 +39,4 @@ class Sponge extends Solid{
public function getName() : string{
return "Sponge";
}
}

View File

@ -32,5 +32,4 @@ class StainedGlass extends Glass{
public function getName() : string{
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Glass";
}
}

View File

@ -32,5 +32,4 @@ class StainedGlassPane extends GlassPane{
public function getName() : string{
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Glass Pane";
}
}

View File

@ -92,8 +92,8 @@ class StandingBanner extends Transparent{
$tile = $this->level->getTile($this);
$drop = ItemFactory::get(Item::BANNER, ($tile instanceof TileBanner ? $tile->getBaseColor() : 0));
if($tile instanceof TileBanner and ($patterns = $tile->namedtag->getListTag(TileBanner::TAG_PATTERNS)) !== null and !$patterns->empty()){
$drop->setNamedTagEntry($patterns);
if($tile instanceof TileBanner and !($patterns = $tile->getPatterns())->empty()){
$drop->setNamedTagEntry(clone $patterns);
}
return [$drop];

View File

@ -30,5 +30,4 @@ class StillLava extends Lava{
public function getName() : string{
return "Still Lava";
}
}

View File

@ -76,5 +76,4 @@ class Stone extends Solid{
return parent::getDropsForCompatibleTool($item);
}
}

View File

@ -48,5 +48,4 @@ class StoneBrickStairs extends Stair{
public function getName() : string{
return "Stone Brick Stairs";
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\item\FlintSteel;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\Player;
@ -46,8 +47,8 @@ class TNT extends Solid{
}
public function onActivate(Item $item, Player $player = null) : bool{
if($item->getId() === Item::FLINT_STEEL){
$item->useOn($this);
if($item instanceof FlintSteel){
$item->applyDamage(1);
$this->ignite();
return true;
}

View File

@ -127,7 +127,7 @@ abstract class Command{
if($this->permissionMessage === null){
$target->sendMessage($target->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
}elseif($this->permissionMessage !== ""){
$target->sendMessage(str_replace("<permission>", $this->getPermission(), $this->permissionMessage));
$target->sendMessage(str_replace("<permission>", $this->permission, $this->permissionMessage));
}
return false;
@ -139,11 +139,11 @@ abstract class Command{
* @return bool
*/
public function testPermissionSilent(CommandSender $target) : bool{
if(($perm = $this->getPermission()) === null or $perm === ""){
if($this->permission === null or $this->permission === ""){
return true;
}
foreach(explode(";", $perm) as $permission){
foreach(explode(";", $this->permission) as $permission){
if($target->hasPermission($permission)){
return true;
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\command;
use pocketmine\snooze\SleeperNotifier;
use pocketmine\Thread;
class CommandReader extends Thread{
@ -31,20 +32,26 @@ class CommandReader extends Thread{
public const TYPE_STREAM = 1;
public const TYPE_PIPED = 2;
/** @var resource */
private static $stdin;
/** @var \Threaded */
protected $buffer;
private $shutdown = false;
private $type = self::TYPE_STREAM;
public function __construct(){
/** @var SleeperNotifier|null */
private $notifier;
public function __construct(?SleeperNotifier $notifier = null){
$this->buffer = new \Threaded;
$this->notifier = $notifier;
$opts = getopt("", ["disable-readline"]);
if(extension_loaded("readline") and !isset($opts["disable-readline"]) and !$this->isPipe(STDIN)){
$this->type = self::TYPE_READLINE;
}
$this->start();
}
public function shutdown(){
@ -71,14 +78,12 @@ class CommandReader extends Thread{
}
private function initStdin(){
global $stdin;
if(is_resource($stdin)){
fclose($stdin);
if(is_resource(self::$stdin)){
fclose(self::$stdin);
}
$stdin = fopen("php://stdin", "r");
if($this->isPipe($stdin)){
self::$stdin = fopen("php://stdin", "r");
if($this->isPipe(self::$stdin)){
$this->type = self::TYPE_PIPED;
}else{
$this->type = self::TYPE_STREAM;
@ -92,7 +97,7 @@ class CommandReader extends Thread{
* @return bool
*/
private function isPipe($stream) : bool{
return is_resource($stream) and ((function_exists("posix_isatty") and !posix_isatty($stream)) or ((fstat($stream)["mode"] & 0170000) === 0010000));
return is_resource($stream) and (!stream_isatty($stream) or ((fstat($stream)["mode"] & 0170000) === 0010000));
}
/**
@ -109,9 +114,7 @@ class CommandReader extends Thread{
return true;
}
}else{
global $stdin;
if(!is_resource($stdin)){
if(!is_resource(self::$stdin)){
$this->initStdin();
}
@ -119,7 +122,7 @@ class CommandReader extends Thread{
/** @noinspection PhpMissingBreakStatementInspection */
case self::TYPE_STREAM:
//stream_select doesn't work on piped streams for some reason
$r = [$stdin];
$r = [self::$stdin];
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
return true;
}elseif($count === false){ //stream error
@ -127,7 +130,7 @@ class CommandReader extends Thread{
}
case self::TYPE_PIPED:
if(($raw = fgets($stdin)) === false){ //broken pipe or EOF
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
$this->initStdin();
$this->synchronized(function(){
$this->wait(200000);
@ -142,6 +145,9 @@ class CommandReader extends Thread{
if($line !== ""){
$this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
if($this->notifier !== null){
$this->notifier->wakeupSleeper();
}
}
return true;
@ -161,6 +167,8 @@ class CommandReader extends Thread{
}
public function run(){
$this->registerClassLoader();
if($this->type !== self::TYPE_READLINE){
$this->initStdin();
}
@ -168,8 +176,7 @@ class CommandReader extends Thread{
while(!$this->shutdown and $this->readLine());
if($this->type !== self::TYPE_READLINE){
global $stdin;
fclose($stdin);
fclose(self::$stdin);
}
}

View File

@ -145,5 +145,4 @@ class ConsoleCommandSender implements CommandSender{
}
$this->lineHeight = $height;
}
}

View File

@ -47,13 +47,12 @@ class FormattedCommandAlias extends Command{
foreach($this->formatStrings as $formatString){
try{
$commands[] = $this->buildCommand($formatString, $args);
}catch(\InvalidArgumentException $e){
$sender->sendMessage(TextFormat::RED . $e->getMessage());
return false;
}catch(\Throwable $e){
if($e instanceof \InvalidArgumentException){
$sender->sendMessage(TextFormat::RED . $e->getMessage());
}else{
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception"));
$sender->getServer()->getLogger()->logException($e);
}
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception"));
$sender->getServer()->getLogger()->logException($e);
return false;
}
@ -155,5 +154,4 @@ class FormattedCommandAlias extends Command{
private static function inRange(int $i, int $j, int $k) : bool{
return $i >= $j and $i <= $k;
}
}

View File

@ -38,7 +38,7 @@ class PluginCommand extends Command implements PluginIdentifiableCommand{
* @param string $name
* @param Plugin $owner
*/
public function __construct($name, Plugin $owner){
public function __construct(string $name, Plugin $owner){
parent::__construct($name);
$this->owningPlugin = $owner;
$this->executor = $owner;

View File

@ -47,6 +47,4 @@ class RemoteConsoleCommandSender extends ConsoleCommandSender{
public function getName() : string{
return "Rcon";
}
}

View File

@ -345,6 +345,4 @@ class SimpleCommandMap implements CommandMap{
}
}
}

View File

@ -59,7 +59,7 @@ class EnchantCommand extends VanillaCommand{
$item = $player->getInventory()->getItemInHand();
if($item->getId() <= 0){
if($item->isNull()){
$sender->sendMessage(new TranslationContainer("commands.enchant.noItem"));
return true;
}

View File

@ -99,5 +99,4 @@ class HelpCommand extends VanillaCommand{
return true;
}
}
}

View File

@ -62,15 +62,7 @@ class KillCommand extends VanillaCommand{
$player = $sender->getServer()->getPlayer($args[0]);
if($player instanceof Player){
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
if($ev->isCancelled()){
return true;
}
$player->setLastDamageCause($ev);
$player->setHealth(0);
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kill.successful", [$player->getName()]));
}else{
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound"));
@ -86,14 +78,7 @@ class KillCommand extends VanillaCommand{
return true;
}
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
if($ev->isCancelled()){
return true;
}
$sender->setLastDamageCause($ev);
$sender->setHealth(0);
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
$sender->sendMessage(new TranslationContainer("commands.kill.successful", [$sender->getName()]));
}else{
throw new InvalidCommandSyntaxException();

View File

@ -50,9 +50,7 @@ class TellCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$name = strtolower(array_shift($args));
$player = $sender->getServer()->getPlayer($name);
$player = $sender->getServer()->getPlayer(array_shift($args));
if($player === $sender){
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.message.sameTarget"));

View File

@ -98,23 +98,35 @@ class TimingsCommand extends VanillaCommand{
if($paste){
fseek($fileTimings, 0);
$data = [
"syntax" => "text",
"poster" => $sender->getServer()->getName(),
"content" => stream_get_contents($fileTimings)
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
"data" => $content = stream_get_contents($fileTimings)
];
fclose($fileTimings);
$sender->getServer()->getScheduler()->scheduleAsyncTask(new class([
["page" => "http://paste.ubuntu.com", "extraOpts" => [
CURLOPT_HTTPHEADER => ["User-Agent: " . $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion()],
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $data,
CURLOPT_AUTOREFERER => false,
CURLOPT_FOLLOWLOCATION => false
]]
], $sender) extends BulkCurlTask{
$host = $sender->getServer()->getProperty("timings.host", "timings.pmmp.io");
$sender->getServer()->getAsyncPool()->submitTask(new class($sender, $host, $agent, $data) extends BulkCurlTask{
/** @var string */
private $host;
public function __construct(CommandSender $sender, string $host, string $agent, array $data){
parent::__construct([
["page" => "https://$host?upload=true", "extraOpts" => [
CURLOPT_HTTPHEADER => [
"User-Agent: $agent",
"Content-Type: application/x-www-form-urlencoded"
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_AUTOREFERER => false,
CURLOPT_FOLLOWLOCATION => false
]]
], $sender);
$this->host = $host;
}
public function onCompletion(Server $server){
$sender = $this->fetchLocal($server);
$sender = $this->fetchLocal();
if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
return;
}
@ -123,23 +135,14 @@ class TimingsCommand extends VanillaCommand{
$server->getLogger()->logException($result);
return;
}
list(, $headers) = $result;
foreach($headers as $headerGroup){
if(isset($headerGroup["location"]) and preg_match('#^http://paste\\.ubuntu\\.com/([A-Za-z0-9+\/=]+)/#', trim($headerGroup["location"]), $match)){
$pasteId = $match[1];
break;
}
}
if(isset($pasteId)){
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsUpload", ["http://paste.ubuntu.com/" . $pasteId . "/"]));
if(isset($result[0]) && is_array($response = json_decode($result[0], true)) && isset($response["id"])){
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsRead",
["http://" . $sender->getServer()->getProperty("timings.host", "timings.pmmp.io") . "/?url=" . urlencode($pasteId)]));
["https://" . $this->host . "/?id=" . $response["id"]]));
}else{
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.pasteError"));
}
}
});
}else{
fclose($fileTimings);
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings]));

View File

@ -50,8 +50,6 @@ class VersionCommand extends VanillaCommand{
$sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended", [
$sender->getServer()->getName(),
$sender->getServer()->getPocketMineVersion(),
$sender->getServer()->getCodename(),
$sender->getServer()->getApiVersion(),
$sender->getServer()->getVersion(),
ProtocolInfo::CURRENT_PROTOCOL
]));

View File

@ -51,7 +51,7 @@ class Attribute{
/** @var Attribute[] */
protected static $attributes = [];
public static function init(){
public static function init() : void{
self::addAttribute(self::ABSORPTION, "minecraft:absorption", 0.00, 340282346638528859811704183484516925440.00, 0.00);
self::addAttribute(self::SATURATION, "minecraft:player.saturation", 0.00, 20.00, 20.00);
self::addAttribute(self::EXHAUSTION, "minecraft:player.exhaustion", 0.00, 5.00, 0.0);
@ -92,7 +92,7 @@ class Attribute{
*
* @return Attribute|null
*/
public static function getAttribute(int $id){
public static function getAttribute(int $id) : ?Attribute{
return isset(self::$attributes[$id]) ? clone self::$attributes[$id] : null;
}
@ -101,7 +101,7 @@ class Attribute{
*
* @return Attribute|null
*/
public static function getAttributeByName(string $name){
public static function getAttributeByName(string $name) : ?Attribute{
foreach(self::$attributes as $a){
if($a->getName() === $name){
return clone $a;
@ -170,7 +170,7 @@ class Attribute{
return $this;
}
public function resetToDefault(){
public function resetToDefault() : void{
$this->setValue($this->getDefaultValue());
}
@ -219,7 +219,7 @@ class Attribute{
return $this->shouldSend and $this->desynchronized;
}
public function markSynchronized(bool $synced = true){
public function markSynchronized(bool $synced = true) : void{
$this->desynchronized = !$synced;
}
}

View File

@ -27,7 +27,7 @@ class AttributeMap implements \ArrayAccess{
/** @var Attribute[] */
private $attributes = [];
public function addAttribute(Attribute $attribute){
public function addAttribute(Attribute $attribute) : void{
$this->attributes[$attribute->getId()] = $attribute;
}
@ -36,7 +36,7 @@ class AttributeMap implements \ArrayAccess{
*
* @return Attribute|null
*/
public function getAttribute(int $id){
public function getAttribute(int $id) : ?Attribute{
return $this->attributes[$id] ?? null;
}
@ -56,19 +56,28 @@ class AttributeMap implements \ArrayAccess{
});
}
public function offsetExists($offset){
public function offsetExists($offset) : bool{
return isset($this->attributes[$offset]);
}
public function offsetGet($offset){
/**
* @param int $offset
*
* @return float
*/
public function offsetGet($offset) : float{
return $this->attributes[$offset]->getValue();
}
public function offsetSet($offset, $value){
/**
* @param int $offset
* @param float $value
*/
public function offsetSet($offset, $value) : void{
$this->attributes[$offset]->setValue($value);
}
public function offsetUnset($offset){
public function offsetUnset($offset) : void{
throw new \RuntimeException("Could not unset an attribute from an attribute map");
}
}

View File

@ -111,8 +111,8 @@ class Effect{
*/
public static function getEffectByName(string $name) : ?Effect{
$const = self::class . "::" . strtoupper($name);
if(\defined($const)){
return self::getEffect(\constant($const));
if(defined($const)){
return self::getEffect(constant($const));
}
return null;
}

View File

@ -53,7 +53,6 @@ use pocketmine\level\Level;
use pocketmine\level\Location;
use pocketmine\level\Position;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Math;
use pocketmine\math\Vector2;
use pocketmine\math\Vector3;
use pocketmine\metadata\Metadatable;
@ -170,7 +169,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_LIMITED_LIFE = 77;
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
/* 80 (byte) something to do with nametag visibility? */
public const DATA_ALWAYS_SHOW_NAMETAG = 80; //byte: -1 = default, 0 = only when looked at, 1 = always
public const DATA_COLOR_2 = 81; //byte
/* 82 (unknown) */
public const DATA_SCORE_TAG = 83; //string
@ -388,23 +387,17 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/** @var float|null */
public $lastZ = null;
/** @var float */
public $motionX = 0.0;
/** @var float */
public $motionY = 0.0;
/** @var float */
public $motionZ = 0.0;
/** @var Vector3 */
public $temporalVector;
/** @var float */
public $lastMotionX;
/** @var float */
public $lastMotionY;
/** @var float */
public $lastMotionZ;
protected $motion;
/** @var Vector3 */
protected $lastMotion;
/** @var bool */
protected $forceMovementUpdate = false;
/** @var Vector3 */
public $temporalVector;
/** @var float */
public $lastYaw;
/** @var float */
@ -560,9 +553,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->setGenericFlag(self::DATA_FLAG_AFFECTED_BY_GRAVITY, true);
$this->setGenericFlag(self::DATA_FLAG_HAS_COLLISION, true);
$this->initEntity();
$this->propertyManager->clearDirtyProperties(); //Prevents resending properties that were set during construction
$this->chunk->addEntity($this);
$this->level->addEntity($this);
$this->initEntity();
$this->lastUpdate = $this->server->getTick();
$this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
@ -573,7 +569,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @return string
*/
public function getNameTag(){
public function getNameTag() : string{
return $this->propertyManager->getString(self::DATA_NAMETAG);
}
@ -595,22 +591,22 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @param string $name
*/
public function setNameTag($name){
public function setNameTag(string $name) : void{
$this->propertyManager->setString(self::DATA_NAMETAG, $name);
}
/**
* @param bool $value
*/
public function setNameTagVisible(bool $value = true){
public function setNameTagVisible(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_CAN_SHOW_NAMETAG, $value);
}
/**
* @param bool $value
*/
public function setNameTagAlwaysVisible(bool $value = true){
$this->setGenericFlag(self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value);
public function setNameTagAlwaysVisible(bool $value = true) : void{
$this->propertyManager->setByte(self::DATA_ALWAYS_SHOW_NAMETAG, $value ? 1 : 0);
}
/**
@ -623,7 +619,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @param float $value
*/
public function setScale(float $value){
public function setScale(float $value) : void{
$multiplier = $value / $this->getScale();
$this->width *= $multiplier;
@ -635,7 +631,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->propertyManager->setFloat(self::DATA_SCALE, $value);
}
public function getBoundingBox(){
public function getBoundingBox() : AxisAlignedBB{
return $this->boundingBox;
}
@ -656,7 +652,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return $this->getGenericFlag(self::DATA_FLAG_SNEAKING);
}
public function setSneaking(bool $value = true){
public function setSneaking(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_SNEAKING, $value);
}
@ -664,7 +660,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return $this->getGenericFlag(self::DATA_FLAG_SPRINTING);
}
public function setSprinting(bool $value = true){
public function setSprinting(bool $value = true) : void{
if($value !== $this->isSprinting()){
$this->setGenericFlag(self::DATA_FLAG_SPRINTING, $value);
$attr = $this->attributeMap->getAttribute(Attribute::MOVEMENT_SPEED);
@ -676,7 +672,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return $this->getGenericFlag(self::DATA_FLAG_IMMOBILE);
}
public function setImmobile(bool $value = true){
public function setImmobile(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_IMMOBILE, $value);
}
@ -698,9 +694,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* Sets whether the entity is able to climb climbable blocks.
*
* @param bool $value
*/
public function setCanClimb(bool $value = true){
public function setCanClimb(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_CAN_CLIMB, $value);
}
@ -718,7 +715,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @param bool $value
*/
public function setCanClimbWalls(bool $value = true){
public function setCanClimbWalls(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_WALLCLIMBING, $value);
}
@ -726,7 +723,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* Returns the entity ID of the owning entity, or null if the entity doesn't have an owner.
* @return int|null
*/
public function getOwningEntityId(){
public function getOwningEntityId() : ?int{
return $this->propertyManager->getLong(self::DATA_OWNER_EID);
}
@ -734,7 +731,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* Returns the owning entity, or null if the entity was not found.
* @return Entity|null
*/
public function getOwningEntity(){
public function getOwningEntity() : ?Entity{
$eid = $this->getOwningEntityId();
if($eid !== null){
return $this->server->findEntity($eid, $this->level);
@ -750,7 +747,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @throws \InvalidArgumentException if the supplied entity is not valid
*/
public function setOwningEntity(Entity $owner = null){
public function setOwningEntity(?Entity $owner) : void{
if($owner === null){
$this->propertyManager->removeProperty(self::DATA_OWNER_EID);
}elseif($owner->closed){
@ -764,7 +761,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* Returns the entity ID of the entity's target, or null if it doesn't have a target.
* @return int|null
*/
public function getTargetEntityId(){
public function getTargetEntityId() : ?int{
return $this->propertyManager->getLong(self::DATA_TARGET_EID);
}
@ -774,7 +771,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @return Entity|null
*/
public function getTargetEntity(){
public function getTargetEntity() : ?Entity{
$eid = $this->getTargetEntityId();
if($eid !== null){
return $this->server->findEntity($eid, $this->level);
@ -790,7 +787,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @throws \InvalidArgumentException if the target entity is not valid
*/
public function setTargetEntity(Entity $target = null){
public function setTargetEntity(?Entity $target) : void{
if($target === null){
$this->propertyManager->removeProperty(self::DATA_TARGET_EID);
}elseif($target->closed){
@ -823,7 +820,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @return string
*/
public function getSaveId(){
public function getSaveId() : string{
if(!isset(self::$saveNames[static::class])){
throw new \InvalidStateException("Entity is not registered");
}
@ -831,7 +828,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return current(self::$saveNames[static::class]);
}
public function saveNBT(){
public function saveNBT() : void{
if(!($this instanceof Player)){
$this->namedtag->setString("id", $this->getSaveId(), true);
@ -850,9 +847,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
]));
$this->namedtag->setTag(new ListTag("Motion", [
new DoubleTag("", $this->motionX),
new DoubleTag("", $this->motionY),
new DoubleTag("", $this->motionZ)
new DoubleTag("", $this->motion->x),
new DoubleTag("", $this->motion->y),
new DoubleTag("", $this->motion->z)
]));
$this->namedtag->setTag(new ListTag("Rotation", [
@ -867,7 +864,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->namedtag->setByte("Invulnerable", $this->invulnerable ? 1 : 0);
}
protected function initEntity(){
protected function initEntity() : void{
assert($this->namedtag instanceof CompoundTag);
if($this->namedtag->hasTag("CustomName", StringTag::class)){
@ -881,18 +878,16 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->setNameTagVisible($this->namedtag->getByte("CustomNameVisible", 1) !== 0);
}
}
$this->scheduleUpdate();
}
protected function addAttributes(){
protected function addAttributes() : void{
}
/**
* @param EntityDamageEvent $source
*/
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
$this->server->getPluginManager()->callEvent($source);
if($source->isCancelled()){
return;
@ -906,7 +901,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @param EntityRegainHealthEvent $source
*/
public function heal(EntityRegainHealthEvent $source){
public function heal(EntityRegainHealthEvent $source) : void{
$this->server->getPluginManager()->callEvent($source);
if($source->isCancelled()){
return;
@ -915,7 +910,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->setHealth($this->getHealth() + $source->getAmount());
}
public function kill(){
public function kill() : void{
$this->health = 0;
$this->scheduleUpdate();
}
@ -946,7 +941,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @param float $amount
*/
public function setHealth(float $amount){
public function setHealth(float $amount) : void{
if($amount == $this->health){
return;
}
@ -973,25 +968,25 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @param int $amount
*/
public function setMaxHealth(int $amount){
public function setMaxHealth(int $amount) : void{
$this->maxHealth = $amount;
}
/**
* @param EntityDamageEvent $type
*/
public function setLastDamageCause(EntityDamageEvent $type){
public function setLastDamageCause(EntityDamageEvent $type) : void{
$this->lastDamageCause = $type;
}
/**
* @return EntityDamageEvent|null
*/
public function getLastDamageCause(){
public function getLastDamageCause() : ?EntityDamageEvent{
return $this->lastDamageCause;
}
public function getAttributeMap(){
public function getAttributeMap() : AttributeMap{
return $this->attributeMap;
}
@ -1041,7 +1036,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return $this->fireTicks > 0;
}
public function setOnFire(int $seconds){
public function setOnFire(int $seconds) : void{
$ticks = $seconds * 20;
if($ticks > $this->fireTicks){
$this->fireTicks = $ticks;
@ -1064,7 +1059,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->fireTicks = $fireTicks;
}
public function extinguish(){
public function extinguish() : void{
$this->fireTicks = 0;
$this->setGenericFlag(self::DATA_FLAG_ONFIRE, false);
}
@ -1096,7 +1091,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* Called to deal damage to entities when they are on fire.
*/
protected function dealFireDamage(){
protected function dealFireDamage() : void{
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1);
$this->attack($ev);
}
@ -1106,14 +1101,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
public function canBeCollidedWith() : bool{
return true;
return $this->isAlive();
}
protected function updateMovement(bool $teleport = false){
protected function updateMovement(bool $teleport = false) : void{
$diffPosition = ($this->x - $this->lastX) ** 2 + ($this->y - $this->lastY) ** 2 + ($this->z - $this->lastZ) ** 2;
$diffRotation = ($this->yaw - $this->lastYaw) ** 2 + ($this->pitch - $this->lastPitch) ** 2;
$diffMotion = ($this->motionX - $this->lastMotionX) ** 2 + ($this->motionY - $this->lastMotionY) ** 2 + ($this->motionZ - $this->lastMotionZ) ** 2;
$diffMotion = $this->motion->subtract($this->lastMotion)->lengthSquared();
if($teleport or $diffPosition > 0.0001 or $diffRotation > 1.0){
$this->lastX = $this->x;
@ -1126,10 +1121,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->broadcastMovement($teleport);
}
if($diffMotion > 0.0025 or ($diffMotion > 0.0001 and $this->getMotion()->lengthSquared() <= 0.0001)){ //0.05 ** 2
$this->lastMotionX = $this->motionX;
$this->lastMotionY = $this->motionY;
$this->lastMotionZ = $this->motionZ;
if($diffMotion > 0.0025 or ($diffMotion > 0.0001 and $this->motion->lengthSquared() <= 0.0001)){ //0.05 ** 2
$this->lastMotion = clone $this->motion;
$this->broadcastMotion();
}
@ -1139,7 +1132,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return new Vector3($vector3->x, $vector3->y + $this->baseOffset, $vector3->z);
}
protected function broadcastMovement(bool $teleport = false){
protected function broadcastMovement(bool $teleport = false) : void{
if($this->chunk !== null){
$pk = new MoveEntityPacket();
$pk->entityRuntimeId = $this->id;
@ -1153,7 +1146,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
protected function broadcastMotion(){
protected function broadcastMotion() : void{
if($this->chunk !== null){
$pk = new SetEntityMotionPacket();
$pk->entityRuntimeId = $this->id;
@ -1167,29 +1160,29 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return false;
}
protected function applyGravity(){
$this->motionY -= $this->gravity;
protected function applyGravity() : void{
$this->motion->y -= $this->gravity;
}
protected function tryChangeMovement(){
protected function tryChangeMovement() : void{
$friction = 1 - $this->drag;
if($this->applyDragBeforeGravity()){
$this->motionY *= $friction;
$this->motion->y *= $friction;
}
$this->applyGravity();
if(!$this->applyDragBeforeGravity()){
$this->motionY *= $friction;
$this->motion->y *= $friction;
}
if($this->onGround){
$friction *= $this->level->getBlockAt(Math::floorFloat($this->x), Math::floorFloat($this->y) - 1, Math::floorFloat($this->z))->getFrictionFactor();
$friction *= $this->level->getBlockAt((int) floor($this->x), (int) floor($this->y - 1), (int) floor($this->z))->getFrictionFactor();
}
$this->motionX *= $friction;
$this->motionZ *= $friction;
$this->motion->x *= $friction;
$this->motion->z *= $friction;
}
protected function checkObstruction(float $x, float $y, float $z) : bool{
@ -1197,9 +1190,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return false;
}
$floorX = Math::floorFloat($x);
$floorY = Math::floorFloat($y);
$floorZ = Math::floorFloat($z);
$floorX = (int) floor($x);
$floorY = (int) floor($y);
$floorZ = (int) floor($z);
$diffX = $x - $floorX;
$diffY = $y - $floorY;
@ -1248,37 +1241,37 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$force = lcg_value() * 0.2 + 0.1;
if($direction === Vector3::SIDE_WEST){
$this->motionX = -$force;
$this->motion->x = -$force;
return true;
}
if($direction === Vector3::SIDE_EAST){
$this->motionX = $force;
$this->motion->x = $force;
return true;
}
if($direction === Vector3::SIDE_DOWN){
$this->motionY = -$force;
$this->motion->y = -$force;
return true;
}
if($direction === Vector3::SIDE_UP){
$this->motionY = $force;
$this->motion->y = $force;
return true;
}
if($direction === Vector3::SIDE_NORTH){
$this->motionZ = -$force;
$this->motion->z = -$force;
return true;
}
if($direction === Vector3::SIDE_SOUTH){
$this->motionZ = $force;
$this->motion->z = $force;
return true;
}
@ -1290,7 +1283,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @return int|null
*/
public function getDirection(){
public function getDirection() : ?int{
$rotation = ($this->yaw - 90) % 360;
if($rotation < 0){
$rotation += 360.0;
@ -1340,11 +1333,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->lastUpdate = $currentTick;
if($this->needsDespawn){
$this->close();
return false;
}
if(!$this->isAlive()){
if($this->onDeathUpdate($tickDiff)){
$this->flagForDespawn();
@ -1358,16 +1346,19 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($this->hasMovementUpdate()){
$this->tryChangeMovement();
$this->move($this->motionX, $this->motionY, $this->motionZ);
if(abs($this->motionX) <= self::MOTION_THRESHOLD){
$this->motionX = 0;
if(abs($this->motion->x) <= self::MOTION_THRESHOLD){
$this->motion->x = 0;
}
if(abs($this->motionY) <= self::MOTION_THRESHOLD){
$this->motionY = 0;
if(abs($this->motion->y) <= self::MOTION_THRESHOLD){
$this->motion->y = 0;
}
if(abs($this->motionZ) <= self::MOTION_THRESHOLD){
$this->motionZ = 0;
if(abs($this->motion->z) <= self::MOTION_THRESHOLD){
$this->motion->z = 0;
}
if($this->motion->x != 0 or $this->motion->y != 0 or $this->motion->z != 0){
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
}
$this->forceMovementUpdate = false;
@ -1388,7 +1379,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
//return !($this instanceof Player);
}
final public function scheduleUpdate(){
final public function scheduleUpdate() : void{
if($this->closed){
throw new \InvalidStateException("Cannot schedule update on garbage entity " . get_class($this));
}
$this->level->updateEntities[$this->id] = $this;
}
@ -1403,7 +1397,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @param bool $value
*/
final public function setForceMovementUpdate(bool $value = true){
final public function setForceMovementUpdate(bool $value = true) : void{
$this->forceMovementUpdate = $value;
$this->blocksAround = null;
@ -1416,9 +1410,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public function hasMovementUpdate() : bool{
return (
$this->forceMovementUpdate or
$this->motionX != 0 or
$this->motionY != 0 or
$this->motionZ != 0 or
$this->motion->x != 0 or
$this->motion->y != 0 or
$this->motion->z != 0 or
!$this->onGround
);
}
@ -1427,7 +1421,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return true;
}
public function resetFallDistance(){
public function resetFallDistance() : void{
$this->fallDistance = 0.0;
}
@ -1435,7 +1429,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* @param float $distanceThisTick
* @param bool $onGround
*/
protected function updateFallState(float $distanceThisTick, bool $onGround){
protected function updateFallState(float $distanceThisTick, bool $onGround) : void{
if($onGround){
if($this->fallDistance > 0){
$this->fall($this->fallDistance);
@ -1451,11 +1445,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @param float $fallDistance
*/
public function fall(float $fallDistance){
}
public function handleLavaMovement(){ //TODO
public function fall(float $fallDistance) : void{
}
@ -1463,16 +1453,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return $this->eyeHeight;
}
public function moveFlying(){ //TODO
public function onCollideWithPlayer(Player $player) : void{
}
public function onCollideWithPlayer(Player $player){
}
public function isInsideOfWater() : bool{
$block = $this->level->getBlockAt(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z));
public function isUnderwater() : bool{
$block = $this->level->getBlockAt((int) floor($this->x), (int) floor($y = ($this->y + $this->getEyeHeight())), (int) floor($this->z));
if($block instanceof Water){
$f = ($block->y + 1) - ($block->getFluidHeightPercent() - 0.1111111);
@ -1483,7 +1469,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
public function isInsideOfSolid() : bool{
$block = $this->level->getBlockAt(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z));
$block = $this->level->getBlockAt((int) floor($this->x), (int) floor($y = ($this->y + $this->getEyeHeight())), (int) floor($this->z));
return $block->isSolid() and !$block->isTransparent() and $block->collidesWithBB($this->getBoundingBox());
}
@ -1497,7 +1483,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
Timings::$entityMoveTimer->startTiming();
$newBB = $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz);
$newBB = $this->boundingBox->offsetCopy($dx, $dy, $dz);
$list = $this->level->getCollisionCubes($this, $newBB, false);
@ -1585,7 +1571,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
assert(abs($dx) <= 20 and abs($dy) <= 20 and abs($dz) <= 20, "Movement distance is excessive: dx=$dx, dy=$dy, dz=$dz");
$list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false);
$list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->offsetCopy($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false);
foreach($list as $bb){
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
@ -1661,15 +1647,15 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->updateFallState($dy, $this->onGround);
if($movX != $dx){
$this->motionX = 0;
$this->motion->x = 0;
}
if($movY != $dy){
$this->motionY = 0;
$this->motion->y = 0;
}
if($movZ != $dz){
$this->motionZ = 0;
$this->motion->z = 0;
}
//TODO: vehicle collision events (first we need to spawn them!)
@ -1677,7 +1663,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
Timings::$entityMoveTimer->stopTiming();
}
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz){
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
$this->isCollidedVertically = $movY != $dy;
$this->isCollidedHorizontally = ($movX != $dx or $movZ != $dz);
$this->isCollided = ($this->isCollidedHorizontally or $this->isCollidedVertically);
@ -1691,12 +1677,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($this->blocksAround === null){
$inset = 0.001; //Offset against floating-point errors
$minX = Math::floorFloat($this->boundingBox->minX + $inset);
$minY = Math::floorFloat($this->boundingBox->minY + $inset);
$minZ = Math::floorFloat($this->boundingBox->minZ + $inset);
$maxX = Math::floorFloat($this->boundingBox->maxX - $inset);
$maxY = Math::floorFloat($this->boundingBox->maxY - $inset);
$maxZ = Math::floorFloat($this->boundingBox->maxZ - $inset);
$minX = (int) floor($this->boundingBox->minX + $inset);
$minY = (int) floor($this->boundingBox->minY + $inset);
$minZ = (int) floor($this->boundingBox->minZ + $inset);
$maxX = (int) floor($this->boundingBox->maxX - $inset);
$maxY = (int) floor($this->boundingBox->maxY - $inset);
$maxZ = (int) floor($this->boundingBox->maxZ - $inset);
$this->blocksAround = [];
@ -1724,7 +1710,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return true;
}
protected function checkBlockCollision(){
protected function checkBlockCollision() : void{
$vector = $this->temporalVector->setComponents(0, 0, 0);
foreach($this->getBlocksAround() as $block){
@ -1735,9 +1721,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($vector->lengthSquared() > 0){
$vector = $vector->normalize();
$d = 0.014;
$this->motionX += $vector->x * $d;
$this->motionY += $vector->y * $d;
$this->motionZ += $vector->z * $d;
$this->motion->x += $vector->x * $d;
$this->motion->y += $vector->y * $d;
$this->motion->z += $vector->z * $d;
}
}
@ -1773,7 +1759,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return true;
}
public function setRotation(float $yaw, float $pitch){
public function setRotation(float $yaw, float $pitch) : void{
$this->yaw = $yaw;
$this->pitch = $pitch;
$this->scheduleUpdate();
@ -1789,7 +1775,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return false;
}
protected function checkChunks(){
protected function checkChunks() : void{
$chunkX = $this->getFloorX() >> 4;
$chunkZ = $this->getFloorZ() >> 4;
if($this->chunk === null or ($this->chunk->getX() !== $chunkX or $this->chunk->getZ() !== $chunkZ)){
@ -1820,17 +1806,17 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
protected function resetLastMovements(){
protected function resetLastMovements() : void{
list($this->lastX, $this->lastY, $this->lastZ) = [$this->x, $this->y, $this->z];
list($this->lastYaw, $this->lastPitch) = [$this->yaw, $this->pitch];
list($this->lastMotionX, $this->lastMotionY, $this->lastMotionZ) = [$this->motionX, $this->motionY, $this->motionZ];
$this->lastMotion = clone $this->motion;
}
public function getMotion() : Vector3{
return new Vector3($this->motionX, $this->motionY, $this->motionZ);
return clone $this->motion;
}
public function setMotion(Vector3 $motion){
public function setMotion(Vector3 $motion) : bool{
if(!$this->justCreated){
$this->server->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion));
if($ev->isCancelled()){
@ -1838,9 +1824,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
$this->motionX = $motion->x;
$this->motionY = $motion->y;
$this->motionZ = $motion->z;
$this->motion = clone $motion;
if(!$this->justCreated){
$this->updateMovement();
@ -1860,7 +1844,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @return bool
*/
public function teleport(Vector3 $pos, float $yaw = null, float $pitch = null) : bool{
public function teleport(Vector3 $pos, ?float $yaw = null, ?float $pitch = null) : bool{
if($pos instanceof Location){
$yaw = $yaw ?? $pos->yaw;
$pitch = $pitch ?? $pos->pitch;
@ -1945,7 +1929,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* @param Player $player
*/
public function spawnTo(Player $player){
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())])){
$this->hasSpawned[$player->getLoaderId()] = $player;
@ -1953,7 +1937,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
public function spawnToAll(){
public function spawnToAll() : void{
if($this->chunk === null or $this->closed){
return;
}
@ -1964,7 +1948,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
public function respawnToAll(){
public function respawnToAll() : void{
foreach($this->hasSpawned as $key => $player){
unset($this->hasSpawned[$key]);
$this->spawnTo($player);
@ -1975,7 +1959,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* @param Player $player
* @param bool $send
*/
public function despawnFrom(Player $player, bool $send = true){
public function despawnFrom(Player $player, bool $send = true) : void{
if(isset($this->hasSpawned[$player->getLoaderId()])){
if($send){
$pk = new RemoveEntityPacket();
@ -1986,7 +1970,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
public function despawnFromAll(){
public function despawnFromAll() : void{
foreach($this->hasSpawned as $player){
$this->despawnFrom($player);
}
@ -1997,6 +1981,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*/
public function flagForDespawn() : void{
$this->needsDespawn = true;
$this->scheduleUpdate();
}
public function isFlaggedForDespawn() : bool{
@ -2016,7 +2001,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* WARNING: Entities are unusable after this has been executed!
*/
public function close(){
public function close() : void{
if(!$this->closed){
$this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this));
$this->closed = true;
@ -2045,7 +2030,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* @param bool $value
* @param int $propertyType
*/
public function setDataFlag(int $propertyId, int $flagId, bool $value = true, int $propertyType = self::DATA_TYPE_LONG){
public function setDataFlag(int $propertyId, int $flagId, bool $value = true, int $propertyType = self::DATA_TYPE_LONG) : void{
if($this->getDataFlag($propertyId, $flagId) !== $value){
$flags = (int) $this->propertyManager->getPropertyValue($propertyId, $propertyType);
$flags ^= 1 << $flagId;
@ -2079,7 +2064,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* @param int $flagId
* @param bool $value
*/
public function setGenericFlag(int $flagId, bool $value = true){
public function setGenericFlag(int $flagId, bool $value = true) : void{
$this->setDataFlag(self::DATA_FLAGS, $flagId, $value, self::DATA_TYPE_LONG);
}
@ -2087,7 +2072,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* @param Player[]|Player $player
* @param array $data Properly formatted entity data, defaults to everything
*/
public function sendData($player, array $data = null){
public function sendData($player, ?array $data = null) : void{
if(!is_array($player)){
$player = [$player];
}
@ -2140,5 +2125,4 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public function __toString(){
return (new \ReflectionClass($this))->getShortName() . "(" . $this->getId() . ")";
}
}

View File

@ -46,7 +46,7 @@ interface EntityIds{
public const POLAR_BEAR = 28;
public const LLAMA = 29;
public const PARROT = 30;
public const DOLPHIN = 31;
public const ZOMBIE = 32;
public const CREEPER = 33;
public const SKELETON = 34;
@ -86,6 +86,7 @@ interface EntityIds{
public const EYE_OF_ENDER_SIGNAL = 70;
public const ENDER_CRYSTAL = 71;
public const FIREWORKS_ROCKET = 72;
public const TRIDENT = 73;
public const SHULKER_BULLET = 76;
public const FISHING_HOOK = 77;
@ -116,4 +117,11 @@ interface EntityIds{
public const EVOCATION_FANG = 103;
public const EVOCATION_ILLAGER = 104;
public const VEX = 105;
public const ICE_BOMB = 106;
public const BALLOON = 107;
public const PUFFERFISH = 108;
public const SALMON = 109;
public const DROWNED = 110;
public const TROPICAL_FISH = 111;
public const FISH = 112;
}

View File

@ -30,11 +30,14 @@ use pocketmine\event\entity\EntityRegainHealthEvent;
use pocketmine\event\player\PlayerExhaustEvent;
use pocketmine\event\player\PlayerExperienceChangeEvent;
use pocketmine\inventory\EnderChestInventory;
use pocketmine\inventory\EntityInventoryEventProcessor;
use pocketmine\inventory\InventoryHolder;
use pocketmine\inventory\PlayerInventory;
use pocketmine\item\Consumable;
use pocketmine\item\enchantment\Enchantment;
use pocketmine\item\FoodSource;
use pocketmine\item\Item;
use pocketmine\item\Totem;
use pocketmine\level\Level;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\ByteArrayTag;
@ -43,6 +46,7 @@ use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\EntityEventPacket;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
@ -111,7 +115,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
/**
* @return UUID|null
*/
public function getUniqueId(){
public function getUniqueId() : ?UUID{
return $this->uuid;
}
@ -151,14 +155,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
*
* @param Player[]|null $targets
*/
public function sendSkin(array $targets = null) : void{
public function sendSkin(?array $targets = null) : void{
$pk = new PlayerSkinPacket();
$pk->uuid = $this->getUniqueId();
$pk->skin = $this->skin;
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
}
public function jump(){
public function jump() : void{
parent::jump();
if($this->isSprinting()){
$this->exhaust(0.8, PlayerExhaustEvent::CAUSE_SPRINT_JUMPING);
@ -179,7 +183,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
*
* @throws \InvalidArgumentException
*/
public function setFood(float $new){
public function setFood(float $new) : void{
$attr = $this->attributeMap->getAttribute(Attribute::HUNGER);
$old = $attr->getValue();
$attr->setValue($new);
@ -202,7 +206,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
return $this->attributeMap->getAttribute(Attribute::HUNGER)->getMaxValue();
}
public function addFood(float $amount){
public function addFood(float $amount) : void{
$attr = $this->attributeMap->getAttribute(Attribute::HUNGER);
$amount += $attr->getValue();
$amount = max(min($amount, $attr->getMaxValue()), $attr->getMinValue());
@ -230,11 +234,11 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
*
* @throws \InvalidArgumentException
*/
public function setSaturation(float $saturation){
public function setSaturation(float $saturation) : void{
$this->attributeMap->getAttribute(Attribute::SATURATION)->setValue($saturation);
}
public function addSaturation(float $amount){
public function addSaturation(float $amount) : void{
$attr = $this->attributeMap->getAttribute(Attribute::SATURATION);
$attr->setValue($attr->getValue() + $amount, true);
}
@ -249,7 +253,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
*
* @param float $exhaustion
*/
public function setExhaustion(float $exhaustion){
public function setExhaustion(float $exhaustion) : void{
$this->attributeMap->getAttribute(Attribute::EXHAUSTION)->setValue($exhaustion);
}
@ -529,14 +533,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
return $this->inventory;
}
public function getEnderChestInventory(){
public function getEnderChestInventory() : EnderChestInventory{
return $this->enderChestInventory;
}
/**
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
*/
protected function initHumanData(){
protected function initHumanData() : void{
if($this->namedtag->hasTag("NameTag", StringTag::class)){
$this->setNameTag($this->namedtag->getString("NameTag"));
}
@ -555,18 +559,21 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->uuid = UUID::fromData((string) $this->getId(), $this->skin->getSkinData(), $this->getNameTag());
}
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->setPlayerFlag(self::DATA_PLAYER_FLAG_SLEEP, false);
$this->propertyManager->setBlockPos(self::DATA_PLAYER_BED_POSITION, null);
$this->inventory = new PlayerInventory($this);
$this->enderChestInventory = new EnderChestInventory($this);
$this->enderChestInventory = new EnderChestInventory();
$this->initHumanData();
$inventoryTag = $this->namedtag->getListTag("Inventory");
if($inventoryTag !== null){
$armorListener = $this->armorInventory->getEventProcessor();
$this->armorInventory->setEventProcessor(null);
/** @var CompoundTag $item */
foreach($inventoryTag as $i => $item){
$slot = $item->getByte("Slot");
@ -578,6 +585,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->inventory->setItem($slot - 9, Item::nbtDeserialize($item));
}
}
$this->armorInventory->setEventProcessor($armorListener);
}
$enderChestInventoryTag = $this->namedtag->getListTag("EnderChestInventory");
@ -590,6 +599,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->inventory->setHeldItemIndex($this->namedtag->getInt("SelectedInventorySlot", 0), false);
$this->inventory->setEventProcessor(new EntityInventoryEventProcessor($this));
$this->setFood((float) $this->namedtag->getInt("foodLevel", (int) $this->getFood(), true));
$this->setExhaustion($this->namedtag->getFloat("foodExhaustionLevel", $this->getExhaustion(), true));
@ -607,7 +617,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
}
protected function addAttributes(){
protected function addAttributes() : void{
parent::addAttributes();
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::SATURATION));
@ -629,7 +639,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
return $hasUpdate;
}
public function doFoodTick(int $tickDiff = 1){
protected function doFoodTick(int $tickDiff = 1) : void{
if($this->isAlive()){
$food = $this->getFood();
$health = $this->getHealth();
@ -641,8 +651,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
if($difficulty === Level::DIFFICULTY_PEACEFUL and $this->foodTickTimer % 10 === 0){
if($food < 20){
if($food < $this->getMaxFood()){
$this->addFood(1.0);
$food = $this->getFood();
}
if($this->foodTickTimer % 20 === 0 and $health < $this->getMaxHealth()){
$this->heal(new EntityRegainHealthEvent($this, 1, EntityRegainHealthEvent::CAUSE_SATURATION));
@ -656,16 +667,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->exhaust(3.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN);
}
}elseif($food <= 0){
if(($difficulty === 1 and $health > 10) or ($difficulty === 2 and $health > 1) or $difficulty === 3){
if(($difficulty === Level::DIFFICULTY_EASY and $health > 10) or ($difficulty === Level::DIFFICULTY_NORMAL and $health > 1) or $difficulty === Level::DIFFICULTY_HARD){
$this->attack(new EntityDamageEvent($this, EntityDamageEvent::CAUSE_STARVATION, 1));
}
}
}
if($food <= 6){
if($this->isSprinting()){
$this->setSprinting(false);
}
$this->setSprinting(false);
}
}
}
@ -674,14 +683,49 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
return $this->getNameTag();
}
public function getDrops() : array{
return array_merge(
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : []
);
public function applyDamageModifiers(EntityDamageEvent $source) : void{
parent::applyDamageModifiers($source);
$type = $source->getCause();
if($type !== EntityDamageEvent::CAUSE_SUICIDE and $type !== EntityDamageEvent::CAUSE_VOID
and $this->inventory->getItemInHand() instanceof Totem){ //TODO: check offhand as well (when it's implemented)
$compensation = $this->getHealth() - $source->getFinalDamage() - 1;
if($compensation < 0){
$source->setModifier($compensation, EntityDamageEvent::MODIFIER_TOTEM);
}
}
}
public function saveNBT(){
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
parent::applyPostDamageEffects($source);
$totemModifier = $source->getModifier(EntityDamageEvent::MODIFIER_TOTEM);
if($totemModifier < 0){ //Totem prevented death
$this->removeAllEffects();
$this->addEffect(new EffectInstance(Effect::getEffect(Effect::REGENERATION), 40 * 20, 1));
$this->addEffect(new EffectInstance(Effect::getEffect(Effect::FIRE_RESISTANCE), 40 * 20, 1));
$this->addEffect(new EffectInstance(Effect::getEffect(Effect::ABSORPTION), 5 * 20, 1));
$this->broadcastEntityEvent(EntityEventPacket::CONSUME_TOTEM);
$this->level->broadcastLevelEvent($this->add(0, $this->eyeHeight, 0), LevelEventPacket::EVENT_SOUND_TOTEM);
$hand = $this->inventory->getItemInHand();
if($hand instanceof Totem){
$hand->pop(); //Plugins could alter max stack size
$this->inventory->setItemInHand($hand);
}
}
}
public function getDrops() : array{
return array_filter(array_merge(
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : []
), function(Item $item) : bool{ return !$item->hasEnchantment(Enchantment::VANISHING); });
}
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setInt("foodLevel", (int) $this->getFood(), true);
@ -743,7 +787,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
}
public function spawnTo(Player $player){
public function spawnTo(Player $player) : void{
if($player !== $this){
parent::spawnTo($player);
}
@ -776,7 +820,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
}
public function close(){
public function close() : void{
if(!$this->closed){
if($this->inventory !== null){
$this->inventory->removeAllViewers(true);
@ -806,7 +850,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* @param int $flagId
* @param bool $value
*/
public function setPlayerFlag(int $flagId, bool $value = true){
public function setPlayerFlag(int $flagId, bool $value = true) : void{
$this->setDataFlag(self::DATA_PLAYER_FLAGS, $flagId, $value, self::DATA_TYPE_BYTE);
}
}

View File

@ -31,6 +31,7 @@ use pocketmine\event\entity\EntityDeathEvent;
use pocketmine\event\entity\EntityEffectAddEvent;
use pocketmine\event\entity\EntityEffectRemoveEvent;
use pocketmine\inventory\ArmorInventory;
use pocketmine\inventory\ArmorInventoryEventProcessor;
use pocketmine\item\Armor;
use pocketmine\item\Consumable;
use pocketmine\item\enchantment\Enchantment;
@ -72,10 +73,12 @@ abstract class Living extends Entity implements Damageable{
abstract public function getName() : string;
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->armorInventory = new ArmorInventory($this);
//TODO: load/save armor inventory contents
$this->armorInventory->setEventProcessor(new ArmorInventoryEventProcessor($this));
$health = $this->getMaxHealth();
@ -112,7 +115,7 @@ abstract class Living extends Entity implements Damageable{
}
}
protected function addAttributes(){
protected function addAttributes() : void{
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::HEALTH));
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::FOLLOW_RANGE));
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::KNOCKBACK_RESISTANCE));
@ -121,7 +124,7 @@ abstract class Living extends Entity implements Damageable{
$this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::ABSORPTION));
}
public function setHealth(float $amount){
public function setHealth(float $amount) : void{
$wasAlive = $this->isAlive();
parent::setHealth($amount);
$this->attributeMap->getAttribute(Attribute::HEALTH)->setValue(ceil($this->getHealth()), true);
@ -134,7 +137,7 @@ abstract class Living extends Entity implements Damageable{
return (int) $this->attributeMap->getAttribute(Attribute::HEALTH)->getMaxValue();
}
public function setMaxHealth(int $amount){
public function setMaxHealth(int $amount) : void{
$this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount);
}
@ -142,11 +145,11 @@ abstract class Living extends Entity implements Damageable{
return $this->attributeMap->getAttribute(Attribute::ABSORPTION)->getValue();
}
public function setAbsorption(float $absorption){
public function setAbsorption(float $absorption) : void{
$this->attributeMap->getAttribute(Attribute::ABSORPTION)->setValue($absorption);
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setFloat("Health", $this->getHealth(), true);
@ -186,7 +189,7 @@ abstract class Living extends Entity implements Damageable{
/**
* Removes all effects from the mob.
*/
public function removeAllEffects(){
public function removeAllEffects() : void{
foreach($this->effects as $effect){
$this->removeEffect($effect->getId());
}
@ -197,11 +200,15 @@ abstract class Living extends Entity implements Damageable{
*
* @param int $effectId
*/
public function removeEffect(int $effectId){
public function removeEffect(int $effectId) : void{
if(isset($this->effects[$effectId])){
$effect = $this->effects[$effectId];
$hasExpired = $effect->hasExpired();
$this->server->getPluginManager()->callEvent($ev = new EntityEffectRemoveEvent($this, $effect));
if($ev->isCancelled()){
if($hasExpired and !$ev->getEffect()->hasExpired()){ //altered duration of an expired effect to make it not get removed
$this->sendEffectAdd($ev->getEffect(), true);
}
return;
}
@ -221,7 +228,7 @@ abstract class Living extends Entity implements Damageable{
*
* @return EffectInstance|null
*/
public function getEffect(int $effectId){
public function getEffect(int $effectId) : ?EffectInstance{
return $this->effects[$effectId] ?? null;
}
@ -292,7 +299,7 @@ abstract class Living extends Entity implements Damageable{
/**
* Recalculates the mob's potion bubbles colour based on the active effects.
*/
protected function recalculateEffectColor(){
protected function recalculateEffectColor() : void{
/** @var Color[] $colors */
$colors = [];
$ambient = true;
@ -323,7 +330,7 @@ abstract class Living extends Entity implements Damageable{
* Sends the mob's potion effects to the specified player.
* @param Player $player
*/
public function sendPotionEffects(Player $player){
public function sendPotionEffects(Player $player) : void{
foreach($this->effects as $effect){
$pk = new MobEffectPacket();
$pk->entityRuntimeId = $this->id;
@ -374,13 +381,13 @@ abstract class Living extends Entity implements Damageable{
/**
* Called when the entity jumps from the ground. This method adds upwards velocity to the entity.
*/
public function jump(){
public function jump() : void{
if($this->onGround){
$this->motionY = $this->getJumpVelocity(); //Y motion should already be 0 if we're jumping from the ground.
$this->motion->y = $this->getJumpVelocity(); //Y motion should already be 0 if we're jumping from the ground.
}
}
public function fall(float $fallDistance){
public function fall(float $fallDistance) : void{
$damage = ceil($fallDistance - 3 - ($this->hasEffect(Effect::JUMP) ? $this->getEffect(Effect::JUMP)->getEffectLevel() : 0));
if($damage > 0){
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
@ -427,7 +434,7 @@ abstract class Living extends Entity implements Damageable{
return $this->armorInventory;
}
public function setOnFire(int $seconds){
public function setOnFire(int $seconds) : void{
parent::setOnFire($seconds - (int) min($seconds, $seconds * $this->getHighestArmorEnchantmentLevel(Enchantment::FIRE_PROTECTION) * 0.15));
}
@ -440,12 +447,12 @@ abstract class Living extends Entity implements Damageable{
public function applyDamageModifiers(EntityDamageEvent $source) : void{
if($source->canBeReducedByArmor()){
//MCPE uses the same system as PC did pre-1.9
$source->setDamage(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
$source->setModifier(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
}
$cause = $source->getCause();
if($this->hasEffect(Effect::DAMAGE_RESISTANCE) and $cause !== EntityDamageEvent::CAUSE_VOID and $cause !== EntityDamageEvent::CAUSE_SUICIDE){
$source->setDamage(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
$source->setModifier(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
}
$totalEpf = 0;
@ -454,20 +461,21 @@ abstract class Living extends Entity implements Damageable{
$totalEpf += $item->getEnchantmentProtectionFactor($source);
}
}
$source->setDamage(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
$source->setModifier(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
$source->setDamage(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
$source->setModifier(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
}
/**
* Called after EntityDamageEvent execution to apply post-hurt effects, such as reducing absorption or modifying
* armour durability.
* This will not be called by damage sources causing death.
*
* @param EntityDamageEvent $source
*/
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getDamage(EntityDamageEvent::MODIFIER_ABSORPTION)));
$this->damageArmor($source->getDamage(EntityDamageEvent::MODIFIER_BASE));
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
$this->damageArmor($source->getBaseDamage());
}
/**
@ -492,10 +500,10 @@ abstract class Living extends Entity implements Damageable{
$this->armorInventory->setContents($armor);
}
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
if($this->attackTime > 0 or $this->noDamageTicks > 0){
$lastCause = $this->getLastDamageCause();
if($lastCause !== null and $lastCause->getDamage() >= $source->getDamage()){
if($lastCause !== null and $lastCause->getBaseDamage() >= $source->getBaseDamage()){
$source->setCancelled();
}
}
@ -527,6 +535,8 @@ abstract class Living extends Entity implements Damageable{
return;
}
$this->attackTime = 10; //0.5 seconds cooldown
if($source instanceof EntityDamageByEntityEvent){
$e = $source->getDamager();
if($source instanceof EntityDamageByChildEntityEvent){
@ -540,55 +550,52 @@ abstract class Living extends Entity implements Damageable{
$deltaX = $this->x - $e->x;
$deltaZ = $this->z - $e->z;
$this->knockBack($e, $source->getDamage(), $deltaX, $deltaZ, $source->getKnockBack());
$this->knockBack($e, $source->getBaseDamage(), $deltaX, $deltaZ, $source->getKnockBack());
}
}
$this->applyPostDamageEffects($source);
if($this->isAlive()){
$this->applyPostDamageEffects($source);
$this->doHitAnimation();
}else{
$this->startDeathAnimation();
}
$this->attackTime = 10; //0.5 seconds cooldown
}
protected function doHitAnimation() : void{
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION);
}
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4){
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4) : void{
$f = sqrt($x * $x + $z * $z);
if($f <= 0){
return;
}
if(mt_rand() / mt_getrandmax() > $this->getAttributeMap()->getAttribute(Attribute::KNOCKBACK_RESISTANCE)->getValue()){
$f = 1 / $f;
$f = 1 / $f;
$motion = clone $this->motion;
$motion = new Vector3($this->motionX, $this->motionY, $this->motionZ);
$motion->x /= 2;
$motion->y /= 2;
$motion->z /= 2;
$motion->x += $x * $f * $base;
$motion->y += $base;
$motion->z += $z * $f * $base;
$motion->x /= 2;
$motion->y /= 2;
$motion->z /= 2;
$motion->x += $x * $f * $base;
$motion->y += $base;
$motion->z += $z * $f * $base;
if($motion->y > $base){
$motion->y = $base;
}
if($motion->y > $base){
$motion->y = $base;
$this->setMotion($motion);
}
$this->setMotion($motion);
}
public function kill(){
public function kill() : void{
parent::kill();
$this->onDeath();
$this->startDeathAnimation();
}
protected function onDeath(){
protected function onDeath() : void{
$this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops()));
foreach($ev->getDrops() as $item){
$this->getLevel()->dropItem($this, $item);
@ -649,7 +656,7 @@ abstract class Living extends Entity implements Damageable{
return $hasUpdate;
}
protected function doEffectsTick(int $tickDiff = 1){
protected function doEffectsTick(int $tickDiff = 1) : void{
foreach($this->effects as $instance){
$type = $instance->getType();
if($type->canTick($instance)){
@ -666,7 +673,7 @@ abstract class Living extends Entity implements Damageable{
* Ticks the entity's air supply when it cannot breathe.
* @param int $tickDiff
*/
protected function doAirSupplyTick(int $tickDiff){
protected function doAirSupplyTick(int $tickDiff) : void{
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or
lcg_value() <= (1 / ($respirationLevel + 1))
){
@ -686,7 +693,7 @@ abstract class Living extends Entity implements Damageable{
* @return bool
*/
public function canBreathe() : bool{
return $this->hasEffect(Effect::WATER_BREATHING) or !$this->isInsideOfWater();
return $this->hasEffect(Effect::WATER_BREATHING) or !$this->isUnderwater();
}
/**
@ -703,7 +710,7 @@ abstract class Living extends Entity implements Damageable{
*
* @param bool $value
*/
public function setBreathing(bool $value = true){
public function setBreathing(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_BREATHING, $value);
}
@ -721,7 +728,7 @@ abstract class Living extends Entity implements Damageable{
* Sets the number of air ticks left in the entity's air supply.
* @param int $ticks
*/
public function setAirSupplyTicks(int $ticks){
public function setAirSupplyTicks(int $ticks) : void{
$this->propertyManager->setShort(self::DATA_AIR, $ticks);
}
@ -737,7 +744,7 @@ abstract class Living extends Entity implements Damageable{
* Sets the maximum amount of air ticks the air supply can hold.
* @param int $ticks
*/
public function setMaxAirSupplyTicks(int $ticks){
public function setMaxAirSupplyTicks(int $ticks) : void{
$this->propertyManager->setShort(self::DATA_MAX_AIR, $ticks);
}
@ -745,7 +752,7 @@ abstract class Living extends Entity implements Damageable{
* Called when the entity's air supply ticks reaches -20 or lower. The entity will usually take damage at this point
* and then the supply is reset to 0, so this method will be called roughly every second.
*/
public function onAirExpired(){
public function onAirExpired() : void{
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2);
$this->attack($ev);
}
@ -815,13 +822,10 @@ abstract class Living extends Entity implements Damageable{
*
* @return Block|null
*/
public function getTargetBlock(int $maxDistance, array $transparent = []){
try{
$block = $this->getLineOfSight($maxDistance, 1, $transparent)[0];
if($block instanceof Block){
return $block;
}
}catch(\ArrayOutOfBoundsException $e){
public function getTargetBlock(int $maxDistance, array $transparent = []) : ?Block{
$line = $this->getLineOfSight($maxDistance, 1, $transparent);
if(!empty($line)){
return array_shift($line);
}
return null;
@ -852,7 +856,7 @@ abstract class Living extends Entity implements Damageable{
$this->armorInventory->sendContents($player);
}
public function close(){
public function close() : void{
if(!$this->closed){
if($this->armorInventory !== null){
$this->armorInventory->removeAllViewers(true);

View File

@ -99,5 +99,4 @@ class Skin{
$this->geometryData = (string) json_encode(json_decode($this->geometryData));
}
}
}

View File

@ -42,7 +42,7 @@ class Squid extends WaterAnimal{
private $switchDirectionTicker = 0;
public function initEntity(){
public function initEntity() : void{
$this->setMaxHealth(10);
parent::initEntity();
}
@ -51,7 +51,7 @@ class Squid extends WaterAnimal{
return "Squid";
}
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
parent::attack($source);
if($source->isCancelled()){
return;
@ -93,30 +93,28 @@ class Squid extends WaterAnimal{
$this->swimDirection->y = -0.5;
}
$inWater = $this->isInsideOfWater();
$inWater = $this->isUnderwater();
if(!$inWater){
$this->swimDirection = null;
}elseif($this->swimDirection !== null){
if($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()){
$this->motionX = $this->swimDirection->x * $this->swimSpeed;
$this->motionY = $this->swimDirection->y * $this->swimSpeed;
$this->motionZ = $this->swimDirection->z * $this->swimSpeed;
if($this->motion->lengthSquared() <= $this->swimDirection->lengthSquared()){
$this->motion = $this->swimDirection->multiply($this->swimSpeed);
}
}else{
$this->swimDirection = $this->generateRandomDirection();
$this->swimSpeed = mt_rand(50, 100) / 2000;
}
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
$this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI);
$this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI);
$f = sqrt(($this->motion->x ** 2) + ($this->motion->z ** 2));
$this->yaw = (-atan2($this->motion->x, $this->motion->z) * 180 / M_PI);
$this->pitch = (-atan2($f, $this->motion->y) * 180 / M_PI);
}
return $hasUpdate;
}
protected function applyGravity(){
if(!$this->isInsideOfWater()){
protected function applyGravity() : void{
if(!$this->isUnderwater()){
parent::applyGravity();
}
}

View File

@ -39,7 +39,7 @@ class Villager extends Creature implements NPC, Ageable{
return "Villager";
}
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
/** @var int $profession */
@ -52,7 +52,7 @@ class Villager extends Creature implements NPC, Ageable{
$this->setProfession($profession);
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setInt("Profession", $this->getProfession());
}
@ -62,7 +62,7 @@ class Villager extends Creature implements NPC, Ageable{
*
* @param int $profession
*/
public function setProfession(int $profession){
public function setProfession(int $profession) : void{
$this->propertyManager->setInt(self::DATA_VARIANT, $profession);
}

View File

@ -32,10 +32,10 @@ abstract class WaterAnimal extends Creature implements Ageable{
}
public function canBreathe() : bool{
return $this->isInsideOfWater();
return $this->isUnderwater();
}
public function onAirExpired(){
public function onAirExpired() : void{
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2);
$this->attack($ev);
}

View File

@ -100,7 +100,7 @@ class ExperienceOrb extends Entity{
*/
protected $targetPlayerRuntimeId = null;
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->age = $this->namedtag->getShort("Age", 0);
@ -115,7 +115,7 @@ class ExperienceOrb extends Entity{
$this->setXpValue($value);
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setShort("Age", $this->age);
@ -165,7 +165,7 @@ class ExperienceOrb extends Entity{
}
$currentTarget = $this->getTargetPlayer();
if($currentTarget !== null and $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2){
if($currentTarget !== null and (!$currentTarget->isAlive() or $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2)){
$currentTarget = null;
}
@ -192,9 +192,9 @@ class ExperienceOrb extends Entity{
$oneMinusDistance = (1 - $distance) ** 2;
if($oneMinusDistance > 0){
$this->motionX += $vector->x / $distance * $oneMinusDistance * 0.2;
$this->motionY += $vector->y / $distance * $oneMinusDistance * 0.2;
$this->motionZ += $vector->z / $distance * $oneMinusDistance * 0.2;
$this->motion->x += $vector->x / $distance * $oneMinusDistance * 0.2;
$this->motion->y += $vector->y / $distance * $oneMinusDistance * 0.2;
$this->motion->z += $vector->z / $distance * $oneMinusDistance * 0.2;
}
if($currentTarget->canPickupXp() and $this->boundingBox->intersectsWith($currentTarget->getBoundingBox())){
@ -210,7 +210,7 @@ class ExperienceOrb extends Entity{
return $hasUpdate;
}
protected function tryChangeMovement(){
protected function tryChangeMovement() : void{
$this->checkObstruction($this->x, $this->y, $this->z);
parent::tryChangeMovement();
}

View File

@ -50,7 +50,7 @@ class FallingBlock extends Entity{
public $canCollide = false;
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$blockId = 0;
@ -64,8 +64,7 @@ class FallingBlock extends Entity{
}
if($blockId === 0){
$this->close();
return;
throw new \UnexpectedValueException("Invalid " . get_class($this) . " entity: block ID is 0 or missing");
}
$damage = $this->namedtag->getByte("Data", 0);
@ -83,7 +82,7 @@ class FallingBlock extends Entity{
return false;
}
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
parent::attack($source);
}
@ -126,15 +125,15 @@ class FallingBlock extends Entity{
return $hasUpdate;
}
public function getBlock(){
public function getBlock() : int{
return $this->block->getId();
}
public function getDamage(){
public function getDamage() : int{
return $this->block->getDamage();
}
public function saveNBT(){
public function saveNBT() : void{
$this->namedtag->setInt("TileID", $this->block->getId(), true);
$this->namedtag->setByte("Data", $this->block->getDamage());
}

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\entity\object;
use pocketmine\entity\Entity;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\ItemDespawnEvent;
use pocketmine\event\entity\ItemSpawnEvent;
use pocketmine\event\inventory\InventoryPickupItemEvent;
@ -54,7 +53,7 @@ class ItemEntity extends Entity{
public $canCollide = false;
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->setMaxHealth(5);
@ -67,8 +66,7 @@ class ItemEntity extends Entity{
$itemTag = $this->namedtag->getCompoundTag("Item");
if($itemTag === null){
$this->close();
return;
throw new \UnexpectedValueException("Invalid " . get_class($this) . " entity: expected \"Item\" NBT tag not found");
}
$this->item = Item::nbtDeserialize($itemTag);
@ -77,18 +75,6 @@ class ItemEntity extends Entity{
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
}
public function attack(EntityDamageEvent $source){
if(
$source->getCause() === EntityDamageEvent::CAUSE_VOID or
$source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK or
$source->getCause() === EntityDamageEvent::CAUSE_LAVA or
$source->getCause() === EntityDamageEvent::CAUSE_ENTITY_EXPLOSION or
$source->getCause() === EntityDamageEvent::CAUSE_BLOCK_EXPLOSION
){
parent::attack($source);
}
}
public function entityBaseTick(int $tickDiff = 1) : bool{
if($this->closed){
return false;
@ -119,7 +105,7 @@ class ItemEntity extends Entity{
return $hasUpdate;
}
protected function tryChangeMovement(){
protected function tryChangeMovement() : void{
$this->checkObstruction($this->x, $this->y, $this->z);
parent::tryChangeMovement();
}
@ -128,7 +114,7 @@ class ItemEntity extends Entity{
return true;
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setTag($this->item->nbtSerialize(-1, "Item"));
$this->namedtag->setShort("Health", (int) $this->getHealth());
@ -167,7 +153,7 @@ class ItemEntity extends Entity{
/**
* @param int $delay
*/
public function setPickupDelay(int $delay){
public function setPickupDelay(int $delay) : void{
$this->pickupDelay = $delay;
}
@ -181,7 +167,7 @@ class ItemEntity extends Entity{
/**
* @param string $owner
*/
public function setOwner(string $owner){
public function setOwner(string $owner) : void{
$this->owner = $owner;
}
@ -195,7 +181,7 @@ class ItemEntity extends Entity{
/**
* @param string $thrower
*/
public function setThrower(string $thrower){
public function setThrower(string $thrower) : void{
$this->thrower = $thrower;
}
@ -210,7 +196,7 @@ class ItemEntity extends Entity{
$player->dataPacket($pk);
}
public function onCollideWithPlayer(Player $player){
public function onCollideWithPlayer(Player $player) : void{
if($this->getPickupDelay() > 0){
return;
}

View File

@ -70,13 +70,13 @@ class Painting extends Entity{
parent::__construct($level, $nbt);
}
protected function initEntity(){
protected function initEntity() : void{
$this->setMaxHealth(1);
$this->setHealth(1);
parent::initEntity();
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setInt("TileX", (int) $this->blockIn->x);
$this->namedtag->setInt("TileY", (int) $this->blockIn->y);
@ -86,7 +86,7 @@ class Painting extends Entity{
$this->namedtag->setByte("Direction", (int) $this->direction); //Save both for full compatibility
}
public function kill(){
public function kill() : void{
parent::kill();
$drops = true;
@ -138,7 +138,7 @@ class Painting extends Entity{
return false;
}
protected function updateMovement(bool $teleport = false){
protected function updateMovement(bool $teleport = false) : void{
}
@ -166,7 +166,7 @@ class Painting extends Entity{
return PaintingMotive::getMotiveByName($this->motive);
}
public function getDirection() : int{
public function getDirection() : ?int{
return $this->direction;
}

View File

@ -47,13 +47,13 @@ class PrimedTNT extends Entity implements Explosive{
public $canCollide = false;
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
parent::attack($source);
}
}
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
if($this->namedtag->hasTag("Fuse", ShortTag::class)){
@ -73,7 +73,7 @@ class PrimedTNT extends Entity implements Explosive{
return false;
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setShort("Fuse", $this->fuse, true); //older versions incorrectly saved this as a byte
}
@ -101,7 +101,7 @@ class PrimedTNT extends Entity implements Explosive{
return $hasUpdate or $this->fuse >= 0;
}
public function explode(){
public function explode() : void{
$this->server->getPluginManager()->callEvent($ev = new ExplosionPrimeEvent($this, 4));
if(!$ev->isCancelled()){

View File

@ -48,7 +48,7 @@ class Arrow extends Projectile{
protected $damage = 2;
public function __construct(Level $level, CompoundTag $nbt, Entity $shootingEntity = null, bool $critical = false){
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){
parent::__construct($level, $nbt, $shootingEntity);
$this->setCritical($critical);
}
@ -57,7 +57,7 @@ class Arrow extends Projectile{
return $this->getGenericFlag(self::DATA_FLAG_CRITICAL);
}
public function setCritical(bool $value = true){
public function setCritical(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_CRITICAL, $value);
}
@ -95,7 +95,7 @@ class Arrow extends Projectile{
$this->broadcastEntityEvent(EntityEventPacket::ARROW_SHAKE, 7); //7 ticks
}
public function onCollideWithPlayer(Player $player){
public function onCollideWithPlayer(Player $player) : void{
if($this->blockHit === null){
return;
}

View File

@ -55,20 +55,20 @@ abstract class Projectile extends Entity{
/** @var int|null */
protected $blockHitData;
public function __construct(Level $level, CompoundTag $nbt, Entity $shootingEntity = null){
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null){
parent::__construct($level, $nbt);
if($shootingEntity !== null){
$this->setOwningEntity($shootingEntity);
}
}
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
parent::attack($source);
}
}
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->setMaxHealth(1);
@ -117,10 +117,10 @@ abstract class Projectile extends Entity{
* @return int
*/
public function getResultDamage() : int{
return (int) ceil(sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2) * $this->damage);
return (int) ceil($this->motion->length() * $this->damage);
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setShort("Age", $this->age);
@ -159,7 +159,7 @@ abstract class Projectile extends Entity{
Timings::$entityMoveTimer->startTiming();
$start = $this->asVector3();
$end = $start->add($this->motionX, $this->motionY, $this->motionZ);
$end = $start->add($this->motion);
$blockHit = null;
$entityHit = null;
@ -185,7 +185,7 @@ abstract class Projectile extends Entity{
continue;
}
$entityBB = $entity->boundingBox->grow(0.3, 0.3, 0.3);
$entityBB = $entity->boundingBox->expandedCopy(0.3, 0.3, 0.3);
$entityHitResult = $entityBB->calculateIntercept($start, $end);
if($entityHitResult === null){
@ -215,7 +215,7 @@ abstract class Projectile extends Entity{
}elseif($blockHit !== null){
$ev = new ProjectileHitBlockEvent($this, $hitResult, $blockHit);
}else{
\assert(false, "unknown hit type");
assert(false, "unknown hit type");
}
if($ev !== null){
@ -230,15 +230,15 @@ abstract class Projectile extends Entity{
}
$this->isCollided = $this->onGround = true;
$this->motionX = $this->motionY = $this->motionZ = 0;
$this->motion->x = $this->motion->y = $this->motion->z = 0;
}else{
$this->isCollided = $this->onGround = false;
$this->blockHit = $this->blockHitId = $this->blockHitData = null;
//recompute angles...
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
$this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI);
$this->pitch = (atan2($this->motionY, $f) * 180 / M_PI);
$f = sqrt(($this->motion->x ** 2) + ($this->motion->z ** 2));
$this->yaw = (atan2($this->motion->x, $this->motion->z) * 180 / M_PI);
$this->pitch = (atan2($this->motion->y, $f) * 180 / M_PI);
}
$this->checkChunks();

View File

@ -42,13 +42,13 @@ class SplashPotion extends Throwable{
protected $gravity = 0.05;
protected $drag = 0.01;
protected function initEntity(){
protected function initEntity() : void{
parent::initEntity();
$this->setPotionId($this->namedtag->getShort("PotionId", 0));
}
public function saveNBT(){
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setShort("PotionId", $this->getPotionId());
}
@ -81,7 +81,7 @@ class SplashPotion extends Throwable{
if($hasEffects){
if(!$this->willLinger()){
foreach($this->level->getNearbyEntities($this->boundingBox->grow(4.125, 2.125, 4.125), $this) as $entity){
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(4.125, 2.125, 4.125), $this) as $entity){
if($entity instanceof Living){
$distanceSquared = $entity->distanceSquared($this);
if($distanceSquared > 16){ //4 blocks

View File

@ -59,7 +59,7 @@ abstract class Event{
*
* @throws \BadMethodCallException
*/
public function setCancelled(bool $value = true){
public function setCancelled(bool $value = true) : void{
if(!($this instanceof Cancellable)){
throw new \BadMethodCallException("Event is not Cancellable");
}

View File

@ -82,8 +82,8 @@ abstract class EventPriority{
public static function fromString(string $name) : int{
$name = strtoupper($name);
$const = self::class . "::" . $name;
if($name !== "ALL" and \defined($const)){
return \constant($const);
if($name !== "ALL" and defined($const)){
return constant($const);
}
throw new \InvalidArgumentException("Unable to resolve priority \"$name\"");

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