Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/network/Server.cc @ 2009

Last change on this file since 2009 was 2009, checked in by scheusso, 16 years ago

server is now able to deliver ping time and packet loss ratio for all its clients

  • Property svn:eol-style set to native
File size: 12.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 *      Oliver Scheuss, (C) 2007
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29//
30// C++ Implementation: Server
31//
32// Description:
33//
34//
35// Author:  Oliver Scheuss, (C) 2007
36//
37// Copyright: See COPYING file that comes with this distribution
38//
39//
40
41#include "Server.h"
42
43#include <iostream>
44#include <cassert>
45
46
47#include "ConnectionManager.h"
48#include "ClientConnectionListener.h"
49#include "GamestateManager.h"
50#include "ClientInformation.h"
51#include "util/Sleep.h"
52#include "core/ConsoleCommand.h"
53#include "core/CoreIncludes.h"
54#include "core/Iterator.h"
55#include "packet/Chat.h"
56#include "packet/Packet.h"
57#include "packet/Welcome.h"
58#include "packet/DeleteObjects.h"
59#include <util/Convert.h>
60#include "ChatListener.h"
61
62namespace network
63{
64  const int MAX_FAILURES = 20;
65  const int NETWORK_FREQUENCY = 30;
66
67  /**
68  * Constructor for default values (bindaddress is set to ENET_HOST_ANY
69  *
70  */
71  Server::Server() {
72    timeSinceLastUpdate_=0;
73    connection = new ConnectionManager();
74    gamestates_ = new GamestateManager();
75  }
76
77  Server::Server(int port){
78    timeSinceLastUpdate_=0;
79    connection = new ConnectionManager(port);
80    gamestates_ = new GamestateManager();
81  }
82
83  /**
84  * Constructor
85  * @param port Port to listen on
86  * @param bindAddress Address to listen on
87  */
88  Server::Server(int port, const std::string& bindAddress) {
89    timeSinceLastUpdate_=0;
90    connection = new ConnectionManager(port, bindAddress);
91    gamestates_ = new GamestateManager();
92  }
93
94  /**
95  * Constructor
96  * @param port Port to listen on
97  * @param bindAddress Address to listen on
98  */
99  Server::Server(int port, const char *bindAddress) {
100    timeSinceLastUpdate_=0;
101    connection = new ConnectionManager(port, bindAddress);
102    gamestates_ = new GamestateManager();
103  }
104
105  /**
106  * @brief Destructor
107  */
108  Server::~Server(){
109    if(connection)
110      delete connection;
111    if(gamestates_)
112      delete gamestates_;
113  }
114
115  /**
116  * This function opens the server by creating the listener thread
117  */
118  void Server::open() {
119    connection->createListener();
120    return;
121  }
122
123  /**
124  * This function closes the server
125  */
126  void Server::close() {
127    connection->quitListener();
128    return;
129  }
130
131  bool Server::processChat(const std::string& message, unsigned int playerID){
132    ClientInformation *temp = ClientInformation::getBegin();
133    packet::Chat *chat;
134    while(temp){
135      chat = new packet::Chat(message, playerID);
136      chat->setClientID(temp->getID());
137      if(!chat->send())
138        COUT(3) << "could not send Chat message to client ID: " << temp->getID() << std::endl;
139      temp = temp->next();
140    }
141//    COUT(1) << "Player " << playerID << ": " << message << std::endl;
142    return true;
143  }
144
145
146  /**
147  * Run this function once every tick
148  * calls processQueue and updateGamestate
149  * @param time time since last tick
150  */
151  void Server::tick(float time) {
152    processQueue();
153    //this steers our network frequency
154    timeSinceLastUpdate_+=time;
155    if(timeSinceLastUpdate_>=(1./NETWORK_FREQUENCY)){
156      timeSinceLastUpdate_=(float)((int)(timeSinceLastUpdate_*NETWORK_FREQUENCY))/timeSinceLastUpdate_;
157      gamestates_->processGamestates();
158      updateGamestate();
159    }
160  }
161
162  bool Server::queuePacket(ENetPacket *packet, int clientID){
163    return connection->addPacket(packet, clientID);
164  }
165 
166  /**
167   * @brief: returns ping time to client in milliseconds
168   */
169  unsigned int Server::getPing(unsigned int clientID){
170    assert(ClientInformation::findClient(clientID));
171    return ClientInformation::findClient(clientID)->getRTT();
172  }
173
174  /**
175   * @brief: return packet loss ratio to client (scales from 0 to 1)
176   */
177  double Server::getPacketLoss(unsigned int clientID){
178    assert(ClientInformation::findClient(clientID));
179    return ClientInformation::findClient(clientID)->getPacketLoss();
180  }
181 
182  /**
183  * processes all the packets waiting in the queue
184  */
185  void Server::processQueue() {
186    ENetEvent *event;
187    while(!connection->queueEmpty()){
188      //std::cout << "Client " << clientID << " sent: " << std::endl;
189      //clientID here is a reference to grab clientID from ClientInformation
190      event = connection->getEvent();
191      if(!event)
192        continue;
193      assert(event->type != ENET_EVENT_TYPE_NONE);
194      switch( event->type ) {
195      case ENET_EVENT_TYPE_CONNECT:
196        COUT(3) << "processing event_Type_connect" << std::endl;
197        addClient(event);
198        break;
199      case ENET_EVENT_TYPE_DISCONNECT:
200        if(ClientInformation::findClient(&event->peer->address))
201          disconnectClient(event);
202        break;
203      case ENET_EVENT_TYPE_RECEIVE:
204        if(!processPacket(event->packet, event->peer))
205          COUT(3) << "processing incoming packet failed" << std::endl;
206        break;
207      default:
208        break;
209      }
210      delete event;
211      //if statement to catch case that packetbuffer is empty
212    }
213  }
214
215  /**
216  * takes a new snapshot of the gamestate and sends it to the clients
217  */
218  void Server::updateGamestate() {
219    gamestates_->update();
220    COUT(5) << "Server: one gamestate update complete, goig to sendGameState" << std::endl;
221    //std::cout << "updated gamestate, sending it" << std::endl;
222    //if(clients->getGamestateID()!=GAMESTATEID_INITIAL)
223    sendGameState();
224    sendObjectDeletes();
225    COUT(5) << "Server: one sendGameState turn complete, repeat in next tick" << std::endl;
226    //std::cout << "sent gamestate" << std::endl;
227  }
228
229  bool Server::processPacket( ENetPacket *packet, ENetPeer *peer ){
230    packet::Packet *p = packet::Packet::createPacket(packet, peer);
231    return p->process();
232  }
233
234  /**
235  * sends the gamestate
236  */
237  bool Server::sendGameState() {
238    COUT(5) << "Server: starting function sendGameState" << std::endl;
239    ClientInformation *temp = ClientInformation::getBegin();
240    bool added=false;
241    while(temp != NULL){
242      if( !(temp->getSynched()) ){
243        COUT(5) << "Server: not sending gamestate" << std::endl;
244        temp=temp->next();
245        if(!temp)
246          break;
247        //think this works without continue
248        continue;
249      }
250      COUT(4) << "client id: " << temp->getID() << " RTT: " << temp->getRTT() << " loss: " << temp->getPacketLoss() << std::endl;
251      COUT(5) << "Server: doing gamestate gamestate preparation" << std::endl;
252      int gid = temp->getGamestateID(); //get gamestate id
253      int cid = temp->getID(); //get client id
254      COUT(5) << "Server: got acked (gamestate) ID from clientlist: " << gid << std::endl;
255      packet::Gamestate *gs = gamestates_->popGameState(cid);
256      if(gs==NULL){
257        COUT(2) << "Server: could not generate gamestate (NULL from compress)" << std::endl;
258        temp = temp->next();
259        continue;
260      }
261      //std::cout << "adding gamestate" << std::endl;
262      gs->setClientID(cid);
263      if ( !gs->send() ){
264        COUT(3) << "Server: packet with client id (cid): " << cid << " not sended: " << temp->getFailures() << std::endl;
265        temp->addFailure();
266      }else
267        temp->resetFailures();
268      added=true;
269      temp=temp->next();
270      // gs gets automatically deleted by enet callback
271    }
272    return true;
273  }
274
275  bool Server::sendObjectDeletes(){
276    ClientInformation *temp = ClientInformation::getBegin();
277    packet::DeleteObjects *del = new packet::DeleteObjects();
278    if(!del->fetchIDs())
279      return true;  //everything ok (no deletes this tick)
280//     COUT(3) << "sending DeleteObjects" << std::endl;
281    while(temp != NULL){
282      if( !(temp->getSynched()) ){
283        COUT(5) << "Server: not sending gamestate" << std::endl;
284        temp=temp->next();
285        continue;
286      }
287      int cid = temp->getID(); //get client id
288      packet::DeleteObjects *cd = new packet::DeleteObjects(*del);
289      assert(cd);
290      cd->setClientID(cid);
291      if ( !cd->send() )
292        COUT(3) << "Server: packet with client id (cid): " << cid << " not sended: " << temp->getFailures() << std::endl;
293      temp=temp->next();
294      // gs gets automatically deleted by enet callback
295    }
296    return true;
297  }
298
299
300  bool Server::addClient(ENetEvent *event){
301    static unsigned int newid=1;
302
303    COUT(2) << "Server: adding client" << std::endl;
304    ClientInformation *temp = ClientInformation::insertBack(new ClientInformation);
305    if(!temp){
306      COUT(2) << "Server: could not add client" << std::endl;
307      return false;
308    }
309    /*if(temp==ClientInformation::getBegin()) { //not good if you use anything else than insertBack
310      newid=1;
311    }
312    else
313      newid=temp->prev()->getID()+1;*/
314    temp->setID(newid);
315    temp->setPeer(event->peer);
316
317    // inform all the listeners
318    orxonox::ObjectList<ClientConnectionListener>::iterator listener = orxonox::ObjectList<ClientConnectionListener>::begin();
319    while(listener){
320      listener->clientConnected(newid);
321      listener++;
322    }
323
324    newid++;
325
326    COUT(3) << "Server: added client id: " << temp->getID() << std::endl;
327    return createClient(temp->getID());
328}
329
330  bool Server::createClient(int clientID){
331    ClientInformation *temp = ClientInformation::findClient(clientID);
332    if(!temp){
333      COUT(2) << "Conn.Man. could not create client with id: " << clientID << std::endl;
334      return false;
335    }
336    COUT(4) << "Con.Man: creating client id: " << temp->getID() << std::endl;
337    connection->syncClassid(temp->getID());
338    temp->setSynched(true);
339    COUT(3) << "sending welcome" << std::endl;
340    packet::Welcome *w = new packet::Welcome(temp->getID(), temp->getShipID());
341    w->setClientID(temp->getID());
342    bool b = w->send();
343    assert(b);
344    packet::Gamestate *g = new packet::Gamestate();
345    g->setClientID(temp->getID());
346    b = g->collectData(0);
347    if(!b)
348      return false; //no data for the client
349    b = g->compressData();
350    assert(b);
351    b = g->send();
352    assert(b);
353    return true;
354  }
355
356  bool Server::disconnectClient(ENetEvent *event){
357    COUT(4) << "removing client from list" << std::endl;
358    //return removeClient(head_->findClient(&(peer->address))->getID());
359
360    //boost::recursive_mutex::scoped_lock lock(head_->mutex_);
361    ClientInformation *client = ClientInformation::findClient(&event->peer->address);
362    if(!client)
363      return false;
364    gamestates_->removeClient(client);
365
366// inform all the listeners
367    orxonox::ObjectList<ClientConnectionListener>::iterator listener = orxonox::ObjectList<ClientConnectionListener>::begin();
368    while(listener){
369      listener->clientDisconnected(client->getID());
370      listener++;
371    }
372
373    return ClientInformation::removeClient(event->peer);
374  }
375
376  void Server::disconnectClient(int clientID){
377    ClientInformation *client = ClientInformation::findClient(clientID);
378    if(client)
379      disconnectClient(client);
380  }
381  void Server::disconnectClient( ClientInformation *client){
382    connection->disconnectClient(client);
383    gamestates_->removeClient(client);
384  }
385
386  bool Server::chat(const std::string& message){
387      return this->sendChat(message, Host::getPlayerID());
388  }
389
390  bool Server::broadcast(const std::string& message){
391      return this->sendChat(message, CLIENTID_UNKNOWN);
392  }
393
394  bool Server::sendChat(const std::string& message, unsigned int clientID){
395    ClientInformation *temp = ClientInformation::getBegin();
396    packet::Chat *chat;
397    while(temp){
398      chat = new packet::Chat(message, clientID);
399      chat->setClientID(temp->getID());
400      if(!chat->send())
401        COUT(3) << "could not send Chat message to client ID: " << temp->getID() << std::endl;
402      temp = temp->next();
403    }
404//    COUT(1) << "Player " << Host::getPlayerID() << ": " << message << std::endl;
405    for (orxonox::ObjectList<ChatListener>::iterator it = orxonox::ObjectList<ChatListener>::begin(); it != orxonox::ObjectList<ChatListener>::end(); ++it)
406      it->incomingChat(message, clientID);
407
408    return true;
409  }
410
411}
Note: See TracBrowser for help on using the repository browser.