Compare commits

...

774 Commits

Author SHA1 Message Date
aa7206126a Release 3.3.4 2018-12-01 10:00:38 +00:00
1a6db1c7ce DataPacket: add missing field
this must have been missing for how many years now? thanks @shoghicp

this is why we don't do releases on friday night... in my defence my device had the beta installed...
2018-12-01 09:29:53 +00:00
f1c071ce7f Release 3.3.3 2018-11-30 19:41:36 +00:00
e2f46a4358 Remove unused import... 2018-11-30 19:40:35 +00:00
4c08a05fae Barf on trying to read/write nonexisting fields of packets
this should make it easier to debug problems when content of packets changes during protocol updates.
2018-11-30 18:36:28 +00:00
b473ffdedc Remove async playerdata saving, closes #2515
this technically involves non-breaking API changes which should happen on a patch release, but I can't be bothered with the dust cleanup, so we'll just blow it away now. It doesn't hurt anyone anyway.
2018-11-29 19:47:15 +00:00
60dddcd12a Painting: clean up guard checks, remove unnecessary checks 2018-11-29 19:29:10 +00:00
93c26a0b0c Living: Suspend effects ticking on death
This was the cause of a bug with regeneration which caused players taking fatal damage under regeneration not to die correctly. On the server side they would die and immediately regenerate some health, which would cause the next attribute sync to not report the health drop to zero, which made the client unaware that it was dead.

