Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8474 was 8280, checked in by smerkli, 14 years ago

ms-delserver command implemented to kick servers from the master server list

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