Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9700 was 8952, checked in by dafrick, 13 years ago

Removing some unused variables and taking care of some other warnings (NULL as argument to non-pointer and depricated use of COUT).

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