Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8242 was 8242, checked in by smerkli, 13 years ago

Implemented a list servers command for the masterserver, more commands to come next week.

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