Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Jun 28, 2009, 3:04:30 PM (16 years ago)
Author:
scheusso
Message:

a lot of cleanup
some bugfixes (Thread, ThreadPool)
the biggest part of the network (~80% cpu time) is now multithreaded (1 thread for each client)

Location:
code/branches/netp6/src
Files:
14 edited

Legend:

Unmodified
Added
Removed
  • code/branches/netp6/src/core/CorePrereqs.h

    r3231 r3240  
    189189    // multithreading
    190190    class Thread;
    191     class ThreadGroup;
     191    class ThreadPool;
    192192}
    193193
  • code/branches/netp6/src/core/Thread.cc

    r3231 r3240  
    3636
    3737#include "util/Sleep.h"
    38 #include "Functor.h"
     38#include "Executor.h"
    3939
    4040namespace orxonox
     
    4444   
    4545    Thread::Thread():
    46         functor_(0),
     46        executor_(0),
    4747        isWorking_(false),
    4848        stopThread_(false)
    4949    {
    50         this->communicationMutex_ = new boost::mutex;
     50        this->executorMutex_ = new boost::mutex;
     51        this->isWorkingMutex_ = new boost::mutex;
     52        this->stopThreadMutex_ = new boost::mutex;
    5153        this->workerThread_ = new boost::thread( boost::bind(&Thread::threadLoop, this) );
    5254    }
     
    5456    Thread::~Thread()
    5557    {
     58        this->stopThreadMutex_->lock();
    5659        this->stopThread_ = true;
     60        this->stopThreadMutex_->unlock();
    5761        if( !this->workerThread_->timed_join( THREAD_WAIT_BEFORE_DETACH ) )
    5862            assert(0); // this should not happen
    5963        delete this->workerThread_;
    60         delete this->communicationMutex_;
     64        delete this->executorMutex_;
     65        delete this->stopThreadMutex_;
     66        delete this->isWorkingMutex_;
    6167    }
    6268   
    63     bool Thread::evaluateFunctor( Functor* functor )
     69    bool Thread::isWorking()
    6470    {
    65         if( this->communicationMutex_->try_lock() )
    66         {
    67             this->functor_ = functor;
    68             this->communicationMutex_->unlock();
    69             return true;
    70         }
    71         else
    72             return false;
     71      this->isWorkingMutex_->lock();
     72      bool isWorking = this->isWorking_;
     73      this->isWorkingMutex_->unlock();
     74      return isWorking;
     75    }
     76   
     77    bool Thread::evaluateExecutor( Executor* executor )
     78    {
     79        this->isWorkingMutex_->lock();
     80        this->isWorking_=true;
     81        this->isWorkingMutex_->unlock();
     82        this->executorMutex_->lock();
     83        this->executor_ = executor;
     84        this->executorMutex_->unlock();
     85        return true;
    7386    }
    7487   
    7588    void Thread::threadLoop()
    7689    {
    77         while( !this->stopThread_ )
     90        bool stopThread = false;
     91        while( !stopThread )
    7892        {
    79             this->communicationMutex_->lock();
    80             if( this->functor_ )
     93            this->executorMutex_->lock();
     94            Executor* executor = this->executor_;
     95            this->executorMutex_->unlock();
     96            if( executor )
    8197            {
    82                 (*this->functor_)();
    83                 this->communicationMutex_->unlock();
     98                (*executor)();
     99                this->executorMutex_->lock();
     100                delete this->executor_;
     101                this->executor_ = 0;
     102                this->executorMutex_->unlock();
     103                this->isWorkingMutex_->lock();
     104                this->isWorking_=false;
     105                this->isWorkingMutex_->unlock();
    84106            }
    85107            else
    86108            {
    87                 this->communicationMutex_->unlock();
    88109                this->workerThread_->yield();
    89110            }
     111            this->stopThreadMutex_->lock();
     112            stopThread = this->stopThread_;
     113            this->stopThreadMutex_->unlock();
    90114        }
    91115    }
     
    96120        while( stillWorking )
    97121        {
    98             this->communicationMutex_->lock();
     122            this->isWorkingMutex_->lock();
    99123            stillWorking = this->isWorking_;
    100             this->communicationMutex_->unlock();
     124            this->isWorkingMutex_->unlock();
    101125            if( stillWorking )
    102126                msleep( 1 );
  • code/branches/netp6/src/core/Thread.h

    r3231 r3240  
    3232#include "CorePrereqs.h"
    3333
     34namespace boost{
     35  class recursive_mutex;
     36}
     37
    3438 namespace orxonox
    3539{
     
    4044        virtual ~Thread();
    4145
    42         inline bool isWorking() { return this->isWorking_; }
     46        bool isWorking();
    4347        void waitUntilFinished();
    44         bool evaluateFunctor( Functor* functor );
     48        bool evaluateExecutor( Executor* executor );
    4549
    4650    private:
    4751        void            threadLoop();
    4852       
    49         Functor*        functor_;
     53        Executor*       executor_;
    5054        bool            isWorking_;
    5155        bool            stopThread_;
    5256        boost::thread*  workerThread_;
    53         boost::mutex*   communicationMutex_;
     57        boost::mutex*   executorMutex_;
     58        boost::mutex*     isWorkingMutex_;
     59        boost::mutex*   stopThreadMutex_;
    5460    };
    5561
  • code/branches/netp6/src/core/ThreadPool.cc

    r3231 r3240  
    2828
    2929#include "ThreadPool.h"
     30#include "Thread.h"
    3031#include <cassert>
    3132
     
    3940    ThreadPool::~ThreadPool()
    4041    {
     42        unsigned int a = this->setNrOfThreads(0);
     43        assert(a == 0);
    4144    }
    4245   
     
    4447    {
    4548        for( unsigned int i=0; i<nr; i++ )
    46             this->threadPool_.push_back(Thread());
     49            this->threadPool_.push_back(new Thread());
    4750    }
    4851    unsigned int ThreadPool::removeThreads( unsigned int nr )
    4952    {
    5053        unsigned int i=0;
    51         std::vector<Thread>::iterator it;
    52         for( it = this->threadPool_.begin(); it != threadPool_.end() && i<nr; ++it )
     54        std::vector<Thread*>::iterator it;
     55        for( it = this->threadPool_.begin(); it != threadPool_.end() && i<nr; )
    5356        {
    54             if( ! it->isWorking() )
     57            if( ! (*it)->isWorking() )
    5558            {
    56                 this->threadPool_.erase( it++ );
     59                Thread* temp = *it;
     60                it=this->threadPool_.erase( it );
     61                delete temp;
    5762                ++i;
    5863            }
     64            else
     65              ++it;
    5966        }
    6067        return i;
     
    7481    }
    7582   
    76     bool ThreadPool::passFunction( Functor* functor, bool addThread )
     83    bool ThreadPool::passFunction( Executor* executor, bool addThread )
    7784    {
    78         std::vector<Thread>::iterator it;
     85        std::vector<Thread*>::iterator it;
    7986        for ( it=this->threadPool_.begin(); it!=this->threadPool_.end(); ++it )
    8087        {
    81             if ( ! it->isWorking() )
     88            if ( ! (*it)->isWorking() )
    8289            {
    83                 bool b = it->evaluateFunctor( functor );
     90                bool b = (*it)->evaluateExecutor( executor );
    8491                assert(b); // if b is false then there is some code error
    8592                return true;
     
    8996        {
    9097            addThreads( 1 );
    91             this->threadPool_.back().evaluateFunctor( functor ); // access the last element
     98            bool b = this->threadPool_.back()->evaluateExecutor( executor ); // access the last element
     99            assert(b);
    92100            return true;
    93101        }
     
    98106    void ThreadPool::synchronise()
    99107    {
    100         std::vector<Thread>::iterator it;
     108        std::vector<Thread*>::iterator it;
    101109        for ( it=this->threadPool_.begin(); it!=this->threadPool_.end(); ++it )
    102110        {
    103             it->waitUntilFinished();
     111            (*it)->waitUntilFinished();
    104112        }
    105113    }
  • code/branches/netp6/src/core/ThreadPool.h

    r3231 r3240  
    3333
    3434#include <vector>
    35 #include "Thread.h"
    3635
    3736 namespace orxonox
     
    4746        unsigned int setNrOfThreads( unsigned int nr );
    4847       
    49         bool passFunction( Functor* functor, bool addThread=false );
     48        bool passFunction( Executor* executor, bool addThread=false );
    5049        void synchronise();
    5150       
    5251    private:
    53         std::vector<Thread> threadPool_;
     52        std::vector<Thread*> threadPool_;
    5453       
    5554    };
  • code/branches/netp6/src/network/GamestateManager.cc

    r3227 r3240  
    4242
    4343#include <cassert>
     44#include <queue>
     45#include <boost/thread/mutex.hpp>
    4446
    4547#include "util/Debug.h"
     48#include "core/Executor.h"
     49#include "core/ThreadPool.h"
    4650#include "ClientInformation.h"
    4751#include "packet/Acknowledgement.h"
     
    5660  {
    5761    trafficControl_ = new TrafficControl();
     62    threadMutex_ = new boost::mutex();
     63    threadPool_ = new ThreadPool();
    5864  }
    5965
     
    7177        delete (*it2).second;
    7278    }
    73     delete trafficControl_;
     79    delete this->trafficControl_;
     80    delete this->threadMutex_;
     81    delete this->threadPool_;
    7482  }
    7583
     
    116124    return true;
    117125  }
    118 
    119 
    120   packet::Gamestate *GamestateManager::popGameState(unsigned int clientID) {
     126 
     127  void GamestateManager::sendGamestates()
     128  {
     129    ClientInformation *temp = ClientInformation::getBegin();
     130    std::queue<packet::Gamestate*> clientGamestates;
     131    while(temp != NULL){
     132      if( !(temp->getSynched()) ){
     133        COUT(5) << "Server: not sending gamestate" << std::endl;
     134        temp=temp->next();
     135        if(!temp)
     136          break;
     137        continue;
     138      }
     139      COUT(4) << "client id: " << temp->getID() << " RTT: " << temp->getRTT() << " loss: " << temp->getPacketLoss() << std::endl;
     140      COUT(5) << "Server: doing gamestate gamestate preparation" << std::endl;
     141      int cid = temp->getID(); //get client id
     142     
     143      packet::Gamestate *gs;
     144      unsigned int gID = temp->getGamestateID();
     145      if(!reference)
     146        return;
     147     
     148      packet::Gamestate *client=0;
     149      if(gID != GAMESTATEID_INITIAL){
     150        assert(gamestateMap_.find(cid)!=gamestateMap_.end());
     151        std::map<unsigned int, packet::Gamestate*>::iterator it = gamestateMap_[cid].find(gID);
     152        if(it!=gamestateMap_[cid].end())
     153        {
     154          client = it->second;
     155        }
     156      }
     157     
     158      clientGamestates.push(0);
     159//       finishGamestate( cid, clientGamestates.back(), client, reference );
     160      //FunctorMember<GamestateManager>* functor =
     161      ExecutorMember<GamestateManager>* executor = createExecutor( createFunctor(&GamestateManager::finishGamestate) );
     162      executor->setObject(this);
     163      executor->setDefaultValues( cid, &clientGamestates.back(), client, reference );
     164//       (*static_cast<Executor*>(executor))();
     165      this->threadPool_->passFunction( executor, true );
     166//       (*functor)( cid, &(clientGamestates.back()), client, reference );
     167     
     168      temp = temp->next();
     169    }
     170   
     171    threadPool_->synchronise();
     172   
     173    while( !clientGamestates.empty() )
     174    {
     175      if(clientGamestates.front())
     176        clientGamestates.front()->send();
     177      clientGamestates.pop();
     178    }
     179  }
     180
     181
     182  void GamestateManager::finishGamestate( unsigned int clientID, packet::Gamestate** destgamestate, packet::Gamestate* base, packet::Gamestate* gamestate ) {
    121183    //why are we searching the same client's gamestate id as we searched in
    122184    //Server::sendGameState?
    123     packet::Gamestate *gs;
    124     unsigned int gID = ClientInformation::findClient(clientID)->getGamestateID();
    125     if(!reference)
    126       return 0;
    127     gs = reference->doSelection(clientID, 10000);
    128185    // save the (undiffed) gamestate in the clients gamestate map
    129     gamestateMap_[clientID][gs->getID()]=gs;
    130186    //chose wheather the next gamestate is the first or not
    131     packet::Gamestate *client=0;
    132     if(gID != GAMESTATEID_INITIAL){
    133       assert(gamestateMap_.find(clientID)!=gamestateMap_.end());
    134       std::map<unsigned int, packet::Gamestate*>::iterator it = gamestateMap_[clientID].find(gID);
    135       if(it!=gamestateMap_[clientID].end())
    136       {
    137         client = it->second;
    138       }
    139     }
    140     if(client){
     187   
     188    packet::Gamestate *gs = gamestate->doSelection(clientID, 20000);
     189//     packet::Gamestate *gs = new packet::Gamestate(*gamestate);
     190//     packet::Gamestate *gs = new packet::Gamestate();
     191//     gs->collectData( id_, 0x1 );
     192    this->threadMutex_->lock();
     193    gamestateMap_[clientID][gamestate->getID()]=gs;
     194    this->threadMutex_->unlock();
     195   
     196    if(base)
     197    {
     198       
    141199//       COUT(3) << "diffing" << std::endl;
    142200//       packet::Gamestate* gs1  = gs;
    143       packet::Gamestate *diffed = gs->diff(client);
     201      packet::Gamestate *diffed = gs->diff(base);
    144202      //packet::Gamestate *gs2 = diffed->undiff(gs);
    145203//       assert(*gs == *gs2);
     
    150208    }
    151209    else{
    152 //       COUT(3) << "not diffing" << std::endl;
    153210      gs = new packet::Gamestate(*gs);
    154211    }
     212   
     213   
    155214    bool b = gs->compressData();
    156215    assert(b);
    157     COUT(4) << "sending gamestate with id " << gs->getID();
    158     if(gs->isDiffed())
    159     COUT(4) << " and baseid " << gs->getBaseID() << endl;
    160     else
    161     COUT(4) << endl;
    162     return gs;
     216//     COUT(4) << "sending gamestate with id " << gs->getID();
     217//     if(gamestate->isDiffed())
     218//     COUT(4) << " and baseid " << gs->getBaseID() << endl;
     219//     else
     220//     COUT(4) << endl;
     221    gs->setClientID(clientID);
     222    *destgamestate = gs;
    163223  }
    164224
  • code/branches/netp6/src/network/GamestateManager.h

    r3214 r3240  
    4545#include <map>
    4646#include "GamestateHandler.h"
     47#include "core/CorePrereqs.h"
    4748
    4849namespace orxonox
     
    7374    bool processGamestates();
    7475    bool update();
    75     packet::Gamestate *popGameState(unsigned int clientID);
     76    void sendGamestates();
     77//     packet::Gamestate *popGameState(unsigned int clientID);
     78    void finishGamestate( unsigned int clientID, packet::Gamestate** destgamestate, packet::Gamestate* base, packet::Gamestate* gamestate );
    7679
    7780    bool getSnapshot();
     
    7982    bool ack(unsigned int gamestateID, unsigned int clientID);
    8083    void removeClient(ClientInformation *client);
    81     private:
     84  private:
    8285    bool processGamestate(packet::Gamestate *gs);
    8386
    8487    std::map<unsigned int, std::map<unsigned int, packet::Gamestate*> > gamestateMap_;
    85     //std::map<int, packet::Gamestate*> gamestateMap; //map gsID to gamestate*
    86     //std::map<int, int> gamestateUsed; // save the number of clients, that use the specific gamestate
    8788    std::map<unsigned int, packet::Gamestate*> gamestateQueue;
    8889    packet::Gamestate *reference;
    8990    TrafficControl *trafficControl_;
    9091    unsigned int id_;
     92    boost::mutex* threadMutex_;
     93    ThreadPool*   threadPool_;
    9194  };
    9295
  • code/branches/netp6/src/network/Host.cc

    r3214 r3240  
    7474}
    7575
    76 
    77 // bool Host::chat(std::string& message){
    78 //   if(!instance_)
    79 //     return false;
    80 //   packet::Chat *c = new packet::Chat(message, getPlayerID());
    81 //   return instance_->sendChat(c);
    82 // }
    83 
    84 // bool Host::receiveChat(packet::Chat *message, unsigned int clientID){
    85 //   if(instance_)
    86 //     return instance_->processChat(message, clientID);
    87 //   else
    88 //     return false;
    89 // }
    90 
    9176/**
    9277 * This function returns the ID of the player
  • code/branches/netp6/src/network/Server.cc

    r3214 r3240  
    4848#include "core/Clock.h"
    4949#include "core/ObjectList.h"
     50#include "core/Executor.h"
     51#include "core/ThreadPool.h"
    5052#include "packet/Chat.h"
    5153#include "packet/ClassID.h"
     
    6870  */
    6971  Server::Server() {
    70     timeSinceLastUpdate_=0;
    71     gamestates_ = new GamestateManager();
     72    this->timeSinceLastUpdate_=0;
     73    this->threadPool_ = new ThreadPool();
    7274  }
    7375
    7476  Server::Server(int port){
    7577    this->setPort( port );
    76     timeSinceLastUpdate_=0;
    77     gamestates_ = new GamestateManager();
     78    this->timeSinceLastUpdate_=0;
     79    this->threadPool_ = new ThreadPool();
    7880  }
    7981
     
    8688    this->setPort( port );
    8789    this->setBindAddress( bindAddress );
    88     timeSinceLastUpdate_=0;
    89     gamestates_ = new GamestateManager();
     90    this->timeSinceLastUpdate_=0;
     91    this->threadPool_ = new ThreadPool();
    9092  }
    9193
     
    9496  */
    9597  Server::~Server(){
    96     if(gamestates_)
    97       delete gamestates_;
     98    delete this->threadPool_;
    9899  }
    99100
     
    138139  */
    139140  void Server::update(const Clock& time) {
     141    // receive incoming packets
    140142    Connection::processQueue();
    141     gamestates_->processGamestates();
     143    // process incoming gamestates
     144    GamestateManager::processGamestates();
     145   
     146    // pass sendFunctionCalls to worker thread pool
     147    ExecutorStatic* functioncalls = createExecutor( createFunctor(&FunctionCallManager::sendCalls) );
     148    this->threadPool_->passFunction( functioncalls, true );
     149   
     150    this->threadPool_->synchronise();
     151   
    142152    //this steers our network frequency
    143153    timeSinceLastUpdate_+=time.getDeltaTime();
     
    145155    {
    146156      timeSinceLastUpdate_ -= static_cast<unsigned int>( timeSinceLastUpdate_ / NETWORK_PERIOD ) * NETWORK_PERIOD;
     157//       ExecutorMember<GamestateManager>* updategamestate = createExecutor( createFunctor(&GamestateManager::updateGamestate);
     158//       updategamestate->setObject( static_cast<GamestateManager*>(this) );
     159//       this->threadPool_->passFunction( updategamestate );
    147160      updateGamestate();
    148       FunctionCallManager::sendCalls();
    149161    }
    150162    sendPackets(); // flush the enet queue
     
    175187  */
    176188  void Server::updateGamestate() {
    177 //     if( ClientInformation::getBegin()==NULL )
     189    if( ClientInformation::getBegin()==NULL )
    178190      //no client connected
    179 //       return;
    180     gamestates_->update();
     191      return;
     192    GamestateManager::update();
    181193    COUT(5) << "Server: one gamestate update complete, goig to sendGameState" << std::endl;
    182194    //std::cout << "updated gamestate, sending it" << std::endl;
     
    197209  */
    198210  bool Server::sendGameState() {
    199     COUT(5) << "Server: starting function sendGameState" << std::endl;
    200     ClientInformation *temp = ClientInformation::getBegin();
    201     bool added=false;
    202     while(temp != NULL){
    203       if( !(temp->getSynched()) ){
    204         COUT(5) << "Server: not sending gamestate" << std::endl;
    205         temp=temp->next();
    206         if(!temp)
    207           break;
    208         //think this works without continue
    209         continue;
    210       }
    211       COUT(4) << "client id: " << temp->getID() << " RTT: " << temp->getRTT() << " loss: " << temp->getPacketLoss() << std::endl;
    212       COUT(5) << "Server: doing gamestate gamestate preparation" << std::endl;
    213       int gid = temp->getGamestateID(); //get gamestate id
    214       int cid = temp->getID(); //get client id
    215       COUT(5) << "Server: got acked (gamestate) ID from clientlist: " << gid << std::endl;
    216       packet::Gamestate *gs = gamestates_->popGameState(cid);
    217       if(gs==NULL){
    218         COUT(2) << "Server: could not generate gamestate (NULL from compress)" << std::endl;
    219         temp = temp->next();
    220         continue;
    221       }
    222       //std::cout << "adding gamestate" << std::endl;
    223       gs->setClientID(cid);
    224       if ( !gs->send() ){
    225         COUT(3) << "Server: packet with client id (cid): " << cid << " not sended: " << temp->getFailures() << std::endl;
    226         temp->addFailure();
    227       }else
    228         temp->resetFailures();
    229       added=true;
    230       temp=temp->next();
    231       // gs gets automatically deleted by enet callback
    232     }
     211//     COUT(5) << "Server: starting function sendGameState" << std::endl;
     212//     ClientInformation *temp = ClientInformation::getBegin();
     213//     bool added=false;
     214//     while(temp != NULL){
     215//       if( !(temp->getSynched()) ){
     216//         COUT(5) << "Server: not sending gamestate" << std::endl;
     217//         temp=temp->next();
     218//         if(!temp)
     219//           break;
     220//         continue;
     221//       }
     222//       COUT(4) << "client id: " << temp->getID() << " RTT: " << temp->getRTT() << " loss: " << temp->getPacketLoss() << std::endl;
     223//       COUT(5) << "Server: doing gamestate gamestate preparation" << std::endl;
     224//       int cid = temp->getID(); //get client id
     225//       packet::Gamestate *gs = GamestateManager::popGameState(cid);
     226//       if(gs==NULL){
     227//         COUT(2) << "Server: could not generate gamestate (NULL from compress)" << std::endl;
     228//         temp = temp->next();
     229//         continue;
     230//       }
     231//       //std::cout << "adding gamestate" << std::endl;
     232//       gs->setClientID(cid);
     233//       if ( !gs->send() ){
     234//         COUT(3) << "Server: packet with client id (cid): " << cid << " not sended: " << temp->getFailures() << std::endl;
     235//         temp->addFailure();
     236//       }else
     237//         temp->resetFailures();
     238//       added=true;
     239//       temp=temp->next();
     240//       // gs gets automatically deleted by enet callback
     241//     }
     242    GamestateManager::sendGamestates();
    233243    return true;
    234244  }
     
    324334  void Server::disconnectClient( ClientInformation *client ){
    325335    ServerConnection::disconnectClient( client );
    326     gamestates_->removeClient(client);
     336    GamestateManager::removeClient(client);
    327337// inform all the listeners
    328338    ObjectList<ClientConnectionListener>::iterator listener = ObjectList<ClientConnectionListener>::begin();
  • code/branches/netp6/src/network/Server.h

    r3214 r3240  
    3434#include "core/CorePrereqs.h"
    3535#include "Host.h"
     36#include "GamestateManager.h"
    3637#include "ServerConnection.h"
    3738
     
    4344  * It implements all functions necessary for a Server
    4445  */
    45   class _NetworkExport Server : public Host, public ServerConnection{
     46  class _NetworkExport Server : public Host, public ServerConnection, public GamestateManager{
    4647  public:
    4748    Server();
     
    6364    unsigned int shipID(){return 0;}
    6465    unsigned int playerID(){return 0;}
    65 
     66   
    6667    void addClient(ENetEvent *event);
    6768    bool createClient(int clientID);
     
    7576    void syncClassid(unsigned int clientID);
    7677
    77     GamestateManager *gamestates_;
    78 
    79 
     78    ThreadPool* threadPool_;
    8079    float timeSinceLastUpdate_;
    8180  };
  • code/branches/netp6/src/network/synchronisable/NetworkCallbackManager.cc

    r3214 r3240  
    4444    if (it != callbackSet_.end())
    4545    {
    46       delete (*it);
    4746      callbackSet_.erase(it);
     47      delete cb;
    4848    }
    4949  }
  • code/branches/netp6/src/network/synchronisable/Synchronisable.cc

    r3214 r3240  
    247247      return 0;
    248248    uint32_t tempsize = 0;
     249#ifndef NDEBUG
    249250    if (this->classID==0)
    250251      COUT(3) << "classid 0 " << this->getIdentifier()->getName() << std::endl;
     252#endif
    251253
    252254    if (this->classID == static_cast<uint32_t>(-1))
  • code/branches/netp6/src/network/synchronisable/SynchronisableVariable.h

    r3214 r3240  
    114114  {
    115115    if (this->callback_ != 0)
     116    {
    116117      NetworkCallbackManager::deleteCallback(this->callback_); //safe call for deletion
     118      // this is neccessary because for example for a Vector3 all 3 components of the vector use the same callback
     119    }
    117120  }
    118121
  • code/branches/netp6/src/orxonox/gamestates/GSDedicated.cc

    r3205 r3240  
    103103#endif
    104104        //inputThread_->join();
     105        delete this->inputThread_;
    105106
    106107        GameMode::setHasServer(false);
Note: See TracChangeset for help on using the changeset viewer.