Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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