PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/cod_daemon/lib/classes/Commands.php

https://code.google.com/p/php-blackops-rcon/
PHP | 710 lines | 428 code | 102 blank | 180 comment | 49 complexity | 170da169fa81810f18af58b1f7cad781 MD5 | raw file
  1. <?php defined('ROOT_PATH') or die('No direct script access.');
  2. /**
  3. * Commands system
  4. *
  5. * Copyright (c) 2011, EpicLegion
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  9. *
  10. * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation
  12. * and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  15. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  17. * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  18. * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  19. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  20. * POSSIBILITY OF SUCH DAMAGE.
  21. *
  22. * @author EpicLegion
  23. * @package cod_daemon
  24. * @subpackage game
  25. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  26. */
  27. final class Commands
  28. {
  29. // Consts
  30. const GLOBAL_SAY = 0;
  31. const TEAM_SAY = 1;
  32. const BOTH_SAY = 2;
  33. /**
  34. * @var array
  35. */
  36. protected static $aliases = array();
  37. /**
  38. * @var array
  39. */
  40. protected static $badwords = array();
  41. /**
  42. * @var array
  43. */
  44. protected static $commandCharacter = array();
  45. /**
  46. * @var array
  47. */
  48. protected static $commands = array();
  49. /**
  50. * @var array
  51. */
  52. protected static $custom = array();
  53. /**
  54. * @var array
  55. */
  56. protected static $disabled = array();
  57. /**
  58. * Register command
  59. *
  60. * @param string $name
  61. * @param string $callback
  62. * @param string $flag
  63. * @param int $type
  64. */
  65. public static function add($name, $callback, $flag = NULL, $type = 2)
  66. {
  67. // Add command
  68. self::$commands[$name] = array('name' => $name, 'callback' => $callback, 'type' => $type, 'flag' => $flag);
  69. }
  70. /**
  71. * Language filter
  72. *
  73. * @param Player $player
  74. * @param string $text
  75. */
  76. private static function checkBadwords(Player $player, $text)
  77. {
  78. // Ignore
  79. if ($player->hasFlag('immunity'))
  80. {
  81. return;
  82. }
  83. // ID
  84. $id = Server::get('id');
  85. // Join
  86. if (is_array($text)) $text = implode(' ', $text);
  87. // Badword?
  88. foreach (array_keys(self::$badwords[$id]) as $bad)
  89. {
  90. // Check
  91. if (stripos($text, $bad) !== FALSE)
  92. {
  93. Warning::add($player, 'language');
  94. }
  95. }
  96. }
  97. /**
  98. * Clear commands list (and optionally - config)
  99. *
  100. * @param bool $config
  101. */
  102. public static function clear($config = TRUE)
  103. {
  104. // Commands
  105. self::$commands = array();
  106. // Config
  107. if ($config)
  108. {
  109. self::$commandCharacter = array();
  110. self::$aliases = array();
  111. self::$disabled = array();
  112. }
  113. }
  114. /**
  115. * Init commands system
  116. */
  117. public static function init()
  118. {
  119. // Events
  120. Event::add('onChat' , array('Commands', 'onChat'));
  121. Event::add('onTeamChat' , array('Commands', 'onTeamChat'));
  122. Event::add('onConfigReload', array('Commands', 'reloadConfig'));
  123. }
  124. /**
  125. * [Event handler] On global chat
  126. *
  127. * @param Player $player
  128. * @param string $text
  129. */
  130. public static function onChat(Player $player, $text)
  131. {
  132. // Ignore democlient / world chat (that's impossible anyway)
  133. if ($player instanceof DemoclientPlayer OR $player instanceof WorldPlayer)
  134. {
  135. return;
  136. }
  137. // Server ID
  138. $id = Server::get('id');
  139. // Load config
  140. if (!isset(self::$commandCharacter[$id]))
  141. {
  142. // Character
  143. self::$commandCharacter[$id] = Config::get('commands.character', '!');
  144. // Aliases
  145. self::$aliases[$id] = array();
  146. foreach (Config::get('commands.aliases', array()) as $k => $v)
  147. {
  148. self::$aliases[$id][$k] = $v;
  149. }
  150. // Disabled
  151. self::$disabled[$id] = array();
  152. foreach (Config::get('commands.disabled', array()) as $v)
  153. {
  154. self::$disabled[$id][$v] = TRUE;
  155. }
  156. // Badwords
  157. self::$badwords[$id] = array();
  158. foreach (Config::get('commands.badwords', array()) as $v)
  159. {
  160. self::$badwords[$id][$v] = TRUE;
  161. }
  162. // Custom commands
  163. self::$custom[$id] = array();
  164. foreach (Config::get('commands.custom', array()) as $k => $v)
  165. {
  166. self::$custom[$id][$k] = $v;
  167. }
  168. }
  169. // Command
  170. if (substr($text, 0, 1) == self::$commandCharacter[$id])
  171. {
  172. // Tokenize
  173. $text = self::tokenize(substr($text, 1));
  174. // Invalid
  175. if (empty($text))
  176. {
  177. return;
  178. }
  179. // Lower
  180. $text[0] = strtolower($text[0]);
  181. // Alias
  182. if (isset(self::$aliases[$id][$text[0]]))
  183. {
  184. // Recreate token list
  185. $newText = array();
  186. $t = '';
  187. foreach (explode(' ', self::$aliases[$id][$text[0]]) as $t)
  188. {
  189. $newText[] = $t;
  190. }
  191. unset($text[0]);
  192. foreach ($text as $t)
  193. {
  194. $newText[] = $t;
  195. }
  196. // Assign new token list
  197. $text = $newText;
  198. // Clean up
  199. unset($newText, $t);
  200. }
  201. // Custom
  202. if (isset(self::$custom[$id][$text[0]]))
  203. {
  204. Server::get()->message(self::$custom[$id][$text[0]], $player, 'custom');
  205. return;
  206. }
  207. // Commands list
  208. if ($text[0] == 'commands')
  209. {
  210. // Create list
  211. $cmdList = '';
  212. foreach (self::$commands as $info)
  213. {
  214. // Check access
  215. if (!isset(self::$disabled[$id][$info['name']]) AND (!$info['flag'] OR $player->hasFlag($info['flag'])))
  216. {
  217. $cmdList .= '!'.$info['name'].', ';
  218. }
  219. }
  220. foreach (array_keys(self::$custom[$id]) as $v)
  221. {
  222. $cmdList .= '!'.$v.', ';
  223. }
  224. // Reload
  225. if ($player->hasFlag('root'))
  226. {
  227. $cmdList .= '!reload, ';
  228. }
  229. // Send message
  230. Server::get()->message(__('Available commands: :cmd', array(':cmd' => $cmdList.'!commands')), $player->id);
  231. // We're done here
  232. return;
  233. }
  234. // Reload
  235. if ($text[0] == 'reload')
  236. {
  237. // Permissions
  238. if (!$player->hasFlag('root'))
  239. {
  240. return;
  241. }
  242. // Reload config
  243. Config::reload('server-'.$id);
  244. // Done
  245. return;
  246. }
  247. // Invalid command
  248. if (!isset(self::$commands[$text[0]]))
  249. {
  250. self::checkBadwords($player, $text);
  251. VoteManager::onVote($player, array('vote', $text[0]), TRUE);
  252. return;
  253. }
  254. // Disabled
  255. if (isset(self::$disabled[$id][$text[0]]))
  256. {
  257. return;
  258. }
  259. // Correct chat type?
  260. if (self::$commands[$text[0]]['type'] == self::TEAM_SAY)
  261. {
  262. return;
  263. }
  264. // Call home
  265. call_user_func(self::$commands[$text[0]]['callback'], $player, $text);
  266. }
  267. else
  268. {
  269. self::checkBadwords($player, $text);
  270. }
  271. }
  272. /**
  273. * [Event handler] On team chat
  274. *
  275. * @param Player $player
  276. * @param string $text
  277. */
  278. public static function onTeamChat(Player $player, $text)
  279. {
  280. // Ignore democlient / world chat (that's impossible anyway)
  281. if ($player instanceof DemoclientPlayer OR $player instanceof WorldPlayer)
  282. {
  283. return;
  284. }
  285. // Server ID
  286. $id = Server::get('id');
  287. // Load config
  288. if (!isset(self::$commandCharacter[$id]))
  289. {
  290. // Character
  291. self::$commandCharacter[$id] = Config::get('commands.character', '!');
  292. // Aliases
  293. self::$aliases[$id] = array();
  294. foreach (Config::get('commands.aliases', array()) as $k => $v)
  295. {
  296. self::$aliases[$id][$k] = $v;
  297. }
  298. // Disabled
  299. self::$disabled[$id] = array();
  300. foreach (Config::get('commands.disabled', array()) as $v)
  301. {
  302. self::$disabled[$id][$v] = TRUE;
  303. }
  304. // Badwords
  305. self::$badwords[$id] = array();
  306. foreach (Config::get('commands.badwords', array()) as $v)
  307. {
  308. self::$badwords[$id][$v] = TRUE;
  309. }
  310. // Custom commands
  311. self::$custom[$id] = array();
  312. foreach (Config::get('commands.custom', array()) as $k => $v)
  313. {
  314. self::$custom[$id][$k] = $v;
  315. }
  316. }
  317. // Command
  318. if (substr($text, 0, 1) == self::$commandCharacter[$id])
  319. {
  320. // Tokenize
  321. $text = self::tokenize(substr($text, 1));
  322. // Invalid
  323. if (empty($text))
  324. {
  325. return;
  326. }
  327. // Lower
  328. $text[0] = strtolower($text[0]);
  329. // Alias
  330. if (isset(self::$aliases[$id][$text[0]]))
  331. {
  332. // Recreate token list
  333. $newText = array();
  334. $t = '';
  335. foreach (explode(' ', self::$aliases[$id][$text[0]]) as $t)
  336. {
  337. $newText[] = $t;
  338. }
  339. unset($text[0]);
  340. foreach ($text as $t)
  341. {
  342. $newText[] = $t;
  343. }
  344. // Assign new token list
  345. $text = $newText;
  346. // Clean up
  347. unset($newText, $t);
  348. }
  349. // Custom
  350. if (isset(self::$custom[$id][$text[0]]))
  351. {
  352. Server::get()->message(self::$custom[$id][$text[0]], $player, 'custom');
  353. return;
  354. }
  355. // Commands list
  356. if ($text[0] == 'commands')
  357. {
  358. // Create list
  359. $cmdList = '';
  360. foreach (self::$commands as $info)
  361. {
  362. // Check access
  363. if (!isset(self::$disabled[$id][$info['name']]) AND (!$info['flag'] OR $player->hasFlag($info['flag'])))
  364. {
  365. $cmdList .= '!'.$info['name'].', ';
  366. }
  367. }
  368. foreach (array_keys(self::$custom[$id]) as $v)
  369. {
  370. $cmdList .= '!'.$v.', ';
  371. }
  372. // Reload
  373. if ($player->hasFlag('root'))
  374. {
  375. $cmdList .= '!reload, ';
  376. }
  377. // Send message
  378. Server::get()->message(__('Available commands: :cmd', array(':cmd' => $cmdList.'!commands')), $player->id);
  379. // We're done here
  380. return;
  381. }
  382. // Reload
  383. if ($text[0] == 'reload')
  384. {
  385. // Permissions
  386. if (!$player->hasFlag('root'))
  387. {
  388. return;
  389. }
  390. // Reload config
  391. Config::reload('server-'.$id);
  392. // Done
  393. return;
  394. }
  395. // Invalid command
  396. if (!isset(self::$commands[$text[0]]))
  397. {
  398. self::checkBadwords($player, $text);
  399. VoteManager::onVote($player, array('vote', $text[0]), TRUE);
  400. return;
  401. }
  402. // Disabled
  403. if (isset(self::$disabled[$id][$text[0]]))
  404. {
  405. return;
  406. }
  407. // Correct chat type?
  408. if (self::$commands[$text[0]]['type'] == self::GLOBAL_SAY)
  409. {
  410. return;
  411. }
  412. // Call home
  413. call_user_func(self::$commands[$text[0]]['callback'], $player, $text);
  414. }
  415. else
  416. {
  417. self::checkBadwords($player, $text);
  418. }
  419. }
  420. /**
  421. * Reload config
  422. */
  423. public static function reloadConfig()
  424. {
  425. // ID
  426. $id = Server::get('id');
  427. // Character
  428. self::$commandCharacter[$id] = Config::get('commands.character', '!');
  429. // Aliases
  430. self::$aliases[$id] = array();
  431. foreach (Config::get('commands.aliases', array()) as $k => $v)
  432. {
  433. self::$aliases[$id][$k] = $v;
  434. }
  435. // Disabled
  436. self::$disabled[$id] = array();
  437. foreach (Config::get('commands.disabled', array()) as $v)
  438. {
  439. self::$disabled[$id][$v] = TRUE;
  440. }
  441. // Badwords
  442. self::$badwords[$id] = array();
  443. foreach (Config::get('commands.badwords', array()) as $v)
  444. {
  445. self::$badwords[$id][$v] = TRUE;
  446. }
  447. // Custom commands
  448. self::$custom[$id] = array();
  449. foreach (Config::get('commands.custom', array()) as $k => $v)
  450. {
  451. self::$custom[$id][$k] = $v;
  452. }
  453. }
  454. /**
  455. * Remove command
  456. *
  457. * @param string $name
  458. */
  459. public static function remove($name)
  460. {
  461. if (isset(self::$commands[$name]))
  462. {
  463. unset(self::$commands[$name]);
  464. }
  465. }
  466. /**
  467. * Tokenize
  468. *
  469. * @param string $string
  470. * @return array
  471. */
  472. public static function tokenize($string)
  473. {
  474. // Trim
  475. $string = trim($string);
  476. // Variables
  477. $tokens = array();
  478. $length = strlen($string);
  479. $token = '';
  480. $inString = FALSE;
  481. // Empty?
  482. if ($length < 1)
  483. {
  484. return array();
  485. }
  486. // Char by char
  487. for ($i = 0; $i < $length; $i++)
  488. {
  489. // String
  490. if ($inString)
  491. {
  492. // Add char
  493. $token .= $string[$i];
  494. // Close
  495. if ($string[$i] == "'")
  496. {
  497. // Finish
  498. self::tokenizeFinish($token, $tokens);
  499. // Cleanup
  500. $inString = FALSE;
  501. $token = '';
  502. }
  503. // Next
  504. continue;
  505. }
  506. elseif ($string[$i] == "'")
  507. {
  508. // Add quote
  509. $token .= "'";
  510. // Start string
  511. $inString = TRUE;
  512. // Next
  513. continue;
  514. }
  515. // Space?
  516. if ($string[$i] == ' ')
  517. {
  518. // End token
  519. if (!empty($token))
  520. {
  521. // Finish
  522. self::tokenizeFinish($token, $tokens);
  523. // Cleanup
  524. $token = '';
  525. }
  526. // Next
  527. continue;
  528. }
  529. // Add char
  530. $token .= $string[$i];
  531. }
  532. // One more token?
  533. if (!empty($token))
  534. {
  535. self::tokenizeFinish($token, $tokens);
  536. }
  537. // Return
  538. return $tokens;
  539. }
  540. /**
  541. * Validate token and add to array
  542. *
  543. * @param string $tok
  544. * @param array $tokens
  545. * @return mixed
  546. */
  547. private static function tokenizeFinish($tok, &$tokens)
  548. {
  549. // Trim
  550. $tok = trim($tok);
  551. // Literal
  552. if (isset($tok[0]) AND $tok[0] == "'")
  553. {
  554. // Length
  555. $length = strlen($tok);
  556. // At least two chars
  557. if ($length < 2)
  558. {
  559. return;
  560. }
  561. // Valid ending
  562. if ($tok[$length - 1] == "'")
  563. {
  564. // Empty string
  565. if ($length == 2)
  566. {
  567. $tokens[] = '';
  568. }
  569. else
  570. {
  571. $tokens[] = substr($tok, 1, ($length - 2));
  572. }
  573. }
  574. // Done
  575. return;
  576. }
  577. // Int
  578. if (ctype_digit($tok))
  579. {
  580. $tokens[] = (int) $tok;
  581. return;
  582. }
  583. // Float
  584. if (is_numeric($tok))
  585. {
  586. $tokens[] = (float) $tok;
  587. return;
  588. }
  589. // Symbol
  590. $tokens[] = $tok;
  591. }
  592. }