Compare commits

...

338 Commits

Author SHA1 Message Date
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
8c1c8f34cc Player: confine command data resending on permission recalculate to post-spawn only
this fixes crashes when PurePerms causes this to be recalculated on player login - the client doesn't like receiving this before StartGame and crashes.

Confining this to post-spawn should not cause any issues since any permission recalculation in login events will be reflected immediately afterwards when the initial command data is sent anyway.

This same bug popped up at 1.1... I don't know why it wasn't fixed properly back then.
2018-05-17 19:26:42 -04:00
c285295037 Merge branch 'release/alpha12' 2018-05-17 18:40:24 +01:00
2034caf71c Merge branch 'mcpe-1.4.0-alpha12' into release/alpha12 2018-05-17 18:38:58 +01:00
89ccac7a8c updated blockIDs table 2018-05-17 16:17:26 +01:00
f6e71d8296 Protocol changes for 1.4.0 release
this is nearly the same as 1.5.0.0, except the skin packet premium boolean has been dropped.

This isn't production ready yet because the blockID table needs updating (waiting on MrARM to fix his script for that).
2018-05-17 10:36:16 +01:00
b333a0e24c PlayStatusPacket: add new constant 2018-05-17 10:33:18 +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
5b6762d0d5 Fixed lang submodule version 2018-05-09 14:53:22 +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
68638f9779 Fixed translations
Apparently the translation type doesn't translate unless this flag is set now...
2018-05-04 22:14:02 +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
8228774ad4 Remove extra data, this time without API breaks
this is necessary because the next MCPE release will probably be made before the next PM release.
2018-05-02 12:08:44 +01:00
2a54726905 Updated runtimeIDs table
from https://github.com/MCMrARM/minecraft-block-ids/blob/master/blocks_270.json
2018-05-02 12:03:29 +01:00
8b225fc4ef New entity metadata flags and properties 2018-05-02 12:03:29 +01:00
a014b44b69 New PlayerActionPacket constants
anyone know what these are for? something to do with riptide but I didn't manage to find out what...
2018-05-02 12:03:29 +01:00
c7544c1d25 AvailableCommandsPacket: update arg types 2018-05-02 12:03:29 +01:00
eb28622823 Protocol changes for 1.5.0.0
this feels so strange to type... can we ditch the versioning system already?
2018-05-02 12:03:29 +01:00
68494f1c0d CraftingDataPacket: decode chemistry recipes correctly 2018-05-02 12:03:29 +01:00
27ea0d360f updated block IDs table
minified, from https://github.com/MCMrARM/minecraft-block-ids/blob/master/blocks_260.json
2018-05-02 12:03:29 +01:00
d384df1f2e fixed some mistakes in the protocol 2018-05-02 12:03:29 +01:00
fe8102c062 Silence another stupid spam bug 2018-05-02 12:03:29 +01:00
8b15d85469 bump versions for 1.2.20.1 beta 2018-05-02 12:03:29 +01:00
a5ba716232 Updated block ID mappings for 1.2.14 (thanks again MrARM) 2018-05-02 12:03:29 +01:00
db432bb024 Found some things in new packets 2018-05-02 12:03:29 +01:00
91486a23a5 some changes for 1.2.14.2 beta 2018-05-02 12:03:29 +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
411 changed files with 4457 additions and 31862 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

