mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-04 09:10:00 +00:00
Added MemoryManager, new memory properties, improved performance, updated RakLib, fixed misc. bugs
This commit is contained in:
parent
ddc152ae0a
commit
b2c25eaf36
159
src/pocketmine/MemoryManager.php
Normal file
159
src/pocketmine/MemoryManager.php
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<?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/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace pocketmine;
|
||||||
|
|
||||||
|
use pocketmine\event\server\LowMemoryEvent;
|
||||||
|
use pocketmine\event\Timings;
|
||||||
|
use pocketmine\scheduler\GarbageCollectionTask;
|
||||||
|
use pocketmine\utils\Utils;
|
||||||
|
|
||||||
|
class MemoryManager{
|
||||||
|
|
||||||
|
/** @var Server */
|
||||||
|
private $server;
|
||||||
|
|
||||||
|
private $memoryLimit;
|
||||||
|
private $globalMemoryLimit;
|
||||||
|
private $checkRate;
|
||||||
|
private $checkTicker = 0;
|
||||||
|
private $lowMemory = false;
|
||||||
|
|
||||||
|
private $continuousTrigger = true;
|
||||||
|
private $continuousTriggerRate;
|
||||||
|
private $continuousTriggerCount = 0;
|
||||||
|
private $continuousTriggerTicker = 0;
|
||||||
|
|
||||||
|
private $garbageCollectionPeriod;
|
||||||
|
private $garbageCollectionTicker = 0;
|
||||||
|
private $garbageCollectionTrigger;
|
||||||
|
private $garbageCollectionAsync;
|
||||||
|
|
||||||
|
private $chunkLimit;
|
||||||
|
private $chunkTrigger;
|
||||||
|
|
||||||
|
private $chunkCache;
|
||||||
|
private $cacheTrigger;
|
||||||
|
|
||||||
|
public function __construct(Server $server){
|
||||||
|
$this->server = $server;
|
||||||
|
|
||||||
|
$this->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function init(){
|
||||||
|
$this->memoryLimit = ((int) $this->server->getProperty("memory.main-limit", 320)) * 1024 * 1024;
|
||||||
|
$this->globalMemoryLimit = ((int) $this->server->getProperty("memory.global-limit", 512)) * 1024 * 1024;
|
||||||
|
$this->checkRate = ((int) $this->server->getProperty("memory.check-rate", 20));
|
||||||
|
$this->continuousTrigger = (bool) $this->server->getProperty("memory.continuous-trigger", true);
|
||||||
|
$this->continuousTriggerRate = (int) $this->server->getProperty("memory.continuous-trigger-rate", 600);
|
||||||
|
|
||||||
|
$this->garbageCollectionPeriod = (int) $this->server->getProperty("memory.garbage-collection.period", 12000);
|
||||||
|
$this->garbageCollectionTrigger = (bool) $this->server->getProperty("memory.garbage-collection.low-memory-trigger", true);
|
||||||
|
$this->garbageCollectionAsync = (bool) $this->server->getProperty("memory.garbage-collection.collect-async-worker", true);
|
||||||
|
|
||||||
|
$this->chunkLimit = (int) $this->server->getProperty("memory.max-chunks.trigger-limit", 12000);
|
||||||
|
$this->chunkTrigger = (bool) $this->server->getProperty("memory.max-chunks.low-memory-trigger", true);
|
||||||
|
|
||||||
|
$this->chunkCache = (bool) $this->server->getProperty("memory.world-caches.disable-chunk-cache", true);
|
||||||
|
$this->cacheTrigger = (bool) $this->server->getProperty("memory.world-caches.low-memory-trigger", true);
|
||||||
|
|
||||||
|
gc_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLowMemory(){
|
||||||
|
return $this->lowMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canUseChunkCache(){
|
||||||
|
return !($this->lowMemory and $this->chunkTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViewDistance($distance){
|
||||||
|
return $this->lowMemory ? min($this->chunkLimit, $distance) : $distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function trigger($memory, $limit, $global = false, $triggerCount = 0){
|
||||||
|
$this->server->getLogger()->debug("[Memory Manager] ".($global ? "Global " : "") ."Low memory triggered, limit ". round(($limit / 1024) / 1024, 2)."MB, using ". round(($memory / 1024) / 1024, 2)."MB");
|
||||||
|
|
||||||
|
if($this->cacheTrigger){
|
||||||
|
foreach($this->server->getLevels() as $level){
|
||||||
|
$level->clearCache(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ev = new LowMemoryEvent($memory, $limit, $triggerCount);
|
||||||
|
$this->server->getPluginManager()->callEvent($ev);
|
||||||
|
|
||||||
|
if($this->garbageCollectionTrigger){
|
||||||
|
$this->triggerGarbageCollector();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->server->getLogger()->debug("[Memory Manager] Freed " . round(($ev->getMemoryFreed() / 1024) / 1024, 2)."MB");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check(){
|
||||||
|
if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){
|
||||||
|
$this->checkTicker = 0;
|
||||||
|
$memory = Utils::getMemoryUsage(true);
|
||||||
|
$trigger = false;
|
||||||
|
if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){
|
||||||
|
$trigger = true;
|
||||||
|
}elseif($this->globalMemoryLimit > 0 and $memory[1] > $this->globalMemoryLimit){
|
||||||
|
$trigger = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($trigger){
|
||||||
|
if($this->lowMemory and $this->continuousTrigger){
|
||||||
|
if(++$this->continuousTriggerTicker >= $this->continuousTriggerRate){
|
||||||
|
$this->continuousTriggerTicker = 0;
|
||||||
|
$this->trigger($memory, $this->memoryLimit, ++$this->continuousTriggerCount);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$this->lowMemory = true;
|
||||||
|
$this->continuousTriggerCount = 0;
|
||||||
|
$this->trigger($memory, $this->memoryLimit);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$this->lowMemory = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->garbageCollectionPeriod > 0 and ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
|
||||||
|
$this->garbageCollectionTicker = 0;
|
||||||
|
$this->triggerGarbageCollector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function triggerGarbageCollector(){
|
||||||
|
Timings::$garbageCollectorTimer->startTiming();
|
||||||
|
gc_collect_cycles();
|
||||||
|
|
||||||
|
if($this->garbageCollectionAsync){
|
||||||
|
$size = $this->server->getScheduler()->getAsyncTaskPoolSize();
|
||||||
|
for($i = 0; $i < $size; ++$i){
|
||||||
|
$this->server->getScheduler()->scheduleAsyncTaskToWorker(new GarbageCollectionTask(), $i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timings::$garbageCollectorTimer->stopTiming();
|
||||||
|
}
|
||||||
|
}
|
@ -581,7 +581,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendChunk($x, $z, $payload){
|
public function sendChunk($x, $z, &$payload){
|
||||||
if($this->connected === false){
|
if($this->connected === false){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -592,7 +592,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
$pk = new FullChunkDataPacket();
|
$pk = new FullChunkDataPacket();
|
||||||
$pk->chunkX = $x;
|
$pk->chunkX = $x;
|
||||||
$pk->chunkZ = $z;
|
$pk->chunkZ = $z;
|
||||||
$pk->data = $payload;
|
$pk->data =& $payload;
|
||||||
$this->batchDataPacket($pk->setChannel(Network::CHANNEL_WORLD_CHUNKS));
|
$this->batchDataPacket($pk->setChannel(Network::CHANNEL_WORLD_CHUNKS));
|
||||||
|
|
||||||
if($this->spawned){
|
if($this->spawned){
|
||||||
@ -692,8 +692,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
|
|
||||||
$this->nextChunkOrderRun = 200;
|
$this->nextChunkOrderRun = 200;
|
||||||
|
|
||||||
$radiusSquared = $this->viewDistance;
|
$viewDistance = $this->server->getMemoryManager()->getViewDistance($this->viewDistance);
|
||||||
$radius = ceil(sqrt($radiusSquared));
|
$radius = ceil(sqrt($viewDistance));
|
||||||
$side = ceil($radius / 2);
|
$side = ceil($radius / 2);
|
||||||
|
|
||||||
$newOrder = [];
|
$newOrder = [];
|
||||||
@ -716,7 +716,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
asort($currentQueue);
|
asort($currentQueue);
|
||||||
|
|
||||||
|
|
||||||
$limit = $this->viewDistance;
|
$limit = $viewDistance;
|
||||||
foreach($currentQueue as $index => $distance){
|
foreach($currentQueue as $index => $distance){
|
||||||
if($limit-- <= 0){
|
if($limit-- <= 0){
|
||||||
break;
|
break;
|
||||||
@ -733,11 +733,12 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
|
|
||||||
$loadedChunks = count($this->usedChunks);
|
$loadedChunks = count($this->usedChunks);
|
||||||
|
|
||||||
if((count($newOrder) + $loadedChunks) > $this->viewDistance){
|
|
||||||
|
if((count($newOrder) + $loadedChunks) > $viewDistance){
|
||||||
$count = $loadedChunks;
|
$count = $loadedChunks;
|
||||||
$this->loadQueue = [];
|
$this->loadQueue = [];
|
||||||
foreach($newOrder as $k => $distance){
|
foreach($newOrder as $k => $distance){
|
||||||
if(++$count > $this->viewDistance){
|
if(++$count > $viewDistance){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$this->loadQueue[$k] = $distance;
|
$this->loadQueue[$k] = $distance;
|
||||||
@ -1107,7 +1108,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
|
|
||||||
$revert = false;
|
$revert = false;
|
||||||
|
|
||||||
if($distanceSquared > 100){
|
if(($distanceSquared / $this->level->getTickRate()) > 100){
|
||||||
$revert = true;
|
$revert = true;
|
||||||
}else{
|
}else{
|
||||||
if($this->chunk === null or !$this->chunk->isGenerated()){
|
if($this->chunk === null or !$this->chunk->isGenerated()){
|
||||||
@ -2863,7 +2864,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
|||||||
|
|
||||||
if($source->isCancelled()){
|
if($source->isCancelled()){
|
||||||
return;
|
return;
|
||||||
}elseif($this->getLastDamageCause() === $source){
|
}elseif($this->getLastDamageCause() === $source and $this->spawned){
|
||||||
$pk = new EntityEventPacket();
|
$pk = new EntityEventPacket();
|
||||||
$pk->eid = $this->getId();
|
$pk->eid = $this->getId();
|
||||||
$pk->event = 2;
|
$pk->event = 2;
|
||||||
|
@ -314,6 +314,24 @@ namespace pocketmine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $value
|
||||||
|
* @param bool $includeCurrent
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
function getReferenceCount($value, $includeCurrent = true){
|
||||||
|
ob_start();
|
||||||
|
debug_zval_dump($value);
|
||||||
|
$ret = explode("\n", ob_get_contents());
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
if(count($ret) >= 1 and preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
|
||||||
|
return ((int) $m[1]) - ($includeCurrent ? 3 : 4); //$value + zval call + extra call
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
function getTrace($start = 1, $trace = null){
|
function getTrace($start = 1, $trace = null){
|
||||||
if($trace === null){
|
if($trace === null){
|
||||||
if(function_exists("xdebug_get_function_stack")){
|
if(function_exists("xdebug_get_function_stack")){
|
||||||
|
@ -159,6 +159,9 @@ class Server{
|
|||||||
/** @var \AttachableThreadedLogger */
|
/** @var \AttachableThreadedLogger */
|
||||||
private $logger;
|
private $logger;
|
||||||
|
|
||||||
|
/** @var MemoryManager */
|
||||||
|
private $memoryManager;
|
||||||
|
|
||||||
/** @var CommandReader */
|
/** @var CommandReader */
|
||||||
private $console = null;
|
private $console = null;
|
||||||
private $consoleThreaded;
|
private $consoleThreaded;
|
||||||
@ -879,6 +882,13 @@ class Server{
|
|||||||
* @param Player $player
|
* @param Player $player
|
||||||
*/
|
*/
|
||||||
public function removePlayer(Player $player){
|
public function removePlayer(Player $player){
|
||||||
|
if(isset($this->identifiers[$hash = spl_object_hash($player)])){
|
||||||
|
$identifier = $this->identifiers[$hash];
|
||||||
|
unset($this->players[$identifier]);
|
||||||
|
unset($this->identifiers[$hash]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach($this->players as $identifier => $p){
|
foreach($this->players as $identifier => $p){
|
||||||
if($player === $p){
|
if($player === $p){
|
||||||
unset($this->players[$identifier]);
|
unset($this->players[$identifier]);
|
||||||
@ -1521,9 +1531,20 @@ class Server{
|
|||||||
$this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE));
|
$this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE));
|
||||||
$this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()]));
|
$this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()]));
|
||||||
|
|
||||||
|
$this->memoryManager = new MemoryManager($this);
|
||||||
|
|
||||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.start", [TextFormat::AQUA . $this->getVersion()]));
|
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.start", [TextFormat::AQUA . $this->getVersion()]));
|
||||||
|
|
||||||
ServerScheduler::$WORKERS = $this->getProperty("settings.async-workers", ServerScheduler::$WORKERS);
|
if(($poolSize = $this->getProperty("settings.async-workers", "auto")) === "auto"){
|
||||||
|
$poolSize = ServerScheduler::$WORKERS;
|
||||||
|
$processors = Utils::getCoreCount();
|
||||||
|
|
||||||
|
if($processors > 0){
|
||||||
|
$poolSize = max(2, $processors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerScheduler::$WORKERS = $poolSize;
|
||||||
|
|
||||||
if($this->getProperty("network.batch-threshold", 256) >= 0){
|
if($this->getProperty("network.batch-threshold", 256) >= 0){
|
||||||
Network::$BATCH_THRESHOLD = (int) $this->getProperty("network.batch-threshold", 256);
|
Network::$BATCH_THRESHOLD = (int) $this->getProperty("network.batch-threshold", 256);
|
||||||
@ -1560,15 +1581,10 @@ class Server{
|
|||||||
$this->maxPlayers = $this->getConfigInt("max-players", 20);
|
$this->maxPlayers = $this->getConfigInt("max-players", 20);
|
||||||
$this->setAutoSave($this->getConfigBoolean("auto-save", true));
|
$this->setAutoSave($this->getConfigBoolean("auto-save", true));
|
||||||
|
|
||||||
if(($memory = str_replace("B", "", strtoupper($this->getConfigString("memory-limit", -1)))) !== false and $memory > 1){
|
if($this->getConfigString("memory-limit", -1) !== false){
|
||||||
$value = ["M" => 1, "G" => 1024];
|
$this->logger->notice("The memory-limit setting has been deprecated.");
|
||||||
$real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
|
$this->logger->notice("There are new memory settings on pocketmine.yml to tune memory and events.");
|
||||||
if($real < 128){
|
$this->logger->notice("You can also reduce the amount of threads and chunks loaded control the memory usage.");
|
||||||
$this->logger->warning($this->getName() . " may not work right with less than 128MB of memory");
|
|
||||||
}
|
|
||||||
@ini_set("memory_limit", $memory);
|
|
||||||
$this->logger->notice("The memory limit will only affect the main thread, and it's unreliable.");
|
|
||||||
$this->logger->notice("To control the memory usage, reduce the amount of threads and chunks loaded");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
||||||
@ -1791,11 +1807,7 @@ class Server{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!$forceSync and $this->networkCompressionAsync){
|
if(!$forceSync and $this->networkCompressionAsync){
|
||||||
$task = new CompressBatchedTask();
|
$task = new CompressBatchedTask($str, $targets, $this->networkCompressionLevel, $channel);
|
||||||
$task->targets = $targets;
|
|
||||||
$task->data = $str;
|
|
||||||
$task->channel = $channel;
|
|
||||||
$task->level = $this->networkCompressionLevel;
|
|
||||||
$this->getScheduler()->scheduleAsyncTask($task);
|
$this->getScheduler()->scheduleAsyncTask($task);
|
||||||
}else{
|
}else{
|
||||||
$this->broadcastPacketsCallback(zlib_encode($str, ZLIB_ENCODING_DEFLATE, $this->networkCompressionLevel), $targets, $channel);
|
$this->broadcastPacketsCallback(zlib_encode($str, ZLIB_ENCODING_DEFLATE, $this->networkCompressionLevel), $targets, $channel);
|
||||||
@ -1904,17 +1916,6 @@ class Server{
|
|||||||
$this->properties->reload();
|
$this->properties->reload();
|
||||||
$this->maxPlayers = $this->getConfigInt("max-players", 20);
|
$this->maxPlayers = $this->getConfigInt("max-players", 20);
|
||||||
|
|
||||||
if(($memory = str_replace("B", "", strtoupper($this->getConfigString("memory-limit", -1)))) !== false and $memory > 1){
|
|
||||||
$value = ["M" => 1, "G" => 1024];
|
|
||||||
$real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
|
|
||||||
if($real < 256){
|
|
||||||
$this->logger->warning($this->getName() . " may not work right with less than 256MB of memory", true, true, 0);
|
|
||||||
}
|
|
||||||
@ini_set("memory_limit", $memory);
|
|
||||||
$this->logger->notice("The memory limit will only affect the main thread, and it's unreliable.");
|
|
||||||
$this->logger->notice("To control the memory usage, reduce the amount of threads and chunks loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
||||||
$this->setConfigInt("difficulty", 3);
|
$this->setConfigInt("difficulty", 3);
|
||||||
}
|
}
|
||||||
@ -2211,8 +2212,7 @@ class Server{
|
|||||||
if($player->isOnline()){
|
if($player->isOnline()){
|
||||||
$player->save();
|
$player->save();
|
||||||
}elseif(!$player->isConnected()){
|
}elseif(!$player->isConnected()){
|
||||||
unset($this->players[$index]);
|
$this->removePlayer($player);
|
||||||
unset($this->identifiers[spl_object_hash($player)]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2248,8 +2248,8 @@ class Server{
|
|||||||
"port" => $this->getPort(),
|
"port" => $this->getPort(),
|
||||||
"os" => Utils::getOS(),
|
"os" => Utils::getOS(),
|
||||||
"name" => $this->getName(),
|
"name" => $this->getName(),
|
||||||
"memory_total" => $this->getConfigString("memory-limit"),
|
"memory_total" => $this->getConfigString("memory-limit"), //TODO
|
||||||
"memory_usage" => $this->getMemoryUsage(),
|
"memory_usage" => Utils::getMemoryUsage(),
|
||||||
"php_version" => PHP_VERSION,
|
"php_version" => PHP_VERSION,
|
||||||
"version" => $version->get(true),
|
"version" => $version->get(true),
|
||||||
"build" => $version->getBuild(),
|
"build" => $version->getBuild(),
|
||||||
@ -2285,13 +2285,20 @@ class Server{
|
|||||||
return $this->network;
|
return $this->network;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return MemoryManager
|
||||||
|
*/
|
||||||
|
public function getMemoryManager(){
|
||||||
|
return $this->memoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
private function titleTick(){
|
private function titleTick(){
|
||||||
if(!Terminal::hasFormattingCodes()){
|
if(!Terminal::hasFormattingCodes()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$u = $this->getMemoryUsage(true);
|
$u = Utils::getMemoryUsage(true);
|
||||||
$usage = round(($u[0] / 1024) / 1024, 2) . "/".round(($u[1] / 1024) / 1024, 2)." MB @ " . $this->getThreadCount() . " threads";
|
$usage = round(($u[0] / 1024) / 1024, 2) . "/" . round(($u[1] / 1024) / 1024, 2) . "/".round(($u[2] / 1024) / 1024, 2)." MB @ " . Utils::getThreadCount() . " threads";
|
||||||
|
|
||||||
echo "\x1b]0;" . $this->getName() . " " .
|
echo "\x1b]0;" . $this->getName() . " " .
|
||||||
$this->getPocketMineVersion() .
|
$this->getPocketMineVersion() .
|
||||||
@ -2305,47 +2312,6 @@ class Server{
|
|||||||
$this->network->resetStatistics();
|
$this->network->resetStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMemoryUsage($advanced = false){
|
|
||||||
$VmSize = null;
|
|
||||||
$VmRSS = null;
|
|
||||||
if(Utils::getOS() === "linux" or Utils::getOS() === "bsd"){
|
|
||||||
$status = file_get_contents("/proc/self/status");
|
|
||||||
if(preg_match("/VmRSS:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
|
||||||
$VmRSS = $matches[1] * 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
|
||||||
$VmSize = $matches[1] * 1024;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($VmRSS === null){
|
|
||||||
$VmRSS = memory_get_usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$advanced){
|
|
||||||
return $VmRSS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($VmSize === null){
|
|
||||||
$VmSize = memory_get_usage(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$VmRSS, $VmSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getThreadCount(){
|
|
||||||
if(Utils::getOS() === "linux" or Utils::getOS() === "bsd"){
|
|
||||||
|
|
||||||
if(preg_match("/Threads:[ \t]+([0-9]+)/", file_get_contents("/proc/self/status"), $matches) > 0){
|
|
||||||
return (int) $matches[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count(ThreadManager::getInstance()->getAll()) + 3; //RakLib + MainLogger + Main Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $address
|
* @param string $address
|
||||||
* @param int $port
|
* @param int $port
|
||||||
@ -2365,7 +2331,7 @@ class Server{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->blockAddress($address, 600);
|
$this->getNetwork()->blockAddress($address, 600);
|
||||||
}
|
}
|
||||||
//TODO: add raw packet events
|
//TODO: add raw packet events
|
||||||
}
|
}
|
||||||
@ -2398,7 +2364,7 @@ class Server{
|
|||||||
|
|
||||||
if(($this->tickCounter & 0b1111) === 0){
|
if(($this->tickCounter & 0b1111) === 0){
|
||||||
$this->titleTick();
|
$this->titleTick();
|
||||||
if(isset($this->queryHandler) and ($this->tickCounter & 0b111111111) === 0){
|
if($this->queryHandler !== null and ($this->tickCounter & 0b111111111) === 0){
|
||||||
try{
|
try{
|
||||||
$this->queryHandler->regenerateInfo();
|
$this->queryHandler->regenerateInfo();
|
||||||
}catch(\Exception $e){
|
}catch(\Exception $e){
|
||||||
@ -2415,6 +2381,7 @@ class Server{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->getMemoryManager()->check();
|
||||||
|
|
||||||
Timings::$serverTickTimer->stopTiming();
|
Timings::$serverTickTimer->stopTiming();
|
||||||
|
|
||||||
|
@ -22,12 +22,18 @@
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
|
||||||
abstract class Flowable extends Transparent{
|
abstract class Flowable extends Transparent{
|
||||||
|
|
||||||
public function canBeFlowedInto(){
|
public function canBeFlowedInto(){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getBreakTime(Item $item){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public function isSolid(){
|
public function isSolid(){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ class CommandReader extends Thread{
|
|||||||
if(($line = $this->readLine()) !== ""){
|
if(($line = $this->readLine()) !== ""){
|
||||||
$this->buffer->synchronized(function (\Threaded $buffer, $line){
|
$this->buffer->synchronized(function (\Threaded $buffer, $line){
|
||||||
$buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
$buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
||||||
}, $this->buffer, $line );
|
}, $this->buffer, $line);
|
||||||
}elseif((microtime(true) - $lastLine) <= 0.1){ //Non blocking! Sleep to save CPU
|
}elseif((microtime(true) - $lastLine) <= 0.1){ //Non blocking! Sleep to save CPU
|
||||||
usleep(40000);
|
usleep(40000);
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,9 @@ class BanListCommand extends VanillaCommand{
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
$list = $sender->getServer()->getNameBans();
|
||||||
|
$args[0] = "players";
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = "";
|
$message = "";
|
||||||
|
@ -23,6 +23,7 @@ namespace pocketmine\command\defaults;
|
|||||||
|
|
||||||
use pocketmine\command\CommandSender;
|
use pocketmine\command\CommandSender;
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
|
use pocketmine\utils\Utils;
|
||||||
|
|
||||||
class StatusCommand extends VanillaCommand{
|
class StatusCommand extends VanillaCommand{
|
||||||
|
|
||||||
@ -40,14 +41,16 @@ class StatusCommand extends VanillaCommand{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mUsage = Utils::getMemoryUsage(true);
|
||||||
|
|
||||||
$server = $sender->getServer();
|
$server = $sender->getServer();
|
||||||
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
|
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
|
||||||
$sender->sendMessage(TextFormat::GOLD . "TPS: " . TextFormat::WHITE . $server->getTicksPerSecond());
|
$sender->sendMessage(TextFormat::GOLD . "TPS: " . TextFormat::WHITE . $server->getTicksPerSecond());
|
||||||
$sender->sendMessage(TextFormat::GOLD . "TPS Load: " . TextFormat::WHITE . $server->getTickUsage() . "%");
|
$sender->sendMessage(TextFormat::GOLD . "TPS Load: " . TextFormat::WHITE . $server->getTickUsage() . "%");
|
||||||
$sender->sendMessage(TextFormat::GOLD . "Upload: " . TextFormat::WHITE . round($server->getNetwork()->getUpload() / 1024, 2) . " kB/s");
|
$sender->sendMessage(TextFormat::GOLD . "Upload: " . TextFormat::WHITE . round($server->getNetwork()->getUpload() / 1024, 2) . " kB/s");
|
||||||
$sender->sendMessage(TextFormat::GOLD . "Download: " . TextFormat::WHITE . round($server->getNetwork()->getDownload() / 1024, 2) . " kB/s");
|
$sender->sendMessage(TextFormat::GOLD . "Download: " . TextFormat::WHITE . round($server->getNetwork()->getDownload() / 1024, 2) . " kB/s");
|
||||||
$sender->sendMessage(TextFormat::GOLD . "Memory: " . TextFormat::WHITE . round(($server->getMemoryUsage() / 1024) / 1024, 2) . " MB");
|
$sender->sendMessage(TextFormat::GOLD . "Memory: " . TextFormat::WHITE . round(($mUsage[0] / 1024) / 1024, 2) . "/".round(($mUsage[1] / 1024) / 1024, 2) . "/".round(($mUsage[2] / 1024) / 1024, 2) . " MB");
|
||||||
$sender->sendMessage(TextFormat::GOLD . "Threads: " . TextFormat::WHITE . $server->getThreadCount());
|
$sender->sendMessage(TextFormat::GOLD . "Threads: " . TextFormat::WHITE . Utils::getThreadCount());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@ class Squid extends WaterAnimal implements Ageable{
|
|||||||
public $swimDirection = null;
|
public $swimDirection = null;
|
||||||
public $swimSpeed = 0.1;
|
public $swimSpeed = 0.1;
|
||||||
|
|
||||||
|
private $switchDirectionTicker = 0;
|
||||||
|
|
||||||
public function initEntity(){
|
public function initEntity(){
|
||||||
$this->setMaxHealth(5);
|
$this->setMaxHealth(5);
|
||||||
parent::initEntity();
|
parent::initEntity();
|
||||||
@ -80,9 +82,12 @@ class Squid extends WaterAnimal implements Ageable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($currentTick % 20 === 0 and mt_rand(0, 100) < 5){
|
if(++$this->switchDirectionTicker === 100){
|
||||||
|
$this->switchDirectionTicker = 0;
|
||||||
|
if(mt_rand(0, 100) < 50){
|
||||||
$this->swimDirection = null;
|
$this->swimDirection = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->lastUpdate = $currentTick;
|
$this->lastUpdate = $currentTick;
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ abstract class Timings{
|
|||||||
/** @var TimingsHandler */
|
/** @var TimingsHandler */
|
||||||
public static $serverTickTimer;
|
public static $serverTickTimer;
|
||||||
/** @var TimingsHandler */
|
/** @var TimingsHandler */
|
||||||
|
public static $garbageCollectorTimer;
|
||||||
|
/** @var TimingsHandler */
|
||||||
public static $playerListTimer;
|
public static $playerListTimer;
|
||||||
/** @var TimingsHandler */
|
/** @var TimingsHandler */
|
||||||
public static $connectionTimer;
|
public static $connectionTimer;
|
||||||
@ -96,6 +98,7 @@ abstract class Timings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
self::$serverTickTimer = new TimingsHandler("** Full Server Tick");
|
self::$serverTickTimer = new TimingsHandler("** Full Server Tick");
|
||||||
|
self::$garbageCollectorTimer = new TimingsHandler("Garbage Collector");
|
||||||
self::$playerListTimer = new TimingsHandler("Player List");
|
self::$playerListTimer = new TimingsHandler("Player List");
|
||||||
self::$connectionTimer = new TimingsHandler("Connection Handler");
|
self::$connectionTimer = new TimingsHandler("Connection Handler");
|
||||||
self::$tickablesTimer = new TimingsHandler("Tickables");
|
self::$tickablesTimer = new TimingsHandler("Tickables");
|
||||||
|
79
src/pocketmine/event/server/LowMemoryEvent.php
Normal file
79
src/pocketmine/event/server/LowMemoryEvent.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?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/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace pocketmine\event\server;
|
||||||
|
use pocketmine\utils\Utils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the server is in a low-memory state as defined by the properties
|
||||||
|
* Plugins should free caches or other non-essential data.
|
||||||
|
*/
|
||||||
|
class LowMemoryEvent extends ServerEvent{
|
||||||
|
public static $handlerList = null;
|
||||||
|
|
||||||
|
private $memory;
|
||||||
|
private $memoryLimit;
|
||||||
|
private $triggerCount;
|
||||||
|
|
||||||
|
public function __construct($memory, $memoryLimit, $triggerCount = 0){
|
||||||
|
$this->memory = $memory;
|
||||||
|
$this->memoryLimit = $memoryLimit;
|
||||||
|
$this->triggerCount = (int) $triggerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the memory usage at the time of the event call (in bytes)
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMemory(){
|
||||||
|
return $this->memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the memory limit defined (in bytes)
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMemoryLimit(){
|
||||||
|
return $this->memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the times this event has been called in the current low-memory state
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTriggerCount(){
|
||||||
|
return $this->triggerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of memory already freed
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMemoryFreed(){
|
||||||
|
return $this->getMemory() - Utils::getMemoryUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -93,12 +93,10 @@ use pocketmine\scheduler\AsyncTask;
|
|||||||
use pocketmine\Server;
|
use pocketmine\Server;
|
||||||
use pocketmine\tile\Chest;
|
use pocketmine\tile\Chest;
|
||||||
use pocketmine\tile\Tile;
|
use pocketmine\tile\Tile;
|
||||||
use pocketmine\utils\Cache;
|
|
||||||
use pocketmine\utils\LevelException;
|
use pocketmine\utils\LevelException;
|
||||||
use pocketmine\utils\MainLogger;
|
use pocketmine\utils\MainLogger;
|
||||||
use pocketmine\utils\Random;
|
use pocketmine\utils\Random;
|
||||||
use pocketmine\utils\ReversePriorityQueue;
|
use pocketmine\utils\ReversePriorityQueue;
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use pocketmine\level\particle\Particle;
|
use pocketmine\level\particle\Particle;
|
||||||
use pocketmine\level\sound\Sound;
|
use pocketmine\level\sound\Sound;
|
||||||
use pocketmine\entity\Effect;
|
use pocketmine\entity\Effect;
|
||||||
@ -145,6 +143,8 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
|
|
||||||
private $cacheChunks = false;
|
private $cacheChunks = false;
|
||||||
|
|
||||||
|
private $sendTimeTicker = 0;
|
||||||
|
|
||||||
/** @var Server */
|
/** @var Server */
|
||||||
private $server;
|
private $server;
|
||||||
|
|
||||||
@ -512,6 +512,7 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
*/
|
*/
|
||||||
public function freeChunk($X, $Z, Player $player){
|
public function freeChunk($X, $Z, Player $player){
|
||||||
unset($this->usedChunks[$index = Level::chunkHash($X, $Z)][$player->getId()]);
|
unset($this->usedChunks[$index = Level::chunkHash($X, $Z)][$player->getId()]);
|
||||||
|
|
||||||
$this->unloadChunkRequest($X, $Z, true);
|
$this->unloadChunkRequest($X, $Z, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -553,8 +554,9 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
|
|
||||||
$this->checkTime();
|
$this->checkTime();
|
||||||
|
|
||||||
if(($currentTick % 200) === 0){
|
if(++$this->sendTimeTicker === 200){
|
||||||
$this->sendTime();
|
$this->sendTime();
|
||||||
|
$this->sendTimeTicker = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->unloadChunks();
|
$this->unloadChunks();
|
||||||
@ -646,7 +648,11 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
Server::broadcastPacket($target, $pk->setChannel(Network::CHANNEL_BLOCKS));
|
Server::broadcastPacket($target, $pk->setChannel(Network::CHANNEL_BLOCKS));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearCache(){
|
public function clearCache($full = false){
|
||||||
|
if($full){
|
||||||
|
$this->chunkCache = [];
|
||||||
|
}
|
||||||
|
|
||||||
$this->blockCache = [];
|
$this->blockCache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1987,11 +1993,11 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function chunkRequestCallback($x, $z, $payload){
|
public function chunkRequestCallback($x, $z, &$payload){
|
||||||
$index = Level::chunkHash($x, $z);
|
$index = Level::chunkHash($x, $z);
|
||||||
|
|
||||||
if(!isset($this->chunkCache[$index]) and $this->cacheChunks){
|
if(!isset($this->chunkCache[$index]) and $this->cacheChunks and $this->server->getMemoryManager()->canUseChunkCache()){
|
||||||
$this->chunkCache[$index] = $payload;
|
$this->chunkCache[$index] =& $payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->chunkSendTasks[$index])){
|
if(isset($this->chunkSendTasks[$index])){
|
||||||
@ -2077,7 +2083,7 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isChunkInUse($x, $z){
|
public function isChunkInUse($x, $z){
|
||||||
return isset($this->usedChunks[Level::chunkHash($x, $z)]) and count($this->usedChunks[Level::chunkHash($x, $z)]) > 0;
|
return isset($this->usedChunks[$index = Level::chunkHash($x, $z)]) and count($this->usedChunks[$index]) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2118,6 +2124,8 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$chunk->setChanged(false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2145,6 +2153,10 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!$this->isChunkLoaded($x, $z)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$this->timings->doChunkUnload->startTiming();
|
$this->timings->doChunkUnload->startTiming();
|
||||||
|
|
||||||
$index = Level::chunkHash($x, $z);
|
$index = Level::chunkHash($x, $z);
|
||||||
@ -2154,6 +2166,7 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
if($chunk !== null and $chunk->getProvider() !== null){
|
if($chunk !== null and $chunk->getProvider() !== null){
|
||||||
$this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk));
|
$this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk));
|
||||||
if($ev->isCancelled()){
|
if($ev->isCancelled()){
|
||||||
|
$this->timings->doChunkUnload->stopTiming();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2187,6 +2200,10 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
unset($this->chunkTickList[$index]);
|
unset($this->chunkTickList[$index]);
|
||||||
unset($this->chunkCache[$index]);
|
unset($this->chunkCache[$index]);
|
||||||
|
|
||||||
|
$refs = \pocketmine\getReferenceCount($chunk);
|
||||||
|
|
||||||
|
//$this->server->getLogger()->debug("Unloaded $x $z (".count($this->getChunks()).") [refs $refs]");
|
||||||
|
|
||||||
$this->timings->doChunkUnload->stopTiming();
|
$this->timings->doChunkUnload->stopTiming();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -288,25 +288,25 @@ interface FullChunk{
|
|||||||
/**
|
/**
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
public function getBiomeIdArray();
|
public function &getBiomeIdArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int[]
|
* @return int[]
|
||||||
*/
|
*/
|
||||||
public function getBiomeColorArray();
|
public function &getBiomeColorArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int[]
|
* @return int[]
|
||||||
*/
|
*/
|
||||||
public function getHeightMapArray();
|
public function &getHeightMapArray();
|
||||||
|
|
||||||
public function getBlockIdArray();
|
public function &getBlockIdArray();
|
||||||
|
|
||||||
public function getBlockDataArray();
|
public function &getBlockDataArray();
|
||||||
|
|
||||||
public function getBlockSkyLightArray();
|
public function &getBlockSkyLightArray();
|
||||||
|
|
||||||
public function getBlockLightArray();
|
public function &getBlockLightArray();
|
||||||
|
|
||||||
public function toBinary();
|
public function toBinary();
|
||||||
|
|
||||||
@ -328,7 +328,7 @@ interface FullChunk{
|
|||||||
*
|
*
|
||||||
* @return FullChunk
|
* @return FullChunk
|
||||||
*/
|
*/
|
||||||
public static function fromBinary($data, LevelProvider $provider = null);
|
public static function fromBinary(&$data, LevelProvider $provider = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $data
|
* @param string $data
|
||||||
@ -336,6 +336,6 @@ interface FullChunk{
|
|||||||
*
|
*
|
||||||
* @return FullChunk
|
* @return FullChunk
|
||||||
*/
|
*/
|
||||||
public static function fromFastBinary($data, LevelProvider $provider = null);
|
public static function fromFastBinary(&$data, LevelProvider $provider = null);
|
||||||
|
|
||||||
}
|
}
|
@ -87,13 +87,13 @@ abstract class BaseFullChunk implements FullChunk{
|
|||||||
$this->x = (int) $x;
|
$this->x = (int) $x;
|
||||||
$this->z = (int) $z;
|
$this->z = (int) $z;
|
||||||
|
|
||||||
$this->blocks = $blocks;
|
$this->blocks =& $blocks;
|
||||||
$this->data = $data;
|
$this->data =& $data;
|
||||||
$this->skyLight = $skyLight;
|
$this->skyLight =& $skyLight;
|
||||||
$this->blockLight = $blockLight;
|
$this->blockLight =& $blockLight;
|
||||||
|
|
||||||
if(strlen($biomeIds) === 256){
|
if(strlen($biomeIds) === 256){
|
||||||
$this->biomeIds = $biomeIds;
|
$this->biomeIds =& $biomeIds;
|
||||||
}else{
|
}else{
|
||||||
$this->biomeIds = str_repeat("\x01", 256);
|
$this->biomeIds = str_repeat("\x01", 256);
|
||||||
}
|
}
|
||||||
@ -321,31 +321,31 @@ abstract class BaseFullChunk implements FullChunk{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockIdArray(){
|
public function &getBlockIdArray(){
|
||||||
return $this->blocks;
|
return $this->blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockDataArray(){
|
public function &getBlockDataArray(){
|
||||||
return $this->data;
|
return $this->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockSkyLightArray(){
|
public function &getBlockSkyLightArray(){
|
||||||
return $this->skyLight;
|
return $this->skyLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockLightArray(){
|
public function &getBlockLightArray(){
|
||||||
return $this->blockLight;
|
return $this->blockLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBiomeIdArray(){
|
public function &getBiomeIdArray(){
|
||||||
return $this->biomeIds;
|
return $this->biomeIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBiomeColorArray(){
|
public function &getBiomeColorArray(){
|
||||||
return $this->biomeColors;
|
return $this->biomeColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHeightMapArray(){
|
public function &getHeightMapArray(){
|
||||||
return $this->heightMap;
|
return $this->heightMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,11 +357,11 @@ abstract class BaseFullChunk implements FullChunk{
|
|||||||
$this->hasChanged = (bool) $changed;
|
$this->hasChanged = (bool) $changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
public static function &fromFastBinary(&$data, LevelProvider $provider = null){
|
||||||
static::fromBinary($data, $provider);
|
static::fromBinary($data, $provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toFastBinary(){
|
public function &toFastBinary(){
|
||||||
return $this->toBinary();
|
return $this->toBinary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ class Chunk extends BaseFullChunk{
|
|||||||
$this->isGenerated = (bool) $value;
|
$this->isGenerated = (bool) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
public static function fromFastBinary(&$data, LevelProvider $provider = null){
|
||||||
return self::fromBinary($data, $provider);
|
return self::fromBinary($data, $provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ class Chunk extends BaseFullChunk{
|
|||||||
*
|
*
|
||||||
* @return Chunk
|
* @return Chunk
|
||||||
*/
|
*/
|
||||||
public static function fromBinary($data, LevelProvider $provider = null){
|
public static function fromBinary(&$data, LevelProvider $provider = null){
|
||||||
try{
|
try{
|
||||||
$chunkX = Binary::readLInt(substr($data, 0, 4));
|
$chunkX = Binary::readLInt(substr($data, 0, 4));
|
||||||
$chunkZ = Binary::readLInt(substr($data, 4, 4));
|
$chunkZ = Binary::readLInt(substr($data, 4, 4));
|
||||||
|
@ -276,7 +276,7 @@ class Chunk extends BaseFullChunk{
|
|||||||
*
|
*
|
||||||
* @return Chunk
|
* @return Chunk
|
||||||
*/
|
*/
|
||||||
public static function fromBinary($data, LevelProvider $provider = null){
|
public static function fromBinary(&$data, LevelProvider $provider = null){
|
||||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
@ -293,7 +293,7 @@ class Chunk extends BaseFullChunk{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
public static function fromFastBinary(&$data, LevelProvider $provider = null){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
@ -340,11 +340,11 @@ class Chunk extends BaseFullChunk{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toFastBinary(){
|
public function &toFastBinary(){
|
||||||
$biomeColors = pack("N*", ...$this->getBiomeColorArray());
|
$biomeColors = pack("N*", ...$this->getBiomeColorArray());
|
||||||
$heightMap = pack("N*", ...$this->getHeightMapArray());
|
$heightMap = pack("N*", ...$this->getHeightMapArray());
|
||||||
|
|
||||||
return
|
$data =
|
||||||
Binary::writeInt($this->x) .
|
Binary::writeInt($this->x) .
|
||||||
Binary::writeInt($this->z) .
|
Binary::writeInt($this->z) .
|
||||||
$this->getBlockIdArray() .
|
$this->getBlockIdArray() .
|
||||||
@ -355,9 +355,10 @@ class Chunk extends BaseFullChunk{
|
|||||||
$biomeColors .
|
$biomeColors .
|
||||||
$heightMap .
|
$heightMap .
|
||||||
chr(($this->isPopulated() ? 1 << 1 : 0) + ($this->isGenerated() ? 1 : 0));
|
chr(($this->isPopulated() ? 1 << 1 : 0) + ($this->isGenerated() ? 1 : 0));
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toBinary(){
|
public function &toBinary(){
|
||||||
$nbt = clone $this->getNBT();
|
$nbt = clone $this->getNBT();
|
||||||
|
|
||||||
$nbt->xPos = new Int("xPos", $this->x);
|
$nbt->xPos = new Int("xPos", $this->x);
|
||||||
@ -400,7 +401,8 @@ class Chunk extends BaseFullChunk{
|
|||||||
$nbt->setName("Level");
|
$nbt->setName("Level");
|
||||||
$writer->setData(new Compound("", ["Level" => $nbt]));
|
$writer->setData(new Compound("", ["Level" => $nbt]));
|
||||||
|
|
||||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
$data =& $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +130,8 @@ class RegionLoader{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$chunk = Chunk::fromBinary(fread($this->filePointer, $length - 1), $this->levelProvider);
|
$data = fread($this->filePointer, $length - 1);
|
||||||
|
$chunk = Chunk::fromBinary($data, $this->levelProvider);
|
||||||
if($chunk instanceof Chunk){
|
if($chunk instanceof Chunk){
|
||||||
return $chunk;
|
return $chunk;
|
||||||
}elseif($forward === false){
|
}elseif($forward === false){
|
||||||
@ -156,12 +157,15 @@ class RegionLoader{
|
|||||||
$nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
$nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||||
$nbt->V = new Byte("V", self::VERSION);
|
$nbt->V = new Byte("V", self::VERSION);
|
||||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||||
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
|
$biomes = str_repeat(Binary::writeByte(-1), 256);
|
||||||
|
$nbt->Biomes = new ByteArray("Biomes", $biomes);
|
||||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||||
|
|
||||||
$nbt->Blocks = new ByteArray("Blocks", str_repeat("\x00", 32768));
|
$half = str_repeat("\x00", 16384);
|
||||||
$nbt->Data = new ByteArray("Data", $half = str_repeat("\x00", 16384));
|
$full = $half . $half;
|
||||||
|
$nbt->Blocks = new ByteArray("Blocks", $full);
|
||||||
|
$nbt->Data = new ByteArray("Data", $half);
|
||||||
$nbt->SkyLight = new ByteArray("SkyLight", $half);
|
$nbt->SkyLight = new ByteArray("SkyLight", $half);
|
||||||
$nbt->BlockLight = new ByteArray("BlockLight", $half);
|
$nbt->BlockLight = new ByteArray("BlockLight", $half);
|
||||||
|
|
||||||
@ -181,7 +185,7 @@ class RegionLoader{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function saveChunk($x, $z, $chunkData){
|
protected function saveChunk($x, $z, &$chunkData){
|
||||||
$length = strlen($chunkData) + 1;
|
$length = strlen($chunkData) + 1;
|
||||||
if($length + 4 > self::MAX_SECTOR_LENGTH){
|
if($length + 4 > self::MAX_SECTOR_LENGTH){
|
||||||
throw new ChunkException("Chunk is too big! ".($length + 4)." > ".self::MAX_SECTOR_LENGTH);
|
throw new ChunkException("Chunk is too big! ".($length + 4)." > ".self::MAX_SECTOR_LENGTH);
|
||||||
@ -208,7 +212,7 @@ class RegionLoader{
|
|||||||
|
|
||||||
public function writeChunk(FullChunk $chunk){
|
public function writeChunk(FullChunk $chunk){
|
||||||
$this->lastUsed = time();
|
$this->lastUsed = time();
|
||||||
$chunkData = $chunk->toBinary();
|
$chunkData = &$chunk->toBinary();
|
||||||
if($chunkData !== false){
|
if($chunkData !== false){
|
||||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData);
|
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ class NBT{
|
|||||||
return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len);
|
return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function put($v){
|
public function put(&$v){
|
||||||
$this->buffer .= $v;
|
$this->buffer .= $v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ class NBT{
|
|||||||
|
|
||||||
public function read($buffer, $doMultiple = false){
|
public function read($buffer, $doMultiple = false){
|
||||||
$this->offset = 0;
|
$this->offset = 0;
|
||||||
$this->buffer = $buffer;
|
$this->buffer =& $buffer;
|
||||||
$this->data = $this->readTag();
|
$this->data = $this->readTag();
|
||||||
if($doMultiple and $this->offset < strlen($this->buffer)){
|
if($doMultiple and $this->offset < strlen($this->buffer)){
|
||||||
$this->data = [$this->data];
|
$this->data = [$this->data];
|
||||||
@ -117,28 +117,30 @@ class NBT{
|
|||||||
/**
|
/**
|
||||||
* @return string|bool
|
* @return string|bool
|
||||||
*/
|
*/
|
||||||
public function write(){
|
public function &write(){
|
||||||
$this->offset = 0;
|
$this->offset = 0;
|
||||||
|
$data = false;
|
||||||
if($this->data instanceof Compound){
|
if($this->data instanceof Compound){
|
||||||
$this->writeTag($this->data);
|
$this->writeTag($this->data);
|
||||||
|
|
||||||
return $this->buffer;
|
$data =& $this->buffer;
|
||||||
}elseif(is_array($this->data)){
|
}elseif(is_array($this->data)){
|
||||||
foreach($this->data as $tag){
|
foreach($this->data as $tag){
|
||||||
$this->writeTag($tag);
|
$this->writeTag($tag);
|
||||||
}
|
}
|
||||||
return $this->buffer;
|
$data =& $this->buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function writeCompressed($compression = ZLIB_ENCODING_GZIP, $level = 7){
|
public function &writeCompressed($compression = ZLIB_ENCODING_GZIP, $level = 7){
|
||||||
|
$data = false;
|
||||||
if(($write = $this->write()) !== false){
|
if(($write = $this->write()) !== false){
|
||||||
return zlib_encode($write, $compression, $level);
|
$data = zlib_encode($write, $compression, $level);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function readTag(){
|
public function readTag(){
|
||||||
@ -256,7 +258,7 @@ class NBT{
|
|||||||
return $this->get($this->getShort());
|
return $this->get($this->getShort());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function putString($v){
|
public function putString(&$v){
|
||||||
$this->putShort(strlen($v));
|
$this->putShort(strlen($v));
|
||||||
$this->buffer .= $v;
|
$this->buffer .= $v;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,13 @@ use pocketmine\nbt\NBT;
|
|||||||
|
|
||||||
class ByteArray extends NamedTag{
|
class ByteArray extends NamedTag{
|
||||||
|
|
||||||
|
public function __construct($name = "", &$value = null){
|
||||||
|
$this->name = $name;
|
||||||
|
if($value !== null){
|
||||||
|
$this->value =& $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getType(){
|
public function getType(){
|
||||||
return NBT::TAG_ByteArray;
|
return NBT::TAG_ByteArray;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ class IntArray extends NamedTag{
|
|||||||
|
|
||||||
public function write(NBT $nbt){
|
public function write(NBT $nbt){
|
||||||
$nbt->putInt(count($this->value));
|
$nbt->putInt(count($this->value));
|
||||||
$nbt->put(pack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", ...$this->value));
|
$data = pack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", ...$this->value);
|
||||||
|
$nbt->put($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,15 +30,15 @@ abstract class NamedTag extends Tag{
|
|||||||
* @param string $name
|
* @param string $name
|
||||||
* @param bool|float|double|int|byte|short|array|Compound|Enum|string $value
|
* @param bool|float|double|int|byte|short|array|Compound|Enum|string $value
|
||||||
*/
|
*/
|
||||||
public function __construct($name = "", $value = false){
|
public function __construct($name = "", $value = null){
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
if($value !== false){
|
if($value !== null){
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(){
|
public function &getName(){
|
||||||
return $this->name === false ? "" : $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setName($name){
|
public function setName($name){
|
||||||
|
@ -27,6 +27,13 @@ use pocketmine\nbt\NBT;
|
|||||||
|
|
||||||
class String extends NamedTag{
|
class String extends NamedTag{
|
||||||
|
|
||||||
|
public function __construct($name = "", $value = null){
|
||||||
|
$this->name = $name;
|
||||||
|
if($value !== null){
|
||||||
|
$this->value =& $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getType(){
|
public function getType(){
|
||||||
return NBT::TAG_String;
|
return NBT::TAG_String;
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,17 @@ class CompressBatchedTask extends AsyncTask{
|
|||||||
public $channel = 0;
|
public $channel = 0;
|
||||||
public $targets = [];
|
public $targets = [];
|
||||||
|
|
||||||
|
public function __construct(&$data, array $targets, $level = 7, $channel = 0){
|
||||||
|
$this->data = $data;
|
||||||
|
$this->targets = $targets;
|
||||||
|
$this->level = $level;
|
||||||
|
$this->channel = $channel;
|
||||||
|
}
|
||||||
|
|
||||||
public function onRun(){
|
public function onRun(){
|
||||||
try{
|
try{
|
||||||
$this->final = zlib_encode($this->data, ZLIB_ENCODING_DEFLATE, $this->level);
|
$this->final = zlib_encode($this->data, ZLIB_ENCODING_DEFLATE, $this->level);
|
||||||
|
$this->data = null;
|
||||||
}catch(\Exception $e){
|
}catch(\Exception $e){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ use pocketmine\network\protocol\UnknownPacket;
|
|||||||
use pocketmine\Player;
|
use pocketmine\Player;
|
||||||
use pocketmine\Server;
|
use pocketmine\Server;
|
||||||
use pocketmine\utils\MainLogger;
|
use pocketmine\utils\MainLogger;
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use raklib\protocol\EncapsulatedPacket;
|
use raklib\protocol\EncapsulatedPacket;
|
||||||
use raklib\RakLib;
|
use raklib\RakLib;
|
||||||
use raklib\server\RakLibServer;
|
use raklib\server\RakLibServer;
|
||||||
@ -37,9 +36,6 @@ use raklib\server\ServerInstance;
|
|||||||
|
|
||||||
class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||||
|
|
||||||
/** @var \SplFixedArray */
|
|
||||||
private $packetPool;
|
|
||||||
|
|
||||||
/** @var Server */
|
/** @var Server */
|
||||||
private $server;
|
private $server;
|
||||||
|
|
||||||
@ -145,7 +141,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
|||||||
public function handleEncapsulated($identifier, EncapsulatedPacket $packet, $flags){
|
public function handleEncapsulated($identifier, EncapsulatedPacket $packet, $flags){
|
||||||
if(isset($this->players[$identifier])){
|
if(isset($this->players[$identifier])){
|
||||||
try{
|
try{
|
||||||
$pk = $this->getPacket($packet->buffer);
|
$pk = &$this->getPacket($packet->buffer);
|
||||||
$pk->decode();
|
$pk->decode();
|
||||||
$this->players[$identifier]->handleDataPacket($pk);
|
$this->players[$identifier]->handleDataPacket($pk);
|
||||||
}catch(\Exception $e){
|
}catch(\Exception $e){
|
||||||
@ -250,7 +246,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPacket($buffer){
|
private function &getPacket(&$buffer){
|
||||||
$pid = ord($buffer{0});
|
$pid = ord($buffer{0});
|
||||||
|
|
||||||
if(($data = $this->network->getPacket($pid)) === null){
|
if(($data = $this->network->getPacket($pid)) === null){
|
||||||
|
@ -21,7 +21,48 @@ settings:
|
|||||||
#Number of AsyncTask workers.
|
#Number of AsyncTask workers.
|
||||||
#Used for plugin asynchronous tasks, world generation, compression and web communication.
|
#Used for plugin asynchronous tasks, world generation, compression and web communication.
|
||||||
#Set this approximately to your number of cores.
|
#Set this approximately to your number of cores.
|
||||||
async-workers: 2
|
#If set to auto, it'll try to detect the number of cores (or use 2)
|
||||||
|
async-workers: auto
|
||||||
|
|
||||||
|
memory:
|
||||||
|
#Global soft memory limit in megabytes. Set to 0 to disable
|
||||||
|
#This will trigger low-memory-triggers and fire an event to free memory when the usage goes over this
|
||||||
|
global-limit: 512
|
||||||
|
|
||||||
|
#Main thread soft memory limit in megabytes. Set to 0 to disable
|
||||||
|
#This will trigger low-memory-triggers and fire an event to free memory when the usage goes over this
|
||||||
|
main-limit: 320
|
||||||
|
|
||||||
|
#Period in ticks to check memory (default 1 second)
|
||||||
|
check-rate: 20
|
||||||
|
|
||||||
|
#Continue firing low-memory-triggers and event while on low memory
|
||||||
|
continuous-trigger: true
|
||||||
|
|
||||||
|
#Only if memory.continuous-trigger is enabled. Specifies the rate in memory.check-rate steps (default 30 seconds)
|
||||||
|
continuous-trigger-rate: 30
|
||||||
|
|
||||||
|
garbage-collection:
|
||||||
|
#Period in ticks to fire the garbage collector manually (default 10 minutes), set to 0 to disable
|
||||||
|
#This only affect the main thread. Other threads should fire their own collections
|
||||||
|
period: 12000
|
||||||
|
|
||||||
|
#Fire asynchronous tasks to collect garbage from workers
|
||||||
|
collect-async-worker: true
|
||||||
|
|
||||||
|
#Trigger on low memory
|
||||||
|
low-memory-trigger: true
|
||||||
|
|
||||||
|
max-chunks:
|
||||||
|
#Limit of chunks to load per player, overrides chunk-sending.max-chunks
|
||||||
|
trigger-limit: 96
|
||||||
|
#Trigger on low memory
|
||||||
|
low-memory-trigger: true
|
||||||
|
|
||||||
|
world-caches:
|
||||||
|
disable-chunk-cache: true
|
||||||
|
low-memory-trigger: true
|
||||||
|
|
||||||
|
|
||||||
network:
|
network:
|
||||||
#Threshold for batching packets, in bytes. Only these packets will be compressed
|
#Threshold for batching packets, in bytes. Only these packets will be compressed
|
||||||
@ -51,9 +92,9 @@ level-settings:
|
|||||||
|
|
||||||
chunk-sending:
|
chunk-sending:
|
||||||
#Amount of chunks sent to players per tick
|
#Amount of chunks sent to players per tick
|
||||||
per-tick: 8
|
per-tick: 4
|
||||||
#Amount of chunks sent around each player
|
#Amount of chunks sent around each player
|
||||||
max-chunks: 256
|
max-chunks: 192
|
||||||
#Amount of chunks that need to be sent before spawning the player
|
#Amount of chunks that need to be sent before spawning the player
|
||||||
spawn-threshold: 56
|
spawn-threshold: 56
|
||||||
#Save a serialized copy of the chunk in memory for faster sending
|
#Save a serialized copy of the chunk in memory for faster sending
|
||||||
|
@ -47,7 +47,7 @@ class AsyncPool{
|
|||||||
for($i = 0; $i < $this->size; ++$i){
|
for($i = 0; $i < $this->size; ++$i){
|
||||||
$this->workerUsage[$i] = 0;
|
$this->workerUsage[$i] = 0;
|
||||||
$this->workers[$i] = new AsyncWorker;
|
$this->workers[$i] = new AsyncWorker;
|
||||||
$this->workers[$i]->setClassLoader($server->getLoader());
|
$this->workers[$i]->setClassLoader($this->server->getLoader());
|
||||||
$this->workers[$i]->start();
|
$this->workers[$i]->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,13 +56,41 @@ class AsyncPool{
|
|||||||
return $this->size;
|
return $this->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submitTask(AsyncTask $task){
|
public function increaseSize($newSize){
|
||||||
|
$newSize = (int) $newSize;
|
||||||
|
if($newSize > $this->size){
|
||||||
|
$this->size = $newSize;
|
||||||
|
for($i = $this->size; $i < $newSize; ++$i){
|
||||||
|
$this->workerUsage[$i] = 0;
|
||||||
|
$this->workers[$i] = new AsyncWorker;
|
||||||
|
$this->workers[$i]->setClassLoader($this->server->getLoader());
|
||||||
|
$this->workers[$i]->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submitTaskToWorker(AsyncTask $task, $worker){
|
||||||
if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){
|
if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$worker = (int) $worker;
|
||||||
|
if($worker < 0 or $worker >= $this->size){
|
||||||
|
throw new \InvalidArgumentException("Invalid worker $worker");
|
||||||
|
}
|
||||||
|
|
||||||
$this->tasks[$task->getTaskId()] = $task;
|
$this->tasks[$task->getTaskId()] = $task;
|
||||||
|
|
||||||
|
$this->workers[$worker]->stack($task);
|
||||||
|
$this->workerUsage[$worker]++;
|
||||||
|
$this->taskWorkers[$task->getTaskId()] = $worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submitTask(AsyncTask $task){
|
||||||
|
if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$selectedWorker = mt_rand(0, $this->size - 1);
|
$selectedWorker = mt_rand(0, $this->size - 1);
|
||||||
$selectedTasks = $this->workerUsage[$selectedWorker];
|
$selectedTasks = $this->workerUsage[$selectedWorker];
|
||||||
for($i = 0; $i < $this->size; ++$i){
|
for($i = 0; $i < $this->size; ++$i){
|
||||||
@ -72,9 +100,7 @@ class AsyncPool{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->workers[$selectedWorker]->stack($task);
|
$this->submitTaskToWorker($task, $selectedWorker);
|
||||||
$this->workerUsage[$selectedWorker]++;
|
|
||||||
$this->taskWorkers[$task->getTaskId()] = $selectedWorker;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function removeTask(AsyncTask $task){
|
private function removeTask(AsyncTask $task){
|
||||||
@ -85,6 +111,8 @@ class AsyncPool{
|
|||||||
|
|
||||||
unset($this->tasks[$task->getTaskId()]);
|
unset($this->tasks[$task->getTaskId()]);
|
||||||
unset($this->taskWorkers[$task->getTaskId()]);
|
unset($this->taskWorkers[$task->getTaskId()]);
|
||||||
|
|
||||||
|
$task->cleanObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeTasks(){
|
public function removeTasks(){
|
||||||
|
@ -131,4 +131,10 @@ abstract class AsyncTask extends \Collectable{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function cleanObject(){
|
||||||
|
foreach($this as $p => $v){
|
||||||
|
$this->{$p} = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,11 @@ class AsyncWorker extends Worker{
|
|||||||
public function run(){
|
public function run(){
|
||||||
$this->registerClassLoader();
|
$this->registerClassLoader();
|
||||||
gc_enable();
|
gc_enable();
|
||||||
|
ini_set("memory_limit", -1);
|
||||||
|
|
||||||
global $store;
|
global $store;
|
||||||
$store = [];
|
$store = [];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start($options = PTHREADS_INHERIT_NONE){
|
public function start($options = PTHREADS_INHERIT_NONE){
|
||||||
|
30
src/pocketmine/scheduler/GarbageCollectionTask.php
Normal file
30
src/pocketmine/scheduler/GarbageCollectionTask.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?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
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace pocketmine\scheduler;
|
||||||
|
|
||||||
|
class GarbageCollectionTask extends AsyncTask{
|
||||||
|
|
||||||
|
public function onRun(){
|
||||||
|
gc_enable();
|
||||||
|
gc_collect_cycles();
|
||||||
|
}
|
||||||
|
}
|
@ -78,6 +78,28 @@ class ServerScheduler{
|
|||||||
$this->asyncPool->submitTask($task);
|
$this->asyncPool->submitTask($task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits an asynchronous task to a specific Worker in the Pool
|
||||||
|
*
|
||||||
|
* @param AsyncTask $task
|
||||||
|
* @param int $worker
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scheduleAsyncTaskToWorker(AsyncTask $task, $worker){
|
||||||
|
$id = $this->nextId();
|
||||||
|
$task->setTaskId($id);
|
||||||
|
$this->asyncPool->submitTaskToWorker($task, $worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAsyncTaskPoolSize(){
|
||||||
|
return $this->asyncPool->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function increaseAsyncTaskPoolSize($newSize){
|
||||||
|
$this->asyncPool->increaseSize($newSize);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Task $task
|
* @param Task $task
|
||||||
* @param int $delay
|
* @param int $delay
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
* Various Utilities used around the code
|
* Various Utilities used around the code
|
||||||
*/
|
*/
|
||||||
namespace pocketmine\utils;
|
namespace pocketmine\utils;
|
||||||
|
use pocketmine\ThreadManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Big collection of functions
|
* Big collection of functions
|
||||||
@ -177,6 +178,76 @@ class Utils{
|
|||||||
return self::$os;
|
return self::$os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getMemoryUsage($advanced = false){
|
||||||
|
$reserved = memory_get_usage();
|
||||||
|
$VmSize = null;
|
||||||
|
$VmRSS = null;
|
||||||
|
if(Utils::getOS() === "linux" or Utils::getOS() === "android"){
|
||||||
|
$status = file_get_contents("/proc/self/status");
|
||||||
|
if(preg_match("/VmRSS:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
||||||
|
$VmRSS = $matches[1] * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
||||||
|
$VmSize = $matches[1] * 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: more OS
|
||||||
|
|
||||||
|
if($VmRSS === null){
|
||||||
|
$VmRSS = memory_get_usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$advanced){
|
||||||
|
return $VmRSS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($VmSize === null){
|
||||||
|
$VmSize = memory_get_usage(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$reserved, $VmRSS, $VmSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getThreadCount(){
|
||||||
|
if(Utils::getOS() === "linux" or Utils::getOS() === "android"){
|
||||||
|
if(preg_match("/Threads:[ \t]+([0-9]+)/", file_get_contents("/proc/self/status"), $matches) > 0){
|
||||||
|
return (int) $matches[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: more OS
|
||||||
|
|
||||||
|
return count(ThreadManager::getInstance()->getAll()) + 3; //RakLib + MainLogger + Main Thread
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCoreCount(){
|
||||||
|
$processors = 0;
|
||||||
|
switch(Utils::getOS()){
|
||||||
|
case "linux":
|
||||||
|
case "android":
|
||||||
|
if(file_exists("/proc/cpuinfo")){
|
||||||
|
foreach(file("/proc/cpuinfo") as $l){
|
||||||
|
if(preg_match('/^processor[ \t]*:[ \t]*[0-9]+$/m', $l) > 0){
|
||||||
|
++$processors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "bsd":
|
||||||
|
$processors = (int) `sysctl hw.ncpu | awk '{ print $2+1 }'`;
|
||||||
|
break;
|
||||||
|
case "mac":
|
||||||
|
$processors = (int) `sysctl hw.availcpu | awk '{ print $2+1 }'`;
|
||||||
|
break;
|
||||||
|
case "win":
|
||||||
|
$processors = (int) getenv("NUMBER_OF_PROCESSORS");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return $processors;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a prettified hexdump
|
* Returns a prettified hexdump
|
||||||
*
|
*
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit a35e3d1535c327013de94e6d24078dc7573b57ec
|
Subproject commit cba90210cd8af5d924e29db6220927a0c896e12a
|
Loading…
x
Reference in New Issue
Block a user