Skip to content

Instantly share code, notes, and snippets.

@rianorie
Created August 25, 2019 15:33
Show Gist options
  • Save rianorie/9b50eb5bb32a2dc4fa5dbb237feb68b4 to your computer and use it in GitHub Desktop.
Save rianorie/9b50eb5bb32a2dc4fa5dbb237feb68b4 to your computer and use it in GitHub Desktop.
<?php
namespace Game;
use \Game\Connection\Interfaces\Connection;
use Game\Module\Interfaces\Command;
use InvalidArgumentException;
/**
* Game input handler
*
* @package Game
* @author Rian Orie <rian.orie@gmail.com>
* @version 1.0
* @created 2015-07-23
*/
class Input
{
const PRIORITY_LOW = 1;
const PRIORITY_NORMAL = 2;
const PRIORITY_HIGH = 3;
/**
* List of the registered commands
*
* @var array
*/
private $commands;
/**
* @var Event
*/
private $event;
/**
* Construct the input class
*
* @param Event $event The event handler
*/
public function __construct(Event $event)
{
$this->event = $event;
}
/**
* Register a command in the system
*
* @param string $command The command to register
* @param Command $callback Callable handler that works with the given input
* @param boolean $priority Give the command priority, pushing it to the front of the command list
*
* @throws Exception
*/
public function registerCommand(string $command, Command $callback, $priority = self::PRIORITY_NORMAL)
{
if ( ! preg_match('/^[a-z]+$/', $command)) {
throw new Exception('Command ' . $command . ' contains invalid characters! Only use a-z');
}
// make sure we're not trying to register an already existing command
if (in_array($command, $this->getCommands())) {
throw new Exception('Command ' . $command . ' is already registered!');
}
// call some events
$this->event->dispatch('input.before_register', ['command' => &$command, 'callback' => &$callback]);
$this->event->dispatch('input.before_register.' . $command, ['command' => &$command, 'callback' => &$callback]);
if ($callback instanceof Command) {
$callback = [$callback, 'execute'];
}
// and register the command
$this->commands[$priority][$command] = $callback;
ksort($this->commands[$priority]);
// and some events again
$this->event->dispatch('input.after_register', ['command' => $command, 'callback' => $callback]);
$this->event->dispatch('input.after_register.' . $command, ['command' => $command, 'callback' => $callback]);
}
/**
* Register commands by an associative array
*
* @param array $commandList
* @param bool $priority
*
* @throws Exception
*/
public function registerCommands(array $commandList, $priority = self::PRIORITY_NORMAL)
{
foreach($commandList as $command => $callback) {
if ( ! $callback instanceof Command) {
throw new InvalidArgumentException('Callback should implement the Command interface');
}
$this->registerCommand($command, $callback, $priority);
}
}
/**
* Process the input from a connection
*
* @param Connection $connection The connection that added the input
* @param string $input The actual input
*/
public function handle(Connection $connection, $input)
{
// clean off anything that shouldn't be at the start of the line and then separate the input
$arguments = explode(' ', rtrim($input));
$this->event->dispatch('input.before_process', ['connection' => $connection, 'input' => &$input, 'arguments' => &$arguments]);
if (isset($arguments[0])) {
// try and find the command
$command = $this->findCommand(ltrim($arguments[0]));
// if we can't find it, simply stop
if ( ! $command) {
if (trim($arguments[0]) == '') {
$this->event->dispatch('input.empty', ['connection' => $connection]);
return;
}
$this->event->dispatch('input.not_found', ['connection' => $connection, 'input' => $input]);
return;
}
// but if we did find the command, let the command handle the input
// clean the input. ltrim potential whitespaces, then ltrim the first argument then trim potential whitespaces again
$cleaned = trim(ltrim(ltrim($input), $arguments[0]));
// fire event handlers
$this->event->dispatch('input.before_command', ['connection' => $connection, 'command' => $command, 'input' => $input, 'cleaned' => $cleaned]);
$this->event->dispatch('input.before_command.' . $command, ['connection' => $connection, 'command' => $command, 'input' => $input, 'cleaned' => $cleaned]);
// execute the command
if (isset($this->commands[self::PRIORITY_HIGH][$command])) {
call_user_func_array($this->commands[self::PRIORITY_HIGH][$command], [$connection, $cleaned, $command, $input]);
} elseif (isset($this->commands[self::PRIORITY_NORMAL][$command])) {
call_user_func_array($this->commands[self::PRIORITY_NORMAL][$command], [$connection, $cleaned, $command, $input]);
} else {
call_user_func_array($this->commands[self::PRIORITY_LOW][$command], [$connection, $cleaned, $command, $input]);
}
// fire event handlers
$this->event->dispatch('input.after_command', ['connection' => $connection, 'command' => $command, 'input' => $input, 'cleaned' => $cleaned]);
$this->event->dispatch('input.after_command.' . $command, ['connection' => $connection, 'command' => $command, 'input' => $input, 'cleaned' => $cleaned]);
}
}
/**
* Retrieve an array of the currently registered commands
*
* @return array
*/
public function getCommands()
{
return array_merge(
(isset($this->commands[self::PRIORITY_HIGH]) ? array_keys($this->commands[self::PRIORITY_HIGH]) : []),
(isset($this->commands[self::PRIORITY_NORMAL]) ? array_keys($this->commands[self::PRIORITY_NORMAL]) : []),
(isset($this->commands[self::PRIORITY_LOW]) ? array_keys($this->commands[self::PRIORITY_LOW]) : [])
);
}
/**
* Find the closest matching command in the array of commands
*
* @param string $input The given command
*
* @return string|bool
*/
protected function findCommand($input)
{
// sanitize the input
$input = strtolower(trim($input));
// if the command is empty
if (strlen($input) == 0) {
return false;
}
foreach ($this->getCommands() as $command) {
if (substr($command, 0, strlen($input)) == $input) {
return $command;
}
}
// if not, fail
return false;
}
}
<?php
namespace Modules\CommandNotFound\Observer;
use Game\Module\Observer;
use stdClass;
class NotFound extends Observer
{
public function fire(stdClass $data)
{
$output = [
'Wait, what? I have no idea what that means.',
'Soo.. that doesn\'t make any kind of sense.',
'Command not found.',
"I could not find that command.\nCheck your spelling and check the help files."
];
$this->getOutput()->write($data->connection, ["%s\n", $output[rand(0, 3)]]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment