Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/network/MasterServer.cc @ 10980

Last change on this file since 10980 was 10624, checked in by landauf, 9 years ago

merged branch core7 back to trunk

  • Property svn:eol-style set to native
File size: 13.8 KB
RevLine 
[7569]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Sandro 'smerkli' Merkli
24 *   Co-authors:
25 *      ...
26 *
27 */
28
[7565]29#include "MasterServer.h"
[10624]30#include "core/command/ConsoleCommandIncludes.h"
[7590]31#include "core/CoreIncludes.h"
32#include "core/CorePrereqs.h"
[10624]33#include "core/singleton/ScopedSingletonIncludes.h"
[8937]34#include "util/Output.h"
[7565]35
[10622]36namespace orxonox
[7590]37{
[8937]38  /*** MACROS ***/
39  /* commands for the terminal interface */
40  SetConsoleCommand( "ms-listservers", &MasterServer::listServers );
41  SetConsoleCommand( "ms-delserver", &MasterServer::delServer );
42  //SetConsoleCommand( "ms-serverinfo", &MasterServer::serverInfo );
43
44  /* forward declaration so the linker doesn't complain */
45  MasterServer *MasterServer::instance = NULL;
46
47  /* command: list servers */
[10622]48  void
[8937]49  MasterServer::listServers( void )
50  {
51    /* get an iterator */
52    std::list<ServerListElem>::iterator i;
53
54    /* print list header */
55    orxout(user_info) << "List of connected servers" << std::endl;
56
57    /* loop through list elements */
[10622]58    for( i = MasterServer::getInstance()->mainlist.serverlist.begin();
59      i != MasterServer::getInstance()->mainlist.serverlist.end(); ++i )
[8937]60    {
61      orxout(user_info) << "  " << (*i).ServerInfo.getServerIP() << std::endl;
62    }
63
64    /* display end of list */
65    orxout(user_info) << MasterServer::getInstance()->mainlist.serverlist.size() <<
66      " servers connected." << std::endl;
67  }
68
[10622]69  void
[8937]70  MasterServer::delServer( std::string todeladdr )
71  {
72    /* tell the user we're now removing the entry from the server list */
[10622]73    orxout(user_info) << "MS: Deleting server \"" << todeladdr << "\"..."
[8937]74      << std::endl;
75
76    /* see if we actually have that server on our list */
[10622]77    ServerListSearchResult shandle =
[8937]78      MasterServer::getInstance()->mainlist.findServerByAddress(todeladdr);
79
80    if( !shandle.success )
81    { orxout(user_info) << "MS: Server not found, not removing." << std::endl;
82      return;
83    }
84
[10622]85    /* force-disconnect the server */
[8952]86    enet_peer_disconnect( shandle.result.peer, 0 );
[8937]87
88    /* actually remove the entry from the server list by address */
89    MasterServer::getInstance()->mainlist.delServerByAddress( todeladdr);
90
91    /* tell the user about our success */
92    orxout(user_info) << "MS: Server deletion successful." << std::endl;
93  }
94
95
[7684]96  /* helpers */
[10622]97  static void
[7684]98  helper_output_debug( ENetEvent *event, char *addrconv )
99  {
[8858]100    orxout(verbose, context::master_server)
[10622]101      << "A packet of length"
[7684]102      << event->packet->dataLength
103      << " containing "
104      << (const char*)event->packet->data
105      << " was received from "
[10622]106      << addrconv
[7684]107      << " on channel "
[8858]108      << event->channelID << endl;
[7684]109  }
110
111  void
112  MasterServer::helper_sendlist( ENetEvent *event )
113  {
114    /* get an iterator */
[8937]115    std::list<ServerListElem>::iterator i;
[7684]116
117    /* packet holder */
118    ENetPacket *reply;
119
120    /* loop through list elements */
[10622]121    for( i = mainlist.serverlist.begin(); i
122        != mainlist.serverlist.end(); ++i )
[7684]123    {
124      /* send this particular server */
125      /* build reply string */
[10622]126      int packetlen = MSPROTO_SERVERLIST_ITEM_LEN + 1 + (*i).ServerInfo.getServerIP().length() + 1 + (*i).ServerInfo.getServerName().length() + 1 + sizeof((*i).ServerInfo.getClientNumber()) + 1;
127      char *tosend = (char *)calloc(packetlen ,1 );
128      if( !tosend )
[8858]129      { orxout(internal_warning, context::master_server) << "Masterserver.cc: Memory allocation failed." << endl;
[7684]130        continue;
[10622]131      }
132      sprintf( tosend, "%s %s %s %u", MSPROTO_SERVERLIST_ITEM,
133          (*i).ServerInfo.getServerIP().c_str(), (*i).ServerInfo.getServerName().c_str(), (*i).ServerInfo.getClientNumber());
[7684]134
135      /* create packet from it */
136      reply = enet_packet_create( tosend,
[10622]137          strlen( tosend ) + 1,
[7684]138          ENET_PACKET_FLAG_RELIABLE);
139
140      /* Send the reply to the peer over channel id 0. */
141      enet_peer_send( event->peer, 0, reply );
142
143      /* One could just use enet_host_service() instead. */
144      enet_host_flush( this->server );
145
146      /* free the tosend buffer */
147      free( tosend );
[10622]148    }
[7684]149
[7750]150    /* create end-of-list packet */
[7684]151    reply = enet_packet_create( MSPROTO_SERVERLIST_END,
152        MSPROTO_SERVERLIST_END_LEN + 1,
153        ENET_PACKET_FLAG_RELIABLE );
154
[7750]155    /* send end-of-list packet */
[7684]156    enet_peer_send( event->peer, 0, reply );
157
158    /* One could just use enet_host_service() instead. */
159    enet_host_flush( this->server );
160  }
161
[10622]162  /* maybe the two methods below can be merged into one and
163   * made to use ENet's RTT functionality to check for disconnected
[8937]164   * servers.
165   */
[10622]166  void
[8937]167  MasterServer::helper_cleanupServers( void )
168  {
169    /* get an iterator */
170    std::list<ServerListElem>::iterator i;
[10622]171
[8937]172    if( mainlist.serverlist.size() == 0 )
173      return;
[7684]174
[8937]175    /* loop through list elements */
[10622]176    for( i = mainlist.serverlist.begin(); i
177        != mainlist.serverlist.end(); ++i )
[8937]178    { /* see if we have a disconnected peer */
[10622]179      if( (*i).peer &&
[8937]180         ((*i).peer->state == ENET_PEER_STATE_DISCONNECTED ||
181          (*i).peer->state == ENET_PEER_STATE_ZOMBIE ))
[10622]182      {
[8937]183        /* Remove it from the list */
184        orxout(internal_warning) << (char*)(*i).peer->data << " timed out.\n";
185        mainlist.delServerByName( (*i).ServerInfo.getServerName() );
[7684]186
[8937]187        /* stop iterating, we manipulated the list */
188        /* TODO note: this only removes one dead server per loop
[10622]189         * iteration. not beautiful, but one iteration is ~100ms,
[8937]190         * so not really relevant for the moment.
191         */
192        break;
193      }
194    }
[10622]195
[8937]196  }
[7684]197
[8937]198
199
200
[7590]201  /***** EVENTS *****/
202  /* connect event */
[10622]203  int
[7611]204  MasterServer::eventConnect( ENetEvent *event )
[7590]205  { /* check for bad parameters */
206    if( !event )
[8858]207    { orxout(internal_warning, context::master_server) << "MasterServer::eventConnect: No event given." << endl;
[7590]208      return -1;
209    }
[7565]210
[7611]211    /* convert address to string. */
212    char *addrconv = (char *) calloc( 50, 1 );
213    enet_address_get_host_ip( &(event->peer->address), addrconv, 49 );
214
[7590]215    /* output debug info */
[10622]216    orxout(verbose, context::master_server) << "A new client connected from "
217      << addrconv
218      << " on port "
[8858]219      << event->peer->address.port << endl;
[7565]220
[7611]221    /* store string form of address here */
[10622]222    event->peer->data = addrconv;
[7611]223
224    /* all fine. */
[7590]225    return 0;
[7565]226  }
227
[7590]228  /* disconnect event */
[10622]229  int
[7611]230  MasterServer::eventDisconnect( ENetEvent *event )
[7590]231  { /* check for bad parameters */
232    if( !event )
[8858]233    { orxout(internal_warning, context::master_server) << "No event given." << endl;
[7590]234      return -1;
235    }
[7565]236
[7651]237    /* output that the disconnect happened */
[8858]238    orxout(verbose, context::master_server) << (char*)event->peer->data << " disconnected." << endl;
[7565]239
[7651]240    /* create string from peer data */
241    std::string name = std::string( (char*)event->peer->data );
242
[7590]243    /* remove the server from the list it belongs to */
[7657]244    this->mainlist.delServerByName( name );
[7565]245
[7590]246    /* Reset the peer's client information. */
[7611]247    if( event->peer->data ) free( event->peer->data );
[7651]248
249    /* done */
[7590]250    return 0;
[7565]251  }
252
[7590]253  /* data event */
[10622]254  int
[7611]255  MasterServer::eventData( ENetEvent *event )
[7651]256  { /* validate packet */
[7611]257    if( !event || !(event->packet) || !(event->peer) )
[8858]258    { orxout(internal_warning, context::master_server) << "No complete event given." << endl;
[7590]259      return -1;
260    }
[10622]261
[7611]262    /* generate address in readable form */
263    char *addrconv = (char *) calloc( 50, 1 );
264    enet_address_get_host_ip( &(event->peer->address), addrconv, 49 );
[10622]265    /* convert to string */
266    std::string ip = std::string( addrconv );
267    /* delete addrconv */
268    if( addrconv ) free( addrconv );
[7590]269
[10622]270    /* pointer to full packet data */
271    char * packetdata = (char *)event->packet->data;
272
[7750]273    /* output debug info about the data that has come */
[7684]274    helper_output_debug( event, addrconv );
[7590]275
[7630]276    /* GAME SERVER OR CLIENT CONNECTION? */
[10622]277    if( !strncmp(packetdata, MSPROTO_GAME_SERVER, MSPROTO_GAME_SERVER_LEN ) )
[7651]278    { /* Game server */
[7630]279
[10622]280      if( !strncmp( packetdata + MSPROTO_GAME_SERVER_LEN+1, MSPROTO_REGISTER_SERVER, MSPROTO_REGISTER_SERVER_LEN ) )
[7651]281      { /* register new server */
[10622]282        mainlist.addServer( packet::ServerInformation( event ), event->peer );
283
[7658]284        /* tell people we did so */
[10622]285        orxout(internal_info, context::master_server) << "Added new server to list: " <<
[8858]286          packet::ServerInformation( event ).getServerIP() << endl;
[7651]287      }
[7756]288
[10622]289      else if( !strncmp( packetdata + MSPROTO_GAME_SERVER_LEN+1, MSPROTO_SERVERDC, MSPROTO_SERVERDC_LEN ) )
290      { /* disconnect server */
291
292        /* remove the server from the list it belongs to */
293        this->mainlist.delServerByAddress( ip );
294
295        /* tell the user */
296        orxout(internal_info, context::master_server) << "Removed server " << ip << " from list." << endl;
297      }
298      /* TODO add hook for disconnect here */
299
300      else if( !strncmp( packetdata + MSPROTO_GAME_SERVER_LEN+1, MSPROTO_SET_NAME, MSPROTO_SET_NAME_LEN ) )
301      { /* save server name */
[7763]302        /* create string from peer data */
[10622]303        std::string data (event->packet->data,event->packet->data + event->packet->dataLength );
304        std::string name = data.substr(MSPROTO_GAME_SERVER_LEN+1 + MSPROTO_SET_NAME_LEN + 1);
[7763]305
306        /* remove the server from the list it belongs to */
[10622]307        this->mainlist.setNameByAddress( ip, name );
[7763]308
309        /* tell the user */
[10622]310        orxout(internal_info, context::master_server) << "Updated server " << ip << " with new name " << name << endl;
[7763]311      }
312
[10622]313      else if( !strncmp( packetdata + MSPROTO_GAME_SERVER_LEN+1, MSPROTO_SET_CLIENTS, MSPROTO_SET_CLIENTS_LEN ) )
314      { /* save client count from server */
315        /* create string from peer data */
316        std::string data (event->packet->data,event->packet->data + event->packet->dataLength );
317        std::string textform= data.substr(MSPROTO_GAME_SERVER_LEN + 1 + MSPROTO_SET_CLIENTS_LEN + 1);
318        int clientNumber = Ogre::StringConverter::parseInt(textform);
319
320        this->mainlist.setClientsByAddress( ip, clientNumber);
321
322        /* tell the user */
323        orxout(internal_info, context::master_server) << "Updated server " << ip << " with new client number " << clientNumber << endl;
324      }
[7651]325    }
[10622]326    else if( !strncmp( packetdata, MSPROTO_CLIENT, MSPROTO_CLIENT_LEN) )
[7651]327    { /* client */
[10622]328      if( !strncmp( packetdata + MSPROTO_CLIENT_LEN+1, MSPROTO_REQ_LIST, MSPROTO_REQ_LIST_LEN ) )
[7684]329        /* send server list */
330        helper_sendlist( event );
[7651]331    }
332    else
[10622]333    { /* bad message, don't do anything. */ }
[7651]334
[7590]335    /* Clean up the packet now that we're done using it. */
336    enet_packet_destroy( event->packet );
337    return 0;
338  }
[7565]339
[7611]340
[7590]341  /**** MAIN ROUTINE *****/
[10622]342  int
[7590]343  MasterServer::run()
344  {
345    /***** ENTER MAIN LOOP *****/
346    ENetEvent *event = (ENetEvent *)calloc(sizeof(ENetEvent), sizeof(char));
347    if( event == NULL )
[10622]348    {
[8858]349      orxout(user_error, context::master_server) << "Could not create ENetEvent structure, exiting." << endl;
[7590]350      exit( EXIT_FAILURE );
351    }
[7565]352
[8937]353    /* check for timed out peers and remove those from * the server list */
354    helper_cleanupServers();
355
356
[7743]357    /* create an iterator for the loop */
358    enet_host_service( this->server, event, 100 );
[7611]359
[7743]360    /* check what type of event it is and react accordingly */
361    switch (event->type)
362    { /* new connection */
[10622]363      case ENET_EVENT_TYPE_CONNECT:
[7743]364        eventConnect( event ); break;
[7565]365
[7743]366        /* disconnect */
[10622]367      case ENET_EVENT_TYPE_DISCONNECT:
[7743]368        eventDisconnect( event ); break;
369
370        /* incoming data */
371      case ENET_EVENT_TYPE_RECEIVE: eventData( event ); break;
372      default: break;
[7590]373    }
[7565]374
[7590]375    /* done */
[10622]376    free(event);
[7590]377    return 0;
[10622]378  }
[7565]379
[7590]380  /* constructor */
381  MasterServer::MasterServer()
382  {
383    /***** INITIALIZE NETWORKING *****/
384    if( enet_initialize () != 0)
[8858]385    { orxout(user_error, context::master_server) << "An error occurred while initializing ENet." << endl;
[7590]386      exit( EXIT_FAILURE );
387    }
[7565]388
[7590]389    /* register deinitialization */
390    atexit( enet_deinitialize );
[7565]391
[7729]392    /* set the quit flag to false */
393    this->quit = false;
394
[7590]395    /* Bind the server to the default localhost and port ORX_MSERVER_PORT */
396    this->address.host = ENET_HOST_ANY;
397    this->address.port = ORX_MSERVER_PORT;
[7589]398
[10622]399    /* create a host with the above settings (the last two 0 mean: accept
[7590]400     * any input/output bandwidth */
[10622]401    this->server = enet_host_create( &this->address, ORX_MSERVER_MAXCONNS,
[7801]402        ORX_MSERVER_MAXCHANS, 0, 0 );
403    assert(this->server);
[7590]404
405    /* see if creation worked */
406    if( !this->server )
[10622]407    { orxout(user_error, context::master_server) <<
[8858]408        "An error occurred while trying to create an ENet server host." << endl;
[7611]409      exit( EXIT_FAILURE );
[7590]410    }
[7565]411
[8937]412    /* set pointer to this instance */
413    MasterServer::setInstance( this );
[7743]414
415    /* tell people we're now initialized */
[8858]416    orxout(internal_status, context::master_server) << "MasterServer initialized, waiting for connections." << endl;
[7565]417  }
418
[7590]419  /* destructor */
420  MasterServer::~MasterServer()
421  {
422    /***** CLEANUP PROCESS *****/
423    /* terminate all networking connections */
424    enet_host_destroy( this->server );
[7565]425
[7611]426    /* free all used memory */
[7590]427    /* clear the list of connected game servers */
428    /* clear the list of connected game clients */
429  }
430
431/* end of namespace */
432}
Note: See TracBrowser for help on using the repository browser.