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
- 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

View File

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

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
*/
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();
}
}
}

View File

@ -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";

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
<?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