From 1ae2a9ae10f2c2cd3aaeaf18f4743b0846169d62 Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Sat, 15 May 2010 18:59:17 +0000 Subject: [PATCH] - Added partial threaded network server handling of incoming socket data to try to improve performance - Small changes to client network handling to be more accurate and efficient - Added observer mode when a user loses a network game --- source/glest_game/game/chat_manager.cpp | 11 +- source/glest_game/game/chat_manager.h | 4 + source/glest_game/game/commander.cpp | 10 +- source/glest_game/game/game.cpp | 35 +- .../glest_game/network/client_interface.cpp | 45 ++- source/glest_game/network/connection_slot.cpp | 85 ++-- source/glest_game/network/network_manager.h | 58 +++ source/glest_game/network/network_types.cpp | 5 +- source/glest_game/network/network_types.h | 4 +- .../glest_game/network/server_interface.cpp | 371 ++++++++++++++---- source/glest_game/network/server_interface.h | 60 ++- 11 files changed, 499 insertions(+), 189 deletions(-) create mode 100644 source/glest_game/network/network_manager.h diff --git a/source/glest_game/game/chat_manager.cpp b/source/glest_game/game/chat_manager.cpp index 06cb0859..c759caac 100644 --- a/source/glest_game/game/chat_manager.cpp +++ b/source/glest_game/game/chat_manager.cpp @@ -37,11 +37,13 @@ ChatManager::ChatManager(){ editEnabled= false; teamMode= false; thisTeamIndex= -1; + disableTeamMode = false; } void ChatManager::init(Console* console, int thisTeamIndex){ this->console= console; this->thisTeamIndex= thisTeamIndex; + this->disableTeamMode= false; } void ChatManager::keyUp(char key){ @@ -64,6 +66,13 @@ void ChatManager::keyUp(char key){ SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); } +void ChatManager::setDisableTeamMode(bool value) { + disableTeamMode = value; + + if(disableTeamMode == true) { + teamMode = false; + } +} void ChatManager::keyDown(char key){ SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); @@ -72,7 +81,7 @@ void ChatManager::keyDown(char key){ Lang &lang= Lang::getInstance(); //toggle team mode - if(!editEnabled && key=='H'){ + if(editEnabled == false && disableTeamMode == false && key=='H') { if(teamMode){ teamMode= false; console->addLine(lang.get("ChatMode") + ": " + lang.get("All")); diff --git a/source/glest_game/game/chat_manager.h b/source/glest_game/game/chat_manager.h index 36590239..d805f37d 100644 --- a/source/glest_game/game/chat_manager.h +++ b/source/glest_game/game/chat_manager.h @@ -31,6 +31,7 @@ private: private: bool editEnabled; bool teamMode; + bool disableTeamMode; Console* console; string text; int thisTeamIndex; @@ -47,6 +48,9 @@ public: bool getEditEnabled() const {return editEnabled;} bool getTeamMode() const {return teamMode;} string getText() const {return text;} + + bool getDisableTeamMode() const { return disableTeamMode; } + void setDisableTeamMode(bool value); }; }}//end namespace diff --git a/source/glest_game/game/commander.cpp b/source/glest_game/game/commander.cpp index 17e4673a..8df93965 100644 --- a/source/glest_game/game/commander.cpp +++ b/source/glest_game/game/commander.cpp @@ -375,17 +375,17 @@ Command* Commander::buildCommand(const NetworkCommand* networkCommand) const{ //validate unit if(unit == NULL) { char szBuf[1024]=""; - sprintf(szBuf,"In [%s::%s - %d] Can not find unit with id: %d. Game out of synch.",__FILE__,__FUNCTION__,__LINE__,networkCommand->getUnitId()); + sprintf(szBuf,"In [%s::%s Line: %d] Can not find unit with id: %d. Game out of synch.",__FILE__,__FUNCTION__,__LINE__,networkCommand->getUnitId()); throw runtime_error(szBuf); } else if(unit->getType() == NULL) { char szBuf[1024]=""; - sprintf(szBuf,"In [%s::%s - %d] unit->getType() == NULL for unit with id: %d",__FILE__,__FUNCTION__,__LINE__,networkCommand->getUnitId()); + sprintf(szBuf,"In [%s::%s Line: %d] unit->getType() == NULL for unit with id: %d",__FILE__,__FUNCTION__,__LINE__,networkCommand->getUnitId()); throw runtime_error(szBuf); } else if(unit->getFaction() == NULL) { char szBuf[1024]=""; - sprintf(szBuf,"In [%s::%s - %d] unit->getFaction() == NULL for unit with id: %d",__FILE__,__FUNCTION__,__LINE__,networkCommand->getUnitId()); + sprintf(szBuf,"In [%s::%s Line: %d] unit->getFaction() == NULL for unit with id: %d",__FILE__,__FUNCTION__,__LINE__,networkCommand->getUnitId()); throw runtime_error(szBuf); } @@ -395,8 +395,8 @@ Command* Commander::buildCommand(const NetworkCommand* networkCommand) const{ //validate command type if(ct == NULL) { char szBuf[1024]=""; - sprintf(szBuf,"In [%s::%s - %d] Can not find command type with id = %d\n%s\n in unit = %d [%s][%s].\nGame out of synch.", - __FILE__,__FUNCTION__,__LINE__,networkCommand->getCommandTypeId(),unit->getType()->getCommandTypeListDesc().c_str(),unit->getId(), unit->getFullName().c_str(),unit->getDesc().c_str()); + sprintf(szBuf,"In [%s::%s Line: %d] Can not find command type for network command = [%s]\n%s\n in unit = %d [%s][%s].\nGame out of synch.", + __FILE__,__FUNCTION__,__LINE__,networkCommand->toString().c_str(),unit->getType()->getCommandTypeListDesc().c_str(),unit->getId(), unit->getFullName().c_str(),unit->getDesc().c_str()); throw runtime_error(szBuf); } diff --git a/source/glest_game/game/game.cpp b/source/glest_game/game/game.cpp index 074fada7..10dc6861 100644 --- a/source/glest_game/game/game.cpp +++ b/source/glest_game/game/game.cpp @@ -1,4 +1,3 @@ -// ============================================================== // This file is part of Glest (www.glest.org) // // Copyright (C) 2001-2008 Marti�o Figueroa @@ -1061,36 +1060,43 @@ void Game::checkWinner(){ void Game::checkWinnerStandard(){ //lose bool lose= false; - if(!hasBuilding(world.getThisFaction())){ + if(hasBuilding(world.getThisFaction()) == false) { lose= true; - for(int i=0; iisAlly(world.getThisFaction())){ + for(int i=0; iisAlly(world.getThisFaction()) == false) { world.getStats()->setVictorious(i); } } gameOver= true; + // Let the poor user watch everything unfold + world.setFogOfWar(false); + // but don't let him cheat via teamchat + chatManager.setDisableTeamMode(true); showLoseMessageBox(); } //win - if(!lose){ + if(lose == false) { bool win= true; - for(int i=0; iisAlly(world.getThisFaction())){ + if(hasBuilding(world.getFaction(i)) && world.getFaction(i)->isAlly(world.getThisFaction()) == false) { win= false; } } } //if win - if(win){ - for(int i=0; i< world.getFactionCount(); ++i){ - if(world.getFaction(i)->isAlly(world.getThisFaction())){ + if(win) { + for(int i=0; i< world.getFactionCount(); ++i) { + if(world.getFaction(i)->isAlly(world.getThisFaction())) { world.getStats()->setVictorious(i); } } gameOver= true; + // Let the happy winner view everything left in the world + world.setFogOfWar(false); + showWinMessageBox(); } } @@ -1169,7 +1175,14 @@ int Game::getUpdateLoops(){ void Game::showLoseMessageBox(){ Lang &lang= Lang::getInstance(); - showMessageBox(lang.get("YouLose")+", "+lang.get("ExitGame?"), lang.get("BattleOver"), false); + + NetworkManager &networkManager= NetworkManager::getInstance(); + if(networkManager.isNetworkGame() == true && networkManager.getNetworkRole() == nrServer) { + showMessageBox(lang.get("YouLose")+", "+lang.get("ExitGameServer?"), lang.get("BattleOver"), false); + } + else { + showMessageBox(lang.get("YouLose")+", "+lang.get("ExitGame?"), lang.get("BattleOver"), false); + } } void Game::showWinMessageBox(){ diff --git a/source/glest_game/network/client_interface.cpp b/source/glest_game/network/client_interface.cpp index c8de78a0..2bd41227 100755 --- a/source/glest_game/network/client_interface.cpp +++ b/source/glest_game/network/client_interface.cpp @@ -428,8 +428,7 @@ void ClientInterface::updateKeyframe(int frameCount) { bool done= false; - while(done == false) - { + while(done == false) { //wait for the next message waitForMessage(); @@ -446,9 +445,10 @@ void ClientInterface::updateKeyframe(int frameCount) int waitCount = 0; //make sure we read the message NetworkMessageCommandList networkMessageCommandList; - while(receiveMessage(&networkMessageCommandList) == false) - { - sleep(waitSleepTime); + while(receiveMessage(&networkMessageCommandList) == false && + isConnected() == true) { + //sleep(waitSleepTime); + sleep(0); waitCount++; } @@ -456,8 +456,7 @@ void ClientInterface::updateKeyframe(int frameCount) chrono.start(); //check that we are in the right frame - if(networkMessageCommandList.getFrameCount() != frameCount) - { + if(networkMessageCommandList.getFrameCount() != frameCount) { string sErr = "Network synchronization error, frame counts do not match"; //throw runtime_error("Network synchronization error, frame counts do not match"); sendTextMessage(sErr,-1); @@ -468,8 +467,7 @@ void ClientInterface::updateKeyframe(int frameCount) } // give all commands - for(int i= 0; i messageWaitTimeout) - { + if(chrono.getMillis() > messageWaitTimeout) { //throw runtime_error("Timeout waiting for message"); sendTextMessage("Timeout waiting for message",-1); DisplayErrorMessage("Timeout waiting for message"); diff --git a/source/glest_game/network/connection_slot.cpp b/source/glest_game/network/connection_slot.cpp index 66e5affa..ef9af332 100644 --- a/source/glest_game/network/connection_slot.cpp +++ b/source/glest_game/network/connection_slot.cpp @@ -60,15 +60,12 @@ ConnectionSlot::~ConnectionSlot() SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",__FILE__,__FUNCTION__); } -void ConnectionSlot::update() -{ +void ConnectionSlot::update() { update(true); } -void ConnectionSlot::update(bool checkForNewClients) -{ - if(socket == NULL) - { +void ConnectionSlot::update(bool checkForNewClients) { + if(socket == NULL) { if(networkGameDataSynchCheckOkMap) networkGameDataSynchCheckOkMap = false; if(networkGameDataSynchCheckOkTile) networkGameDataSynchCheckOkTile = false; if(networkGameDataSynchCheckOkTech) networkGameDataSynchCheckOkTech = false; @@ -76,8 +73,7 @@ void ConnectionSlot::update(bool checkForNewClients) // Is the listener socket ready to be read? //if(serverInterface->getServerSocket()->isReadable() == true) - if(checkForNewClients == true) - { + if(checkForNewClients == true) { //SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] BEFORE accept new client connection, serverInterface->getOpenSlotCount() = %d\n",__FILE__,__FUNCTION__,serverInterface->getOpenSlotCount()); bool hasOpenSlots = (serverInterface->getOpenSlotCount() > 0); if(serverInterface->getServerSocket()->hasDataToRead() == true) { @@ -110,10 +106,8 @@ void ConnectionSlot::update(bool checkForNewClients) } } } - else - { - if(socket->isConnected()) - { + else { + if(socket->isConnected()) { chatText.clear(); chatSender.clear(); chatTeamIndex= -1; @@ -121,7 +115,7 @@ void ConnectionSlot::update(bool checkForNewClients) NetworkMessageType networkMessageType= getNextMessageType(); //process incoming commands - switch(networkMessageType){ + switch(networkMessageType) { case nmtInvalid: break; @@ -131,8 +125,7 @@ void ConnectionSlot::update(bool checkForNewClients) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got nmtText\n",__FILE__,__FUNCTION__); NetworkMessageText networkMessageText; - if(receiveMessage(&networkMessageText)) - { + if(receiveMessage(&networkMessageText)) { chatText = networkMessageText.getText(); chatSender = networkMessageText.getSender(); chatTeamIndex = networkMessageText.getTeamIndex(); @@ -148,10 +141,8 @@ void ConnectionSlot::update(bool checkForNewClients) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got nmtCommandList\n",__FILE__,__FUNCTION__); NetworkMessageCommandList networkMessageCommandList; - if(receiveMessage(&networkMessageCommandList)) - { - for(int i= 0; irequestCommand(networkMessageCommandList.getCommand(i)); } } @@ -222,19 +213,15 @@ void ConnectionSlot::update(bool checkForNewClients) if( networkGameDataSynchCheckOkMap == true && networkGameDataSynchCheckOkTile == true && - networkGameDataSynchCheckOkTech == true) // && - //networkGameDataSynchCheckOkFogOfWar == true) - { + networkGameDataSynchCheckOkTech == true) { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] client data synch ok\n",__FILE__,__FUNCTION__); } - else - { + else { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] mapCRC = %d, remote = %d\n",__FILE__,__FUNCTION__,mapCRC,networkMessageSynchNetworkGameDataStatus.getMapCRC()); SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] tilesetCRC = %d, remote = %d\n",__FILE__,__FUNCTION__,tilesetCRC,networkMessageSynchNetworkGameDataStatus.getTilesetCRC()); SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] techCRC = %d, remote = %d\n",__FILE__,__FUNCTION__,techCRC,networkMessageSynchNetworkGameDataStatus.getTechCRC()); - if(allowDownloadDataSynch == true) - { + if(allowDownloadDataSynch == true) { // Now get all filenames with their CRC values and send to the client vctFileList.clear(); @@ -250,34 +237,27 @@ void ConnectionSlot::update(bool checkForNewClients) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] gameSettings.getScenarioDir() = [%s] gameSettings.getScenario() = [%s] scenarioDir = [%s]\n",__FILE__,__FUNCTION__,__LINE__,serverInterface->getGameSettings()->getScenarioDir().c_str(),serverInterface->getGameSettings()->getScenario().c_str(),scenarioDir.c_str()); } - if(networkGameDataSynchCheckOkTile == false) - { - if(tilesetCRC == 0) - { + if(networkGameDataSynchCheckOkTile == false) { + if(tilesetCRC == 0) { //vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_tilesets) + "/" + serverInterface->getGameSettings()->getTileset() + "/*", "", &vctFileList); vctFileList = getFolderTreeContentsCheckSumListRecursively(config.getPathListForType(ptTilesets,scenarioDir), string("/") + serverInterface->getGameSettings()->getTileset() + string("/*"), "", &vctFileList); } - else - { + else { //vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_tilesets) + "/" + serverInterface->getGameSettings()->getTileset() + "/*", ".xml", &vctFileList); vctFileList = getFolderTreeContentsCheckSumListRecursively(config.getPathListForType(ptTilesets,scenarioDir), "/" + serverInterface->getGameSettings()->getTileset() + "/*", ".xml", &vctFileList); } } - if(networkGameDataSynchCheckOkTech == false) - { - if(techCRC == 0) - { + if(networkGameDataSynchCheckOkTech == false) { + if(techCRC == 0) { //vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_techs) + "/" + serverInterface->getGameSettings()->getTech() + "/*", "", &vctFileList); vctFileList = getFolderTreeContentsCheckSumListRecursively(config.getPathListForType(ptTechs,scenarioDir),"/" + serverInterface->getGameSettings()->getTech() + "/*", "", &vctFileList); } - else - { + else { //vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_techs) + "/" + serverInterface->getGameSettings()->getTech() + "/*", ".xml", &vctFileList); vctFileList = getFolderTreeContentsCheckSumListRecursively(config.getPathListForType(ptTechs,scenarioDir),"/" + serverInterface->getGameSettings()->getTech() + "/*", ".xml", &vctFileList); } } - if(networkGameDataSynchCheckOkMap == false) - { + if(networkGameDataSynchCheckOkMap == false) { vctFileList.push_back(std::pair(Map::getMapPath(serverInterface->getGameSettings()->getMap(),scenarioDir),mapCRC)); } @@ -313,8 +293,7 @@ void ConnectionSlot::update(bool checkForNewClients) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got nmtSynchNetworkGameDataFileGet\n",__FILE__,__FUNCTION__); NetworkMessageSynchNetworkGameDataFileGet networkMessageSynchNetworkGameDataFileGet; - if(receiveMessage(&networkMessageSynchNetworkGameDataFileGet)) - { + if(receiveMessage(&networkMessageSynchNetworkGameDataFileGet)) { FileTransferInfo fileInfo; fileInfo.hostType = eServer; //fileInfo.serverIP = this->ip.getString(); @@ -330,10 +309,8 @@ void ConnectionSlot::update(bool checkForNewClients) case nmtSwitchSetupRequest: { SwitchSetupRequest switchSetupRequest; - if(receiveMessage(&switchSetupRequest)) - { - if(serverInterface->getSwitchSetupRequests()[switchSetupRequest.getCurrentFactionIndex()]==NULL) - { + if(receiveMessage(&switchSetupRequest)) { + if(serverInterface->getSwitchSetupRequests()[switchSetupRequest.getCurrentFactionIndex()]==NULL) { serverInterface->getSwitchSetupRequests()[switchSetupRequest.getCurrentFactionIndex()]= new SwitchSetupRequest(); } *(serverInterface->getSwitchSetupRequests()[switchSetupRequest.getCurrentFactionIndex()])=switchSetupRequest; @@ -367,8 +344,7 @@ void ConnectionSlot::update(bool checkForNewClients) close(); } } - else - { + else { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] calling close...\n",__FILE__,__FUNCTION__); close(); @@ -376,8 +352,7 @@ void ConnectionSlot::update(bool checkForNewClients) } } -void ConnectionSlot::close() -{ +void ConnectionSlot::close() { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] START\n",__FILE__,__FUNCTION__); delete socket; @@ -394,15 +369,7 @@ void ConnectionSlot::close() SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",__FILE__,__FUNCTION__); } -/* -bool ConnectionSlot::getFogOfWar() -{ - return networkGameDataSynchCheckOkFogOfWar; -} -*/ - -bool ConnectionSlot::hasValidSocketId() -{ +bool ConnectionSlot::hasValidSocketId() { bool result = (socket != NULL && socket->getSocketId() > 0); return result; } diff --git a/source/glest_game/network/network_manager.h b/source/glest_game/network/network_manager.h new file mode 100644 index 00000000..f6077808 --- /dev/null +++ b/source/glest_game/network/network_manager.h @@ -0,0 +1,58 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martio Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_NETWORKMANAGER_H_ +#define _GLEST_GAME_NETWORKMANAGER_H_ + +#include + +#include "socket.h" +#include "checksum.h" +#include "server_interface.h" +#include "client_interface.h" + +using Shared::Util::Checksum; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkManager +// ===================================================== + +enum NetworkRole{ + nrServer, + nrClient, + nrIdle +}; + +class NetworkManager{ +private: + GameNetworkInterface* gameNetworkInterface; + NetworkRole networkRole; + +public: + static NetworkManager &getInstance(); + + NetworkManager(); + void init(NetworkRole networkRole); + void end(); + void update(); + + bool isNetworkGame(); + GameNetworkInterface* getGameNetworkInterface(); + ServerInterface* getServerInterface(); + ClientInterface* getClientInterface(); + NetworkRole getNetworkRole() { return networkRole; } +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/network_types.cpp b/source/glest_game/network/network_types.cpp index e970662a..aca0348f 100644 --- a/source/glest_game/network/network_types.cpp +++ b/source/glest_game/network/network_types.cpp @@ -37,6 +37,7 @@ NetworkCommand::NetworkCommand(World *world, int networkCommandType, int unitId, , wantQueue(wantQueue) { assert(targetId == -1 || facing == -1); this->targetId = targetId >= 0 ? targetId : facing; + this->fromFactionIndex = world->getThisFactionIndex(); if(this->networkCommandType == nctGiveCommand) { const Unit *unit= world->findUnitById(this->unitId); @@ -74,8 +75,8 @@ void NetworkCommand::preprocessNetworkCommand(World *world) { string NetworkCommand::toString() const { char szBuf[1024]=""; - sprintf(szBuf,"networkCommandType = %d\nunitId = %d\ncommandTypeId = %d\npositionX = %d\nthis->positionY = %d\nunitTypeId = %d\ntargetId = %d\nwantQueue= %d", - networkCommandType,unitId,commandTypeId,positionX,this->positionY,unitTypeId,targetId,wantQueue); + sprintf(szBuf,"networkCommandType = %d\nunitId = %d\ncommandTypeId = %d\npositionX = %d\npositionY = %d\nunitTypeId = %d\ntargetId = %d\nwantQueue= %d\nfromFactionIndex = %d", + networkCommandType,unitId,commandTypeId,positionX,this->positionY,unitTypeId,targetId,wantQueue,fromFactionIndex); string result = szBuf; return result; diff --git a/source/glest_game/network/network_types.h b/source/glest_game/network/network_types.h index 29bd7a37..08fa4e96 100644 --- a/source/glest_game/network/network_types.h +++ b/source/glest_game/network/network_types.h @@ -1,7 +1,7 @@ // ============================================================== // This file is part of Glest (www.glest.org) // -// Copyright (C) 2001-2008 Martiño Figueroa +// Copyright (C) 2001-2008 Martio Figueroa // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published @@ -68,6 +68,7 @@ private: int16 unitTypeId; int16 targetId; int16 wantQueue; + int16 fromFactionIndex; public: NetworkCommand(){}; @@ -91,6 +92,7 @@ public: int getUnitTypeId() const {return unitTypeId;} int getTargetId() const {return targetId;} int getWantQueue() const {return wantQueue;} + int getFromFactionIndex() const {return fromFactionIndex;} void preprocessNetworkCommand(World *world); string toString() const; diff --git a/source/glest_game/network/server_interface.cpp b/source/glest_game/network/server_interface.cpp index eea20193..7bab4eb8 100644 --- a/source/glest_game/network/server_interface.cpp +++ b/source/glest_game/network/server_interface.cpp @@ -30,6 +30,113 @@ using namespace Shared::Util; namespace Glest{ namespace Game{ +ConnectionSlotThread::ConnectionSlotThread() : BaseThread() { + this->slotInterface = NULL; +} + +ConnectionSlotThread::ConnectionSlotThread(ConnectionSlotCallbackInterface *slotInterface) : BaseThread() { + this->slotInterface = slotInterface; +} + +void ConnectionSlotThread::setQuitStatus(bool value) { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d value = %d\n",__FILE__,__FUNCTION__,__LINE__,value); + + BaseThread::setQuitStatus(value); + if(value == true) { + signalUpdate(NULL); + } + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); +} + +void ConnectionSlotThread::signalUpdate(ConnectionSlotEvent *event) { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + if(event != NULL) { + triggerIdMutex.p(); + this->event = event; + triggerIdMutex.v(); + } + semTaskSignalled.signal(); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); +} + +void ConnectionSlotThread::setTaskCompleted(ConnectionSlotEvent *event) { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + if(event != NULL) { + triggerIdMutex.p(); + event->eventCompleted = true; + triggerIdMutex.v(); + } + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); +} + +bool ConnectionSlotThread::isSignalCompleted() { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + triggerIdMutex.p(); + bool result = this->event->eventCompleted; + triggerIdMutex.v(); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + return result; +} + +void ConnectionSlotThread::execute() { + try { + setRunningStatus(true); + + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + + unsigned int idx = 0; + for(;this->slotInterface != NULL;) { + if(getQuitStatus() == true) { + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + break; + } + + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + semTaskSignalled.waitTillSignalled(); + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + if(getQuitStatus() == true) { + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + break; + } + + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + + this->slotInterface->slotUpdateTask(this->event); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + if(getQuitStatus() == true) { + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + break; + } + + setTaskCompleted(this->event); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + } + + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + } + catch(const exception &ex) { + setRunningStatus(false); + + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + + throw runtime_error(ex.what()); + } + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + setRunningStatus(false); +} + // ===================================================== // class ServerInterface // ===================================================== @@ -41,6 +148,7 @@ ServerInterface::ServerInterface(){ for(int i= 0; istart(); + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",__FILE__,__FUNCTION__); } bool ServerInterface::switchSlot(int fromPlayerIndex,int toPlayerIndex){ - SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] START\n",__FILE__,__FUNCTION__); + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); bool result=false; assert(fromPlayerIndex>=0 && fromPlayerIndex=0 && toPlayerIndexconnectionSlot; + bool &socketTriggered = event->socketTriggered; + bool checkForNewClients = true; + + if(connectionSlot != NULL && + (gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggered == true))) { + if(connectionSlot->isConnected() == false || socketTriggered == true) { + if(gameHasBeenInitiated) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] socketTriggeredList[i] = %i\n",__FILE__,__FUNCTION__,(socketTriggered ? 1 : 0)); + + if(connectionSlot->isConnected()) { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] about to call connectionSlot->update() for socketId = %d\n",__FILE__,__FUNCTION__,__LINE__,connectionSlot->getSocket()->getSocketId()); + } + else { + if(gameHasBeenInitiated) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] getSocket() == NULL\n",__FILE__,__FUNCTION__); + } + connectionSlot->update(checkForNewClients); + + // This means no clients are trying to connect at the moment + if(connectionSlot != NULL && connectionSlot->getSocket() == NULL) { + checkForNewClients = false; + } + + if(connectionSlot != NULL && + connectionSlot->getChatText().empty() == false) { + chatText = connectionSlot->getChatText(); + chatSender = connectionSlot->getChatSender(); + chatTeamIndex = connectionSlot->getChatTeamIndex(); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #1 about to broadcast nmtText chatText [%s] chatSender [%s] chatTeamIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,chatText.c_str(),chatSender.c_str(),chatTeamIndex); + + NetworkMessageText networkMessageText(chatText,chatSender,chatTeamIndex); + broadcastMessage(&networkMessageText, connectionSlot->getPlayerIndex()); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + } + } + } + } + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); +} + +void ServerInterface::update() { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + std::map socketTriggeredList; //update all slots - for(int i= 0; i < GameConstants::maxPlayers; ++i) - { + for(int i= 0; i < GameConstants::maxPlayers; ++i) { ConnectionSlot* connectionSlot= slots[i]; if(connectionSlot != NULL && connectionSlot->getSocket() != NULL && - slots[i]->getSocket()->getSocketId() > 0) - { + slots[i]->getSocket()->getSocketId() > 0) { socketTriggeredList[connectionSlot->getSocket()->getSocketId()] = false; } } + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + chatText.clear(); chatSender.clear(); chatTeamIndex= -1; - if(gameHasBeenInitiated == false || socketTriggeredList.size() > 0) - { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + if(gameHasBeenInitiated == false || socketTriggeredList.size() > 0) { if(gameHasBeenInitiated) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] socketTriggeredList.size() = %d\n",__FILE__,__FUNCTION__,socketTriggeredList.size()); + std::map eventList; bool hasData = Socket::hasDataToRead(socketTriggeredList); if(hasData) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] hasData == true\n",__FILE__,__FUNCTION__); - if(gameHasBeenInitiated == false || hasData == true) - { - //if(gameHasBeenInitiated && Socket::enableNetworkDebugInfo) printf("In [%s::%s] hasData == true\n",__FILE__,__FUNCTION__); - - //std::vector vctTeamMessages; - + if(gameHasBeenInitiated == false || hasData == true) { //update all slots bool checkForNewClients = true; - for(int i= 0; igetSocket() != NULL ? socketTriggeredList[connectionSlot->getSocket()->getSocketId()] : false); + ConnectionSlotEvent &event = eventList[i]; + event.connectionSlot = connectionSlot; + event.socketTriggered = socketTriggered; + event.triggerId = i; + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + if(slotThreads[i] != NULL) { + slotThreads[i]->signalUpdate(&event); + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + } + //updateSlot(event); + +/* if(connectionSlot != NULL && - (gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true))) - { + (gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true))) { if(connectionSlot->isConnected() == false || - (socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true)) - { + (socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true)) { if(gameHasBeenInitiated) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] socketTriggeredList[i] = %i\n",__FILE__,__FUNCTION__,(socketTriggeredList[connectionSlot->getSocket()->getSocketId()] ? 1 : 0)); - if(connectionSlot->isConnected()) - { - SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] calling slots[i]->update() for slot = %d socketId = %d\n", + if(connectionSlot->isConnected()) { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] about to call slots[i]->update() for slot = %d socketId = %d\n", __FILE__,__FUNCTION__,i,connectionSlot->getSocket()->getSocketId()); } - else - { + else { if(gameHasBeenInitiated) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] slot = %d getSocket() == NULL\n",__FILE__,__FUNCTION__,i); } connectionSlot->update(checkForNewClients); // This means no clients are trying to connect at the moment - if(connectionSlot != NULL && connectionSlot->getSocket() == NULL) - { + if(connectionSlot != NULL && connectionSlot->getSocket() == NULL) { checkForNewClients = false; } if(connectionSlot != NULL && - //connectionSlot->isConnected() == true && - connectionSlot->getChatText().empty() == false) - { + connectionSlot->getChatText().empty() == false) { chatText = connectionSlot->getChatText(); chatSender = connectionSlot->getChatSender(); chatTeamIndex = connectionSlot->getChatTeamIndex(); - //TeamMessageData teamMessageData; - //teamMessageData.chatSender = connectionSlot->getChatSender(); - //teamMessageData.chatText = connectionSlot->getChatText(); - //teamMessageData.chatTeamIndex = connectionSlot->getChatTeamIndex(); - //teamMessageData.sourceTeamIndex = i; - //vctTeamMessages.push_back(teamMessageData); - SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #1 about to broadcast nmtText chatText [%s] chatSender [%s] chatTeamIndex = %d for SlotIndex# %d\n",__FILE__,__FUNCTION__,__LINE__,chatText.c_str(),chatSender.c_str(),chatTeamIndex,i); NetworkMessageText networkMessageText(chatText,chatSender,chatTeamIndex); @@ -212,32 +391,51 @@ void ServerInterface::update() } } } +*/ } + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + + std::map slotsCompleted; + for(bool threadsDone = false; threadsDone == false;) { + threadsDone = true; + // Examine all threads for completion of delegation + for(int i= 0; i< GameConstants::maxPlayers; ++i) { + if(slotThreads[i] != NULL && slotsCompleted.find(i) == slotsCompleted.end()) { + if(slotThreads[i]->isSignalCompleted() == false && + slotThreads[i]->getQuitStatus() == false && + slotThreads[i]->getRunningStatus() == true) { + threadsDone = false; + break; + } + else { + slotsCompleted[i] = true; + } + } + } + sleep(0); + } + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + //process text messages - if(chatText.empty() == true) - { + if(chatText.empty() == true) { chatText.clear(); chatSender.clear(); chatTeamIndex= -1; - for(int i= 0; i< GameConstants::maxPlayers; ++i) - { + for(int i= 0; i< GameConstants::maxPlayers; ++i) { ConnectionSlot* connectionSlot= slots[i]; if(connectionSlot!= NULL && - (gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true))) - { - if(connectionSlot->isConnected() && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true) - { + (gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true))) { + if(connectionSlot->isConnected() && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true) { if(connectionSlot->getSocket() != NULL) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] calling connectionSlot->getNextMessageType() for slots[i]->getSocket()->getSocketId() = %d\n", __FILE__,__FUNCTION__,connectionSlot->getSocket()->getSocketId()); - if(connectionSlot->getNextMessageType() == nmtText) - { + if(connectionSlot->getNextMessageType() == nmtText) { NetworkMessageText networkMessageText; - if(connectionSlot->receiveMessage(&networkMessageText)) - { + if(connectionSlot->receiveMessage(&networkMessageText)) { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] #2 about to broadcast nmtText msg for SlotIndex# %d\n",__FILE__,__FUNCTION__,i); broadcastMessage(&networkMessageText, i); @@ -251,8 +449,12 @@ void ServerInterface::update() } } } + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); } } + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); } void ServerInterface::updateKeyframe(int frameCount){ @@ -262,12 +464,12 @@ void ServerInterface::updateKeyframe(int frameCount){ NetworkMessageCommandList networkMessageCommandList(frameCount); //build command list, remove commands from requested and add to pending - while(!requestedCommands.empty()){ - if(networkMessageCommandList.addCommand(&requestedCommands.back())){ + while(!requestedCommands.empty()) { + if(networkMessageCommandList.addCommand(&requestedCommands.back())) { pendingCommands.push_back(requestedCommands.back()); requestedCommands.pop_back(); } - else{ + else { break; } } @@ -507,6 +709,8 @@ bool ServerInterface::launchGame(const GameSettings* gameSettings){ SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + serverSynchAccessor.p(); + for(int i= 0; iisConnected()) - { + if(i != excludeSlot && connectionSlot != NULL) { + if(connectionSlot->isConnected()) { - //SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] before sendMessage\n",__FILE__,__FUNCTION__); + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] before sendMessage\n",__FILE__,__FUNCTION__); connectionSlot->sendMessage(networkMessage); } - else if(gameHasBeenInitiated == true) - { + else if(gameHasBeenInitiated == true) { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] #1 before removeSlot for slot# %d\n",__FILE__,__FUNCTION__,i); removeSlot(i); } } else if(i == excludeSlot && gameHasBeenInitiated == true && - connectionSlot != NULL && connectionSlot->isConnected() == false) - { + connectionSlot != NULL && connectionSlot->isConnected() == false) { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] #2 before removeSlot for slot# %d\n",__FILE__,__FUNCTION__,i); removeSlot(i); } } - //SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + serverSynchAccessor.v(); + + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); } void ServerInterface::broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot){ + serverSynchAccessor.p(); SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); - for(int i= 0; iisConnected()){ + if(i!= excludeSlot && connectionSlot!= NULL) { + if(connectionSlot->isConnected()) { //SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] before sendMessage\n",__FILE__,__FUNCTION__); - connectionSlot->sendMessage(networkMessage); } } } SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); + serverSynchAccessor.v(); } -void ServerInterface::updateListen() -{ +void ServerInterface::updateListen() { + if(gameHasBeenInitiated == true) { + return; + } + + serverSynchAccessor.p(); + int openSlotCount= 0; for(int i= 0; iisConnected() == false) @@ -624,6 +837,8 @@ int ServerInterface::getOpenSlotCount() { } } + serverSynchAccessor.v(); + return openSlotCount; } diff --git a/source/glest_game/network/server_interface.h b/source/glest_game/network/server_interface.h index f658de2b..4219e043 100644 --- a/source/glest_game/network/server_interface.h +++ b/source/glest_game/network/server_interface.h @@ -18,32 +18,71 @@ #include "network_interface.h" #include "connection_slot.h" #include "socket.h" +#include "base_thread.h" using std::vector; using Shared::Platform::ServerSocket; namespace Glest{ namespace Game{ + +// ===================================================== +// class ConnectionSlotThread +// ===================================================== + +class ConnectionSlotEvent { +public: + + int64 triggerId; + ConnectionSlot* connectionSlot; + bool socketTriggered; + bool eventCompleted; +}; + +// +// This interface describes the methods a callback object must implement +// +class ConnectionSlotCallbackInterface { +public: + virtual void slotUpdateTask(ConnectionSlotEvent *event) = 0; +}; + +class ConnectionSlotThread : public BaseThread +{ +protected: + + ConnectionSlotCallbackInterface *slotInterface; + Semaphore semTaskSignalled; + Mutex triggerIdMutex; + ConnectionSlotEvent *event; + + virtual void setQuitStatus(bool value); + virtual void setTaskCompleted(ConnectionSlotEvent *event); + +public: + ConnectionSlotThread(); + ConnectionSlotThread(ConnectionSlotCallbackInterface *slotInterface); + virtual void execute(); + void signalUpdate(ConnectionSlotEvent *event); + bool isSignalCompleted(); + +}; + // ===================================================== // class ServerInterface // ===================================================== -typedef struct -{ - string chatText; - string chatSender; - int chatTeamIndex; - int sourceTeamIndex; +class ServerInterface: public GameNetworkInterface, public ConnectionSlotCallbackInterface { -} TeamMessageData; - -class ServerInterface: public GameNetworkInterface{ private: ConnectionSlot* slots[GameConstants::maxPlayers]; ServerSocket serverSocket; bool gameHasBeenInitiated; int gameSettingsUpdateCount; SwitchSetupRequest* switchSetupRequests[GameConstants::maxPlayers]; + Mutex serverSynchAccessor; + + ConnectionSlotThread* slotThreads[GameConstants::maxPlayers]; public: ServerInterface(); @@ -81,10 +120,13 @@ public: void updateListen(); virtual bool getConnectHasHandshaked() const { return false; } + virtual void slotUpdateTask(ConnectionSlotEvent *event); + private: void broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot= -1); void broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot = -1); bool shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,ConnectionSlot* connectionSlot); + void updateSlot(ConnectionSlotEvent *event); }; }}//end namespace