Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8241 was 8241, checked in by smerkli, 15 years ago

Generic witty commit comment

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