Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 11842 was 11829, checked in by mdedial, 7 years ago

WIP 22.03.18: Begin documenting server-side code

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