/* * The Mana World Server * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * * The Mana World 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 of the License, or any later version. * * The Mana World is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with The Mana World; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id$ */ #include #include #include #include "connectionhandler.h" #include "netsession.h" #include "utils/logger.h" #ifdef SCRIPT_SUPPORT #include "script.h" #endif /** * TEMPORARY * Split a string into a std::vector delimiting elements by 'split'. This * function could be used for ASCII message handling (as we do not have * a working client yet, using ASCII allows tools like Netcat to be used * to test server functionality). * * This function may seem unoptimized, except it is this way to allow for * thread safety. */ std::vector stringSplit(const std::string &str, const std::string &split) { std::vector result; // temporary result unsigned int i; unsigned int last = 0; // iterate through string for (i = 0; i < str.length(); i++) { if (str.compare(i, split.length(), split.c_str(), split.length()) == 0) { result.push_back(str.substr(last, i - last)); last = i + 1; } } // add remainder of string if (last < str.length()) { result.push_back(str.substr(last, str.length())); } return result; } /** * Convert a IP4 address into its string representation */ std::string ip4ToString(unsigned int ip4addr) { std::stringstream ss; ss << (ip4addr & 0x000000ff) << "." << ((ip4addr & 0x0000ff00) >> 8) << "." << ((ip4addr & 0x00ff0000) >> 16) << "." << ((ip4addr & 0xff000000) >> 24); return ss.str(); } //////////////// ClientData::ClientData(): inp(0) { } ConnectionHandler::ConnectionHandler() { } void ConnectionHandler::startListen(ListenThreadData *ltd) { // Allocate a socket set SDLNet_SocketSet set = SDLNet_AllocSocketSet(MAX_CLIENTS); if (!set) { LOG_FATAL("SDLNet_AllocSocketSet: " << SDLNet_GetError(), 0) exit(1); } // Add the server socket to the socket set if (SDLNet_TCP_AddSocket(set, ltd->socket) < 0) { LOG_FATAL("SDLNet_AddSocket: " << SDLNet_GetError(), 0) exit(1); } // Keep checking for socket activity while running while (ltd->running) { int numready = SDLNet_CheckSockets(set, 100); if (numready == -1) { LOG_ERROR("SDLNet_CheckSockets: " << SDLNet_GetError(), 0) // When this is a system error, perror may help us perror("SDLNet_CheckSockets"); } else if (numready > 0) { LOG_INFO(numready << " socket(s) with activity!", 0) // Check server socket if (SDLNet_SocketReady(ltd->socket)) { TCPsocket client = SDLNet_TCP_Accept(ltd->socket); if (client) { // Add the client socket to the socket set if (SDLNet_TCP_AddSocket(set, client) < 0) { LOG_ERROR("SDLNet_AddSocket: " << SDLNet_GetError(), 0) } else { NetComputer *comp = new NetComputer(this, client); clients.push_back(comp); computerConnected(comp); LOG_INFO(clients.size() << " client(s) connected", 0) } } } // Check client sockets NetComputers::iterator i; for (i = clients.begin(); i != clients.end(); ) { NetComputer *comp = *i; TCPsocket s = (*i)->getSocket(); if (SDLNet_SocketReady(s)) { char buffer[1024]; int result = SDLNet_TCP_Recv(s, buffer, 1024); if (result <= 0) { SDLNet_TCP_DelSocket(set, s); SDLNet_TCP_Close(s); computerDisconnected(comp); delete comp; comp = NULL; } else { // Copy the incoming data to the in buffer of this // client //buffer[result] = 0; //LOG_INFO("Received: " << buffer << ", Length: " // << result); LOG_INFO("Received length: " << result, 2); #ifdef SCRIPT_SUPPORT // This could be good if you wanted to extend the // server protocol using a scripting language. This // could be attained by using allowing scripts to // "hook" certain messages. //script->message(buffer); #endif // If the scripting subsystem didn't hook the message // it will be handled by the default message handler. // Convert the client IP address to string // representation std::string ipaddr = ip4ToString( SDLNet_TCP_GetPeerAddress(s)->host); // Make sure that the packet is big enough (> short) if (result >= 2) { Packet *packet = new Packet(buffer, result); MessageIn msg(packet); // (MessageIn frees packet) short messageId = msg.getId(); if (handlers.find(messageId) != handlers.end()) { // send message to appropriate handler handlers[messageId]->receiveMessage( *comp, msg); } else { // bad message (no registered handler) LOG_ERROR("Unhandled message (" << messageId << ") received from " << ipaddr, 0); } } else { LOG_ERROR("Message too short from " << ipaddr, 0); } } } // Traverse to next client, possibly deleting current if (comp == NULL) { i = clients.erase(i); } else { i++; } } } } // - Disconnect all clients (close sockets) SDLNet_FreeSocketSet(set); } void ConnectionHandler::computerConnected(NetComputer *comp) { LOG_INFO("A client connected!", 0) } void ConnectionHandler::computerDisconnected(NetComputer *comp) { LOG_INFO("A client disconnected!", 0) } void ConnectionHandler::registerHandler( unsigned int msgId, MessageHandler *handler) { handlers[msgId] = handler; } void ConnectionHandler::sendTo(tmwserv::BeingPtr beingPtr, MessageOut &msg) { for (NetComputers::iterator i = clients.begin(); i != clients.end(); i++) { if ((*i)->getCharacter().get() == beingPtr.get()) { (*i)->send(msg.getPacket()); break; } } } void ConnectionHandler::sendTo(std::string name, MessageOut &msg) { for (NetComputers::iterator i = clients.begin(); i != clients.end(); i++) { if ((*i)->getCharacter().get()->getName() == name) { (*i)->send(msg.getPacket()); break; } } } void ConnectionHandler::sendToEveryone(MessageOut &msg) { for (NetComputers::iterator i = clients.begin(); i != clients.end(); i++) { (*i)->send(msg.getPacket()); break; } } void ConnectionHandler::sendAround(tmwserv::BeingPtr beingPtr, MessageOut &msg) { for (NetComputers::iterator i = clients.begin(); i != clients.end(); i++) { // See if the other being is near enough, then send the message if (abs((*i)->getCharacter().get()->getX() - beingPtr.get()->getX()) <= AROUND_AREA_IN_TILES ) { if (abs((*i)->getCharacter().get()->getY() - beingPtr.get()->getY()) <= AROUND_AREA_IN_TILES ) { (*i)->send(msg.getPacket()); break; } } } } unsigned int ConnectionHandler::getClientNumber() { return clients.size(); }