Compare commits

..

79 Commits
3.2.2 ... 3.2.6

Author SHA1 Message Date
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
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
09985c5763 Fixed async light population producing garbage when generator isn't registered, closes #2488 2018-10-24 15:49:00 +01:00
196cf8a68d Fixed missing MoveEntityDeltaPacket field 2018-10-24 12:00:25 +01:00
45c9caa38c Fixup some formatting issues 2018-10-21 18:15:25 +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
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
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
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
104 changed files with 470 additions and 244 deletions

22
composer.lock generated
View File

@ -76,16 +76,16 @@
}, },
{ {
"name": "pocketmine/nbt", "name": "pocketmine/nbt",
"version": "0.2.1", "version": "0.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/NBT.git", "url": "https://github.com/pmmp/NBT.git",
"reference": "a4ee39f313c6870153fb7c7e513b211217f7daf8" "reference": "474f0cf0a47656d0122b4f3f71302e694ed6977b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/a4ee39f313c6870153fb7c7e513b211217f7daf8", "url": "https://api.github.com/repos/pmmp/NBT/zipball/474f0cf0a47656d0122b4f3f71302e694ed6977b",
"reference": "a4ee39f313c6870153fb7c7e513b211217f7daf8", "reference": "474f0cf0a47656d0122b4f3f71302e694ed6977b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -109,10 +109,10 @@
], ],
"description": "PHP library for working with Named Binary Tags", "description": "PHP library for working with Named Binary Tags",
"support": { "support": {
"source": "https://github.com/pmmp/NBT/tree/0.2.1", "source": "https://github.com/pmmp/NBT/tree/0.2.2",
"issues": "https://github.com/pmmp/NBT/issues" "issues": "https://github.com/pmmp/NBT/issues"
}, },
"time": "2018-09-04T10:36:02+00:00" "time": "2018-10-12T08:26:44+00:00"
}, },
{ {
"name": "pocketmine/raklib", "name": "pocketmine/raklib",
@ -191,16 +191,16 @@
}, },
{ {
"name": "pocketmine/spl", "name": "pocketmine/spl",
"version": "0.3.1", "version": "0.3.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/SPL.git", "url": "https://github.com/pmmp/SPL.git",
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5" "reference": "7fd53857cd000491ba69e8db865792a024dd2c49"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/SPL/zipball/ca3912099543ddc4b4b14f40e258d84ca547dfa5", "url": "https://api.github.com/repos/pmmp/SPL/zipball/7fd53857cd000491ba69e8db865792a024dd2c49",
"reference": "ca3912099543ddc4b4b14f40e258d84ca547dfa5", "reference": "7fd53857cd000491ba69e8db865792a024dd2c49",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -219,7 +219,7 @@
"support": { "support": {
"source": "https://github.com/pmmp/SPL/tree/master" "source": "https://github.com/pmmp/SPL/tree/master"
}, },
"time": "2018-06-09T17:30:36+00:00" "time": "2018-08-12T15:17:39+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],

View File

@ -212,11 +212,11 @@ class CrashDump{
$this->addLine("Code:"); $this->addLine("Code:");
$this->data["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); $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10; ++$l){ for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->addLine("[" . ($l + 1) . "] " . @$file[$l]); $this->addLine("[" . ($l + 1) . "] " . $file[$l]);
$this->data["code"][$l + 1] = @$file[$l]; $this->data["code"][$l + 1] = $file[$l];
} }
} }

View File

@ -473,7 +473,7 @@ class MemoryManager{
self::continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize); self::continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize);
} }
}elseif(is_string($from)){ }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)){ }elseif(is_resource($from)){
$data = "(resource) " . print_r($from, true); $data = "(resource) " . print_r($from, true);
}else{ }else{

View File

@ -173,6 +173,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** /**
* Checks a supplied username and checks it is valid. * Checks a supplied username and checks it is valid.
*
* @param string $name * @param string $name
* *
* @return bool * @return bool
@ -1326,6 +1327,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* TODO: remove this when Spectator Mode gets added properly to MCPE * TODO: remove this when Spectator Mode gets added properly to MCPE
* *
* @param int $gamemode * @param int $gamemode
*
* @return int * @return int
*/ */
public static function getClientFriendlyGamemode(int $gamemode) : int{ public static function getClientFriendlyGamemode(int $gamemode) : int{
@ -2266,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. * Don't expect much from this handler. Most of it is roughly hacked and duct-taped together.
* *
* @param InventoryTransactionPacket $packet * @param InventoryTransactionPacket $packet
*
* @return bool * @return bool
*/ */
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{ public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
@ -2721,7 +2724,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$target = $this->level->getBlock($pos); $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)){ if($this->level->checkSpawnProtection($this, $target)){
$ev->setCancelled(); $ev->setCancelled();
} }
@ -2785,7 +2788,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
break; //TODO break; //TODO
case PlayerActionPacket::ACTION_CONTINUE_BREAK: case PlayerActionPacket::ACTION_CONTINUE_BREAK:
$block = $this->level->getBlock($pos); $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 //TODO: destroy-progress level event
break; break;
case PlayerActionPacket::ACTION_START_SWIMMING: case PlayerActionPacket::ACTION_START_SWIMMING:
@ -2845,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. * Drops an item on the ground in front of the player. Returns if the item drop was successful.
* *
* @param Item $item * @param Item $item
*
* @return bool if the item was dropped or if the item was null * @return bool if the item was dropped or if the item was null
*/ */
public function dropItem(Item $item) : bool{ public function dropItem(Item $item) : bool{
@ -3365,6 +3369,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk = new ModalFormRequestPacket(); $pk = new ModalFormRequestPacket();
$pk->formId = $id; $pk->formId = $id;
$pk->formData = json_encode($form); $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)){ if($this->dataPacket($pk)){
$this->forms[$id] = $form; $this->forms[$id] = $form;
} }

View File

@ -37,7 +37,7 @@ namespace pocketmine {
use pocketmine\wizard\SetupWizard; use pocketmine\wizard\SetupWizard;
const NAME = "PocketMine-MP"; const NAME = "PocketMine-MP";
const BASE_VERSION = "3.2.2"; const BASE_VERSION = "3.2.6";
const IS_DEVELOPMENT_BUILD = false; const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0; const BUILD_NUMBER = 0;
@ -145,12 +145,18 @@ namespace pocketmine {
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR); 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);
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){ if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH); require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{ }else{
critical_error("Composer autoloader not found."); critical_error("Composer autoloader not found at " . $bootstrap);
critical_error("Please install/update Composer dependencies or use provided builds."); critical_error("Please install/update Composer dependencies or use provided builds.");
exit(1); exit(1);
} }

View File

@ -991,6 +991,7 @@ class Server{
/** /**
* @internal * @internal
*
* @param Level $level * @param Level $level
*/ */
public function removeLevel(Level $level) : void{ public function removeLevel(Level $level) : void{
@ -2195,6 +2196,14 @@ class Server{
if($this->getProperty("auto-report.enabled", true) !== false){ if($this->getProperty("auto-report.enabled", true) !== false){
$report = true; $report = true;
$stamp = $this->getDataPath() . "crashdumps/.last_crash";
$crashInterval = 120; //2 minutes
if(file_exists($stamp) and !($report = (filemtime($stamp) + $crashInterval < time()))){
$this->logger->debug("Not sending crashdump due to last crash less than $crashInterval seconds ago");
}
@touch($stamp); //update file timestamp
$plugin = $dump->getData()["plugin"]; $plugin = $dump->getData()["plugin"];
if(is_string($plugin)){ if(is_string($plugin)){
$p = $this->pluginManager->getPlugin($plugin); $p = $this->pluginManager->getPlugin($plugin);
@ -2348,11 +2357,15 @@ class Server{
} }
//Do level ticks //Do level ticks
foreach($this->getLevels() as $level){ foreach($this->levels as $k => $level){
if(!isset($this->levels[$k])){
// Level unloaded during the tick of a level earlier in this loop, perhaps by plugin
continue;
}
if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){ if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){
continue; continue;
} }
try{
$levelTime = microtime(true); $levelTime = microtime(true);
$level->doTick($currentTick); $level->doTick($currentTick);
$tickMs = (microtime(true) - $levelTime) * 1000; $tickMs = (microtime(true) - $levelTime) * 1000;
@ -2376,14 +2389,6 @@ class Server{
$level->tickRateCounter = $level->getTickRate(); $level->tickRateCounter = $level->getTickRate();
} }
} }
}catch(\Throwable $e){
if(!$level->isClosed()){
$this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickError", [$level->getName(), $e->getMessage()]));
}else{
$this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickUnloadError", [$level->getName()]));
}
$this->logger->logException($e);
}
} }
} }

View File

