diff --git a/source/glest_game/ai/ai_interface.cpp b/source/glest_game/ai/ai_interface.cpp index 5c9afee8..ae42aad0 100644 --- a/source/glest_game/ai/ai_interface.cpp +++ b/source/glest_game/ai/ai_interface.cpp @@ -32,9 +32,198 @@ using namespace Shared::Graphics; namespace Glest{ namespace Game{ -AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex, int useStartLocation) : fp(NULL) { +// ===================================================== +// class FactionThread +// ===================================================== + +AiInterfaceThread::AiInterfaceThread(AiInterface *aiIntf) : BaseThread() { + this->triggerIdMutex = new Mutex(); + this->aiIntf = aiIntf; +} + +AiInterfaceThread::~AiInterfaceThread() { + delete triggerIdMutex; + triggerIdMutex = NULL; +} + +void AiInterfaceThread::setQuitStatus(bool value) { + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s] Line: %d value = %d\n",__FILE__,__FUNCTION__,__LINE__,value); + + BaseThread::setQuitStatus(value); + if(value == true) { + signal(-1); + } + + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); +} + +void AiInterfaceThread::signal(int frameIndex) { + if(frameIndex >= 0) { + static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); + MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId); + this->frameIndex.first = frameIndex; + this->frameIndex.second = false; + + safeMutex.ReleaseLock(); + } + semTaskSignalled.signal(); +} + +void AiInterfaceThread::setTaskCompleted(int frameIndex) { + if(frameIndex >= 0) { + static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); + MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId); + if(this->frameIndex.first == frameIndex) { + this->frameIndex.second = true; + } + safeMutex.ReleaseLock(); + } +} + +bool AiInterfaceThread::canShutdown(bool deleteSelfIfShutdownDelayed) { + bool ret = (getExecutingTask() == false); + if(ret == false && deleteSelfIfShutdownDelayed == true) { + setDeleteSelfOnExecutionDone(deleteSelfIfShutdownDelayed); + signalQuit(); + } + + return ret; +} + +bool AiInterfaceThread::isSignalCompleted(int frameIndex) { + if(getRunningStatus() == false) { + return true; + } + static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); + MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId); + //bool result = (event != NULL ? event->eventCompleted : true); + bool result = (this->frameIndex.first == frameIndex && this->frameIndex.second == true); + + //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] worker thread this = %p, this->frameIndex.first = %d, this->frameIndex.second = %d\n",__FILE__,__FUNCTION__,__LINE__,this,this->frameIndex.first,this->frameIndex.second); + + safeMutex.ReleaseLock(); + return result; +} + +void AiInterfaceThread::execute() { + RunningStatusSafeWrapper runningStatus(this); + try { + //setRunningStatus(true); + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] ****************** STARTING worker thread this = %p\n",__FILE__,__FUNCTION__,__LINE__,this); + + bool minorDebugPerformance = false; + Chrono chrono; + + //unsigned int idx = 0; + for(;this->aiIntf != NULL;) { + if(getQuitStatus() == true) { + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + break; + } + + semTaskSignalled.waitTillSignalled(); + + if(getQuitStatus() == true) { + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + break; + } + + static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); + MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId); + bool executeTask = (frameIndex.first >= 0); + + //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] frameIndex = %d this = %p executeTask = %d\n",__FILE__,__FUNCTION__,__LINE__,frameIndex.first, this, executeTask); + + safeMutex.ReleaseLock(); + + if(executeTask == true) { + ExecutingTaskSafeWrapper safeExecutingTaskMutex(this); + + MutexSafeWrapper safeMutex(this->aiIntf->getMutex(),string(__FILE__) + "_" + intToStr(__LINE__)); + + this->aiIntf->update(); + +/* + World *world = faction->getWorld(); + + //Config &config= Config::getInstance(); + //bool sortedUnitsAllowed = config.getBool("AllowGroupedUnitCommands","true"); + bool sortedUnitsAllowed = false; + if(sortedUnitsAllowed) { + faction->sortUnitsByCommandGroups(); + } + + MutexSafeWrapper safeMutex(faction->getUnitMutex(),string(__FILE__) + "_" + intToStr(__LINE__)); + + //if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start(); + if(minorDebugPerformance) chrono.start(); + + int unitCount = faction->getUnitCount(); + for(int j = 0; j < unitCount; ++j) { + Unit *unit = faction->getUnit(j); + if(unit == NULL) { + throw megaglest_runtime_error("unit == NULL"); + } + + int64 elapsed1 = 0; + if(minorDebugPerformance) elapsed1 = chrono.getMillis(); + + bool update = unit->needToUpdate(); + + if(minorDebugPerformance && (chrono.getMillis() - elapsed1) >= 1) printf("Faction [%d - %s] #1-unit threaded updates on frame: %d for [%d] unit # %d, unitCount = %d, took [%lld] msecs\n",faction->getStartLocationIndex(),faction->getType()->getName().c_str(),frameIndex.first,faction->getUnitPathfindingListCount(),j,unitCount,(long long int)chrono.getMillis() - elapsed1); + + //update = true; + if(update == true) { + + int64 elapsed2 = 0; + if(minorDebugPerformance) elapsed2 = chrono.getMillis(); + + world->getUnitUpdater()->updateUnitCommand(unit,frameIndex.first); + + if(minorDebugPerformance && (chrono.getMillis() - elapsed2) >= 1) printf("Faction [%d - %s] #2-unit threaded updates on frame: %d for [%d] unit # %d, unitCount = %d, took [%lld] msecs\n",faction->getStartLocationIndex(),faction->getType()->getName().c_str(),frameIndex.first,faction->getUnitPathfindingListCount(),j,unitCount,(long long int)chrono.getMillis() - elapsed2); + } + } + + if(minorDebugPerformance && chrono.getMillis() >= 1) printf("Faction [%d - %s] threaded updates on frame: %d for [%d] units took [%lld] msecs\n",faction->getStartLocationIndex(),faction->getType()->getName().c_str(),frameIndex.first,faction->getUnitPathfindingListCount(),(long long int)chrono.getMillis()); +*/ + + safeMutex.ReleaseLock(); + + setTaskCompleted(frameIndex.first); + } + + if(getQuitStatus() == true) { + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + break; + } + } + + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] ****************** ENDING worker thread this = %p\n",__FILE__,__FUNCTION__,__LINE__,this); + } + catch(const exception &ex) { + //setRunningStatus(false); + + SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",__FILE__,__FUNCTION__,__LINE__,ex.what()); + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + + throw megaglest_runtime_error(ex.what()); + } + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); +} + + + + + + +AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex, + int useStartLocation) : fp(NULL) { if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + this->aiMutex = new Mutex(); + this->workerThread = NULL; this->world= game.getWorld(); this->commander= game.getCommander(); this->console= game.getConsole(); @@ -75,6 +264,21 @@ AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex, int useSta } fprintf(fp, "MegaGlest AI log file for Tech [%s] Faction [%s] #%d\n\n",this->gameSettings->getTech().c_str(),this->world->getFaction(this->factionIndex)->getType()->getName().c_str(),this->factionIndex); } + + + if( Config::getInstance().getBool("EnableAIWorkerThreads","true") == true) { + if(workerThread != NULL) { + workerThread->signalQuit(); + if(workerThread->shutdownAndWait() == true) { + delete workerThread; + } + workerThread = NULL; + } + this->workerThread = new AiInterfaceThread(this); + this->workerThread->setUniqueID(__FILE__); + this->workerThread->start(); + } + if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); } @@ -82,11 +286,39 @@ AiInterface::~AiInterface() { if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI factionIndex = %d, teamIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,this->factionIndex,this->teamIndex); cacheUnitHarvestResourceLookup.clear(); + if(workerThread != NULL) { + workerThread->signalQuit(); + if(workerThread->shutdownAndWait() == true) { + delete workerThread; + } + workerThread = NULL; + } + if(fp) { fclose(fp); fp = NULL; } + + delete aiMutex; + aiMutex = NULL; } + +void AiInterface::signalWorkerThread(int frameIndex) { + if(workerThread != NULL) { + workerThread->signal(frameIndex); + } + else { + this->update(); + } +} + +bool AiInterface::isWorkerThreadSignalCompleted(int frameIndex) { + if(workerThread != NULL) { + return workerThread->isSignalCompleted(frameIndex); + } + return true; +} + // ==================== main ==================== void AiInterface::update() { @@ -100,6 +332,7 @@ void AiInterface::printLog(int logLevel, const string &s){ if(this->logLevel >= logLevel) { string logString= "(" + intToStr(factionIndex) + ") " + s; + MutexSafeWrapper safeMutex(aiMutex,string(__FILE__) + "_" + intToStr(__LINE__)); //print log to file if(fp != NULL) { fprintf(fp, "%s\n", logString.c_str()); diff --git a/source/glest_game/ai/ai_interface.h b/source/glest_game/ai/ai_interface.h index 43affe85..9c035d84 100644 --- a/source/glest_game/ai/ai_interface.h +++ b/source/glest_game/ai/ai_interface.h @@ -31,6 +31,26 @@ namespace Glest{ namespace Game{ /// The AI will interact with the game through this interface // ===================================================== +class AiInterfaceThread : public BaseThread { +protected: + + AiInterface *aiIntf; + Semaphore semTaskSignalled; + Mutex *triggerIdMutex; + std::pair frameIndex; + + virtual void setQuitStatus(bool value); + virtual void setTaskCompleted(int frameIndex); + virtual bool canShutdown(bool deleteSelfIfShutdownDelayed=false); + +public: + AiInterfaceThread(AiInterface *aiIntf); + virtual ~AiInterfaceThread(); + virtual void execute(); + void signal(int frameIndex); + bool isSignalCompleted(int frameIndex); +}; + class AiInterface { private: World *world; @@ -52,6 +72,10 @@ private: std::map cacheUnitHarvestResourceLookup; + Mutex *aiMutex; + + AiInterfaceThread *workerThread; + public: AiInterface(Game &game, int factionIndex, int teamIndex, int useStartLocation=-1); ~AiInterface(); @@ -59,6 +83,11 @@ public: //main void update(); + inline Mutex * getMutex() {return aiMutex;} + + void signalWorkerThread(int frameIndex); + bool isWorkerThreadSignalCompleted(int frameIndex); + //get int getTimer() const {return timer;} int getFactionIndex() const {return factionIndex;} diff --git a/source/glest_game/game/commander.h b/source/glest_game/game/commander.h index 4ba5c25c..ae449bf5 100644 --- a/source/glest_game/game/commander.h +++ b/source/glest_game/game/commander.h @@ -83,6 +83,7 @@ private: World *world; Chrono perfTimer; + Mutex commandMutex; //CommanderNetworkThread *networkThread; //Game *game; std::vector > replayCommandList; diff --git a/source/glest_game/game/game.cpp b/source/glest_game/game/game.cpp index 1533a3e5..7cb2e41e 100644 --- a/source/glest_game/game/game.cpp +++ b/source/glest_game/game/game.cpp @@ -1476,6 +1476,9 @@ void Game::update() { chrono.start(); //AiInterface if(commander.hasReplayCommandListForFrame() == false) { + + + /* for(int j = 0; j < world.getFactionCount(); ++j) { Faction *faction = world.getFaction(j); @@ -1492,6 +1495,54 @@ void Game::update() { if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] [i = %d] faction = %d, factionCount = %d, took msecs: %lld [after AI updates]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,j,world.getFactionCount(),chrono.getMillis()); } } + */ + + // Signal the faction threads to do any pre-processing + for(int j = 0; j < world.getFactionCount(); ++j) { + Faction *faction = world.getFaction(j); + + //printf("Faction Index = %d enableServerControlledAI = %d, isNetworkGame = %d, role = %d isCPU player = %d scriptManager.getPlayerModifiers(j)->getAiEnabled() = %d\n",j,enableServerControlledAI,isNetworkGame,role,faction->getCpuControl(enableServerControlledAI,isNetworkGame,role),scriptManager.getPlayerModifiers(j)->getAiEnabled()); + + if( faction->getCpuControl(enableServerControlledAI,isNetworkGame,role) == true && + scriptManager.getPlayerModifiers(j)->getAiEnabled() == true) { + + if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] [i = %d] faction = %d, factionCount = %d, took msecs: %lld [before AI updates]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,j,world.getFactionCount(),chrono.getMillis()); + aiInterfaces[j]->signalWorkerThread(world.getFrameCount()); + } + } + + bool workThreadsFinished = false; + Chrono chrono; + chrono.start(); + + const int MAX_FACTION_THREAD_WAIT_MILLISECONDS = 20000; + for(;chrono.getMillis() < MAX_FACTION_THREAD_WAIT_MILLISECONDS;) { + workThreadsFinished = true; + for(int j = 0; j < world.getFactionCount(); ++j) { + Faction *faction = world.getFaction(j); + if(faction == NULL) { + throw megaglest_runtime_error("faction == NULL"); + } + if( faction->getCpuControl(enableServerControlledAI,isNetworkGame,role) == true && + scriptManager.getPlayerModifiers(j)->getAiEnabled() == true) { + if(aiInterfaces[j]->isWorkerThreadSignalCompleted(world.getFrameCount()) == false) { + workThreadsFinished = false; + break; + } + } + } + if(workThreadsFinished == false) { + //sleep(0); + } + else { + break; + } + } + + + + + } else { // Simply show a progress message while replaying commands diff --git a/source/glest_game/graphics/renderer.cpp b/source/glest_game/graphics/renderer.cpp index 91dd3e37..71732e14 100644 --- a/source/glest_game/graphics/renderer.cpp +++ b/source/glest_game/graphics/renderer.cpp @@ -946,7 +946,7 @@ void Renderer::swapBuffers() { return; } //glFlush(); // should not be required - http://www.opengl.org/wiki/Common_Mistakes - glFlush(); + //glFlush(); GraphicsInterface::getInstance().getCurrentContext()->swapBuffers(); } diff --git a/source/glest_game/network/network_interface.cpp b/source/glest_game/network/network_interface.cpp index 8164b98b..2151cb39 100644 --- a/source/glest_game/network/network_interface.cpp +++ b/source/glest_game/network/network_interface.cpp @@ -226,14 +226,16 @@ GameNetworkInterface::GameNetworkInterface(){ void GameNetworkInterface::requestCommand(const NetworkCommand *networkCommand, bool insertAtStart) { assert(networkCommand != NULL); - //Mutex *mutex = getServerSynchAccessor(); + Mutex *mutex = getServerSynchAccessor(); if(insertAtStart == false) { + MutexSafeWrapper safeMutex(mutex,string(__FILE__) + "_" + intToStr(__LINE__)); //if(mutex != NULL) mutex->p(); requestedCommands.push_back(*networkCommand); //if(mutex != NULL) mutex->v(); } else { + MutexSafeWrapper safeMutex(mutex,string(__FILE__) + "_" + intToStr(__LINE__)); //if(mutex != NULL) mutex->p(); requestedCommands.insert(requestedCommands.begin(),*networkCommand); //if(mutex != NULL) mutex->v();