diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index d96e6a0e7..ddc76db29 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -122,6 +122,7 @@ use pocketmine\tile\Sign; use pocketmine\tile\Spawnable; use pocketmine\tile\Tile; use pocketmine\utils\TextFormat; +use pocketmine\utils\Utils; /** * Main class that handles networking, recovery, and packet sending to the server part @@ -175,6 +176,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ public $loginData = []; protected $randomClientId; + protected $uuid; protected $lastMovement = 0; /** @var Vector3 */ @@ -235,6 +237,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ return $this->randomClientId; } + public function getUniqueId(){ + return $this->uuid; + } + public function isBanned(){ return $this->server->getNameBans()->isBanned(strtolower($this->getName())); } @@ -461,6 +467,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->viewDistance = $this->server->getViewDistance(); $this->newPosition = new Vector3(0, 0, 0); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); + + $this->uuid = Utils::dataToUUID($ip, $port, $clientID); } /** @@ -1099,6 +1107,66 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->moveToSend[$entityId] = [$entityId, $x, $y, $z, $yaw, $headYaw === null ? $yaw : $headYaw, $pitch]; } + protected function checkNearEntities($currentTick){ + foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){ + if(($currentTick - $entity->lastUpdate) > 1){ + $entity->scheduleUpdate(); + } + + if($entity instanceof Arrow and $entity->hadCollision){ + if($entity->dead !== true){ + $item = Item::get(Item::ARROW, 0, 1); + if($this->isSurvival() and !$this->inventory->canAddItem($item)){ + continue; + } + + $this->server->getPluginManager()->callEvent($ev = new InventoryPickupArrowEvent($this->inventory, $entity)); + if($ev->isCancelled()){ + continue; + } + + $pk = new TakeItemEntityPacket(); + $pk->eid = $this->getId(); + $pk->target = $entity->getId(); + Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $this->inventory->addItem(clone $item); + $entity->kill(); + } + }elseif($entity instanceof DroppedItem){ + if($entity->dead !== true and $entity->getPickupDelay() <= 0){ + $item = $entity->getItem(); + + if($item instanceof Item){ + if($this->isSurvival() and !$this->inventory->canAddItem($item)){ + continue; + } + + $this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($this->inventory, $entity)); + if($ev->isCancelled()){ + continue; + } + + switch($item->getId()){ + case Item::WOOD: + $this->awardAchievement("mineWood"); + break; + case Item::DIAMOND: + $this->awardAchievement("diamond"); + break; + } + + $pk = new TakeItemEntityPacket(); + $pk->eid = $this->getId(); + $pk->target = $entity->getId(); + Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $this->inventory->addItem(clone $item); + $entity->kill(); + } + } + } + } + } + protected function processMovement($currentTick){ if($this->dead or !$this->spawned or !($this->newPosition instanceof Vector3)){ return; @@ -1192,6 +1260,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } } + $this->checkNearEntities($currentTick); + $this->speed = $from->subtract($to); }elseif($distanceSquared == 0){ $this->speed = new Vector3(0, 0, 0); @@ -1269,64 +1339,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ ++$this->inAirTicks; } - - foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){ - if(($currentTick - $entity->lastUpdate) > 1){ - $entity->scheduleUpdate(); - } - - if($entity instanceof Arrow and $entity->hadCollision){ - if($entity->dead !== true){ - $item = Item::get(Item::ARROW, 0, 1); - if($this->isSurvival() and !$this->inventory->canAddItem($item)){ - continue; - } - - $this->server->getPluginManager()->callEvent($ev = new InventoryPickupArrowEvent($this->inventory, $entity)); - if($ev->isCancelled()){ - continue; - } - - $pk = new TakeItemEntityPacket(); - $pk->eid = $this->getId(); - $pk->target = $entity->getId(); - Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); - $this->inventory->addItem(clone $item); - $entity->kill(); - } - }elseif($entity instanceof DroppedItem){ - if($entity->dead !== true and $entity->getPickupDelay() <= 0){ - $item = $entity->getItem(); - - if($item instanceof Item){ - if($this->isSurvival() and !$this->inventory->canAddItem($item)){ - continue; - } - - $this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($this->inventory, $entity)); - if($ev->isCancelled()){ - continue; - } - - switch($item->getId()){ - case Item::WOOD: - $this->awardAchievement("mineWood"); - break; - case Item::DIAMOND: - $this->awardAchievement("diamond"); - break; - } - - $pk = new TakeItemEntityPacket(); - $pk->eid = $this->getId(); - $pk->target = $entity->getId(); - Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); - $this->inventory->addItem(clone $item); - $entity->kill(); - } - } - } - } } if($this->nextChunkOrderRun-- <= 0 or $this->chunk === null){ @@ -1441,6 +1453,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->setSkin($packet->skin, $packet->slim); + + $this->uuid = Utils::dataToUUID($this->getClientId(), $this->iusername, $this->getAddress()); + $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); if($ev->isCancelled()){ $this->close("", $ev->getKickMessage()); @@ -2578,8 +2593,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ /** * @param string $message Message to be broadcasted * @param string $reason Reason showed in console + * @param bool $notify */ - public function close($message = "", $reason = "generic reason"){ + public function close($message = "", $reason = "generic reason", $notify = true){ foreach($this->tasks as $task){ $task->cancel(); @@ -2587,7 +2603,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->tasks = []; if($this->connected and !$this->closed){ - if($reason != ""){ + if($notify and $reason != ""){ $pk = new DisconnectPacket; $pk->message = $reason; $this->directDataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); @@ -2612,7 +2628,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->removeWindow($window); } - $this->interface->close($this, $reason); + $this->interface->close($this, $notify ? $reason : ""); $chunkX = $chunkZ = null; foreach($this->usedChunks as $index => $d){ diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index b6171a534..80a44e431 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -480,8 +480,13 @@ namespace pocketmine { } } }elseif(!$thread->isJoined()){ - $logger->debug("Joining " . (new \ReflectionClass($thread))->getShortName() . " thread"); - $thread->join(); + if(!$thread->isTerminated()){ + $logger->debug("Joining " . (new \ReflectionClass($thread))->getShortName() . " thread"); + $thread->join(); + }else{ + $logger->debug("Killing " . (new \ReflectionClass($thread))->getShortName() . " thread"); + $thread->kill(); + } } } diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index e2f82bdc6..9e78f2f02 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1602,7 +1602,7 @@ class Server{ $this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp() === "" ? "*" : $this->getIp(), $this->getPort()])); define("BOOTUP_RANDOM", @Utils::getRandomBytes(16)); - $this->serverID = Binary::readLong(substr(Utils::getUniqueID(true, $this->getIp() . $this->getPort()), 0, 8)); + $this->serverID = Utils::getServerUniqueId($this->getIp() . $this->getPort()); $this->network = new Network($this); $this->network->setName($this->getMotd()); @@ -2234,7 +2234,7 @@ class Server{ $version = new VersionString(); $this->lastSendUsage = new SendUsageTask("https://stats.pocketmine.net/usage.php", [ - "serverid" => $this->serverID, + "serverid" => Binary::readLong(substr(hex2bin(str_replace("-", "", $this->serverID)), 0, 8)), "port" => $this->getPort(), "os" => Utils::getOS(), "name" => $this->getName(), diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index ad8f7efed..b7d20de63 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -68,6 +68,8 @@ use pocketmine\level\format\generic\EmptyChunkSection; use pocketmine\level\format\LevelProvider; use pocketmine\level\generator\GenerationTask; use pocketmine\level\generator\Generator; +use pocketmine\level\generator\GeneratorRegisterTask; +use pocketmine\level\generator\GeneratorUnregisterTask; use pocketmine\level\generator\PopulationTask; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Math; @@ -341,6 +343,22 @@ class Level implements ChunkManager, Metadatable{ $generator = $this->generator; $this->generatorInstance = new $generator($this->provider->getGeneratorOptions()); $this->generatorInstance->init($this, new Random($this->getSeed())); + + $this->registerGenerator(); + } + + public function registerGenerator(){ + $size = $this->server->getScheduler()->getAsyncTaskPoolSize(); + for($i = 0; $i < $size; ++$i){ + $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorRegisterTask($this, $this->generatorInstance), $i); + } + } + + public function unregisterGenerator(){ + $size = $this->server->getScheduler()->getAsyncTaskPoolSize(); + for($i = 0; $i < $size; ++$i){ + $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorUnregisterTask($this, $this->generatorInstance), $i); + } } /** @@ -383,6 +401,8 @@ class Level implements ChunkManager, Metadatable{ $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); } + $this->unregisterGenerator(); + $this->provider->close(); $this->provider = null; $this->blockMetadata = null; @@ -2378,7 +2398,7 @@ class Level implements ChunkManager, Metadatable{ $this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true; } } - $task = new PopulationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true)); + $task = new PopulationTask($this, $this->getChunk($x, $z, true)); $this->server->getScheduler()->scheduleAsyncTask($task); } Timings::$generationTimer->stopTiming(); @@ -2400,7 +2420,7 @@ class Level implements ChunkManager, Metadatable{ if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){ Timings::$generationTimer->startTiming(); $this->chunkGenerationQueue[$index] = true; - $task = new GenerationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true)); + $task = new GenerationTask($this, $this->getChunk($x, $z, true)); $this->server->getScheduler()->scheduleAsyncTask($task); Timings::$generationTimer->stopTiming(); } diff --git a/src/pocketmine/level/generator/GenerationTask.php b/src/pocketmine/level/generator/GenerationTask.php index 28bc3d1f6..ef6c8fb64 100644 --- a/src/pocketmine/level/generator/GenerationTask.php +++ b/src/pocketmine/level/generator/GenerationTask.php @@ -32,17 +32,13 @@ use pocketmine\utils\Random; class GenerationTask extends AsyncTask{ - public $generator; - public $settings; - public $seed; + public $state; public $levelId; public $chunk; public $chunkClass; - public function __construct(Level $level, Generator $generator, FullChunk $chunk){ - $this->generator = get_class($generator); - $this->settings = $generator->getSettings(); - $this->seed = $level->getSeed(); + public function __construct(Level $level, FullChunk $chunk){ + $this->state = true; $this->levelId = $level->getId(); $this->chunk = $chunk->toFastBinary(); $this->chunkClass = get_class($chunk); @@ -50,19 +46,12 @@ class GenerationTask extends AsyncTask{ public function onRun(){ /** @var SimpleChunkManager $manager */ - $manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager"); + $manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager"); /** @var Generator $generator */ - $generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator"); + $generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator"); if($manager === null or $generator === null){ - Block::init(); - Biome::init(); - $manager = new SimpleChunkManager($this->seed); - $this->saveToThreadStore($key, $manager); - /** @var Generator $generator */ - $generator = $this->generator; - $generator = new $generator($this->settings); - $generator->init($manager, new Random($manager->getSeed())); - $this->saveToThreadStore($gKey, $generator); + $this->state = false; + return; } /** @var FullChunk $chunk */ @@ -87,6 +76,10 @@ class GenerationTask extends AsyncTask{ public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level !== null){ + if($this->state === false){ + $level->registerGenerator(); + return; + } /** @var FullChunk $chunk */ $chunk = $this->chunkClass; $chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider()); diff --git a/src/pocketmine/level/generator/GeneratorRegisterTask.php b/src/pocketmine/level/generator/GeneratorRegisterTask.php new file mode 100644 index 000000000..27cf1acfe --- /dev/null +++ b/src/pocketmine/level/generator/GeneratorRegisterTask.php @@ -0,0 +1,58 @@ +generator = get_class($generator); + $this->settings = $generator->getSettings(); + $this->seed = $level->getSeed(); + $this->levelId = $level->getId(); + } + + public function onRun(){ + Block::init(); + Biome::init(); + $manager = new SimpleChunkManager($this->seed); + $this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager); + /** @var Generator $generator */ + $generator = $this->generator; + $generator = new $generator($this->settings); + $generator->init($manager, new Random($manager->getSeed())); + $this->saveToThreadStore("generation.level{$this->levelId}.generator", $generator); + } +} diff --git a/src/pocketmine/level/generator/GeneratorUnregisterTask.php b/src/pocketmine/level/generator/GeneratorUnregisterTask.php new file mode 100644 index 000000000..dff150004 --- /dev/null +++ b/src/pocketmine/level/generator/GeneratorUnregisterTask.php @@ -0,0 +1,45 @@ +levelId = $level->getId(); + } + + public function onRun(){ + $this->saveToThreadStore("generation.level{$this->levelId}.manager", null); + $this->saveToThreadStore("generation.level{$this->levelId}.generator", null); + } +} diff --git a/src/pocketmine/level/generator/PopulationTask.php b/src/pocketmine/level/generator/PopulationTask.php index ecd8708cb..825260790 100644 --- a/src/pocketmine/level/generator/PopulationTask.php +++ b/src/pocketmine/level/generator/PopulationTask.php @@ -32,9 +32,8 @@ use pocketmine\utils\Random; class PopulationTask extends AsyncTask{ - public $generator; - public $settings; - public $seed; + + public $state; public $levelId; public $chunk; public $chunkClass; @@ -49,10 +48,8 @@ class PopulationTask extends AsyncTask{ public $chunk21; public $chunk22; - public function __construct(Level $level, Generator $generator, FullChunk $chunk){ - $this->generator = get_class($generator); - $this->settings = $generator->getSettings(); - $this->seed = $level->getSeed(); + public function __construct(Level $level, FullChunk $chunk){ + $this->state = true; $this->levelId = $level->getId(); $this->chunk = $chunk->toFastBinary(); $this->chunkClass = get_class($chunk); @@ -70,19 +67,12 @@ class PopulationTask extends AsyncTask{ public function onRun(){ /** @var SimpleChunkManager $manager */ - $manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager"); + $manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager"); /** @var Generator $generator */ - $generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator"); + $generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator"); if($manager === null or $generator === null){ - Block::init(); - Biome::init(); - $manager = new SimpleChunkManager($this->seed); - $this->saveToThreadStore($key, $manager); - /** @var Generator $generator */ - $generator = $this->generator; - $generator = new $generator($this->settings); - $generator->init($manager, new Random($manager->getSeed())); - $this->saveToThreadStore($gKey, $generator); + $this->state = false; + return; } /** @var FullChunk[] $chunks */ @@ -149,6 +139,10 @@ class PopulationTask extends AsyncTask{ public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level !== null){ + if($this->state === false){ + $level->registerGenerator(); + return; + } /** @var FullChunk[] $chunks */ $chunks = []; /** @var FullChunk $chunkC */ diff --git a/src/pocketmine/utils/Utils.php b/src/pocketmine/utils/Utils.php index 6214542f3..942b80b85 100644 --- a/src/pocketmine/utils/Utils.php +++ b/src/pocketmine/utils/Utils.php @@ -48,18 +48,36 @@ class Utils{ } } + public static function randomUUID(){ + return Utils::toUUID(Binary::writeInt(time()) . Binary::writeShort(getmypid()) . Binary::writeShort(getmyuid()) . Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)) . Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), 2); + } + + public static function dataToUUID(...$params){ + return Utils::toUUID(hash("md5", implode($params), true), 3); + } + + public static function toUUID($data, $version = 2, $fixed = "8"){ + if(strlen($data) !== 16){ + throw new \InvalidArgumentException("Data bust be 16 bytes"); + } + + $hex = bin2hex($data); + + //xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 8-4-4-12 + return substr($hex, 0, 8) . "-" . substr($hex, 8, 4) . "-" . hexdec($version) . substr($hex, 13, 3) . "-" . $fixed{0} . substr($hex, 17, 3) . "-" . substr($hex, 20, 12); + } + /** * Gets this machine / server instance unique ID * Returns a hash, the first 32 characters (or 16 if raw) * will be an identifier that won't change frequently. * The rest of the hash will change depending on other factors. * - * @param bool $raw default false, if true, returns the raw identifier, not hexadecimal * @param string $extra optional, additional data to identify the machine * * @return string */ - public static function getUniqueID($raw = false, $extra = ""){ + public static function getServerUniqueId($extra = ""){ $machine = php_uname("a"); $machine .= file_exists("/proc/cpuinfo") ? implode(preg_grep("/model name/", file("/proc/cpuinfo"))) : ""; $machine .= sys_get_temp_dir(); @@ -96,7 +114,7 @@ class Utils{ $data .= $ext . ":" . phpversion($ext); } - return hash("md5", $machine, $raw) . hash("sha512", $data, $raw); + return Utils::dataToUUID($machine, $data); } /**