Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/masterserver2/src/libraries/network/MasterServer.cc @ 8204

Last change on this file since 8204 was 8204, checked in by smerkli, 15 years ago
File size: 9.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"
[7590]30#include "util/ScopedSingletonManager.h"
31#include "core/CoreIncludes.h"
32#include "core/CorePrereqs.h"
[7565]33
[7590]34namespace orxonox
35{
[7684]36  /* helpers */
37  static void 
38  helper_output_debug( ENetEvent *event, char *addrconv )
39  {
40    COUT(4) << "A packet of length" 
41      << event->packet->dataLength
42      << " containing "
43      << (const char*)event->packet->data
44      << " was received from "
45      << addrconv
46      << " on channel "
47      << event->channelID << "\n";
48  }
49
50  void
51  MasterServer::helper_sendlist( ENetEvent *event )
52  {
53    /* get an iterator */
[8202]54    std::list<ServerListElem>::iterator i;
[7684]55
56    /* packet holder */
57    ENetPacket *reply;
58
59    /* loop through list elements */
60    for( i = mainlist.serverlist.begin(); i
61        != mainlist.serverlist.end(); ++i ) 
62    {
63      /* send this particular server */
64      /* build reply string */
[8202]65      char *tosend = (char *)calloc( (*i).ServerInfo.getServerIP().length() 
[7684]66          + MSPROTO_SERVERLIST_ITEM_LEN + 2,1 );
67      if( !tosend ) 
68      { COUT(2) << "Masterserver.cc: Memory allocation failed.\n";
69        continue;
70      } 
71      sprintf( tosend, "%s %s", MSPROTO_SERVERLIST_ITEM, 
[8202]72          (*i).ServerInfo.getServerIP().c_str() );
[7684]73
74      /* create packet from it */
75      reply = enet_packet_create( tosend,
76          strlen( tosend ) + 1, 
77          ENET_PACKET_FLAG_RELIABLE);
78
79      /* Send the reply to the peer over channel id 0. */
80      enet_peer_send( event->peer, 0, reply );
81
82      /* One could just use enet_host_service() instead. */
83      enet_host_flush( this->server );
84
85      /* free the tosend buffer */
86      free( tosend );
87    } 
88
[7750]89    /* create end-of-list packet */
[7684]90    reply = enet_packet_create( MSPROTO_SERVERLIST_END,
91        MSPROTO_SERVERLIST_END_LEN + 1,
92        ENET_PACKET_FLAG_RELIABLE );
93
[7750]94    /* send end-of-list packet */
[7684]95    enet_peer_send( event->peer, 0, reply );
96
97    /* One could just use enet_host_service() instead. */
98    enet_host_flush( this->server );
99  }
100
[8163]101  /* maybe the two methods below can be merged into one and
102   * made to use ENet's RTT functionality to check for disconnected
103   * servers.
104   */
105  void 
[8204]106  MasterServer::helper_cleanupServers( void )
[8163]107  {
108    /* get an iterator */
[8202]109    std::list<ServerListElem>::iterator i;
[8203]110     
111    if( mainlist.serverlist.size() == 0 )
112      return;
[8202]113
[8163]114    /* loop through list elements */
115    for( i = mainlist.serverlist.begin(); i
116        != mainlist.serverlist.end(); ++i ) 
[8204]117    { /* see if we have a disconnected peer */
[8203]118      if( (*i).peer && 
[8202]119         ((*i).peer->state == ENET_PEER_STATE_DISCONNECTED ||
120          (*i).peer->state == ENET_PEER_STATE_ZOMBIE ))
[8204]121      { 
122        /* Remove it from the list */
123        COUT(2) << (char*)(*i).peer->data << " timed out.\n";
124        mainlist.delServerByName( (*i).ServerInfo.getServerName() );
125
126        /* stop iterating, we manipulated the list */
127        /* TODO note: this only removes one dead server per loop
128         * iteration. not beautiful, but one iteration is ~100ms,
129         * so not really relevant for the moment.
130         */
[8203]131        break;
[8202]132      }
[8163]133    }
134 
135  }
[7684]136
137
138
[8163]139
[7590]140  /***** EVENTS *****/
141  /* connect event */
142  int 
[7611]143  MasterServer::eventConnect( ENetEvent *event )
[7590]144  { /* check for bad parameters */
145    if( !event )
[7611]146    { COUT(2) << "MasterServer::eventConnect: No event given.\n" ;
[7590]147      return -1;
148    }
[7565]149
[7611]150    /* convert address to string. */
151    char *addrconv = (char *) calloc( 50, 1 );
152    enet_address_get_host_ip( &(event->peer->address), addrconv, 49 );
153
[7590]154    /* output debug info */
[7611]155    COUT(4) << "A new client connected from " 
156      << addrconv
157      << " on port " 
158      << event->peer->address.port << "\n";
[7565]159
[7611]160    /* store string form of address here */
161    event->peer->data = addrconv; 
162
163    /* all fine. */
[7590]164    return 0;
[7565]165  }
166
[7590]167  /* disconnect event */
168  int 
[7611]169  MasterServer::eventDisconnect( ENetEvent *event )
[7590]170  { /* check for bad parameters */
171    if( !event )
[7611]172    { COUT(2) << "No event given.\n";
[7590]173      return -1;
174    }
[7565]175
[7651]176    /* output that the disconnect happened */
[8202]177    COUT(2) << (char*)event->peer->data << " disconnected.\n";
[7565]178
[7651]179    /* create string from peer data */
180    std::string name = std::string( (char*)event->peer->data );
181
[7590]182    /* remove the server from the list it belongs to */
[7657]183    this->mainlist.delServerByName( name );
[7565]184
[7590]185    /* Reset the peer's client information. */
[7611]186    if( event->peer->data ) free( event->peer->data );
[7651]187
188    /* done */
[7590]189    return 0;
[7565]190  }
191
[7590]192  /* data event */
193  int 
[7611]194  MasterServer::eventData( ENetEvent *event )
[7651]195  { /* validate packet */
[7611]196    if( !event || !(event->packet) || !(event->peer) )
197    { COUT(2) << "No complete event given.\n";
[7590]198      return -1;
199    }
[7611]200     
201    /* generate address in readable form */
202    char *addrconv = (char *) calloc( 50, 1 );
203    enet_address_get_host_ip( &(event->peer->address), addrconv, 49 );
[7590]204
[7750]205    /* output debug info about the data that has come */
[7684]206    helper_output_debug( event, addrconv );
[7590]207
[7630]208    /* GAME SERVER OR CLIENT CONNECTION? */
[7651]209    if( !strncmp( (char *)event->packet->data, MSPROTO_GAME_SERVER, 
210      MSPROTO_GAME_SERVER_LEN ) )
211    { /* Game server */
[7630]212
[7651]213      if( !strncmp( (char *)event->packet->data
214        + MSPROTO_GAME_SERVER_LEN+1, 
215        MSPROTO_REGISTER_SERVER, MSPROTO_REGISTER_SERVER_LEN ) )
216      { /* register new server */
[8202]217        mainlist.addServer( packet::ServerInformation( event ),
218          event->peer );
[7658]219       
220        /* tell people we did so */
221        COUT(2) << "Added new server to list: " << 
222          packet::ServerInformation( event ).getServerIP() << "\n";
[7651]223      }
[7756]224
[7763]225      else if( !strncmp( (char *)event->packet->data
226        + MSPROTO_GAME_SERVER_LEN+1,
227        MSPROTO_SERVERDC, MSPROTO_SERVERDC_LEN ) )
228      {
229        /* create string from peer data */
230        std::string name = std::string( addrconv );
231
232        /* remove the server from the list it belongs to */
[7765]233        this->mainlist.delServerByAddress( name );
[7763]234
235        /* tell the user */
236        COUT(2) << "Removed server " << name << " from list.\n";
237      }
238
[7756]239      /* TODO add hook for disconnect here */
[7651]240    }
241    else if( !strncmp( (char *)event->packet->data, MSPROTO_CLIENT, 
242      MSPROTO_CLIENT_LEN) )
243    { /* client */
244      if( !strncmp( (char *)event->packet->data + MSPROTO_CLIENT_LEN+1,
[7657]245        MSPROTO_REQ_LIST, MSPROTO_REQ_LIST_LEN ) )
[7684]246        /* send server list */
247        helper_sendlist( event );
[7651]248    }
249    else
250    { /* bad message, don't do anything. */ } 
251
[7611]252    /* delete addrconv */
253    if( addrconv ) free( addrconv );
254
[7590]255    /* Clean up the packet now that we're done using it. */
256    enet_packet_destroy( event->packet );
257    return 0;
258  }
[7565]259
[7611]260
[7590]261  /**** MAIN ROUTINE *****/
262  int 
263  MasterServer::run()
264  {
265    /***** ENTER MAIN LOOP *****/
266    ENetEvent *event = (ENetEvent *)calloc(sizeof(ENetEvent), sizeof(char));
267    if( event == NULL )
[7611]268    { 
269      COUT(1) << "Could not create ENetEvent structure, exiting.\n";
[7590]270      exit( EXIT_FAILURE );
271    }
[7565]272
[8204]273    /* check for timed out peers and remove those from * the server list */
[8163]274    helper_cleanupServers();
275
276
[7743]277    /* create an iterator for the loop */
278    enet_host_service( this->server, event, 100 );
[7611]279
[7743]280    /* check what type of event it is and react accordingly */
281    switch (event->type)
282    { /* new connection */
283      case ENET_EVENT_TYPE_CONNECT: 
284        eventConnect( event ); break;
[7565]285
[7743]286        /* disconnect */
287      case ENET_EVENT_TYPE_DISCONNECT: 
288        eventDisconnect( event ); break;
289
290        /* incoming data */
291      case ENET_EVENT_TYPE_RECEIVE: eventData( event ); break;
292      default: break;
[7590]293    }
[7565]294
[7590]295    /* done */
296    return 0;
297  } 
[7565]298
[7590]299  /* constructor */
300  MasterServer::MasterServer()
301  {
302    /***** INITIALIZE NETWORKING *****/
303    if( enet_initialize () != 0)
[7611]304    { COUT(1) << "An error occurred while initializing ENet.\n";
[7590]305      exit( EXIT_FAILURE );
306    }
[7565]307
[7590]308    /* register deinitialization */
309    atexit( enet_deinitialize );
[7565]310
[7729]311    /* set the quit flag to false */
312    this->quit = false;
313
[7590]314    /* Bind the server to the default localhost and port ORX_MSERVER_PORT */
315    this->address.host = ENET_HOST_ANY;
316    this->address.port = ORX_MSERVER_PORT;
[7589]317
[7590]318    /* create a host with the above settings (the last two 0 mean: accept
319     * any input/output bandwidth */
320    this->server = enet_host_create( &this->address, ORX_MSERVER_MAXCONNS, 
[7801]321        ORX_MSERVER_MAXCHANS, 0, 0 );
322    assert(this->server);
[7590]323
324    /* see if creation worked */
325    if( !this->server )
[7611]326    { COUT(1) << 
327        "An error occurred while trying to create an ENet server host.\n";
328      exit( EXIT_FAILURE );
[7590]329    }
[7565]330
[7611]331    /***** INITIALIZE GAME SERVER AND PEER LISTS *****/
332    this->peers = new PeerList();
[7743]333
334    /* tell people we're now initialized */
335    COUT(0) << "MasterServer initialized, waiting for connections.\n";
[7565]336  }
337
[7590]338  /* destructor */
339  MasterServer::~MasterServer()
340  {
341    /***** CLEANUP PROCESS *****/
342    /* terminate all networking connections */
343    enet_host_destroy( this->server );
[7565]344
[7611]345    /* free all used memory */
[7590]346    /* clear the list of connected game servers */
347    /* clear the list of connected game clients */
348  }
349
350/* end of namespace */
351}
Note: See TracBrowser for help on using the repository browser.