Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 12211 was 12027, checked in by merholzl, 6 years ago

Merged Masterserver, refresh button had to be removed

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