From 91400b3392289dd6bb32e60758acce58b30b6dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Sun, 21 Jan 2018 10:29:58 +0100 Subject: [PATCH 1/9] PHP 7.2 Update --- src/pocketmine/PocketMine.php | 6 ++-- src/pocketmine/Thread.php | 9 +++--- src/pocketmine/Worker.php | 9 +++--- src/pocketmine/utils/Terminal.php | 30 +++++++++++-------- start.cmd | 2 +- start.ps1 | 48 +++++++++++++++++++++++++++++++ 6 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 start.ps1 diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 0955fe63..6142e35c 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -88,6 +88,8 @@ function dummy(){ const CODENAME = "Wolf"; const TURANIC_API_VERSION = '2.0.0'; + const MIN_PHP_VERSION = "7.2.1"; + /* * Startup code. Do not look at it, it may harm you. * Most of them are hacks to fix date-related bugs, or basic functions used after this @@ -101,8 +103,8 @@ function dummy(){ @define('pocketmine\PATH', \getcwd() . DIRECTORY_SEPARATOR); } - if(version_compare("7.0.15", PHP_VERSION) > 0){ - echo "[CRITICAL] You must use PHP >= 7.0.15" . PHP_EOL; + if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){ + echo "[CRITICAL] You must use PHP >= " . MIN_PHP_VERSION . PHP_EOL; echo "[CRITICAL] Please use the installer provided on the homepage." . PHP_EOL; exit(1); } diff --git a/src/pocketmine/Thread.php b/src/pocketmine/Thread.php index 2602e6b9..00669422 100644 --- a/src/pocketmine/Thread.php +++ b/src/pocketmine/Thread.php @@ -63,7 +63,7 @@ public function registerClassLoader(){ * * @return bool */ - public function start(int $options = \PTHREADS_INHERIT_ALL){ + public function start(?int $options = \PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){ @@ -93,9 +93,10 @@ public function quit(){ ThreadManager::getInstance()->remove($this); } - /** - * @return string - */ + /** + * @return string + * @throws \ReflectionException + */ public function getThreadName(){ return (new \ReflectionClass($this))->getShortName(); } diff --git a/src/pocketmine/Worker.php b/src/pocketmine/Worker.php index 5e3d07b5..dc088d7d 100644 --- a/src/pocketmine/Worker.php +++ b/src/pocketmine/Worker.php @@ -64,7 +64,7 @@ public function registerClassLoader(){ * * @return bool */ - public function start(int $options = \PTHREADS_INHERIT_ALL){ + public function start(?int $options = \PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){ @@ -98,9 +98,10 @@ public function quit(){ ThreadManager::getInstance()->remove($this); } - /** - * @return string - */ + /** + * @return string + * @throws \ReflectionException + */ public function getThreadName(){ return (new \ReflectionClass($this))->getShortName(); } diff --git a/src/pocketmine/utils/Terminal.php b/src/pocketmine/utils/Terminal.php index 65ee241e..3a8f1ffa 100644 --- a/src/pocketmine/utils/Terminal.php +++ b/src/pocketmine/utils/Terminal.php @@ -2,11 +2,11 @@ /* * - * ____ _ _ __ __ _ __ __ ____ - * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ + * ____ _ _ __ __ _ __ __ ____ + * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | - * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ - * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| + * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ + * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| * * 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 @@ -15,7 +15,7 @@ * * @author PocketMine Team * @link http://www.pocketmine.net/ - * + * * */ @@ -54,13 +54,19 @@ abstract class Terminal { */ public static function hasFormattingCodes(){ if(self::$formattingCodes === null){ - $opts = getopt("", ["enable-ansi", "disable-ansi"]); - if(isset($opts["disable-ansi"])){ - self::$formattingCodes = false; - }else{ - self::$formattingCodes = ((Utils::getOS() !== "win" and getenv("TERM") != "" and (!function_exists("posix_ttyname") or !defined("STDOUT") or posix_ttyname(STDOUT) !== false)) or isset($opts["enable-ansi"])); - } - } + $opts = getopt("", ["enable-ansi", "disable-ansi"]); + if(isset($opts["disable-ansi"])){ + self::$formattingCodes = false; + }else{ + self::$formattingCodes = (isset($opts["enable-ansi"]) or ( //user explicitly told us to enable ANSI + stream_isatty(STDOUT) and //STDOUT isn't being piped + ( + getenv('TERM') !== false or //Console says it supports colours + (function_exists('sapi_windows_vt100_support') and sapi_windows_vt100_support(STDOUT)) //we're on windows and have vt100 support + ) + )); + } + } return self::$formattingCodes; } diff --git a/start.cmd b/start.cmd index 38e5c0b2..cb7e5f15 100644 --- a/start.cmd +++ b/start.cmd @@ -32,5 +32,5 @@ if exist Turanic*.phar ( if exist bin\mintty.exe ( start "" bin\mintty.exe -o Columns=88 -o Rows=32 -o AllowBlinking=0 -o FontQuality=3 -o Font="Consolas" -o FontHeight=10 -o CursorType=0 -o CursorBlinks=1 -h error -t "Turanic" -w max %PHP_BINARY% %POCKETMINE_FILE% --enable-ansi %* ) else ( - %PHP_BINARY% -c bin\php %POCKETMINE_FILE% %* + %PHP_BINARY% -c bin\php %POCKETMINE_FILE% %* ) \ No newline at end of file diff --git a/start.ps1 b/start.ps1 new file mode 100644 index 00000000..86e5c42c --- /dev/null +++ b/start.ps1 @@ -0,0 +1,48 @@ +[CmdletBinding(PositionalBinding=$false)] +param ( + [string]$php = "", + [switch]$Loop = $false, + [string]$file = "", + [string][Parameter(ValueFromRemainingArguments)]$extraPocketMineArgs +) + +if($php -ne ""){ + $binary = $php +}elseif(Test-Path "bin\php\php.exe"){ + $env:PHPRC = "" + $binary = "bin\php\php.exe" +}else{ + $binary = "php" +} + +if($file -eq ""){ + if(Test-Path "PocketMine-MP.phar"){ + $file = "PocketMine-MP.phar" + }elseif(Test-Path "src\pocketmine\PocketMine.php"){ + $file = "src\pocketmine\PocketMine.php" + }else{ + echo "Couldn't find a valid PocketMine-MP installation" + pause + exit 1 + } +} + +function StartServer{ + $command = "powershell " + $binary + " " + $file + " --enable-ansi " + $extraPocketMineArgs + iex $command +} + +$loops = 0 + +StartServer + +while($Loop){ + if($loops -ne 0){ + echo ("Restarted " + $loops + " times") + } + $loops++ + echo "To escape the loop, press CTRL+C now. Otherwise, wait 5 seconds for the server to restart." + echo "" + Start-Sleep 5 + StartServer +} \ No newline at end of file From 5b852852e23eff4050d20f42e28280c60eca1202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Sun, 21 Jan 2018 15:32:28 +0100 Subject: [PATCH 2/9] TravisCI : Updated PHP to 7.2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5eece2d4..4cac71a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: true language: php php: - - 7.0 + - 7.2.1 before_script: - pecl install channel://pecl.php.net/pthreads-3.1.6 From 3417e71e1c4a9088673735c86993636026987c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Sat, 27 Jan 2018 17:56:06 +0100 Subject: [PATCH 3/9] Add TypeHints --- src/pocketmine/Worker.php | 2 +- src/pocketmine/entity/Human.php | 26 ++++++++--------- .../network/mcpe/NetworkBinaryStream.php | 28 +++++++++---------- src/raklib/server/RakLibServer.php | 12 ++------ src/raklib/server/SessionManager.php | 16 ++--------- src/raklib/server/UDPServerSocket.php | 16 +---------- 6 files changed, 34 insertions(+), 66 deletions(-) diff --git a/src/pocketmine/Worker.php b/src/pocketmine/Worker.php index dc088d7d..b21bc891 100644 --- a/src/pocketmine/Worker.php +++ b/src/pocketmine/Worker.php @@ -102,7 +102,7 @@ public function quit(){ * @return string * @throws \ReflectionException */ - public function getThreadName(){ + public function getThreadName() : string{ return (new \ReflectionClass($this))->getShortName(); } } \ No newline at end of file diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 6648db03..1875b857 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -304,7 +304,7 @@ public function getXpLevel() : int{ * * @param int $level */ - public function setXpLevel(int $level){ + public function setXpLevel(int $level) : void{ $this->attributeMap->getAttribute(Attribute::EXPERIENCE_LEVEL)->setValue($level); } @@ -314,7 +314,7 @@ public function setXpLevel(int $level){ * @param int $amount * @param bool $playSound */ - public function addXpLevels(int $amount, bool $playSound = true){ + public function addXpLevels(int $amount, bool $playSound = true) : void{ $oldLevel = $this->getXpLevel(); $this->setXpLevel($oldLevel + $amount); @@ -330,7 +330,7 @@ public function addXpLevels(int $amount, bool $playSound = true){ * Subtracts a number of XP levels from the player. * @param int $amount */ - public function subtractXpLevels(int $amount){ + public function subtractXpLevels(int $amount) : void{ $this->setXpLevel($this->getXpLevel() - $amount); } @@ -347,7 +347,7 @@ public function getXpProgress() : float{ * * @param float $progress */ - public function setXpProgress(float $progress){ + public function setXpProgress(float $progress) : void{ $this->attributeMap->getAttribute(Attribute::EXPERIENCE)->setValue($progress); } @@ -376,7 +376,7 @@ public function getCurrentTotalXp() : int{ * * @param int $amount */ - public function setCurrentTotalXp(int $amount){ + public function setCurrentTotalXp(int $amount) : void{ $newLevel = ExperienceUtils::getLevelFromXp($amount); $this->setXpLevel((int) $newLevel); @@ -390,7 +390,7 @@ public function setCurrentTotalXp(int $amount){ * @param int $amount * @param bool $playSound Whether to play level-up and XP gained sounds. */ - public function addXp(int $amount, bool $playSound = true){ + public function addXp(int $amount, bool $playSound = true) : void{ $this->totalXp += $amount; $oldLevel = $this->getXpLevel(); @@ -409,7 +409,7 @@ public function addXp(int $amount, bool $playSound = true){ } } - private function playLevelUpSound(int $newLevel){ + private function playLevelUpSound(int $newLevel) : void{ $volume = 0x10000000 * (min(30, $newLevel) / 5); //No idea why such odd numbers, but this works... $this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_LEVELUP, 1, (int) $volume); } @@ -418,7 +418,7 @@ private function playLevelUpSound(int $newLevel){ * Takes an amount of XP from the player, recalculating their XP level and progress. * @param int $amount */ - public function subtractXp(int $amount){ + public function subtractXp(int $amount) : void{ $this->addXp(-$amount); } @@ -428,7 +428,7 @@ public function subtractXp(int $amount){ * * @return int */ - public function getLifetimeTotalXp(){ + public function getLifetimeTotalXp() : int{ return $this->totalXp; } @@ -438,7 +438,7 @@ public function getLifetimeTotalXp(){ * * @param int $amount */ - public function setLifetimeTotalXp(int $amount){ + public function setLifetimeTotalXp(int $amount) : void{ if($amount < 0){ throw new \InvalidArgumentException("XP must be greater than 0"); } @@ -459,7 +459,7 @@ public function canPickupXp() : bool{ * * @param int $value */ - public function resetXpCooldown(int $value = 2){ + public function resetXpCooldown(int $value = 2) : void{ $this->xpCooldown = $value; } @@ -470,14 +470,14 @@ public function getXpDropAmount() : int{ /** * @return PlayerInventory */ - public function getInventory(){ + public function getInventory() : PlayerInventory{ return $this->inventory; } /** * @return EnderChestInventory */ - public function getEnderChestInventory(){ + public function getEnderChestInventory() : EnderChestInventory{ return $this->enderChestInventory; } diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 16879881..cbae7937 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -39,7 +39,7 @@ public function getString() : string{ return $this->get($this->getUnsignedVarInt()); } - public function putString(string $v){ + public function putString(string $v) : void{ $this->putUnsignedVarInt(strlen($v)); $this->put($v); } @@ -54,7 +54,7 @@ public function getUUID() : UUID{ return new UUID($part0, $part1, $part2, $part3); } - public function putUUID(UUID $uuid){ + public function putUUID(UUID $uuid) : void{ $this->putLInt($uuid->getPart(1)); $this->putLInt($uuid->getPart(0)); $this->putLInt($uuid->getPart(3)); @@ -101,7 +101,7 @@ public function getSlot() : Item{ } - public function putSlot(Item $item){ + public function putSlot(Item $item) : void{ if($item->getId() === 0){ $this->putVarInt(0); @@ -181,7 +181,7 @@ public function getEntityMetadata(bool $types = true) : array{ * * @param array $metadata */ - public function putEntityMetadata(array $metadata){ + public function putEntityMetadata(array $metadata) : void{ $this->putUnsignedVarInt(count($metadata)); foreach($metadata as $key => $d){ $this->putUnsignedVarInt($key); //data key @@ -260,7 +260,7 @@ public function getAttributeList() : array{ * * @param Attribute[] ...$attributes */ - public function putAttributeList(Attribute ...$attributes){ + public function putAttributeList(Attribute ...$attributes) : void{ $this->putUnsignedVarInt(count($attributes)); foreach($attributes as $attribute){ $this->putLFloat($attribute->getMinValue()); @@ -284,7 +284,7 @@ public function getEntityUniqueId() : int{ * * @param int $eid */ - public function putEntityUniqueId(int $eid){ + public function putEntityUniqueId(int $eid) : void{ $this->putVarLong($eid); } @@ -301,7 +301,7 @@ public function getEntityRuntimeId() : int{ * * @param int $eid */ - public function putEntityRuntimeId(int $eid){ + public function putEntityRuntimeId(int $eid) : void{ $this->putUnsignedVarLong($eid); } @@ -325,7 +325,7 @@ public function getBlockPosition(&$x, &$y, &$z){ * @param int $y * @param int $z */ - public function putBlockPosition(int $x, int $y, int $z){ + public function putBlockPosition(int $x, int $y, int $z) : void{ $this->putVarInt($x); $this->putUnsignedVarInt($y); $this->putVarInt($z); @@ -351,7 +351,7 @@ public function getSignedBlockPosition(&$x, &$y, &$z){ * @param int $y * @param int $z */ - public function putSignedBlockPosition(int $x, int $y, int $z){ + public function putSignedBlockPosition(int $x, int $y, int $z) : void{ $this->putVarInt($x); $this->putVarInt($y); $this->putVarInt($z); @@ -378,7 +378,7 @@ public function getVector3() : Vector3{ * * @param Vector3|null $vector */ - public function putVector3Nullable($vector){ + public function putVector3Nullable(?Vector3 $vector) : void{ if($vector){ $this->putVector3($vector); }else{ @@ -394,7 +394,7 @@ public function putVector3Nullable($vector){ * * @param Vector3 $vector */ - public function putVector3(Vector3 $vector){ + public function putVector3(Vector3 $vector) : void{ $this->putLFloat($vector->x); $this->putLFloat($vector->y); $this->putLFloat($vector->z); @@ -404,7 +404,7 @@ public function getByteRotation() : float{ return (float) ($this->getByte() * (360 / 256)); } - public function putByteRotation(float $rotation){ + public function putByteRotation(float $rotation) : void{ $this->putByte((int) ($rotation / (360 / 256))); } @@ -445,7 +445,7 @@ public function getGameRules() : array{ * * @param array $rules */ - public function putGameRules(array $rules){ + public function putGameRules(array $rules) : void{ $this->putUnsignedVarInt(count($rules)); foreach($rules as $name => $rule){ $this->putString($name); @@ -481,7 +481,7 @@ protected function getEntityLink() : EntityLink{ /** * @param EntityLink $link */ - protected function putEntityLink(EntityLink $link){ + protected function putEntityLink(EntityLink $link) : void{ $this->putEntityUniqueId($link->fromEntityUniqueId); $this->putEntityUniqueId($link->toEntityUniqueId); $this->putByte($link->type); diff --git a/src/raklib/server/RakLibServer.php b/src/raklib/server/RakLibServer.php index 7d93080a..3355bd90 100644 --- a/src/raklib/server/RakLibServer.php +++ b/src/raklib/server/RakLibServer.php @@ -129,10 +129,7 @@ public function pushMainToThreadPacket(string $str){ $this->internalQueue[] = $str; } - /** - * @return string|null - */ - public function readMainToThreadPacket(){ + public function readMainToThreadPacket() : ?string{ return $this->internalQueue->shift(); } @@ -140,10 +137,7 @@ public function pushThreadToMainPacket(string $str){ $this->externalQueue[] = $str; } - /** - * @return string|null - */ - public function readThreadToMainPacket(){ + public function readThreadToMainPacket() : ?string{ return $this->externalQueue->shift(); } @@ -223,7 +217,7 @@ public function cleanPath($path){ return str_replace(["\\", ".php", "phar://", str_replace(["\\", "phar://"], ["/", ""], $this->mainPath)], ["/", "", "", ""], $path); } - public function run(){ + public function run() : void{ try{ $this->loader->register(true); diff --git a/src/raklib/server/SessionManager.php b/src/raklib/server/SessionManager.php index 57ab6f29..c9a729d7 100644 --- a/src/raklib/server/SessionManager.php +++ b/src/raklib/server/SessionManager.php @@ -407,13 +407,7 @@ protected static function addressHash(string $ip, int $port) : string{ return $ip . ":" . $port; } - /** - * @param string $ip - * @param int $port - * - * @return Session|null - */ - public function getSession(string $ip, int $port){ + public function getSession(string $ip, int $port) : ?Session{ return $this->sessions[self::addressHash($ip, $port)] ?? null; } @@ -480,13 +474,7 @@ private function registerPacket(int $id, string $class){ $this->packetPool[$id] = new $class; } - /** - * @param int $id - * @param string $buffer - * - * @return Packet|null - */ - public function getPacketFromPool(int $id, string $buffer = ""){ + public function getPacketFromPool(int $id, string $buffer = "") : ?Packet{ $pk = $this->packetPool[$id]; if($pk !== null){ $pk = clone $pk; diff --git a/src/raklib/server/UDPServerSocket.php b/src/raklib/server/UDPServerSocket.php index c5c45a63..6ed40a5f 100644 --- a/src/raklib/server/UDPServerSocket.php +++ b/src/raklib/server/UDPServerSocket.php @@ -45,24 +45,10 @@ public function close(){ socket_close($this->socket); } - /** - * @param string|null &$buffer - * @param string|null &$source - * @param int|null &$port - * - * @return int|bool - */ - public function readPacket(&$buffer, &$source, &$port){ + public function readPacket(?string &$buffer, ?string &$source, ?int &$port){ return socket_recvfrom($this->socket, $buffer, 65535, 0, $source, $port); } - /** - * @param string $buffer - * @param string $dest - * @param int $port - * - * @return int|bool - */ public function writePacket(string $buffer, string $dest, int $port){ return socket_sendto($this->socket, $buffer, strlen($buffer), 0, $dest, $port); } From b38e962345fa2807d18d0ba5a9361e48cbcd203a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Tue, 30 Jan 2018 15:06:03 +0100 Subject: [PATCH 4/9] Terminal::hasFormattingCodes() workaround for STDOUT not being defined From pmmp --- src/pocketmine/utils/Terminal.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/utils/Terminal.php b/src/pocketmine/utils/Terminal.php index 97d38201..0508c1e9 100644 --- a/src/pocketmine/utils/Terminal.php +++ b/src/pocketmine/utils/Terminal.php @@ -61,13 +61,15 @@ public static function hasFormattingCodes(){ if(isset($opts["disable-ansi"])){ self::$formattingCodes = false; }else{ + $stdout = fopen("php://stdout", "w"); self::$formattingCodes = (isset($opts["enable-ansi"]) or ( //user explicitly told us to enable ANSI - stream_isatty(STDOUT) and //STDOUT isn't being piped + stream_isatty($stdout) and //STDOUT isn't being piped ( getenv('TERM') !== false or //Console says it supports colours - (function_exists('sapi_windows_vt100_support') and sapi_windows_vt100_support(STDOUT)) //we're on windows and have vt100 support + (function_exists('sapi_windows_vt100_support') and sapi_windows_vt100_support($stdout)) //we're on windows and have vt100 support ) )); + fclose($stdout); } } From ee7442349bd0873eaf0954b58e2528a893b64ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Tue, 30 Jan 2018 15:42:21 +0100 Subject: [PATCH 5/9] Fixed AsyncWorker --- src/pocketmine/scheduler/AsyncWorker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/scheduler/AsyncWorker.php b/src/pocketmine/scheduler/AsyncWorker.php index 3e791d69..32d3e622 100644 --- a/src/pocketmine/scheduler/AsyncWorker.php +++ b/src/pocketmine/scheduler/AsyncWorker.php @@ -68,7 +68,7 @@ public function handleException(\Throwable $e){ /** * @return string */ - public function getThreadName(){ + public function getThreadName() : string{ return "Asynchronous Worker #" . $this->id; } From 4104a9a07c79602aa037b54a1e087364cca4d7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Tue, 30 Jan 2018 15:44:50 +0100 Subject: [PATCH 6/9] Change PHP Version (Revert etcem) --- src/pocketmine/PocketMine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 6142e35c..1408ed6c 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -88,7 +88,7 @@ function dummy(){ const CODENAME = "Wolf"; const TURANIC_API_VERSION = '2.0.0'; - const MIN_PHP_VERSION = "7.2.1"; + const MIN_PHP_VERSION = "7.2.0"; /* * Startup code. Do not look at it, it may harm you. From 5f084805d73261324a2e1e9950adb33b7fd0c95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Tue, 30 Jan 2018 15:52:54 +0100 Subject: [PATCH 7/9] Fix RaklibServer --- src/raklib/server/RakLibServer.php | 403 ++++++++++++++--------------- 1 file changed, 201 insertions(+), 202 deletions(-) diff --git a/src/raklib/server/RakLibServer.php b/src/raklib/server/RakLibServer.php index 3deb8232..b59d58b3 100644 --- a/src/raklib/server/RakLibServer.php +++ b/src/raklib/server/RakLibServer.php @@ -20,207 +20,206 @@ use raklib\utils\InternetAddress; class RakLibServer extends \Thread{ - /** @var InternetAddress */ - private $address; - - /** @var \ThreadedLogger */ - protected $logger; - - /** @var string */ - protected $loaderPath; - - /** @var bool */ - protected $shutdown = false; - - /** @var \Threaded */ - protected $externalQueue; - /** @var \Threaded */ - protected $internalQueue; - - /** @var string */ - protected $mainPath; - - /** @var int */ - protected $serverId = 0; - /** @var int */ - protected $maxMtuSize; - - - /** - * @param \ThreadedLogger $logger - * @param string $autoloaderPath Path to Composer autoloader - * @param InternetAddress $address - * @param int $maxMtuSize - * - * @throws \Exception - */ - public function __construct(\ThreadedLogger $logger, string $autoloaderPath, InternetAddress $address, int $maxMtuSize = 1492){ - $this->address = $address; - - $this->serverId = mt_rand(0, PHP_INT_MAX); - $this->maxMtuSize = $maxMtuSize; - - $this->logger = $logger; - $this->loaderPath = $autoloaderPath; - - $this->externalQueue = new \Threaded; - $this->internalQueue = new \Threaded; - - if(\Phar::running(true) !== ""){ - $this->mainPath = \Phar::running(true); - }else{ - $this->mainPath = \realpath(\getcwd()) . DIRECTORY_SEPARATOR; - } - } - - public function isShutdown() : bool{ - return $this->shutdown === true; - } - - public function shutdown() : void{ - $this->shutdown = true; - } - - /** - * Returns the RakNet server ID - * @return int - */ - public function getServerId() : int{ - return $this->serverId; - } - - /** - * @return \ThreadedLogger - */ - public function getLogger() : \ThreadedLogger{ - return $this->logger; - } - - /** - * @return \Threaded - */ - public function getExternalQueue() : \Threaded{ - return $this->externalQueue; - } - - /** - * @return \Threaded - */ - public function getInternalQueue() : \Threaded{ - return $this->internalQueue; - } - - public function pushMainToThreadPacket(string $str) : void{ - $this->internalQueue[] = $str; - } - - public function readMainToThreadPacket() : ?string{ - return $this->internalQueue->shift(); - } - - public function pushThreadToMainPacket(string $str) : void{ - $this->externalQueue[] = $str; - } - - public function readThreadToMainPacket() : ?string{ - return $this->externalQueue->shift(); - } - - public function shutdownHandler(){ - if($this->shutdown !== true){ - $this->getLogger()->emergency("RakLib crashed!"); - } - } - - public function errorHandler($errno, $errstr, $errfile, $errline){ - if(error_reporting() === 0){ - return false; - } - - $errorConversion = [ - E_ERROR => "E_ERROR", - E_WARNING => "E_WARNING", - E_PARSE => "E_PARSE", - E_NOTICE => "E_NOTICE", - E_CORE_ERROR => "E_CORE_ERROR", - E_CORE_WARNING => "E_CORE_WARNING", - E_COMPILE_ERROR => "E_COMPILE_ERROR", - E_COMPILE_WARNING => "E_COMPILE_WARNING", - E_USER_ERROR => "E_USER_ERROR", - E_USER_WARNING => "E_USER_WARNING", - E_USER_NOTICE => "E_USER_NOTICE", - E_STRICT => "E_STRICT", - E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", - E_DEPRECATED => "E_DEPRECATED", - E_USER_DEPRECATED => "E_USER_DEPRECATED" - ]; - - $errno = $errorConversion[$errno] ?? $errno; - - $errstr = preg_replace('/\s+/', ' ', trim($errstr)); - $errfile = $this->cleanPath($errfile); - - $this->getLogger()->debug("An $errno error happened: \"$errstr\" in \"$errfile\" at line $errline"); - - foreach($this->getTrace(2) as $i => $line){ - $this->getLogger()->debug($line); - } - - return true; - } - - public function getTrace($start = 0, $trace = null){ - if($trace === null){ - if(function_exists("xdebug_get_function_stack")){ - $trace = array_reverse(xdebug_get_function_stack()); - }else{ - $e = new \Exception(); - $trace = $e->getTrace(); - } - } - - $messages = []; - $j = 0; - for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){ - $params = ""; - if(isset($trace[$i]["args"]) or isset($trace[$i]["params"])){ - if(isset($trace[$i]["args"])){ - $args = $trace[$i]["args"]; - }else{ - $args = $trace[$i]["params"]; - } - foreach($args as $name => $value){ - $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . @strval($value)) . ", "; - } - } - $messages[] = "#$j " . (isset($trace[$i]["file"]) ? $this->cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")"; - } - - return $messages; - } - - public function cleanPath($path){ - return str_replace(["\\", ".php", "phar://", str_replace(["\\", "phar://"], ["/", ""], $this->mainPath)], ["/", "", "", ""], $path); - } - - public function run() : void{ - try{ - require $this->loaderPath; - - gc_enable(); - error_reporting(-1); - ini_set("display_errors", '1'); - ini_set("display_startup_errors", '1'); - - set_error_handler([$this, "errorHandler"], E_ALL); - register_shutdown_function([$this, "shutdownHandler"]); - - - $socket = new UDPServerSocket($this->address); - new SessionManager($this, $socket, $this->maxMtuSize); - }catch(\Throwable $e){ - $this->logger->logException($e); - } - } + /** @var InternetAddress */ + private $address; + + /** @var \ThreadedLogger */ + protected $logger; + + /** @var \ClassLoader */ + protected $loader; + + /** @var bool */ + protected $shutdown = false; + + /** @var \Threaded */ + protected $externalQueue; + /** @var \Threaded */ + protected $internalQueue; + + /** @var string */ + protected $mainPath; + + /** @var int */ + protected $serverId = 0; + /** @var int */ + protected $maxMtuSize; + + + /** + * @param \ThreadedLogger $logger + * @param \ClassLoader $loader + * @param InternetAddress $address + * @param int $maxMtuSize + * + */ + public function __construct(\ThreadedLogger $logger, \ClassLoader $loader, InternetAddress $address, int $maxMtuSize = 1492){ + $this->address = $address; + + $this->serverId = mt_rand(0, PHP_INT_MAX); + $this->maxMtuSize = $maxMtuSize; + + $this->logger = $logger; + $this->loader = $loader; + + $this->externalQueue = new \Threaded; + $this->internalQueue = new \Threaded; + + if(\Phar::running(true) !== ""){ + $this->mainPath = \Phar::running(true); + }else{ + $this->mainPath = \realpath(\getcwd()) . DIRECTORY_SEPARATOR; + } + } + + public function isShutdown() : bool{ + return $this->shutdown === true; + } + + public function shutdown(){ + $this->shutdown = true; + } + + /** + * Returns the RakNet server ID + * @return int + */ + public function getServerId() : int{ + return $this->serverId; + } + + /** + * @return \ThreadedLogger + */ + public function getLogger() : \ThreadedLogger{ + return $this->logger; + } + + /** + * @return \Threaded + */ + public function getExternalQueue() : \Threaded{ + return $this->externalQueue; + } + + /** + * @return \Threaded + */ + public function getInternalQueue() : \Threaded{ + return $this->internalQueue; + } + + public function pushMainToThreadPacket(string $str){ + $this->internalQueue[] = $str; + } + + public function readMainToThreadPacket(){ + return $this->internalQueue->shift(); + } + + public function pushThreadToMainPacket(string $str){ + $this->externalQueue[] = $str; + } + + public function readThreadToMainPacket(){ + return $this->externalQueue->shift(); + } + + public function shutdownHandler(){ + if($this->shutdown !== true){ + $this->getLogger()->emergency("RakLib crashed!"); + } + } + + public function errorHandler($errno, $errstr, $errfile, $errline){ + if(error_reporting() === 0){ + return false; + } + + $errorConversion = [ + E_ERROR => "E_ERROR", + E_WARNING => "E_WARNING", + E_PARSE => "E_PARSE", + E_NOTICE => "E_NOTICE", + E_CORE_ERROR => "E_CORE_ERROR", + E_CORE_WARNING => "E_CORE_WARNING", + E_COMPILE_ERROR => "E_COMPILE_ERROR", + E_COMPILE_WARNING => "E_COMPILE_WARNING", + E_USER_ERROR => "E_USER_ERROR", + E_USER_WARNING => "E_USER_WARNING", + E_USER_NOTICE => "E_USER_NOTICE", + E_STRICT => "E_STRICT", + E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", + E_DEPRECATED => "E_DEPRECATED", + E_USER_DEPRECATED => "E_USER_DEPRECATED" + ]; + + $errno = $errorConversion[$errno] ?? $errno; + + $errstr = preg_replace('/\s+/', ' ', trim($errstr)); + $errfile = $this->cleanPath($errfile); + + $this->getLogger()->debug("An $errno error happened: \"$errstr\" in \"$errfile\" at line $errline"); + + foreach($this->getTrace(2) as $i => $line){ + $this->getLogger()->debug($line); + } + + return true; + } + + public function getTrace($start = 0, $trace = null){ + if($trace === null){ + if(function_exists("xdebug_get_function_stack")){ + $trace = array_reverse(xdebug_get_function_stack()); + }else{ + $e = new \Exception(); + $trace = $e->getTrace(); + } + } + + $messages = []; + $j = 0; + for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){ + $params = ""; + if(isset($trace[$i]["args"]) or isset($trace[$i]["params"])){ + if(isset($trace[$i]["args"])){ + $args = $trace[$i]["args"]; + }else{ + $args = $trace[$i]["params"]; + } + foreach($args as $name => $value){ + $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . @strval($value)) . ", "; + } + } + $messages[] = "#$j " . (isset($trace[$i]["file"]) ? $this->cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")"; + } + + return $messages; + } + + public function cleanPath($path){ + return str_replace(["\\", ".php", "phar://", str_replace(["\\", "phar://"], ["/", ""], $this->mainPath)], ["/", "", "", ""], $path); + } + + public function run() : void{ + try{ + $this->loader->register(true); + + gc_enable(); + error_reporting(-1); + ini_set("display_errors", '1'); + ini_set("display_startup_errors", '1'); + + set_error_handler([$this, "errorHandler"], E_ALL); + register_shutdown_function([$this, "shutdownHandler"]); + + + $socket = new UDPServerSocket($this->address); + new SessionManager($this, $socket, $this->maxMtuSize); + }catch(\Throwable $e){ + $this->logger->logException($e); + } + } } \ No newline at end of file From 347087aa038a5efb81ec050455e5a14bc3b5f6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Tue, 30 Jan 2018 15:59:27 +0100 Subject: [PATCH 8/9] =?UTF-8?q?F=C4=B0XES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/raklib/server/SessionManager.php | 952 ++++++++++++++------------- src/raklib/utils/InternetAddress.php | 2 +- 2 files changed, 479 insertions(+), 475 deletions(-) diff --git a/src/raklib/server/SessionManager.php b/src/raklib/server/SessionManager.php index 8e53ecc5..649cbe87 100644 --- a/src/raklib/server/SessionManager.php +++ b/src/raklib/server/SessionManager.php @@ -37,483 +37,487 @@ class SessionManager{ - const RAKLIB_TPS = 100; - const RAKLIB_TIME_PER_TICK = 1 / self::RAKLIB_TPS; + const RAKLIB_TPS = 100; + const RAKLIB_TIME_PER_TICK = 1 / self::RAKLIB_TPS; - /** @var \SplFixedArray */ - protected $packetPool; + /** @var \SplFixedArray */ + protected $packetPool; - /** @var RakLibServer */ - protected $server; - /** @var UDPServerSocket */ - protected $socket; + /** @var RakLibServer */ + protected $server; + /** @var UDPServerSocket */ + protected $socket; - /** @var int */ - protected $receiveBytes = 0; - /** @var int */ - protected $sendBytes = 0; + /** @var int */ + protected $receiveBytes = 0; + /** @var int */ + protected $sendBytes = 0; - /** @var Session[] */ - protected $sessions = []; + /** @var Session[] */ + protected $sessions = []; - /** @var OfflineMessageHandler */ - protected $offlineMessageHandler; - /** @var string */ - protected $name = ""; - - /** @var int */ - protected $packetLimit = 200; - - /** @var bool */ - protected $shutdown = false; - - /** @var int */ - protected $ticks = 0; - /** @var float */ - protected $lastMeasure; - - /** @var float[] string (address) => float (unblock time) */ - protected $block = []; - /** @var int[] string (address) => int (number of packets) */ - protected $ipSec = []; - - public $portChecking = false; - - /** @var int */ - protected $startTimeMS; - - /** @var int */ - protected $maxMtuSize; - - protected $reusableAddress; - - public function __construct(RakLibServer $server, UDPServerSocket $socket, int $maxMtuSize){ - $this->server = $server; - $this->socket = $socket; - - $this->startTimeMS = (int) (microtime(true) * 1000); - $this->maxMtuSize = $maxMtuSize; - - $this->offlineMessageHandler = new OfflineMessageHandler($this); - - $this->reusableAddress = new InternetAddress("0.0.0.0", 0, 4); - - $this->registerPackets(); - - $this->run(); - } - - /** - * Returns the time in milliseconds since server start. - * @return int - */ - public function getRakNetTimeMS() : int{ - return ((int) (microtime(true) * 1000)) - $this->startTimeMS; - } - - public function getPort() : int{ - return $this->socket->getBindAddress()->port; - } - - public function getMaxMtuSize() : int{ - return $this->maxMtuSize; - } - - public function getLogger() : \ThreadedLogger{ - return $this->server->getLogger(); - } - - public function run() : void{ - $this->tickProcessor(); - } - - private function tickProcessor() : void{ - $this->lastMeasure = microtime(true); - - while(!$this->shutdown){ - $start = microtime(true); - while($this->receivePacket()){} - while($this->receiveStream()){} - $time = microtime(true) - $start; - if($time < self::RAKLIB_TIME_PER_TICK){ - @time_sleep_until(microtime(true) + self::RAKLIB_TIME_PER_TICK - $time); - } - $this->tick(); - } - } - - private function tick() : void{ - $time = microtime(true); - foreach($this->sessions as $session){ - $session->update($time); - } - - $this->ipSec = []; - - if(($this->ticks % self::RAKLIB_TPS) === 0){ - $diff = max(0.005, $time - $this->lastMeasure); - $this->streamOption("bandwidth", serialize([ - "up" => $this->sendBytes / $diff, - "down" => $this->receiveBytes / $diff - ])); - $this->lastMeasure = $time; - $this->sendBytes = 0; - $this->receiveBytes = 0; - - if(count($this->block) > 0){ - asort($this->block); - $now = microtime(true); - foreach($this->block as $address => $timeout){ - if($timeout <= $now){ - unset($this->block[$address]); - }else{ - break; - } - } - } - } - - ++$this->ticks; - } - - - private function receivePacket() : bool{ - $address = $this->reusableAddress; - - $len = $this->socket->readPacket($buffer, $address->ip, $address->port); - if($len === false){ - return false; - } - - $this->receiveBytes += $len; - if(isset($this->block[$address->ip])){ - return true; - } - - if(isset($this->ipSec[$address->ip])){ - if(++$this->ipSec[$address->ip] >= $this->packetLimit){ - $this->blockAddress($address->ip); - return true; - } - }else{ - $this->ipSec[$address->ip] = 1; - } - - if($len < 1){ - return true; - } - - try{ - $pid = ord($buffer{0}); - - $session = $this->getSession($address); - if($session !== null){ - if(($pid & Datagram::BITFLAG_VALID) !== 0){ - if($pid & Datagram::BITFLAG_ACK){ - $session->handlePacket(new ACK($buffer)); - }elseif($pid & Datagram::BITFLAG_NAK){ - $session->handlePacket(new NACK($buffer)); - }else{ - $session->handlePacket(new Datagram($buffer)); - } - }else{ - $this->server->getLogger()->debug("Ignored unconnected packet from $address due to session already opened (0x" . dechex($pid) . ")"); - } - }elseif(($pk = $this->getPacketFromPool($pid, $buffer)) instanceof OfflineMessage){ - /** @var OfflineMessage $pk */ - - do{ - try{ - $pk->decode(); - if(!$pk->isValid()){ - throw new \InvalidArgumentException("Packet magic is invalid"); - } - }catch(\Throwable $e){ - $logger = $this->server->getLogger(); - $logger->debug("Received garbage message from $address (" . $e->getMessage() . "): " . bin2hex($pk->buffer)); - foreach($this->server->getTrace(0, $e->getTrace()) as $line){ - $logger->debug($line); - } - $this->blockAddress($address->ip, 5); - break; - } - - if(!$this->offlineMessageHandler->handle($pk, $address)){ - $this->server->getLogger()->debug("Unhandled unconnected packet " . get_class($pk) . " received from $address"); - } - }while(false); - }elseif(($pid & Datagram::BITFLAG_VALID) !== 0 and ($pid & 0x03) === 0){ - // Loose datagram, don't relay it as a raw packet - // RakNet does not currently use the 0x02 or 0x01 bitflags on any datagram header, so we can use - // this to identify the difference between loose datagrams and packets like Query. - $this->server->getLogger()->debug("Ignored connected packet from $address due to no session opened (0x" . dechex($pid) . ")"); - }else{ - $this->streamRaw($address, $buffer); - } - }catch(\Throwable $e){ - $logger = $this->getLogger(); - $logger->debug("Packet from $address (" . strlen($buffer) . " bytes): 0x" . bin2hex($buffer)); - $logger->logException($e); - $this->blockAddress($address->ip, 5); - } - - return true; - } - - public function sendPacket(Packet $packet, InternetAddress $address) : void{ - $packet->encode(); - $this->sendBytes += $this->socket->writePacket($packet->buffer, $address->ip, $address->port); - } - - public function streamEncapsulated(Session $session, EncapsulatedPacket $packet, int $flags = RakLib::PRIORITY_NORMAL) : void{ - $id = $session->getAddress()->toString(); - $buffer = chr(RakLib::PACKET_ENCAPSULATED) . chr(strlen($id)) . $id . chr($flags) . $packet->toInternalBinary(); - $this->server->pushThreadToMainPacket($buffer); - } - - public function streamRaw(InternetAddress $source, string $payload) : void{ - $buffer = chr(RakLib::PACKET_RAW) . chr(strlen($source->ip)) . $source->ip . Binary::writeShort($source->port) . $payload; - $this->server->pushThreadToMainPacket($buffer); - } - - protected function streamClose(string $identifier, string $reason) : void{ - $buffer = chr(RakLib::PACKET_CLOSE_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($reason)) . $reason; - $this->server->pushThreadToMainPacket($buffer); - } - - protected function streamInvalid(string $identifier) : void{ - $buffer = chr(RakLib::PACKET_INVALID_SESSION) . chr(strlen($identifier)) . $identifier; - $this->server->pushThreadToMainPacket($buffer); - } - - protected function streamOpen(Session $session) : void{ - $address = $session->getAddress(); - $identifier = $address->toString(); - $buffer = chr(RakLib::PACKET_OPEN_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($address->ip)) . $address->ip . Binary::writeShort($address->port) . Binary::writeLong($session->getID()); - $this->server->pushThreadToMainPacket($buffer); - } - - protected function streamACK(string $identifier, int $identifierACK) : void{ - $buffer = chr(RakLib::PACKET_ACK_NOTIFICATION) . chr(strlen($identifier)) . $identifier . Binary::writeInt($identifierACK); - $this->server->pushThreadToMainPacket($buffer); - } - - /** - * @param string $name - * @param mixed $value - */ - protected function streamOption(string $name, $value) : void{ - $buffer = chr(RakLib::PACKET_SET_OPTION) . chr(strlen($name)) . $name . $value; - $this->server->pushThreadToMainPacket($buffer); - } - - public function streamPingMeasure(Session $session, int $pingMS) : void{ - $identifier = $session->getAddress()->toString(); - $buffer = chr(RakLib::PACKET_REPORT_PING) . chr(strlen($identifier)) . $identifier . Binary::writeInt($pingMS); - $this->server->pushThreadToMainPacket($buffer); - } - - public function receiveStream() : bool{ - if(($packet = $this->server->readMainToThreadPacket()) !== null){ - $id = ord($packet{0}); - $offset = 1; - if($id === RakLib::PACKET_ENCAPSULATED){ - $len = ord($packet{$offset++}); - $identifier = substr($packet, $offset, $len); - $offset += $len; - $session = $this->sessions[$identifier] ?? null; - if($session !== null and $session->isConnected()){ - $flags = ord($packet{$offset++}); - $buffer = substr($packet, $offset); - $session->addEncapsulatedToQueue(EncapsulatedPacket::fromInternalBinary($buffer), $flags); - }else{ - $this->streamInvalid($identifier); - } - }elseif($id === RakLib::PACKET_RAW){ - $len = ord($packet{$offset++}); - $address = substr($packet, $offset, $len); - $offset += $len; - $port = Binary::readShort(substr($packet, $offset, 2)); - $offset += 2; - $payload = substr($packet, $offset); - $this->socket->writePacket($payload, $address, $port); - }elseif($id === RakLib::PACKET_CLOSE_SESSION){ - $len = ord($packet{$offset++}); - $identifier = substr($packet, $offset, $len); - if(isset($this->sessions[$identifier])){ - $this->sessions[$identifier]->flagForDisconnection(); - }else{ - $this->streamInvalid($identifier); - } - }elseif($id === RakLib::PACKET_INVALID_SESSION){ - $len = ord($packet{$offset++}); - $identifier = substr($packet, $offset, $len); - if(isset($this->sessions[$identifier])){ - $this->removeSession($this->sessions[$identifier]); - } - }elseif($id === RakLib::PACKET_SET_OPTION){ - $len = ord($packet{$offset++}); - $name = substr($packet, $offset, $len); - $offset += $len; - $value = substr($packet, $offset); - switch($name){ - case "name": - $this->name = $value; - break; - case "portChecking": - $this->portChecking = (bool) $value; - break; - case "packetLimit": - $this->packetLimit = (int) $value; - break; - } - }elseif($id === RakLib::PACKET_BLOCK_ADDRESS){ - $len = ord($packet{$offset++}); - $address = substr($packet, $offset, $len); - $offset += $len; - $timeout = Binary::readInt(substr($packet, $offset, 4)); - $this->blockAddress($address, $timeout); - }elseif($id === RakLib::PACKET_UNBLOCK_ADDRESS){ - $len = ord($packet{$offset++}); - $address = substr($packet, $offset, $len); - $this->unblockAddress($address); - }elseif($id === RakLib::PACKET_SHUTDOWN){ - foreach($this->sessions as $session){ - $this->removeSession($session); - } - - $this->socket->close(); - $this->shutdown = true; - }elseif($id === RakLib::PACKET_EMERGENCY_SHUTDOWN){ - $this->shutdown = true; - }else{ - return false; - } - - return true; - } - - return false; - } - - public function blockAddress(string $address, int $timeout = 300) : void{ - $final = microtime(true) + $timeout; - if(!isset($this->block[$address]) or $timeout === -1){ - if($timeout === -1){ - $final = PHP_INT_MAX; - }else{ - $this->getLogger()->notice("Blocked $address for $timeout seconds"); - } - $this->block[$address] = $final; - }elseif($this->block[$address] < $final){ - $this->block[$address] = $final; - } - } - - public function unblockAddress(string $address) : void{ - unset($this->block[$address]); - $this->getLogger()->debug("Unblocked $address"); - } - - /** - * @param InternetAddress $address - * - * @return Session|null - */ - public function getSession(InternetAddress $address) : ?Session{ - return $this->sessions[$address->toString()] ?? null; - } - - public function sessionExists(InternetAddress $address) : bool{ - return isset($this->sessions[$address->toString()]); - } - - public function createSession(InternetAddress $address, int $clientId, int $mtuSize) : Session{ - $this->checkSessions(); - - $this->sessions[$address->toString()] = $session = new Session($this, clone $address, $clientId, $mtuSize); - $this->getLogger()->debug("Created session for $address with MTU size $mtuSize"); - - return $session; - } - - public function removeSession(Session $session, string $reason = "unknown") : void{ - $id = $session->getAddress()->toString(); - if(isset($this->sessions[$id])){ - $this->sessions[$id]->close(); - $this->removeSessionInternal($session); - $this->streamClose($id, $reason); - } - } - - public function removeSessionInternal(Session $session) : void{ - unset($this->sessions[$session->getAddress()->toString()]); - } - - public function openSession(Session $session) : void{ - $this->streamOpen($session); - } - - private function checkSessions() : void{ - if(count($this->sessions) > 4096){ - foreach($this->sessions as $i => $s){ - if($s->isTemporal()){ - unset($this->sessions[$i]); - if(count($this->sessions) <= 4096){ - break; - } - } - } - } - } - - public function notifyACK(Session $session, int $identifierACK) : void{ - $this->streamACK($session->getAddress()->toString(), $identifierACK); - } - - public function getName() : string{ - return $this->name; - } - - public function getID() : int{ - return $this->server->getServerId(); - } - - /** - * @param int $id - * @param string $class - */ - private function registerPacket(int $id, string $class) : void{ - $this->packetPool[$id] = new $class; - } - - /** - * @param int $id - * @param string $buffer - * - * @return Packet|null - */ - public function getPacketFromPool(int $id, string $buffer = "") : ?Packet{ - $pk = $this->packetPool[$id]; - if($pk !== null){ - $pk = clone $pk; - $pk->buffer = $buffer; - return $pk; - } - - return null; - } - - private function registerPackets() : void{ - $this->packetPool = new \SplFixedArray(256); - - $this->registerPacket(UnconnectedPing::$ID, UnconnectedPing::class); - $this->registerPacket(UnconnectedPingOpenConnections::$ID, UnconnectedPingOpenConnections::class); - $this->registerPacket(OpenConnectionRequest1::$ID, OpenConnectionRequest1::class); - $this->registerPacket(OpenConnectionReply1::$ID, OpenConnectionReply1::class); - $this->registerPacket(OpenConnectionRequest2::$ID, OpenConnectionRequest2::class); - $this->registerPacket(OpenConnectionReply2::$ID, OpenConnectionReply2::class); - $this->registerPacket(UnconnectedPong::$ID, UnconnectedPong::class); - $this->registerPacket(AdvertiseSystem::$ID, AdvertiseSystem::class); - } + /** @var OfflineMessageHandler */ + protected $offlineMessageHandler; + /** @var string */ + protected $name = ""; + + /** @var int */ + protected $packetLimit = 200; + + /** @var bool */ + protected $shutdown = false; + + /** @var int */ + protected $ticks = 0; + /** @var float */ + protected $lastMeasure; + + /** @var float[] string (address) => float (unblock time) */ + protected $block = []; + /** @var int[] string (address) => int (number of packets) */ + protected $ipSec = []; + + public $portChecking = false; + + /** @var int */ + protected $startTimeMS; + + /** @var int */ + protected $maxMtuSize; + + protected $reusableAddress; + + public function __construct(RakLibServer $server, UDPServerSocket $socket, int $maxMtuSize){ + $this->server = $server; + $this->socket = $socket; + + $this->startTimeMS = (int) (microtime(true) * 1000); + $this->maxMtuSize = $maxMtuSize; + + $this->offlineMessageHandler = new OfflineMessageHandler($this); + + $this->reusableAddress = new InternetAddress("0.0.0.0", 0, 4); + + $this->registerPackets(); + + $this->run(); + } + + /** + * Returns the time in milliseconds since server start. + * @return int + */ + public function getRakNetTimeMS() : int{ + return ((int) (microtime(true) * 1000)) - $this->startTimeMS; + } + + public function getPort() : int{ + return $this->socket->getBindAddress()->port; + } + + public function getMaxMtuSize() : int{ + return $this->maxMtuSize; + } + + public function getLogger() : \ThreadedLogger{ + return $this->server->getLogger(); + } + + public function run(){ + $this->tickProcessor(); + } + + private function tickProcessor(){ + $this->lastMeasure = microtime(true); + + while(!$this->shutdown){ + $start = microtime(true); + while($this->receivePacket()){} + while($this->receiveStream()){} + $time = microtime(true) - $start; + if($time < self::RAKLIB_TIME_PER_TICK){ + @time_sleep_until(microtime(true) + self::RAKLIB_TIME_PER_TICK - $time); + } + $this->tick(); + } + } + + private function tick(){ + $time = microtime(true); + foreach($this->sessions as $session){ + $session->update($time); + } + + $this->ipSec = []; + + if(($this->ticks % self::RAKLIB_TPS) === 0){ + $diff = max(0.005, $time - $this->lastMeasure); + $this->streamOption("bandwidth", serialize([ + "up" => $this->sendBytes / $diff, + "down" => $this->receiveBytes / $diff + ])); + $this->lastMeasure = $time; + $this->sendBytes = 0; + $this->receiveBytes = 0; + + if(count($this->block) > 0){ + asort($this->block); + $now = microtime(true); + foreach($this->block as $address => $timeout){ + if($timeout <= $now){ + unset($this->block[$address]); + }else{ + break; + } + } + } + } + + ++$this->ticks; + } + + + private function receivePacket() : bool{ + $address = $this->reusableAddress; + + $len = $this->socket->readPacket($buffer, $address->ip, $address->port); + if($len === false){ + return false; + } + + $this->receiveBytes += $len; + if(isset($this->block[$address->ip])){ + return true; + } + + if(isset($this->ipSec[$address->ip])){ + if(++$this->ipSec[$address->ip] >= $this->packetLimit){ + $this->blockAddress($address->ip); + return true; + } + }else{ + $this->ipSec[$address->ip] = 1; + } + + if($len < 1){ + return true; + } + + try{ + $pid = ord($buffer{0}); + + $session = $this->getSession($address); + if($session !== null){ + if(($pid & Datagram::BITFLAG_VALID) !== 0){ + if($pid & Datagram::BITFLAG_ACK){ + $session->handlePacket(new ACK($buffer)); + }elseif($pid & Datagram::BITFLAG_NAK){ + $session->handlePacket(new NACK($buffer)); + }else{ + $session->handlePacket(new Datagram($buffer)); + } + }else{ + $this->server->getLogger()->debug("Ignored unconnected packet from $address due to session already opened (0x" . dechex($pid) . ")"); + } + }elseif(($pk = $this->getPacketFromPool($pid, $buffer)) instanceof OfflineMessage){ + /** @var OfflineMessage $pk */ + + do{ + try{ + $pk->decode(); + if(!$pk->isValid()){ + throw new \InvalidArgumentException("Packet magic is invalid"); + } + }catch(\Throwable $e){ + $logger = $this->server->getLogger(); + $logger->debug("Received garbage message from $address (" . $e->getMessage() . "): " . bin2hex($pk->buffer)); + foreach($this->server->getTrace(0, $e->getTrace()) as $line){ + $logger->debug($line); + } + $this->blockAddress($address->ip, 5); + break; + } + + if(!$this->offlineMessageHandler->handle($pk, $address)){ + $this->server->getLogger()->debug("Unhandled unconnected packet " . get_class($pk) . " received from $address"); + } + }while(false); + }elseif(($pid & Datagram::BITFLAG_VALID) !== 0 and ($pid & 0x03) === 0){ + // Loose datagram, don't relay it as a raw packet + // RakNet does not currently use the 0x02 or 0x01 bitflags on any datagram header, so we can use + // this to identify the difference between loose datagrams and packets like Query. + $this->server->getLogger()->debug("Ignored connected packet from $address due to no session opened (0x" . dechex($pid) . ")"); + }else{ + $this->streamRaw($address, $buffer); + } + }catch(\Throwable $e){ + $logger = $this->getLogger(); + $logger->debug("Packet from $address (" . strlen($buffer) . " bytes): 0x" . bin2hex($buffer)); + $logger->logException($e); + $this->blockAddress($address->ip, 5); + } + + return true; + } + + public function sendPacket(Packet $packet, InternetAddress $address){ + $packet->encode(); + $this->sendBytes += $this->socket->writePacket($packet->buffer, $address->ip, $address->port); + } + + public function streamEncapsulated(Session $session, EncapsulatedPacket $packet, int $flags = RakLib::PRIORITY_NORMAL){ + $id = (string) $session->getAddress(); + $buffer = chr(RakLib::PACKET_ENCAPSULATED) . chr(strlen($id)) . $id . chr($flags) . $packet->toInternalBinary(); + $this->server->pushThreadToMainPacket($buffer); + } + + public function streamRaw(InternetAddress $source, string $payload){ + $buffer = chr(RakLib::PACKET_RAW) . chr(strlen($source->ip)) . $source->ip . Binary::writeShort($source->port) . $payload; + $this->server->pushThreadToMainPacket($buffer); + } + + protected function streamClose(string $identifier, string $reason){ + $buffer = chr(RakLib::PACKET_CLOSE_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($reason)) . $reason; + $this->server->pushThreadToMainPacket($buffer); + } + + protected function streamInvalid(string $identifier){ + $buffer = chr(RakLib::PACKET_INVALID_SESSION) . chr(strlen($identifier)) . $identifier; + $this->server->pushThreadToMainPacket($buffer); + } + + protected function streamOpen(Session $session){ + $address = $session->getAddress(); + $identifier = (string) $address; + $buffer = chr(RakLib::PACKET_OPEN_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($address->ip)) . $address->ip . Binary::writeShort($address->port) . Binary::writeLong($session->getID()); + $this->server->pushThreadToMainPacket($buffer); + } + + protected function streamACK(string $identifier, int $identifierACK){ + $buffer = chr(RakLib::PACKET_ACK_NOTIFICATION) . chr(strlen($identifier)) . $identifier . Binary::writeInt($identifierACK); + $this->server->pushThreadToMainPacket($buffer); + } + + /** + * @param string $name + * @param mixed $value + */ + protected function streamOption(string $name, $value){ + $buffer = chr(RakLib::PACKET_SET_OPTION) . chr(strlen($name)) . $name . $value; + $this->server->pushThreadToMainPacket($buffer); + } + + public function streamPingMeasure(Session $session, int $pingMS){ + $identifier = (string) $session->getAddress(); + $buffer = chr(RakLib::PACKET_REPORT_PING) . chr(strlen($identifier)) . $identifier . Binary::writeInt($pingMS); + $this->server->pushThreadToMainPacket($buffer); + } + + public function receiveStream() : bool{ + if(($packet = $this->server->readMainToThreadPacket()) !== null){ + $id = ord($packet{0}); + $offset = 1; + if($id === RakLib::PACKET_ENCAPSULATED){ + $len = ord($packet{$offset++}); + $identifier = substr($packet, $offset, $len); + $offset += $len; + $session = $this->sessions[$identifier] ?? null; + if($session !== null and $session->isConnected()){ + $flags = ord($packet{$offset++}); + $buffer = substr($packet, $offset); + $session->addEncapsulatedToQueue(EncapsulatedPacket::fromInternalBinary($buffer), $flags); + }else{ + $this->streamInvalid($identifier); + } + }elseif($id === RakLib::PACKET_RAW){ + $len = ord($packet{$offset++}); + $address = substr($packet, $offset, $len); + $offset += $len; + $port = Binary::readShort(substr($packet, $offset, 2)); + $offset += 2; + $payload = substr($packet, $offset); + $this->socket->writePacket($payload, $address, $port); + }elseif($id === RakLib::PACKET_CLOSE_SESSION){ + $len = ord($packet{$offset++}); + $identifier = substr($packet, $offset, $len); + if(isset($this->sessions[$identifier])){ + $this->sessions[$identifier]->flagForDisconnection(); + }else{ + $this->streamInvalid($identifier); + } + }elseif($id === RakLib::PACKET_INVALID_SESSION){ + $len = ord($packet{$offset++}); + $identifier = substr($packet, $offset, $len); + if(isset($this->sessions[$identifier])){ + $this->removeSession($this->sessions[$identifier]); + } + }elseif($id === RakLib::PACKET_SET_OPTION){ + $len = ord($packet{$offset++}); + $name = substr($packet, $offset, $len); + $offset += $len; + $value = substr($packet, $offset); + switch($name){ + case "name": + $this->name = $value; + break; + case "portChecking": + $this->portChecking = (bool) $value; + break; + case "packetLimit": + $this->packetLimit = (int) $value; + break; + } + }elseif($id === RakLib::PACKET_BLOCK_ADDRESS){ + $len = ord($packet{$offset++}); + $address = substr($packet, $offset, $len); + $offset += $len; + $timeout = Binary::readInt(substr($packet, $offset, 4)); + $this->blockAddress($address, $timeout); + }elseif($id === RakLib::PACKET_UNBLOCK_ADDRESS){ + $len = ord($packet{$offset++}); + $address = substr($packet, $offset, $len); + $this->unblockAddress($address); + }elseif($id === RakLib::PACKET_SHUTDOWN){ + foreach($this->sessions as $session){ + $this->removeSession($session); + } + + $this->socket->close(); + $this->shutdown = true; + }elseif($id === RakLib::PACKET_EMERGENCY_SHUTDOWN){ + $this->shutdown = true; + }else{ + return false; + } + + return true; + } + + return false; + } + + public function blockAddress(string $address, int $timeout = 300){ + $final = microtime(true) + $timeout; + if(!isset($this->block[$address]) or $timeout === -1){ + if($timeout === -1){ + $final = PHP_INT_MAX; + }else{ + $this->getLogger()->notice("Blocked $address for $timeout seconds"); + } + $this->block[$address] = $final; + }elseif($this->block[$address] < $final){ + $this->block[$address] = $final; + } + } + + public function unblockAddress(string $address){ + unset($this->block[$address]); + $this->getLogger()->debug("Unblocked $address"); + } + + protected static function addressHash(string $ip, int $port) : string{ + return $ip . ":" . $port; + } + + /** + * @param InternetAddress $address + * + * @return Session|null + */ + public function getSession(InternetAddress $address){ + return $this->sessions[(string) $address] ?? null; + } + + public function sessionExists(InternetAddress $address) : bool{ + return isset($this->sessions[(string) $address]); + } + + public function createSession(InternetAddress $address, int $clientId, int $mtuSize) : Session{ + $this->checkSessions(); + + $this->sessions[(string) $address] = $session = new Session($this, clone $address, $clientId, $mtuSize); + $this->getLogger()->debug("Created session for $address with MTU size $mtuSize"); + + return $session; + } + + public function removeSession(Session $session, string $reason = "unknown"){ + $id = (string) $session->getAddress(); + if(isset($this->sessions[$id])){ + $this->sessions[$id]->close(); + $this->removeSessionInternal($session); + $this->streamClose($id, $reason); + } + } + + public function removeSessionInternal(Session $session){ + unset($this->sessions[(string) $session->getAddress()]); + } + + public function openSession(Session $session){ + $this->streamOpen($session); + } + + private function checkSessions(){ + if(count($this->sessions) > 4096){ + foreach($this->sessions as $i => $s){ + if($s->isTemporal()){ + unset($this->sessions[$i]); + if(count($this->sessions) <= 4096){ + break; + } + } + } + } + } + + public function notifyACK(Session $session, int $identifierACK){ + $this->streamACK((string) $session->getAddress(), $identifierACK); + } + + public function getName() : string{ + return $this->name; + } + + public function getID() : int{ + return $this->server->getServerId(); + } + + /** + * @param int $id + * @param string $class + */ + private function registerPacket(int $id, string $class){ + $this->packetPool[$id] = new $class; + } + + /** + * @param int $id + * @param string $buffer + * + * @return Packet|null + */ + public function getPacketFromPool(int $id, string $buffer = "") : ?Packet{ + $pk = $this->packetPool[$id]; + if($pk !== null){ + $pk = clone $pk; + $pk->buffer = $buffer; + return $pk; + } + + return null; + } + + private function registerPackets(){ + $this->packetPool = new \SplFixedArray(256); + + $this->registerPacket(UnconnectedPing::$ID, UnconnectedPing::class); + $this->registerPacket(UnconnectedPingOpenConnections::$ID, UnconnectedPingOpenConnections::class); + $this->registerPacket(OpenConnectionRequest1::$ID, OpenConnectionRequest1::class); + $this->registerPacket(OpenConnectionReply1::$ID, OpenConnectionReply1::class); + $this->registerPacket(OpenConnectionRequest2::$ID, OpenConnectionRequest2::class); + $this->registerPacket(OpenConnectionReply2::$ID, OpenConnectionReply2::class); + $this->registerPacket(UnconnectedPong::$ID, UnconnectedPong::class); + $this->registerPacket(AdvertiseSystem::$ID, AdvertiseSystem::class); + } } \ No newline at end of file diff --git a/src/raklib/utils/InternetAddress.php b/src/raklib/utils/InternetAddress.php index e948ecfb..02ce9c2f 100644 --- a/src/raklib/utils/InternetAddress.php +++ b/src/raklib/utils/InternetAddress.php @@ -72,4 +72,4 @@ public function getVersion() : int{ public function __toString(){ return $this->ip . ":" . $this->port; } -} +} \ No newline at end of file From e33d14cd87bb71310c799859227488c25c95ee45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Y=C4=B1ld=C4=B1r=C4=B1m?= Date: Tue, 30 Jan 2018 16:12:50 +0100 Subject: [PATCH 9/9] Add return type --- src/raklib/server/SessionManager.php | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/raklib/server/SessionManager.php b/src/raklib/server/SessionManager.php index 649cbe87..ec48257d 100644 --- a/src/raklib/server/SessionManager.php +++ b/src/raklib/server/SessionManager.php @@ -419,12 +419,7 @@ protected static function addressHash(string $ip, int $port) : string{ return $ip . ":" . $port; } - /** - * @param InternetAddress $address - * - * @return Session|null - */ - public function getSession(InternetAddress $address){ + public function getSession(InternetAddress $address) : ?Session{ return $this->sessions[(string) $address] ?? null; } @@ -483,20 +478,10 @@ public function getID() : int{ return $this->server->getServerId(); } - /** - * @param int $id - * @param string $class - */ private function registerPacket(int $id, string $class){ $this->packetPool[$id] = new $class; } - /** - * @param int $id - * @param string $buffer - * - * @return Packet|null - */ public function getPacketFromPool(int $id, string $buffer = "") : ?Packet{ $pk = $this->packetPool[$id]; if($pk !== null){