Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/network/MasterServer.cc @ 10362

Last change on this file since 10362 was 10347, checked in by landauf, 9 years ago

moved console command macros to a new file (ConsoleCommandIncludes.h)

  • Property svn:eol-style set to native
File size: 12.3 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"
[10347]31#include "core/command/ConsoleCommandIncludes.h"
[7590]32#include "core/CoreIncludes.h"
33#include "core/CorePrereqs.h"
[8937]34#include "util/Output.h"
[7565]35
[7590]36namespace orxonox
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
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 */ 
[8952]89    enet_peer_disconnect( shandle.result.peer, 0 );
[8937]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
[7684]99  /* helpers */
100  static void 
101  helper_output_debug( ENetEvent *event, char *addrconv )
102  {
[8858]103    orxout(verbose, context::master_server)
104      << "A packet of length" 
[7684]105      << event->packet->dataLength
106      << " containing "
107      << (const char*)event->packet->data
108      << " was received from "
109      << addrconv
110      << " on channel "
[8858]111      << event->channelID << endl;
[7684]112  }
113
114  void
115  MasterServer::helper_sendlist( ENetEvent *event )
116  {
117    /* get an iterator */
[8937]118    std::list<ServerListElem>::iterator i;
[7684]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 */
[8937]129      char *tosend = (char *)calloc( (*i).ServerInfo.getServerIP().length() 
[7684]130          + MSPROTO_SERVERLIST_ITEM_LEN + 2,1 );
131      if( !tosend ) 
[8858]132      { orxout(internal_warning, context::master_server) << "Masterserver.cc: Memory allocation failed." << endl;
[7684]133        continue;
134      } 
135      sprintf( tosend, "%s %s", MSPROTO_SERVERLIST_ITEM, 
[8937]136          (*i).ServerInfo.getServerIP().c_str() );
[7684]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
[7750]153    /* create end-of-list packet */
[7684]154    reply = enet_packet_create( MSPROTO_SERVERLIST_END,
155        MSPROTO_SERVERLIST_END_LEN + 1,
156        ENET_PACKET_FLAG_RELIABLE );
157
[7750]158    /* send end-of-list packet */
[7684]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
[8937]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;
[7684]177
[8937]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() );
[7684]189
[8937]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  }
[7684]200
[8937]201
202
203
[7590]204  /***** EVENTS *****/
205  /* connect event */
206  int 
[7611]207  MasterServer::eventConnect( ENetEvent *event )
[7590]208  { /* check for bad parameters */
209    if( !event )
[8858]210    { orxout(internal_warning, context::master_server) << "MasterServer::eventConnect: No event given." << endl;
[7590]211      return -1;
212    }
[7565]213
[7611]214    /* convert address to string. */
215    char *addrconv = (char *) calloc( 50, 1 );
216    enet_address_get_host_ip( &(event->peer->address), addrconv, 49 );
217
[7590]218    /* output debug info */
[8858]219    orxout(verbose, context::master_server) << "A new client connected from " 
[7611]220      << addrconv
221      << " on port " 
[8858]222      << event->peer->address.port << endl;
[7565]223
[7611]224    /* store string form of address here */
225    event->peer->data = addrconv; 
226
227    /* all fine. */
[7590]228    return 0;
[7565]229  }
230
[7590]231  /* disconnect event */
232  int 
[7611]233  MasterServer::eventDisconnect( ENetEvent *event )
[7590]234  { /* check for bad parameters */
235    if( !event )
[8858]236    { orxout(internal_warning, context::master_server) << "No event given." << endl;
[7590]237      return -1;
238    }
[7565]239
[7651]240    /* output that the disconnect happened */
[8858]241    orxout(verbose, context::master_server) << (char*)event->peer->data << " disconnected." << endl;
[7565]242
[7651]243    /* create string from peer data */
244    std::string name = std::string( (char*)event->peer->data );
245
[7590]246    /* remove the server from the list it belongs to */
[7657]247    this->mainlist.delServerByName( name );
[7565]248
[7590]249    /* Reset the peer's client information. */
[7611]250    if( event->peer->data ) free( event->peer->data );
[7651]251
252    /* done */
[7590]253    return 0;
[7565]254  }
255
[7590]256  /* data event */
257  int 
[7611]258  MasterServer::eventData( ENetEvent *event )
[7651]259  { /* validate packet */
[7611]260    if( !event || !(event->packet) || !(event->peer) )
[8858]261    { orxout(internal_warning, context::master_server) << "No complete event given." << endl;
[7590]262      return -1;
263    }
[7611]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 );
[7590]268
[7750]269    /* output debug info about the data that has come */
[7684]270    helper_output_debug( event, addrconv );
[7590]271
[7630]272    /* GAME SERVER OR CLIENT CONNECTION? */
[7651]273    if( !strncmp( (char *)event->packet->data, MSPROTO_GAME_SERVER, 
274      MSPROTO_GAME_SERVER_LEN ) )
275    { /* Game server */
[7630]276
[7651]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 */
[8937]281        mainlist.addServer( packet::ServerInformation( event ),
282          event->peer );
[7658]283       
284        /* tell people we did so */
[8858]285        orxout(internal_info, context::master_server) << "Added new server to list: " << 
286          packet::ServerInformation( event ).getServerIP() << endl;
[7651]287      }
[7756]288
[7763]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 */
[7765]297        this->mainlist.delServerByAddress( name );
[7763]298
299        /* tell the user */
[8858]300        orxout(internal_info, context::master_server) << "Removed server " << name << " from list." << endl;
[7763]301      }
302
[7756]303      /* TODO add hook for disconnect here */
[7651]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,
[7657]309        MSPROTO_REQ_LIST, MSPROTO_REQ_LIST_LEN ) )
[7684]310        /* send server list */
311        helper_sendlist( event );
[7651]312    }
313    else
314    { /* bad message, don't do anything. */ } 
315
[7611]316    /* delete addrconv */
317    if( addrconv ) free( addrconv );
318
[7590]319    /* Clean up the packet now that we're done using it. */
320    enet_packet_destroy( event->packet );
321    return 0;
322  }
[7565]323
[7611]324
[7590]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 )
[7611]332    { 
[8858]333      orxout(user_error, context::master_server) << "Could not create ENetEvent structure, exiting." << endl;
[7590]334      exit( EXIT_FAILURE );
335    }
[7565]336
[8937]337    /* check for timed out peers and remove those from * the server list */
338    helper_cleanupServers();
339
340
[7743]341    /* create an iterator for the loop */
342    enet_host_service( this->server, event, 100 );
[7611]343
[7743]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;
[7565]349
[7743]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;
[7590]357    }
[7565]358
[7590]359    /* done */
360    return 0;
361  } 
[7565]362
[7590]363  /* constructor */
364  MasterServer::MasterServer()
365  {
366    /***** INITIALIZE NETWORKING *****/
367    if( enet_initialize () != 0)
[8858]368    { orxout(user_error, context::master_server) << "An error occurred while initializing ENet." << endl;
[7590]369      exit( EXIT_FAILURE );
370    }
[7565]371
[7590]372    /* register deinitialization */
373    atexit( enet_deinitialize );
[7565]374
[7729]375    /* set the quit flag to false */
376    this->quit = false;
377
[7590]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;
[7589]381
[7590]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, 
[7801]385        ORX_MSERVER_MAXCHANS, 0, 0 );
386    assert(this->server);
[7590]387
388    /* see if creation worked */
389    if( !this->server )
[8858]390    { orxout(user_error, context::master_server) << 
391        "An error occurred while trying to create an ENet server host." << endl;
[7611]392      exit( EXIT_FAILURE );
[7590]393    }
[7565]394
[8937]395    /* set pointer to this instance */
396    MasterServer::setInstance( this );
[7743]397
398    /* tell people we're now initialized */
[8858]399    orxout(internal_status, context::master_server) << "MasterServer initialized, waiting for connections." << endl;
[7565]400  }
401
[7590]402  /* destructor */
403  MasterServer::~MasterServer()
404  {
405    /***** CLEANUP PROCESS *****/
406    /* terminate all networking connections */
407    enet_host_destroy( this->server );
[7565]408
[7611]409    /* free all used memory */
[7590]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.