Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/network/Server.cc @ 1574

Last change on this file since 1574 was 1574, checked in by landauf, 16 years ago

big change in ObjectList: separated the class into a non-template base and a template wrapper for the base. this also changes the Iterator, there is now a non-template IteratorBase. this brings much more flexibility, like iterating through all objects of a given identifier without knowing the type. however this needs a dynamic_cast, which isn't quite optimal, but I think there are much worser things than that out there. ;)

there isn't much you have to know about this, except there is no more ObjectList<myClass>::start() function but a ObjectList<myClass>::begin() to be more STLish. another thing: ObjectList<myClass>::end() points now to the element _after_ the last element, so it's possible to iterate in a for-loop until (it != ObjectList<myClass>::end()). the reason is the same as above. however, (it) as a boolean still works perfectly fine.

  • Property svn:eol-style set to native
File size: 13.2 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
45
46#include "ConnectionManager.h"
47#include "PacketTypes.h"
48#include "GameStateManager.h"
49#include "ClientInformation.h"
50//#include "NetworkFrameListener.h"
51#include "util/Sleep.h"
52#include "objects/SpaceShip.h"
53#include "core/ConsoleCommand.h"
54
55namespace network
56{
57  #define MAX_FAILURES 20;
58  #define NETWORK_FREQUENCY 30
59
60  Server *Server::instance_=0;
61
62  Server *Server::createSingleton(){
63    if(!instance_)
64      instance_ = new Server();
65    return instance_;
66  }
67  Server *Server::createSingleton(int port){
68    if(!instance_)
69      instance_ = new Server(port);
70    return instance_;
71  }
72  Server *Server::createSingleton(int port, std::string bindAddress){
73    if(!instance_)
74      instance_ = new Server(port, bindAddress);
75    return instance_;
76  }
77  Server *Server::createSingleton(int port, const char *bindAddress){
78    if(!instance_)
79      instance_ = new Server(port, bindAddress);
80    return instance_;
81  }
82
83  Server *Server::getSingleton(){
84    return instance_;
85  }
86
87
88  /**
89  * Constructor for default values (bindaddress is set to ENET_HOST_ANY
90  *
91  */
92  Server::Server() {
93    timeSinceLastUpdate_=0;
94    packet_gen = PacketGenerator();
95    clients = new ClientInformation(true);
96    connection = new ConnectionManager(clients);
97    gamestates = new GameStateManager(clients);
98  }
99
100  Server::Server(int port){
101    timeSinceLastUpdate_=0;
102    packet_gen = PacketGenerator();
103    clients = new ClientInformation(true);
104    connection = new ConnectionManager(clients, port);
105    gamestates = new GameStateManager(clients);
106  }
107
108  /**
109  * Constructor
110  * @param port Port to listen on
111  * @param bindAddress Address to listen on
112  */
113  Server::Server(int port, std::string bindAddress) {
114    timeSinceLastUpdate_=0;
115    packet_gen = PacketGenerator();
116    clients = new ClientInformation();
117    connection = new ConnectionManager(port, bindAddress, clients);
118    gamestates = new GameStateManager(clients);
119  }
120
121  /**
122  * Constructor
123  * @param port Port to listen on
124  * @param bindAddress Address to listen on
125  */
126  Server::Server(int port, const char *bindAddress) {
127    timeSinceLastUpdate_=0;
128    packet_gen = PacketGenerator();
129    clients = new ClientInformation();
130    connection = new ConnectionManager(port, bindAddress, clients);
131    gamestates = new GameStateManager(clients);
132  }
133
134  /**
135  * This function opens the server by creating the listener thread
136  */
137  void Server::open() {
138    connection->createListener();
139    return;
140  }
141
142  /**
143  * This function closes the server
144  */
145  void Server::close() {
146    connection->quitListener();
147    return;
148  }
149
150  /**
151  * This function sends out a message to all clients
152  * @param msg message
153  * @return true/false
154  */
155  bool Server::sendChat(std::string msg) {
156    return sendChat(msg.c_str());
157  }
158
159  /**
160  * This function sends out a message to all clients
161  * @param msg message
162  * @return true/false
163  */
164  bool Server::sendChat(const char *msg) {
165    char *message = new char [strlen(msg)+10+1];
166    sprintf(message, "Player %d: %s", CLIENTID_SERVER, msg);
167    COUT(1) << message << std::endl;
168    ENetPacket *packet = packet_gen.chatMessage(message);
169    COUT(5) <<"Server: adding Packets" << std::endl;
170    return connection->addPacketAll(packet);
171  }
172
173  /**
174  * Run this function once every tick
175  * calls processQueue and updateGamestate
176  * @param time time since last tick
177  */
178  void Server::tick(float time) {
179    processQueue();
180    //this steers our network frequency
181    timeSinceLastUpdate_+=time;
182    if(timeSinceLastUpdate_>=(1./NETWORK_FREQUENCY)){
183      timeSinceLastUpdate_=(float)((int)(timeSinceLastUpdate_*NETWORK_FREQUENCY))/timeSinceLastUpdate_;
184//      timeSinceLastUpdate_-=1./NETWORK_FREQUENCY;
185      gamestates->processGameStates();
186      updateGamestate();
187    }
188    /*while(timeSinceLastUpdate_>1./NETWORK_FREQUENCY)
189      timeSinceLastUpdate_-=1./NETWORK_FREQUENCY;*/
190//     usleep(5000); // TODO remove
191    return;
192  }
193
194  /**
195  * processes all the packets waiting in the queue
196  */
197  void Server::processQueue() {
198    ENetEvent *event;
199    int clientID=-1;
200    while(!connection->queueEmpty()){
201      //std::cout << "Client " << clientID << " sent: " << std::endl;
202      //clientID here is a reference to grab clientID from ClientInformation
203      event = connection->getEvent();
204      if(!event)
205        continue;
206      assert(event->type != ENET_EVENT_TYPE_NONE);
207      switch( event->type ) {
208      case ENET_EVENT_TYPE_CONNECT:
209        COUT(3) << "processing event_Type_connect" << std::endl;
210        addClient(event);
211        break;
212      case ENET_EVENT_TYPE_DISCONNECT:
213        if(clients->findClient(&event->peer->address))
214          disconnectClient(event);
215        break;
216      case ENET_EVENT_TYPE_RECEIVE:
217        if(clients->findClient(&event->peer->address)){
218          clientID = clients->findClient(&event->peer->address)->getID();
219          if( !elaborate(event->packet, clientID) )
220            COUT(3) << "Server: could not elaborate" << std::endl;
221        }
222        break;
223      default:
224        break;
225      }
226      delete event;
227      //if statement to catch case that packetbuffer is empty
228    }
229  }
230
231  /**
232  * takes a new snapshot of the gamestate and sends it to the clients
233  */
234  void Server::updateGamestate() {
235    gamestates->update();
236    COUT(5) << "Server: one gamestate update complete, goig to sendGameState" << std::endl;
237    //std::cout << "updated gamestate, sending it" << std::endl;
238    //if(clients->getGamestateID()!=GAMESTATEID_INITIAL)
239    sendGameState();
240    COUT(5) << "Server: one sendGameState turn complete, repeat in next tick" << std::endl;
241    //std::cout << "sent gamestate" << std::endl;
242  }
243
244  /**
245  * sends the gamestate
246  */
247  bool Server::sendGameState() {
248    COUT(5) << "Server: starting function sendGameState" << std::endl;
249    ClientInformation *temp = clients;
250    bool added=false;
251    while(temp != NULL){
252      if(temp->getHead()){
253        temp=temp->next();
254        //think this works without continue
255        continue;
256      }
257      if( !(temp->getSynched()) ){
258        COUT(5) << "Server: not sending gamestate" << std::endl;
259        temp=temp->next();
260        //think this works without continue
261        continue;
262      }
263      COUT(4) << "client id: " << temp->getID() << " RTT: " << temp->getRTT() << " loss: " << temp->getPacketLoss() << std::endl;
264      COUT(5) << "Server: doing gamestate gamestate preparation" << std::endl;
265      int gid = temp->getGamestateID(); //get gamestate id
266      int cid = temp->getID(); //get client id
267      COUT(5) << "Server: got acked (gamestate) ID from clientlist: " << gid << std::endl;
268      GameStateCompressed *gs = gamestates->popGameState(cid);
269      if(gs==NULL){
270        COUT(2) << "Server: could not generate gamestate (NULL from compress)" << std::endl;
271        continue;
272      }
273      //std::cout << "adding gamestate" << std::endl;
274      ENetPacket *packet = packet_gen.gstate(gs);
275      if(!packet)
276        continue;
277      if ( !(connection->addPacket(packet, cid)) ){
278        COUT(3) << "Server: packet with client id (cid): " << cid << " not sended: " << temp->getFailures() << std::endl;
279        temp->addFailure();
280        /*if(temp->getFailures() > 0 )
281          disconnectClient(temp);*/
282      //std::cout << "added gamestate" << std::endl;
283      }else
284        temp->resetFailures();
285      added=true;
286      temp=temp->next();
287      // now delete gamestate
288      delete[] gs->data;
289      delete gs;
290    }
291    /*if(added) {
292      //std::cout << "send gamestates from server.cc in sendGameState" << std::endl;
293      return connection->sendPackets();
294    }*/
295    //COUT(5) << "Server: had no gamestates to send" << std::endl;
296    return true;
297  }
298
299  void Server::processAck( ack *data, int clientID) {
300    COUT(4) << "Server: processing ack from client: " << clientID << "; ack-id: " << data->a << std::endl;
301    gamestates->ackGameState(clientID, data->a);
302    delete data;
303  }
304
305  bool Server::processConnectRequest( connectRequest *con, int clientID ){
306    //(COUT(3) << "processing connectRequest " << std::endl;
307    //connection->addPacket(packet_gen.gstate(gamestates->popGameState(clientID)) , clientID);
308    //createClient(clientID);
309    delete con;
310    return true;
311  }
312
313  void Server::processGamestate( GameStateCompressed *data, int clientID){
314    COUT(4) << "processing partial gamestate from client " << clientID << std::endl;
315    gamestates->addGameState(data, clientID);
316        /*COUT(3) << "Could not push gamestate\t\t\t\t=====" << std::endl;
317    else
318      if(clients->findClient(clientID))
319        clients->findClient(clientID)->resetFailures();*/
320  }
321
322  void Server::processChat( chat *data, int clientId){
323    char *message = new char [strlen(data->message)+10+1];
324    sprintf(message, "Player %d: %s", clientId, data->message);
325    COUT(1) << message << std::endl;
326    ENetPacket *pck = packet_gen.chatMessage(message);
327    connection->addPacketAll(pck);
328    delete[] data->message;
329    delete data;
330  }
331
332  bool Server::addClient(ENetEvent *event){
333    ClientInformation *temp = clients->insertBack(new ClientInformation);
334    if(!temp){
335      COUT(2) << "Server: could not add client" << std::endl;
336      return false;
337    }
338    if(temp->prev()->getHead()) { //not good if you use anything else than insertBack
339      temp->prev()->setID(0); //bugfix: not necessary but usefull
340      temp->setID(1);
341    }
342    else
343      temp->setID(temp->prev()->getID()+1);
344    temp->setPeer(event->peer);
345    COUT(3) << "Server: added client id: " << temp->getID() << std::endl;
346    return createClient(temp->getID());
347  }
348
349  bool Server::createClient(int clientID){
350    ClientInformation *temp = clients->findClient(clientID);
351    if(!temp){
352      COUT(2) << "Conn.Man. could not create client with id: " << clientID << std::endl;
353      return false;
354    }
355    COUT(4) << "Con.Man: creating client id: " << temp->getID() << std::endl;
356    connection->syncClassid(temp->getID());
357    COUT(5) << "creating spaceship for clientid: " << temp->getID() << std::endl;
358    // TODO: this is only a hack, untill we have a possibility to define default player-join actions
359    if(!createShip(temp))
360      COUT(2) << "Con.Man. could not create ship for clientid: " << clientID << std::endl;
361    else
362      COUT(3) << "created spaceship" << std::endl;
363    temp->setSynched(true);
364    COUT(3) << "sending welcome" << std::endl;
365    connection->sendWelcome(temp->getID(), temp->getShipID(), true);
366    return true;
367  }
368
369  bool Server::createShip(ClientInformation *client){
370    if(!client)
371      return false;
372    orxonox::Identifier* id = ID("SpaceShip");
373    if(!id){
374      COUT(4) << "We could not create the SpaceShip for client: " << client->getID() << std::endl;
375      return false;
376    }
377    orxonox::SpaceShip *no = dynamic_cast<orxonox::SpaceShip *>(id->fabricate());
378    no->setPosition(orxonox::Vector3(0,0,80));
379    no->setScale(10);
380    //no->setYawPitchRoll(orxonox::Degree(-90),orxonox::Degree(-90),orxonox::Degree(0));
381    no->setMesh("assff.mesh");
382    no->setMaxSpeed(500);
383    no->setMaxSideAndBackSpeed(50);
384    no->setMaxRotation(1.0);
385    no->setTransAcc(200);
386    no->setRotAcc(3.0);
387    no->setTransDamp(75);
388    no->setRotDamp(1.0);
389    no->setCamera("cam_"+client->getID());
390    no->classID = id->getNetworkID();
391    no->create();
392
393    client->setShipID(no->objectID);
394    return true;
395  }
396
397  bool Server::disconnectClient(ENetEvent *event){
398    COUT(4) << "removing client from list" << std::endl;
399    //return removeClient(head_->findClient(&(peer->address))->getID());
400
401    //boost::recursive_mutex::scoped_lock lock(head_->mutex_);
402    orxonox::Iterator<orxonox::SpaceShip> it = orxonox::ObjectList<orxonox::SpaceShip>::begin();
403    ClientInformation *client = clients->findClient(&event->peer->address);
404    if(!client)
405      return false;
406    while(it){
407      if(it->objectID!=client->getShipID()){
408        ++it;
409        continue;
410      }
411      orxonox::Iterator<orxonox::SpaceShip> temp=it;
412      ++it;
413      delete  *temp;
414      return clients->removeClient(event->peer);
415    }
416    return false;
417  }
418
419  void Server::disconnectClient(int clientID){
420    ClientInformation *client = clients->findClient(clientID);
421    if(client)
422      disconnectClient(client);
423  }
424  void Server::disconnectClient( ClientInformation *client){
425    connection->disconnectClient(client);
426    gamestates->removeClient(client);
427  }
428
429}
Note: See TracBrowser for help on using the repository browser.