mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-05 19:37:17 +00:00
Merge branch 'master' into mcpe-1.2
This commit is contained in:
commit
6b34c47c96
@ -25,7 +25,9 @@ namespace pocketmine;
|
||||
|
||||
use pocketmine\event\server\LowMemoryEvent;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\scheduler\DumpWorkerMemoryTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class MemoryManager{
|
||||
@ -74,6 +76,9 @@ class MemoryManager{
|
||||
/** @var bool */
|
||||
private $cacheTrigger;
|
||||
|
||||
/** @var bool */
|
||||
private $dumpWorkers = true;
|
||||
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
|
||||
@ -131,6 +136,7 @@ class MemoryManager{
|
||||
$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);
|
||||
|
||||
$this->dumpWorkers = (bool) $this->server->getProperty("memory.memory-dump.dump-async-worker", true);
|
||||
gc_enable();
|
||||
}
|
||||
|
||||
@ -261,6 +267,27 @@ class MemoryManager{
|
||||
* @param int $maxStringSize
|
||||
*/
|
||||
public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize){
|
||||
MainLogger::getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
|
||||
self::dumpMemory($this->server, $this->server->getLoader(), $outputFolder, $maxNesting, $maxStringSize);
|
||||
|
||||
if($this->dumpWorkers){
|
||||
$scheduler = $this->server->getScheduler();
|
||||
for($i = 0, $size = $scheduler->getAsyncTaskPoolSize(); $i < $size; ++$i){
|
||||
$scheduler->scheduleAsyncTaskToWorker(new DumpWorkerMemoryTask($outputFolder, $maxNesting, $maxStringSize), $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static memory dumper accessible from any thread.
|
||||
*
|
||||
* @param mixed $startingObject
|
||||
* @param \ClassLoader $loader
|
||||
* @param string $outputFolder
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
*/
|
||||
public static function dumpMemory($startingObject, \ClassLoader $loader, string $outputFolder, int $maxNesting, int $maxStringSize){
|
||||
$hardLimit = ini_get('memory_limit');
|
||||
ini_set('memory_limit', '-1');
|
||||
gc_disable();
|
||||
@ -269,12 +296,8 @@ class MemoryManager{
|
||||
mkdir($outputFolder, 0777, true);
|
||||
}
|
||||
|
||||
$this->server->getLogger()->notice("[Dump] After the memory dump is done, the server might crash");
|
||||
|
||||
$obData = fopen($outputFolder . "/objects.js", "wb+");
|
||||
|
||||
$staticProperties = [];
|
||||
|
||||
$data = [];
|
||||
|
||||
$objects = [];
|
||||
@ -283,8 +306,10 @@ class MemoryManager{
|
||||
|
||||
$instanceCounts = [];
|
||||
|
||||
$staticProperties = [];
|
||||
$staticCount = 0;
|
||||
foreach($this->server->getLoader()->getClasses() as $className){
|
||||
|
||||
foreach($loader->getClasses() as $className){
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$staticProperties[$className] = [];
|
||||
foreach($reflection->getProperties() as $property){
|
||||
@ -297,7 +322,7 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
$staticCount++;
|
||||
$this->continueDump($property->getValue(), $staticProperties[$className][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
self::continueDump($property->getValue(), $staticProperties[$className][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
if(count($staticProperties[$className]) === 0){
|
||||
@ -305,9 +330,39 @@ class MemoryManager{
|
||||
}
|
||||
}
|
||||
|
||||
echo "[Dump] Wrote $staticCount static properties\n";
|
||||
file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
MainLogger::getLogger()->info("[Dump] Wrote $staticCount static properties");
|
||||
|
||||
$this->continueDump($this->server, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
if($GLOBALS !== null){ //This might be null if we're on a different thread
|
||||
$globalVariables = [];
|
||||
$globalCount = 0;
|
||||
|
||||
$ignoredGlobals = [
|
||||
'GLOBALS' => true,
|
||||
'_SERVER' => true,
|
||||
'_REQUEST' => true,
|
||||
'_POST' => true,
|
||||
'_GET' => true,
|
||||
'_FILES' => true,
|
||||
'_ENV' => true,
|
||||
'_COOKIE' => true,
|
||||
'_SESSION' => true
|
||||
];
|
||||
|
||||
foreach($GLOBALS as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$globalCount++;
|
||||
self::continueDump($value, $globalVariables[$varName], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
file_put_contents($outputFolder . "/globalVariables.js", json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
MainLogger::getLogger()->info("[Dump] Wrote $globalCount global variables");
|
||||
}
|
||||
|
||||
self::continueDump($startingObject, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
|
||||
do{
|
||||
$continue = false;
|
||||
@ -349,25 +404,26 @@ class MemoryManager{
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
$this->continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
self::continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
fwrite($obData, "$hash@$className: " . json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
|
||||
}
|
||||
|
||||
echo "[Dump] Wrote " . count($objects) . " objects\n";
|
||||
|
||||
}while($continue);
|
||||
|
||||
MainLogger::getLogger()->info("[Dump] Wrote " . count($objects) . " objects");
|
||||
|
||||
fclose($obData);
|
||||
|
||||
file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents($outputFolder . "/serverEntry.js", json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
file_put_contents($outputFolder . "/referenceCounts.js", json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
|
||||
arsort($instanceCounts, SORT_NUMERIC);
|
||||
file_put_contents($outputFolder . "/instanceCounts.js", json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
|
||||
echo "[Dump] Finished!\n";
|
||||
MainLogger::getLogger()->info("[Dump] Finished!");
|
||||
|
||||
ini_set('memory_limit', $hardLimit);
|
||||
gc_enable();
|
||||
@ -382,7 +438,7 @@ class MemoryManager{
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
*/
|
||||
private function continueDump($from, &$data, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize){
|
||||
private static function continueDump($from, &$data, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize){
|
||||
if($maxNesting <= 0){
|
||||
$data = "(error) NESTING LIMIT REACHED";
|
||||
return;
|
||||
@ -406,7 +462,7 @@ class MemoryManager{
|
||||
}
|
||||
$data = [];
|
||||
foreach($from as $key => $value){
|
||||
$this->continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize);
|
||||
self::continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}elseif(is_string($from)){
|
||||
$data = "(string) len(". strlen($from) .") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||
|
@ -80,7 +80,7 @@ namespace pocketmine {
|
||||
use raklib\RakLib;
|
||||
|
||||
const VERSION = "1.7dev";
|
||||
const API_VERSION = "3.0.0-ALPHA7";
|
||||
const API_VERSION = "3.0.0-ALPHA8";
|
||||
const CODENAME = "[REDACTED]";
|
||||
|
||||
/*
|
||||
@ -558,19 +558,7 @@ namespace pocketmine {
|
||||
$killer->start();
|
||||
usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
|
||||
|
||||
$erroredThreads = 0;
|
||||
foreach(ThreadManager::getInstance()->getAll() as $id => $thread){
|
||||
$logger->debug("Stopping " . $thread->getThreadName() . " thread");
|
||||
try{
|
||||
$thread->quit();
|
||||
$logger->debug($thread->getThreadName() . " thread stopped successfully.");
|
||||
}catch(\ThreadException $e){
|
||||
++$erroredThreads;
|
||||
$logger->debug("Could not stop " . $thread->getThreadName() . " thread: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if($erroredThreads > 0){
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
echo "Some threads could not be stopped, performing a force-kill" . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\utils\MainLogger;
|
||||
|
||||
class ThreadManager extends \Volatile{
|
||||
|
||||
/** @var ThreadManager */
|
||||
@ -68,4 +70,23 @@ class ThreadManager extends \Volatile{
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function stopAll() : int{
|
||||
$logger = MainLogger::getLogger();
|
||||
|
||||
$erroredThreads = 0;
|
||||
|
||||
foreach($this->getAll() as $thread){
|
||||
$logger->debug("Stopping " . $thread->getThreadName() . " thread");
|
||||
try{
|
||||
$thread->quit();
|
||||
$logger->debug($thread->getThreadName() . " thread stopped successfully.");
|
||||
}catch(\ThreadException $e){
|
||||
++$erroredThreads;
|
||||
$logger->debug("Could not stop " . $thread->getThreadName() . " thread: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $erroredThreads;
|
||||
}
|
||||
}
|
@ -341,7 +341,7 @@ class BlockFactory{
|
||||
public static function registerBlock(Block $block, bool $override = false){
|
||||
$id = $block->getId();
|
||||
|
||||
if(self::$list[$id] !== null and !(self::$list[$id] instanceof UnknownBlock) and !$override){
|
||||
if(!$override and self::isRegistered($id)){
|
||||
throw new \RuntimeException("Trying to overwrite an already registered block");
|
||||
}
|
||||
|
||||
@ -403,4 +403,15 @@ class BlockFactory{
|
||||
public static function getBlockStatesArray() : \SplFixedArray{
|
||||
return self::$fullList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a specified block ID is already registered in the block factory.
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRegistered(int $id) : bool{
|
||||
$b = self::$list[$id];
|
||||
return $b !== null and !($b instanceof UnknownBlock);
|
||||
}
|
||||
}
|
@ -256,7 +256,7 @@ class ItemFactory{
|
||||
*/
|
||||
public static function registerItem(Item $item, bool $override = false){
|
||||
$id = $item->getId();
|
||||
if(!$override and self::$list[$id] !== null){
|
||||
if(!$override and self::isRegistered($id)){
|
||||
throw new \RuntimeException("Trying to overwrite an already registered item");
|
||||
}
|
||||
|
||||
@ -350,4 +350,17 @@ class ItemFactory{
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified item ID is already registered in the item factory.
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRegistered(int $id) : bool{
|
||||
if($id < 256){
|
||||
return BlockFactory::isRegistered($id);
|
||||
}
|
||||
return self::$list[$id] !== null;
|
||||
}
|
||||
}
|
@ -41,12 +41,9 @@ class Location extends Position{
|
||||
* @param Level $level
|
||||
*/
|
||||
public function __construct($x = 0, $y = 0, $z = 0, $yaw = 0.0, $pitch = 0.0, Level $level = null){
|
||||
$this->x = $x;
|
||||
$this->y = $y;
|
||||
$this->z = $z;
|
||||
$this->yaw = $yaw;
|
||||
$this->pitch = $pitch;
|
||||
$this->level = $level;
|
||||
parent::__construct($x, $y, $z, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,6 +62,12 @@ memory:
|
||||
#Trigger on low memory
|
||||
low-memory-trigger: true
|
||||
|
||||
#Settings controlling memory dump handling.
|
||||
memory-dump:
|
||||
#Dump memory from async workers as well as the main thread. If you have issues with segfaults when dumping memory, disable this setting.
|
||||
dump-async-worker: true
|
||||
|
||||
|
||||
max-chunks:
|
||||
#Maximum render distance per player when low memory is triggered
|
||||
chunk-radius: 4
|
||||
|
@ -64,4 +64,8 @@ class AsyncWorker extends Worker{
|
||||
public function getThreadName() : string{
|
||||
return "Asynchronous Worker #" . $this->id;
|
||||
}
|
||||
|
||||
public function getAsyncWorkerId() : int{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
61
src/pocketmine/scheduler/DumpWorkerMemoryTask.php
Normal file
61
src/pocketmine/scheduler/DumpWorkerMemoryTask.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\scheduler;
|
||||
|
||||
use pocketmine\MemoryManager;
|
||||
|
||||
/**
|
||||
* Task used to dump memory from AsyncWorkers
|
||||
*/
|
||||
class DumpWorkerMemoryTask extends AsyncTask{
|
||||
/** @var string */
|
||||
private $outputFolder;
|
||||
/** @var int */
|
||||
private $maxNesting;
|
||||
/** @var int */
|
||||
private $maxStringSize;
|
||||
|
||||
/**
|
||||
* @param string $outputFolder
|
||||
* @param int $maxNesting
|
||||
* @param int $maxStringSize
|
||||
*/
|
||||
public function __construct(string $outputFolder, int $maxNesting, int $maxStringSize){
|
||||
$this->outputFolder = $outputFolder;
|
||||
$this->maxNesting = $maxNesting;
|
||||
$this->maxStringSize = $maxStringSize;
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
global $store;
|
||||
|
||||
MemoryManager::dumpMemory(
|
||||
["worker" => $this->worker, "store" => $store],
|
||||
$this->worker->getClassLoader(),
|
||||
$this->outputFolder . DIRECTORY_SEPARATOR . "AsyncWorker#" . $this->worker->getAsyncWorkerId(),
|
||||
$this->maxNesting,
|
||||
$this->maxStringSize
|
||||
);
|
||||
}
|
||||
}
|
@ -89,15 +89,17 @@ class AutoUpdater{
|
||||
* Posts a warning to the console to tell the user there is an update available
|
||||
*/
|
||||
public function showConsoleUpdate(){
|
||||
$logger = $this->server->getLogger();
|
||||
$newVersion = new VersionString($this->updateInfo["version"]);
|
||||
$logger->warning("----- PocketMine-MP Auto Updater -----");
|
||||
$logger->warning("Your version of PocketMine-MP is out of date. Version " . $newVersion->get(false) . " (build #" . $newVersion->getBuild() . ") was released on " . date("D M j h:i:s Y", $this->updateInfo["date"]));
|
||||
|
||||
$messages = [
|
||||
"Your version of " . $this->server->getName() . " is out of date. Version " . $newVersion->get(false) . " (build #" . $newVersion->getBuild() . ") was released on " . date("D M j h:i:s Y", $this->updateInfo["date"])
|
||||
];
|
||||
if($this->updateInfo["details_url"] !== null){
|
||||
$logger->warning("Details: " . $this->updateInfo["details_url"]);
|
||||
$messages[] = "Details: " . $this->updateInfo["details_url"];
|
||||
}
|
||||
$logger->warning("Download: " . $this->updateInfo["download_url"]);
|
||||
$logger->warning("----- -------------------------- -----");
|
||||
$messages[] = "Download: " . $this->updateInfo["download_url"];
|
||||
|
||||
$this->printConsoleMessage($messages, \LogLevel::WARNING);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,24 +107,33 @@ class AutoUpdater{
|
||||
* @param Player $player
|
||||
*/
|
||||
public function showPlayerUpdate(Player $player){
|
||||
$player->sendMessage(TextFormat::DARK_PURPLE . "The version of PocketMine-MP that this server is running is out of date. Please consider updating to the latest version.");
|
||||
$player->sendMessage(TextFormat::DARK_PURPLE . "The version of " . $this->server->getName() . " that this server is running is out of date. Please consider updating to the latest version.");
|
||||
$player->sendMessage(TextFormat::DARK_PURPLE . "Check the console for more details.");
|
||||
}
|
||||
|
||||
protected function showChannelSuggestionStable(){
|
||||
$logger = $this->server->getLogger();
|
||||
$logger->info("----- PocketMine-MP Auto Updater -----");
|
||||
$logger->info("It appears you're running a Stable build, when you've specified that you prefer to run " . ucfirst($this->getChannel()) . " builds.");
|
||||
$logger->info("If you would like to be kept informed about new Stable builds only, it is recommended that you change 'preferred-channel' in your pocketmine.yml to 'stable'.");
|
||||
$logger->info("----- -------------------------- -----");
|
||||
$this->printConsoleMessage([
|
||||
"It appears you're running a Stable build, when you've specified that you prefer to run " . ucfirst($this->getChannel()) . " builds.",
|
||||
"If you would like to be kept informed about new Stable builds only, it is recommended that you change 'preferred-channel' in your pocketmine.yml to 'stable'."
|
||||
]);
|
||||
}
|
||||
|
||||
protected function showChannelSuggestionBeta(){
|
||||
$this->printConsoleMessage([
|
||||
"It appears you're running a Beta build, when you've specified that you prefer to run Stable builds.",
|
||||
"If you would like to be kept informed about new Beta or Development builds, it is recommended that you change 'preferred-channel' in your pocketmine.yml to 'beta' or 'development'."
|
||||
]);
|
||||
}
|
||||
|
||||
protected function printConsoleMessage(array $lines, string $logLevel = \LogLevel::INFO){
|
||||
$logger = $this->server->getLogger();
|
||||
$logger->info("----- PocketMine-MP Auto Updater -----");
|
||||
$logger->info("It appears you're running a Beta build, when you've specified that you prefer to run Stable builds.");
|
||||
$logger->info("If you would like to be kept informed about new Beta or Development builds, it is recommended that you change 'preferred-channel' in your pocketmine.yml to 'beta' or 'development'.");
|
||||
$logger->info("----- -------------------------- -----");
|
||||
|
||||
$title = $this->server->getName() . ' Auto Updater';
|
||||
$logger->log($logLevel, sprintf('----- %s -----', $title));
|
||||
foreach($lines as $line){
|
||||
$logger->log($logLevel, $line);
|
||||
}
|
||||
$logger->log($logLevel, sprintf('----- %s -----', str_repeat('-', strlen($title))));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit bfae42ce8985204b0db276d0b8daed7050567323
|
||||
Subproject commit eec5da2443b84821bd7e7224adea4921c81dc674
|
@ -1 +1 @@
|
||||
Subproject commit c568b5ec9bd0606f0334d28ba60b0fc6c624a8f9
|
||||
Subproject commit 87d6b81989af33bfffea030e0015c3b6d43dc8e0
|
Loading…
x
Reference in New Issue
Block a user