Perhaps attributes should be forcibly synced in some circumstances, but nonetheless regeneration shouldn't apply post-death.
2018-11-29 18:45:46 +00:00
545ec9c881 Updated PreProcessor submodule 2018-11-26 14:02:23 +00:00
d5a1961e6b Force minimum uptime to be >= 120 seconds if a crash occurs (#2534)
This is an incremental improvement over 4a6841a5a4. This change works better because it also reduces disk spam of crashdumps.

This will now sleep if the server uptime was less than 120 seconds before crashing. If unattended, this will clamp down on automated crashdump spam. If attended, the user can simply press CTRL+C to abort the process and skip the delay.
2018-11-25 14:35:35 +00:00
6bc79149c3 SubChunk: Fixed $changed not getting set in setBlock() when only block data changed
it was comparing a string and an int. This now compares the integer values first.
2018-11-22 16:47:25 +00:00
e018311e73 Make start script errors a bit more noob-friendly 2018-11-13 18:23:54 +00:00
71d02e5870 Improve dev build error messages 2018-11-12 22:07:14 +00:00
d312aef1ac 3.3.3 is next 2018-11-11 11:58:51 +00:00
200de3fe84 Release 3.3.2 2018-11-11 11:58:25 +00:00
f560a6efea Merge tag '3.2.7' into release/3.3 2018-11-11 11:24:23 +00:00
7ecd7fd13f Release 3.2.7 2018-11-11 11:23:20 +00:00
b893645a81 Merge branch 'release/3.2' into release/3.3 2018-11-11 11:15:39 +00:00
b19b3134ad PluginManager: reduce unnecessary indentation 2018-11-11 11:15:27 +00:00
243f86b0a0 Merge branch 'release/3.2' into release/3.3 2018-11-10 22:37:02 +00:00
9156cbc269 PluginManager: Make registerEvents() check order more logical
Prioritize validating that the function is actually a handler, before trying to parse its doc comment.
2018-11-10 22:36:46 +00:00
43fe6a1934 Merge branch 'release/3.2' into release/3.3 2018-11-07 20:02:28 +00:00
342a74ffcb Level: Collect garbage from chunk internals in doChunkGarbageCollection() 2018-11-07 20:01:07 +00:00
2183bf875c Merge remote-tracking branch 'origin/release/3.2' into release/3.3 2018-11-04 23:32:33 +00:00
8cc2a4ce5d Remove start script support for source-code installations (#2495)
This was suggested recently by @TheDeibo. We don't want users running source-code installations unless they are developers, and developers should know how to boot a source-code installation anyway.
2018-11-04 23:31:57 +00:00
f61e14e341 Merge branch 'release/3.2' into release/3.3 2018-11-04 22:38:45 +00:00
7b24fbc8db Utils: fix a mistake in getNiceClassName() doc 2018-11-04 22:38:38 +00:00
c4f3426bae Merge branch 'release/3.2' into release/3.3 2018-11-04 22:15:21 +00:00
046c39b02e Remove some Throwable abuse in AsyncTasks 2018-11-04 22:15:06 +00:00
5c3eed40b3 Merge branch 'release/3.2' into release/3.3 2018-11-04 11:57:28 +00:00
3e5237b6e0 ItemEntity: remove useless instanceof 2018-11-04 11:57:22 +00:00
d9a867016c Merge branch 'release/3.2' into release/3.3 2018-11-03 19:43:45 +00:00
a50a863ab7 Chunk: be more intelligent about fast-serializing chunks
This reduces the amount of useless data that pthreads has to copy around.
2018-11-03 19:43:35 +00:00
1b03168b88 Merge branch 'release/3.2' into release/3.3 2018-11-03 15:12:40 +00:00
6b9fee05d6 Fixed performance bug with chunk sending
this process of fast-serialization, fast-deserialize, network-serialize is an order of magnitude slower than just doing the network encode directly on the main thread, and also copies more useless data.

For the main thread, the figures were something like 3x more expensive, and then an extra 7x for deserialization on the worker thread. This is a ridiculously large overhead.
2018-11-03 15:12:30 +00:00
45a18ffe1e Merge branch 'release/3.2' into release/3.3 2018-11-03 12:12:23 +00:00
f0182c9996 TaskHandler: remove incorrect internal warning
this is perfectly fine to use, and preferable to getting a cyclic ref to the scheduler. TaskScheduler->cancelTask() does pretty much the exact same thing, and the scheduler internals are designed to deal with this anyway.
2018-11-03 12:12:02 +00:00
2d88058710 Merge branch 'release/3.2' into release/3.3 2018-10-31 18:55:18 +00:00
ab48d85c35 Properly deal with anonymous tasks in timings 2018-10-31 18:51:30 +00:00
d9b7a28747 Merge branch 'release/3.2' into release/3.3 2018-10-30 15:42:52 +00:00
31ceafa111 Chest: keep inventory better in sync when paired
should fix #2502
2018-10-30 15:42:44 +00:00
694d7d4e20 Merge branch 'release/3.2' into release/3.3 2018-10-29 12:42:10 +00:00
2da2fdd6d4 Added a test for Item->equals() when both items have no NBT 2018-10-29 12:42:03 +00:00
c1c56f29bb Merge branch 'release/3.2' into release/3.3 2018-10-26 20:08:55 +01:00
9b820a0849 Guard against possible overflow bug in NetworkBinaryStream 2018-10-26 20:08:48 +01:00
3128449033 3.3.2 is next 2018-10-25 19:36:20 +01:00
a60154e0b7 Release 3.3.1 2018-10-25 19:29:57 +01:00
4cbbf2e91c Merge branch 'release/3.2' into release/3.3 2018-10-25 19:29:34 +01:00
b0624aff9f 3.2.7 is next 2018-10-25 19:28:54 +01:00
c38e2c5ccb Release 3.2.6 2018-10-25 19:20:26 +01:00
02ef0bfbb4 Remove unnecessary quotes
these appear on the echoed message, which is undesirable.
2018-10-25 18:49:06 +01:00
4835537886 Merge branch 'release/3.2' into release/3.3 2018-10-25 18:41:58 +01:00
4a6841a5a4 Added client side self rate-limiting for crashdump reporting
this should produce some reduction in spam at the source.

This could also be used to control the rate at which constantly-crashing servers restart to stop them spamming the disk as well, but the main concern here is eliminating crash archive involuntary DDoS by crashy servers.
2018-10-25 18:10:59 +01:00
925da62afa Merge branch 'release/3.2' into release/3.3 2018-10-24 15:49:09 +01:00
09985c5763 Fixed async light population producing garbage when generator isn't registered, closes #2488 2018-10-24 15:49:00 +01:00
d1ee9eb960 Merge branch 'release/3.2' into release/3.3 2018-10-24 12:00:55 +01:00
196cf8a68d Fixed missing MoveEntityDeltaPacket field 2018-10-24 12:00:25 +01:00
5e68858ebf Merge branch 'release/3.2' into release/3.3 2018-10-21 18:16:59 +01:00
45c9caa38c Fixup some formatting issues 2018-10-21 18:15:25 +01:00
d749f19c73 Merge branch 'release/3.2' into release/3.3 2018-10-19 18:53:12 +01:00
41fd03f329 LightUpdate: fixed double-updated nodes not getting light propagated appropriately
This can happen when a light source is removed and later encountering another light source to fill the gap. A higher light level may get set and then not propagated. This bug is difficult to explain, but fairly easy to reproduce.
2018-10-19 18:53:04 +01:00
58067b2ad1 Merge branch 'release/3.2' into release/3.3 2018-10-19 15:55:53 +01:00
0c9946621c Level: Do not tick chunks which have unloaded adjacent chunks
Grass can cause issues here by requesting blocks randomly offset away from itself, which can cause silent chunk loading on chunk ticking. It also causes crashes if chunk autoloading is taken away, which is obviously undesired.

It was also noticed that player chunkloaders cause chunks to start getting ticked as soon as they load their first chunk, which is before the entity is visible to everyone else on the server. This is probably undesired behaviour.
2018-10-19 15:48:46 +01:00
af5637e050 PlayerListEntry: remove dead fields 2018-10-16 22:56:21 +01:00
a524b0e447 3.3.1 is next 2018-10-16 18:19:49 +01:00
88a5e92c20 Release 3.3.0 2018-10-16 17:47:35 +01:00
b876ae4ef8 Merge branch 'release/3.2' into release/3.3 2018-10-16 17:26:46 +01:00
1983964f9e 3.2.6 is next 2018-10-16 17:26:04 +01:00
c4c55a45c9 Release 3.2.5 2018-10-16 17:17:53 +01:00
c5cd813b76 bump PM version 2018-10-16 17:15:49 +01:00
bc2dff3f51 version numbers 2018-10-16 17:15:26 +01:00
839d5eab7b Protocol changes for 1.7
there's also some new cases in stats, but we don't care about those anyway.
2018-10-16 17:13:52 +01:00
78923177f9 VersionString: use appropriate regex for number matching 2018-10-16 16:46:27 +01:00
b7062e7bff CrashDump: don't try to report code that doesn't exist
this can happen when eval() is used, and then we get a big blank mess with nothing on it. eval() is a special case that should be handled separately, but for now this is just fixing a bug.
2018-10-16 09:50:59 +01:00
97980d4516 Update composer dependency versions 2018-10-12 09:31:19 +01:00
d9220395d1 Dummy decode for ResourcePacksInfoPacket and ResourcePackStackPacket
while we can't deal with this information, it's needed for the sake of unit testing so we don't shit on every bit of incoming data of these packet types.
2018-10-11 19:42:00 +01:00
2858db430e Fixed AsyncTask publishProgress() race condition on task exit
It's possible for a progress update to be lost due to the task finishing before the main thread found the progress update.
2018-10-10 13:41:15 +01:00
32836cbfb8 Don't handle remaining packets in a batch when an earlier one triggered a disconnect 2018-10-09 22:50:02 +01:00
8316e00927 Player: Throw exception on failure to encode form JSON 2018-10-09 22:39:48 +01:00
fd459cda54 3.2.5 is next 2018-10-07 19:45:06 +01:00
a66dd4a7d9 Release 3.2.4 2018-10-07 19:32:04 +01:00
3617eba4a3 Merge branch 'release/3.1' into release/3.2 2018-10-07 19:31:16 +01:00
e79cc98883 Release 3.1.8 2018-10-07 19:20:20 +01:00
d259b2c9ee Merge branch 'release/3.1' into release/3.2 2018-10-07 17:48:19 +01:00
10fa74b417 Make clear that Plugin->setEnabled() is @internal
Use of this by plugins will produce a lot of undefined behaviour, such as event handlers not being unregistered, scheduled tasks not being removed, and registered permissions causing memory leaks.
2018-10-07 17:48:11 +01:00
17ceb27af4 Merge branch 'release/3.1' into release/3.2 2018-10-06 14:45:05 +01:00
adbd1c7bed RCON: remove redundant sleep
this dates back to the days where PM used to kill threads to stop them. Today we're more civilized and ask it to stop nicely, so this isn't necessary anymore.
2018-10-06 14:44:56 +01:00
cf20e626e2 Merge branch 'release/3.1' into release/3.2 2018-10-05 17:43:54 +01:00
d75c830a7e Add -f parameter to lint.sh to allow it to not be useless in cygwin
find can conflict with windows' built in find command, which causes it to bug out when running tests.
2018-10-05 17:43:45 +01:00
722924a779 Merge branch 'release/3.1' into release/3.2 2018-10-04 16:40:55 +01:00
60e1b29462 RegionLoader: Remove incorrect size cap
This assumes that the region is properly garbage-collected and packed, but if the file contains uncollected garbage this may not be the case, resulting in a region larger than a gigabyte.
2018-10-04 16:40:45 +01:00
5b511f6d06 Merge branch 'release/3.1' into release/3.2 2018-09-29 15:39:27 +01:00
426dee04a6 Potion: remove unnecessary exception throw in getPotionEffectsById()
this is only used by Potion and SplashPotion, and simply causes errors when trying to use potions with unknown IDs.
2018-09-29 15:39:20 +01:00
bb1944ca40 Merge branch 'release/3.1' into release/3.2 2018-09-26 13:12:20 +01:00
d1a20ecb4a CommandReader: Require readline to be explicitly enabled on Windows
readline on Windows causes issues with console output corruption. Additionally, PM readline impl is extremely buggy and probably ought to be removed. However, have a hotfix for now.
2018-09-26 13:11:21 +01:00
f6a8ec83a1 Merge branch 'release/3.1' into release/3.2 2018-09-24 18:26:39 -04:00
28137efb53 Fixed server freezing when using chorus fruit from large Y coordinates 2018-09-24 18:26:20 -04:00
7b0836d399 Merge branch 'release/3.1' into release/3.2 2018-09-23 16:35:11 +01:00
cea146e335 Thin: use bounding box instead of collision boxes 2018-09-23 16:35:01 +01:00
8db1ccc1ae Merge branch 'release/3.1' into release/3.2 2018-09-20 19:02:09 +01:00
5d56030afa Item: make nbtDeserialize() return AIR when reading an unknown PC item
This is scummy, but it's better than crashing the whole server just because a chest contained an unknown item.
2018-09-20 19:00:44 +01:00
d9c251b613 Merge branch 'release/3.1' into release/3.2 2018-09-20 17:04:45 +01:00
8085b81f5c fix phars 2018-09-20 17:04:34 +01:00
33d3fff3c5 Merge branch 'release/3.1' into release/3.2 2018-09-20 16:49:57 +01:00
7c092b93b4 Fixed bug when placing blocks by clicking on redstone ore 2018-09-20 16:49:50 +01:00
aa05650994 Fixed block picking for mob heads 2018-09-20 13:11:45 +01:00
758d9b9784 Farmland: fixed block picking 2018-09-20 12:03:01 +01:00
24a6bf7365 PocketMine.php: Allow overriding autoloader path using --bootstrap
I've gotten tired of re-running composer every time I switch branches...
2018-09-20 12:01:39 +01:00
9a5d51fd3d Fixed block-picking cake giving the block instead of item 2018-09-20 11:31:48 +01:00
6a7f39978b Merge branch 'release/3.1' into release/3.2 2018-09-20 10:03:47 +01:00
c52e1ea9f9 Fixed block picking double slabs giving the double slab block 2018-09-20 10:02:55 +01:00
a0bb747d6d Merge branch 'release/3.1' into release/3.2 2018-09-19 16:16:18 +01:00
4bc0d850b1 Added Block->getRuntimeId(), clean up some mess 2018-09-19 16:16:10 +01:00
97583c8b04 Merge branch 'release/3.1' into release/3.2 2018-09-18 12:32:01 +01:00
107192c753 Bed: fixed block-pick giving wrong colour items 2018-09-18 12:31:53 +01:00
870f9abc20 Merge branch 'release/3.1' into release/3.2 2018-09-18 12:22:20 +01:00
0e2bbc44db Fixed drops and item picking of Brewing Stand 2018-09-18 12:22:12 +01:00
d9768abe47 Merge branch 'release/3.1' into release/3.2 2018-09-16 17:47:01 +01:00
e9b84ecc8b Fixed incorrect break check for torch 2018-09-16 17:46:50 +01:00
c83d12790e Merge branch 'release/3.1' into release/3.2 2018-09-14 17:09:41 +01:00
5863d4c066 Fixed PermissibleBase->clearPermissions() not unsubscribing from permissions that aren't explicitly assigned
This came to light after observing cfb6856634 in a fresh light. I noticed that this fix should not have been necessary because clearPermissions() should have dealt with it. Unfortunately, permissions can be set without being set in PermissibleBase->permissions, so this misses things.
2018-09-14 17:06:32 +01:00
7d54d18732 Merge branch 'release/3.1' into release/3.2 2018-09-14 16:18:12 +01:00
bfbc845efa Remove impossible uses of PlayerInteractEvent CLICK_AIR constants 2018-09-14 16:17:55 +01:00
2ff4228fb7 Merge branch 'release/3.1' into release/3.2 2018-09-14 11:06:11 +01:00
06c4f31db7 Server: Account for later levels being unloaded by earlier levels' ticking function in checkTickUpdates()
should fix #2434

This happens when a plugin causes a level to be unloaded during an event fired on level tick.
2018-09-14 11:05:51 +01:00
6c70e84fa2 Merge branch 'release/3.1' into release/3.2 2018-09-11 19:47:46 +01:00
7d0e631a75 RakLibInterface: fixed processing hook being registered too early
this would cause bugs if the interface was not added directly to the network.
2018-09-11 19:47:26 +01:00
65b751d080 3.2.4 is next 2018-09-11 12:27:45 +01:00
27effff403 Release 3.2.3 2018-09-11 12:19:56 +01:00
a940cc5b5e Merge branch 'release/3.1' into release/3.2 2018-09-11 12:19:32 +01:00
15e654131c 3.1.8 is next 2018-09-11 12:18:54 +01:00
6e6cda91ce Release 3.1.7 2018-09-11 11:45:17 +01:00
53a76c0d14 Merge branch 'release/3.1' into release/3.2 2018-09-11 11:35:38 +01:00
69500fe183 LightUpdate: Remove garbage left over from dab73d8950 2018-09-11 11:35:31 +01:00
5af4dd20df Merge branch 'release/3.1' into release/3.2 2018-09-08 14:25:11 +01:00
c7d58db7eb Cleanup Entity age handling, fixed arrows despawning too quickly after long flight 2018-09-08 14:23:06 +01:00
a3b78236eb Server: don't catch Throwable for level ticking
this usually causes the console to get spammed with errors. Additionally, in the case where doTick() throws any exception, it's usually because we're in a state we didn't want to be in, so we really should not carry on trying to keep ticking when something breaks here. Instead, this should generate a crashdump.
2018-09-08 14:13:28 +01:00
d8e27e6081 Bow: fix wrong arithmetic for Flame fire ticks, closes #2420 2018-09-06 19:30:55 +01:00
14a2ffa51b Merge branch 'release/3.1' into release/3.2 2018-09-06 18:43:28 +01:00
c447d51e3f Bucket: use ItemFactory instead of self-clone
in the future Item->setDamage() will be removed.
2018-09-06 18:42:09 +01:00
9f4722f537 3.2.3 is next 2018-09-04 11:56:27 +01:00
cb04f287eb Release 3.2.2 2018-09-04 11:51:28 +01:00
f649ef5195 Sync 3.2 and 3.1 branches 2018-09-04 11:51:05 +01:00
b615cad22d 3.1.7 is next 2018-09-04 11:50:05 +01:00
b93e219231 Release 3.1.6 2018-09-04 11:44:05 +01:00
a4a9309193 Updated NBT dependency for bug fix 2018-09-04 11:43:38 +01:00
56ee957fda 3.2.2 is next 2018-08-31 19:03:58 +01:00
1193efd69e Release 3.2.1 2018-08-31 18:58:53 +01:00
f466fd5568 Updated runtime IDs table
apparently 1 (!) missing entry (for barrier) was causing client sided crashes unexplained. This is infuriating.
2018-08-31 16:19:42 +01:00
d5a5209334 Merge branch 'release/3.1' into release/3.2 2018-08-30 15:46:46 +01:00
3a85e6cab9 Backport ce58294305 for 3.x line 2018-08-30 15:46:26 +01:00
bca493a682 3.2.1 is next 2018-08-30 11:04:47 +01:00
ba12dfafd6 Release 3.2.0 2018-08-29 16:28:35 +01:00
e09087de26 Fix version numbers 2018-08-29 16:27:01 +01:00
888dba704b Merge branch 'mcpe-1.6' into release/3.2 2018-08-29 16:23:53 +01:00
511249c562 Sync 3.2 and 3.1 branches 2018-08-29 16:23:28 +01:00
17f1bf5512 3.1.6 is next 2018-08-29 16:08:45 +01:00
5179bb1d30 Release 3.1.5 2018-08-29 15:57:58 +01:00
6bff840293 Sync 3.1 and 3.0 branches 2018-08-29 15:55:14 +01:00
08897c6941 Release 3.0.12 2018-08-29 15:47:28 +01:00
05d9bb45d0 Merge branch 'release/3.1' into release/3.2 2018-08-26 19:15:16 +01:00
dfe2aa9c67 Merge branch 'release/3.0' into release/3.1 2018-08-26 19:15:08 +01:00
4006be35d9 Update BinaryUtils dependency 2018-08-26 19:15:01 +01:00
e5cda34548 Merge branch 'release/3.1' into mcpe-1.6 2018-08-26 18:05:14 +01:00
032b20f659 Server: remove premature optimization of findEntity() 2018-08-25 18:37:18 +01:00
fe6d546190 Merge branch 'release/3.1' into release/3.2 2018-08-25 17:49:21 +01:00
c7af1cf785 Merge branch 'release/3.0' into release/3.1 2018-08-25 17:49:14 +01:00
22fcfffa53 PluginsCommand: remove useless private function 2018-08-25 16:09:51 +01:00
7dd53f2397 Replace unnecessary strlen > 0 calls with !== "" checks 2018-08-25 16:07:49 +01:00
298259b473 PluginsCommand: clean up garbage code 2018-08-25 16:05:25 +01:00
c123f2d10b SimpleCommandMap: clean up error reporting in registerServerAliases() 2018-08-25 15:43:23 +01:00
3e6f70ddf6 Merge branch 'release/3.1' into mcpe-1.6 2018-08-23 18:00:06 +01:00
bea634a9b7 Change air tank regeneration to match UA (#2396) 2018-08-22 19:10:53 +01:00
8daf3dc8b4 Merge branch 'release/3.1' into release/3.2 2018-08-22 15:41:52 +01:00
4cc7573a64 Merge branch 'release/3.0' into release/3.1 2018-08-22 15:40:43 +01:00
9d80802e53 Living: fixed some update conditions not being set 2018-08-22 15:40:32 +01:00
ec1e257e21 Entity: Fixed fire damage not taking effect in the void
this is an absurd bug that nobody would ever otherwise notice, but the problem is that the doOnFireTick() call isn't evaluated if hasUpdate is already true.
2018-08-22 15:36:31 +01:00
d419d4308f Add a couple of item constants, register Scute item
leaving out turtle helmet for now because of complications relating to the effect application - I REALLY don't want to tick armour if I can avoid it, due to the performance concerns.
2018-08-21 19:54:24 +01:00
9ca38ba868 Protocol changes for 1.6.0.8 + resource packs "fix" 2018-08-21 17:36:55 +01:00
424c50e1e9 Protocol changes for 1.6.0.5, minus Entity->Actor rename 2018-08-21 17:36:29 +01:00
566f3c6262 AvailableCommandsPacket: stricter decode and handling 2018-08-21 17:14:54 +01:00
0d05dcec08 AvailableCommandsPacket: deal with dynamic enums
somehow I missed this, thanks @NiclasOlofsson for pointing it out
2018-08-21 17:14:53 +01:00
986077e03c Protocol changes for 1.6.0.1 2018-08-21 17:14:53 +01:00
ddcb2f002a Tile: Be explicit about not calling Tile::createNBT() (#2388)
A common pitfall developers fall into with this function is that it has to be called from the scope of the tile class you're creating NBT for, but people commonly do Tile::createNBT() directly, which then results in cryptic "Tile is not registered" errors. This now throws a BadMethodCallException instead to be fully clear about this.

In the future this will be removed completely once NBT is no longer required to create a tile, but for now this is a confusing issue that should be dealt with.
2018-08-19 19:54:22 +01:00
c496480d2b Merge branch 'release/3.1' into release/3.2 2018-08-19 19:52:40 +01:00
6fce2b3349 Merge branch 'release/3.0' into release/3.1 2018-08-19 19:52:30 +01:00
64ed8adefc Cactus: fixed place/break logic mismatch causing item spewing 2018-08-19 19:52:07 +01:00
2eda8cfad3 Merge branch 'release/3.1' into release/3.2 2018-08-19 16:01:47 +01:00
91be5aba0c Merge branch 'release/3.0' into release/3.1 2018-08-19 16:00:25 +01:00
5df601c817 Add @see docs so PhpStorm can see dynamic constructor usages
PhpStorm can't see constructor usages when the class name is dynamic. This causes maintenance problems because cross-referencing constructors called like this doesn't show up dynamic calls.
2018-08-19 16:00:15 +01:00
21e7b5ea43 TesterPlugin: removed usage of deprecated ServerCommandEvent 2018-08-19 14:05:26 +01:00
8304675af7 Merge branch 'release/3.1' into release/3.2 2018-08-19 13:59:58 +01:00
1a47735d84 Merge branch 'release/3.0' into release/3.1 2018-08-19 13:59:34 +01:00
0cdf4d0c55 Spoon the TesterPlugin into the main repository
I am eating my own words this once, because having the tester plugin as a separate repository makes no sense - it is just added barriers to writing proper tests with no actual benefit. Since the tester plugin is specifically intended for CI, it doesn't make sense for it to be in its own module.
2018-08-19 13:58:13 +01:00
e6e28b74b5 Nuke the TesterPlugin submodule 2018-08-19 13:47:43 +01:00
ebffff0caa Added CommandEvent, deprecated (Remote)?ServerCommandEvent (#2376) 2018-08-19 12:59:16 +01:00
0dc4bd36e1 Form: change handleResponse() return type to void
this returning is counter intuitive and doesn't make any sense without the queuing mechanism. Instead it's simpler to just use Player->sendForm().
2018-08-19 11:44:59 +01:00
9d17c9a09d Merge branch 'release/3.1' into release/3.2 2018-08-19 11:22:58 +01:00
72f46b4631 Merge branch 'release/3.0' into release/3.1 2018-08-19 11:22:51 +01:00
3892f2f404 Config: Properly prevent keys getting transformed into bools
The original regex almost completely failed at its objective, because it a) only worked if there was no value for the key, and b) did not prevent all such occurrences getting transformed, while quoting patterns that would not get transformed anyway.
2018-08-19 11:22:36 +01:00
bfa415e108 Add unit tests for json decoding hack 2018-08-19 10:30:52 +01:00
b66095cb36 Added a hack for MC W10 JSON empty strings bug (#2383) 2018-08-19 10:10:40 +01:00
0336ae8229 Merge branch 'release/3.1' into release/3.2 2018-08-17 19:47:41 +01:00
4a1d67cb91 Merge branch 'release/3.0' into release/3.1 2018-08-17 19:47:34 +01:00
b4694092b7 Painting: fix motive not being saved 2018-08-17 19:47:26 +01:00
4b3e17e681 Sync 3.2 and 3.1 branches 2018-08-16 18:33:54 +01:00
d99ee515c6 3.1.5 is next 2018-08-16 18:33:27 +01:00
17f7dc34be Release 3.1.4 2018-08-16 18:26:49 +01:00
a63d66c048 Sync 3.1 and 3.0 branches 2018-08-16 18:26:26 +01:00
95f6995ae0 3.0.12 is next 2018-08-16 18:25:31 +01:00
4a24d7909e Release 3.0.11 2018-08-16 18:14:04 +01:00
5424644ca1 Merge branch 'release/3.1' into release/3.2 2018-08-16 15:52:19 +01:00
4e2387edc1 Merge branch 'release/3.0' into release/3.1 2018-08-16 15:52:12 +01:00
a5e38576ef ItemEntity: fixed using -1 for infinite pickup delay not working
closes #2382 (squash-merge)
2018-08-16 15:52:05 +01:00
aa7c4bc64d Merge branch 'release/3.1' into release/3.2 2018-08-15 13:48:46 +01:00
381151dedc Merge branch 'release/3.0' into release/3.1 2018-08-15 13:48:36 +01:00
a604e6835e CoalOre: fixed ignoring silk touch for XP drops, closes #2374 2018-08-15 13:48:29 +01:00
df8e10cad9 Forms API, part 1: add Player->sendForm() and Form interface
There's no implementation here yet, but that can come later. This lays the ground for allowing plugins to have an integrated method to send forms, as well as a solution to the ID conflict problem.

A built in implementation should not be a concretion and it should be able to be swapped for third party implementations. This enables the possiblity to do so.
2018-08-14 16:06:55 +01:00
d98a6e566c Merge branch 'release/3.1' into release/3.2 2018-08-14 15:03:30 +01:00
142a6d7678 Merge branch 'release/3.0' into release/3.1 2018-08-14 15:03:22 +01:00
b2ca364de0 SplashPotion: Don't apply effects to entities which are not alive
fixes #2372
2018-08-14 15:03:15 +01:00
ade2be9eee Merge branch 'release/3.1' into release/3.2 2018-08-13 13:22:40 +01:00
09ed40a921 Merge branch 'release/3.0' into release/3.1 2018-08-13 13:22:32 +01:00
565373cee6 OfflinePlayer: remove unnecessary getName() usages 2018-08-13 13:22:00 +01:00
c29723e3c4 OfflinePlayer: remove unnecessary strtolower() calls
closes #2371
2018-08-13 13:18:58 +01:00
39ed6a7cdf Merge branch 'release/3.1' into release/3.2 2018-08-11 19:59:53 +01:00
a8811ab2b3 Fixed 1.5.0 PlayerSkinPacket protocol change that somehow disappeared
I am 100% sure I committed this change, but it isn't in the merge...
2018-08-11 19:59:44 +01:00
bec5aaa54b Merge branch 'release/3.1' into release/3.2 2018-08-11 19:37:18 +01:00
974583a853 Merge branch 'release/3.0' into release/3.1 2018-08-11 19:37:10 +01:00
03f8fe62d4 Fixed structure of GuiDataPickItemPacket
this changed in 1.2.0.7 beta and I didn't spot it.
2018-08-11 19:36:53 +01:00
cf29ab1f17 Arrow: remove unused import 2018-08-07 19:06:40 +01:00
c5c5a53a13 Merge branch 'release/3.1' into release/3.2 2018-08-07 18:49:13 +01:00
699f35cc05 Merge branch 'release/3.0' into release/3.1 2018-08-07 18:49:06 +01:00
8fa196efc9 FallingBlock: fixed state not being saved 2018-08-07 18:48:52 +01:00
63a65680ac typo 2018-08-07 14:43:29 +01:00
47cd6fe105 EntityDamageEvent: Add API to customize Living entity attack cooldown time
closes #2310
2018-08-07 14:39:26 +01:00
f582b5a3db Merge branch 'release/3.1' into release/3.2 2018-08-07 14:32:38 +01:00
b1ab881b99 Merge branch 'release/3.0' into release/3.1 2018-08-07 14:32:28 +01:00
69c54e789a Clear the title bar when the server shutdown. 2018-08-07 14:31:39 +01:00
7f0fa2ac3d PluginBase: Do not fill defaults from resources/config.yml (#2316)
This fixes #2219.
2018-08-07 12:33:24 +01:00
max
f3b2bcfd13 Added Conduit Power effect 2018-08-06 21:30:15 +01:00
c947909c2e Updated language submodule 2018-08-06 20:52:53 +01:00
09dadc72bc Merge branch 'release/3.1' into release/3.2 2018-08-06 18:45:08 +01:00
e33d1279fa Merge branch 'release/3.0' into release/3.1 2018-08-06 18:45:01 +01:00
9e1fa453ad Level: Fixed leak of global packets when no players are online
If a global packet was broadcasted when no players were online, it would be held in memory indefinitely (until a player joined).
2018-08-06 18:44:53 +01:00
ca541032ae Move Player death message derivation to PlayerDeathEvent static method
This now no longer requires a Player to operate, only a player name.
2018-08-05 12:19:16 +01:00
bcf9915082 Merge branch 'release/3.1' into release/3.2 2018-08-05 11:33:11 +01:00
6a05edb4e9 Merge branch 'release/3.0' into release/3.1 2018-08-05 11:33:02 +01:00
70635d0870 DropItemAction: Consider invalid if the target item is null
it's not possible to drop a null item.
2018-08-05 11:32:50 +01:00
8d6dc4e188 Merge branch 'release/3.1' into release/3.2 2018-08-04 16:47:26 +01:00
46bd096f06 3.1.4 is next 2018-08-04 16:46:51 +01:00
51a8905fb3 Release 3.1.3 2018-08-04 16:41:16 +01:00
f954d7c3dc Bring 3.1 up to speed with 3.0 2018-08-04 16:40:40 +01:00
7ad0aa56b1 3.0.11 is next 2018-08-04 16:39:53 +01:00
1ff6f8846e disable dev flag 2018-08-04 16:30:23 +01:00
12d8d925c8 TimingsCommand: check for instances of InternetException only 2018-08-04 14:59:31 +01:00
f3f229ef7c Internet: only catch InternetExceptions - anything else is an unexpected fault condition 2018-08-04 14:51:26 +01:00
6614183c7f Merge branch 'release/3.1' into release/3.2 2018-08-03 20:07:52 +01:00
e6f53cc56b Merge branch 'release/3.0' into release/3.1 2018-08-03 20:07:46 +01:00
87f458f9bd AsyncPool: remove now-unnecessary isTerminated() call 2018-08-03 20:07:37 +01:00
5a7e575c3a AsyncPool: isCrashed() now returns true when a fatal error occurred
the fix for chunks earlier didn't fix...
2018-08-03 20:06:41 +01:00
7ebf3c7bf4 Merge branch 'release/3.1' into release/3.2 2018-08-03 18:50:22 +01:00
20b37d0208 Merge branch 'release/3.0' into release/3.1 2018-08-03 18:50:14 +01:00
d6d98183ea MainLogger: Log messages and exception traces in a synchronized block
this ensures that stack traces are emitted coherently without messages from other threads landing in the middle.
2018-08-03 18:50:06 +01:00
334caaaa34 Merge branch 'release/3.1' into release/3.2 2018-08-03 18:24:44 +01:00
89cf76363f Merge branch 'release/3.0' into release/3.1 2018-08-03 18:24:36 +01:00
9ff5c65fb6 Level: Make async chunk sending aware of faults
Previously any random error could occur during an AsyncTask preparing a chunk, and the Level would never know about it and thus never send the chunk.

I don't know how many invisible-chunk bug cases this fixes, but I expect it's quite a lot.
2018-08-03 18:23:32 +01:00
1532b0ef6d Level: Remove chunks from chunk send queue on unload
When a chunk request task crashes, these can get stuck and never get removed. This allows using /gc to collect the bad chunk in order to fix the bug.
2018-08-03 18:04:56 +01:00
6fcaef068f Merge branch 'release/3.1' into release/3.2 2018-08-02 14:43:51 +01:00
61accee682 Merge branch 'release/3.0' into release/3.1 2018-08-02 14:43:44 +01:00
9ece971a2b Server: remove useless check from exceptionHandler()
this cannot be null... @shoghicp y u litter the code with these useless checks ???
2018-08-02 14:41:28 +01:00
5546c88f88 Server: Fixed parse errors getting reported to CA
this changed to throwing errors as of PHP 7
2018-08-02 14:40:36 +01:00
c09ad9263b Empty merge of 3.1 into 3.2 2018-07-30 15:21:42 +01:00
4c4761d200 back to dev 2018-07-30 15:21:10 +01:00
5492495d38 disable dev flag 2018-07-30 15:10:12 +01:00
6bef07db7c Empty merge of 3.0 into 3.1 2018-07-30 15:09:53 +01:00
e8c7ae595d back to dev 2018-07-30 15:08:32 +01:00
0d9f40873f disable dev flag 2018-07-30 14:57:51 +01:00
4cc2f037a9 Merge branch 'release/3.1' into release/3.2 2018-07-30 14:54:10 +01:00
f7358cd7e1 Merge branch 'release/3.0' into release/3.1 2018-07-30 14:54:01 +01:00
a4aee98cba TimingsCommand: some code cleanup 2018-07-30 14:53:10 +01:00
a97c7d3132 Fix for timings 2018-07-30 14:42:16 +01:00
99045fe21a Entity: Implement setting score tag 2018-07-30 09:36:32 +01:00
bda271ca63 Merge branch 'release/3.1' into release/3.2 2018-07-27 11:47:36 +01:00
808d289610 Merge branch 'release/3.0' into release/3.1 2018-07-27 11:47:22 +01:00
4a1ed21e52 PluginManager: Fix patch level check to allow loading the plugin when the server's minor level is higher than the plugins declared minor level. 2018-07-27 11:46:24 +01:00
b3f2396ea5 UPnP: Make error message less useless 2018-07-26 16:00:35 +01:00
ab0510cb37 Merge branch 'release/3.1' into release/3.2 2018-07-26 14:40:33 +01:00
06c035bfe6 Merge branch 'release/3.0' into release/3.1 2018-07-26 14:40:24 +01:00
1b053c7928 Clean up pointless checks in Thread/Worker 2018-07-26 14:20:55 +01:00
c684f99cc4 Clean up Thread/Worker quit() 2018-07-26 14:17:01 +01:00
9a423be1db Internet: Throw more specific exceptions
RuntimeException is very generic and might be thrown for other reasons apart from web request failures.

This is backwards compatible because InternetException is a descendent of RuntimeException. Additionally, getURL() and postURL() have intentionally been left untouched for backwards compatibility's sake.
2018-07-26 12:34:14 +01:00
08be51dc23 Clear permissions on server reload 2018-07-26 10:40:28 +01:00
94352782d5 https://media.giphy.com/media/UAUtB4Oi9U4EM/giphy.gif 2018-07-26 10:31:57 +01:00
8fae79f85b Merge branch 'release/3.1' into release/3.2 2018-07-26 10:25:19 +01:00
8d47a222b4 Merge branch 'release/3.0' into release/3.1 2018-07-26 10:25:13 +01:00
695793795e PluginManager: Remove dead $pluginParentTimer left over from 9e4d88a852 2018-07-26 10:25:01 +01:00
9a2845640b Permissions management cleanup (#2332)
* Added a new PermissionManager, remove ridiculous cyclic dependencies of Permissions on Server

Aside from all the other ridiculous design problems with the permission system, the biggest problems are its API. This is, once again, a result of poor API design copied from Bukkit.

This pull request removes all permission-related functionality from `PluginManager` and moves it to the `pocketmine\permission\PermissionManager` class.

As can be observed from the removed code in the diff, the permissions system was previously entirely dependent on the Server, because it needed to get the PluginManager for registering permissions. This is utterly ridiculous. This refactor isolates _most_ permission-related functionality within the `permission` namespace.

As mentioned above, this stupid API is a direct result of copying from Bukkit. If you look at the API documentation for Bukkit for `PluginManager` you will see that the methods I'm deprecating here are also in there.

## Changes
- Added a new `PermissionManager` class. This can be accessed via its singleton `getInstance()` static method.
- Deprecated the following `PluginManager` methods - these will be removed no later than 4.0.0:
  - `getPermission()`
  - `addPermission()`
  - `removePermission()`
  - `getDefaultPermissions()`
  - `recalculatePermissionDefaults()`
  - `subscribeToPermission()`
  - `unsubscribeFromPermission()`
  - `getPermissionSubscriptions()`
  - `subscribeToDefaultPerms()`
  - `unsubscribeFromDefaultPerms()`
  - `getDefaultPermSubscriptions()`
  - `getPermissions()`
2018-07-26 10:21:41 +01:00
580f71d496 Permission: cosmetic reorg 2018-07-25 20:56:23 +01:00
24f11779f2 Level: don't try to unregister generators from non-live workers
this was causing garbage-collected workers to get restarted on shutdown if they previously had the generator registered for that level.
2018-07-25 18:52:17 +01:00
706c620d04 Move Internet-related functions from Utils into their own class (#2324)
- Added `Internet::getIP()`, `Internet::getURL()`, `Internet::postURL()`, and `Internet::simpleCurl()`.
- Deprecated the corresponding functions in `Utils`. Updating to the new functions is as simple as replacing `Utils` with `Internet`, since this doesn't break backwards compatibility.

The deprecations should be catered for by plugin developers. These deprecated redirects will be removed no later than 4.0.0.
2018-07-25 15:51:18 +01:00
951870e6ec Merge branch 'release/3.1' into release/3.2 2018-07-25 15:30:40 +01:00
9f425bbe2b Merge branch 'release/3.0' into release/3.1 2018-07-25 15:30:31 +01:00
a4965842d6 Remove $handlerList from PlayerExperienceChangeEvent 2018-07-25 15:30:01 +01:00
1405099768 Merge branch 'release/3.1' into release/3.2 2018-07-24 17:19:18 +01:00
d0339796b4 Added DATA_FLAG_SHOW_TRIDENT_ROPE 2018-07-24 17:19:06 +01:00
1464487945 Utils: remove unnecessary do...while(false) from getIP() 2018-07-24 16:33:03 +01:00
40c28f4d26 PluginManager: Automatically create data directories for plugins (#2284) 2018-07-21 15:57:37 +01:00
90bf94f8f7 Merge branch 'release/3.1' into release/3.2 2018-07-21 09:53:41 +01:00
5e13e2e777 Merge branch 'release/3.0' into release/3.1 2018-07-21 09:53:31 +01:00
1ef6f5d166 ZippedResourcePack: Make manifest parse errors less useless 2018-07-21 09:53:16 +01:00
eccc249009 KillCommand: clean up old shitcode 2018-07-20 19:44:41 +01:00
522ef042a7 yet another empty merge... this is tiresome 2018-07-20 12:21:49 +01:00
4be36914d6 back to dev 2018-07-20 12:21:15 +01:00
e3ef1ecb30 another empty merge 2018-07-20 12:20:54 +01:00
dbaf7287bc back to dev 2018-07-20 12:20:24 +01:00
3640062142 disable dev flag 2018-07-20 12:12:26 +01:00
9af70283fd Empty merge 2018-07-20 12:11:58 +01:00
b3b240e25b disable dev flag 2018-07-20 12:05:14 +01:00
76ee6bc298 Merge branch 'release/3.1' into release/3.2 2018-07-20 11:57:13 +01:00
b18872fbc6 Merge branch 'release/3.0' into release/3.1 2018-07-20 11:57:06 +01:00
2b30ef1671 Revert "Living: fix knockback condition, take 2"
This reverts commit 0081e30a89.

The logic introduced by this commit is correct in MC JAVA 1.9+. Unfortunately, nobody likes 1.9+ for combat.
Some testing in MCPE vanilla made it apparent that this logic isn't correct for MCPE. The old logic is correct for pre-1.9 knockback.
2018-07-20 11:55:10 +01:00
04f20c703c Merge branch 'release/3.1' into release/3.2 2018-07-20 11:30:39 +01:00
dd8499e202 Merge branch 'release/3.0' into release/3.1 2018-07-20 11:30:27 +01:00
124ebf69c5 PlayStatusPacket: default to current protocol if not specified 2018-07-20 11:29:40 +01:00
efe4b0cd3a Merge branch 'release/3.1' into release/3.2 2018-07-18 15:14:34 +01:00
4d1e56069d Merge branch 'release/3.0' into release/3.1 2018-07-18 15:14:27 +01:00
4274640845 Player: fixed on-ground state not being updated when walking horizontally
it's possible to walk off a tower while flying without moving vertically, and this code previously wouldn't detect that, leaving a gaping hole in the anti-cheat.
2018-07-18 15:14:18 +01:00
527d8e9374 Merge branch 'release/3.1' into release/3.2 2018-07-17 18:35:16 +01:00
c1c70a8a98 move up a version
pushing this back to 3.2 so that we can make space for MCPE 1.5 line as 3.1.
2018-07-17 18:34:55 +01:00
45d30d53cc back to dev 2018-07-17 18:33:36 +01:00
cfc8dfa369 disable dev flag 2018-07-17 18:21:02 +01:00
93a2f397c6 Merge branch 'mc-broken-ed-1.5' into release/3.1 2018-07-17 18:13:06 +01:00
62fc875cdc bump version 2018-07-17 18:12:49 +01:00
58b665985e back to dev 2018-07-17 18:09:24 +01:00
0f5c48e342 Disable dev flag for release 2018-07-17 16:59:00 +01:00
b7f15b6574 Merge branch 'release/3.0' into release/3.1 2018-07-17 16:56:57 +01:00
08ad5db05b Config: remove useless switch cases
CNF is the same type as PROPERTIES (it's an alias) so these cases are useless.
2018-07-17 16:56:47 +01:00
6ab2fa84da added some tests for ItemFactory::fromString() 2018-07-17 14:52:47 +01:00
b480c63060 Fixed ItemFactory::fromString() meta handling bug introduced by 71c3c34976 2018-07-17 14:46:08 +01:00
f6b54f5116 Server: don't create levels inside catch-all
Under normal circumstances, none of the boxed code will throw exceptions. Under exceptional circumstances, the caller should know about it. Usually the caller is the server. We don't want to catch unexpected exceptions because those should crash the server and generate a crashdump.
2018-07-17 12:18:46 +01:00
89bfc380e3 Merge branch 'release/3.0' into release/3.1 2018-07-17 12:14:33 +01:00
94e8623c75 Server: account for default provider being missing 2018-07-17 12:14:26 +01:00
40030e9800 added some LevelProviderManager tests 2018-07-17 12:02:08 +01:00
ad1cf38c21 LevelProviderManager: tighten up checks on registering 2018-07-17 12:02:08 +01:00
5d769147ca LevelProviderManager: make addProvider() throw InvalidArgumentException instead of LevelException
LevelException is not useful because it's too generic.
2018-07-17 12:02:08 +01:00
6f00a30ad7 Merge branch 'release/3.0' into release/3.1 2018-07-17 10:12:52 +01:00
b4bf6901e3 Server: remove useless try/catch around Query event firing
this doesn't raise any exceptions, and if it causes Errors to be thrown, those are defects that should be fixed. A catch-all is a bad thing.
2018-07-17 10:10:28 +01:00
921f7e8f6a Level: remove useless check from populateChunk()
this is already checked at the top of the function.
2018-07-16 17:36:36 +01:00
71c3c34976 ItemFactory: prepare for handling items with negative IDs 2018-07-16 13:24:12 +01:00
16c253d7a9 Item: allow negative IDs
this will be needed in the future for extended blocks support.
2018-07-16 12:46:16 +01:00
7efe767f1f Merge branch 'release/3.0' into release/3.1 2018-07-16 12:08:22 +01:00
710e1d014d Entity: fixed 0-length motion vectors being passed to move()
this was an interesting bug.

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

This also led me to realize that there's an edge case in the current movement system - if an entity's motion reaches 0, it will stop getting movement updates. This can be undesirable when things such as gravity cause motion to become zero when throwing a projectile directly upwards. This will need to be fixed separately.
2018-07-16 12:08:13 +01:00
2e18fe710c MemoryManager: Shut down idle workers during GC to reclaim memory
workers can be a major memory hog, especially if you have lots of them.
2018-07-14 18:07:37 +01:00
165aac1ba3 Merge branch 'release/3.0' into mc-broken-ed-1.5 2018-07-14 16:09:57 +01:00
878dd3b842 Merge branch 'release/3.0' into release/3.1 2018-07-14 16:06:02 +01:00
7fc22d3227 Entity: fixed setNameTagAlwaysVisible()
mojang >.<

this doesn't fix the problem of invisibility making nametags hidden though.
2018-07-14 16:05:46 +01:00
478a131aa5 Flat: separate logic of parsing preset and generating base chunk
now always generates the base chunk on init, because the generator won't be created unless there is generation to be done.
2018-07-14 11:51:49 +01:00
53068caf3c Level: Only register generators when attempting to actually generate chunks
This saves a ton of memory on servers which don't generate any chunks during their runtime (which is most servers).
2018-07-14 11:34:55 +01:00
fe7ad7a5b3 Merge branch 'release/3.0' into release/3.1 2018-07-14 10:39:28 +01:00
7bfe487ee5 ConcretePowder: fixed a missed usage of Block::get() 2018-07-14 10:35:05 +01:00
24f749a933 Merge branch 'release/3.0' into release/3.1 2018-07-13 12:36:10 +01:00
d8cf835f92 BlockFactory: better handling for dodgy IDs
I thought I'd already dealt with this, but it seems not.
2018-07-13 12:31:22 +01:00
65e44364e5 Added some debug for raw packets and Query handling 2018-07-13 10:07:11 +01:00
af80aefd45 Remove async config save (#2298)
As discussed in #2297:

Honestly I don't see a fit purpose for async saving at all. It should either always be synchronous or always asynchronous, and at the user's own option. However, this isn't currently possible because Config doesn't enable you to get the serialized content without writing it to disk.

Consider the following code:
```php
		for($i = 0, $size = $this->getServer()->getAsyncPool()->getSize(); $i < $size; ++$i){
			$this->getServer()->getAsyncPool()->submitTask(new class extends AsyncTask{
				public function onRun(){
					sleep(5);
				}
			});
		}
		$config = $this->getConfig();
		$config->set("steve", "hi");
		$config->save(true);
		$config->set("steve", "bye");
		$config->save(false);
```
Output:
```yml
---
steve: hi
...
```
Expected output:
```yml
---
steve: bye
...
```

Additionally, if your configs are causing you performance issues when you're saving, it's a clear sign that
a) you're saving too much
b) you're abusing configs and should consider using a database.

Configs should be used for _simple_ data which does not change much. Configuration is such that the _user_ is expected to be able to modify it. As such, it should never be an issue to save synchronously.

In the future, something like ReactPHP may be introduced to allow proper async saving. When this happens, async saving would always be sequential but non blocking. Using threads for this makes no sense.
2018-07-12 19:31:00 +01:00
1d5c741f28 PluginBase: Automatically save default config if it doesn't exist (#2285)
I wasn't sure whether this would be considered a bug fix or a feature. Nonetheless, it's a behavioural change, so it belongs in 3.1 if anywhere.

Prior to this, plugins would be required to call saveDefaultConfig() before calling getConfig() or anything else. Calling getConfig() without saveDefaultConfig() first would generate an empty configuration file. Instead, it now saves the default config before loading it.
2018-07-12 19:25:48 +01:00
3a373b880d Listener: Add documentation on functionality (#2292)
The Listener interface is one of the most magical parts of PocketMine-MP, and before this pull request it didn't have a single bit of documentation.
2018-07-12 19:24:46 +01:00
1b7cd156aa Merge branch 'release/3.0' into release/3.1 2018-07-12 18:04:19 +01:00
ebbbc581ca Player: clean up cursor inventory when closing main inventory 2018-07-12 17:52:22 +01:00
8aa8280a63 Level: Make spawn protection always active regardless of op count (#2290)
I don't care if this matches PC behaviour or not. bugs.mojang.com is full of bug reports about this. Just search for "minecraft spawn protection not working" and you'll see what I mean.

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

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

This commit reverses the stupidity done in eb0525e892.
2018-07-12 17:25:05 +01:00
6a637d9099 update pthreads version for travis 2018-07-12 17:23:52 +01:00
7a164a8254 PluginManager: Allow @ignoreCancelled annotation on event handlers to not have parameters (#2294) 2018-07-12 17:12:14 +01:00
4a5ff32d2e hacks for NPC and floating text
I didn't think mojang could break this fucking game any worse
2018-07-11 19:45:48 +01:00
066c990301 Merge branch 'release/3.0' into release/3.1 2018-07-11 10:21:16 +01:00
06b80a9536 Level: Make getSafeSpawn() account for non-generated chunks
fixes #2295

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

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

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

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


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

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

fixed @notHandler and such not being detected when CRLF is used
2018-07-10 12:46:20 +01:00
0c350f2f57 Add quitMessage parameter to Player::kick() 2018-07-09 18:40:30 +01:00
bfcef2ab6b Add setReason() method to PlayerKickEvent 2018-07-09 18:36:19 +01:00
2994d0f3ae Merge branch 'release/3.0' into release/3.1 2018-07-09 10:06:28 +01:00
2d454ae56f PluginManager: fixed bug in YML commands permission type checking 2018-07-08 16:19:46 +01:00
066c9d4fd4 PluginManager: simplify isPluginEnabled() 2018-07-08 16:16:39 +01:00
23829952c3 PermissibleBase: removed nonsensical code
it's not possible for this to be null, unless a child class doesn't call the constructor, and anything could break in that case anyway.
2018-07-08 13:04:51 +01:00
57cc0ebe75 Merge branch 'release/3.0' into release/3.1 2018-07-08 12:17:06 +01:00
7ee98ff139 Config: fixed whitespace between key and = being invalid
it tolerates whitespace everywhere except here already ^.^
2018-07-08 11:54:06 +01:00
f1cab91ac9 Config: fixed interpreting invalid keys as empty strings
these should just be ignored completely.
2018-07-08 11:50:17 +01:00
7554d9a370 Empty merge 2018-07-07 19:22:30 +01:00
e0bc9c5e96 back to dev 2018-07-07 19:20:55 +01:00
32574118ea Implemented Mending enchantment (#2257) 2018-07-06 13:28:33 +01:00
5a3135659b Merge branch 'release/3.0' into release/3.1 2018-07-06 13:12:13 +01:00
70caa00266 disable dev flag for release 2018-07-06 12:59:02 +01:00
ee7c838040 LoginPacket: barf on finding extraData multiple times
this fixes a potential exploit where clients could append JWTs signed with their own keys to the end of the chain containing fake XUID/UUID/username which would then overwrite the legitimate ones in earlier links.
This stems from the fact that the final link of the vanilla chain contains the client's own pubkey, so the client is able to append its own data to the end of the chain.
2018-07-06 12:54:43 +01:00
34e9e93210 PluginBase: fixed crashing on getConfig() when data dir doesn't exist
I considered making this instead save the default config instead of creating an empty config file, but that would be (albeit minor) a behavioural change which therefore belongs in 3.1.
2018-07-05 19:59:08 +01:00
b90d7d1839 Merge branch 'release/3.0' into release/3.1 2018-07-05 17:43:11 +01:00
5dbb0d177e Fixed double chest inventory desync issues, closes #2261 (#2279)
chest pairing really needs rewriting... this code really sucks
2018-07-05 17:42:30 +01:00
670b940837 PocketMine.php: clean up on platform dependency checks 2018-07-05 17:32:13 +01:00
6cad7be3ef Merge branch 'release/3.0' into release/3.1 2018-07-05 12:11:12 +01:00
28a72a93b4 Chunk: Use an SplFixedArray for heightmap
this goes on 3.1 because it changes the behaviour of chunk cloning, which might possibly break some plugins, and this isn't a bug fix.

This should see no change in behaviour other than a minor performance improvement and slight reduction in memory usage.
2018-07-05 11:58:20 +01:00
58f0ad3e3e Command: remove unnecessary getPermission() calls 2018-07-05 10:38:31 +01:00
0df3585c81 TellCommand: remove useless strtolower() and temp variable 2018-07-05 09:12:21 +01:00
697723b551 DoubleChestInventory: remove redundant clear() override
this calls setItem() which deals with the necessary logic anyway.
2018-07-04 20:06:42 +01:00
5926d80525 DoubleChestInventory: fixed wrong logic for setting items into the right-hand side 2018-07-04 20:04:40 +01:00
0f0d12bebc Merge branch 'release/3.0' into release/3.1 2018-07-02 16:58:15 +01:00
dfc11abf2d Level: fixed sendBlocks() documentation 2018-07-02 16:53:48 +01:00
17eef9f902 Level: stricten type checks on sendBlocks()
because people are morons
2018-07-02 16:53:00 +01:00
a57ec1b1ba Living: fixed death animation not being played when kill() is used
this fixes players having a random delayed despawn when using /kill on themselves
2018-06-29 16:49:40 +01:00
905259a4e1 Fixed not being able to place blocks inside dead players
closes #2265
2018-06-29 16:38:35 +01:00
b04319a4ab Merge branch 'release/3.0' into release/3.1 2018-06-29 12:30:52 +01:00
ca6930006c back to dev 2018-06-29 12:30:08 +01:00
33eeeb856e disable dev flag 2018-06-29 12:21:56 +01:00
0afbf6c547 Merge branch 'release/3.0' into release/3.1 2018-06-29 12:19:25 +01:00
c43ce5c8fa RCONInstance: apply stfu operator 2018-06-29 12:16:17 +01:00
57cfe9fd43 Level: fixed logic for sending changed blocks to players
If there is an empty list of blocks in the changedBlocks array for a chunk, that means that blocks changed the normal way and then were later set the direct way in the same tick. This means that no action needs to be taken on these chunks.
2018-06-29 11:10:31 +01:00
d8824e7ee1 Level: discard changed blocks on chunk replace
this could cause issues when plugins replace chunks when blocks in the chunk have been changed on the same tick.
2018-06-29 11:06:33 +01:00
3455d0f3b9 Level: cleaned up some nonsensical code in setChunk() 2018-06-29 10:58:31 +01:00
ec2cca04a7 Merge branch 'release/3.0' into release/3.1 2018-06-24 17:34:37 +01:00
6b2250cbce RCONInstance: terminate session on ECONNRESET errors 2018-06-24 17:32:51 +01:00
8dae497610 back to dev 2018-06-24 17:32:51 +01:00
cade15e2dd disable dev flag for release 2018-06-24 16:34:19 +01:00
272b76d24c fix Punch mess 2018-06-24 13:43:52 +01:00
8c672cb7c8 Implemented Sharpness, Fire Aspect and Knockback enchantments 2018-06-24 12:13:54 +01:00
4d9368f205 Merge branch 'release/3.0' into release/3.1 2018-06-24 12:07:45 +01:00
97c267c70c Implemented Punch enchantment 2018-06-23 17:40:01 +01:00
85a3c0e7dc Implemented Flame enchantment 2018-06-23 17:11:20 +01:00
d3e54db146 ExperienceOrb: stop tracking targets if they die while being tracked 2018-06-23 16:41:21 +01:00
0081e30a89 Living: fix knockback condition, take 2
onGround doesn't necessarily reflect 0 motion, because something else could change the motion prior to the onGround flag getting updated - for example 2 knockbacks in a row.
2018-06-23 14:30:26 +01:00
2f70a1eefb Implemented Thorns enchantment (#2258)
This implementation is rough and can probably be improved to make it extendable, but this works for now and can be improved later.
2018-06-23 13:36:58 +01:00
7ba6e92b6c Merge branch 'release/3.0' into release/3.1 2018-06-23 13:04:05 +01:00
76174f1920 Explosion: avoid leaving arrows stuck in nonexistent blocks 2018-06-23 13:03:46 +01:00
47c862bc38 Projectile: check for blockhit change on nearby blockupdate 2018-06-23 12:57:13 +01:00
860c20109b TNT can now be ignited by burning arrows 2018-06-23 12:54:21 +01:00
1c0b49343c Implemented Infinity enchantment (#2259) 2018-06-23 11:44:35 +01:00
814a949580 Implemented Power enchantment 2018-06-23 11:39:39 +01:00
b393f5f17e Projectile: ensure that damage multiplier gets saved and restored 2018-06-23 10:41:04 +01:00
f1970492c1 Projectile: added API to modify projectile base damage multiplier
This adds two new methods:
- Projectile->getBaseDamage()
- Projectile->setBaseDamage()
2018-06-23 10:38:58 +01:00
dd6b5902a6 EmeraldOre: fixed not dropping XP on break 2018-06-22 21:35:58 +01:00
87852f2fe1 EmeraldOre: remove excess indentation 2018-06-22 21:31:22 +01:00
056d24c67d Add MUTTON as an ID constant
fixes crashdump #518862 - Unable to resolve "minecraft:mutton" to a valid item

PC refers to these as just mutton, but PE calls them muttonraw
2018-06-22 19:39:18 +01:00
4c9ca53b32 Merge branch 'release/3.0' into release/3.1 2018-06-22 17:47:33 +01:00
484d34fe04 Living: Reset attack cooldown before applying post damage effects
this fixes things causing damage during post-damage calls coming back and being able to do even more damage
2018-06-22 17:47:11 +01:00
6c6630d845 Player: avoid doing some post-melee attack actions if attacking killed the attacker
This can happen when an attacker attacks a victim wearing thorns armour while having low health, which prior to this commit would cause the tool to be duplicated.
2018-06-22 17:17:40 +01:00
a5a236084f Living: don't applyPostDamageEffects() for dead mobs
this has already been seen to cause duplication bugs when thorns is used. Anything else that modifies inventory during applyPostDamageEffects() when the mob is possibly dead will also cause duplication issues.
2018-06-22 14:31:48 +01:00
390db976e5 Arrow: allow controlling pickup mode (like PC)
This allows controlling how arrows are picked up:
- by anything
- by only creative players
- by nothing

This adds new API methods to Arrow:
- getPickupMode()
- setPickupMode()

This adds new public constants to Arrow:
- PICKUP_NONE
- PICKUP_ANY
- PICKUP_CREATIVE
2018-06-22 13:40:32 +01:00
98ac534820 bump version 2018-06-22 13:22:11 +01:00
641a5a5e23 fixed damaged anvils dropping the wrong items
they changed this in 1.2.13 to use regular masks instead of bitshifts. The item was fixed, but not the block.
2018-06-22 09:57:39 +01:00
ebacb8525f SignPost: fixed possible field read on null 2018-06-22 09:12:48 +01:00
579ab5866b Versions again 2018-06-22 09:12:48 +01:00
56b04fa0bb disable dev flag 2018-06-21 12:55:36 +01:00
95787c2be9 TaskScheduler: fixed tasks not getting destroyed when all tasks in scheduler are cancelled before next heartbeat
they got removed from the tasks array, but not from the queue (for performance reasons). The queue gets cleaned up by the heartbeat, but it was checking if there were things in the main array, not in the queue.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1
.gitignore vendored
View File

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

6
.gitmodules vendored
View File

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

View File

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

View File

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

View File

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

View File

@ -6,8 +6,11 @@
"license": "LGPL-3.0",
"require": {
"php": ">=7.2.0",
"php-64bit": "*",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-ctype": "*",
"ext-date": "*",
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
@ -21,17 +24,23 @@
"ext-yaml": ">=2.0.0",
"ext-zip": "*",
"ext-zlib": ">=1.2.11",
"pocketmine/raklib": "0.11.0",
"pocketmine/spl": "0.3.0",
"pocketmine/binaryutils": "0.0.1",
"pocketmine/nbt": "0.1.0",
"pocketmine/math": "0.1.0"
"pocketmine/raklib": "^0.12.0",
"pocketmine/spl": "^0.3.0",
"pocketmine/binaryutils": "^0.1.0",
"pocketmine/nbt": "^0.2.1",
"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"
}
]
}

125
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": "3536995c56bfc3dbd6ccc0994e88a636",
"packages": [
{
"name": "pocketmine/binaryutils",
"version": "0.0.1",
"version": "0.1.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BinaryUtils.git",
"reference": "03e6851f814aba96487ec64181a6ae948edd9f7a"
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/03e6851f814aba96487ec64181a6ae948edd9f7a",
"reference": "03e6851f814aba96487ec64181a6ae948edd9f7a",
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/54efeb978be0ff9335022729fe63c1e2077bf1be",
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be",
"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-08-26T18:11:05+00:00"
},
{
"name": "pocketmine/math",
"version": "0.1.0",
"version": "0.2.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Math.git",
"reference": "1df74f0352309a9c1e6728fa416a3f0493d07b16"
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Math/zipball/1df74f0352309a9c1e6728fa416a3f0493d07b16",
"reference": "1df74f0352309a9c1e6728fa416a3f0493d07b16",
"url": "https://api.github.com/repos/pmmp/Math/zipball/ee299f5c9c444ca526c9c691b920f321458cf0b6",
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6",
"shasum": ""
},
"require": {
"php": ">=7.2.0"
"php": ">=7.2.0",
"php-64bit": "*"
},
"type": "library",
"autoload": {
@ -67,28 +69,29 @@
],
"description": "PHP library containing math related code used in PocketMine-MP",
"support": {
"source": "https://github.com/pmmp/Math/tree/master",
"source": "https://github.com/pmmp/Math/tree/0.2.1",
"issues": "https://github.com/pmmp/Math/issues"
},
"time": "2018-03-18T18:01:56+00:00"
"time": "2018-08-15T15:43:27+00:00"
},
{
"name": "pocketmine/nbt",
"version": "0.1.0",
"version": "0.2.2",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "d79f8615442887bb45cfacdb52e1e6eb47c38fd7"
"reference": "474f0cf0a47656d0122b4f3f71302e694ed6977b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/d79f8615442887bb45cfacdb52e1e6eb47c38fd7",
"reference": "d79f8615442887bb45cfacdb52e1e6eb47c38fd7",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/474f0cf0a47656d0122b4f3f71302e694ed6977b",
"reference": "474f0cf0a47656d0122b4f3f71302e694ed6977b",
"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.2",
"issues": "https://github.com/pmmp/NBT/issues"
},
"time": "2018-04-13T18:43:03+00:00"
"time": "2018-10-12T08:26:44+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.2",
"source": {
"type": "git",
"url": "https://github.com/pmmp/SPL.git",
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/SPL/zipball/7fd53857cd000491ba69e8db865792a024dd2c49",
"reference": "7fd53857cd000491ba69e8db865792a024dd2c49",
"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-08-12T15:17:39+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{
@ -210,11 +212,11 @@ class CrashDump{
$this->addLine("Code:");
$this->data["code"] = [];
if($this->server->getProperty("auto-report.send-code", true) !== false){
if($this->server->getProperty("auto-report.send-code", true) !== false and file_exists($error["fullFile"])){
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10; ++$l){
$this->addLine("[" . ($l + 1) . "] " . @$file[$l]);
$this->data["code"][$l + 1] = @$file[$l];
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
$this->data["code"][$l + 1] = $file[$l];
}
}
@ -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,12 @@ 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();
if(($w = $pool->shutdownUnusedWorkers()) > 0){
$this->server->getLogger()->debug("Shut down $w idle async pool workers");
}
foreach($pool->getRunningWorkers() as $i){
$pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
}
}
@ -264,13 +266,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 +280,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 +332,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 +360,7 @@ class MemoryManager{
}
file_put_contents($outputFolder . "/globalVariables.js", json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
MainLogger::getLogger()->info("[Dump] Wrote $globalCount global variables");
$logger->info("[Dump] Wrote $globalCount global variables");
}
self::continueDump($startingObject, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
@ -394,15 +397,22 @@ class MemoryManager{
$info["implements"] = implode(", ", $reflection->getInterfaceNames());
}
foreach($reflection->getProperties() as $property){
if($property->isStatic()){
continue;
}
for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
foreach($reflection->getProperties() as $property){
if($property->isStatic()){
continue;
}
if(!$property->isPublic()){
$property->setAccessible(true);
$name = $property->getName();
if($reflection !== $original and !$property->isPublic()){
$name = $reflection->getName() . ":" . $name;
}
if(!$property->isPublic()){
$property->setAccessible(true);
}
self::continueDump($property->getValue($object), $info["properties"][$name], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
self::continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
fwrite($obData, "$hash@$className: " . json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
@ -411,7 +421,7 @@ class MemoryManager{
}while($continue);
MainLogger::getLogger()->info("[Dump] Wrote " . count($objects) . " objects");
$logger->info("[Dump] Wrote " . count($objects) . " objects");
fclose($obData);
@ -421,7 +431,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();
@ -463,7 +473,7 @@ class MemoryManager{
self::continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize);
}
}elseif(is_string($from)){
$data = "(string) len(". strlen($from) .") " . substr(Utils::printable($from), 0, $maxStringSize);
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
}elseif(is_resource($from)){
$data = "(resource) " . print_r($from, true);
}else{

View File

@ -44,7 +44,7 @@ class OfflinePlayer implements IPlayer, Metadatable{
public function __construct(Server $server, string $name){
$this->server = $server;
$this->name = $name;
if(file_exists($this->server->getDataPath() . "players/" . strtolower($this->getName()) . ".dat")){
if(file_exists($this->server->getDataPath() . "players/" . strtolower($this->name) . ".dat")){
$this->namedtag = $this->server->getOfflinePlayerData($this->name);
}else{
$this->namedtag = null;
@ -64,7 +64,7 @@ class OfflinePlayer implements IPlayer, Metadatable{
}
public function isOp() : bool{
return $this->server->isOp(strtolower($this->getName()));
return $this->server->isOp($this->name);
}
public function setOp(bool $value){
@ -73,38 +73,38 @@ class OfflinePlayer implements IPlayer, Metadatable{
}
if($value){
$this->server->addOp(strtolower($this->getName()));
$this->server->addOp($this->name);
}else{
$this->server->removeOp(strtolower($this->getName()));
$this->server->removeOp($this->name);
}
}
public function isBanned() : bool{
return $this->server->getNameBans()->isBanned(strtolower($this->getName()));
return $this->server->getNameBans()->isBanned($this->name);
}
public function setBanned(bool $value){
if($value){
$this->server->getNameBans()->addBan($this->getName(), null, null, null);
$this->server->getNameBans()->addBan($this->name, null, null, null);
}else{
$this->server->getNameBans()->remove($this->getName());
$this->server->getNameBans()->remove($this->name);
}
}
public function isWhitelisted() : bool{
return $this->server->isWhitelisted(strtolower($this->getName()));
return $this->server->isWhitelisted($this->name);
}
public function setWhitelisted(bool $value){
if($value){
$this->server->addWhitelist(strtolower($this->getName()));
$this->server->addWhitelist($this->name);
}else{
$this->server->removeWhitelist(strtolower($this->getName()));
$this->server->removeWhitelist($this->name);
}
}
public function getPlayer(){
return $this->server->getPlayerExact($this->getName());
return $this->server->getPlayerExact($this->name);
}
public function getFirstPlayed(){
@ -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

@ -32,11 +32,9 @@ use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\Living;
use pocketmine\entity\object\ItemEntity;
use pocketmine\entity\projectile\Arrow;
use pocketmine\entity\Skin;
use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageByEntityEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\inventory\InventoryCloseEvent;
@ -68,6 +66,8 @@ use pocketmine\event\player\PlayerToggleSneakEvent;
use pocketmine\event\player\PlayerToggleSprintEvent;
use pocketmine\event\player\PlayerTransferEvent;
use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\form\Form;
use pocketmine\form\FormValidationException;
use pocketmine\inventory\CraftingGrid;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\PlayerCursorInventory;
@ -76,6 +76,9 @@ 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\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\MeleeWeaponEnchantment;
use pocketmine\item\Item;
use pocketmine\item\WritableBook;
use pocketmine\item\WrittenBook;
@ -86,7 +89,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;
@ -116,6 +118,7 @@ use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\LoginPacket;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
@ -146,6 +149,7 @@ use pocketmine\network\SourceInterface;
use pocketmine\permission\PermissibleBase;
use pocketmine\permission\PermissionAttachment;
use pocketmine\permission\PermissionAttachmentInfo;
use pocketmine\permission\PermissionManager;
use pocketmine\plugin\Plugin;
use pocketmine\resourcepacks\ResourcePack;
use pocketmine\tile\ItemFrame;
@ -169,6 +173,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* Checks a supplied username and checks it is valid.
*
* @param string $name
*
* @return bool
@ -193,9 +198,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
*/
protected $sessionAdapter;
/** @var int */
protected $protocol = -1;
/** @var string */
protected $ip;
/** @var int */
@ -298,7 +300,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
@ -322,6 +324,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var int[] ID => ticks map */
protected $usedItemsCooldown = [];
/** @var int */
protected $formIdCounter = 0;
/** @var Form[] */
protected $forms = [];
/**
* @return TranslationContainer|string
*/
@ -401,7 +408,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
*
* @return UUID|null
*/
public function getUniqueId(){
public function getUniqueId() : ?UUID{
return parent::getUniqueId();
}
@ -459,7 +466,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* @param Player $player
*/
public function spawnTo(Player $player){
public function spawnTo(Player $player) : void{
if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevel() === $this->level and $player->canSee($this) and !$this->isSpectator()){
parent::spawnTo($player);
}
@ -535,10 +542,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function canBeCollidedWith() : bool{
return !$this->isSpectator();
return !$this->isSpectator() and parent::canBeCollidedWith();
}
public function resetFallDistance(){
public function resetFallDistance() : void{
parent::resetFallDistance();
if($this->inAirTicks !== 0){
$this->startAirTicks = 5;
@ -621,7 +628,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* @param Plugin $plugin
* @param string $name
* @param bool $value
* @param bool $value
*
* @return PermissionAttachment
*/
@ -637,8 +644,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function recalculatePermissions(){
$this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
$this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
$permManager = PermissionManager::getInstance();
$permManager->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
$permManager->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
if($this->perm === null){
return;
@ -647,10 +655,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->perm->recalculatePermissions();
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
}
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
}
if($this->spawned){
@ -666,8 +674,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function sendCommandData(){
//TODO: this needs fixing
$pk = new AvailableCommandsPacket();
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
if(isset($pk->commandData[$command->getName()]) or $command->getName() === "help"){
@ -688,7 +694,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$aliases = $command->getAliases();
if(!empty($aliases)){
if(!\in_array($data->commandName, $aliases, true)){
if(!in_array($data->commandName, $aliases, true)){
//work around a client bug which makes the original name not show when aliases are used
$aliases[] = $data->commandName;
}
@ -812,7 +818,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
*
* If null is given, will additionally send the skin to the player itself as well as its viewers.
*/
public function sendSkin(array $targets = null) : void{
public function sendSkin(?array $targets = null) : void{
parent::sendSkin($targets ?? $this->server->getOnlinePlayers());
}
@ -923,12 +929,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);
@ -1017,10 +1026,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN);
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
}
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
}
$this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this,
@ -1061,9 +1070,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->dataPacket($pk);
}
protected function orderChunks(){
protected function orderChunks() : void{
if(!$this->isConnected() or $this->viewDistance === -1){
return false;
return;
}
Timings::$playerChunkOrderTimer->startTiming();
@ -1148,8 +1157,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->loadQueue = $newOrder;
Timings::$playerChunkOrderTimer->stopTiming();
return true;
}
/**
@ -1169,7 +1176,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @return bool
*/
public function hasValidSpawnPosition() : bool{
return $this->spawnPosition instanceof WeakPosition and $this->spawnPosition->isValid();
return $this->spawnPosition !== null and $this->spawnPosition->isValid();
}
/**
@ -1184,7 +1191,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();
@ -1320,6 +1327,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* TODO: remove this when Spectator Mode gets added properly to MCPE
*
* @param int $gamemode
*
* @return int
*/
public static function getClientFriendlyGamemode(int $gamemode) : int{
@ -1487,23 +1495,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return 0;
}
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz){
if(!$this->onGround or $movY != 0){
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
$this->onGround = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
$this->isCollided = $this->onGround;
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
public function canBeMovedByCurrents() : bool{
return false; //currently has no server-side movement
}
protected function checkNearEntities(int $tickDiff){
foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){
protected function checkNearEntities(){
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(1, 0.5, 1), $this) as $entity){
$entity->scheduleUpdate();
if(!$entity->isAlive() or $entity->isFlaggedForDespawn()){
@ -1637,17 +1642,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->newPosition = null;
}
public function jump(){
public function jump() : void{
$this->server->getPluginManager()->callEvent(new PlayerJumpEvent($this));
parent::jump();
}
public function setMotion(Vector3 $mot){
if(parent::setMotion($mot)){
public function setMotion(Vector3 $motion) : bool{
if(parent::setMotion($motion)){
$this->broadcastMotion();
if($this->motionY > 0){
$this->startAirTicks = (-log($this->gravity / ($this->gravity + $this->drag * $this->motionY)) / $this->drag) * 2 + 5;
if($this->motion->y > 0){
$this->startAirTicks = (-log($this->gravity / ($this->gravity + $this->drag * $this->motion->y)) / $this->drag) * 2 + 5;
}
return true;
@ -1655,11 +1660,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
protected function updateMovement(bool $teleport = false){
protected function updateMovement(bool $teleport = false) : void{
}
protected function tryChangeMovement(){
protected function tryChangeMovement() : void{
}
@ -1702,14 +1707,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($this->spawned){
$this->processMovement($tickDiff);
$this->motionX = $this->motionY = $this->motionZ = 0; //TODO: HACK! (Fixes player knockback being messed up)
$this->motion->x = $this->motion->y = $this->motion->z = 0; //TODO: HACK! (Fixes player knockback being messed up)
Timings::$timerEntityBaseTick->startTiming();
$this->entityBaseTick($tickDiff);
Timings::$timerEntityBaseTick->stopTiming();
if(!$this->isSpectator() and $this->isAlive()){
$this->checkNearEntities($tickDiff);
Timings::$playerCheckNearEntitiesTimer->startTiming();
$this->checkNearEntities();
Timings::$playerCheckNearEntitiesTimer->stopTiming();
if($this->speed !== null){
if($this->onGround){
@ -1744,7 +1751,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
public function doFoodTick(int $tickDiff = 1){
protected function doFoodTick(int $tickDiff = 1) : void{
if($this->isSurvival()){
parent::doFoodTick($tickDiff);
}
@ -1827,11 +1834,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();
}
@ -1841,8 +1848,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
$this->protocol = $packet->protocol;
if($packet->protocol !== ProtocolInfo::CURRENT_PROTOCOL){
if($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL){
$this->sendPlayStatus(PlayStatusPacket::LOGIN_FAILED_CLIENT, true);
@ -1914,7 +1919,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);
}
@ -1925,7 +1930,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
public function sendPlayStatus(int $status, bool $immediate = false){
$pk = new PlayStatusPacket();
$pk->status = $status;
$pk->protocol = $this->protocol;
$this->sendDataPacket($pk, false, $immediate);
}
@ -1946,7 +1950,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$xuid = "";
}
if($xuid === ""){
if($xuid === "" or !is_string($xuid)){
if($signedByMojang){
$this->server->getLogger()->error($this->getName() . " should have an XUID, but none found");
}
@ -1955,7 +1959,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return;
}
$this->server->getLogger()->debug($this->getName() . " is NOT logged into to Xbox Live");
$this->server->getLogger()->debug($this->getName() . " is NOT logged into Xbox Live");
}else{
$this->server->getLogger()->debug($this->getName() . " is logged into Xbox Live");
$this->xuid = $xuid;
@ -1994,7 +1998,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),
@ -2033,7 +2037,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case ResourcePackClientResponsePacket::STATUS_SEND_PACKS:
$manager = $this->server->getResourcePackManager();
foreach($packet->packIds as $uuid){
$pack = $manager->getPackById($uuid);
$pack = $manager->getPackById(substr($uuid, 0, strpos($uuid, "_"))); //dirty hack for mojang's dirty hack for versions
if(!($pack instanceof ResourcePack)){
//Client requested a resource pack but we don't have it available on the server
$this->close("", "disconnectionScreen.resourcePack", true);
@ -2084,9 +2088,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->hasValidSpawnPosition()){
if(($level = $this->server->getLevelByName($this->namedtag->getString("SpawnLevel", ""))) instanceof Level){
$this->spawnPosition = new WeakPosition($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
$this->spawnPosition = new Position($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
}else{
$this->spawnPosition = WeakPosition::fromObject($this->level->getSafeSpawn());
$this->spawnPosition = $this->level->getSafeSpawn();
}
}
@ -2145,8 +2149,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendPotionEffects($this);
$this->sendData($this);
$this->inventory->sendContents($this);
$this->armorInventory->sendContents($this);
$this->sendAllInventories();
$this->inventory->sendCreativeContents();
$this->inventory->sendHeldItem($this);
$this->dataPacket($this->server->getCraftingManager()->getCraftingDataPacket());
@ -2168,7 +2171,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$message = TextFormat::clean($message, $this->removeFormat);
foreach(explode("\n", $message) as $messagePart){
@ -2243,7 +2246,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->spawned or !$this->isAlive()){
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
switch($packet->event){
case EntityEventPacket::EATING_ITEM:
@ -2265,6 +2268,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* Don't expect much from this handler. Most of it is roughly hacked and duct-taped together.
*
* @param InventoryTransactionPacket $packet
*
* @return bool
*/
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
@ -2285,7 +2289,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($action !== null){
$actions[] = $action;
}
}catch(\Throwable $e){
}catch(\Exception $e){
$this->server->getLogger()->debug("Unhandled inventory action from " . $this->getName() . ": " . $e->getMessage());
$this->sendAllInventories();
return false;
@ -2394,7 +2398,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
$this->resetCraftingGridType();
$this->doCloseInventory();
$item = $this->inventory->getItemInHand();
$oldItem = clone $item;
@ -2502,24 +2506,37 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
$ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints());
$meleeEnchantmentDamage = 0;
/** @var EnchantmentInstance[] $meleeEnchantments */
$meleeEnchantments = [];
foreach($heldItem->getEnchantments() as $enchantment){
$type = $enchantment->getType();
if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){
$meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel());
$meleeEnchantments[] = $enchantment;
}
}
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
if($cancelled){
$ev->setCancelled();
}
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isInsideOfWater()){
$ev->setDamage($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
}
$target->attack($ev);
if($ev->isCancelled()){
if($heldItem->isTool() and $this->isSurvival()){
if($heldItem instanceof Durable and $this->isSurvival()){
$this->inventory->sendContents($this);
}
return true;
}
if($ev->getDamage(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
$pk = new AnimatePacket();
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
$pk->entityRuntimeId = $target->getId();
@ -2529,8 +2546,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
if($this->isSurvival()){
if($heldItem->useOn($target)){
foreach($meleeEnchantments as $enchantment){
$type = $enchantment->getType();
assert($type instanceof MeleeWeaponEnchantment);
$type->onPostAttack($this, $target, $enchantment->getLevel());
}
if($this->isAlive()){
//reactive damage like thorns might cause us to be killed by attacking another mob, which
//would mean we'd already have dropped the inventory by the time we reached here
if($heldItem->onAttackEntity($target) and $this->isSurvival()){ //always fire the hook, even if we are survival
$this->inventory->setItemInHand($heldItem);
}
@ -2633,7 +2658,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$target = $this->level->getEntity($packet->target);
if($target === null){
@ -2699,7 +2724,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$target = $this->level->getBlock($pos);
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK);
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $packet->face, PlayerInteractEvent::LEFT_CLICK_BLOCK);
if($this->level->checkSpawnProtection($this, $target)){
$ev->setCancelled();
}
@ -2747,47 +2772,23 @@ 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:
break; //TODO
case PlayerActionPacket::ACTION_CONTINUE_BREAK:
$block = $this->level->getBlock($pos);
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, BlockFactory::toStaticRuntimeId($block->getId(), $block->getDamage()) | ($packet->face << 24));
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, $block->getRuntimeId() | ($packet->face << 24));
//TODO: destroy-progress level event
break;
case PlayerActionPacket::ACTION_START_SWIMMING:
@ -2805,6 +2806,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;
@ -2827,6 +2848,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* Drops an item on the ground in front of the player. Returns if the item drop was successful.
*
* @param Item $item
*
* @return bool if the item was dropped or if the item was null
*/
public function dropItem(Item $item) : bool{
@ -2851,7 +2873,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
if(isset($this->windowIndex[$packet->windowId])){
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
@ -2901,7 +2923,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->spawned or !$this->isAlive()){
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$pos = new Vector3($packet->x, $packet->y, $packet->z);
if($pos->distanceSquared($this) > 10000 or $this->level->checkSpawnProtection($this, $pos)){
@ -3126,7 +3148,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* Transfers a player to another server.
*
* @param string $address The IP address or hostname of the destination server
* @param int $port The destination port, defaults to 19132
* @param int $port The destination port, defaults to 19132
* @param string $message Message to show in the console when closing the player
*
* @return bool if transfer was successful.
@ -3150,14 +3172,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* Kicks a player from the server
*
* @param string $reason
* @param bool $isAdmin
* @param string $reason
* @param bool $isAdmin
* @param TextContainer|string $quitMessage
*
* @return bool
*/
public function kick(string $reason = "", bool $isAdmin = true) : bool{
$this->server->getPluginManager()->callEvent($ev = new PlayerKickEvent($this, $reason, $this->getLeaveMessage()));
public function kick(string $reason = "", bool $isAdmin = true, $quitMessage = null) : bool{
$this->server->getPluginManager()->callEvent($ev = new PlayerKickEvent($this, $reason, $quitMessage ?? $this->getLeaveMessage()));
if(!$ev->isCancelled()){
$reason = $ev->getReason();
$message = $reason;
if($isAdmin){
if(!$this->isBanned()){
@ -3251,7 +3275,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* Internal function used for sending titles.
*
* @param string $title
* @param int $type
* @param int $type
*/
protected function sendTitleText(string $title, int $type){
$pk = new SetTitlePacket();
@ -3335,6 +3359,48 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->dataPacket($pk);
}
/**
* Sends a Form to the player, or queue to send it if a form is already open.
*
* @param Form $form
*/
public function sendForm(Form $form) : void{
$id = $this->formIdCounter++;
$pk = new ModalFormRequestPacket();
$pk->formId = $id;
$pk->formData = json_encode($form);
if($pk->formData === false){
throw new \InvalidArgumentException("Failed to encode form JSON: " . json_last_error_msg());
}
if($this->dataPacket($pk)){
$this->forms[$id] = $form;
}
}
/**
* @param int $formId
* @param mixed $responseData
*
* @return bool
*/
public function onFormSubmit(int $formId, $responseData) : bool{
if(!isset($this->forms[$formId])){
$this->server->getLogger()->debug("Got unexpected response for form $formId");
return false;
}
try{
$this->forms[$formId]->handleResponse($this, $responseData);
}catch(FormValidationException $e){
$this->server->getLogger()->critical("Failed to validate form " . get_class($this->forms[$formId]) . ": " . $e->getMessage());
$this->server->getLogger()->logException($e);
}finally{
unset($this->forms[$formId]);
}
return true;
}
/**
* Note for plugin developers: use kick() with the isAdmin
* flag set to kick without the "Kicked by admin" part instead of this method.
@ -3343,7 +3409,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{
@ -3355,8 +3421,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->interface->close($this, $notify ? $reason : "");
$this->sessionAdapter = null;
$this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
$this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
$this->stopSleep();
@ -3449,11 +3515,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/**
* Handles player data saving
*
* @param bool $async
*
* @throws \InvalidStateException if the player is closed
*/
public function save(bool $async = false){
public function save(){
if($this->closed){
throw new \InvalidStateException("Tried to save closed player");
}
@ -3490,11 +3554,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->namedtag->setLong("lastPlayed", (int) floor(microtime(true) * 1000));
if($this->username != "" and $this->namedtag instanceof CompoundTag){
$this->server->saveOfflinePlayerData($this->username, $this->namedtag, $async);
$this->server->saveOfflinePlayerData($this->username, $this->namedtag);
}
}
public function kill(){
public function kill() : void{
if(!$this->spawned){
return;
}
@ -3504,124 +3568,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendRespawnPacket($this->getSpawn());
}
protected function onDeath(){
$message = "death.attack.generic";
$params = [
$this->getDisplayName()
];
$cause = $this->getLastDamageCause();
switch($cause === null ? EntityDamageEvent::CAUSE_CUSTOM : $cause->getCause()){
case EntityDamageEvent::CAUSE_ENTITY_ATTACK:
if($cause instanceof EntityDamageByEntityEvent){
$e = $cause->getDamager();
if($e instanceof Player){
$message = "death.attack.player";
$params[] = $e->getDisplayName();
break;
}elseif($e instanceof Living){
$message = "death.attack.mob";
$params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName();
break;
}else{
$params[] = "Unknown";
}
}
break;
case EntityDamageEvent::CAUSE_PROJECTILE:
if($cause instanceof EntityDamageByEntityEvent){
$e = $cause->getDamager();
if($e instanceof Player){
$message = "death.attack.arrow";
$params[] = $e->getDisplayName();
}elseif($e instanceof Living){
$message = "death.attack.arrow";
$params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName();
break;
}else{
$params[] = "Unknown";
}
}
break;
case EntityDamageEvent::CAUSE_SUICIDE:
$message = "death.attack.generic";
break;
case EntityDamageEvent::CAUSE_VOID:
$message = "death.attack.outOfWorld";
break;
case EntityDamageEvent::CAUSE_FALL:
if($cause instanceof EntityDamageEvent){
if($cause->getFinalDamage() > 2){
$message = "death.fell.accident.generic";
break;
}
}
$message = "death.attack.fall";
break;
case EntityDamageEvent::CAUSE_SUFFOCATION:
$message = "death.attack.inWall";
break;
case EntityDamageEvent::CAUSE_LAVA:
$message = "death.attack.lava";
break;
case EntityDamageEvent::CAUSE_FIRE:
$message = "death.attack.onFire";
break;
case EntityDamageEvent::CAUSE_FIRE_TICK:
$message = "death.attack.inFire";
break;
case EntityDamageEvent::CAUSE_DROWNING:
$message = "death.attack.drown";
break;
case EntityDamageEvent::CAUSE_CONTACT:
if($cause instanceof EntityDamageByBlockEvent){
if($cause->getDamager()->getId() === Block::CACTUS){
$message = "death.attack.cactus";
}
}
break;
case EntityDamageEvent::CAUSE_BLOCK_EXPLOSION:
case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION:
if($cause instanceof EntityDamageByEntityEvent){
$e = $cause->getDamager();
if($e instanceof Player){
$message = "death.attack.explosion.player";
$params[] = $e->getDisplayName();
}elseif($e instanceof Living){
$message = "death.attack.explosion.player";
$params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName();
break;
}
}else{
$message = "death.attack.explosion";
}
break;
case EntityDamageEvent::CAUSE_MAGIC:
$message = "death.attack.magic";
break;
case EntityDamageEvent::CAUSE_CUSTOM:
break;
default:
break;
}
protected function onDeath() : void{
//Crafting grid must always be evacuated even if keep-inventory is true. This dumps the contents into the
//main inventory and drops the rest on the ground.
$this->resetCraftingGridType();
$this->doCloseInventory();
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params)));
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops()));
if(!$ev->getKeepInventory()){
foreach($ev->getDrops() as $item){
@ -3680,8 +3632,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->sendData($this->getViewers());
$this->sendSettings();
$this->inventory->sendContents($this);
$this->armorInventory->sendContents($this);
$this->sendAllInventories();
$this->spawnToAll();
$this->scheduleUpdate();
@ -3693,7 +3644,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_DAMAGE);
}
public function attack(EntityDamageEvent $source){
public function attack(EntityDamageEvent $source) : void{
if(!$this->isAlive()){
return;
}
@ -3710,11 +3661,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
parent::attack($source);
}
protected function doHitAnimation() : void{
parent::doHitAnimation();
if($this->spawned){
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION, null, [$this]);
public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{
if($this->spawned and $players === null){
$players = $this->getViewers();
$players[] = $this;
}
parent::broadcastEntityEvent($eventId, $eventData, $players);
}
public function getOffsetPosition(Vector3 $vector3) : Vector3{
@ -3802,15 +3754,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->craftingGrid = $grid;
}
public function resetCraftingGridType() : void{
$contents = $this->craftingGrid->getContents();
if(count($contents) > 0){
$drops = $this->inventory->addItem(...$contents);
foreach($drops as $drop){
$this->dropItem($drop);
}
public function doCloseInventory() : void{
/** @var Inventory[] $inventories */
$inventories = [$this->craftingGrid, $this->cursorInventory];
foreach($inventories as $inventory){
$contents = $inventory->getContents();
if(count($contents) > 0){
$drops = $this->inventory->addItem(...$contents);
foreach($drops as $drop){
$this->dropItem($drop);
}
$this->craftingGrid->clearAll();
$inventory->clearAll();
}
}
if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){

View File

@ -24,49 +24,6 @@ declare(strict_types=1);
namespace {
const INT32_MIN = -0x80000000;
const INT32_MAX = 0x7fffffff;
function safe_var_dump(){
static $cnt = 0;
foreach(func_get_args() as $var){
switch(true){
case is_array($var):
echo str_repeat(" ", $cnt) . "array(" . count($var) . ") {" . PHP_EOL;
foreach($var as $key => $value){
echo str_repeat(" ", $cnt + 1) . "[" . (is_int($key) ? $key : '"' . $key . '"') . "]=>" . PHP_EOL;
++$cnt;
safe_var_dump($value);
--$cnt;
}
echo str_repeat(" ", $cnt) . "}" . PHP_EOL;
break;
case is_int($var):
echo str_repeat(" ", $cnt) . "int(" . $var . ")" . PHP_EOL;
break;
case is_float($var):
echo str_repeat(" ", $cnt) . "float(" . $var . ")" . PHP_EOL;
break;
case is_bool($var):
echo str_repeat(" ", $cnt) . "bool(" . ($var === true ? "true" : "false") . ")" . PHP_EOL;
break;
case is_string($var):
echo str_repeat(" ", $cnt) . "string(" . strlen($var) . ") \"$var\"" . PHP_EOL;
break;
case is_resource($var):
echo str_repeat(" ", $cnt) . "resource() of type (" . get_resource_type($var) . ")" . PHP_EOL;
break;
case is_object($var):
echo str_repeat(" ", $cnt) . "object(" . get_class($var) . ")" . PHP_EOL;
break;
case is_null($var):
echo str_repeat(" ", $cnt) . "NULL" . PHP_EOL;
break;
}
}
}
function dummy(){
}
}
namespace pocketmine {
@ -76,13 +33,13 @@ namespace pocketmine {
use pocketmine\utils\Terminal;
use pocketmine\utils\Timezone;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
use pocketmine\wizard\SetupWizard;
use raklib\RakLib;
const NAME = "PocketMine-MP";
const VERSION = "1.7dev";
const API_VERSION = "3.0.0-ALPHA12";
const CODENAME = "[REDACTED]";
const BASE_VERSION = "3.3.4";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;
const MIN_PHP_VERSION = "7.2.0";
@ -96,118 +53,115 @@ namespace pocketmine {
* Enjoy it as much as I did writing it. I don't want to do it again.
*/
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.");
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.");
exit(1);
}
/* Dependencies check */
$errors = 0;
if(php_sapi_name() !== "cli"){
critical_error("You must run " . \pocketmine\NAME . " using the CLI.");
++$errors;
}
$extensions = [
"bcmath" => "BC Math",
"curl" => "cURL",
"json" => "JSON",
"mbstring" => "Multibyte String",
"openssl" => "OpenSSL",
"phar" => "Phar",
"pthreads" => "pthreads",
"sockets" => "Sockets",
"yaml" => "YAML",
"zip" => "Zip",
"zlib" => "Zlib"
];
foreach($extensions as $ext => $name){
if(!extension_loaded($ext)){
critical_error("Unable to find the $name ($ext) extension.");
++$errors;
}
}
if(extension_loaded("pthreads")){
$pthreads_version = phpversion("pthreads");
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.");
++$errors;
}
}
if(extension_loaded("leveldb")){
$leveldb_version = phpversion("leveldb");
if(version_compare($leveldb_version, "0.2.1") < 0){
critical_error("php-leveldb >= 0.2.1 is required, while you have $leveldb_version");
++$errors;
}
}
if(extension_loaded("pocketmine")){
critical_error("The native PocketMine extension is no longer supported.");
++$errors;
}
if($errors > 0){
critical_error("Please use the installer provided on the homepage, or recompile PHP again.");
/**
* @return string[]
*/
function check_platform_dependencies(){
if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
//If PHP version isn't high enough, anything below might break, so don't bother checking it.
return [
\pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . "."
];
}
$messages = [];
if(PHP_INT_SIZE < 8){
$messages[] = "Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
}
if(php_sapi_name() !== "cli"){
$messages[] = "You must run " . \pocketmine\NAME . " using the CLI.";
}
$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"
];
foreach($extensions as $ext => $name){
if(!extension_loaded($ext)){
$messages[] = "Unable to find the $name ($ext) extension.";
}
}
if(extension_loaded("pthreads")){
$pthreads_version = phpversion("pthreads");
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "3.1.7dev") < 0){
$messages[] = "pthreads >= 3.1.7dev is required, while you have $pthreads_version.";
}
}
if(extension_loaded("leveldb")){
$leveldb_version = phpversion("leveldb");
if(version_compare($leveldb_version, "0.2.1") < 0){
$messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
}
}
if(extension_loaded("pocketmine")){
$messages[] = "The native PocketMine extension is no longer supported.";
}
return $messages;
}
if(!empty($messages = check_platform_dependencies())){
echo PHP_EOL;
$binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
critical_error("Selected PHP binary ($binary) does not satisfy some requirements.");
foreach($messages as $m){
echo " - $m" . PHP_EOL;
}
critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
echo PHP_EOL;
exit(1);
}
unset($messages);
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{
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR);
}
define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
$opts = getopt("", ["bootstrap:"]);
if(isset($opts["bootstrap"])){
$bootstrap = realpath($opts["bootstrap"]) ?: $opts["bootstrap"];
}else{
$bootstrap = \pocketmine\PATH . 'vendor/autoload.php';
}
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
function composer_error_die($message){
critical_error($message);
if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{
critical_error("Composer autoloader not found at " . $bootstrap);
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 +181,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 +201,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 +209,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";

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -85,7 +85,7 @@ class Bed extends Transparent{
$this->getLevel()->setBlock($this, $this, false, false);
if(($other = $this->getOtherHalf()) !== null and !$other->isOccupied()){
if(($other = $this->getOtherHalf()) !== null and $other->isOccupied() !== $occupied){
$other->setOccupied($occupied);
}
}
@ -192,21 +192,25 @@ class Bed extends Transparent{
public function getDropsForCompatibleTool(Item $item) : array{
if($this->isHeadPart()){
$tile = $this->getLevel()->getTile($this);
if($tile instanceof TileBed){
return [
ItemFactory::get($this->getItemId(), $tile->getColor())
];
}else{
return [
ItemFactory::get($this->getItemId(), 14) //Red
];
}
return [$this->getItem()];
}
return [];
}
public function getPickedItem() : Item{
return $this->getItem();
}
private function getItem() : Item{
$tile = $this->getLevel()->getTile($this);
if($tile instanceof TileBed){
return ItemFactory::get($this->getItemId(), $tile->getColor());
}
return ItemFactory::get($this->getItemId(), 14); //Red
}
public function isAffectedBySilkTouch() : bool{
return false;
}

View File

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

View File

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

View File

@ -25,14 +25,11 @@ namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\level\Position;
use pocketmine\utils\MainLogger;
/**
* Manages block registration and instance creation
*/
class BlockFactory{
/** @var \SplFixedArray<Block> */
private static $list = null;
/** @var \SplFixedArray<Block> */
private static $fullList = null;
@ -65,7 +62,6 @@ class BlockFactory{
* this if you need to reset the block factory back to its original defaults for whatever reason.
*/
public static function init() : void{
self::$list = new \SplFixedArray(256);
self::$fullList = new \SplFixedArray(4096);
self::$light = new \SplFixedArray(256);
@ -327,17 +323,15 @@ 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"]);
}
public static function isInit() : bool{
return self::$fullList !== null;
}
/**
@ -360,8 +354,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 +364,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();
}
@ -423,13 +415,22 @@ class BlockFactory{
* Returns whether a specified block ID is already registered in the block factory.
*
* @param int $id
*
* @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 $k => $obj){
self::registerMapping($k, $obj["id"], $obj["data"]);
}
}
/**
* @internal
*
@ -439,19 +440,12 @@ class BlockFactory{
* @return int
*/
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
if($id === Block::AIR){
//TODO: HACK! (weird air blocks with non-zero damage values shouldn't turn into update! blocks)
$meta = 0;
}
$index = ($id << 4) | $meta;
if(!isset(self::$staticRuntimeIdMap[$index])){
self::registerMapping($rtId = ++self::$lastRuntimeId, $id, $meta);
MainLogger::getLogger()->error("ID $id meta $meta does not have a corresponding block static runtime ID, added a new unknown runtime ID ($rtId)");
return $rtId;
}
return self::$staticRuntimeIdMap[$index];
/*
* try id+meta first
* if not found, try id+0 (strip meta)
* if still not found, return update! block
*/
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
}
/**

View File

@ -23,12 +23,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\item\TieredTool;
class BrewingStand extends Transparent{
protected $id = self::BREWING_STAND_BLOCK;
protected $itemId = Item::BREWING_STAND;
public function __construct(int $meta = 0){
$this->meta = $meta;
}

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

@ -77,7 +77,7 @@ class Cactus extends Transparent{
}else{
for($side = 2; $side <= 5; ++$side){
$b = $this->getSide($side);
if(!$b->canBeFlowedInto()){
if($b->isSolid()){
$this->getLevel()->useBreakOn($this);
break;
}
@ -117,7 +117,7 @@ class Cactus extends Transparent{
$block1 = $this->getSide(Vector3::SIDE_SOUTH);
$block2 = $this->getSide(Vector3::SIDE_WEST);
$block3 = $this->getSide(Vector3::SIDE_EAST);
if($block0->isTransparent() and $block1->isTransparent() and $block2->isTransparent() and $block3->isTransparent()){
if(!$block0->isSolid() and !$block1->isSolid() and !$block2->isSolid() and !$block3->isSolid()){
$this->getLevel()->setBlock($this, $this, true);
return true;

View File

@ -35,6 +35,8 @@ class Cake extends Transparent implements FoodSource{
protected $id = self::CAKE_BLOCK;
protected $itemId = Item::CAKE;
public function __construct(int $meta = 0){
$this->meta = $meta;
}

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{
];
}
protected function getXpDropAmount() : int{
return mt_rand(0, 2);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -111,4 +111,8 @@ class Farmland extends Transparent{
public function isAffectedBySilkTouch() : bool{
return false;
}
public function getPickedItem() : Item{
return ItemFactory::get(Item::DIRT);
}
}

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

@ -50,7 +50,8 @@ class RedstoneOre extends Solid{
}
public function onActivate(Item $item, Player $player = null) : bool{
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
return false; //this shouldn't prevent block placement
}
public function onNearbyBlockChange() : void{
@ -70,4 +71,8 @@ class RedstoneOre extends Solid{
ItemFactory::get(Item::REDSTONE_DUST, 0, mt_rand(4, 5))
];
}
protected function getXpDropAmount() : int{
return mt_rand(1, 5);
}
}

View File

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

View File

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

View File

@ -90,7 +90,7 @@ class Sapling extends Flowable{
}
public function onRandomTick() : void{
if(mt_rand(1, 7) === 1){
if($this->level->getFullLightAt($this->x, $this->y, $this->z) >= 8 and mt_rand(1, 7) === 1){
if(($this->meta & 0x08) === 0x08){
Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
}else{

View File

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

View File

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

View File

@ -71,18 +71,20 @@ class Skull extends Flowable{
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
private function getItem() : Item{
$tile = $this->level->getTile($this);
if($tile instanceof TileSkull){
return [
ItemFactory::get(Item::SKULL, $tile->getType())
];
}
return ItemFactory::get(Item::SKULL, $tile instanceof TileSkull ? $tile->getType() : 0);
}
return [];
public function getDropsForCompatibleTool(Item $item) : array{
return [$this->getItem()];
}
public function isAffectedBySilkTouch() : bool{
return false;
}
public function getPickedItem() : Item{
return $this->getItem();
}
}

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,8 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\entity\projectile\Arrow;
use pocketmine\item\FlintSteel;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\Player;
@ -46,8 +48,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;
}
@ -55,6 +57,16 @@ class TNT extends Solid{
return false;
}
public function hasEntityCollision() : bool{
return true;
}
public function onEntityCollide(Entity $entity) : void{
if($entity instanceof Arrow and $entity->isOnFire()){
$this->ignite();
}
}
public function ignite(int $fuse = 80){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true);

View File

@ -100,11 +100,7 @@ abstract class Thin extends Transparent{
}
//FIXME: currently there's no proper way to tell if a block is a full-block, so we check the bounding box size
$bbs = $block->getCollisionBoxes();
if(count($bbs) === 1){
return $bbs[0]->getAverageEdgeLength() >= 1;
}
return false;
$bb = $block->getBoundingBox();
return $bb !== null and $bb->getAverageEdgeLength() >= 1;
}
}

View File

@ -55,7 +55,7 @@ class Torch extends Flowable{
5 => Vector3::SIDE_DOWN
];
if($this->getSide($faces[$side])->isTransparent() and !($side === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
if($this->getSide($faces[$side])->isTransparent() and !($faces[$side] === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
$this->getLevel()->useBreakOn($this);
}
}

View File

@ -28,6 +28,7 @@ namespace pocketmine\command;
use pocketmine\lang\TextContainer;
use pocketmine\lang\TranslationContainer;
use pocketmine\permission\PermissionManager;
use pocketmine\Server;
use pocketmine\timings\TimingsHandler;
use pocketmine\utils\TextFormat;
@ -127,7 +128,7 @@ abstract class Command{
if($this->permissionMessage === null){
$target->sendMessage($target->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
}elseif($this->permissionMessage !== ""){
$target->sendMessage(str_replace("<permission>", $this->getPermission(), $this->permissionMessage));
$target->sendMessage(str_replace("<permission>", $this->permission, $this->permissionMessage));
}
return false;
@ -139,11 +140,11 @@ abstract class Command{
* @return bool
*/
public function testPermissionSilent(CommandSender $target) : bool{
if(($perm = $this->getPermission()) === null or $perm === ""){
if($this->permission === null or $this->permission === ""){
return true;
}
foreach(explode(";", $perm) as $permission){
foreach(explode(";", $this->permission) as $permission){
if($target->hasPermission($permission)){
return true;
}
@ -293,7 +294,7 @@ abstract class Command{
$m = clone $message;
$result = "[" . $source->getName() . ": " . ($source->getServer()->getLanguage()->get($m->getText()) !== $m->getText() ? "%" : "") . $m->getText() . "]";
$users = $source->getServer()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
$users = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
$colored = TextFormat::GRAY . TextFormat::ITALIC . $result;
$m->setText($result);
@ -301,7 +302,7 @@ abstract class Command{
$m->setText($colored);
$colored = clone $m;
}else{
$users = $source->getServer()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
$users = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
$result = new TranslationContainer("chat.type.admin", [$source->getName(), $message]);
$colored = new TranslationContainer(TextFormat::GRAY . TextFormat::ITALIC . "%chat.type.admin", [$source->getName(), $message]);
}

View File

@ -27,7 +27,7 @@ namespace pocketmine\command;
interface CommandMap{
/**
* @param string $fallbackPrefix
* @param string $fallbackPrefix
* @param Command[] $commands
*/
public function registerAll(string $fallbackPrefix, array $commands);

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\command;
use pocketmine\snooze\SleeperNotifier;
use pocketmine\Thread;
use pocketmine\utils\Utils;
class CommandReader extends Thread{
@ -31,20 +33,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(){
$this->buffer = new \Threaded;
$opts = getopt("", ["disable-readline"]);
/** @var SleeperNotifier|null */
private $notifier;
if(extension_loaded("readline") and !isset($opts["disable-readline"]) and !$this->isPipe(STDIN)){
public function __construct(?SleeperNotifier $notifier = null){
$this->buffer = new \Threaded;
$this->notifier = $notifier;
$opts = getopt("", ["disable-readline", "enable-readline"]);
if(extension_loaded("readline") and (Utils::getOS() === "win" ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){
$this->type = self::TYPE_READLINE;
}
$this->start();
}
public function shutdown(){
@ -71,14 +79,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;
@ -89,10 +95,11 @@ class CommandReader extends Thread{
* Checks if the specified stream is a FIFO pipe.
*
* @param resource $stream
*
* @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 +116,7 @@ class CommandReader extends Thread{
return true;
}
}else{
global $stdin;
if(!is_resource($stdin)){
if(!is_resource(self::$stdin)){
$this->initStdin();
}
@ -119,7 +124,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 +132,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 +147,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 +169,8 @@ class CommandReader extends Thread{
}
public function run(){
$this->registerClassLoader();
if($this->type !== self::TYPE_READLINE){
$this->initStdin();
}
@ -168,8 +178,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

@ -54,6 +54,7 @@ interface CommandSender extends Permissible{
/**
* Sets the line height used for command output pagination for this command sender. `null` will reset it to default.
*
* @param int|null $height
*/
public function setScreenLineHeight(int $height = null);

View File

@ -64,7 +64,7 @@ class ConsoleCommandSender implements CommandSender{
/**
* @param Plugin $plugin
* @param string $name
* @param bool $value
* @param bool $value
*
* @return PermissionAttachment
*/
@ -145,5 +145,4 @@ class ConsoleCommandSender implements CommandSender{
}
$this->lineHeight = $height;
}
}

View File

@ -47,20 +47,19 @@ 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;
}
}
foreach($commands as $command){
$result |= Server::getInstance()->dispatchCommand($sender, $command);
$result |= Server::getInstance()->dispatchCommand($sender, $command, true);
}
return (bool) $result;
@ -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

@ -193,9 +193,9 @@ class SimpleCommandMap implements CommandMap{
/**
* @param Command $command
* @param bool $isAlias
* @param string $fallbackPrefix
* @param string $label
* @param bool $isAlias
* @param string $fallbackPrefix
* @param string $label
*
* @return bool
*/
@ -302,9 +302,9 @@ class SimpleCommandMap implements CommandMap{
}
$targets = [];
$bad = [];
$recursive = [];
$bad = "";
$recursive = "";
foreach($commandStrings as $commandString){
$args = explode(" ", $commandString);
$commandName = "";
@ -312,27 +312,21 @@ class SimpleCommandMap implements CommandMap{
if($command === null){
if(strlen($bad) > 0){
$bad .= ", ";
}
$bad .= $commandString;
$bad[] = $commandString;
}elseif($commandName === $alias){
if($recursive !== ""){
$recursive .= ", ";
}
$recursive .= $commandString;
$recursive[] = $commandString;
}else{
$targets[] = $commandString;
}
}
if($recursive !== ""){
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, $recursive]));
if(!empty($recursive)){
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, implode(", ", $recursive)]));
continue;
}
if(strlen($bad) > 0){
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, $bad]));
if(!empty($bad)){
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, implode(", ", $bad)]));
continue;
}
@ -345,6 +339,4 @@ class SimpleCommandMap implements CommandMap{
}
}
}

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\lang\TranslationContainer;
use pocketmine\plugin\Plugin;
use pocketmine\utils\TextFormat;
class PluginsCommand extends VanillaCommand{
@ -43,20 +44,12 @@ class PluginsCommand extends VanillaCommand{
if(!$this->testPermission($sender)){
return true;
}
$this->sendPluginList($sender);
$list = array_map(function(Plugin $plugin) : string{
return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName();
}, $sender->getServer()->getPluginManager()->getPlugins());
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($list), implode(TextFormat::WHITE . ", ", $list)]));
return true;
}
private function sendPluginList(CommandSender $sender){
$list = "";
foreach(($plugins = $sender->getServer()->getPluginManager()->getPlugins()) as $plugin){
if(strlen($list) > 0){
$list .= TextFormat::WHITE . ", ";
}
$list .= $plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED;
$list .= $plugin->getDescription()->getFullName();
}
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($plugins), $list]));
}
}

View File

@ -49,7 +49,7 @@ class TeleportCommand extends VanillaCommand{
}
$args = array_values(array_filter($args, function($arg){
return strlen($arg) > 0;
return $arg !== "";
}));
if(count($args) < 1 or count($args) > 6){
throw new InvalidCommandSyntaxException();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -143,7 +143,8 @@ class DataPropertyManager{
*/
public function getItem(int $key) : ?Item{
$value = $this->getPropertyValue($key, Entity::DATA_TYPE_SLOT);
assert($value instanceof Item or $value === null);
assert($value instanceof Item or $value === null);
return $value;
}

View File

@ -56,6 +56,7 @@ class Effect{
public const SATURATION = 23;
public const LEVITATION = 24; //TODO
public const FATAL_POISON = 25;
public const CONDUIT_POWER = 26;
/** @var Effect[] */
protected static $effects = [];
@ -86,6 +87,7 @@ class Effect{
self::registerEffect(new Effect(Effect::SATURATION, "%potion.saturation", new Color(0xf8, 0x24, 0x23), false, 1));
self::registerEffect(new Effect(Effect::LEVITATION, "%potion.levitation", new Color(0xce, 0xff, 0xff)));
self::registerEffect(new Effect(Effect::FATAL_POISON, "%potion.poison", new Color(0x4e, 0x93, 0x31), true));
self::registerEffect(new Effect(Effect::CONDUIT_POWER, "%potion.conduitPower", new Color(0x1d, 0xc2, 0xd1)));
}
/**
@ -111,8 +113,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

@ -81,6 +81,7 @@ class EffectInstance{
/**
* @param int $duration
*
* @throws \InvalidArgumentException
*
* @return $this

File diff suppressed because it is too large Load Diff

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,15 @@ 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\Durable;
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,9 +47,12 @@ 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\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\Player;
use pocketmine\utils\UUID;
@ -111,7 +118,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
/**
* @return UUID|null
*/
public function getUniqueId(){
public function getUniqueId() : ?UUID{
return $this->uuid;
}
@ -151,14 +158,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 +186,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 +209,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 +237,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 +256,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);
}
@ -512,6 +519,42 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
return $this->xpCooldown === 0;
}
public function onPickupXp(int $xpValue) : void{
static $mainHandIndex = -1;
//TODO: replace this with a more generic equipment getting/setting interface
/** @var Durable[] $equipment */
$equipment = [];
if(($item = $this->inventory->getItemInHand()) instanceof Durable and $item->hasEnchantment(Enchantment::MENDING)){
$equipment[$mainHandIndex] = $item;
}
//TODO: check offhand
foreach($this->armorInventory->getContents() as $k => $item){
if($item instanceof Durable and $item->hasEnchantment(Enchantment::MENDING)){
$equipment[$k] = $item;
}
}
if(!empty($equipment)){
$repairItem = $equipment[$k = array_rand($equipment)];
if($repairItem->getDamage() > 0){
$repairAmount = min($repairItem->getDamage(), $xpValue * 2);
$repairItem->setDamage($repairItem->getDamage() - $repairAmount);
$xpValue -= (int) ceil($repairAmount / 2);
if($k === $mainHandIndex){
$this->inventory->setItemInHand($repairItem);
}else{
$this->armorInventory->setItem($k, $repairItem);
}
}
}
$this->addXp($xpValue); //this will still get fired even if the value is 0 due to mending, to play sounds
$this->resetXpCooldown();
}
/**
* Sets the duration in ticks until the human can pick up another XP orb.
*
@ -529,14 +572,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 +598,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 +624,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 +638,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 +656,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 +678,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 +690,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 +706,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 +722,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 +826,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);
}
@ -754,6 +837,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set");
}
if(!($this instanceof Player)){
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_ADD;
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)];
$player->dataPacket($pk);
}
$pk = new AddPlayerPacket();
$pk->uuid = $this->getUniqueId();
$pk->username = $this->getName();
@ -772,11 +863,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->armorInventory->sendContents($player);
if(!($this instanceof Player)){
$this->sendSkin([$player]);
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_REMOVE;
$pk->entries = [PlayerListEntry::createRemovalEntry($this->uuid)];
$player->dataPacket($pk);
}
}
public function close(){
public function close() : void{
if(!$this->closed){
if($this->inventory !== null){
$this->inventory->removeAllViewers(true);
@ -794,6 +888,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* Wrapper around {@link Entity#getDataFlag} for player-specific data flag reading.
*
* @param int $flagId
*
* @return bool
*/
public function getPlayerFlag(int $flagId) : bool{
@ -806,7 +901,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,8 +31,10 @@ 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\Durable;
use pocketmine\item\enchantment\Enchantment;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
@ -72,10 +74,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 +116,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 +125,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 +138,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 +146,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 +190,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 +201,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 +229,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 +300,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;
@ -321,9 +329,10 @@ 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 +383,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 +436,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 +449,12 @@ abstract class Living extends Entity implements Damageable{
public function applyDamageModifiers(EntityDamageEvent $source) : void{
if($source->canBeReducedByArmor()){
//MCPE uses the same system as PC did pre-1.9
$source->setDamage(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
$source->setModifier(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
}
$cause = $source->getCause();
if($this->hasEffect(Effect::DAMAGE_RESISTANCE) and $cause !== EntityDamageEvent::CAUSE_VOID and $cause !== EntityDamageEvent::CAUSE_SUICIDE){
$source->setDamage(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
$source->setModifier(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
}
$totalEpf = 0;
@ -454,20 +463,41 @@ abstract class Living extends Entity implements Damageable{
$totalEpf += $item->getEnchantmentProtectionFactor($source);
}
}
$source->setDamage(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
$source->setModifier(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
$source->setDamage(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
$source->setModifier(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
}
/**
* Called after EntityDamageEvent execution to apply post-hurt effects, such as reducing absorption or modifying
* armour durability.
* This will not be called by damage sources causing death.
*
* @param EntityDamageEvent $source
*/
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getDamage(EntityDamageEvent::MODIFIER_ABSORPTION)));
$this->damageArmor($source->getDamage(EntityDamageEvent::MODIFIER_BASE));
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
$this->damageArmor($source->getBaseDamage());
if($source instanceof EntityDamageByEntityEvent){
$damage = 0;
foreach($this->armorInventory->getContents() as $k => $item){
if($item instanceof Armor and ($thornsLevel = $item->getEnchantmentLevel(Enchantment::THORNS)) > 0){
if(mt_rand(0, 99) < $thornsLevel * 15){
$this->damageItem($item, 3);
$damage += ($thornsLevel > 10 ? $thornsLevel - 10 : 1 + mt_rand(0, 3));
}else{
$this->damageItem($item, 1); //thorns causes an extra +1 durability loss even if it didn't activate
}
$this->armorInventory->setItem($k, $item);
}
}
if($damage > 0){
$source->getDamager()->attack(new EntityDamageByEntityEvent($this, $source->getDamager(), EntityDamageEvent::CAUSE_MAGIC, $damage));
}
}
}
/**
@ -482,20 +512,24 @@ abstract class Living extends Entity implements Damageable{
$armor = $this->armorInventory->getContents(true);
foreach($armor as $item){
if($item instanceof Armor){
$item->applyDamage($durabilityRemoved);
if($item->isBroken()){
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_BREAK);
}
$this->damageItem($item, $durabilityRemoved);
}
}
$this->armorInventory->setContents($armor);
}
public function attack(EntityDamageEvent $source){
private function damageItem(Durable $item, int $durabilityRemoved) : void{
$item->applyDamage($durabilityRemoved);
if($item->isBroken()){
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_BREAK);
}
}
public function attack(EntityDamageEvent $source) : void{
if($this->attackTime > 0 or $this->noDamageTicks > 0){
$lastCause = $this->getLastDamageCause();
if($lastCause !== null and $lastCause->getDamage() >= $source->getDamage()){
if($lastCause !== null and $lastCause->getBaseDamage() >= $source->getBaseDamage()){
$source->setCancelled();
}
}
@ -527,6 +561,8 @@ abstract class Living extends Entity implements Damageable{
return;
}
$this->attackTime = $source->getAttackCooldown();
if($source instanceof EntityDamageByEntityEvent){
$e = $source->getDamager();
if($source instanceof EntityDamageByChildEntityEvent){
@ -540,55 +576,52 @@ abstract class Living extends Entity implements Damageable{
$deltaX = $this->x - $e->x;
$deltaZ = $this->z - $e->z;
$this->knockBack($e, $source->getDamage(), $deltaX, $deltaZ, $source->getKnockBack());
$this->knockBack($e, $source->getBaseDamage(), $deltaX, $deltaZ, $source->getKnockBack());
}
}
$this->applyPostDamageEffects($source);
if($this->isAlive()){
$this->applyPostDamageEffects($source);
$this->doHitAnimation();
}else{
$this->startDeathAnimation();
}
$this->attackTime = 10; //0.5 seconds cooldown
}
protected function doHitAnimation() : void{
$this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION);
}
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4){
public function knockBack(Entity $attacker, float $damage, float $x, float $z, float $base = 0.4) : void{
$f = sqrt($x * $x + $z * $z);
if($f <= 0){
return;
}
if(mt_rand() / mt_getrandmax() > $this->getAttributeMap()->getAttribute(Attribute::KNOCKBACK_RESISTANCE)->getValue()){
$f = 1 / $f;
$f = 1 / $f;
$motion = clone $this->motion;
$motion = new Vector3($this->motionX, $this->motionY, $this->motionZ);
$motion->x /= 2;
$motion->y /= 2;
$motion->z /= 2;
$motion->x += $x * $f * $base;
$motion->y += $base;
$motion->z += $z * $f * $base;
$motion->x /= 2;
$motion->y /= 2;
$motion->z /= 2;
$motion->x += $x * $f * $base;
$motion->y += $base;
$motion->z += $z * $f * $base;
if($motion->y > $base){
$motion->y = $base;
}
if($motion->y > $base){
$motion->y = $base;
$this->setMotion($motion);
}
$this->setMotion($motion);
}
public function kill(){
public function kill() : void{
parent::kill();
$this->onDeath();
$this->startDeathAnimation();
}
protected function onDeath(){
protected function onDeath() : void{
$this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops()));
foreach($ev->getDrops() as $item){
$this->getLevel()->dropItem($this, $item);
@ -622,21 +655,19 @@ abstract class Living extends Entity implements Damageable{
$hasUpdate = parent::entityBaseTick($tickDiff);
$this->doEffectsTick($tickDiff);
if($this->isAlive()){
if($this->doEffectsTick($tickDiff)){
$hasUpdate = true;
}
if($this->isInsideOfSolid()){
$hasUpdate = true;
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 1);
$this->attack($ev);
}
if(!$this->canBreathe()){
$this->setBreathing(false);
$this->doAirSupplyTick($tickDiff);
}elseif(!$this->isBreathing()){
$this->setBreathing(true);
$this->setAirSupplyTicks($this->getMaxAirSupplyTicks());
if($this->doAirSupplyTick($tickDiff)){
$hasUpdate = true;
}
}
@ -649,7 +680,7 @@ abstract class Living extends Entity implements Damageable{
return $hasUpdate;
}
protected function doEffectsTick(int $tickDiff = 1){
protected function doEffectsTick(int $tickDiff = 1) : bool{
foreach($this->effects as $instance){
$type = $instance->getType();
if($type->canTick($instance)){
@ -660,25 +691,47 @@ abstract class Living extends Entity implements Damageable{
$this->removeEffect($instance->getId());
}
}
return !empty($this->effects);
}
/**
* Ticks the entity's air supply when it cannot breathe.
* Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water.
*
* @param int $tickDiff
*
* @return bool
*/
protected function doAirSupplyTick(int $tickDiff){
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or
lcg_value() <= (1 / ($respirationLevel + 1))
){
$ticks = $this->getAirSupplyTicks() - $tickDiff;
protected function doAirSupplyTick(int $tickDiff) : bool{
$ticks = $this->getAirSupplyTicks();
$oldTicks = $ticks;
if(!$this->canBreathe()){
$this->setBreathing(false);
if($ticks <= -20){
$this->setAirSupplyTicks(0);
$this->onAirExpired();
}else{
$this->setAirSupplyTicks($ticks);
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or
lcg_value() <= (1 / ($respirationLevel + 1))
){
$ticks -= $tickDiff;
if($ticks <= -20){
$ticks = 0;
$this->onAirExpired();
}
}
}elseif(!$this->isBreathing()){
if($ticks < ($max = $this->getMaxAirSupplyTicks())){
$ticks += $tickDiff * 5;
}
if($ticks >= $max){
$ticks = $max;
$this->setBreathing(true);
}
}
if($ticks !== $oldTicks){
$this->setAirSupplyTicks($ticks);
}
return $ticks !== $oldTicks;
}
/**
@ -686,7 +739,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->hasEffect(Effect::CONDUIT_POWER) or !$this->isUnderwater();
}
/**
@ -703,7 +756,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);
}
@ -719,9 +772,10 @@ 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);
}
@ -735,9 +789,10 @@ 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 +800,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 +870,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 +904,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));
}
}
}

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