Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/network/Connection.cc @ 12398

Last change on this file since 12398 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: 11.6 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
76  /**
77   * Start the main communication thread.
78   */
79  void Connection::startCommunicationThread()
80  {
81    this->bCommunicationThreadRunning_ = true;
82    this->communicationThread_ = new boost::thread(&Connection::communicationThread, this);
83  }
84 
85  /**
86   * Stop the main communication thread.
87   */
88  void Connection::stopCommunicationThread()
89  {
90    this->bCommunicationThreadRunning_ = false;
91    // Wait for peaceful termination
92    if(!this->communicationThread_->timed_join(NETWORK_COMMUNICATION_THREAD_WAIT_TIME))
93    {
94      // Force thread to stop if the waiting time runs out.
95      this->communicationThread_->interrupt();
96    }
97    delete this->communicationThread_;
98  }
99
100  /**
101   * Send an outgoing event of type 'disconnectPeer'.
102   * @param peerID The peer to which the event is sent
103   */
104  void Connection::disconnectPeer(uint32_t peerID)
105  {
106    outgoingEvent outEvent = { peerID, OutgoingEventType::disconnectPeer, nullptr, 0 };
107   
108    this->outgoingEventsMutex_->lock();
109    this->outgoingEvents_.push_back(outEvent);
110    this->outgoingEventsMutex_->unlock();
111  }
112
113  /**
114   * Send an outgoing event of type 'disconnectPeers'.
115   */
116  void Connection::disconnectPeers()
117  {
118    outgoingEvent outEvent = { 0, OutgoingEventType::disconnectPeers, nullptr, 0 };
119   
120    this->outgoingEventsMutex_->lock();
121    this->outgoingEvents_.push_back(outEvent);
122    this->outgoingEventsMutex_->unlock();
123  }
124
125  /**
126   * Send a packet.
127   * @param packet Pointer to the packet to send
128   * @param peerID The peer to which the event is sent
129   * @param channelId The channel ID
130   */
131  void Connection::addPacket(ENetPacket* packet, uint32_t peerID, uint8_t channelID)
132  {
133    outgoingEvent outEvent = { peerID, OutgoingEventType::sendPacket, packet, channelID };
134   
135    this->outgoingEventsMutex_->lock();
136    this->outgoingEvents_.push_back(outEvent);
137    this->outgoingEventsMutex_->unlock();
138  }
139 
140  /**
141   * Send a broadcast packet.
142   * @param packet Pointer to the packet to send
143   * @param channelId The channel ID
144   */
145  void Connection::broadcastPacket(ENetPacket* packet, uint8_t channelID)
146  {
147    outgoingEvent outEvent = { 0, OutgoingEventType::broadcastPacket, packet, channelID };
148   
149    this->outgoingEventsMutex_->lock();
150    this->outgoingEvents_.push_back(outEvent);
151    this->outgoingEventsMutex_->unlock();
152  }
153
154 
155  /**
156   * Main communication thread
157   */
158  void Connection::communicationThread()
159  {
160    ENetEvent event;
161   
162    while(this->bCommunicationThreadRunning_)
163    {
164      // Receive all pending incoming Events (such as packets, connects and disconnects)
165      while(enet_host_check_events(this->host_, &event ) > 0)
166      {
167        this->processIncomingEvent(event);
168      }
169     
170      // Sleep for 1ms
171      msleep(1);
172     
173      // Send all waiting outgoing packets
174      this->outgoingEventsMutex_->lock();
175      uint32_t outgoingEventsCount = this->outgoingEvents_.size();
176      this->outgoingEventsMutex_->unlock();
177
178      while(outgoingEventsCount > 0)
179      {
180        this->outgoingEventsMutex_->lock();
181        outgoingEvent outEvent = this->outgoingEvents_.front();
182        this->outgoingEvents_.pop_front();
183        this->outgoingEventsMutex_->unlock();
184       
185        this->processOutgoingEvent(outEvent);
186       
187        this->outgoingEventsMutex_->lock();
188        outgoingEventsCount = this->outgoingEvents_.size();
189        this->outgoingEventsMutex_->unlock();
190      }
191     
192      // Wait for incoming events (at most NETWORK_WAIT_TIMEOUT ms)
193      if(enet_host_service(this->host_, &event, NETWORK_WAIT_TIMEOUT) > 0)
194      {
195        this->processIncomingEvent(event);
196      }
197    }
198  }
199 
200  /**
201   * Handle an incoming event.
202   * @param event The incoming event
203   */
204  void Connection::processIncomingEvent(ENetEvent& event)
205  {
206    incomingEvent inEvent;
207    // preprocess event
208    switch(event.type)
209    {
210      case ENET_EVENT_TYPE_CONNECT:
211        inEvent = preprocessConnectEvent(event);
212        break;
213      case ENET_EVENT_TYPE_RECEIVE:
214        inEvent = preprocessReceiveEvent(event);
215        break;
216      case ENET_EVENT_TYPE_DISCONNECT:
217        inEvent = preprocessDisconnectEvent(event);
218        break;
219      case ENET_EVENT_TYPE_NONE:
220      default:
221        return;
222    }
223   
224    // pushing event to queue
225    this->incomingEventsMutex_->lock();
226    this->incomingEvents_.push_back(inEvent);
227    this->incomingEventsMutex_->unlock();
228  }
229 
230  /**
231   * Send an event.
232   * @param event The event to send
233   */
234  void Connection::processOutgoingEvent(outgoingEvent& event)
235  {
236    ENetPeer* peer;
237    switch(event.type)
238    {
239      case OutgoingEventType::sendPacket:
240        // check whether the peer is still/already in the peer list
241        if(this->peerMap_.find(event.peerID) != this->peerMap_.end())
242        {
243          peer = this->peerMap_[event.peerID];
244          enet_peer_send(peer, event.channelID, event.packet);
245        }
246        else
247        {
248          // peer probably already disconnected so just discard packet
249          orxout(message) << "Trying to send packet to peer that is not in peer list. Ignoring packet." << endl;
250          enet_packet_destroy(event.packet);
251        }
252        break;
253      case OutgoingEventType::disconnectPeer:
254        if(this->peerMap_.find(event.peerID) != this->peerMap_.end())
255        {
256          peer = this->peerMap_[event.peerID];
257          enet_peer_disconnect(peer, 0);
258        }
259        else
260        {
261          // peer probably already disconnected so just discard disconnect event
262          assert(event.peerID < this->nextPeerID_);
263        }
264        break;
265      case OutgoingEventType::disconnectPeers:
266        this->disconnectPeersInternal();
267        break;
268      case OutgoingEventType::broadcastPacket:
269        enet_host_broadcast( this->host_, event.channelID, event.packet );
270        break;
271      default:
272        assert(0);
273    }
274  }
275
276  void Connection::disconnectPeersInternal()
277  {
278    for( const auto& mapEntry : this->peerMap_ )
279    {
280      enet_peer_disconnect(mapEntry.second, 0);
281    }
282    uint32_t iterations = NETWORK_DISCONNECT_TIMEOUT / NETWORK_WAIT_TIMEOUT;
283    uint32_t i = 0;
284    while( this->peerMap_.size() && i++ < iterations )
285    {
286      ENetEvent event;
287      if( enet_host_service( this->host_, &event, NETWORK_WAIT_TIMEOUT ) > 0 )
288      {
289        processIncomingEvent(event);
290      }
291    }
292  }
293
294  void Connection::processQueue()
295  {
296    incomingEvent inEvent;
297
298    this->incomingEventsMutex_->lock();
299    uint32_t incomingEventsCount = this->incomingEvents_.size();
300    this->incomingEventsMutex_->unlock();
301    while( incomingEventsCount > 0 )
302    {
303      // pop event from queue
304      this->incomingEventsMutex_->lock();
305      inEvent = this->incomingEvents_.front();
306      this->incomingEvents_.pop_front();
307      this->incomingEventsMutex_->unlock();
308     
309      // process event
310      switch( inEvent.type )
311      {
312        case IncomingEventType::peerConnect:
313          addPeer(inEvent.peerID);
314          break;
315        case IncomingEventType::peerDisconnect:
316          removePeer(inEvent.peerID);
317          break;
318        case IncomingEventType::receivePacket:
319          processPacket(inEvent.packet);
320          break;
321        default:
322          break;
323      }
324     
325      // check whether there are still events in the queue
326      this->incomingEventsMutex_->lock();
327      incomingEventsCount = this->incomingEvents_.size();
328      this->incomingEventsMutex_->unlock();
329    }
330  }
331 
332  void Connection::waitOutgoingQueue()
333  {
334    uint32_t outgoingEventsCount;
335    this->outgoingEventsMutex_->lock();
336    outgoingEventsCount = this->outgoingEvents_.size();
337    this->outgoingEventsMutex_->unlock();
338    while( outgoingEventsCount )
339    {
340      msleep(1);
341      this->outgoingEventsMutex_->lock();
342      outgoingEventsCount = this->outgoingEvents_.size();
343      this->outgoingEventsMutex_->unlock();
344    }
345  }
346
347
348  incomingEvent Connection::preprocessConnectEvent(ENetEvent& event)
349  {
350    // make sure this peer doesn't exist
351    assert( this->peerMap_.find(this->nextPeerID_) == this->peerMap_.end() );
352    assert( this->peerIDMap_.find(event.peer) == this->peerIDMap_.end() );
353   
354    // give peer a new id and increase peerID for next peer
355    uint32_t peerID = this->nextPeerID_;
356    ++this->nextPeerID_;
357   
358    // add peer/peerID into peerMap_ and peerIDMap_
359    this->peerMap_[peerID] = event.peer;
360    this->peerIDMap_[event.peer] = peerID;
361   
362    // create new peerEvent and return it
363    incomingEvent inEvent = { peerID, IncomingEventType::peerConnect, nullptr };
364    return inEvent;
365  }
366 
367  incomingEvent Connection::preprocessDisconnectEvent(ENetEvent& event)
368  {
369    // assert that the peer exists and get peerID
370    assert( this->peerIDMap_.find(event.peer) != this->peerIDMap_.end() );
371    uint32_t peerID = this->peerIDMap_[event.peer];
372   
373    // remove peer/peerID from maps
374    this->peerIDMap_.erase(this->peerIDMap_.find(event.peer));
375    this->peerMap_.erase(this->peerMap_.find(peerID));
376   
377    // create new peerEvent and return it
378    incomingEvent inEvent = { peerID, IncomingEventType::peerDisconnect, nullptr };
379    return inEvent;
380  }
381 
382  incomingEvent Connection::preprocessReceiveEvent(ENetEvent& event)
383  {
384    // assert that the peer exists and get peerID
385    assert( this->peerIDMap_.find(event.peer) != this->peerIDMap_.end() );
386    uint32_t peerID = this->peerIDMap_[event.peer];
387   
388    // create new Packet from ENetPacket
389    packet::Packet* p = packet::Packet::createPacket(event.packet, peerID);
390   
391    // create new peerEvent and return it
392    incomingEvent inEvent = { peerID, IncomingEventType::receivePacket, p };
393    return inEvent;
394  }
395
396 
397  void Connection::enableCompression()
398  {
399    enet_host_compress_with_range_coder( this->host_ );
400  }
401
402}
Note: See TracBrowser for help on using the repository browser.