1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace ManiaLive\Features\ChatCommand;
13:
14: use ManiaLive\Event\Dispatcher;
15: use ManiaLive\DedicatedApi\Callback\Listener as ServerListener;
16: use ManiaLive\DedicatedApi\Callback\Event as ServerEvent;
17: use DedicatedApi\Connection;
18: use ManiaLive\Utilities\Logger;
19:
20: final class Interpreter extends \ManiaLib\Utils\Singleton implements ServerListener
21: {
22: const NOT_REGISTERED_AT_ALL = 0;
23: const REGISTERED_DIFFERENTLY = 1;
24: const REGISTERED_AS_POLYMORPHIC = 2;
25: const REGISTERED_EXACTLY = 3;
26:
27: private $registeredCommands = array();
28:
29: 30: 31:
32: private $connection;
33:
34: protected function __construct()
35: {
36: Dispatcher::register(ServerEvent::getClass(), $this, ServerEvent::ON_PLAYER_CHAT);
37:
38: $command = new Command('help', 0);
39: $command->addLoginAsFirstParameter = true;
40: $command->log = false;
41: $command->help = 'Display all visible commands to a player it takes no parameter';
42: $command->callback = array($this, 'help');
43:
44: $this->register($command);
45:
46: $command = new Command('man', 1);
47: $command->addLoginAsFirstParameter = true;
48: $command->help = 'Display help for every commands you give as parameter'."\n".'exemple of usage: /man man';
49: $command->isPublic = true;
50: $command->log = false;
51: $command->callback = array($this, 'man');
52:
53: $this->register($command);
54:
55: $command = new Command('man', 2);
56: $command->addLoginAsFirstParameter = true;
57: $command->help = 'Display help for the command with the corresponding parameters'."\n".'exemple of usage: /man man 2';
58: $command->isPublic = true;
59: $command->log = false;
60: $command->callback = array($this, 'man');
61:
62: $this->register($command);
63:
64: $config = \ManiaLive\DedicatedApi\Config::getInstance();
65: $this->connection = Connection::factory($config->host, $config->port, $config->timeout, $config->user, $config->password);
66: }
67:
68: 69: 70: 71: 72:
73: function register(Command $command)
74: {
75: list($requiredCount, $totalCount) = $this->getCommandParametersCount($command);
76: $commandName = strtolower($command->name);
77: $count = $requiredCount;
78: try
79: {
80: if($command->parametersCount == -1 && isset($this->registeredCommands[$commandName]))
81: throw new CommandAlreadyRegisteredException($command->name.'|'.$command->parametersCount);
82: do
83: {
84: if($this->isRegistered($commandName, $count) > self::REGISTERED_DIFFERENTLY)
85: throw new CommandAlreadyRegisteredException($command->name.'|'.$command->parametersCount);
86: $this->registeredCommands[$commandName][$count] = $command;
87: }
88: while($count++ < $totalCount);
89: }
90: catch(\Exception $e)
91: {
92: while(--$count >= $requiredCount)
93: unset($this->registeredCommands[$commandName][$count]);
94: throw $e;
95: }
96: }
97:
98: 99: 100: 101: 102: 103:
104: function isRegistered($commandName, $parametersCount = -2)
105: {
106: $commandName = strtolower($commandName);
107: if(isset($this->registeredCommands[$commandName]))
108: {
109: if(isset($this->registeredCommands[$commandName][$parametersCount]))
110: return self::REGISTERED_EXACTLY;
111: else if(isset($this->registeredCommands[$commandName][-1]))
112: return self::REGISTERED_AS_POLYMORPHIC;
113: else
114: return self::REGISTERED_DIFFERENTLY;
115: }
116: return self::NOT_REGISTERED_AT_ALL;
117: }
118:
119: 120: 121: 122:
123: function getRegisteredCommands()
124: {
125: $commands = array();
126: foreach($this->registeredCommands as $commandName => $value)
127: {
128: $commands[$commandName] = array();
129: foreach($value as $parametersCount => $command)
130: $commands[$commandName][$parametersCount] = clone $command;
131: }
132: return $commands;
133: }
134:
135: 136: 137: 138: 139:
140: function unregister(Command $command)
141: {
142: list($requiredCount, $totalCount) = $this->getCommandParametersCount($command);
143: $commandName = strtolower($command->name);
144: $count = $requiredCount;
145: do
146: {
147: if($this->isRegistered($commandName, $count) == self::REGISTERED_EXACTLY
148: && $this->registeredCommands[$commandName][$count] === $command)
149: unset($this->registeredCommands[$commandName][$count]);
150: }
151: while($count++ < $totalCount);
152: if(!$this->registeredCommands[$commandName])
153: unset($this->registeredCommands[$commandName]);
154: }
155:
156: function onPlayerChat($playerUid, $login, $text, $isRegistredCmd)
157: {
158: if(!$isRegistredCmd)
159: return;
160:
161: $cmdAndArgs = explode(' ', $text, 2);
162: $command = substr($cmdAndArgs[0], 1);
163: $parameters = count($cmdAndArgs) > 1 ? $cmdAndArgs[1] : '';
164:
165: if($command === 'version')
166: return;
167:
168: $isRegistered = $this->isRegistered($command);
169: if($isRegistered == self::REGISTERED_AS_POLYMORPHIC)
170: $this->callCommand($login, $text, $command, $parameters ? array($parameters) : array(), true);
171: else if($isRegistered == self::NOT_REGISTERED_AT_ALL)
172: $this->connection->chatSendServerMessage(
173: 'Command $<$o$FC4'.$command.'$> does not exist, try /help to see a list of the available commands.',
174: $login, true);
175: else
176: {
177: if(strlen($parameters))
178: {
179: $matches = array();
180: preg_match_all('/(?!\\\\)"((?:\\\\"|[^"])+)"?|([^\s]+)/', $parameters, $matches);
181: $parameters = array_map(
182: function($str, $word) { return str_replace('\\"', '"', $str != '' ? $str : $word); },
183: $matches[1], $matches[2]);
184: }
185: else
186: $parameters = array();
187:
188: $isRegistered = $this->isRegistered($command, count($parameters));
189: if($isRegistered == self::REGISTERED_EXACTLY)
190: $this->callCommand($login, $text, $command, $parameters);
191: else
192: {
193: $this->connection->chatSendServerMessage(
194: 'The command you entered exists but has not the correct number of parameters, use $<$o$FC4/man '.$command.'$> for more details',
195: $login, true);
196: $this->man($login, $command);
197: }
198: }
199: }
200:
201: private function callCommand($login, $text, $commandName, $parameters = array(), $polymorphicCommand = false)
202: {
203: $command = $this->registeredCommands[strtolower($commandName)][$polymorphicCommand ? -1 : count($parameters)];
204:
205: if(!count($command->authorizedLogin) || in_array($login, $command->authorizedLogin))
206: {
207: if($command->log)
208: Logger::getLog('commands')->write('[ChatCommand from '.$login.'] '.$text);
209:
210: if($command->addLoginAsFirstParameter)
211: array_unshift($parameters, $login);
212:
213: call_user_func_array($command->callback, $parameters);
214: }
215: else
216: $this->connection->chatSendServerMessage(
217: '$f00You are not authorized to use this command!', $login, true);
218: }
219:
220: function help($login)
221: {
222: $availableCommands = array();
223: foreach($this->registeredCommands as $commands)
224: foreach($commands as $command)
225: if($command->isPublic && (!count($command->authorizedLogin) || in_array($login, $command->authorizedLogin)))
226: {
227: $availableCommands[] = $command->name;
228: continue 2;
229: }
230:
231: if(count($availableCommands))
232: $this->connection->chatSendServerMessage(
233: 'Available commands are: '.implode(', ', $availableCommands), $login, true);
234: else
235: $this->connection->chatSendServerMessage(
236: 'There is no command available', $login, true);
237: }
238:
239: function man($login, $commandName, $parametersCount = -2)
240: {
241: $commandName = strtolower($commandName);
242: if($parametersCount == -2 && isset($this->registeredCommands[$commandName]))
243: {
244: $help = array();
245: $help[] = 'Available $<$o$FC4'.$commandName.'$> commands:';
246: foreach($this->registeredCommands[$commandName] as $command)
247: {
248: if(!count($command->authorizedLogin) || in_array($login, $command->authorizedLogin))
249: {
250: $help[] = '$<$o$FC4'.$command->name.' ('.$command->parametersCount.')$>'.
251: ($command->help ? ':'.$command->help : '');
252: }
253: }
254: $text = implode("\n", $help);
255: }
256: else if(isset($this->registeredCommands[$commandName][$parametersCount]))
257: {
258: $command = $this->registeredCommands[$commandName][$parametersCount];
259: if(!count($command->authorizedLogin) || in_array($login, $command->authorizedLogin))
260: $text = '$<$o$FC4'.$command->name.' ('.$command->parametersCount.')$>'.
261: ($command->help ? ':'.$command->help : '');
262: else
263: $text = 'This command does not exists use help to see available commands';
264: }
265: else
266: $text = 'This command does not exists use help to see available commands';
267:
268: $this->connection->chatSendServerMessage($text, $login, true);
269: }
270:
271: private function getCommandParametersCount(Command $command)
272: {
273: if($command->parametersCount !== null)
274: return array($command->parametersCount, $command->parametersCount);
275: else
276: {
277: if(is_array($command->callback))
278: list($class, $method) = $command->callback;
279: else
280: list($class, $method) = explode('::', $command->callback);
281:
282: $reflection = new \ReflectionMethod($class, $method);
283:
284: $requiredCount = $reflection->getNumberOfRequiredParameters();
285: $totalCount = $reflection->getNumberOfParameters();
286:
287: if($command->addLoginAsFirstParameter)
288: return array($requiredCount-1, $totalCount-1);
289: return array($requiredCount, $totalCount);
290: }
291: }
292:
293: function onPlayerConnect($login, $isSpectator) {}
294: function onPlayerDisconnect($login) {}
295: function onPlayerManialinkPageAnswer($playerUid, $login, $answer, array $entries) {}
296: function onEcho($internal, $public) {}
297: function onServerStart() {}
298: function onServerStop() {}
299: function onBeginMatch() {}
300: function onEndMatch($rankings, $winnerTeamOrMap) {}
301: function onBeginMap($map, $warmUp, $matchContinuation) {}
302: function onEndMap($rankings, $map, $wasWarmUp, $matchContinuesOnNextMap, $restartMap) {}
303: function onBeginRound() {}
304: function onEndRound() {}
305: function onStatusChanged($statusCode, $statusName) {}
306: function onPlayerCheckpoint($playerUid, $login, $timeOrScore, $curLap, $checkpointIndex) {}
307: function onPlayerFinish($playerUid, $login, $timeOrScore) {}
308: function onPlayerIncoherence($playerUid, $login) {}
309: function onBillUpdated($billId, $state, $stateName, $transactionId) {}
310: function onTunnelDataReceived($playerUid, $login, $data) {}
311: function onMapListModified($curMapIndex, $nextMapIndex, $isListModified) {}
312: function onPlayerInfoChanged($playerInfo) {}
313: function onManualFlowControlTransition($transition) {}
314: function onVoteUpdated($stateName, $login, $cmdName, $cmdParam) {}
315: function onModeScriptCallback($param1, $param2) {}
316: }
317:
318: class CommandAlreadyRegisteredException extends \Exception {}
319:
320: ?>
321: