- attempt to fix game freeze when lag causes game pauses

This commit is contained in:
Mark Vejvoda 2011-01-11 08:45:58 +00:00
parent b778098d13
commit e319c2cf62
8 changed files with 281 additions and 22 deletions

View File

@ -37,6 +37,135 @@ namespace Glest{ namespace Game{
// ===================== PUBLIC ========================
CommanderNetworkThread::CommanderNetworkThread() : BaseThread() {
this->idStatus = make_pair<int,bool>(-1,false);
this->commanderInterface = NULL;
}
CommanderNetworkThread::CommanderNetworkThread(CommanderNetworkCallbackInterface *commanderInterface) : BaseThread() {
this->idStatus = make_pair<int,bool>(-1,false);
this->commanderInterface = commanderInterface;
}
void CommanderNetworkThread::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(-1);
}
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
}
void CommanderNetworkThread::signalUpdate(int id) {
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] event = %p\n",__FILE__,__FUNCTION__,__LINE__,event);
MutexSafeWrapper safeMutex(&idMutex);
this->idStatus.first = id;
this->idStatus.second = false;
safeMutex.ReleaseLock();
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
semTaskSignalled.signal();
}
void CommanderNetworkThread::setTaskCompleted(int id) {
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
MutexSafeWrapper safeMutex(&idMutex);
this->idStatus.second = true;
safeMutex.ReleaseLock();
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
}
bool CommanderNetworkThread::isSignalCompleted(int id) {
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] slotIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,slotIndex);
MutexSafeWrapper safeMutex(&idMutex);
bool result = this->idStatus.second;
safeMutex.ReleaseLock();
//if(result == false) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] slotIndex = %d, result = %d\n",__FILE__,__FUNCTION__,__LINE__,slotIndex,result);
return result;
}
void CommanderNetworkThread::execute() {
RunningStatusSafeWrapper runningStatus(this);
try {
//setRunningStatus(true);
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
unsigned int idx = 0;
for(;this->commanderInterface != 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__);
MutexSafeWrapper safeMutex(&idMutex);
if(idStatus.first > 0) {
int updateId = this->idStatus.first;
safeMutex.ReleaseLock();
this->commanderInterface->commanderNetworkUpdateTask(updateId);
setTaskCompleted(updateId);
}
else {
safeMutex.ReleaseLock();
}
//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::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::debugError,"In [%s::%s Line: %d] Error [%s]\n",__FILE__,__FUNCTION__,__LINE__,ex.what());
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
// =====================================================
Commander::Commander() {
this->networkThread = new CommanderNetworkThread(this);
this->networkThread->setUniqueID(__FILE__);
this->networkThread->start();
}
Commander::~Commander() {
if(BaseThread::shutdownAndWait(networkThread) == true) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
delete networkThread;
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
networkThread = NULL;
}
void Commander::init(World *world){
this->world= world;
}
@ -325,11 +454,29 @@ CommandResult Commander::pushNetworkCommand(const NetworkCommand* networkCommand
return cr;
}
void Commander::updateNetwork() {
void Commander::signalNetworkUpdate(Game *game) {
if(this->networkThread != NULL) {
this->game = game;
this->networkThread->signalUpdate(1);
time_t elapsedWait = time(NULL);
for(;difftime(time(NULL),elapsedWait) <= 4 &&
this->networkThread->isSignalCompleted(1) == false;) {
game->render();
}
}
}
void Commander::commanderNetworkUpdateTask(int id) {
updateNetwork(game);
}
void Commander::updateNetwork(Game *game) {
NetworkManager &networkManager= NetworkManager::getInstance();
//check that this is a keyframe
GameSettings *gameSettings = this->world->getGame()->getGameSettings();
//GameSettings *gameSettings = this->world->getGame()->getGameSettings();
GameSettings *gameSettings = game->getGameSettings();
if( networkManager.isNetworkGame() == false ||
(world->getFrameCount() % gameSettings->getNetworkFramePeriod()) == 0) {

View File

@ -18,6 +18,7 @@
#include "selection.h"
#include "command_type.h"
#include "platform_util.h"
#include "base_thread.h"
#include "leak_dumper.h"
using std::vector;
@ -32,6 +33,7 @@ class Unit;
class Command;
class CommandType;
class NetworkCommand;
class Game;
// =====================================================
// class Commander
@ -39,7 +41,35 @@ class NetworkCommand;
/// Gives commands to the units
// =====================================================
class Commander{
//
// This interface describes the methods a callback object must implement
//
class CommanderNetworkCallbackInterface {
public:
virtual void commanderNetworkUpdateTask(int id) = 0;
};
class CommanderNetworkThread : public BaseThread
{
protected:
CommanderNetworkCallbackInterface *commanderInterface;
Semaphore semTaskSignalled;
virtual void setQuitStatus(bool value);
virtual void setTaskCompleted(int id);
Mutex idMutex;
std::pair<int,bool> idStatus;
public:
CommanderNetworkThread();
CommanderNetworkThread(CommanderNetworkCallbackInterface *commanderInterface);
virtual void execute();
void signalUpdate(int id);
bool isSignalCompleted(int id);
};
class Commander : public CommanderNetworkCallbackInterface {
private:
typedef vector<CommandResult> CommandResultContainer;
@ -47,9 +77,16 @@ private:
World *world;
Chrono perfTimer;
CommanderNetworkThread *networkThread;
Game *game;
public:
Commander();
~Commander();
void signalNetworkUpdate(Game *game);
void init(World *world);
void updateNetwork();
void updateNetwork(Game *game);
CommandResult tryGiveCommand(const Selection *selection, const CommandType *commandType,
const Vec2i &pos, const UnitType* unitType,
@ -68,6 +105,8 @@ private:
CommandResult computeResult(const CommandResultContainer &results) const;
void giveNetworkCommand(NetworkCommand* networkCommand) const;
Command* buildCommand(const NetworkCommand* networkCommand) const;
virtual void commanderNetworkUpdateTask(int id);
};
}} //end namespace

View File

@ -735,7 +735,9 @@ void Game::update() {
//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
// Commander
commander.updateNetwork();
//commander.updateNetwork();
commander.signalNetworkUpdate(this);
if(chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld [commander updateNetwork i = %d]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),i);
if(chrono.getMillis() > 0) chrono.start();

View File

@ -62,30 +62,60 @@ void ConnectionSlotThread::signalUpdate(ConnectionSlotEvent *event) {
if(event != NULL) {
MutexSafeWrapper safeMutex(&triggerIdMutex);
eventList.push_back(event);
eventList.push_back(*event);
safeMutex.ReleaseLock();
}
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
semTaskSignalled.signal();
}
void ConnectionSlotThread::setTaskCompleted(ConnectionSlotEvent *event) {
void ConnectionSlotThread::setTaskCompleted(int eventId) {
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
if(event != NULL) {
if(eventId > 0) {
MutexSafeWrapper safeMutex(&triggerIdMutex);
event->eventCompleted = true;
eventList.erase(eventList.begin());
//event->eventCompleted = true;
for(int i = 0; i < eventList.size(); ++i) {
ConnectionSlotEvent &slotEvent = eventList[i];
if(slotEvent.eventId == eventId) {
//eventList.erase(eventList.begin() + i);
slotEvent.eventCompleted = true;
break;
}
}
safeMutex.ReleaseLock();
}
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
}
void ConnectionSlotThread::purgeCompletedEvents() {
MutexSafeWrapper safeMutex(&triggerIdMutex);
//event->eventCompleted = true;
for(int i = eventList.size() - 1; i >= 0; i--) {
ConnectionSlotEvent &slotEvent = eventList[i];
if(slotEvent.eventCompleted == true) {
eventList.erase(eventList.begin() + i);
}
}
safeMutex.ReleaseLock();
}
bool ConnectionSlotThread::isSignalCompleted(ConnectionSlotEvent *event) {
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] slotIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,slotIndex);
MutexSafeWrapper safeMutex(&triggerIdMutex);
bool result = (event != NULL ? event->eventCompleted : true);
//bool result = (event != NULL ? event->eventCompleted : true);
bool result = false;
if(event != NULL) {
for(int i = 0; i < eventList.size(); ++i) {
ConnectionSlotEvent &slotEvent = eventList[i];
if(slotEvent.eventId == event->eventId) {
//eventList.erase(eventList.begin() + i);
result = slotEvent.eventCompleted;
break;
}
}
}
safeMutex.ReleaseLock();
//if(result == false) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] slotIndex = %d, result = %d\n",__FILE__,__FUNCTION__,__LINE__,slotIndex,result);
return result;
@ -118,11 +148,22 @@ void ConnectionSlotThread::execute() {
MutexSafeWrapper safeMutex(&triggerIdMutex);
int eventCount = eventList.size();
if(eventCount > 0) {
ConnectionSlotEvent *event = eventList[0];
ConnectionSlotEvent *event = NULL;
for(int i = 0; i < eventList.size(); ++i) {
ConnectionSlotEvent &slotEvent = eventList[i];
if(slotEvent.eventCompleted == false) {
event = &slotEvent;
break;
}
}
safeMutex.ReleaseLock();
this->slotInterface->slotUpdateTask(event);
setTaskCompleted(event);
if(event != NULL) {
this->slotInterface->slotUpdateTask(event);
setTaskCompleted(event->eventId);
}
}
else {
safeMutex.ReleaseLock();
@ -201,6 +242,9 @@ ConnectionSlot::~ConnectionSlot() {
}
void ConnectionSlot::update() {
if(slotThreadWorker != NULL) {
slotThreadWorker->purgeCompletedEvents();
}
update(true);
}

View File

@ -49,6 +49,7 @@ public:
networkMessage = NULL;
socketTriggered = false;
eventCompleted = false;
eventId = -1;
}
int64 triggerId;
@ -57,6 +58,7 @@ public:
const NetworkMessage *networkMessage;
bool socketTriggered;
bool eventCompleted;
int64 eventId;
};
//
@ -74,11 +76,11 @@ protected:
ConnectionSlotCallbackInterface *slotInterface;
Semaphore semTaskSignalled;
Mutex triggerIdMutex;
vector<ConnectionSlotEvent *> eventList;
vector<ConnectionSlotEvent> eventList;
int slotIndex;
virtual void setQuitStatus(bool value);
virtual void setTaskCompleted(ConnectionSlotEvent *event);
virtual void setTaskCompleted(int eventId);
public:
ConnectionSlotThread(int slotIndex);
@ -87,6 +89,8 @@ public:
void signalUpdate(ConnectionSlotEvent *event);
bool isSignalCompleted(ConnectionSlotEvent *event);
int getSlotIndex() const {return slotIndex; }
void purgeCompletedEvents();
};
// =====================================================

View File

@ -58,10 +58,11 @@ double LAG_CHECK_GRACE_PERIOD = 15;
// The max amount of time to 'freeze' gameplay per packet when a client is lagging
// badly and we want to give time for them to catch up
double MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE = 1;
double MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE = 2;
ServerInterface::ServerInterface() : GameNetworkInterface() {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
nextEventId = 1;
gameHasBeenInitiated = false;
exitServer = false;
gameSettingsUpdateCount = 0;
@ -349,11 +350,21 @@ int ServerInterface::getConnectedSlotCount() {
return connectedSlotCount;
}
int64 ServerInterface::getNextEventId() {
nextEventId++;
// Rollover when # gets large
if(nextEventId > INT_MAX) {
nextEventId = 1;
}
return nextEventId;
}
void ServerInterface::slotUpdateTask(ConnectionSlotEvent *event) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
if(event != NULL) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] event->eventType = %d\n",__FILE__,__FUNCTION__,__LINE__,event->eventType);
if(event->eventType == eSendSocketData) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] before sendMessage, event->networkMessage = %p\n",__FILE__,__FUNCTION__,event->networkMessage);
@ -364,6 +375,9 @@ void ServerInterface::slotUpdateTask(ConnectionSlotEvent *event) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
updateSlot(event);
}
else {
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__);
}
@ -536,6 +550,7 @@ bool ServerInterface::signalClientReceiveCommands(ConnectionSlot* connectionSlot
event.connectionSlot = connectionSlot;
event.socketTriggered = socketTriggered;
event.triggerId = slotIndex;
event.eventId = getNextEventId();
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] slotIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,slotIndex);
@ -580,6 +595,7 @@ void ServerInterface::validateConnectedClients() {
void ServerInterface::update() {
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
const int MAX_SLOT_THREAD_WAIT_TIME = 3;
std::vector<string> errorMsgList;
try {
// The first thing we will do is check all clients to ensure they have
@ -619,7 +635,6 @@ void ServerInterface::update() {
// Step #2 check all connection slot worker threads for completed status
time_t waitForThreadElapsed = time(NULL);
const int MAX_SLOT_THREAD_WAIT_TIME = 6;
std::map<int,bool> slotsCompleted;
for(bool threadsDone = false;
exitServer == false && threadsDone == false &&
@ -725,7 +740,10 @@ void ServerInterface::update() {
if(connectionSlot != NULL) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d, clientLagExceededOrWarned.first = %d, clientLagExceededOrWarned.second = %d\n",__FILE__,__FUNCTION__,__LINE__,clientLagExceededOrWarned.first,clientLagExceededOrWarned.second);
bool socketTriggered = (connectionSlot != NULL && connectionSlot->getSocket() != NULL ? socketTriggeredList[connectionSlot->getSocket()->getSocketId()] : false);
bool socketTriggered = false;
if(connectionSlot->getSocket() != NULL && connectionSlot->getSocket()->getSocketId() > 0) {
socketTriggered = socketTriggeredList[connectionSlot->getSocket()->getSocketId()];
}
ConnectionSlotEvent &event = eventList[i];
mapSlotSignalledList[i] = signalClientReceiveCommands(connectionSlot,i,socketTriggered,event);
threadsDone = false;
@ -1249,6 +1267,7 @@ void ServerInterface::broadcastMessage(const NetworkMessage* networkMessage, int
event.connectionSlot = connectionSlot;
event.socketTriggered = true;
event.triggerId = i;
event.eventId = getNextEventId();
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
@ -1574,7 +1593,8 @@ void ServerInterface::simpleTask(BaseThread *callingThread) {
//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d]\n",__FILE__,__FUNCTION__,__LINE__);
MutexSafeWrapper safeMutex(&masterServerThreadAccessor,intToStr(__LINE__));
if(difftime(time(NULL),lastMasterserverHeartbeatTime) >= 30) {
const int MASTERSERVER_HEARTBEAT_GAME_STATUS_SECONDS = 30;
if(difftime(time(NULL),lastMasterserverHeartbeatTime) >= MASTERSERVER_HEARTBEAT_GAME_STATUS_SECONDS) {
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d]\n",__FILE__,__FUNCTION__,__LINE__);
lastMasterserverHeartbeatTime = time(NULL);

View File

@ -55,6 +55,7 @@ private:
Shared::PlatformCommon::FTPServerThread *ftpServer;
bool exitServer;
int64 nextEventId;
public:
ServerInterface();
@ -135,6 +136,8 @@ private:
void validateConnectedClients();
std::map<string,string> publishToMasterserver();
int64 getNextEventId();
};
}}//end namespace

View File

@ -69,7 +69,7 @@ public:
protected:
friend class SoundPlayerOpenAL;
static const size_t STREAMBUFFERSIZE = 1024 * 500;
static const size_t STREAMFRAGMENTS = 10;
static const size_t STREAMFRAGMENTS = 5;
static const size_t STREAMFRAGMENTSIZE
= STREAMBUFFERSIZE / STREAMFRAGMENTS;