Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/Masterserver_FS18/src/libraries/network/Connection.cc @ 11983

Last change on this file since 11983 was 11880, checked in by mdedial, 7 years ago

Replace assert by log message.

  • Property svn:eol-style set to native
File size: 12.0 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
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Connection.h"
30
31#include <cassert>
32#include <deque>
33#define WIN32_LEAN_AND_MEAN
34#include <enet/enet.h>
35#include <boost/thread.hpp>
36#include <boost/thread/mutex.hpp>
37#include <boost/date_time.hpp>
38
39#include "packet/Packet.h"
40#include "util/Output.h"
41#include <util/Sleep.h>
42
43namespace orxonox
44{
45  const boost::posix_time::millisec NETWORK_COMMUNICATION_THREAD_WAIT_TIME(200);
46  const unsigned int                NETWORK_DISCONNECT_TIMEOUT = 500;
47
48  /**
49   * Constructor
50   * @param firstPeerId The initial value of nextPeerID_
51   */
52  Connection::Connection(uint32_t firstPeerID):
53    host_(nullptr), bCommunicationThreadRunning_(false), nextPeerID_(firstPeerID)
54  {
55    // Global initialization of ENet
56    enet_initialize();
57
58    // Register enet_deinitialize to be executed when the program exits normally
59    atexit(enet_deinitialize);
60
61    // Create mutexes for incoming and outgoing events
62    this->incomingEventsMutex_ = new boost::mutex;
63    this->outgoingEventsMutex_ = new boost::mutex;
64  }
65
66  /**
67   * Destructor
68   */
69  Connection::~Connection()
70  {
71    // Delete the mutexes
72    delete this->incomingEventsMutex_;
73    delete this->outgoingEventsMutex_;
74
75    // TODO: Why is enet_deinitialize() not called here?
76    // Would make sense, since its counterpart, enet_initialize(), is called in the constructor.
77  }
78
79  /**
80   * Start the main communication thread.
81   */
82  void Connection::startCommunicationThread()
83  {
84    this->bCommunicationThreadRunning_ = true;
85    this->communicationThread_ = new boost::thread(&Connection::communicationThread, this);
86  }
87 
88  /**
89   * Stop the main communication thread.
90   */
91  void Connection::stopCommunicationThread()
92  {
93    this->bCommunicationThreadRunning_ = false;
94    // Wait for peaceful termination
95    if(!this->communicationThread_->timed_join(NETWORK_COMMUNICATION_THREAD_WAIT_TIME))
96    {
97      // Force thread to stop if the waiting time runs out.
98      this->communicationThread_->interrupt();
99    }
100    delete this->communicationThread_;
101  }
102
103  /**
104   * Send an outgoing event of type 'disconnectPeer'.
105   * @param peerID The peer to which the event is sent
106   */
107  void Connection::disconnectPeer(uint32_t peerID)
108  {
109    outgoingEvent outEvent = { peerID, OutgoingEventType::disconnectPeer, nullptr, 0 };
110   
111    this->outgoingEventsMutex_->lock();
112    this->outgoingEvents_.push_back(outEvent);
113    this->outgoingEventsMutex_->unlock();
114  }
115
116  /**
117   * Send an outgoing event of type 'disconnectPeers'.
118   */
119  void Connection::disconnectPeers()
120  {
121    outgoingEvent outEvent = { 0, OutgoingEventType::disconnectPeers, nullptr, 0 };
122   
123    this->outgoingEventsMutex_->lock();
124    this->outgoingEvents_.push_back(outEvent);
125    this->outgoingEventsMutex_->unlock();
126  }
127
128  /**
129   * Send a packet.
130   * @param packet Pointer to the packet to send
131   * @param peerID The peer to which the event is sent
132   * @param channelId The channel ID
133   */
134  void Connection::addPacket(ENetPacket* packet, uint32_t peerID, uint8_t channelID)
135  {
136    outgoingEvent outEvent = { peerID, OutgoingEventType::sendPacket, packet, channelID };
137   
138    this->outgoingEventsMutex_->lock();
139    this->outgoingEvents_.push_back(outEvent);
140    this->outgoingEventsMutex_->unlock();
141  }
142 
143  /**
144   * Send a broadcast packet.
145   * @param packet Pointer to the packet to send
146   * @param channelId The channel ID
147   */
148  void Connection::broadcastPacket(ENetPacket* packet, uint8_t channelID)
149  {
150    outgoingEvent outEvent = { 0, OutgoingEventType::broadcastPacket, packet, channelID };
151   
152    this->outgoingEventsMutex_->lock();
153    this->outgoingEvents_.push_back(outEvent);
154    this->outgoingEventsMutex_->unlock();
155  }
156
157 
158  /**
159   * Main communication thread
160   */
161  void Connection::communicationThread()
162  {
163    ENetEvent event;
164   
165    while(this->bCommunicationThreadRunning_)
166    {
167      // Receive all pending incoming Events (such as packets, connects and disconnects)
168      while(enet_host_check_events(this->host_, &event ) > 0)
169      {
170        this->processIncomingEvent(event);
171      }
172     
173      // Sleep for 1ms
174      // TODO: Why?
175      msleep(1);
176     
177      // Send all waiting outgoing packets
178      // TODO: Why do we need a mutex to read a single variable?
179      this->outgoingEventsMutex_->lock();
180      uint32_t outgoingEventsCount = this->outgoingEvents_.size();
181      this->outgoingEventsMutex_->unlock();
182
183      // TODO: Not convinced about how mutexes are used here, seems kinda pointless
184      while(outgoingEventsCount > 0)
185      {
186        this->outgoingEventsMutex_->lock();
187        outgoingEvent outEvent = this->outgoingEvents_.front();
188        this->outgoingEvents_.pop_front();
189        this->outgoingEventsMutex_->unlock();
190       
191        this->processOutgoingEvent(outEvent);
192       
193        this->outgoingEventsMutex_->lock();
194        outgoingEventsCount = this->outgoingEvents_.size();
195        this->outgoingEventsMutex_->unlock();
196      }
197     
198      // Wait for incoming events (at most NETWORK_WAIT_TIMEOUT ms)
199      if(enet_host_service(this->host_, &event, NETWORK_WAIT_TIMEOUT) > 0)
200      {
201        this->processIncomingEvent(event);
202      }
203    }
204  }
205 
206  /**
207   * Handle an incoming event.
208   * @param event The incoming event
209   */
210  void Connection::processIncomingEvent(ENetEvent& event)
211  {
212    incomingEvent inEvent;
213    // preprocess event
214    switch(event.type)
215    {
216      case ENET_EVENT_TYPE_CONNECT:
217        inEvent = preprocessConnectEvent(event);
218        break;
219      case ENET_EVENT_TYPE_RECEIVE:
220        inEvent = preprocessReceiveEvent(event);
221        break;
222      case ENET_EVENT_TYPE_DISCONNECT:
223        inEvent = preprocessDisconnectEvent(event);
224        break;
225      case ENET_EVENT_TYPE_NONE:
226      default:
227        return;
228    }
229   
230    // pushing event to queue
231    this->incomingEventsMutex_->lock();
232    this->incomingEvents_.push_back(inEvent);
233    this->incomingEventsMutex_->unlock();
234  }
235 
236  /**
237   * Send an event.
238   * @param event The event to send
239   */
240  void Connection::processOutgoingEvent(outgoingEvent& event)
241  {
242    ENetPeer* peer;
243    switch(event.type)
244    {
245      case OutgoingEventType::sendPacket:
246        // check whether the peer is still/already in the peer list
247        if(this->peerMap_.find(event.peerID) != this->peerMap_.end())
248        {
249          peer = this->peerMap_[event.peerID];
250          enet_peer_send(peer, event.channelID, event.packet);
251        }
252        else
253        {
254          // peer probably already disconnected so just discard packet
255          orxout(message) << "Trying to send packet to peer that is not in peer list. Ignoring packet." << endl;
256          enet_packet_destroy(event.packet);
257        }
258        break;
259      case OutgoingEventType::disconnectPeer:
260        if(this->peerMap_.find(event.peerID) != this->peerMap_.end())
261        {
262          peer = this->peerMap_[event.peerID];
263          enet_peer_disconnect(peer, 0);
264        }
265        else
266        {
267          // peer probably already disconnected so just discard disconnect event
268          assert(event.peerID < this->nextPeerID_);
269        }
270        break;
271      case OutgoingEventType::disconnectPeers:
272        this->disconnectPeersInternal();
273        break;
274      case OutgoingEventType::broadcastPacket:
275        enet_host_broadcast( this->host_, event.channelID, event.packet );
276        break;
277      default:
278        assert(0);
279    }
280  }
281
282  void Connection::disconnectPeersInternal()
283  {
284    for( const auto& mapEntry : this->peerMap_ )
285    {
286      enet_peer_disconnect(mapEntry.second, 0);
287    }
288    uint32_t iterations = NETWORK_DISCONNECT_TIMEOUT / NETWORK_WAIT_TIMEOUT;
289    uint32_t i = 0;
290    while( this->peerMap_.size() && i++ < iterations )
291    {
292      ENetEvent event;
293      if( enet_host_service( this->host_, &event, NETWORK_WAIT_TIMEOUT ) > 0 )
294      {
295        processIncomingEvent(event);
296      }
297    }
298  }
299
300  void Connection::processQueue()
301  {
302    incomingEvent inEvent;
303
304    this->incomingEventsMutex_->lock();
305    uint32_t incomingEventsCount = this->incomingEvents_.size();
306    this->incomingEventsMutex_->unlock();
307    while( incomingEventsCount > 0 )
308    {
309      // pop event from queue
310      this->incomingEventsMutex_->lock();
311      inEvent = this->incomingEvents_.front();
312      this->incomingEvents_.pop_front();
313      this->incomingEventsMutex_->unlock();
314     
315      // process event
316      switch( inEvent.type )
317      {
318        case IncomingEventType::peerConnect:
319          addPeer(inEvent.peerID);
320          break;
321        case IncomingEventType::peerDisconnect:
322          removePeer(inEvent.peerID);
323          break;
324        case IncomingEventType::receivePacket:
325          processPacket(inEvent.packet);
326          break;
327        default:
328          break;
329      }
330     
331      // check whether there are still events in the queue
332      this->incomingEventsMutex_->lock();
333      incomingEventsCount = this->incomingEvents_.size();
334      this->incomingEventsMutex_->unlock();
335    }
336  }
337 
338  void Connection::waitOutgoingQueue()
339  {
340    uint32_t outgoingEventsCount;
341    this->outgoingEventsMutex_->lock();
342    outgoingEventsCount = this->outgoingEvents_.size();
343    this->outgoingEventsMutex_->unlock();
344    while( outgoingEventsCount )
345    {
346      msleep(1);
347      this->outgoingEventsMutex_->lock();
348      outgoingEventsCount = this->outgoingEvents_.size();
349      this->outgoingEventsMutex_->unlock();
350    }
351  }
352
353
354  incomingEvent Connection::preprocessConnectEvent(ENetEvent& event)
355  {
356    // make sure this peer doesn't exist
357    assert( this->peerMap_.find(this->nextPeerID_) == this->peerMap_.end() );
358    assert( this->peerIDMap_.find(event.peer) == this->peerIDMap_.end() );
359   
360    // give peer a new id and increase peerID for next peer
361    uint32_t peerID = this->nextPeerID_;
362    ++this->nextPeerID_;
363   
364    // add peer/peerID into peerMap_ and peerIDMap_
365    this->peerMap_[peerID] = event.peer;
366    this->peerIDMap_[event.peer] = peerID;
367   
368    // create new peerEvent and return it
369    incomingEvent inEvent = { peerID, IncomingEventType::peerConnect, nullptr };
370    return inEvent;
371  }
372 
373  incomingEvent Connection::preprocessDisconnectEvent(ENetEvent& event)
374  {
375    // assert that the peer exists and get peerID
376    assert( this->peerIDMap_.find(event.peer) != this->peerIDMap_.end() );
377    uint32_t peerID = this->peerIDMap_[event.peer];
378   
379    // remove peer/peerID from maps
380    this->peerIDMap_.erase(this->peerIDMap_.find(event.peer));
381    this->peerMap_.erase(this->peerMap_.find(peerID));
382   
383    // create new peerEvent and return it
384    incomingEvent inEvent = { peerID, IncomingEventType::peerDisconnect, nullptr };
385    return inEvent;
386  }
387 
388  incomingEvent Connection::preprocessReceiveEvent(ENetEvent& event)
389  {
390    // assert that the peer exists and get peerID
391    assert( this->peerIDMap_.find(event.peer) != this->peerIDMap_.end() );
392    uint32_t peerID = this->peerIDMap_[event.peer];
393   
394    // create new Packet from ENetPacket
395    packet::Packet* p = packet::Packet::createPacket(event.packet, peerID);
396   
397    // create new peerEvent and return it
398    incomingEvent inEvent = { peerID, IncomingEventType::receivePacket, p };
399    return inEvent;
400  }
401
402 
403  void Connection::enableCompression()
404  {
405    enet_host_compress_with_range_coder( this->host_ );
406  }
407
408
409}
Note: See TracBrowser for help on using the repository browser.