@ -192,21 +192,25 @@ class Bed extends Transparent{
public function getDropsForCompatibleTool(Item $item) : array{ public function getDropsForCompatibleTool(Item $item) : array{
if($this->isHeadPart()){ if($this->isHeadPart()){
$tile = $this->getLevel()->getTile($this); return [$this->getItem()];
if($tile instanceof TileBed){
return [
ItemFactory::get($this->getItemId(), $tile->getColor())
];
}else{
return [
ItemFactory::get($this->getItemId(), 14) //Red
];
}
} }
return []; 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{ public function isAffectedBySilkTouch() : bool{
return false; return false;
} }

View File

@ -109,6 +109,14 @@ class Block extends Position implements BlockIds, Metadatable{
return $this->itemId ?? $this->getId(); return $this->itemId ?? $this->getId();
} }
/**
* @internal
* @return int
*/
public function getRuntimeId() : int{
return BlockFactory::toStaticRuntimeId($this->getId(), $this->getDamage());
}
/** /**
* @return int * @return int
*/ */

View File

@ -330,6 +330,10 @@ class BlockFactory{
} }
} }
public static function isInit() : bool{
return self::$fullList !== null;
}
/** /**
* Registers a block type into the index. Plugins may use this method to register new block types or override * Registers a block type into the index. Plugins may use this method to register new block types or override
* existing ones. * existing ones.
@ -411,6 +415,7 @@ class BlockFactory{
* Returns whether a specified block ID is already registered in the block factory. * Returns whether a specified block ID is already registered in the block factory.
* *
* @param int $id * @param int $id
*
* @return bool * @return bool
*/ */
public static function isRegistered(int $id) : bool{ public static function isRegistered(int $id) : bool{

View File

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

View File

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

View File

@ -47,4 +47,8 @@ abstract class DoubleSlab extends Solid{
public function isAffectedBySilkTouch() : bool{ public function isAffectedBySilkTouch() : bool{
return false; return false;
} }
public function getPickedItem() : Item{
return ItemFactory::get($this->getSlabId(), $this->getVariant());
}
} }

View File

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

View File

@ -50,7 +50,8 @@ class RedstoneOre extends Solid{
} }
public function onActivate(Item $item, Player $player = null) : bool{ 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{ public function onNearbyBlockChange() : void{

View File

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

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 //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(); $bb = $block->getBoundingBox();
if(count($bbs) === 1){ return $bb !== null and $bb->getAverageEdgeLength() >= 1;
return $bbs[0]->getAverageEdgeLength() >= 1;
}
return false;
} }
} }

View File

@ -55,7 +55,7 @@ class Torch extends Flowable{
5 => Vector3::SIDE_DOWN 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); $this->getLevel()->useBreakOn($this);
} }
} }

View File

