/* 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: Christoph Renner co-programmer: */ #include "udp_server_socket.h" #include "debug.h" ObjectListDefinition(UdpServerSocket); /** * constructor * @param port port to listen on */ UdpServerSocket::UdpServerSocket( int port ) : ServerSocket( port ) { packet = SDLNet_AllocPacket( UDP_PACKET_SIZE ); if ( !packet ) { PRINTF(1)("SDLNet_AllocPacket: %s\n", SDLNet_GetError()); assert( false ); bOk = false; } memset( packet->data, 0, UDP_PACKET_SIZE ); listen( port ); } /** * default destructor */ UdpServerSocket::~UdpServerSocket( ) { for ( int i = 0; i < (int)packetBuffer.size(); i++ ) removeUserPackets( i ); if ( packet ) SDLNet_FreePacket( packet ); if ( socket ) SDLNet_UDP_Close( socket ); } /** * tell udpServerSocket to recieve packets on port * @param port port * @return true on success */ bool UdpServerSocket::listen( unsigned int port ) { socket = SDLNet_UDP_Open( port ); PRINTF(0)("listening on port: %d\n", port); if ( !socket ) { bOk = false; return false; } return true; } /** * get newly connected socket. note * @return new socket or NULL if no new socket exists */ NetworkSocket * UdpServerSocket::getNewSocket( void ) { NetworkSocket * result = NULL; if ( newSocketList.size() > 0 ) { result = newSocketList.front(); newSocketList.pop_front(); } return result; } /** * stop listening on server */ void UdpServerSocket::close( ) { for ( int i = 0; i < (int)packetBuffer.size(); i++ ) removeUserPackets( i ); packetBuffer.clear(); userList.clear(); SDLNet_UDP_Close( socket ); socket = NULL; } /** * clean up users buffer * @param userId users userid */ void UdpServerSocket::removeUserPackets( int userId ) { if ( userId >= (int)packetBuffer.size() ) return; for ( NetworkPacketList::iterator it = packetBuffer[userId].begin(); it!=packetBuffer[userId].end(); it++ ) { if ( it->data ) { free( it->data ); it->data = NULL; } } packetBuffer[userId].clear(); } /** * get next packet for user * @param userId user id * @return recieved packet or packet with length 0 if no packet available */ NetworkPacket UdpServerSocket::getPacket( int userId ) { NetworkPacket res; res.data = NULL; res.length = 0; if ( (int)packetBuffer.size() > userId && (int)packetBuffer[userId].size() > 0 ) { res.data = packetBuffer[userId].front().data; res.length = packetBuffer[userId].front().length; packetBuffer[userId].pop_front(); } return res; } /** * get number of packets recieved for user * @param userId user id * @return number of packets in buffer */ int UdpServerSocket::getPacketCount( int userId ) { if ( userId >= (int)packetBuffer.size() ) return -1; return packetBuffer[userId].size(); } /** * will set user state * @param userId users id * @param ip users host / port */ void UdpServerSocket::initUser( int userId, IPaddress ip, byte randomByte ) { int channel = SDLNet_UDP_Bind( socket, userId, &ip ); if( channel != userId ) { PRINTF(1)("SDLNet_UDP_Bind: %s\n", SDLNet_GetError()); assert(false); return; } if ( userId < (int)packetBuffer.size() ) removeUserPackets( userId ); if ( (int)packetBuffer.size() <= userId ) packetBuffer.resize( userId + 1 ); if ( (int)userList.size() <= userId ) userList.resize( userId + 1 ); userList[ userId ].addr = ip; userList[ userId ].randomByte = randomByte; } /** * remove user from list * @param userId user id */ void UdpServerSocket::removeUser( int userId ) { removeUserPackets( userId ); if ( userId >= (int)userList.size() ) return; userList[userId].addr.host = 0; userList[userId].addr.port = 0; SDLNet_UDP_Unbind( socket, userId ); } /** * send one packet to client associated to userId * @param networkPacket packet to send * @param userId users id * @return true on success */ bool UdpServerSocket::sendPacket( NetworkPacket networkPacket , int userId ) { if ( !socket ) return false; assert( networkPacket.length <= UDP_PACKET_SIZE ); memcpy( packet->data, networkPacket.data, networkPacket.length ); packet->len = networkPacket.length; packet->channel = -1; if ( SDLNet_UDP_Send( socket, userId, packet ) == 0 ) { PRINTF(1)("SDLNet_UDP_Send: %s\n", SDLNet_GetError()); return false; } return true; } /** * do periodically things */ void UdpServerSocket::update( ) { int res; int newConn = 0; // iterate through all newly received packets and assign them to the users packet buffer for ( res = SDLNet_UDP_Recv( socket, packet ); res == 1; res = SDLNet_UDP_Recv( socket, packet ) ) { int userId; bool isNewConnection = false; if ( packet->len <= 0 ) continue; // search the user id this backet belongs to for ( userId = 0; userId < (int)userList.size(); userId++ ) { if ( userList[userId].addr.host == packet->address.host && userList[userId].addr.port == packet->address.port && userList[userId].randomByte == ( packet->data[0] & 0xFC ) ) break; } // is it a new packet initializing a new connecion? if ( userId >= (int)userList.size() ) { newConn++; isNewConnection = true; if ( newConn > MAX_NEW_CONNECTIONS ) { PRINTF(2)("Too many new connections. Dropping packet\n"); continue; } for ( userId =0; userId < (int)userList.size(); userId++ ) if ( userList[userId].addr.host == 0 && userList[userId].addr.port == 0 ) break; this->initUser( userId, packet->address, packet->data[0] & 0xFC ); UdpSocket * sock = new UdpSocket( this, packet->address, userId, packet->data[0] & 0xFC ); newSocketList.push_back( sock ); PRINTF(0)("NEW CONNECTION %x\n", packet->address.host ); } // add new packet to packetbuffer NetworkPacket networkPacket; networkPacket.length = packet->len; if ( packet->len != 0 ) { networkPacket.data = (byte*)malloc( packet->len ); assert( networkPacket.data ); } else { networkPacket.data = NULL; } memcpy( networkPacket.data, packet->data, packet->len ); packetBuffer[userId].push_back( networkPacket ); } assert( res == 0 ); }