improvements of sockethandler, possibility for maniacontrol to maniacontrol connections

This commit is contained in:
kremsy 2015-06-22 22:52:26 +02:00
parent 687bb7be1b
commit 35de1b1b87
6 changed files with 192 additions and 18 deletions

View File

@ -2,7 +2,8 @@
#Additions #Additions
- added changelog - 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 "//removerights login" command
- added new EchoManager which handles Interactions between different Controllers - 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 - It is possible to send an Echo command via the Method sendEcho, as message Parameter strings, objects or arrays can get used

View File

@ -53,7 +53,7 @@ class Chat implements CallbackListener, CommunicationListener {
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERCHAT, $this, 'onPlayerChat'); $this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERCHAT, $this, 'onPlayerChat');
//Socket Listenings //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; return $this->chatBuffer;
}); });
} }

View 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);
}
}
}

View File

@ -22,6 +22,13 @@ use React\Socket\Server;
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/ */
class CommunicationManager implements CallbackListener { 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 */ /** @var ManiaControl $maniaControl */
private $maniaControl = null; private $maniaControl = null;
@ -34,10 +41,8 @@ class CommunicationManager implements CallbackListener {
/** @var Server $socket */ /** @var Server $socket */
private $socket = null; private $socket = null;
/** @var Communication[] $communcations */
const SETTING_SOCKET_ENABLED = "Activate Socket"; private $communications = array();
const SETTING_SOCKET_PASSWORD = "Password for the Socket Connection";
const SETTING_SOCKET_PORT = "Socket Port for Server ";
/** /**
* Create a new Communication Handler Instance * 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(SettingManager::CB_SETTING_CHANGED, $this, 'updateSettings');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'initCommunicationManager'); $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 * 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); $socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED);
if ($socketEnabled && !$this->socket) { if ($socketEnabled && !$this->socket) {
$this->createSocket(); $this->createListeningSocket();
} }
if (!$socketEnabled) { if (!$socketEnabled) {
@ -165,7 +210,7 @@ class CommunicationManager implements CallbackListener {
/** /**
* Creates The Socket * Creates The Socket
*/ */
private function createSocket() { private function createListeningSocket() {
$socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED); $socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED);
if ($socketEnabled) { if ($socketEnabled) {
@ -183,7 +228,7 @@ class CommunicationManager implements CallbackListener {
$serverLogin = $this->maniaControl->getServer()->login; $serverLogin = $this->maniaControl->getServer()->login;
$socketPort = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_PORT . $serverLogin); $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 { try {
$this->loop = Factory::create(); $this->loop = Factory::create();
@ -205,15 +250,16 @@ class CommunicationManager implements CallbackListener {
$buffer = substr($buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer $buffer = substr($buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer
// Decode Message // 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); $data = json_decode($data);
if ($data == null) { if ($data == null) {
$data = array("error" => true, "data" => "Data is not provided as an valid AES-196-encrypted encrypted JSON"); $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")) { } else if (!property_exists($data, "method") || !property_exists($data, "data")) {
$data = array("error" => true, "data" => "Invalid Message"); $data = array("error" => true, "data" => "Invalid Message");
} else { } else {
$answer = $this->triggerCommuncationCallback($data->method, $data->data); $answer = $this->triggerCommuncationCallback($data->method, $data->error, $data->data);
//Prepare Response //Prepare Response
if (!$answer) { if (!$answer) {
$data = array("error" => true, "data" => "No listener or response on the given Message"); $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 //Encode, Encrypt and Send Response
$data = json_encode($data); $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); $connection->write(strlen($data) . "\n" . $data);
// next msg // next msg
@ -250,5 +296,9 @@ class CommunicationManager implements CallbackListener {
if ($this->loop) { if ($this->loop) {
$this->loop->tick(); $this->loop->tick();
} }
foreach ($this->communications as $communication) {
$communication->tick();
}
} }
} }

View File

@ -2,7 +2,13 @@
namespace ManiaControl\Communication; 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 { interface CommunicationMethods {
/** Returns the last 200 lines of the chat (inclusive player logins and nicknames) */ /** Returns the last 200 lines of the chat (inclusive player logins and nicknames) */
const GET_SERVER_CHAT = "Chat.GetServerChat"; const GET_SERVER_CHAT = "Chat.GetServerChat";

View File

@ -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 There the following settings are existing:
<?php -- 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; $errno = null;
$errstr = null; $errstr = null;
$socket = fsockopen("xx.xxx.xx.xx", xxxxx, $errno, $errstr, 2); $socket = fsockopen("xx.xxx.xx.xx", xxxxx, $errno, $errstr, 2);
@ -31,4 +45,4 @@ Sample Web Implementation
//Close the Socket //Close the Socket
fclose($socket); fclose($socket);
?> ##php code end