mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 16:24:05 +00:00
Separate CommandReader impl from CommandReaderThread
This commit is contained in:
parent
4bcc7e09cb
commit
0402e7e697
@ -28,7 +28,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandReader;
|
||||
use pocketmine\command\CommandReaderThread;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\ConsoleCommandSender;
|
||||
use pocketmine\command\SimpleCommandMap;
|
||||
@ -227,7 +227,7 @@ class Server{
|
||||
/** @var MemoryManager */
|
||||
private $memoryManager;
|
||||
|
||||
/** @var CommandReader */
|
||||
/** @var CommandReaderThread */
|
||||
private $console;
|
||||
|
||||
/** @var SimpleCommandMap */
|
||||
@ -1133,11 +1133,12 @@ class Server{
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender);
|
||||
|
||||
$consoleNotifier = new SleeperNotifier();
|
||||
$this->console = new CommandReader($consoleNotifier);
|
||||
$this->tickSleeper->addNotifier($consoleNotifier, function() use ($consoleSender) : void{
|
||||
$commandBuffer = new \Threaded();
|
||||
$this->console = new CommandReaderThread($commandBuffer, $consoleNotifier);
|
||||
$this->tickSleeper->addNotifier($consoleNotifier, function() use ($commandBuffer, $consoleSender) : void{
|
||||
Timings::$serverCommand->startTiming();
|
||||
while(($line = $this->console->getLine()) !== null){
|
||||
$this->dispatchCommand($consoleSender, $line);
|
||||
while(($line = $commandBuffer->shift()) !== null){
|
||||
$this->dispatchCommand($consoleSender, (string) $line);
|
||||
}
|
||||
Timings::$serverCommand->stopTiming();
|
||||
});
|
||||
@ -1422,7 +1423,7 @@ class Server{
|
||||
$this->configGroup->save();
|
||||
}
|
||||
|
||||
if($this->console instanceof CommandReader){
|
||||
if($this->console instanceof CommandReaderThread){
|
||||
$this->getLogger()->debug("Closing console");
|
||||
$this->console->shutdown();
|
||||
$this->console->notify();
|
||||
|
@ -23,149 +23,58 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\thread\Thread;
|
||||
use pocketmine\thread\ThreadException;
|
||||
use function fclose;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function fstat;
|
||||
use function is_resource;
|
||||
use function microtime;
|
||||
use function preg_replace;
|
||||
use function readline;
|
||||
use function stream_isatty;
|
||||
use function stream_select;
|
||||
use function trim;
|
||||
use function usleep;
|
||||
|
||||
class CommandReader extends Thread{
|
||||
|
||||
public const TYPE_READLINE = 0;
|
||||
public const TYPE_STREAM = 1;
|
||||
public const TYPE_PIPED = 2;
|
||||
|
||||
final class CommandReader{
|
||||
/** @var resource */
|
||||
private static $stdin;
|
||||
private $stdin;
|
||||
|
||||
/** @var \Threaded */
|
||||
protected $buffer;
|
||||
/** @var bool */
|
||||
private $shutdown = false;
|
||||
/** @var int */
|
||||
private $type = self::TYPE_STREAM;
|
||||
|
||||
/** @var SleeperNotifier|null */
|
||||
private $notifier;
|
||||
|
||||
public function __construct(?SleeperNotifier $notifier = null){
|
||||
$this->buffer = new \Threaded;
|
||||
$this->notifier = $notifier;
|
||||
}
|
||||
|
||||
public function shutdown() : void{
|
||||
$this->shutdown = true;
|
||||
}
|
||||
|
||||
public function quit() : void{
|
||||
$wait = microtime(true) + 0.5;
|
||||
while(microtime(true) < $wait){
|
||||
if($this->isRunning()){
|
||||
usleep(100000);
|
||||
}else{
|
||||
parent::quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$message = "Thread blocked for unknown reason";
|
||||
if($this->type === self::TYPE_PIPED){
|
||||
$message = "STDIN is being piped from another location and the pipe is blocked, cannot stop safely";
|
||||
}
|
||||
|
||||
throw new ThreadException($message);
|
||||
public function __construct(){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
private function initStdin() : void{
|
||||
if(is_resource(self::$stdin)){
|
||||
fclose(self::$stdin);
|
||||
if(is_resource($this->stdin)){
|
||||
fclose($this->stdin);
|
||||
}
|
||||
|
||||
self::$stdin = fopen("php://stdin", "r");
|
||||
if($this->isPipe(self::$stdin)){
|
||||
$this->type = self::TYPE_PIPED;
|
||||
}else{
|
||||
$this->type = self::TYPE_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified stream is a FIFO pipe.
|
||||
*
|
||||
* @param resource $stream
|
||||
*/
|
||||
private function isPipe($stream) : bool{
|
||||
return is_resource($stream) and (!stream_isatty($stream) or ((fstat($stream)["mode"] & 0170000) === 0010000));
|
||||
$this->stdin = fopen("php://stdin", "r");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line from the console and adds it to the buffer. This method may block the thread.
|
||||
*
|
||||
* @return bool if the main execution should continue reading lines
|
||||
*/
|
||||
private function readLine() : bool{
|
||||
if(!is_resource(self::$stdin)){
|
||||
public function readLine() : ?string{
|
||||
if(!is_resource($this->stdin)){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
$r = [self::$stdin];
|
||||
$r = [$this->stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
return null;
|
||||
}elseif($count === false){ //stream error
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
|
||||
if(($raw = fgets($this->stdin)) === false){ //broken pipe or EOF
|
||||
$this->initStdin();
|
||||
$this->synchronized(function() : void{
|
||||
$this->wait(200000);
|
||||
}); //prevent CPU waste if it's end of pipe
|
||||
return true; //loop back round
|
||||
usleep(200000); //prevent CPU waste if it's end of pipe
|
||||
return null; //loop back round
|
||||
}
|
||||
|
||||
$line = trim($raw);
|
||||
|
||||
if($line !== ""){
|
||||
$this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
||||
if($this->notifier !== null){
|
||||
$this->notifier->wakeupSleeper();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return $line !== "" ? $line : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line from console, if available. Returns null if not available
|
||||
*/
|
||||
public function getLine() : ?string{
|
||||
if($this->buffer->count() !== 0){
|
||||
return (string) $this->buffer->shift();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function onRun() : void{
|
||||
$this->initStdin();
|
||||
|
||||
while(!$this->shutdown and $this->readLine());
|
||||
|
||||
fclose(self::$stdin);
|
||||
}
|
||||
|
||||
public function getThreadName() : string{
|
||||
return "Console";
|
||||
public function __destruct(){
|
||||
fclose($this->stdin);
|
||||
}
|
||||
}
|
||||
|
82
src/command/CommandReaderThread.php
Normal file
82
src/command/CommandReaderThread.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?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\command;
|
||||
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\thread\Thread;
|
||||
use pocketmine\thread\ThreadException;
|
||||
use function microtime;
|
||||
use function preg_replace;
|
||||
use function usleep;
|
||||
|
||||
final class CommandReaderThread extends Thread{
|
||||
private \Threaded $buffer;
|
||||
private ?SleeperNotifier $notifier;
|
||||
|
||||
public bool $shutdown = false;
|
||||
|
||||
public function __construct(\Threaded $buffer, ?SleeperNotifier $notifier = null){
|
||||
$this->buffer = $buffer;
|
||||
$this->notifier = $notifier;
|
||||
}
|
||||
|
||||
public function shutdown() : void{
|
||||
$this->shutdown = true;
|
||||
}
|
||||
|
||||
public function quit() : void{
|
||||
$wait = microtime(true) + 0.5;
|
||||
while(microtime(true) < $wait){
|
||||
if($this->isRunning()){
|
||||
usleep(100000);
|
||||
}else{
|
||||
parent::quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ThreadException("CommandReader is stuck in a blocking STDIN read");
|
||||
}
|
||||
|
||||
protected function onRun() : void{
|
||||
$buffer = $this->buffer;
|
||||
$notifier = $this->notifier;
|
||||
|
||||
$reader = new CommandReader();
|
||||
while(!$this->shutdown){
|
||||
$line = $reader->readLine();
|
||||
|
||||
if($line !== null){
|
||||
$buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
||||
if($notifier !== null){
|
||||
$notifier->wakeupSleeper();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getThreadName() : string{
|
||||
return "Console";
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ parameters:
|
||||
path: ../../../src/CrashDump.php
|
||||
|
||||
-
|
||||
message: "#^Instanceof between pocketmine\\\\command\\\\CommandReader and pocketmine\\\\command\\\\CommandReader will always evaluate to true\\.$#"
|
||||
message: "#^Instanceof between pocketmine\\\\command\\\\CommandReaderThread and pocketmine\\\\command\\\\CommandReaderThread will always evaluate to true\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
|
@ -37,7 +37,7 @@ parameters:
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 1
|
||||
count: 2
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
@ -80,11 +80,6 @@ parameters:
|
||||
count: 2
|
||||
path: ../../../src/VersionInfo.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/CommandReader.php
|
||||
|
||||
-
|
||||
message: "#^Part \\$host \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
|
||||
count: 1
|
||||
|
@ -436,17 +436,7 @@ parameters:
|
||||
path: ../../../src/block/tile/TileFactory.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'mode' on array\\(0 \\=\\> int, 1 \\=\\> int, 2 \\=\\> int, 3 \\=\\> int, 4 \\=\\> int, 5 \\=\\> int, 6 \\=\\> int, 7 \\=\\> int, \\.\\.\\.\\)\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/CommandReader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of method pocketmine\\\\command\\\\CommandReader\\:\\:isPipe\\(\\) expects resource, resource\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/CommandReader.php
|
||||
|
||||
-
|
||||
message: "#^Static property pocketmine\\\\command\\\\CommandReader\\:\\:\\$stdin \\(resource\\) does not accept resource\\|false\\.$#"
|
||||
message: "#^Property pocketmine\\\\command\\\\CommandReader\\:\\:\\$stdin \\(resource\\) does not accept resource\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/CommandReader.php
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user