Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/libraries/network/MasterServer.cc @ 11034

Last change on this file since 11034 was 10919, checked in by landauf, 9 years ago

use range-based for-loop where it makes sense (e.g. ObjectList)

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