PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/raklib/server/SessionManager.php

https://gitlab.com/Skull3x/GladiatorMine
PHP | 431 lines | 343 code | 61 blank | 27 comment | 23 complexity | d294de7ca14ffe082cf78e9b5d47116e MD5 | raw file
  1. <?php
  2. /*
  3. * RakLib network library
  4. *
  5. *
  6. * This project is not affiliated with Jenkins Software LLC nor RakNet.
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. */
  14. namespace raklib\server;
  15. use raklib\Binary;
  16. use raklib\protocol\ACK;
  17. use raklib\protocol\ADVERTISE_SYSTEM;
  18. use raklib\protocol\DATA_PACKET_0;
  19. use raklib\protocol\DATA_PACKET_1;
  20. use raklib\protocol\DATA_PACKET_2;
  21. use raklib\protocol\DATA_PACKET_3;
  22. use raklib\protocol\DATA_PACKET_4;
  23. use raklib\protocol\DATA_PACKET_5;
  24. use raklib\protocol\DATA_PACKET_6;
  25. use raklib\protocol\DATA_PACKET_7;
  26. use raklib\protocol\DATA_PACKET_8;
  27. use raklib\protocol\DATA_PACKET_9;
  28. use raklib\protocol\DATA_PACKET_A;
  29. use raklib\protocol\DATA_PACKET_B;
  30. use raklib\protocol\DATA_PACKET_C;
  31. use raklib\protocol\DATA_PACKET_D;
  32. use raklib\protocol\DATA_PACKET_E;
  33. use raklib\protocol\DATA_PACKET_F;
  34. use raklib\protocol\EncapsulatedPacket;
  35. use raklib\protocol\NACK;
  36. use raklib\protocol\OPEN_CONNECTION_REPLY_1;
  37. use raklib\protocol\OPEN_CONNECTION_REPLY_2;
  38. use raklib\protocol\OPEN_CONNECTION_REQUEST_1;
  39. use raklib\protocol\OPEN_CONNECTION_REQUEST_2;
  40. use raklib\protocol\Packet;
  41. use raklib\protocol\UNCONNECTED_PING;
  42. use raklib\protocol\UNCONNECTED_PING_OPEN_CONNECTIONS;
  43. use raklib\protocol\UNCONNECTED_PONG;
  44. use raklib\RakLib;
  45. class SessionManager{
  46. protected $packetPool = [];
  47. /** @var RakLibServer */
  48. protected $server;
  49. protected $socket;
  50. protected $receiveBytes = 0;
  51. protected $sendBytes = 0;
  52. /** @var Session[] */
  53. protected $sessions = [];
  54. protected $name = "";
  55. protected $packetLimit = 1000;
  56. protected $shutdown = false;
  57. protected $ticks = 0;
  58. protected $lastMeasure;
  59. protected $block = [];
  60. protected $ipSec = [];
  61. public $portChecking = true;
  62. public function __construct(RakLibServer $server, UDPServerSocket $socket){
  63. $this->server = $server;
  64. $this->socket = $socket;
  65. $this->registerPackets();
  66. $this->serverId = mt_rand(0, PHP_INT_MAX);
  67. $this->run();
  68. }
  69. public function getPort(){
  70. return $this->server->getPort();
  71. }
  72. public function getLogger(){
  73. return $this->server->getLogger();
  74. }
  75. public function run(){
  76. $this->tickProcessor();
  77. }
  78. private function tickProcessor(){
  79. $this->lastMeasure = microtime(true);
  80. while(!$this->shutdown){
  81. $start = microtime(true);
  82. $max = 5000;
  83. while(--$max and $this->receivePacket());
  84. while($this->receiveStream());
  85. $time = microtime(true) - $start;
  86. if($time < 0.05){
  87. time_sleep_until(microtime(true) + 0.05 - $time);
  88. }
  89. $this->tick();
  90. }
  91. }
  92. private function tick(){
  93. $time = microtime(true);
  94. foreach($this->sessions as $session){
  95. $session->update($time);
  96. }
  97. foreach($this->ipSec as $address => $count){
  98. if($count >= $this->packetLimit){
  99. $this->blockAddress($address);
  100. }
  101. }
  102. $this->ipSec = [];
  103. if(($this->ticks & 0b1111) === 0){
  104. $diff = max(0.005, $time - $this->lastMeasure);
  105. $this->streamOption("bandwidth", serialize([
  106. "up" => $this->sendBytes / $diff,
  107. "down" => $this->receiveBytes / $diff
  108. ]));
  109. $this->lastMeasure = $time;
  110. $this->sendBytes = 0;
  111. $this->receiveBytes = 0;
  112. if(count($this->block) > 0){
  113. asort($this->block);
  114. $now = microtime(true);
  115. foreach($this->block as $address => $timeout){
  116. if($timeout <= $now){
  117. unset($this->block[$address]);
  118. }else{
  119. break;
  120. }
  121. }
  122. }
  123. }
  124. ++$this->ticks;
  125. }
  126. private function receivePacket(){
  127. if(($len = $this->socket->readPacket($buffer, $source, $port)) > 0){
  128. $this->receiveBytes += $len;
  129. if(isset($this->block[$source])){
  130. return true;
  131. }
  132. if(isset($this->ipSec[$source])){
  133. $this->ipSec[$source]++;
  134. }else{
  135. $this->ipSec[$source] = 1;
  136. }
  137. $pid = ord($buffer{0});
  138. if(($packet = $this->getPacketFromPool($pid)) !== null){
  139. $packet->buffer = $buffer;
  140. $this->getSession($source, $port)->handlePacket($packet);
  141. return true;
  142. }elseif($pid === UNCONNECTED_PING::$ID){
  143. //No need to create a session for just pings
  144. $packet = new UNCONNECTED_PING;
  145. $packet->buffer = $buffer;
  146. $packet->decode();
  147. $pk = new UNCONNECTED_PONG();
  148. $pk->serverID = $this->getID();
  149. $pk->pingID = $packet->pingID;
  150. $pk->serverName = $this->getName();
  151. $this->sendPacket($pk, $source, $port);
  152. }elseif($buffer !== ""){
  153. $this->streamRaw($source, $port, $buffer);
  154. return true;
  155. }else{
  156. return false;
  157. }
  158. }
  159. return false;
  160. }
  161. public function sendPacket(Packet $packet, $dest, $port){
  162. $packet->encode();
  163. $this->sendBytes += $this->socket->writePacket($packet->buffer, $dest, $port);
  164. }
  165. public function streamEncapsulated(Session $session, EncapsulatedPacket $packet, $flags = RakLib::PRIORITY_NORMAL){
  166. $id = $session->getAddress() . ":" . $session->getPort();
  167. $buffer = chr(RakLib::PACKET_ENCAPSULATED) . chr(strlen($id)) . $id . chr($flags) . $packet->toBinary(true);
  168. $this->server->pushThreadToMainPacket($buffer);
  169. }
  170. public function streamRaw($address, $port, $payload){
  171. $buffer = chr(RakLib::PACKET_RAW) . chr(strlen($address)) . $address . Binary::writeShort($port) . $payload;
  172. $this->server->pushThreadToMainPacket($buffer);
  173. }
  174. protected function streamClose($identifier, $reason){
  175. $buffer = chr(RakLib::PACKET_CLOSE_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($reason)) . $reason;
  176. $this->server->pushThreadToMainPacket($buffer);
  177. }
  178. protected function streamInvalid($identifier){
  179. $buffer = chr(RakLib::PACKET_INVALID_SESSION) . chr(strlen($identifier)) . $identifier;
  180. $this->server->pushThreadToMainPacket($buffer);
  181. }
  182. protected function streamOpen(Session $session){
  183. $identifier = $session->getAddress() . ":" . $session->getPort();
  184. $buffer = chr(RakLib::PACKET_OPEN_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($session->getAddress())) . $session->getAddress() . Binary::writeShort($session->getPort()) . Binary::writeLong($session->getID());
  185. $this->server->pushThreadToMainPacket($buffer);
  186. }
  187. protected function streamACK($identifier, $identifierACK){
  188. $buffer = chr(RakLib::PACKET_ACK_NOTIFICATION) . chr(strlen($identifier)) . $identifier . Binary::writeInt($identifierACK);
  189. $this->server->pushThreadToMainPacket($buffer);
  190. }
  191. protected function streamOption($name, $value){
  192. $buffer = chr(RakLib::PACKET_SET_OPTION) . chr(strlen($name)) . $name . $value;
  193. $this->server->pushThreadToMainPacket($buffer);
  194. }
  195. private function checkSessions(){
  196. if(count($this->sessions) > 4096){
  197. foreach($this->sessions as $i => $s){
  198. if($s->isTemporal()){
  199. unset($this->sessions[$i]);
  200. if(count($this->sessions) <= 4096){
  201. break;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. public function receiveStream(){
  208. if(strlen($packet = $this->server->readMainToThreadPacket()) > 0){
  209. $id = ord($packet{0});
  210. $offset = 1;
  211. if($id === RakLib::PACKET_ENCAPSULATED){
  212. $len = ord($packet{$offset++});
  213. $identifier = substr($packet, $offset, $len);
  214. $offset += $len;
  215. if(isset($this->sessions[$identifier])){
  216. $flags = ord($packet{$offset++});
  217. $buffer = substr($packet, $offset);
  218. $this->sessions[$identifier]->addEncapsulatedToQueue(EncapsulatedPacket::fromBinary($buffer, true), $flags);
  219. }else{
  220. $this->streamInvalid($identifier);
  221. }
  222. }elseif($id === RakLib::PACKET_RAW){
  223. $len = ord($packet{$offset++});
  224. $address = substr($packet, $offset, $len);
  225. $offset += $len;
  226. $port = Binary::readShort(substr($packet, $offset, 2));
  227. $offset += 2;
  228. $payload = substr($packet, $offset);
  229. $this->socket->writePacket($payload, $address, $port);
  230. }elseif($id === RakLib::PACKET_CLOSE_SESSION){
  231. $len = ord($packet{$offset++});
  232. $identifier = substr($packet, $offset, $len);
  233. if(isset($this->sessions[$identifier])){
  234. $this->removeSession($this->sessions[$identifier]);
  235. }else{
  236. $this->streamInvalid($identifier);
  237. }
  238. }elseif($id === RakLib::PACKET_INVALID_SESSION){
  239. $len = ord($packet{$offset++});
  240. $identifier = substr($packet, $offset, $len);
  241. if(isset($this->sessions[$identifier])){
  242. $this->removeSession($this->sessions[$identifier]);
  243. }
  244. }elseif($id === RakLib::PACKET_SET_OPTION){
  245. $len = ord($packet{$offset++});
  246. $name = substr($packet, $offset, $len);
  247. $offset += $len;
  248. $value = substr($packet, $offset);
  249. switch($name){
  250. case "name":
  251. $this->name = $value;
  252. break;
  253. case "portChecking":
  254. $this->portChecking = (bool) $value;
  255. break;
  256. case "packetLimit":
  257. $this->packetLimit = (int) $value;
  258. break;
  259. }
  260. }elseif($id === RakLib::PACKET_BLOCK_ADDRESS){
  261. $len = ord($packet{$offset++});
  262. $address = substr($packet, $offset, $len);
  263. $offset += $len;
  264. $timeout = Binary::readInt(substr($packet, $offset, 4));
  265. $this->blockAddress($address, $timeout);
  266. }elseif($id === RakLib::PACKET_SHUTDOWN){
  267. foreach($this->sessions as $session){
  268. $this->removeSession($session);
  269. }
  270. $this->socket->close();
  271. $this->shutdown = true;
  272. }elseif($id === RakLib::PACKET_EMERGENCY_SHUTDOWN){
  273. $this->shutdown = true;
  274. }else{
  275. return false;
  276. }
  277. return true;
  278. }
  279. return false;
  280. }
  281. public function blockAddress($address, $timeout = 300){
  282. $final = microtime(true) + $timeout;
  283. if(!isset($this->block[$address]) or $timeout === -1){
  284. if($timeout === -1){
  285. $final = PHP_INT_MAX;
  286. }else{
  287. $this->getLogger()->notice("Blocked $address for $timeout seconds");
  288. }
  289. $this->block[$address] = $final;
  290. }elseif($this->block[$address] < $final){
  291. $this->block[$address] = $final;
  292. }
  293. }
  294. /**
  295. * @param string $ip
  296. * @param int $port
  297. *
  298. * @return Session
  299. */
  300. public function getSession($ip, $port){
  301. $id = $ip . ":" . $port;
  302. if(!isset($this->sessions[$id])){
  303. $this->checkSessions();
  304. $this->sessions[$id] = new Session($this, $ip, $port);
  305. }
  306. return $this->sessions[$id];
  307. }
  308. public function removeSession(Session $session, $reason = "unknown"){
  309. $id = $session->getAddress() . ":" . $session->getPort();
  310. if(isset($this->sessions[$id])){
  311. $this->sessions[$id]->close();
  312. unset($this->sessions[$id]);
  313. $this->streamClose($id, $reason);
  314. }
  315. }
  316. public function openSession(Session $session){
  317. $this->streamOpen($session);
  318. }
  319. public function notifyACK(Session $session, $identifierACK){
  320. $this->streamACK($session->getAddress() . ":" . $session->getPort(), $identifierACK);
  321. }
  322. public function getName() : string{
  323. return $this->name;
  324. }
  325. public function getID(){
  326. return $this->serverId;
  327. }
  328. private function registerPacket($id, $class){
  329. $this->packetPool[$id] = new $class;
  330. }
  331. /**
  332. * @param $id
  333. *
  334. * @return Packet
  335. */
  336. public function getPacketFromPool($id){
  337. if(isset($this->packetPool[$id])){
  338. return clone $this->packetPool[$id];
  339. }
  340. return null;
  341. }
  342. private function registerPackets(){
  343. //$this->registerPacket(UNCONNECTED_PING::$ID, UNCONNECTED_PING::class);
  344. $this->registerPacket(UNCONNECTED_PING_OPEN_CONNECTIONS::$ID, UNCONNECTED_PING_OPEN_CONNECTIONS::class);
  345. $this->registerPacket(OPEN_CONNECTION_REQUEST_1::$ID, OPEN_CONNECTION_REQUEST_1::class);
  346. $this->registerPacket(OPEN_CONNECTION_REPLY_1::$ID, OPEN_CONNECTION_REPLY_1::class);
  347. $this->registerPacket(OPEN_CONNECTION_REQUEST_2::$ID, OPEN_CONNECTION_REQUEST_2::class);
  348. $this->registerPacket(OPEN_CONNECTION_REPLY_2::$ID, OPEN_CONNECTION_REPLY_2::class);
  349. $this->registerPacket(UNCONNECTED_PONG::$ID, UNCONNECTED_PONG::class);
  350. $this->registerPacket(ADVERTISE_SYSTEM::$ID, ADVERTISE_SYSTEM::class);
  351. $this->registerPacket(DATA_PACKET_0::$ID, DATA_PACKET_0::class);
  352. $this->registerPacket(DATA_PACKET_1::$ID, DATA_PACKET_1::class);
  353. $this->registerPacket(DATA_PACKET_2::$ID, DATA_PACKET_2::class);
  354. $this->registerPacket(DATA_PACKET_3::$ID, DATA_PACKET_3::class);
  355. $this->registerPacket(DATA_PACKET_4::$ID, DATA_PACKET_4::class);
  356. $this->registerPacket(DATA_PACKET_5::$ID, DATA_PACKET_5::class);
  357. $this->registerPacket(DATA_PACKET_6::$ID, DATA_PACKET_6::class);
  358. $this->registerPacket(DATA_PACKET_7::$ID, DATA_PACKET_7::class);
  359. $this->registerPacket(DATA_PACKET_8::$ID, DATA_PACKET_8::class);
  360. $this->registerPacket(DATA_PACKET_9::$ID, DATA_PACKET_9::class);
  361. $this->registerPacket(DATA_PACKET_A::$ID, DATA_PACKET_A::class);
  362. $this->registerPacket(DATA_PACKET_B::$ID, DATA_PACKET_B::class);
  363. $this->registerPacket(DATA_PACKET_C::$ID, DATA_PACKET_C::class);
  364. $this->registerPacket(DATA_PACKET_D::$ID, DATA_PACKET_D::class);
  365. $this->registerPacket(DATA_PACKET_E::$ID, DATA_PACKET_E::class);
  366. $this->registerPacket(DATA_PACKET_F::$ID, DATA_PACKET_F::class);
  367. $this->registerPacket(NACK::$ID, NACK::class);
  368. $this->registerPacket(ACK::$ID, ACK::class);
  369. }
  370. }