mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-14 09:49:50 +00:00
Merge branch 'master' into api3/network
This commit is contained in:
commit
4245274aec
@ -143,7 +143,7 @@ class CrashDump{
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = (array) error_get_last();
|
||||
$error["trace"] = @getTrace(3);
|
||||
$error["trace"] = getTrace(4); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$errorConversion = [
|
||||
E_ERROR => "E_ERROR",
|
||||
E_WARNING => "E_WARNING",
|
||||
|
@ -3722,15 +3722,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
public function setHealth($amount){
|
||||
parent::setHealth($amount);
|
||||
if($this->spawned === true){
|
||||
$pk = new SetHealthPacket();
|
||||
$pk->health = $this->getHealth();
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
if(!$this->isAlive()){
|
||||
return;
|
||||
|
@ -357,7 +357,7 @@ namespace pocketmine {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getTrace($start = 1, $trace = null){
|
||||
function getTrace($start = 0, $trace = null){
|
||||
if($trace === null){
|
||||
if(function_exists("xdebug_get_function_stack")){
|
||||
$trace = array_reverse(xdebug_get_function_stack());
|
||||
|
@ -158,6 +158,9 @@ class Server{
|
||||
private $currentTPS = 20;
|
||||
private $currentUse = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $doTitleTick = true;
|
||||
|
||||
private $sendUsageTicker = 0;
|
||||
|
||||
private $dispatchSignals = false;
|
||||
@ -1466,6 +1469,8 @@ class Server{
|
||||
$this->alwaysTickPlayers = (int) $this->getProperty("level-settings.always-tick-players", false);
|
||||
$this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1);
|
||||
|
||||
$this->doTitleTick = (bool) $this->getProperty("console.title-tick", true);
|
||||
|
||||
$this->scheduler = new ServerScheduler();
|
||||
|
||||
if($this->getConfigBoolean("enable-rcon", false) === true){
|
||||
@ -2040,9 +2045,8 @@ class Server{
|
||||
$errline = $e->getLine();
|
||||
|
||||
$type = ($errno === E_ERROR or $errno === E_USER_ERROR) ? \LogLevel::ERROR : (($errno === E_USER_WARNING or $errno === E_WARNING) ? \LogLevel::WARNING : \LogLevel::NOTICE);
|
||||
if(($pos = strpos($errstr, "\n")) !== false){
|
||||
$errstr = substr($errstr, 0, $pos);
|
||||
}
|
||||
|
||||
$errstr = preg_replace('/\s+/', ' ', trim($errstr));
|
||||
|
||||
$errfile = cleanPath($errfile);
|
||||
|
||||
@ -2054,7 +2058,7 @@ class Server{
|
||||
"fullFile" => $e->getFile(),
|
||||
"file" => $errfile,
|
||||
"line" => $errline,
|
||||
"trace" => @getTrace(1, $trace)
|
||||
"trace" => getTrace(0, $trace)
|
||||
];
|
||||
|
||||
global $lastExceptionError, $lastError;
|
||||
@ -2263,7 +2267,9 @@ class Server{
|
||||
}
|
||||
|
||||
public function sendUsage($type = SendUsageTask::TYPE_STATUS){
|
||||
$this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers));
|
||||
if($this->getProperty("anonymous-statistics.enabled", true)){
|
||||
$this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers));
|
||||
}
|
||||
$this->uniquePlayers = [];
|
||||
}
|
||||
|
||||
@ -2297,10 +2303,6 @@ class Server{
|
||||
}
|
||||
|
||||
private function titleTick(){
|
||||
if(!Terminal::hasFormattingCodes()){
|
||||
return;
|
||||
}
|
||||
|
||||
$d = Utils::getRealMemoryUsage();
|
||||
|
||||
$u = Utils::getMemoryUsage(true);
|
||||
@ -2376,7 +2378,9 @@ class Server{
|
||||
}
|
||||
|
||||
if(($this->tickCounter & 0b1111) === 0){
|
||||
$this->titleTick();
|
||||
if($this->doTitleTick and Terminal::hasFormattingCodes()){
|
||||
$this->titleTick();
|
||||
}
|
||||
$this->currentTPS = 20;
|
||||
$this->currentUse = 0;
|
||||
|
||||
@ -2441,4 +2445,13 @@ class Server{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when something attempts to serialize the server instance.
|
||||
*
|
||||
* @throws \BadMethodCallException because Server instances cannot be serialized
|
||||
*/
|
||||
public function __sleep(){
|
||||
throw new \BadMethodCallException("Cannot serialize Server instance");
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace pocketmine\block;
|
||||
|
||||
|
||||
class GlowingObsidian extends Transparent{
|
||||
class GlowingObsidian extends Solid{
|
||||
|
||||
protected $id = self::GLOWING_OBSIDIAN;
|
||||
|
||||
|
@ -32,4 +32,8 @@ class RedstoneTorch extends Torch{
|
||||
public function getName(){
|
||||
return "Redstone Torch";
|
||||
}
|
||||
|
||||
public function getLightLevel(){
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class Torch extends Flowable{
|
||||
}
|
||||
|
||||
public function getLightLevel(){
|
||||
return 15;
|
||||
return 14;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
|
@ -136,7 +136,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
if($label === null){
|
||||
$label = $command->getName();
|
||||
}
|
||||
$label = strtolower(trim($label));
|
||||
$label = trim($label);
|
||||
$fallbackPrefix = strtolower(trim($fallbackPrefix));
|
||||
|
||||
$registered = $this->registerAlias($command, false, $fallbackPrefix, $label);
|
||||
@ -188,7 +188,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
* @return Command|null
|
||||
*/
|
||||
public function matchCommand(string &$commandName, array &$args){
|
||||
$count = max(count($args), 255);
|
||||
$count = min(count($args), 255);
|
||||
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$commandName .= array_shift($args);
|
||||
|
@ -470,7 +470,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
if(isset($this->effects[$effect->getId()])){
|
||||
$oldEffect = $this->effects[$effect->getId()];
|
||||
if(
|
||||
abs($effect->getAmplifier()) <= ($oldEffect->getAmplifier())
|
||||
abs($effect->getAmplifier()) < ($oldEffect->getAmplifier())
|
||||
or (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier())
|
||||
and $effect->getDuration() < $oldEffect->getDuration())
|
||||
){
|
||||
|
@ -408,6 +408,12 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->foodLevel = new IntTag("foodLevel", $this->getFood());
|
||||
$this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion());
|
||||
$this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation());
|
||||
$this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer);
|
||||
|
||||
$this->namedtag->Inventory = new ListTag("Inventory", []);
|
||||
$this->namedtag->Inventory->setTagType(NBT::TAG_Compound);
|
||||
if($this->inventory !== null){
|
||||
@ -437,7 +443,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
|
||||
for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){
|
||||
$item = $this->inventory->getItem($slot - 9);
|
||||
$this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot);
|
||||
if($item->getId() !== ItemItem::AIR){
|
||||
$this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot);
|
||||
}
|
||||
}
|
||||
|
||||
//Armor
|
||||
|
@ -25,6 +25,13 @@ use pocketmine\level\Level;
|
||||
|
||||
class LevelTimings{
|
||||
|
||||
/** @var TimingsHandler */
|
||||
public $setBlock;
|
||||
/** @var TimingsHandler */
|
||||
public $doBlockLightUpdates;
|
||||
/** @var TimingsHandler */
|
||||
public $doBlockSkyLightUpdates;
|
||||
|
||||
/** @var TimingsHandler */
|
||||
public $mobSpawn;
|
||||
/** @var TimingsHandler */
|
||||
@ -79,6 +86,10 @@ class LevelTimings{
|
||||
public function __construct(Level $level){
|
||||
$name = $level->getFolderName() . " - ";
|
||||
|
||||
$this->setBlock = new TimingsHandler("** " . $name . "setBlock");
|
||||
$this->doBlockLightUpdates = new TimingsHandler("** " . $name . "doBlockLightUpdates");
|
||||
$this->doBlockSkyLightUpdates = new TimingsHandler("** " . $name . "doBlockSkyLightUpdates");
|
||||
|
||||
$this->mobSpawn = new TimingsHandler("** " . $name . "mobSpawn");
|
||||
$this->doChunkUnload = new TimingsHandler("** " . $name . "doChunkUnload");
|
||||
$this->doTickPending = new TimingsHandler("** " . $name . "doTickPending");
|
||||
|
@ -65,13 +65,19 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
|
||||
/** @var \SplFixedArray */
|
||||
public static $list = null;
|
||||
/** @var Block|null */
|
||||
protected $block;
|
||||
/** @var int */
|
||||
protected $id;
|
||||
/** @var int */
|
||||
protected $meta;
|
||||
/** @var string */
|
||||
private $tags = "";
|
||||
/** @var CompoundTag|null */
|
||||
private $cachedNBT = null;
|
||||
/** @var int */
|
||||
public $count;
|
||||
protected $durability = 0;
|
||||
/** @var string */
|
||||
protected $name;
|
||||
|
||||
public function canBeActivated(){
|
||||
@ -1007,4 +1013,12 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function __clone(){
|
||||
if($this->block !== null){
|
||||
$this->block = clone $this->block;
|
||||
}
|
||||
|
||||
$this->cachedNBT = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,10 +37,6 @@ class ItemBlock extends Item{
|
||||
$this->block->setDamage($this->meta !== -1 ? $this->meta : 0);
|
||||
}
|
||||
|
||||
public function __clone(){
|
||||
$this->block = clone $this->block;
|
||||
}
|
||||
|
||||
public function getBlock() : Block{
|
||||
return $this->block;
|
||||
}
|
||||
|
@ -1286,17 +1286,42 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
public function updateBlockSkyLight(int $x, int $y, int $z){
|
||||
$this->timings->doBlockSkyLightUpdates->startTiming();
|
||||
//TODO
|
||||
$this->timings->doBlockSkyLightUpdates->stopTiming();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest light level available in the positions adjacent to the specified block coordinates.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHighestAdjacentBlockLight(int $x, int $y, int $z) : int{
|
||||
return max([
|
||||
$this->getBlockLightAt($x + 1, $y, $z),
|
||||
$this->getBlockLightAt($x - 1, $y, $z),
|
||||
$this->getBlockLightAt($x, $y + 1, $z),
|
||||
$this->getBlockLightAt($x, $y - 1, $z),
|
||||
$this->getBlockLightAt($x, $y, $z + 1),
|
||||
$this->getBlockLightAt($x, $y, $z - 1)
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateBlockLight(int $x, int $y, int $z){
|
||||
$this->timings->doBlockLightUpdates->startTiming();
|
||||
|
||||
$lightPropagationQueue = new \SplQueue();
|
||||
$lightRemovalQueue = new \SplQueue();
|
||||
$visited = [];
|
||||
$removalVisited = [];
|
||||
|
||||
$id = $this->getBlockIdAt($x, $y, $z);
|
||||
$oldLevel = $this->getBlockLightAt($x, $y, $z);
|
||||
$newLevel = (int) Block::$light[$this->getBlockIdAt($x, $y, $z)];
|
||||
$newLevel = max(Block::$light[$id], $this->getHighestAdjacentBlockLight($x, $y, $z) - Block::$lightFilter[$id]);
|
||||
|
||||
if($oldLevel !== $newLevel){
|
||||
$this->setBlockLightAt($x, $y, $z, $newLevel);
|
||||
@ -1328,7 +1353,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var Vector3 $node */
|
||||
$node = $lightPropagationQueue->dequeue();
|
||||
|
||||
$lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z) - (int) Block::$lightFilter[$this->getBlockIdAt($node->x, $node->y, $node->z)];
|
||||
$lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z);
|
||||
|
||||
if($lightLevel >= 1){
|
||||
$this->computeSpreadBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited);
|
||||
@ -1339,6 +1364,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->computeSpreadBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightPropagationQueue, $visited);
|
||||
}
|
||||
}
|
||||
|
||||
$this->timings->doBlockLightUpdates->stopTiming();
|
||||
}
|
||||
|
||||
private function computeRemoveBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, \SplQueue $spreadQueue, array &$visited, array &$spreadVisited){
|
||||
@ -1365,6 +1392,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
private function computeSpreadBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, array &$visited){
|
||||
if($y < 0) return;
|
||||
$current = $this->getBlockLightAt($x, $y, $z);
|
||||
$currentLight -= Block::$lightFilter[$this->getBlockIdAt($x, $y, $z)];
|
||||
|
||||
if($current < $currentLight){
|
||||
$this->setBlockLightAt($x, $y, $z, $currentLight);
|
||||
@ -1402,6 +1430,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->timings->setBlock->startTiming();
|
||||
|
||||
if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f, $block->getId(), $block->getDamage())){
|
||||
if(!($pos instanceof Position)){
|
||||
$pos = $this->temporalPosition->setComponents($pos->x, $pos->y, $pos->z);
|
||||
@ -1441,9 +1471,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->updateAround($pos);
|
||||
}
|
||||
|
||||
$this->timings->setBlock->stopTiming();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->timings->setBlock->stopTiming();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1508,7 +1542,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if(($player->isSurvival() and $item instanceof Item and !$target->isBreakable($item)) or $player->isSpectator()){
|
||||
$ev->setCancelled();
|
||||
}elseif(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
}elseif(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
$t = new Vector2($target->x, $target->z);
|
||||
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
|
||||
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this
|
||||
@ -1645,7 +1679,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if($player !== null){
|
||||
$ev = new PlayerInteractEvent($player, $item, $target, $face, $target->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK);
|
||||
if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
$t = new Vector2($target->x, $target->z);
|
||||
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
|
||||
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this
|
||||
@ -1735,7 +1769,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if($player !== null){
|
||||
$ev = new BlockPlaceEvent($player, $hand, $block, $target, $item);
|
||||
if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
$t = new Vector2($target->x, $target->z);
|
||||
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
|
||||
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this
|
||||
|
@ -250,7 +250,7 @@ class Vector3{
|
||||
if($f < 0 or $f > 1){
|
||||
return null;
|
||||
}else{
|
||||
return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f);
|
||||
return new Vector3($x, $this->y + $yDiff * $f, $this->z + $zDiff * $f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +277,7 @@ class Vector3{
|
||||
if($f < 0 or $f > 1){
|
||||
return null;
|
||||
}else{
|
||||
return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f);
|
||||
return new Vector3($this->x + $xDiff * $f, $y, $this->z + $zDiff * $f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,7 +304,7 @@ class Vector3{
|
||||
if($f < 0 or $f > 1){
|
||||
return null;
|
||||
}else{
|
||||
return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f);
|
||||
return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $z);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,13 @@ abstract class DefaultPermissions{
|
||||
$parent = self::registerPermission(new Permission(self::ROOT, "Allows using all PocketMine commands and utilities"));
|
||||
|
||||
$broadcasts = self::registerPermission(new Permission(self::ROOT . ".broadcast", "Allows the user to receive all broadcast messages"), $parent);
|
||||
|
||||
self::registerPermission(new Permission(self::ROOT . ".broadcast.admin", "Allows the user to receive administrative broadcasts", Permission::DEFAULT_OP), $broadcasts);
|
||||
self::registerPermission(new Permission(self::ROOT . ".broadcast.user", "Allows the user to receive user broadcasts", Permission::DEFAULT_TRUE), $broadcasts);
|
||||
|
||||
$broadcasts->recalculatePermissibles();
|
||||
|
||||
$spawnprotect = self::registerPermission(new Permission(self::ROOT . ".spawnprotect.bypass", "Allows the user to edit blocks within the protected spawn radius", Permission::DEFAULT_OP), $parent);
|
||||
$spawnprotect->recalculatePermissibles();
|
||||
|
||||
$commands = self::registerPermission(new Permission(self::ROOT . ".command", "Allows using all PocketMine commands"), $parent);
|
||||
|
||||
$whitelist = self::registerPermission(new Permission(self::ROOT . ".command.whitelist", "Allows the user to modify the server whitelist", Permission::DEFAULT_OP), $commands);
|
||||
|
@ -185,6 +185,11 @@ timings:
|
||||
#Choose the host to use for viewing your timings results.
|
||||
host: mcpetimings.com
|
||||
|
||||
console:
|
||||
#Choose whether to enable server stats reporting on the console title.
|
||||
#NOTE: The title ticker will be disabled regardless if console colours are not enabled.
|
||||
title-tick: true
|
||||
|
||||
aliases:
|
||||
#Examples:
|
||||
#showtheversion: version
|
||||
|
@ -63,6 +63,10 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
foreach($this->getInventory()->getViewers() as $player){
|
||||
$player->removeWindow($this->getRealInventory());
|
||||
}
|
||||
|
||||
$this->inventory = null;
|
||||
$this->doubleInventory = null;
|
||||
|
||||
parent::close();
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
foreach($this->getInventory()->getViewers() as $player){
|
||||
$player->removeWindow($this->getInventory());
|
||||
}
|
||||
|
||||
$this->inventory = null;
|
||||
|
||||
parent::close();
|
||||
}
|
||||
}
|
||||
|
@ -134,12 +134,10 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
$type = ($errno === E_ERROR or $errno === E_USER_ERROR) ? LogLevel::ERROR : (($errno === E_USER_WARNING or $errno === E_WARNING) ? LogLevel::WARNING : LogLevel::NOTICE);
|
||||
}
|
||||
$errno = $errorConversion[$errno] ?? $errno;
|
||||
if(($pos = strpos($errstr, "\n")) !== false){
|
||||
$errstr = substr($errstr, 0, $pos);
|
||||
}
|
||||
$errstr = preg_replace('/\s+/', ' ', trim($errstr));
|
||||
$errfile = \pocketmine\cleanPath($errfile);
|
||||
$this->log($type, get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline");
|
||||
foreach(@\pocketmine\getTrace(1, $trace) as $i => $line){
|
||||
foreach(\pocketmine\getTrace(0, $trace) as $i => $line){
|
||||
$this->debug($line);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user