/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli (patrick@orxonox.ethz.ch) */ #include "proxy_control.h" #include "shell_command.h" #include "state.h" #include "shared_network_data.h" #include "network_manager.h" #include "network_game_manager.h" #include "ip.h" #include "peer_info.h" #include "network_stream.h" #include "converter.h" #include "preferences.h" #include "debug.h" #include "monitor/network_monitor.h" #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_NETWORK ProxyControl* ProxyControl::singletonRef = NULL; SHELL_COMMAND(forceReconnect, ProxyControl, forceReconnectionShell); ObjectListDefinition(ProxyControl); /** * constructor */ ProxyControl::ProxyControl() { this->registerObject(this, ProxyControl::_objectList); this->setSynchronized(false); MessageManager::getInstance()->registerMessageHandler( MSGID_PROXY_NEWCLIENT, messageHandlerNewClient, NULL ); MessageManager::getInstance()->registerMessageHandler( MSGID_PROXY_LEAVECLIENT, messageHandlerLeaveClient, NULL ); MessageManager::getInstance()->registerMessageHandler( MSGID_PROXY_COMMAND, messageHandlerCommand, NULL ); PRINTF(0)("ProxyControl created\n"); } /** * standard deconstructor */ ProxyControl::~ProxyControl() { ProxyControl::singletonRef = NULL; } /** * override this function to be notified on change * of your registred variables. * @param id id's which have changed */ void ProxyControl::varChangeHandler( std::list< int > & id ) { // if ( std::find( id.begin(), id.end(), playableUniqueId_handle ) != id.end() ) // { // this->setPlayableUniqueId( this->playableUniqueId ); // // PRINTF(0)("uniqueID changed %d %d %d\n", userId, SharedNetworkData::getInstance()->getHostID(), getUniqueID()); // } } /** * signals new client connected to this local proxy * * byte 0 - 3 : userId * byte 4 - 7 : ip address (IPaddress.host) * * @param userId userId of the new client */ void ProxyControl::signalNewClient(int userId) { PRINTF(0)("Signaling new Client: %i\n", userId); // make sure we are a proxy server assert(SharedNetworkData::getInstance()->isProxyServerActive()); byte data[2 * INTSIZE]; // write the userId in the message assert( Converter::intToByteArray( userId, data, INTSIZE ) == INTSIZE ); // and the ip as an int PeerInfo* pInfo = SharedNetworkData::getInstance()->getNetworkMonitor()->getPeerByUserId(userId); assert(pInfo != NULL); assert( Converter::intToByteArray( pInfo->ip.host(), data + INTSIZE, INTSIZE ) == INTSIZE ); MessageManager::getInstance()->sendMessage( MSGID_PROXY_NEWCLIENT, data, 2*INTSIZE, RT_SERVER, NET_UNASSIGNED, MP_HIGHBANDWIDTH ); } /** * this is the handler for proxy signals: new clients * * @param messageType the type of the message * @param data message data * @param dataLength length of the message data * @param someData some other atteched data * @param senderId id of the sender client * @param destinationId id of the destination client * @return true if succeeded */ bool ProxyControl::messageHandlerNewClient( MessageType messageType, byte * data, int dataLength, void * someData, int senderId, int destinationId ) { // body data length correct? if ( dataLength != 2 * INTSIZE ) { PRINTF(2)("new client message has wrong size: %d\n", dataLength ); return true; } // read the userId fromt he message body int newClientId = 0; assert( Converter::byteArrayToInt( data, &newClientId) == INTSIZE ); // now read the ip address int ipHost = 0; assert( Converter::byteArrayToInt( data + INTSIZE, &ipHost) == INTSIZE ); // register the new node at the network monitor NetworkMonitor* netMon = SharedNetworkData::getInstance()->getNetworkMonitor(); NetworkNode* nNode = netMon->getNodeByUserId(senderId); // this gets the proxy server who sent the msg if( nNode == NULL) PRINTF(0)("Couldn't find node %i! Error\n", senderId); PeerInfo* pInfo = new PeerInfo(); pInfo->bLocal = false; pInfo->ip = IP(ipHost, 9999); pInfo->nodeType = NET_CLIENT; pInfo->userId = newClientId; netMon->addNode(nNode, pInfo); PRINTF(0)("Got Signal: from %i new player arrived with userId: %i and ip: %s on %i\n", senderId, newClientId, pInfo->ip.ipString().c_str(), senderId); // part for the master server if( SharedNetworkData::getInstance()->isMasterServer()) { // we now create the new player ship and stuff... NetworkGameManager::getInstance()->signalNewPlayer(newClientId); } return true; } /** * signals client disconnect * @param userId userId of the old client */ void ProxyControl::signalLeaveClient(int userId) { PRINTF(0)("Signaling new Client: %i\n", userId); // make sure we are a proxy server assert(SharedNetworkData::getInstance()->isProxyServerActive()); byte data[INTSIZE]; assert( Converter::intToByteArray( userId, data, INTSIZE ) == INTSIZE ); MessageManager::getInstance()->sendMessage( MSGID_PROXY_LEAVECLIENT, data, INTSIZE, RT_SERVER, NET_UNASSIGNED, MP_HIGHBANDWIDTH ); } /** * this is the handler for proxy signals: removing clients * * @param messageType the type of the message * @param data message data * @param dataLength length of the message data * @param someData some other atteched data * @param senderId id of the sender client * @param destinationId id of the destination client * @return true if succeeded */ bool ProxyControl::messageHandlerLeaveClient( MessageType messageType, byte * data, int dataLength, void * someData, int senderId, int destinationId ) { // body data length correct? if ( dataLength != INTSIZE ) { PRINTF(2)("leave client message has wrong size: %d\n", dataLength ); return true; } // read the userId fromt he message body int leaveClientId = 0; assert( Converter::byteArrayToInt( data, &leaveClientId) == INTSIZE ); // remove the node from the network monitor NetworkMonitor* netMon = SharedNetworkData::getInstance()->getNetworkMonitor(); NetworkNode* nNode = netMon->getNodeByUserId(senderId); // this gets the proxy server who sent the msg netMon->removeNode(nNode, netMon->getPeerByUserId(leaveClientId)); PRINTF(0)("Got Signal: from %i player left with userId: %i\n", senderId, leaveClientId); // part for the master server if( SharedNetworkData::getInstance()->isMasterServer()) { // we now create the new player ship and stuff... NetworkGameManager::getInstance()->signalLeftPlayer(leaveClientId); } else if(SharedNetworkData::getInstance()->isProxyServerActive()) { } return true; } /** * forces a client to reconnect to another server * @param userId the userId of the client/user :D * @param newAddress the addresss the client should connect to (fully quali dns) */ void ProxyControl::forceReconnection(int userId, const std::string& newAddress) { IP ipAddr = IP(newAddress, 9999); this->forceReconnection(userId, newAddress); } /** * thumb command: staticly reconnect node 10 to node 0 */ void ProxyControl::forceReconnectionShellThumb() { this->forceReconnectionShell(10, 0); } /** * a shell command wrapper * @param userId the userId of the client/user :D * @param serverId the userId of the server to connect to */ void ProxyControl::forceReconnectionShell(int userId, int serverId) { PRINTF(0)("shell reconnectoin command: %i to %i\n", userId, serverId); this->forceReconnection(userId, serverId); } /** * forces a client to reconnect to another server * @param userId the userId of the client/user :D * @param serverId the userId of the server to connect to */ void ProxyControl::forceReconnection(int userId, int serverId) { PeerInfo* serverInfo = SharedNetworkData::getInstance()->getNetworkMonitor()->getPeerByUserId(serverId); if( serverInfo == NULL) { PRINTF(0)("There is no server with userId %i registered in this network. Check the uid again\n", serverId); return; } else if( serverInfo->isClient()) { PRINTF(0)("You can't connec to to a client (userId %i)\n", serverId); return; } this->forceReconnection(userId, serverId, serverInfo->ip); } /** * forces a client to reconnect to another server * @param userId the userId of the client/user :D * @param newAddress the addresss the client should connect to */ void ProxyControl::forceReconnection(int userId, int serverId, IP newAddress) { PRINTF(0)("forcing reconnection: userId %i to %s\n", userId, newAddress.ipString().c_str()); // make sure we are a proxy server if( SharedNetworkData::getInstance()->isClient()) { PRINTF(0)("I am client, got no right to force reconnection\n"); return; } byte data[3 * INTSIZE]; // write type of message int type = PXY_RECONNECT; assert( Converter::intToByteArray( type, data, INTSIZE ) == INTSIZE ); // write the userId in the message assert( Converter::intToByteArray( userId, data + INTSIZE, INTSIZE ) == INTSIZE ); // and the ip as an int PeerInfo* pInfo = SharedNetworkData::getInstance()->getNetworkMonitor()->getPeerByUserId(userId); if( pInfo == NULL) { PRINTF(0)("There is no client with userId %i registered in this network. Check the uid again\n", userId); return; } else if( pInfo->isMasterServer() || pInfo->isProxyServerActive()) { PRINTF(0)("You cannont reconnect a %s, abording\n", pInfo->getNodeTypeString().c_str()); return; } assert( Converter::intToByteArray( newAddress.host(), data + 2 * INTSIZE, INTSIZE ) == INTSIZE ); PRINTF(0)("Sending reconnection command to: %i\n", userId); // send the reconnection signal to the client who must reconnect MessageManager::getInstance()->sendMessage( MSGID_PROXY_COMMAND, data, 3*INTSIZE, RT_USER, userId, MP_HIGHBANDWIDTH ); // and to the server who needs to accept the new client MessageManager::getInstance()->sendMessage( MSGID_PROXY_COMMAND, data, 3*INTSIZE, RT_USER, serverId, MP_HIGHBANDWIDTH ); } /** * this is the handler for proxy commands * * @param messageType the type of the message * @param data message data * @param dataLength length of the message data * @param someData some other atteched data * @param senderId id of the sender client * @param destinationId id of the destination client * @return true if succeeded */ bool ProxyControl::messageHandlerCommand( MessageType messageType, byte * data, int dataLength, void * someData, int senderId, int destinationId ) { // body data length correct? if ( dataLength != 3 * INTSIZE ) { PRINTF(1)("leave client message has wrong size: %d\n", dataLength ); return true; } // read the command type int type = 0; assert( Converter::byteArrayToInt( data, &type) == INTSIZE ); PRINTF(0)("got command from %i with code %i\n", senderId, type); // now distingush all different sorts of commands switch( type) { case PXY_RECONNECT: { // now read the user id int userId; assert( Converter::byteArrayToInt( data + INTSIZE, &userId) == INTSIZE ); // and read the dest address int ipHost = 0; assert( Converter::byteArrayToInt( data + INTSIZE, &ipHost) == INTSIZE ); PRINTF(0)("Got reconnection command from %i: reconnect!\n", senderId); // handle it if( SharedNetworkData::getInstance()->getHostID() == userId) { if( SharedNetworkData::getInstance()->isClient()) SharedNetworkData::getInstance()->getDefaultSyncStream()->softReconnectToServer(userId, IP(ipHost, 9999)); else SharedNetworkData::getInstance()->getDefaultSyncStream()->prepareSoftConnection(userId); } break; } default: PRINTF(0)("Command not known with id %i\n", type); } return true; }