@ -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);
@ -411,7 +411,7 @@ class MemoryManager{
}while($continue);
MainLogger::getLogger()->info("[Dump] Wrote " . count($objects) . " objects");
$logger->info("[Dump] Wrote " . count($objects) . " objects");
fclose($obData);
@ -421,7 +421,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);
}
@ -538,7 +538,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return !$this->isSpectator();
}
public function resetFallDistance(){
public function resetFallDistance() : void{
parent::resetFallDistance();
if($this->inAirTicks !== 0){
$this->startAirTicks = 5;
@ -653,7 +653,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
}
$this->sendCommandData();
if($this->spawned){
$this->sendCommandData();
}
}
/**
@ -664,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"){
@ -686,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;
}
@ -810,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());
}
@ -921,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);
@ -1059,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();
@ -1146,8 +1149,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->loadQueue = $newOrder;
Timings::$playerChunkOrderTimer->stopTiming();
return true;
}
/**
@ -1167,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();
}
/**
@ -1182,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();
@ -1485,7 +1486,7 @@ 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){
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
if(!$this->onGround or $movY != 0){
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
@ -1500,8 +1501,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
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()){
@ -1635,17 +1636,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;
@ -1653,11 +1654,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{
}
@ -1700,14 +1701,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){
@ -1742,7 +1745,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);
}
@ -1825,11 +1828,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();
}
@ -1912,7 +1915,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);
}
@ -1944,7 +1947,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");
}
@ -1953,7 +1956,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;
@ -1992,7 +1995,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),
@ -2082,9 +2085,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();
}
}
@ -2143,8 +2146,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());
@ -2283,7 +2285,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;
@ -2504,20 +2506,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();
@ -2527,14 +2529,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
if($this->isSurvival()){
if($heldItem->useOn($target)){
$this->inventory->setItemInHand($heldItem);
}
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
if($heldItem->onAttackEntity($target) and $this->isSurvival()){ //always fire the hook, even if we are survival
$this->inventory->setItemInHand($heldItem);
}
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
return true;
default:
break; //unknown
@ -2745,40 +2745,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:
@ -2788,6 +2764,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, BlockFactory::toStaticRuntimeId($block->getId(), $block->getDamage()) | ($packet->face << 24));
//TODO: destroy-progress level event
break;
case PlayerActionPacket::ACTION_START_SWIMMING:
break; //TODO
case PlayerActionPacket::ACTION_STOP_SWIMMING:
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
break;
default:
$this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName());
return false;
@ -2798,6 +2779,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;
@ -3281,6 +3282,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk = new TextPacket();
if(!$this->server->isLanguageForced()){
$pk->type = TextPacket::TYPE_TRANSLATION;
$pk->needsTranslation = true;
$pk->message = $this->server->getLanguage()->translateString($message, $parameters, "pocketmine.");
foreach($parameters as $i => $p){
$parameters[$i] = $this->server->getLanguage()->translateString($p, $parameters, "pocketmine.");
@ -3335,7 +3337,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{
@ -3486,7 +3488,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
public function kill(){
public function kill() : void{
if(!$this->spawned){
return;
}
@ -3496,7 +3498,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 = [
@ -3672,8 +3674,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();
@ -3685,7 +3686,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;
}
@ -3702,11 +3703,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{

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.1";
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,21 @@ 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");
}
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 +1406,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 +1437,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 +1449,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 +1500,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 +1528,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 +1550,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 +1595,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 +1608,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 +1622,7 @@ class Server{
Entity::init();
Tile::init();
BlockFactory::init();
BlockFactory::registerStaticRuntimeIdMappings();
Enchantment::init();
ItemFactory::init();
Item::initCreativeItems();
@ -1608,13 +1630,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 +1655,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 +1684,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 +1784,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 +1880,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 +1934,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 +1990,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 +1999,7 @@ class Server{
}
/**
* Shutdowns the server correctly
* Shuts the server down correctly
*/
public function shutdown(){
$this->isRunning = false;
@ -2035,20 +2044,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 +2108,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());
}
}
@ -2152,7 +2162,7 @@ class Server{
$this->logger->logException($e, $trace);
$lastError = [
"type" => \get_class($e),
"type" => get_class($e),
"message" => $errstr,
"fullFile" => $e->getFile(),
"file" => $errfile,
@ -2224,9 +2234,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 +2244,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 +2337,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 +2406,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 = [];
}
@ -2479,31 +2486,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 +2515,7 @@ class Server{
}
if(($this->tickCounter % 20) === 0){
if($this->doTitleTick and Terminal::hasFormattingCodes()){
if($this->doTitleTick){
$this->titleTick();
}
$this->currentTPS = 20;
@ -2576,8 +2580,6 @@ class Server{
}else{
$this->nextTick += 0.05;
}
return true;
}
/**

View File

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

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

@ -31,8 +31,6 @@ 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 +63,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 +324,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 +351,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 +361,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 +415,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
*

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

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

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

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

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

@ -104,7 +104,7 @@ class TimingsCommand extends VanillaCommand{
];
fclose($fileTimings);
$sender->getServer()->getScheduler()->scheduleAsyncTask(new class([
$sender->getServer()->getAsyncPool()->submitTask(new class([
["page" => "http://paste.ubuntu.com", "extraOpts" => [
CURLOPT_HTTPHEADER => ["User-Agent: " . $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion()],
CURLOPT_POST => 1,
@ -114,7 +114,7 @@ class TimingsCommand extends VanillaCommand{
]]
], $sender) extends BulkCurlTask{
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;
}

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;
@ -134,9 +133,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_URL_TAG = 41; //string
public const DATA_MAX_AIR = 42; //short
public const DATA_MARK_VARIANT = 43; //int
/* 44 (byte) container stuff
* 45 (int) container stuff
* 46 (int) container stuff */
public const DATA_CONTAINER_TYPE = 44; //byte (ContainerComponent)
public const DATA_CONTAINER_BASE_SIZE = 45; //int (ContainerComponent)
public const DATA_CONTAINER_EXTRA_SLOTS_PER_STRENGTH = 46; //int (used for llamas, inventory size is baseSize + thisProp * strength)
public const DATA_BLOCK_TARGET = 47; //block coords (ender crystal)
public const DATA_WITHER_INVULNERABLE_TICKS = 48; //int
public const DATA_WITHER_TARGET_1 = 49; //long
@ -171,6 +170,11 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
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_COLOR_2 = 81; //byte
/* 82 (unknown) */
public const DATA_SCORE_TAG = 83; //string
public const DATA_BALLOON_ATTACHED_ENTITY = 84; //int64, entity unique ID of owner
public const DATA_PUFFERFISH_SIZE = 85; //byte
public const DATA_FLAG_ONFIRE = 0;
@ -224,6 +228,12 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_FLAG_FIRE_IMMUNE = 48;
public const DATA_FLAG_DANCING = 49;
public const DATA_FLAG_ENCHANTED = 50;
//51 is something to do with tridents
public const DATA_FLAG_CONTAINER_PRIVATE = 52; //inventory is private, doesn't drop contents when killed if true
//53 TransformationComponent
public const DATA_FLAG_SPIN_ATTACK = 54;
public const DATA_FLAG_SWIMMING = 55;
public const DATA_FLAG_BRIBED = 56; //dolphins have this set when they go to find treasure for the player
public static $entityCount = 1;
/** @var Entity[] */
@ -377,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 */
@ -549,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));
@ -562,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);
}
@ -584,21 +591,21 @@ 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){
public function setNameTagAlwaysVisible(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value);
}
@ -612,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;
@ -624,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;
}
@ -645,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);
}
@ -653,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);
@ -665,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);
}
@ -687,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);
}
@ -707,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);
}
@ -715,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);
}
@ -723,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);
@ -739,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){
@ -753,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);
}
@ -763,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);
@ -779,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){
@ -812,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");
}
@ -820,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);
@ -839,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", [
@ -856,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)){
@ -870,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;
@ -895,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;
@ -904,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();
}
@ -935,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;
}
@ -962,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;
}
@ -1030,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;
@ -1053,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);
}
@ -1085,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);
}
@ -1098,11 +1104,11 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return true;
}
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;
@ -1115,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();
}
@ -1128,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;
@ -1142,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;
@ -1156,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{
@ -1186,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;
@ -1237,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;
}
@ -1279,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;
@ -1329,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();
@ -1347,16 +1346,16 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($this->hasMovementUpdate()){
$this->tryChangeMovement();
$this->move($this->motionX, $this->motionY, $this->motionZ);
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
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;
}
$this->forceMovementUpdate = false;
@ -1377,7 +1376,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;
}
@ -1392,7 +1394,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;
@ -1405,9 +1407,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
);
}
@ -1416,7 +1418,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
return true;
}
public function resetFallDistance(){
public function resetFallDistance() : void{
$this->fallDistance = 0.0;
}
@ -1424,7 +1426,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);
@ -1440,11 +1442,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{
}
@ -1452,16 +1450,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);
@ -1472,7 +1466,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());
}
@ -1486,7 +1480,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);
@ -1574,7 +1568,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);
@ -1650,15 +1644,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!)
@ -1666,7 +1660,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);
@ -1680,12 +1674,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 = [];
@ -1713,7 +1707,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){
@ -1724,9 +1718,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;
}
}
@ -1762,7 +1756,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();
@ -1778,7 +1772,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)){
@ -1809,17 +1803,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()){
@ -1827,9 +1821,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();
@ -1849,7 +1841,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;
@ -1934,7 +1926,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;
@ -1942,7 +1934,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
public function spawnToAll(){
public function spawnToAll() : void{
if($this->chunk === null or $this->closed){
return;
}
@ -1953,7 +1945,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);
@ -1964,7 +1956,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();
@ -1975,7 +1967,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}
}
public function despawnFromAll(){
public function despawnFromAll() : void{
foreach($this->hasSpawned as $player){
$this->despawnFrom($player);
}
@ -1986,6 +1978,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*/
public function flagForDespawn() : void{
$this->needsDespawn = true;
$this->scheduleUpdate();
}
public function isFlaggedForDespawn() : bool{
@ -2005,7 +1998,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;
@ -2034,7 +2027,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;
@ -2068,7 +2061,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);
}
@ -2076,7 +2069,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];
}
@ -2129,5 +2122,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,9 +461,9 @@ 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);
}
/**
@ -466,8 +473,8 @@ abstract class Living extends Entity implements Damageable{
* @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 +499,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();
}
}
@ -540,7 +547,7 @@ 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());
}
}
@ -559,36 +566,37 @@ abstract class Living extends Entity implements Damageable{
$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();
}
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 +657,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 +674,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 +694,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 +711,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 +729,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 +745,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 +753,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 +823,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 +857,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);
@ -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\"");

View File

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace pocketmine\event;
use pocketmine\plugin\Plugin;
use pocketmine\plugin\PluginManager;
use pocketmine\plugin\RegisteredListener;
use pocketmine\utils\Utils;
class HandlerList{
/**
@ -69,7 +69,7 @@ class HandlerList{
}
$class = new \ReflectionClass($event);
$tags = PluginManager::parseDocComment((string) $class->getDocComment());
$tags = Utils::parseDocComment((string) $class->getDocComment());
if($class->isAbstract() && !isset($tags["allowHandle"])){
return null;

View File

@ -42,6 +42,8 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
protected $instaBreak = false;
/** @var Item[] */
protected $blockDrops = [];
/** @var int */
protected $xpDrops;
/**
* @param Player $player
@ -49,14 +51,16 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
* @param Item $item
* @param bool $instaBreak
* @param Item[] $drops
* @param int $xpDrops
*/
public function __construct(Player $player, Block $block, Item $item, bool $instaBreak = false, array $drops){
public function __construct(Player $player, Block $block, Item $item, bool $instaBreak = false, array $drops, int $xpDrops = 0){
parent::__construct($block);
$this->item = $item;
$this->player = $player;
$this->instaBreak = $instaBreak;
$this->setDrops($drops);
$this->xpDrops = $xpDrops;
}
/**
@ -88,7 +92,7 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
/**
* @param bool $instaBreak
*/
public function setInstaBreak(bool $instaBreak){
public function setInstaBreak(bool $instaBreak) : void{
$this->instaBreak = $instaBreak;
}
@ -103,7 +107,7 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
/**
* @param Item[] $drops
*/
public function setDrops(array $drops){
public function setDrops(array $drops) : void{
$this->setDropsVariadic(...$drops);
}
@ -112,7 +116,28 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
*
* @param Item ...$drops
*/
public function setDropsVariadic(Item ...$drops){
public function setDropsVariadic(Item ...$drops) : void{
$this->blockDrops = $drops;
}
/**
* Returns how much XP will be dropped by breaking this block.
*
* @return int
*/
public function getXpDropAmount() : int{
return $this->xpDrops;
}
/**
* Sets how much XP will be dropped by breaking this block.
*
* @param int $amount
*/
public function setXpDropAmount(int $amount) : void{
if($amount < 0){
throw new \InvalidArgumentException("Amount must be at least zero");
}
$this->xpDrops = $amount;
}
}

View File

@ -44,5 +44,4 @@ class BlockGrowEvent extends BlockEvent implements Cancellable{
public function getNewState() : Block{
return $this->newState;
}
}

View File

@ -43,5 +43,4 @@ class BlockSpreadEvent extends BlockFormEvent{
public function getSource() : Block{
return $this->source;
}
}

View File

@ -81,7 +81,7 @@ class SignChangeEvent extends BlockEvent implements Cancellable{
*
* @throws \InvalidArgumentException if there are more or less than 4 lines in the passed array
*/
public function setLines(array $lines){
public function setLines(array $lines) : void{
if(count($lines) !== 4){
throw new \InvalidArgumentException("Array size must be 4!");
}
@ -94,7 +94,7 @@ class SignChangeEvent extends BlockEvent implements Cancellable{
*
* @throws \InvalidArgumentException if the index is out of bounds
*/
public function setLine(int $index, string $line){
public function setLine(int $index, string $line) : void{
if($index < 0 or $index > 3){
throw new \InvalidArgumentException("Index must be in the range 0-3!");
}

View File

@ -55,5 +55,4 @@ class EntityBlockChangeEvent extends EntityEvent implements Cancellable{
public function getTo() : Block{
return $this->to;
}
}

View File

@ -46,5 +46,4 @@ class EntityCombustByBlockEvent extends EntityCombustEvent{
public function getCombuster() : Block{
return $this->combuster;
}
}

View File

@ -45,5 +45,4 @@ class EntityCombustByEntityEvent extends EntityCombustEvent{
public function getCombuster() : Entity{
return $this->combuster;
}
}

View File

@ -46,8 +46,7 @@ class EntityCombustEvent extends EntityEvent implements Cancellable{
return $this->duration;
}
public function setDuration(int $duration){
public function setDuration(int $duration) : void{
$this->duration = $duration;
}
}

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