From 68227b1d4e70b42ce6c61ca2009bf702b5a5f9df Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 23 Apr 2024 14:49:16 +0100 Subject: [PATCH] Improve ease of use without threads, get rid of ServerEventSource this leaves the responsibility of reading packets and ticking the server up to the user, which allows for more dynamic ticking, custom sleep mechanisms (perhaps RakLib is to share a thread with something else), and easier API use when used without threads. --- src/server/Server.php | 68 +++++++++++++------------------- src/server/ServerEventSource.php | 22 ----------- 2 files changed, 28 insertions(+), 62 deletions(-) delete mode 100644 src/server/ServerEventSource.php diff --git a/src/server/Server.php b/src/server/Server.php index 359ca1d..bf865f6 100644 --- a/src/server/Server.php +++ b/src/server/Server.php @@ -44,8 +44,8 @@ class Server implements ServerInterface{ - private const RAKLIB_TPS = 100; - private const RAKLIB_TIME_PER_TICK = 1 / self::RAKLIB_TPS; + public const RAKLIB_TPS = 100; + public const RAKLIB_TIME_PER_TICK = 1 / self::RAKLIB_TPS; protected int $receiveBytes = 0; protected int $sendBytes = 0; @@ -87,7 +87,6 @@ public function __construct( protected ServerSocket $socket, protected int $maxMtuSize, ProtocolAcceptor $protocolAcceptor, - private ServerEventSource $eventSource, private ServerEventListener $eventListener, private ExceptionTraceCleaner $traceCleaner, private int $recvMaxSplitParts = ServerSession::DEFAULT_MAX_SPLIT_PART_COUNT, @@ -113,58 +112,40 @@ public function getLogger() : \Logger{ return $this->logger; } - public function tickProcessor() : void{ - $start = microtime(true); - - /* - * The below code is designed to allow co-op between sending and receiving to avoid slowing down either one - * when high traffic is coming either way. Yielding will occur after 100 messages. - */ - do{ - $stream = !$this->shutdown; - for($i = 0; $i < 100 && $stream && !$this->shutdown; ++$i){ //if we received a shutdown event, we don't care about any more messages from the event source - $stream = $this->eventSource->process($this); - } - - $socket = true; - for($i = 0; $i < 100 && $socket; ++$i){ - $socket = $this->receivePacket(); - } - }while($stream || $socket); - - $this->tick(); - - $time = microtime(true) - $start; - if($time < self::RAKLIB_TIME_PER_TICK){ - @time_sleep_until(microtime(true) + self::RAKLIB_TIME_PER_TICK - $time); - } - } - /** * Disconnects all sessions and blocks until everything has been shut down properly. */ public function waitShutdown() : void{ $this->shutdown = true; - while($this->eventSource->process($this)){ - //Ensure that any late messages are processed before we start initiating server disconnects, so that if the - //server implementation used a custom disconnect mechanism (e.g. a server transfer), we don't break it in - //race conditions. - } - foreach($this->sessions as $session){ $session->initiateDisconnect(DisconnectReason::SERVER_SHUTDOWN); } while(count($this->sessions) > 0){ - $this->tickProcessor(); + $start = microtime(true); + + while($this->receivePacket()){ + //NOOP + } + $this->tick(); + + $time = microtime(true) - $start; + if($time < self::RAKLIB_TIME_PER_TICK && count($this->sessions) > 0){ + @time_sleep_until(microtime(true) + self::RAKLIB_TIME_PER_TICK - $time); + } } $this->socket->close(); $this->logger->debug("Graceful shutdown complete"); } - private function tick() : void{ + /** + * Ticks sessions, updates bandwidth stats and IP bans, and other heartbeat tasks. + * This should be called once per 10ms. It must be called in regular intervals. + * @see self::RAKLIB_TIME_PER_TICK + */ + public function tick() : void{ $time = microtime(true); foreach($this->sessions as $session){ $session->update($time); @@ -198,8 +179,15 @@ private function tick() : void{ ++$this->ticks; } - /** @phpstan-impure */ - private function receivePacket() : bool{ + /** + * Reads a packet from the socket and processes it. + * + * This should be called in a loop until it returns false. However, it may be desirable to break out of the loop + * to do other tasks even if there are packets left to process, to ensure high traffic doesn't starve other tasks. + * + * @phpstan-impure + */ + public function receivePacket() : bool{ try{ $buffer = $this->socket->readPacket($addressIp, $addressPort); }catch(SocketException $e){ diff --git a/src/server/ServerEventSource.php b/src/server/ServerEventSource.php deleted file mode 100644 index 78e3da2..0000000 --- a/src/server/ServerEventSource.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * RakLib is not affiliated with Jenkins Software LLC nor RakNet. - * - * RakLib is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - */ - -declare(strict_types=1); - -namespace raklib\server; - -interface ServerEventSource{ - - public function process(ServerInterface $server) : bool; -}