@ -25,6 +25,7 @@ namespace pocketmine\command;
use pocketmine\snooze\SleeperNotifier; use pocketmine\snooze\SleeperNotifier;
use pocketmine\Thread; use pocketmine\Thread;
use pocketmine\utils\Utils;
class CommandReader extends Thread{ class CommandReader extends Thread{
@ -47,9 +48,9 @@ class CommandReader extends Thread{
$this->buffer = new \Threaded; $this->buffer = new \Threaded;
$this->notifier = $notifier; $this->notifier = $notifier;
$opts = getopt("", ["disable-readline"]); $opts = getopt("", ["disable-readline", "enable-readline"]);
if(extension_loaded("readline") and !isset($opts["disable-readline"]) and !$this->isPipe(STDIN)){ 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->type = self::TYPE_READLINE;
} }
} }
@ -94,6 +95,7 @@ class CommandReader extends Thread{
* Checks if the specified stream is a FIFO pipe. * Checks if the specified stream is a FIFO pipe.
* *
* @param resource $stream * @param resource $stream
*
* @return bool * @return bool
*/ */
private function isPipe($stream) : bool{ private function isPipe($stream) : bool{

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. * Sets the line height used for command output pagination for this command sender. `null` will reset it to default.
*
* @param int|null $height * @param int|null $height
*/ */
public function setScreenLineHeight(int $height = null); public function setScreenLineHeight(int $height = null);

View File

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

View File

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

View File

@ -342,7 +342,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* *
* @return CompoundTag * @return CompoundTag
*/ */
public static function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null , float $yaw = 0.0, float $pitch = 0.0) : CompoundTag{ public static function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null, float $yaw = 0.0, float $pitch = 0.0) : CompoundTag{
return new CompoundTag("", [ return new CompoundTag("", [
new ListTag("Pos", [ new ListTag("Pos", [
new DoubleTag("", $pos->x), new DoubleTag("", $pos->x),
@ -408,8 +408,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public $boundingBox; public $boundingBox;
/** @var bool */ /** @var bool */
public $onGround; public $onGround;
/** @var int */
protected $age = 0;
/** @var float */ /** @var float */
public $eyeHeight = null; public $eyeHeight = null;
@ -934,6 +932,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* Called to tick entities while dead. Returns whether the entity should be flagged for despawn yet. * Called to tick entities while dead. Returns whether the entity should be flagged for despawn yet.
* *
* @param int $tickDiff * @param int $tickDiff
*
* @return bool * @return bool
*/ */
protected function onDeathUpdate(int $tickDiff) : bool{ protected function onDeathUpdate(int $tickDiff) : bool{
@ -1041,7 +1040,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
} }
} }
$this->age += $tickDiff;
$this->ticksLived += $tickDiff; $this->ticksLived += $tickDiff;
return $hasUpdate; return $hasUpdate;
@ -1393,7 +1391,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
Timings::$timerEntityBaseTick->stopTiming(); Timings::$timerEntityBaseTick->stopTiming();
$this->timings->stopTiming(); $this->timings->stopTiming();
//if($this->isStatic()) //if($this->isStatic())
@ -2074,6 +2071,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* Wrapper around {@link Entity#getDataFlag} for generic data flag reading. * Wrapper around {@link Entity#getDataFlag} for generic data flag reading.
* *
* @param int $flagId * @param int $flagId
*
* @return bool * @return bool
*/ */
public function getGenericFlag(int $flagId) : bool{ public function getGenericFlag(int $flagId) : bool{

View File

@ -888,6 +888,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* Wrapper around {@link Entity#getDataFlag} for player-specific data flag reading. * Wrapper around {@link Entity#getDataFlag} for player-specific data flag reading.
* *
* @param int $flagId * @param int $flagId
*
* @return bool * @return bool
*/ */
public function getPlayerFlag(int $flagId) : bool{ public function getPlayerFlag(int $flagId) : bool{

View File

@ -329,6 +329,7 @@ abstract class Living extends Entity implements Damageable{
/** /**
* Sends the mob's potion effects to the specified player. * Sends the mob's potion effects to the specified player.
*
* @param Player $player * @param Player $player
*/ */
public function sendPotionEffects(Player $player) : void{ public function sendPotionEffects(Player $player) : void{
@ -771,6 +772,7 @@ abstract class Living extends Entity implements Damageable{
/** /**
* Sets the number of air ticks left in the entity's air supply. * Sets the number of air ticks left in the entity's air supply.
*
* @param int $ticks * @param int $ticks
*/ */
public function setAirSupplyTicks(int $ticks) : void{ public function setAirSupplyTicks(int $ticks) : void{
@ -787,6 +789,7 @@ abstract class Living extends Entity implements Damageable{
/** /**
* Sets the maximum amount of air ticks the air supply can hold. * Sets the maximum amount of air ticks the air supply can hold.
*
* @param int $ticks * @param int $ticks
*/ */
public function setMaxAirSupplyTicks(int $ticks) : void{ public function setMaxAirSupplyTicks(int $ticks) : void{

View File

@ -88,6 +88,9 @@ class ExperienceOrb extends Entity{
public $gravity = 0.04; public $gravity = 0.04;
public $drag = 0.02; public $drag = 0.02;
/** @var int */
protected $age = 0;
/** /**
* @var int * @var int
* Ticker used for determining interval in which to look for new target players. * Ticker used for determining interval in which to look for new target players.
@ -159,6 +162,7 @@ class ExperienceOrb extends Entity{
public function entityBaseTick(int $tickDiff = 1) : bool{ public function entityBaseTick(int $tickDiff = 1) : bool{
$hasUpdate = parent::entityBaseTick($tickDiff); $hasUpdate = parent::entityBaseTick($tickDiff);
$this->age += $tickDiff;
if($this->age > 6000){ if($this->age > 6000){
$this->flagForDespawn(); $this->flagForDespawn();
return true; return true;

View File

@ -71,7 +71,7 @@ class FallingBlock extends Entity{
$this->block = BlockFactory::get($blockId, $damage); $this->block = BlockFactory::get($blockId, $damage);
$this->propertyManager->setInt(self::DATA_VARIANT, BlockFactory::toStaticRuntimeId($this->block->getId(), $this->block->getDamage())); $this->propertyManager->setInt(self::DATA_VARIANT, $this->block->getRuntimeId());
} }
public function canCollideWith(Entity $entity) : bool{ public function canCollideWith(Entity $entity) : bool{

View File

@ -53,6 +53,9 @@ class ItemEntity extends Entity{
public $canCollide = false; public $canCollide = false;
/** @var int */
protected $age = 0;
protected function initEntity() : void{ protected function initEntity() : void{
parent::initEntity(); parent::initEntity();
@ -70,6 +73,9 @@ class ItemEntity extends Entity{
} }
$this->item = Item::nbtDeserialize($itemTag); $this->item = Item::nbtDeserialize($itemTag);
if($this->item->isNull()){
throw new \UnexpectedValueException("Item for " . get_class($this) . " is invalid");
}
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this)); $this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
@ -82,14 +88,13 @@ class ItemEntity extends Entity{
$hasUpdate = parent::entityBaseTick($tickDiff); $hasUpdate = parent::entityBaseTick($tickDiff);
if(!$this->isFlaggedForDespawn()){ if(!$this->isFlaggedForDespawn() and $this->pickupDelay > -1 and $this->pickupDelay < 32767){ //Infinite delay
if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay
$this->pickupDelay -= $tickDiff; $this->pickupDelay -= $tickDiff;
if($this->pickupDelay < 0){ if($this->pickupDelay < 0){
$this->pickupDelay = 0; $this->pickupDelay = 0;
} }
}
$this->age += $tickDiff;
if($this->age > 6000){ if($this->age > 6000){
$this->server->getPluginManager()->callEvent($ev = new ItemDespawnEvent($this)); $this->server->getPluginManager()->callEvent($ev = new ItemDespawnEvent($this));
if($ev->isCancelled()){ if($ev->isCancelled()){
@ -99,7 +104,6 @@ class ItemEntity extends Entity{
$hasUpdate = true; $hasUpdate = true;
} }
} }
} }
return $hasUpdate; return $hasUpdate;

View File

@ -73,6 +73,7 @@ class PaintingMotive{
/** /**
* @param string $name * @param string $name
*
* @return PaintingMotive|null * @return PaintingMotive|null
*/ */
public static function getMotiveByName(string $name) : ?PaintingMotive{ public static function getMotiveByName(string $name) : ?PaintingMotive{

View File

@ -61,6 +61,9 @@ class Arrow extends Projectile{
/** @var float */ /** @var float */
protected $punchKnockback = 0.0; protected $punchKnockback = 0.0;
/** @var int */
protected $collideTicks = 0;
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){ public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){
parent::__construct($level, $nbt, $shootingEntity); parent::__construct($level, $nbt, $shootingEntity);
$this->setCritical($critical); $this->setCritical($critical);
@ -70,12 +73,14 @@ class Arrow extends Projectile{
parent::initEntity(); parent::initEntity();
$this->pickupMode = $this->namedtag->getByte(self::TAG_PICKUP, self::PICKUP_ANY, true); $this->pickupMode = $this->namedtag->getByte(self::TAG_PICKUP, self::PICKUP_ANY, true);
$this->collideTicks = $this->namedtag->getShort("life", $this->collideTicks);
} }
public function saveNBT() : void{ public function saveNBT() : void{
parent::saveNBT(); parent::saveNBT();
$this->namedtag->setByte(self::TAG_PICKUP, $this->pickupMode, true); $this->namedtag->setByte(self::TAG_PICKUP, $this->pickupMode, true);
$this->namedtag->setShort("life", $this->collideTicks);
} }
public function isCritical() : bool{ public function isCritical() : bool{
@ -116,10 +121,15 @@ class Arrow extends Projectile{
$hasUpdate = parent::entityBaseTick($tickDiff); $hasUpdate = parent::entityBaseTick($tickDiff);
if($this->age > 1200){ if($this->isCollided){
$this->collideTicks += $tickDiff;
if($this->collideTicks > 1200){
$this->flagForDespawn(); $this->flagForDespawn();
$hasUpdate = true; $hasUpdate = true;
} }
}else{
$this->collideTicks = 0;
}
return $hasUpdate; return $hasUpdate;
} }

View File

@ -66,7 +66,5 @@ class EnderPearl extends Throwable{
$owner->attack(new EntityDamageEvent($owner, EntityDamageEvent::CAUSE_FALL, 5)); $owner->attack(new EntityDamageEvent($owner, EntityDamageEvent::CAUSE_FALL, 5));
} }
$this->flagForDespawn();
} }
} }

View File

@ -42,7 +42,5 @@ class ExperienceBottle extends Throwable{
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_GLASS); $this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_GLASS);
$this->level->dropExperience($this, mt_rand(3, 11)); $this->level->dropExperience($this, mt_rand(3, 11));
$this->flagForDespawn();
} }
} }

View File

@ -74,7 +74,6 @@ abstract class Projectile extends Entity{
$this->setMaxHealth(1); $this->setMaxHealth(1);
$this->setHealth(1); $this->setHealth(1);
$this->age = $this->namedtag->getShort("Age", $this->age);
$this->damage = $this->namedtag->getDouble("damage", $this->damage); $this->damage = $this->namedtag->getDouble("damage", $this->damage);
do{ do{
@ -144,7 +143,6 @@ abstract class Projectile extends Entity{
public function saveNBT() : void{ public function saveNBT() : void{
parent::saveNBT(); parent::saveNBT();
$this->namedtag->setShort("Age", $this->age);
$this->namedtag->setDouble("damage", $this->damage); $this->namedtag->setDouble("damage", $this->damage);
if($this->blockHit !== null){ if($this->blockHit !== null){

View File

@ -27,8 +27,8 @@ use pocketmine\block\Block;
use pocketmine\block\BlockFactory; use pocketmine\block\BlockFactory;
use pocketmine\entity\EffectInstance; use pocketmine\entity\EffectInstance;
use pocketmine\entity\Living; use pocketmine\entity\Living;
use pocketmine\event\entity\ProjectileHitEntityEvent;
use pocketmine\event\entity\ProjectileHitBlockEvent; use pocketmine\event\entity\ProjectileHitBlockEvent;
use pocketmine\event\entity\ProjectileHitEntityEvent;
use pocketmine\event\entity\ProjectileHitEvent; use pocketmine\event\entity\ProjectileHitEvent;
use pocketmine\item\Potion; use pocketmine\item\Potion;
use pocketmine\network\mcpe\protocol\LevelEventPacket; use pocketmine\network\mcpe\protocol\LevelEventPacket;
@ -124,8 +124,6 @@ class SplashPotion extends Throwable{
} }
} }
} }
$this->flagForDespawn();
} }
/** /**
@ -153,6 +151,7 @@ class SplashPotion extends Throwable{
/** /**
* Sets whether this splash potion will create an area-effect-cloud when it lands. * Sets whether this splash potion will create an area-effect-cloud when it lands.
*
* @param bool $value * @param bool $value
*/ */
public function setLinger(bool $value = true) : void{ public function setLinger(bool $value = true) : void{

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace pocketmine\entity\projectile; namespace pocketmine\entity\projectile;
use pocketmine\block\Block;
use pocketmine\math\RayTraceResult;
abstract class Throwable extends Projectile{ abstract class Throwable extends Projectile{
public $width = 0.25; public $width = 0.25;
@ -31,18 +34,8 @@ abstract class Throwable extends Projectile{
protected $gravity = 0.03; protected $gravity = 0.03;
protected $drag = 0.01; protected $drag = 0.01;
public function entityBaseTick(int $tickDiff = 1) : bool{ protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{
if($this->closed){ parent::onHitBlock($blockHit, $hitResult);
return false;
}
$hasUpdate = parent::entityBaseTick($tickDiff);
if($this->age > 1200 or $this->isCollided){
$this->flagForDespawn(); $this->flagForDespawn();
$hasUpdate = true;
}
return $hasUpdate;
} }
} }

View File

@ -31,6 +31,7 @@ abstract class ExperienceUtils{
* Calculates and returns the amount of XP needed to get from level 0 to level $level * Calculates and returns the amount of XP needed to get from level 0 to level $level
* *
* @param int $level * @param int $level
*
* @return int * @return int
*/ */
public static function getXpToReachLevel(int $level) : int{ public static function getXpToReachLevel(int $level) : int{

View File

@ -63,6 +63,7 @@ class PlayerChangeSkinEvent extends PlayerEvent implements Cancellable{
/** /**
* @param Skin $skin * @param Skin $skin
*
* @throws \InvalidArgumentException if the specified skin is not valid * @throws \InvalidArgumentException if the specified skin is not valid
*/ */
public function setNewSkin(Skin $skin) : void{ public function setNewSkin(Skin $skin) : void{

View File

@ -384,6 +384,7 @@ abstract class BaseInventory implements Inventory{
/** /**
* Removes the inventory window from all players currently viewing it. * Removes the inventory window from all players currently viewing it.
*
* @param bool $force Force removal of permanent windows such as the player's own inventory. Used internally. * @param bool $force Force removal of permanent windows such as the player's own inventory. Used internally.
*/ */
public function removeAllViewers(bool $force = false) : void{ public function removeAllViewers(bool $force = false) : void{

View File

@ -244,6 +244,7 @@ interface Inventory{
* Returns whether the specified slot exists in the inventory. * Returns whether the specified slot exists in the inventory.
* *
* @param int $slot * @param int $slot
*
* @return bool * @return bool
*/ */
public function slotExists(int $slot) : bool; public function slotExists(int $slot) : bool;

View File

@ -87,6 +87,7 @@ class PlayerInventory extends BaseInventory{
/** /**
* @param int $slot * @param int $slot
*
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
private function throwIfNotHotbarSlot(int $slot){ private function throwIfNotHotbarSlot(int $slot){
@ -99,6 +100,7 @@ class PlayerInventory extends BaseInventory{
* Returns the item in the specified hotbar slot. * Returns the item in the specified hotbar slot.
* *
* @param int $hotbarSlot * @param int $hotbarSlot
*
* @return Item * @return Item
* *
* @throws \InvalidArgumentException if the hotbar slot index is out of range * @throws \InvalidArgumentException if the hotbar slot index is out of range
@ -159,6 +161,7 @@ class PlayerInventory extends BaseInventory{
/** /**
* Sends the currently-held item to specified targets. * Sends the currently-held item to specified targets.
*
* @param Player|Player[] $target * @param Player|Player[] $target
*/ */
public function sendHeldItem($target){ public function sendHeldItem($target){

View File

@ -54,6 +54,7 @@ class DropItemAction extends InventoryAction{
* Drops the target item in front of the player. * Drops the target item in front of the player.
* *
* @param Player $source * @param Player $source
*
* @return bool * @return bool
*/ */
public function execute(Player $source) : bool{ public function execute(Player $source) : bool{

View File

@ -53,6 +53,7 @@ abstract class Armor extends Durable{
/** /**
* Sets the dyed colour of this armour piece. This generally only applies to leather armour. * Sets the dyed colour of this armour piece. This generally only applies to leather armour.
*
* @param Color $color * @param Color $color
*/ */
public function setCustomColor(Color $color) : void{ public function setCustomColor(Color $color) : void{

View File

@ -79,7 +79,7 @@ class Bow extends Tool{
$entity->setBaseDamage($entity->getBaseDamage() + (($powerLevel + 1) / 2)); $entity->setBaseDamage($entity->getBaseDamage() + (($powerLevel + 1) / 2));
} }
if($this->hasEnchantment(Enchantment::FLAME)){ if($this->hasEnchantment(Enchantment::FLAME)){
$entity->setOnFire($entity->getFireTicks() * 20 + 100); $entity->setOnFire(intdiv($entity->getFireTicks(), 20) + 100);
} }
$ev = new EntityShootBowEvent($player, $this, $entity, $force); $ev = new EntityShootBowEvent($player, $this, $entity, $force);

View File

@ -57,8 +57,8 @@ class Bucket extends Item implements Consumable{
if($blockClicked instanceof Liquid and $blockClicked->getDamage() === 0){ if($blockClicked instanceof Liquid and $blockClicked->getDamage() === 0){
$stack = clone $this; $stack = clone $this;
$resultItem = $stack->pop(); $stack->pop();
$resultItem->setDamage($blockClicked->getFlowingForm()->getId()); $resultItem = ItemFactory::get(Item::BUCKET, $blockClicked->getFlowingForm()->getId());
$player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem)); $player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem));
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$player->getLevel()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true); $player->getLevel()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true);
@ -80,9 +80,7 @@ class Bucket extends Item implements Consumable{
} }
} }
}elseif($resultBlock instanceof Liquid and $blockReplace->canBeReplaced()){ }elseif($resultBlock instanceof Liquid and $blockReplace->canBeReplaced()){
$resultItem = clone $this; $player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, ItemFactory::get(Item::BUCKET)));
$resultItem->setDamage(0);
$player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, $resultItem));
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$player->getLevel()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true); $player->getLevel()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true);
$player->getLevel()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound()); $player->getLevel()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound());

View File

@ -47,17 +47,17 @@ class ChorusFruit extends Food{
} }
public function onConsume(Living $consumer){ public function onConsume(Living $consumer){
$level = $consumer->getLevel();
assert($level !== null);
$minX = $consumer->getFloorX() - 8; $minX = $consumer->getFloorX() - 8;
$minY = $consumer->getFloorY() - 8; $minY = min($consumer->getFloorY(), $consumer->getLevel()->getWorldHeight()) - 8;
$minZ = $consumer->getFloorZ() - 8; $minZ = $consumer->getFloorZ() - 8;
$maxX = $minX + 16; $maxX = $minX + 16;
$maxY = $minY + 16; $maxY = $minY + 16;
$maxZ = $minZ + 16; $maxZ = $minZ + 16;
$level = $consumer->getLevel();
assert($level !== null);
for($attempts = 0; $attempts < 16; ++$attempts){ for($attempts = 0; $attempts < 16; ++$attempts){
$x = mt_rand($minX, $maxX); $x = mt_rand($minX, $maxX);
$y = mt_rand($minY, $maxY); $y = mt_rand($minY, $maxY);

View File

@ -38,6 +38,7 @@ abstract class Durable extends Item{
/** /**
* Sets whether the item will take damage when used. * Sets whether the item will take damage when used.
*
* @param bool $value * @param bool $value
*/ */
public function setUnbreakable(bool $value = true){ public function setUnbreakable(bool $value = true){
@ -46,6 +47,7 @@ abstract class Durable extends Item{
/** /**
* Applies damage to the item. * Applies damage to the item.
*
* @param int $amount * @param int $amount
* *
* @return bool if any damage was applied to the item * @return bool if any damage was applied to the item

View File

@ -523,6 +523,7 @@ class Item implements ItemIds, \JsonSerializable{
/** /**
* @param string $name * @param string $name
*
* @return NamedTag|null * @return NamedTag|null
*/ */
public function getNamedTagEntry(string $name) : ?NamedTag{ public function getNamedTagEntry(string $name) : ?NamedTag{
@ -557,6 +558,7 @@ class Item implements ItemIds, \JsonSerializable{
/** /**
* Sets the Item's NBT from the supplied CompoundTag object. * Sets the Item's NBT from the supplied CompoundTag object.
*
* @param CompoundTag $tag * @param CompoundTag $tag
* *
* @return Item * @return Item
@ -589,6 +591,7 @@ class Item implements ItemIds, \JsonSerializable{
/** /**
* @param int $count * @param int $count
*
* @return Item * @return Item
*/ */
public function setCount(int $count) : Item{ public function setCount(int $count) : Item{
@ -669,6 +672,7 @@ class Item implements ItemIds, \JsonSerializable{
/** /**
* @param int $meta * @param int $meta
*
* @return Item * @return Item
*/ */
public function setDamage(int $meta) : Item{ public function setDamage(int $meta) : Item{
@ -779,6 +783,7 @@ class Item implements ItemIds, \JsonSerializable{
* Returns whether the item was changed, for example count decrease or durability change. * Returns whether the item was changed, for example count decrease or durability change.
* *
* @param Player $player * @param Player $player
*
* @return bool * @return bool
*/ */
public function onReleaseUsing(Player $player) : bool{ public function onReleaseUsing(Player $player) : bool{
@ -844,6 +849,7 @@ class Item implements ItemIds, \JsonSerializable{
/** /**
* Returns whether the specified item stack has the same ID, damage, NBT and count as this item stack. * Returns whether the specified item stack has the same ID, damage, NBT and count as this item stack.
*
* @param Item $other * @param Item $other
* *
* @return bool * @return bool
@ -888,6 +894,7 @@ class Item implements ItemIds, \JsonSerializable{
* Returns an Item from properties created in an array by {@link Item#jsonSerialize} * Returns an Item from properties created in an array by {@link Item#jsonSerialize}
* *
* @param array $data * @param array $data
*
* @return Item * @return Item
*/ */
final public static function jsonDeserialize(array $data) : Item{ final public static function jsonDeserialize(array $data) : Item{
@ -956,7 +963,12 @@ class Item implements ItemIds, \JsonSerializable{
if($idTag instanceof ShortTag){ if($idTag instanceof ShortTag){
$item = ItemFactory::get($idTag->getValue(), $meta, $count); $item = ItemFactory::get($idTag->getValue(), $meta, $count);
}elseif($idTag instanceof StringTag){ //PC item save format }elseif($idTag instanceof StringTag){ //PC item save format
try{
$item = ItemFactory::fromString($idTag->getValue()); $item = ItemFactory::fromString($idTag->getValue());
}catch(\InvalidArgumentException $e){
//TODO: improve error handling
return ItemFactory::get(Item::AIR, 0, 0);
}
$item->setDamage($meta); $item->setDamage($meta);
$item->setCount($count); $item->setCount($count);
}else{ }else{

View File

@ -71,9 +71,8 @@ class Potion extends Item implements Consumable{
* Returns a list of effects applied by potions with the specified ID. * Returns a list of effects applied by potions with the specified ID.
* *
* @param int $id * @param int $id
* @return EffectInstance[]
* *
* @throws \InvalidArgumentException if the potion type is unknown * @return EffectInstance[]
*/ */
public static function getPotionEffectsById(int $id) : array{ public static function getPotionEffectsById(int $id) : array{
switch($id){ switch($id){
@ -213,7 +212,7 @@ class Potion extends Item implements Consumable{
]; ];
} }
throw new \InvalidArgumentException("Unknown potion type $id"); return [];
} }
public function __construct(int $meta = 0){ public function __construct(int $meta = 0){

View File

@ -27,8 +27,8 @@ use pocketmine\entity\Entity;
use pocketmine\entity\projectile\Projectile; use pocketmine\entity\projectile\Projectile;
use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\event\entity\ProjectileLaunchEvent;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\Player; use pocketmine\Player;
abstract class ProjectileItem extends Item{ abstract class ProjectileItem extends Item{

View File

@ -69,6 +69,7 @@ class EnchantmentInstance{
/** /**
* Sets the level of the enchantment. * Sets the level of the enchantment.
*
* @param int $level * @param int $level
* *
* @return $this * @return $this

View File

@ -62,6 +62,7 @@ class ProtectionEnchantment extends Enchantment{
/** /**
* Returns the base EPF this enchantment type offers for the given enchantment level. * Returns the base EPF this enchantment type offers for the given enchantment level.
*
* @param int $level * @param int $level
* *
* @return int * @return int
@ -72,6 +73,7 @@ class ProtectionEnchantment extends Enchantment{
/** /**
* Returns whether this enchantment type offers protection from the specified damage source's cause. * Returns whether this enchantment type offers protection from the specified damage source's cause.
*
* @param EntityDamageEvent $event * @param EntityDamageEvent $event
* *
* @return bool * @return bool

View File

@ -284,6 +284,7 @@ class Level implements ChunkManager, Metadatable{
/** /**
* @param string $str * @param string $str
*
* @return int * @return int
*/ */
public static function getDifficultyFromString(string $str) : int{ public static function getDifficultyFromString(string $str) : int{
@ -872,16 +873,12 @@ class Level implements ChunkManager, Metadatable{
$pk->z = $b->z; $pk->z = $b->z;
if($b instanceof Block){ if($b instanceof Block){
$blockId = $b->getId(); $pk->blockRuntimeId = $b->getRuntimeId();
$blockData = $b->getDamage();
}else{ }else{
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z); $fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
$blockId = $fullBlock >> 4; $pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
$blockData = $fullBlock & 0xf;
} }
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($blockId, $blockData);
$pk->flags = $first ? $flags : UpdateBlockPacket::FLAG_NONE; $pk->flags = $first ? $flags : UpdateBlockPacket::FLAG_NONE;
$packets[] = $pk; $packets[] = $pk;
@ -898,16 +895,12 @@ class Level implements ChunkManager, Metadatable{
$pk->z = $b->z; $pk->z = $b->z;
if($b instanceof Block){ if($b instanceof Block){
$blockId = $b->getId(); $pk->blockRuntimeId = $b->getRuntimeId();
$blockData = $b->getDamage();
}else{ }else{
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z); $fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
$blockId = $fullBlock >> 4; $pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
$blockData = $fullBlock & 0xf;
} }
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($blockId, $blockData);
$pk->flags = $flags; $pk->flags = $flags;
$packets[] = $pk; $packets[] = $pk;
@ -979,13 +972,20 @@ class Level implements ChunkManager, Metadatable{
foreach($this->chunkTickList as $index => $loaders){ foreach($this->chunkTickList as $index => $loaders){
Level::getXZ($index, $chunkX, $chunkZ); Level::getXZ($index, $chunkX, $chunkZ);
if(($chunk = $this->chunks[$index] ?? null) === null){ for($cx = -1; $cx <= 1; ++$cx){
for($cz = -1; $cz <= 1; ++$cz){
if(!isset($this->chunks[Level::chunkHash($chunkX + $cx, $chunkZ + $cz)])){
unset($this->chunkTickList[$index]); unset($this->chunkTickList[$index]);
continue; goto skip_to_next; //no "continue 3" thanks!
}elseif($loaders <= 0){ }
}
}
if($loaders <= 0){
unset($this->chunkTickList[$index]); unset($this->chunkTickList[$index]);
} }
$chunk = $this->chunks[$index];
foreach($chunk->getEntities() as $entity){ foreach($chunk->getEntities() as $entity){
$entity->scheduleUpdate(); $entity->scheduleUpdate();
} }
@ -1014,6 +1014,8 @@ class Level implements ChunkManager, Metadatable{
} }
} }
} }
skip_to_next: //dummy label to break out of nested loops
} }
if($this->clearChunksOnTick){ if($this->clearChunksOnTick){
@ -1776,7 +1778,7 @@ class Level implements ChunkManager, Metadatable{
} }
if($player !== null){ if($player !== null){
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, $blockClicked->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK); $ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK);
if($this->checkSpawnProtection($player, $blockClicked)){ if($this->checkSpawnProtection($player, $blockClicked)){
$ev->setCancelled(); //set it to cancelled so plugins can bypass this $ev->setCancelled(); //set it to cancelled so plugins can bypass this
} }
@ -1864,7 +1866,7 @@ class Level implements ChunkManager, Metadatable{
} }
if($playSound){ if($playSound){
$this->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, 1, BlockFactory::toStaticRuntimeId($hand->getId(), $hand->getDamage())); $this->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, 1, $hand->getRuntimeId());
} }
$item->pop(); $item->pop();

View File

@ -51,7 +51,7 @@ class Chunk{
/** @var bool */ /** @var bool */
protected $isInit = false; protected $isInit = false;
/** @var bool*/ /** @var bool */
protected $lightPopulated = false; protected $lightPopulated = false;
/** @var bool */ /** @var bool */
protected $terrainGenerated = false; protected $terrainGenerated = false;
@ -366,6 +366,7 @@ class Chunk{
/** /**
* Returns the heightmap value at the specified X/Z chunk block coordinates * Returns the heightmap value at the specified X/Z chunk block coordinates
*
* @param int $x 0-15 * @param int $x 0-15
* @param int $z 0-15 * @param int $z 0-15
* @param int $value * @param int $value
@ -465,6 +466,7 @@ class Chunk{
/** /**
* Returns a column of block IDs from bottom to top at the specified X/Z chunk block coordinates. * Returns a column of block IDs from bottom to top at the specified X/Z chunk block coordinates.
*
* @param int $x 0-15 * @param int $x 0-15
* @param int $z 0-15 * @param int $z 0-15
* *
@ -475,11 +477,13 @@ class Chunk{
foreach($this->subChunks as $subChunk){ foreach($this->subChunks as $subChunk){
$result .= $subChunk->getBlockIdColumn($x, $z); $result .= $subChunk->getBlockIdColumn($x, $z);
} }
return $result; return $result;
} }
/** /**
* Returns a column of block meta values from bottom to top at the specified X/Z chunk block coordinates. * Returns a column of block meta values from bottom to top at the specified X/Z chunk block coordinates.
*
* @param int $x 0-15 * @param int $x 0-15
* @param int $z 0-15 * @param int $z 0-15
* *
@ -495,6 +499,7 @@ class Chunk{
/** /**
* Returns a column of sky light values from bottom to top at the specified X/Z chunk block coordinates. * Returns a column of sky light values from bottom to top at the specified X/Z chunk block coordinates.
*
* @param int $x 0-15 * @param int $x 0-15
* @param int $z 0-15 * @param int $z 0-15
* *
@ -510,6 +515,7 @@ class Chunk{
/** /**
* Returns a column of block light values from bottom to top at the specified X/Z chunk block coordinates. * Returns a column of block light values from bottom to top at the specified X/Z chunk block coordinates.
*
* @param int $x 0-15 * @param int $x 0-15
* @param int $z 0-15 * @param int $z 0-15
* *

View File

@ -27,6 +27,7 @@ interface SubChunkInterface{
/** /**
* @param bool $checkLight * @param bool $checkLight
*
* @return bool * @return bool
*/ */
public function isEmpty(bool $checkLight = true) : bool; public function isEmpty(bool $checkLight = true) : bool;

View File

@ -148,6 +148,7 @@ interface LevelProvider{
/** /**
* Sets the world difficulty. * Sets the world difficulty.
*
* @param int $difficulty * @param int $difficulty
*/ */
public function setDifficulty(int $difficulty); public function setDifficulty(int $difficulty);

View File

@ -35,7 +35,6 @@ class RegionLoader{
public const MAX_SECTOR_LENGTH = 256 << 12; //256 sectors, (1 MiB) public const MAX_SECTOR_LENGTH = 256 << 12; //256 sectors, (1 MiB)
public const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps public const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps
public const MAX_REGION_FILE_SIZE = 32 * 32 * self::MAX_SECTOR_LENGTH + self::REGION_HEADER_LENGTH; //32 * 32 1MiB chunks + header size
public static $COMPRESSION_LEVEL = 7; public static $COMPRESSION_LEVEL = 7;
@ -64,14 +63,9 @@ class RegionLoader{
$exists = file_exists($this->filePath); $exists = file_exists($this->filePath);
if(!$exists){ if(!$exists){
touch($this->filePath); touch($this->filePath);
}else{ }elseif(filesize($this->filePath) % 4096 !== 0){
$fileSize = filesize($this->filePath);
if($fileSize > self::MAX_REGION_FILE_SIZE){
throw new CorruptedRegionException("Corrupted oversized region file found, should be a maximum of " . self::MAX_REGION_FILE_SIZE . " bytes, got " . $fileSize . " bytes");
}elseif($fileSize % 4096 !== 0){
throw new CorruptedRegionException("Region file should be padded to a multiple of 4KiB"); throw new CorruptedRegionException("Region file should be padded to a multiple of 4KiB");
} }
}
$this->filePointer = fopen($this->filePath, "r+b"); $this->filePointer = fopen($this->filePath, "r+b");
stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\level\light; namespace pocketmine\level\light;
use pocketmine\block\BlockFactory;
use pocketmine\level\format\Chunk; use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\scheduler\AsyncTask; use pocketmine\scheduler\AsyncTask;
@ -39,6 +40,9 @@ class LightPopulationTask extends AsyncTask{
} }
public function onRun(){ public function onRun(){
if(!BlockFactory::isInit()){
BlockFactory::init();
}
/** @var Chunk $chunk */ /** @var Chunk $chunk */
$chunk = Chunk::fastDeserialize($this->chunk); $chunk = Chunk::fastDeserialize($this->chunk);

View File

@ -54,14 +54,6 @@ abstract class LightUpdate{
$this->subChunkHandler = new SubChunkIteratorManager($this->level); $this->subChunkHandler = new SubChunkIteratorManager($this->level);
} }
public function addSpreadNode(int $x, int $y, int $z){
$this->spreadQueue->enqueue([$x, $y, $z]);
}
public function addRemoveNode(int $x, int $y, int $z, int $oldLight){
$this->spreadQueue->enqueue([$x, $y, $z, $oldLight]);
}
abstract protected function getLight(int $x, int $y, int $z) : int; abstract protected function getLight(int $x, int $y, int $z) : int;
abstract protected function setLight(int $x, int $y, int $z, int $level); abstract protected function setLight(int $x, int $y, int $z, int $level);
@ -114,6 +106,8 @@ abstract class LightUpdate{
while(!$this->spreadQueue->isEmpty()){ while(!$this->spreadQueue->isEmpty()){
list($x, $y, $z) = $this->spreadQueue->dequeue(); list($x, $y, $z) = $this->spreadQueue->dequeue();
unset($this->spreadVisited[Level::blockHash($x, $y, $z)]);
if(!$this->subChunkHandler->moveTo($x, $y, $z)){ if(!$this->subChunkHandler->moveTo($x, $y, $z)){
continue; continue;
} }

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\level\particle; namespace pocketmine\level\particle;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\BlockFactory;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelEventPacket; use pocketmine\network\mcpe\protocol\LevelEventPacket;
@ -35,7 +34,7 @@ class DestroyBlockParticle extends Particle{
public function __construct(Vector3 $pos, Block $b){ public function __construct(Vector3 $pos, Block $b){
parent::__construct($pos->x, $pos->y, $pos->z); parent::__construct($pos->x, $pos->y, $pos->z);
$this->data = BlockFactory::toStaticRuntimeId($b->getId(), $b->getDamage()); $this->data = $b->getRuntimeId();
} }
public function encode(){ public function encode(){

View File

@ -24,11 +24,10 @@ declare(strict_types=1);
namespace pocketmine\level\particle; namespace pocketmine\level\particle;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\BlockFactory;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
class TerrainParticle extends GenericParticle{ class TerrainParticle extends GenericParticle{
public function __construct(Vector3 $pos, Block $b){ public function __construct(Vector3 $pos, Block $b){
parent::__construct($pos, Particle::TYPE_TERRAIN, BlockFactory::toStaticRuntimeId($b->getId(), $b->getDamage())); parent::__construct($pos, Particle::TYPE_TERRAIN, $b->getRuntimeId());
} }
} }

View File

@ -79,6 +79,10 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
} }
public function handleDataPacket(DataPacket $packet){ public function handleDataPacket(DataPacket $packet){
if(!$this->player->isConnected()){
return;
}
$timings = Timings::getReceiveDataPacketTimings($packet); $timings = Timings::getReceiveDataPacketTimings($packet);
$timings->startTiming(); $timings->startTiming();

View File

@ -76,10 +76,6 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
$this->server = $server; $this->server = $server;
$this->sleeper = new SleeperNotifier(); $this->sleeper = new SleeperNotifier();
$server->getTickSleeper()->addNotifier($this->sleeper, function() : void{
$this->server->getNetwork()->processInterface($this);
});
$this->rakLib = new RakLibServer( $this->rakLib = new RakLibServer(
$this->server->getLogger(), $this->server->getLogger(),
\pocketmine\COMPOSER_AUTOLOADER_PATH, \pocketmine\COMPOSER_AUTOLOADER_PATH,
@ -92,6 +88,9 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
} }
public function start(){ public function start(){
$this->server->getTickSleeper()->addNotifier($this->sleeper, function() : void{
$this->server->getNetwork()->processInterface($this);
});
$this->rakLib->start(PTHREADS_INHERIT_CONSTANTS | PTHREADS_INHERIT_INI); //HACK: MainLogger needs INI and constants $this->rakLib->start(PTHREADS_INHERIT_CONSTANTS | PTHREADS_INHERIT_INI); //HACK: MainLogger needs INI and constants
} }

View File

@ -37,6 +37,8 @@ class MoveEntityDeltaPacket extends DataPacket{
public const FLAG_HAS_ROT_Y = 0x10; public const FLAG_HAS_ROT_Y = 0x10;
public const FLAG_HAS_ROT_Z = 0x20; public const FLAG_HAS_ROT_Z = 0x20;
/** @var int */
public $entityRuntimeId;
/** @var int */ /** @var int */
public $flags; public $flags;
/** @var int */ /** @var int */
@ -67,6 +69,7 @@ class MoveEntityDeltaPacket extends DataPacket{
} }
protected function decodePayload(){ protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->flags = $this->getByte(); $this->flags = $this->getByte();
$this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X); $this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X);
$this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y); $this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y);
@ -89,6 +92,7 @@ class MoveEntityDeltaPacket extends DataPacket{
} }
protected function encodePayload(){ protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putByte($this->flags); $this->putByte($this->flags);
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff); $this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff);
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff); $this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff);

View File

@ -159,6 +159,7 @@ class PacketPool{
/** /**
* @param int $pid * @param int $pid
*
* @return DataPacket * @return DataPacket
*/ */
public static function getPacketById(int $pid) : DataPacket{ public static function getPacketById(int $pid) : DataPacket{
@ -167,6 +168,7 @@ class PacketPool{
/** /**
* @param string $buffer * @param string $buffer
*
* @return DataPacket * @return DataPacket
*/ */
public static function getPacket(string $buffer) : DataPacket{ public static function getPacket(string $buffer) : DataPacket{

View File

@ -42,20 +42,20 @@ class ResourcePackStackPacket extends DataPacket{
public $resourcePackStack = []; public $resourcePackStack = [];
protected function decodePayload(){ protected function decodePayload(){
/*$this->mustAccept = $this->getBool(); $this->mustAccept = $this->getBool();
$behaviorPackCount = $this->getUnsignedVarInt(); $behaviorPackCount = $this->getUnsignedVarInt();
while($behaviorPackCount-- > 0){ while($behaviorPackCount-- > 0){
$packId = $this->getString(); $this->getString();
$version = $this->getString(); $this->getString();
$this->behaviorPackStack[] = new ResourcePackInfoEntry($packId, $version); $this->getString();
} }
$resourcePackCount = $this->getUnsignedVarInt(); $resourcePackCount = $this->getUnsignedVarInt();
while($resourcePackCount-- > 0){ while($resourcePackCount-- > 0){
$packId = $this->getString(); $this->getString();
$version = $this->getString(); $this->getString();
$this->resourcePackStack[] = new ResourcePackInfoEntry($packId, $version); $this->getString();
}*/ }
} }
protected function encodePayload(){ protected function encodePayload(){

View File

@ -40,24 +40,26 @@ class ResourcePacksInfoPacket extends DataPacket{
public $resourcePackEntries = []; public $resourcePackEntries = [];
protected function decodePayload(){ protected function decodePayload(){
/*$this->mustAccept = $this->getBool(); $this->mustAccept = $this->getBool();
$behaviorPackCount = $this->getLShort(); $behaviorPackCount = $this->getLShort();
while($behaviorPackCount-- > 0){ while($behaviorPackCount-- > 0){
$id = $this->getString(); $this->getString();
$version = $this->getString(); $this->getString();
$size = $this->getLLong(); $this->getLLong();
$this->behaviorPackEntries[] = new ResourcePackInfoEntry($id, $version, $size); $this->getString();
$this->getString();
$this->getString(); $this->getString();
} }
$resourcePackCount = $this->getLShort(); $resourcePackCount = $this->getLShort();
while($resourcePackCount-- > 0){ while($resourcePackCount-- > 0){
$id = $this->getString();
$version = $this->getString();
$size = $this->getLLong();
$this->resourcePackEntries[] = new ResourcePackInfoEntry($id, $version, $size);
$this->getString(); $this->getString();
}*/ $this->getString();
$this->getLLong();
$this->getString();
$this->getString();
$this->getString();
}
} }
protected function encodePayload(){ protected function encodePayload(){

View File

@ -64,7 +64,7 @@ class StartGamePacket extends DataPacket{
public $difficulty; public $difficulty;
/** @var int */ /** @var int */
public $spawnX; public $spawnX;
/** @var int*/ /** @var int */
public $spawnY; public $spawnY;
/** @var int */ /** @var int */
public $spawnZ; public $spawnZ;

View File

@ -92,6 +92,7 @@ class NetworkInventoryAction{
/** /**
* @param InventoryTransactionPacket $packet * @param InventoryTransactionPacket $packet
*
* @return $this * @return $this
*/ */
public function read(InventoryTransactionPacket $packet){ public function read(InventoryTransactionPacket $packet){

View File

@ -85,7 +85,6 @@ class RCON{
public function stop(){ public function stop(){
$this->instance->close(); $this->instance->close();
socket_write($this->ipcMainSocket, "\x00"); //make select() return socket_write($this->ipcMainSocket, "\x00"); //make select() return
Server::microSleep(50000);
$this->instance->quit(); $this->instance->quit();
@socket_close($this->socket); @socket_close($this->socket);

View File

@ -119,6 +119,7 @@ class BanEntry{
* @link https://bugs.php.net/bug.php?id=75992 * @link https://bugs.php.net/bug.php?id=75992
* *
* @param \DateTime $dateTime * @param \DateTime $dateTime
*
* @throws \RuntimeException if the argument can't be parsed from a formatted date string * @throws \RuntimeException if the argument can't be parsed from a formatted date string
*/ */
private static function validateDate(\DateTime $dateTime) : void{ private static function validateDate(\DateTime $dateTime) : void{

View File

@ -166,9 +166,7 @@ class PermissibleBase implements Permissible{
public function clearPermissions(){ public function clearPermissions(){
$permManager = PermissionManager::getInstance(); $permManager = PermissionManager::getInstance();
foreach(array_keys($this->permissions) as $name){ $permManager->unsubscribeFromAllPermissions($this->parent ?? $this);
$permManager->unsubscribeFromPermission($name, $this->parent ?? $this);
}
$permManager->unsubscribeFromDefaultPerms(false, $this->parent ?? $this); $permManager->unsubscribeFromDefaultPerms(false, $this->parent ?? $this);
$permManager->unsubscribeFromDefaultPerms(true, $this->parent ?? $this); $permManager->unsubscribeFromDefaultPerms(true, $this->parent ?? $this);

View File

@ -160,6 +160,18 @@ class PermissionManager{
} }
} }
/**
* @param Permissible $permissible
*/
public function unsubscribeFromAllPermissions(Permissible $permissible) : void{
foreach($this->permSubs as $permission => &$subs){
unset($subs[spl_object_hash($permissible)]);
if(empty($subs)){
unset($this->permSubs[$permission]);
}
}
}
/** /**
* @param string $permission * @param string $permission
* *

View File

@ -55,6 +55,12 @@ interface Plugin extends CommandExecutor{
public function isEnabled() : bool; public function isEnabled() : bool;
/** /**
* Called by the plugin manager when the plugin is enabled or disabled to inform the plugin of its enabled state.
*
* @internal This is intended for core use only and should not be used by plugins
* @see PluginManager::enablePlugin()
* @see PluginManager::disablePlugin()
*
* @param bool $enabled * @param bool $enabled
*/ */
public function setEnabled(bool $enabled = true) : void; public function setEnabled(bool $enabled = true) : void;

View File

@ -93,6 +93,12 @@ abstract class PluginBase implements Plugin{
} }
/** /**
* Called by the plugin manager when the plugin is enabled or disabled to inform the plugin of its enabled state.
*
* @internal This is intended for core use only and should not be used by plugins
* @see PluginManager::enablePlugin()
* @see PluginManager::disablePlugin()
*
* @param bool $enabled * @param bool $enabled
*/ */
final public function setEnabled(bool $enabled = true) : void{ final public function setEnabled(bool $enabled = true) : void{

View File

@ -359,6 +359,7 @@ class PluginManager{
* Returns whether a specified API version string is considered compatible with the server's API version. * Returns whether a specified API version string is considered compatible with the server's API version.
* *
* @param string ...$versions * @param string ...$versions
*
* @return bool * @return bool
*/ */
public function isCompatibleApi(string ...$versions) : bool{ public function isCompatibleApi(string ...$versions) : bool{
@ -475,6 +476,16 @@ class PluginManager{
PermissionManager::getInstance()->unsubscribeFromPermission($permission, $permissible); PermissionManager::getInstance()->unsubscribeFromPermission($permission, $permissible);
} }
/**
* @deprecated
* @see PermissionManager::unsubscribeFromAllPermissions()
*
* @param Permissible $permissible
*/
public function unsubscribeFromAllPermissions(Permissible $permissible) : void{
PermissionManager::getInstance()->unsubscribeFromAllPermissions($permissible);
}
/** /**
* @deprecated * @deprecated
* @see PermissionManager::getPermissionSubscriptions() * @see PermissionManager::getPermissionSubscriptions()

View File

@ -138,6 +138,7 @@ class ResourcePackManager{
* Returns the resource pack matching the specified UUID string, or null if the ID was not recognized. * Returns the resource pack matching the specified UUID string, or null if the ID was not recognized.
* *
* @param string $id * @param string $id
*
* @return ResourcePack|null * @return ResourcePack|null
*/ */
public function getPackById(string $id){ public function getPackById(string $id){

View File

@ -32,6 +32,7 @@ class ZippedResourcePack implements ResourcePack{
* TODO: add more manifest validation * TODO: add more manifest validation
* *
* @param \stdClass $manifest * @param \stdClass $manifest
*
* @return bool * @return bool
*/ */
public static function verifyManifest(\stdClass $manifest) : bool{ public static function verifyManifest(\stdClass $manifest) : bool{

View File

@ -289,12 +289,20 @@ class AsyncPool{
*/ */
public function collectTasks() : void{ public function collectTasks() : void{
foreach($this->tasks as $task){ foreach($this->tasks as $task){
if(!$task->isGarbage()){
$task->checkProgressUpdates($this->server); $task->checkProgressUpdates($this->server);
}
if($task->isGarbage() and !$task->isRunning() and !$task->isCrashed()){ if($task->isGarbage() and !$task->isRunning() and !$task->isCrashed()){
if(!$task->hasCancelledRun()){ if(!$task->hasCancelledRun()){
try{ try{
/*
* It's possible for a task to submit a progress update and then finish before the progress
* update is detected by the parent thread, so here we consume any missed updates.
*
* When this happens, it's possible for a progress update to arrive between the previous
* checkProgressUpdates() call and the next isGarbage() call, causing progress updates to be
* lost. Thus, it's necessary to do one last check here to make sure all progress updates have
* been consumed before completing.
*/
$task->checkProgressUpdates($this->server);
$task->onCompletion($this->server); $task->onCompletion($this->server);
if($task->removeDanglingStoredObjects()){ if($task->removeDanglingStoredObjects()){
$this->logger->notice("AsyncTask " . get_class($task) . " stored local complex data but did not remove them after completion"); $this->logger->notice("AsyncTask " . get_class($task) . " stored local complex data but did not remove them after completion");

View File

@ -129,6 +129,7 @@ abstract class AsyncTask extends Collectable{
* @see AsyncWorker::getFromThreadStore() * @see AsyncWorker::getFromThreadStore()
* *
* @param string $identifier * @param string $identifier
*
* @return mixed * @return mixed
*/ */
public function getFromThreadStore(string $identifier){ public function getFromThreadStore(string $identifier){

View File

@ -102,6 +102,7 @@ class AsyncWorker extends Worker{
* Objects stored in this storage may ONLY be retrieved while the task is running. * Objects stored in this storage may ONLY be retrieved while the task is running.
* *
* @param string $identifier * @param string $identifier
*
* @return mixed * @return mixed
*/ */
public function getFromThreadStore(string $identifier){ public function getFromThreadStore(string $identifier){

View File

@ -101,6 +101,7 @@ class AutoUpdater{
/** /**
* Shows a warning to a player to tell them there is an update available * Shows a warning to a player to tell them there is an update available
*
* @param Player $player * @param Player $player
*/ */
public function showPlayerUpdate(Player $player){ public function showPlayerUpdate(Player $player){

View File

@ -47,6 +47,7 @@ class Color{
/** /**
* Sets the alpha (opacity) value of this colour, lower = more transparent * Sets the alpha (opacity) value of this colour, lower = more transparent
*
* @param int $a * @param int $a
*/ */
public function setA(int $a){ public function setA(int $a){
@ -63,6 +64,7 @@ class Color{
/** /**
* Sets the red value of this colour. * Sets the red value of this colour.
*
* @param int $r * @param int $r
*/ */
public function setR(int $r){ public function setR(int $r){
@ -79,6 +81,7 @@ class Color{
/** /**
* Sets the green value of this colour. * Sets the green value of this colour.
*
* @param int $g * @param int $g
*/ */
public function setG(int $g){ public function setG(int $g){
@ -95,6 +98,7 @@ class Color{
/** /**
* Sets the blue value of this colour. * Sets the blue value of this colour.
*
* @param int $b * @param int $b
*/ */
public function setB(int $b){ public function setB(int $b){
@ -105,6 +109,7 @@ class Color{
* Mixes the supplied list of colours together to produce a result colour. * Mixes the supplied list of colours together to produce a result colour.
* *
* @param Color ...$colors * @param Color ...$colors
*
* @return Color * @return Color
*/ */
public static function mix(Color ...$colors) : Color{ public static function mix(Color ...$colors) : Color{
@ -127,6 +132,7 @@ class Color{
/** /**
* Returns a Color from the supplied RGB colour code (24-bit) * Returns a Color from the supplied RGB colour code (24-bit)
*
* @param int $code * @param int $code
* *
* @return Color * @return Color

View File

@ -232,6 +232,7 @@ class Config{
* Sets the options for the JSON encoding when saving * Sets the options for the JSON encoding when saving
* *
* @param int $options * @param int $options
*
* @return Config $this * @return Config $this
* @throws \RuntimeException if the Config is not in JSON * @throws \RuntimeException if the Config is not in JSON
* @see json_encode * @see json_encode
@ -250,6 +251,7 @@ class Config{
* Enables the given option in addition to the currently set JSON options * Enables the given option in addition to the currently set JSON options
* *
* @param int $option * @param int $option
*
* @return Config $this * @return Config $this
* @throws \RuntimeException if the Config is not in JSON * @throws \RuntimeException if the Config is not in JSON
* @see json_encode * @see json_encode
@ -268,6 +270,7 @@ class Config{
* Disables the given option for the JSON encoding when saving * Disables the given option for the JSON encoding when saving
* *
* @param int $option * @param int $option
*
* @return Config $this * @return Config $this
* @throws \RuntimeException if the Config is not in JSON * @throws \RuntimeException if the Config is not in JSON
* @see json_encode * @see json_encode

View File

@ -47,6 +47,7 @@ class UUID{
* *
* @param string $uuid * @param string $uuid
* @param int $version * @param int $version
*
* @return UUID * @return UUID
*/ */
public static function fromString(string $uuid, int $version = null) : UUID{ public static function fromString(string $uuid, int $version = null) : UUID{
@ -58,6 +59,7 @@ class UUID{
* *
* @param string $uuid * @param string $uuid
* @param int $version * @param int $version
*
* @return UUID * @return UUID
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
@ -74,6 +76,7 @@ class UUID{
* Creates an UUIDv3 from binary data or list of binary data * Creates an UUIDv3 from binary data or list of binary data
* *
* @param string ...$data * @param string ...$data
*
* @return UUID * @return UUID
*/ */
public static function fromData(string ...$data) : UUID{ public static function fromData(string ...$data) : UUID{

View File

@ -246,6 +246,7 @@ class Utils{
/** /**
* @param bool $recalculate * @param bool $recalculate
*
* @return int * @return int
*/ */
public static function getCoreCount(bool $recalculate = false) : int{ public static function getCoreCount(bool $recalculate = false) : int{

View File

@ -55,7 +55,7 @@ class VersionString{
$this->development = $isDevBuild; $this->development = $isDevBuild;
$this->build = $buildNumber; $this->build = $buildNumber;
preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)(?:-(.*))?$/', $this->baseVersion, $matches); preg_match('/(\d+)\.(\d+)\.(\d+)(?:-(.*))?$/', $this->baseVersion, $matches);
if(count($matches) < 4){ if(count($matches) < 4){
throw new \InvalidArgumentException("Invalid base version \"$baseVersion\", should contain at least 3 version digits"); throw new \InvalidArgumentException("Invalid base version \"$baseVersion\", should contain at least 3 version digits");
} }

View File

@ -15,7 +15,7 @@ if exist PocketMine-MP.phar (
if exist src\pocketmine\PocketMine.php ( if exist src\pocketmine\PocketMine.php (
set POCKETMINE_FILE=src\pocketmine\PocketMine.php set POCKETMINE_FILE=src\pocketmine\PocketMine.php
) else ( ) else (
echo "Couldn't find a valid PocketMine-MP installation" echo Couldn't find a valid PocketMine-MP installation
pause pause
exit 1 exit 1
) )

View File

@ -2,8 +2,9 @@
PHP_BINARY="php" PHP_BINARY="php"
DIR="" DIR=""
FIND="find"
while getopts "p:d:" OPTION 2> /dev/null; do while getopts "p:d:f:" OPTION 2> /dev/null; do
case ${OPTION} in case ${OPTION} in
p) p)
PHP_BINARY="$OPTARG" PHP_BINARY="$OPTARG"
@ -11,6 +12,9 @@ while getopts "p:d:" OPTION 2> /dev/null; do
d) d)
DIR="$OPTARG" DIR="$OPTARG"
;; ;;
f)
FIND="$OPTARG"
;;
esac esac
done done
@ -21,7 +25,7 @@ fi
echo Running PHP lint scans on \"$DIR\"... echo Running PHP lint scans on \"$DIR\"...
OUTPUT=`find "$DIR" -name "*.php" -print0 | xargs -0 -n1 -P4 "$PHP_BINARY" -l` OUTPUT=`$FIND "$DIR" -name "*.php" -print0 | xargs -0 -n1 -P4 "$PHP_BINARY" -l`
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo $OUTPUT | grep -v "No syntax errors" echo $OUTPUT | grep -v "No syntax errors"

View File

@ -44,7 +44,8 @@ class Main extends PluginBase implements Listener{
$this->waitingTests = [ $this->waitingTests = [
new tests\AsyncTaskMemoryLeakTest($this), new tests\AsyncTaskMemoryLeakTest($this),
new tests\AsyncTaskMainLoggerTest($this) new tests\AsyncTaskMainLoggerTest($this),
new tests\AsyncTaskPublishProgressRaceTest($this)
]; ];
} }

View File

@ -0,0 +1,68 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pmmp\TesterPlugin\tests;
use pmmp\TesterPlugin\Test;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
class AsyncTaskPublishProgressRaceTest extends Test{
public function getName() : string{
return "Verify progress updates work as expected when finishing task";
}
public function getDescription() : string{
return "Progress updates would be lost when finishing a task before its remaining progress updates were detected.";
}
public function run() : void{
//this test is racy, but it should fail often enough to be a pest if something is broken
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
private static $success = false;
public function __construct(AsyncTaskPublishProgressRaceTest $t){
$this->storeLocal($t);
}
public function onRun() : void{
$this->publishProgress("hello");
}
public function onProgressUpdate(Server $server, $progress) : void{
if($progress === "hello"){
// thread local on main thread
self::$success = true;
}
}
public function onCompletion(Server $server) : void{
/** @var AsyncTaskPublishProgressRaceTest $t */
$t = $this->fetchLocal();
$t->setResult(self::$success ? Test::RESULT_OK : Test::RESULT_FAILED);
}
});
}
}