public function onCompletion(Server $server) { $chunks = $this->getResult(); $plugin = $server->getPluginManager()->getPlugin("MineReset"); if ($plugin instanceof MineReset and $plugin->isEnabled()) { $level = $server->getLevel($this->levelId); if ($level != null) { foreach ($chunks as $hash => $chunk) { Level::getXZ($hash, $x, $z); $level->setChunk($x, $z, $chunk); } } $plugin->getRegionBlocker()->freeZone($this->regionId, $this->levelId); } }
public function process() { if (count($this->requestQueue) > 0) { $count = 0; foreach ($this->requestQueue as $levelID => $chunks) { if ($count >= $this->maxCount) { break; } if (count($chunks) === 0) { unset($this->requestQueue[$levelID]); } else { Level::getXZ($key = key($chunks), $chunkX, $chunkZ); unset($this->requestQueue[$levelID][$key]); $this->generateChunk($levelID, $chunkX, $chunkZ); ++$count; } } } }
/** * Changes the biome of a plot * * @api * @param Plot $plot * @param Biome $biome * @return bool */ public function setPlotBiome(Plot $plot, Biome $biome) { $plotLevel = $this->getLevelSettings($plot->levelName); if ($plotLevel === null) { return false; } $level = $this->getServer()->getLevelByName($plot->levelName); $pos = $this->getPlotPosition($plot); $plotSize = $plotLevel->plotSize; $xMax = $pos->x + $plotSize; $zMax = $pos->z + $plotSize; $chunkIndexes = []; for ($x = $pos->x; $x < $xMax; $x++) { for ($z = $pos->z; $z < $zMax; $z++) { $index = Level::chunkHash($x >> 4, $z >> 4); if (!in_array($index, $chunkIndexes)) { $chunkIndexes[] = $index; } $color = $biome->getColor(); $R = $color >> 16; $G = $color >> 8 & 0xff; $B = $color & 0xff; $level->setBiomeColor($x, $z, $R, $G, $B); } } foreach ($chunkIndexes as $index) { Level::getXZ($index, $X, $Z); $chunk = $level->getChunk($X, $Z); foreach ($level->getChunkPlayers($X, $Z) as $player) { $player->onChunkChanged($chunk); } } $plot->biome = $biome->getName(); $this->dataProvider->savePlot($plot); return true; }
/** * @param GenerationThread $thread * @param \Logger $logger * @param \ClassLoader $loader */ public function __construct(GenerationThread $thread, \Logger $logger, \ClassLoader $loader) { $this->thread = $thread; $this->logger = $logger; $this->loader = $loader; $chunkX = $chunkZ = null; while ($this->shutdown !== true) { try { if (count($this->requestQueue) > 0) { foreach ($this->requestQueue as $levelID => $chunks) { if (count($chunks) === 0) { unset($this->requestQueue[$levelID]); } else { Level::getXZ($key = key($chunks), $chunkX, $chunkZ); unset($this->requestQueue[$levelID][$key]); $this->generateChunk($levelID, $chunkX, $chunkZ); } } } else { $this->readPacket(); } } catch (\Exception $e) { $this->logger->warning("[Generator Thread] Exception: " . $e->getMessage() . " on file \"" . $e->getFile() . "\" line " . $e->getLine()); } } }
private function unloadChunks() { if (count($this->unloadQueue) > 0) { $X = null; $Z = null; foreach ($this->unloadQueue as $index => $time) { Level::getXZ($index, $X, $Z); //If the chunk can't be unloaded, it stays on the queue if ($this->unloadChunk($X, $Z, true)) { unset($this->unloadQueue[$index]); } } } }
/** * Generates a new level if it does not exists * * @param string $name * @param int $seed * @param string $generator Class name that extends pocketmine\level\generator\Generator * @param array $options * * @return bool */ public function generateLevel($name, $seed = null, $generator = null, $options = []) { if (trim($name) === "" or $this->isLevelGenerated($name)) { return false; } $seed = $seed === null ? Binary::readInt(@Utils::getRandomBytes(4, false)) : (int) $seed; if ($generator !== null and class_exists($generator) and is_subclass_of($generator, Generator::class)) { $generator = new $generator($options); } else { $options["preset"] = $this->getConfigString("generator-settings", ""); $generator = Generator::getGenerator($this->getLevelType()); } if (($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "mcregion"))) === null) { $provider = LevelProviderManager::getProviderByName($providerName = "mcregion"); } try { $path = $this->getDataPath() . "worlds/" . $name . "/"; /** @var \pocketmine\level\format\LevelProvider $provider */ $provider::generate($path, $name, $seed, $generator, $options); $level = new Level($this, $name, $path, $provider); $this->levels[$level->getId()] = $level; $level->initLevel(); } catch (\Exception $e) { $this->logger->error("Could not generate level \"" . $name . "\": " . $e->getMessage()); if ($this->logger instanceof MainLogger) { $this->logger->logException($e); } return false; } $this->getPluginManager()->callEvent(new LevelInitEvent($level)); $this->getPluginManager()->callEvent(new LevelLoadEvent($level)); $this->getLogger()->notice("Spawn terrain for level \"{$name}\" is being generated in the background"); $radiusSquared = ($this->getViewDistance() + 1) / M_PI; $radius = ceil(sqrt($radiusSquared)); $centerX = $level->getSpawnLocation()->getX() >> 4; $centerZ = $level->getSpawnLocation()->getZ() >> 4; $order = []; for ($X = -$radius; $X <= $radius; ++$X) { for ($Z = -$radius; $Z <= $radius; ++$Z) { $distance = $X ** 2 + $Z ** 2; if ($distance > $radiusSquared) { continue; } $chunkX = $X + $centerX; $chunkZ = $Z + $centerZ; $index = Level::chunkHash($chunkX, $chunkZ); $order[$index] = $distance; } } asort($order); $chunkX = $chunkZ = null; foreach ($order as $index => $distance) { Level::getXZ($index, $chunkX, $chunkZ); $level->generateChunk($chunkX, $chunkZ); } return true; }
/** * Generates a new level if it does not exists * * @param string $name * @param int $seed * @param array $options * * @return bool */ public function generateLevel($name, $seed = null, $options = []) { if (trim($name) === "" or $this->isLevelGenerated($name)) { return false; } $seed = $seed === null ? PHP_INT_SIZE === 8 ? unpack("N", @Utils::getRandomBytes(4, false))[1] << 32 >> 32 : unpack("N", @Utils::getRandomBytes(4, false))[1] : (int) $seed; if (($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "mcregion"))) === null) { $provider = LevelProviderManager::getProviderByName($providerName = "mcregion"); } try { $path = $this->getDataPath() . "worlds/" . $name . "/"; /** @var \pocketmine\level\format\LevelProvider $provider */ $provider::generate($path, $name, $seed, $options); $level = new Level($this, $name, $path, $provider); $this->levels[$level->getId()] = $level; $level->initLevel(); } catch (\Exception $e) { $this->logger->error("Could not generate level \"" . $name . "\": " . $e->getMessage()); if ($this->logger instanceof MainLogger) { $this->logger->logException($e); } return false; } $this->getPluginManager()->callEvent(new LevelInitEvent($level)); $this->getPluginManager()->callEvent(new LevelLoadEvent($level)); $centerX = $level->getSpawnLocation()->getX() >> 4; $centerZ = $level->getSpawnLocation()->getZ() >> 4; $order = []; for ($X = -3; $X <= 3; ++$X) { for ($Z = -3; $Z <= 3; ++$Z) { $distance = $X ** 2 + $Z ** 2; $chunkX = $X + $centerX; $chunkZ = $Z + $centerZ; $index = Level::chunkHash($chunkX, $chunkZ); $order[$index] = $distance; } } asort($order); foreach ($order as $index => $distance) { Level::getXZ($index, $chunkX, $chunkZ); $level->generateChunk($chunkX, $chunkZ, true); } return true; }
/** * Note for plugin developers: use kick() with the isAdmin * flag set to kick without the "Kicked by admin" part instead of this method. * * @param string $message Message to be broadcasted * @param string $reason Reason showed in console * @param bool $notify */ public final function close($message = "", $reason = "generic reason", $notify = true) { if ($this->connected and !$this->closed) { if ($notify and strlen((string) $reason) > 0) { $pk = new DisconnectPacket(); $pk->message = $reason; $this->directDataPacket($pk); } $this->connected = false; if (strlen($this->getName()) > 0) { $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message, true)); if ($this->loggedIn === true and $ev->getAutoSave()) { $this->save(); } } foreach ($this->server->getOnlinePlayers() as $player) { if (!$player->canSee($this)) { $player->showPlayer($this); } } $this->hiddenPlayers = []; foreach ($this->windowIndex as $window) { $this->removeWindow($window); } foreach ($this->usedChunks as $index => $d) { Level::getXZ($index, $chunkX, $chunkZ); $this->level->unregisterChunkLoader($this, $chunkX, $chunkZ); unset($this->usedChunks[$index]); } parent::close(); $this->interface->close($this, $notify ? $reason : ""); if ($this->loggedIn) { $this->server->removeOnlinePlayer($this); } $this->loggedIn = false; if (isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != "") { $this->server->broadcastMessage($ev->getQuitMessage()); } $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); $this->spawned = false; $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [TextFormat::AQUA . $this->getName() . TextFormat::WHITE, $this->ip, $this->port, $this->getServer()->getLanguage()->translateString($reason)])); $this->windows = new \SplObjectStorage(); $this->windowIndex = []; $this->usedChunks = []; $this->loadQueue = []; $this->hasSpawned = []; $this->spawnPosition = null; unset($this->buffer); } if ($this->perm !== null) { $this->perm->clearPermissions(); $this->perm = null; } if ($this->inventory !== null) { $this->inventory = null; $this->currentTransaction = null; } $this->chunk = null; $this->server->removePlayer($this); }
public function doChunkGarbageCollection() { $this->timings->doChunkGC->startTiming(); $X = null; $Z = null; foreach ($this->chunks as $index => $chunk) { if (!isset($this->usedChunks[$index])) { Level::getXZ($index, $X, $Z); $this->unloadChunkRequest($X, $Z, true); } } if (count($this->unloadQueue) > 0) { foreach ($this->unloadQueue as $index => $time) { Level::getXZ($index, $X, $Z); if ($this->getAutoSave()) { $this->provider->saveChunk($X, $Z); } //If the chunk can't be unloaded, it stays on the queue if ($this->unloadChunk($X, $Z, true)) { unset($this->unloadQueue[$index]); } } } foreach ($this->usedChunks as $i => $c) { if (count($c) === 0) { Level::getXZ($i, $X, $Z); if (!$this->isSpawnChunk($X, $Z)) { if ($this->getAutoSave()) { $this->provider->saveChunk($X, $Z); } $this->unloadChunk($X, $Z, true); } } } $this->timings->doChunkGC->stopTiming(); }
protected function switchLevel(Level $targetLevel) { if ($this->isValid()) { $this->server->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->level, $targetLevel)); if ($ev->isCancelled()) { return false; } $this->level->removeEntity($this); if ($this->chunk !== null) { $this->chunk->removeEntity($this); } $this->despawnFromAll(); if ($this instanceof Player) { foreach ($this->usedChunks as $index => $d) { Level::getXZ($index, $X, $Z); $this->unloadChunk($X, $Z); } } } $this->setLevel($targetLevel); $this->level->addEntity($this); if ($this instanceof Player) { $this->usedChunks = []; $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); } $this->chunk = null; return true; }
/** * Note for plugin developers: use kick() with the isAdmin * flag set to kick without the "Kicked by admin" part instead of this method. * * @param string $message Message to be broadcasted * @param string $reason Reason showed in console * @param bool $notify */ public final function close($message = "", $reason = "generic reason", $notify = true) { if ($this->connected and !$this->closed) { if ($notify and strlen((string) $reason) > 0) { $pk = new DisconnectPacket(); $pk->message = $reason; $this->directDataPacket($pk); } //$this->setLinked(); if ($this->fishingHook instanceof FishingHook) { $this->fishingHook->close(); $this->fishingHook = null; } $this->removeEffect(Effect::HEALTH_BOOST); $this->connected = false; if (strlen($this->getName()) > 0) { $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message, true)); if ($this->loggedIn === true and $ev->getAutoSave()) { $this->save(); } } foreach ($this->server->getOnlinePlayers() as $player) { if (!$player->canSee($this)) { $player->showPlayer($this); } } $this->hiddenPlayers = []; foreach ($this->windowIndex as $window) { $this->removeWindow($window); } foreach ($this->usedChunks as $index => $d) { Level::getXZ($index, $chunkX, $chunkZ); $this->level->unregisterChunkLoader($this, $chunkX, $chunkZ); unset($this->usedChunks[$index]); } parent::close(); $this->interface->close($this, $notify ? $reason : ""); if ($this->loggedIn) { $this->server->removeOnlinePlayer($this); } $this->loggedIn = false; if (isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != "") { if ($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_MESSAGE) { $this->server->broadcastMessage($ev->getQuitMessage()); } elseif ($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_TIP) { $this->server->broadcastTip(str_replace("@player", $this->getName(), $this->server->playerLogoutMsg)); } elseif ($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_POPUP) { $this->server->broadcastPopup(str_replace("@player", $this->getName(), $this->server->playerLogoutMsg)); } } $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); $this->spawned = false; $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [TextFormat::AQUA . $this->getName() . TextFormat::WHITE, $this->ip, $this->port, $this->getServer()->getLanguage()->translateString($reason)])); $this->windows = new \SplObjectStorage(); $this->windowIndex = []; $this->usedChunks = []; $this->loadQueue = []; $this->hasSpawned = []; $this->spawnPosition = null; unset($this->buffer); if ($this->server->dserverConfig["enable"] and $this->server->dserverConfig["queryAutoUpdate"]) { $this->server->updateQuery(); } } if ($this->perm !== null) { $this->perm->clearPermissions(); $this->perm = null; } $this->inventory = null; $this->transactionQueue = null; $this->chunk = null; $this->server->removePlayer($this); }
private function cleanupMemory(Player $player) { foreach ($player->usedChunks as $index => $c) { Level::getXZ($index, $chunkX, $chunkZ); foreach ($player->getLevel()->getChunkEntities($chunkX, $chunkZ) as $entity) { if ($entity !== $this) { $entity->despawnFrom($player); } } } }
protected function switchLevel(Level $targetLevel) { if ($this->isValid()) { $this->server->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->getLevel(), $targetLevel)); if ($ev->isCancelled()) { return false; } $this->getLevel()->removeEntity($this); $this->chunk->removeEntity($this); $this->despawnFromAll(); if ($this instanceof Player) { foreach ($this->usedChunks as $index => $d) { $X = null; $Z = null; Level::getXZ($index, $X, $Z); foreach ($this->getLevel()->getChunkEntities($X, $Z) as $entity) { $entity->despawnFrom($this); } } $this->getLevel()->freeAllChunks($this); } } $this->setLevel($targetLevel, $this instanceof Player ? true : false); //Hard reference $this->getLevel()->addEntity($this); if ($this instanceof Player) { $this->usedChunks = []; $pk = new SetTimePacket(); $pk->time = $this->getLevel()->getTime(); $pk->started = $this->getLevel()->stopTime == false; $this->dataPacket($pk); } $this->chunk = null; return true; }
protected function sendNextChunk() { if ($this->connected === false) { return; } Timings::$playerChunkSendTimer->startTiming(); $count = 0; foreach ($this->loadQueue as $index => $distance) { if ($count >= $this->chunksPerTick) { break; } $X = null; $Z = null; Level::getXZ($index, $X, $Z); ++$count; $this->usedChunks[$index] = false; $this->level->registerChunkLoader($this, $X, $Z, false); if (!$this->level->populateChunk($X, $Z)) { if ($this->spawned and $this->teleportPosition === null) { continue; } else { break; } } unset($this->loadQueue[$index]); $chunk = new DesktopChunk($this, $X, $Z); $this->bigBrother_sendChunk($X, $Z, $chunk->getData()); $chunk = null; } if ($this->chunkLoadCount >= 4 and $this->spawned === false and $this->teleportPosition === null) { $this->doFirstSpawn(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); } Timings::$playerChunkSendTimer->stopTiming(); }
/** * * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. * * @return bool */ public function orderChunks() { if ($this->connected === false) { return false; } $radiusSquared = $this->viewDistance / M_PI; $radius = ceil(sqrt($radiusSquared)); $newOrder = []; $lastChunk = $this->usedChunks; $centerX = $this->x >> 4; $centerZ = $this->z >> 4; $generateQueue = new ReversePriorityQueue(); for ($X = -$radius; $X <= $radius; ++$X) { for ($Z = -$radius; $Z <= $radius; ++$Z) { $distance = $X * $X + $Z * $Z; if ($distance > $radiusSquared) { continue; } $chunkX = $X + $centerX; $chunkZ = $Z + $centerZ; $index = Level::chunkHash($chunkX, $chunkZ); if (!isset($this->usedChunks[$index])) { if ($this->level->isChunkPopulated($chunkX, $chunkZ)) { $newOrder[$index] = $distance; } else { $generateQueue->insert([$chunkX, $chunkZ], $distance); } } unset($lastChunk[$index]); } } asort($newOrder); if (count($newOrder) > $this->viewDistance) { $count = 0; $this->loadQueue = []; foreach ($newOrder as $k => $distance) { $this->loadQueue[$k] = $distance; if (++$count > $this->viewDistance) { break; } } } else { $this->loadQueue = $newOrder; } $i = 0; while (count($this->loadQueue) < 3 and $generateQueue->count() > 0 and $i < 16) { $d = $generateQueue->extract(); $this->getLevel()->generateChunk($d[0], $d[1]); ++$i; } foreach ($lastChunk as $index => $Yndex) { $X = null; $Z = null; Level::getXZ($index, $X, $Z); foreach ($this->getLevel()->getChunkEntities($X, $Z) as $entity) { if ($entity !== $this) { $entity->despawnFrom($this); } } unset($this->usedChunks[$index]); } }
protected function orderChunks() { if ($this->connected === false) { return false; } $this->nextChunkOrderRun = 200; $radiusSquared = $this->viewDistance; $radius = ceil(sqrt($radiusSquared)); $side = ceil($radius / 2); $newOrder = []; $lastChunk = $this->usedChunks; $currentQueue = []; $centerX = $this->x >> 4; $centerZ = $this->z >> 4; for ($X = -$side; $X <= $side; ++$X) { for ($Z = -$side; $Z <= $side; ++$Z) { $chunkX = $X + $centerX; $chunkZ = $Z + $centerZ; if (!isset($this->usedChunks[$index = Level::chunkHash($chunkX, $chunkZ)])) { $newOrder[$index] = abs($X) + abs($Z); } else { $currentQueue[$index] = abs($X) + abs($Z); } } } asort($newOrder); asort($currentQueue); $limit = $this->viewDistance; foreach ($currentQueue as $index => $distance) { if ($limit-- <= 0) { break; } unset($lastChunk[$index]); } foreach ($lastChunk as $index => $Yndex) { $X = null; $Z = null; Level::getXZ($index, $X, $Z); $this->unloadChunk($X, $Z); } $loadedChunks = count($this->usedChunks); if (count($newOrder) + $loadedChunks > $this->viewDistance) { $count = $loadedChunks; $this->loadQueue = []; foreach ($newOrder as $k => $distance) { if (++$count > $this->viewDistance) { break; } $this->loadQueue[$k] = $distance; } } else { $this->loadQueue = $newOrder; } return true; }
/** * Generates a new level if it does not exists * * @param string $name * @param int $seed * @param string $generator Class name that extends pocketmine\level\generator\Noise * @param array $options * * @return bool */ public function generateLevel($name, $seed = null, $generator = null, $options = []) { if (trim($name) === "" or $this->isLevelGenerated($name)) { return false; } $seed = $seed === null ? Binary::readInt(@Utils::getRandomBytes(4, false)) : (int) $seed; if (!isset($options["preset"])) { $options["preset"] = $this->getConfigString("generator-settings", ""); } if (!($generator !== null and class_exists($generator, true) and is_subclass_of($generator, Generator::class))) { $generator = Generator::getGenerator($this->getLevelType()); } if (($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "mcregion"))) === null) { $provider = LevelProviderManager::getProviderByName($providerName = "mcregion"); } try { $path = $this->getDataPath() . "worlds/" . $name . "/"; /** @var \pocketmine\level\format\LevelProvider $provider */ $provider::generate($path, $name, $seed, $generator, $options); $level = new Level($this, $name, $path, $provider); $this->levels[$level->getId()] = $level; $level->initLevel(); $level->setTickRate($this->baseTickRate); } catch (\Exception $e) { $this->logger->error($this->getLanguage()->translateString("pocketmine.level.generateError", [$name, $e->getMessage()])); if ($this->logger instanceof MainLogger) { $this->logger->logException($e); } return false; } $this->getPluginManager()->callEvent(new LevelInitEvent($level)); $this->getPluginManager()->callEvent(new LevelLoadEvent($level)); $this->getLogger()->notice($this->getLanguage()->translateString("pocketmine.level.backgroundGeneration", [$name])); $centerX = $level->getSpawnLocation()->getX() >> 4; $centerZ = $level->getSpawnLocation()->getZ() >> 4; $order = []; for ($X = -3; $X <= 3; ++$X) { for ($Z = -3; $Z <= 3; ++$Z) { $distance = $X ** 2 + $Z ** 2; $chunkX = $X + $centerX; $chunkZ = $Z + $centerZ; $index = Level::chunkHash($chunkX, $chunkZ); $order[$index] = $distance; } } asort($order); foreach ($order as $index => $distance) { Level::getXZ($index, $chunkX, $chunkZ); $level->populateChunk($chunkX, $chunkZ, true); } return true; }
/** * @param string $message Message to be broadcasted * @param string $reason Reason showed in console */ public function close($message = "", $reason = "generic reason") { foreach ($this->tasks as $task) { $task->cancel(); } $this->tasks = []; if ($this->connected and !$this->closed) { $this->connected = false; if ($this->username != "") { $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message)); if ($this->server->getAutoSave() and $this->loggedIn === true) { $this->save(); } } foreach ($this->windowIndex as $window) { $this->removeWindow($window); } $this->interface->close($this, $reason); $chunkX = $chunkZ = null; foreach ($this->usedChunks as $index => $d) { Level::getXZ($index, $chunkX, $chunkZ); $this->level->freeChunk($chunkX, $chunkZ, $this); unset($this->usedChunks[$index]); } parent::close(); $this->loggedIn = false; if (isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != "") { $this->server->broadcastMessage($ev->getQuitMessage()); } $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); $this->spawned = false; $this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . str_replace(["\n", "\r"], [" ", ""], $reason)); $this->windows = new \SplObjectStorage(); $this->windowIndex = []; $this->usedChunks = []; $this->loadQueue = []; $this->hasSpawned = []; $this->spawnPosition = null; unset($this->buffer); } $this->perm->clearPermissions(); $this->server->removePlayer($this); }
public function unloadChunks($force = false) { if (count($this->unloadQueue) > 0) { $maxUnload = 96; $now = microtime(true); foreach ($this->unloadQueue as $index => $time) { Level::getXZ($index, $X, $Z); if (!$force) { if ($maxUnload <= 0) { break; } elseif ($time > $now - 30) { continue; } } //If the chunk can't be unloaded, it stays on the queue if ($this->unloadChunk($X, $Z, true)) { unset($this->unloadQueue[$index]); --$maxUnload; } } } }
protected function sendNextChunk() { if ($this->connected === \false) { return; } Timings::$playerChunkSendTimer->startTiming(); $count = 0; foreach ($this->loadQueue as $index => $distance) { if ($count >= $this->chunksPerTick) { break; } $X = \null; $Z = \null; Level::getXZ($index, $X, $Z); ++$count; $this->usedChunks[$index] = \false; $this->level->registerChunkLoader($this, $X, $Z, \false); if (!$this->level->populateChunk($X, $Z)) { if ($this->spawned and $this->teleportPosition === \null) { continue; } else { break; } } unset($this->loadQueue[$index]); $this->level->requestChunk($X, $Z, $this); } if ($this->chunkLoadCount >= $this->spawnThreshold and $this->spawned === \false and $this->teleportPosition === \null) { $this->doFirstSpawn(); } Timings::$playerChunkSendTimer->stopTiming(); }
/** * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. * * @param int $currentTick * * @return bool */ public function doTick($currentTick) { $this->timings->doTick->startTiming(); $this->checkTime(); if ($currentTick % 200 === 0) { $this->sendTime(); } $this->unloadChunks(); $X = null; $Z = null; //Do block updates $this->timings->doTickPending->startTiming(); while ($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick) { $block = $this->getBlock($this->updateQueue->extract()["data"]); unset($this->updateQueueIndex[PHP_INT_SIZE === 8 ? ($block->x & 0xfffffff) << 35 | ($block->y & 0x7f) << 28 | $block->z & 0xfffffff : $block->x . ":" . $block->y . ":" . $block->z]); $block->onUpdate(self::BLOCK_UPDATE_SCHEDULED); } $this->timings->doTickPending->stopTiming(); $this->timings->entityTick->startTiming(); //Update entities that need update Timings::$tickEntityTimer->startTiming(); foreach ($this->updateEntities as $id => $entity) { if ($entity->closed or !$entity->onUpdate($currentTick)) { unset($this->updateEntities[$id]); } } Timings::$tickEntityTimer->stopTiming(); $this->timings->entityTick->stopTiming(); $this->timings->tileEntityTick->startTiming(); //Update tiles that need update if (count($this->updateTiles) > 0) { //Timings::$tickTileEntityTimer->startTiming(); foreach ($this->updateTiles as $id => $tile) { if ($tile->onUpdate() !== true) { unset($this->updateTiles[$id]); } } //Timings::$tickTileEntityTimer->stopTiming(); } $this->timings->tileEntityTick->stopTiming(); $this->timings->doTickTiles->startTiming(); $this->tickChunks(); $this->timings->doTickTiles->stopTiming(); if (count($this->changedCount) > 0) { if (count($this->players) > 0) { foreach ($this->changedCount as $index => $mini) { for ($Y = 0; $Y < 8; ++$Y) { if (($mini & 1 << $Y) === 0) { continue; } if (count($this->changedBlocks[$index][$Y]) < 256) { continue; } else { $X = null; $Z = null; if (PHP_INT_SIZE === 8) { $X = $index >> 32 << 32 >> 32; $Z = ($index & 4294967295.0) << 32 >> 32; } else { list($X, $Z) = explode(":", $index); $X = (int) $X; $Z = (int) $Z; } foreach ($this->getUsingChunk($X, $Z) as $p) { $p->unloadChunk($X, $Z); } unset($this->changedBlocks[$index][$Y]); } } } $this->changedCount = []; if (count($this->changedBlocks) > 0) { foreach ($this->changedBlocks as $index => $mini) { foreach ($mini as $blocks) { /** @var Block $b */ foreach ($blocks as $b) { $pk = new UpdateBlockPacket(); $pk->x = $b->x; $pk->y = $b->y; $pk->z = $b->z; $pk->block = $b->getId(); $pk->meta = $b->getDamage(); Server::broadcastPacket($this->getUsingChunk($b->x >> 4, $b->z >> 4), $pk); } } } $this->changedBlocks = []; } } else { $this->changedCount = []; $this->changedBlocks = []; } } $this->processChunkRequest(); foreach ($this->moveToSend as $index => $entry) { Level::getXZ($index, $chunkX, $chunkZ); $pk = new MoveEntityPacket(); $pk->entities = $entry; Server::broadcastPacket($this->getUsingChunk($chunkX, $chunkZ), $pk); } $this->moveToSend = []; foreach ($this->motionToSend as $index => $entry) { Level::getXZ($index, $chunkX, $chunkZ); $pk = new SetEntityMotionPacket(); $pk->entities = $entry; Server::broadcastPacket($this->getUsingChunk($chunkX, $chunkZ), $pk); } $this->motionToSend = []; $this->timings->doTick->stopTiming(); }
protected function sendNextChunk() { if ($this->connected === false) { return; } $count = 0; foreach ($this->loadQueue as $index => $distance) { if ($count >= $this->chunksPerTick) { break; } Level::getXZ($index, $X, $Z); if (!$this->level->isChunkPopulated($X, $Z)) { $this->level->generateChunk($X, $Z); if ($this->spawned) { continue; } else { break; } } ++$count; unset($this->loadQueue[$index]); $this->usedChunks[$index] = true; $this->level->useChunk($X, $Z, $this); $chunk = $this->level->getChunk($X, $Z); if ($chunk instanceof AnvilChunk) { $this->kick("Playing on Anvil worlds is not yet implemented"); //TODO! /*$pk = new ChunkDataPacket(); $pk->chunkX = $X; $pk->chunkZ = $Z; $pk->groundUp = true; $ids = ""; $meta = ""; $blockLight = ""; $skyLight = ""; $biomeIds = $chunk->getBiomeIdArray(); $bitmap = 0; for($s = 0; $s < 8; ++$s){ $section = $chunk->getSection($s); if(!($section instanceof EmptyChunkSection)){ $bitmap |= 1 << $s; }else{ continue; } $ids .= $section->getIdArray(); $meta .= $section->getDataArray(); $blockLight .= $section->getLightArray(); $skyLight .= $section->getSkyLightArray(); } $pk->payload = zlib_encode($ids . $meta . $blockLight . $skyLight . $biomeIds, ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL); $pk->primaryBitmap = $bitmap; $this->putRawPacket($pk);*/ } elseif ($chunk instanceof McRegionChunk) { $task = new McRegionToAnvil($this, $chunk); $this->server->getScheduler()->scheduleAsyncTask($task); } elseif ($chunk instanceof LevelDBChunk) { $task = new LevelDBToAnvil($this, $chunk); $this->server->getScheduler()->scheduleAsyncTask($task); } foreach ($chunk->getEntities() as $entity) { if ($entity !== $this) { $entity->spawnTo($this); } } foreach ($chunk->getTiles() as $tile) { if ($tile instanceof Spawnable) { $tile->spawnTo($this); } } } if (count($this->usedChunks) >= 4 and $this->spawned === false) { $this->bigBrother_setTitleBar(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother.", 0); $this->spawned = true; $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; $this->dataPacket($pk); $pos = $this->level->getSafeSpawn($this); $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); $this->teleport($ev->getRespawnPosition()); $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, TextFormat::YELLOW . $this->getName() . " joined the game")); if (strlen(trim($ev->getJoinMessage())) > 0) { $this->server->broadcastMessage($ev->getJoinMessage()); } $this->spawnToAll(); if ($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)) { $this->server->getUpdater()->showPlayerUpdate($this); } } }