improvements of sockethandler, possibility for maniacontrol to maniacontrol connections
This commit is contained in:
		@@ -2,7 +2,8 @@
 | 
			
		||||
 | 
			
		||||
#Additions
 | 
			
		||||
- added changelog
 | 
			
		||||
- added SocketManager which acts like a communication interface you can connect to and interact with ManiaControl (also thanks to TGYoshi for some help)
 | 
			
		||||
- added CommunicationManager which acts like a communication interface you can connect to and interact with ManiaControl (also thanks to TGYoshi for some help)
 | 
			
		||||
	- You can call ManiaControl from a Website or from ManiaControl itself
 | 
			
		||||
- added "//removerights login" command
 | 
			
		||||
- added new EchoManager which handles Interactions between different Controllers
 | 
			
		||||
	- It is possible to send an Echo command via the Method sendEcho, as message Parameter strings, objects or arrays can get used
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ class Chat implements CallbackListener, CommunicationListener {
 | 
			
		||||
		$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERCHAT, $this, 'onPlayerChat');
 | 
			
		||||
 | 
			
		||||
		//Socket Listenings
 | 
			
		||||
		$this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_SERVER_CHAT, $this, function ($data) {
 | 
			
		||||
		$this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_SERVER_CHAT, $this, function ($error, $data) {
 | 
			
		||||
			return $this->chatBuffer;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										103
									
								
								core/Communication/Communication.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								core/Communication/Communication.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace ManiaControl\Communication;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class for Communicating with other ManiaControls
 | 
			
		||||
 * to call @see ManiaControl\Communication\CommunicationManager\createCommunication
 | 
			
		||||
 *
 | 
			
		||||
 * @author    ManiaControl Team <mail@maniacontrol.com>
 | 
			
		||||
 * @copyright 2014-2015 ManiaControl Team
 | 
			
		||||
 * @license   http://www.gnu.org/licenses/ GNU General Public License, Version 3
 | 
			
		||||
 */
 | 
			
		||||
class Communication {
 | 
			
		||||
	private $socket;
 | 
			
		||||
	private $ip;
 | 
			
		||||
	private $port;
 | 
			
		||||
	private $encryptionPassword;
 | 
			
		||||
 | 
			
		||||
	private $buffer       = "";
 | 
			
		||||
	private $messageQueue = array();
 | 
			
		||||
 | 
			
		||||
	public function __construct($ip, $port, $encryptionPassword) {
 | 
			
		||||
		$this->ip                 = $ip;
 | 
			
		||||
		$this->port               = $port;
 | 
			
		||||
		$this->encryptionPassword = $encryptionPassword;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Create an Connection */
 | 
			
		||||
	public function createConnection() {
 | 
			
		||||
		$errno        = null;
 | 
			
		||||
		$errstr       = null;
 | 
			
		||||
		$this->socket = @fsockopen($this->ip, $this->port, $errno, $errstr, 2);
 | 
			
		||||
 | 
			
		||||
		//socket_set_nonblock($this->socket);
 | 
			
		||||
		stream_set_blocking($this->socket, 0);
 | 
			
		||||
 | 
			
		||||
		if ($errno != 0 || !$this->socket) {
 | 
			
		||||
			var_dump($errstr);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Call an Method Asynchronously
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param callable $function
 | 
			
		||||
	 * @param          $method
 | 
			
		||||
	 * @param string   $data
 | 
			
		||||
	 * @return null
 | 
			
		||||
	 */
 | 
			
		||||
	public function call(callable $function, $method, $data = "") {
 | 
			
		||||
		//TODO throw an exception or smth
 | 
			
		||||
		if (!$this->socket) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$data = json_encode(array("method" => $method, "data" => $data));
 | 
			
		||||
 | 
			
		||||
		$data = openssl_encrypt($data, CommunicationManager::ENCRYPTION_METHOD, $this->encryptionPassword, OPENSSL_RAW_DATA, CommunicationManager::ENCRYPTION_IV);
 | 
			
		||||
 | 
			
		||||
		array_push($this->messageQueue, $function);
 | 
			
		||||
 | 
			
		||||
		// Write Request on Socket
 | 
			
		||||
		fwrite($this->socket, strlen($data) . "\n" . $data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Process data on every Tick
 | 
			
		||||
	 */
 | 
			
		||||
	public function tick() {
 | 
			
		||||
		$data = fgets($this->socket, 1024); // reads as much as possible OR nothing at all
 | 
			
		||||
		if (strlen($data) > 0) { // got new data
 | 
			
		||||
			$this->buffer .= $data; // append new data to buffer
 | 
			
		||||
			// handle the data the exact same way
 | 
			
		||||
			$arr = explode("\n", $this->buffer, 2);
 | 
			
		||||
			while (count($arr) == 2 && strlen($arr[1]) >= (int) $arr[0]) {
 | 
			
		||||
				// received full message
 | 
			
		||||
				$len          = (int) $arr[0];
 | 
			
		||||
				$msg          = substr($arr[1], 0, $len); // clip msg
 | 
			
		||||
				$this->buffer = substr($this->buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer
 | 
			
		||||
 | 
			
		||||
				// Decode Message
 | 
			
		||||
				$data = openssl_decrypt($msg, CommunicationManager::ENCRYPTION_METHOD, $this->encryptionPassword, OPENSSL_RAW_DATA, CommunicationManager::ENCRYPTION_IV);
 | 
			
		||||
				$data = json_decode($data);
 | 
			
		||||
 | 
			
		||||
				// Received something!
 | 
			
		||||
				//Call Function with Data
 | 
			
		||||
				call_user_func(array_shift($this->messageQueue), $data->error, $data->data);
 | 
			
		||||
 | 
			
		||||
				// next msg
 | 
			
		||||
				$arr = explode("\n", $this->buffer, 2);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Closes the connection, don't call yourself, let it do the Communication Manager */
 | 
			
		||||
	public function closeConnection() {
 | 
			
		||||
		if ($this->socket) {
 | 
			
		||||
			fclose($this->socket);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,13 @@ use React\Socket\Server;
 | 
			
		||||
 * @license   http://www.gnu.org/licenses/ GNU General Public License, Version 3
 | 
			
		||||
 */
 | 
			
		||||
class CommunicationManager implements CallbackListener {
 | 
			
		||||
	/** Constants */
 | 
			
		||||
	const SETTING_SOCKET_ENABLED  = "Activate Socket";
 | 
			
		||||
	const SETTING_SOCKET_PASSWORD = "Password for the Socket Connection";
 | 
			
		||||
	const SETTING_SOCKET_PORT     = "Socket Port for Server ";
 | 
			
		||||
 | 
			
		||||
	const ENCRYPTION_IV     = "kZ2Kt0CzKUjN2MJX";
 | 
			
		||||
	const ENCRYPTION_METHOD = "aes-192-cbc";
 | 
			
		||||
 | 
			
		||||
	/** @var ManiaControl $maniaControl */
 | 
			
		||||
	private $maniaControl = null;
 | 
			
		||||
@@ -34,10 +41,8 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
 | 
			
		||||
	/** @var Server $socket */
 | 
			
		||||
	private $socket = null;
 | 
			
		||||
 | 
			
		||||
	const SETTING_SOCKET_ENABLED  = "Activate Socket";
 | 
			
		||||
	const SETTING_SOCKET_PASSWORD = "Password for the Socket Connection";
 | 
			
		||||
	const SETTING_SOCKET_PORT     = "Socket Port for Server ";
 | 
			
		||||
	/** @var Communication[] $communcations */
 | 
			
		||||
	private $communications = array();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a new Communication Handler Instance
 | 
			
		||||
@@ -49,8 +54,48 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
 | 
			
		||||
		$this->maniaControl->getCallbackManager()->registerCallbackListener(SettingManager::CB_SETTING_CHANGED, $this, 'updateSettings');
 | 
			
		||||
		$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'initCommunicationManager');
 | 
			
		||||
		$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONSHUTDOWN, $this, 'onShutDown');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a Communication to another ManiaControl
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param $ip
 | 
			
		||||
	 * @param $port
 | 
			
		||||
	 * @return \ManiaControl\Communication\Communication
 | 
			
		||||
	 */
 | 
			
		||||
	public function createCommunication($ip, $port, $encryptionKey) {
 | 
			
		||||
		$communication = new Communication($ip, $port, $encryptionKey);
 | 
			
		||||
		$communication->createConnection();
 | 
			
		||||
 | 
			
		||||
		$this->communications[] = $communication;
 | 
			
		||||
		return $communication;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Closes a opened Communication
 | 
			
		||||
	 * Does not necessarily need be called, all connections get destroyed on ManiaControl Shutdown
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param Communication $communication
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 */
 | 
			
		||||
	public function closeCommunication($communication) {
 | 
			
		||||
		$key = array_search($communication, $this->communications);
 | 
			
		||||
		if (isset($this->communications[$key])) {
 | 
			
		||||
			$this->communications[$key]->closeConnection();
 | 
			
		||||
			unset($this->communications[$key]);
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Close all Sockets on maniaControl Shutdown */
 | 
			
		||||
	public function onShutDown() {
 | 
			
		||||
		foreach ($this->communications as $communication) {
 | 
			
		||||
			$this->closeCommunication($communication);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Register a new Communication Listener
 | 
			
		||||
@@ -138,7 +183,7 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		$this->createSocket();
 | 
			
		||||
		$this->createListeningSocket();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -154,7 +199,7 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
		$socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED);
 | 
			
		||||
 | 
			
		||||
		if ($socketEnabled && !$this->socket) {
 | 
			
		||||
			$this->createSocket();
 | 
			
		||||
			$this->createListeningSocket();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!$socketEnabled) {
 | 
			
		||||
@@ -165,7 +210,7 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates The Socket
 | 
			
		||||
	 */
 | 
			
		||||
	private function createSocket() {
 | 
			
		||||
	private function createListeningSocket() {
 | 
			
		||||
		$socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED);
 | 
			
		||||
		if ($socketEnabled) {
 | 
			
		||||
 | 
			
		||||
@@ -183,7 +228,7 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
 | 
			
		||||
			$serverLogin = $this->maniaControl->getServer()->login;
 | 
			
		||||
			$socketPort  = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_PORT . $serverLogin);
 | 
			
		||||
			$password    = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_PASSWORD . $serverLogin);
 | 
			
		||||
			$password    = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_PASSWORD);
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				$this->loop   = Factory::create();
 | 
			
		||||
@@ -205,15 +250,16 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
							$buffer = substr($buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer
 | 
			
		||||
 | 
			
		||||
							// Decode Message
 | 
			
		||||
							$data = openssl_decrypt($msg, 'aes-192-cbc', $password, OPENSSL_RAW_DATA, 'kZ2Kt0CzKUjN2MJX');
 | 
			
		||||
							$data = openssl_decrypt($msg, self::ENCRYPTION_METHOD, $password, OPENSSL_RAW_DATA, self::ENCRYPTION_IV);
 | 
			
		||||
							$data = json_decode($data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							if ($data == null) {
 | 
			
		||||
								$data = array("error" => true, "data" => "Data is not provided as an valid AES-196-encrypted encrypted JSON");
 | 
			
		||||
							} else if (!property_exists($data, "method") || !property_exists($data, "data")) {
 | 
			
		||||
								$data = array("error" => true, "data" => "Invalid Message");
 | 
			
		||||
							} else {
 | 
			
		||||
								$answer = $this->triggerCommuncationCallback($data->method, $data->data);
 | 
			
		||||
								$answer = $this->triggerCommuncationCallback($data->method, $data->error, $data->data);
 | 
			
		||||
								//Prepare Response
 | 
			
		||||
								if (!$answer) {
 | 
			
		||||
									$data = array("error" => true, "data" => "No listener or response on the given Message");
 | 
			
		||||
@@ -224,7 +270,7 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
 | 
			
		||||
							//Encode, Encrypt and Send Response
 | 
			
		||||
							$data = json_encode($data);
 | 
			
		||||
							$data = openssl_encrypt($data, 'aes-192-cbc', $password, OPENSSL_RAW_DATA, 'kZ2Kt0CzKUjN2MJX');
 | 
			
		||||
							$data = openssl_encrypt($data, self::ENCRYPTION_METHOD, $password, OPENSSL_RAW_DATA, self::ENCRYPTION_IV);
 | 
			
		||||
							$connection->write(strlen($data) . "\n" . $data);
 | 
			
		||||
 | 
			
		||||
							// next msg
 | 
			
		||||
@@ -250,5 +296,9 @@ class CommunicationManager implements CallbackListener {
 | 
			
		||||
		if ($this->loop) {
 | 
			
		||||
			$this->loop->tick();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		foreach ($this->communications as $communication) {
 | 
			
		||||
			$communication->tick();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,13 @@
 | 
			
		||||
 | 
			
		||||
namespace ManiaControl\Communication;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Communication Methods Interface
 | 
			
		||||
 *
 | 
			
		||||
 * @author    ManiaControl Team <mail@maniacontrol.com>
 | 
			
		||||
 * @copyright 2014-2015 ManiaControl Team
 | 
			
		||||
 * @license   http://www.gnu.org/licenses/ GNU General Public License, Version 3
 | 
			
		||||
 */
 | 
			
		||||
interface CommunicationMethods {
 | 
			
		||||
	/** Returns the last 200 lines of the chat (inclusive player logins and nicknames) */
 | 
			
		||||
	const GET_SERVER_CHAT = "Chat.GetServerChat";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,21 @@
 | 
			
		||||
Ingame you can activate the Communication Manager and set Password and Port of it.
 | 
			
		||||
The CommuncationListening of the Communcation Manager can be enabled in the ingame Settings.
 | 
			
		||||
 | 
			
		||||
Sample Web Implementation
 | 
			
		||||
<?php
 | 
			
		||||
There the following settings are existing:
 | 
			
		||||
-- Enable Socket Listening (Let the CommunicationManager listen for incoming calls)
 | 
			
		||||
-- Passsword (Password which get used to encrypt and decrypt the messages for the openssl connection)
 | 
			
		||||
-- Listening port for every server (this is the port the CommunicationManager listens at)
 | 
			
		||||
 | 
			
		||||
Sample ManiaControl Implementation (for ManiaControl to ManiaControl connections)
 | 
			
		||||
 | 
			
		||||
##php code begin
 | 
			
		||||
	$communication = $this->maniaControl->getCommunicationManager()->createCommunication(IP/Domain, PORT, 'YOUR_PASSWORD');
 | 
			
		||||
	$communication->call(function($error, $data){
 | 
			
		||||
		var_dump($data);
 | 
			
		||||
	}, CommunicationMethods::GET_SERVER_CHAT);
 | 
			
		||||
##php code end
 | 
			
		||||
 | 
			
		||||
Sample Web Implementation (to call ManiaControl from a website)
 | 
			
		||||
##php code begin
 | 
			
		||||
	$errno = null;
 | 
			
		||||
	$errstr = null;
 | 
			
		||||
	$socket = fsockopen("xx.xxx.xx.xx", xxxxx, $errno, $errstr, 2);
 | 
			
		||||
@@ -31,4 +45,4 @@ Sample Web Implementation
 | 
			
		||||
 | 
			
		||||
	//Close the Socket
 | 
			
		||||
	fclose($socket);
 | 
			
		||||
?>
 | 
			
		||||
##php code end
 | 
			
		||||
		Reference in New Issue
	
	Block a user