Bugfixes for network game launching:

- discard inprogress messages that don't apply during launch
- added better error handling of disconnects during game launch and play on both server and clients
This commit is contained in:
Mark Vejvoda
2010-05-04 02:32:43 +00:00
parent acf4bebba6
commit 4b1a392f00
5 changed files with 150 additions and 80 deletions

View File

@@ -405,7 +405,7 @@ void ClientInterface::updateKeyframe(int frameCount)
{ {
bool done= false; bool done= false;
while(!done) while(done == false)
{ {
//wait for the next message //wait for the next message
waitForMessage(); waitForMessage();
@@ -489,13 +489,17 @@ void ClientInterface::updateKeyframe(int frameCount)
DisplayErrorMessage(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected message in client interface: " + intToStr(networkMessageType)); DisplayErrorMessage(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected message in client interface: " + intToStr(networkMessageType));
quit= true; quit= true;
close(); close();
done= true;
} }
} }
if(isConnected() == false && quit == true) {
done = true;
}
} }
} }
void ClientInterface::waitUntilReady(Checksum* checksum) void ClientInterface::waitUntilReady(Checksum* checksum) {
{
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__);
Logger &logger= Logger::getInstance(); Logger &logger= Logger::getInstance();
@@ -514,15 +518,15 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
int64 lastMillisCheck = 0; int64 lastMillisCheck = 0;
//wait until we get a ready message from the server //wait until we get a ready message from the server
while(true) while(true) {
{
if(isConnected() == false) { if(isConnected() == false) {
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__);
string sErr = "Error, Server has disconnected!"; string sErr = "Error, Server has disconnected!";
sendTextMessage(sErr,-1); //sendTextMessage(sErr,-1);
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__);
DisplayErrorMessage(sErr); DisplayErrorMessage(sErr);
@@ -535,18 +539,14 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
return; return;
} }
NetworkMessageType networkMessageType = getNextMessageType(true); NetworkMessageType networkMessageType = getNextMessageType(true);
if(networkMessageType == nmtReady) if(networkMessageType == nmtReady) {
{ if(receiveMessage(&networkMessageReady)) {
if(receiveMessage(&networkMessageReady))
{
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__);
break; break;
} }
} }
else if(networkMessageType == nmtInvalid) else if(networkMessageType == nmtInvalid) {
{ if(chrono.getMillis() > readyWaitTimeout) {
if(chrono.getMillis() > readyWaitTimeout)
{
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__);
//throw runtime_error("Timeout waiting for server"); //throw runtime_error("Timeout waiting for server");
string sErr = "Timeout waiting for server"; string sErr = "Timeout waiting for server";
@@ -564,10 +564,8 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
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__);
return; return;
} }
else else {
{ if(chrono.getMillis() / 1000 > lastMillisCheck) {
if(chrono.getMillis() / 1000 > lastMillisCheck)
{
lastMillisCheck = (chrono.getMillis() / 1000); lastMillisCheck = (chrono.getMillis() / 1000);
char szBuf[1024]=""; char szBuf[1024]="";
@@ -576,8 +574,7 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
} }
} }
} }
else 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__);
//throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType) ); //throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType) );
sendTextMessage("Unexpected network message: " + intToStr(networkMessageType),-1); sendTextMessage("Unexpected network message: " + intToStr(networkMessageType),-1);
@@ -602,8 +599,7 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
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__);
//check checksum //check checksum
if(networkMessageReady.getChecksum() != checksum->getSum()) if(networkMessageReady.getChecksum() != checksum->getSum()) {
{
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__);
string sErr = "Checksum error, you don't have the same data as the server"; string sErr = "Checksum error, you don't have the same data as the server";
@@ -633,8 +629,7 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
return; return;
} }
//delay the start a bit, so clients have more room to get messages
//delay the start a bit, so clients have nore room to get messages
sleep(GameConstants::networkExtraLatency); sleep(GameConstants::networkExtraLatency);
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",__FILE__,__FUNCTION__); SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",__FILE__,__FUNCTION__);
@@ -657,11 +652,11 @@ void ClientInterface::waitForMessage()
int waitLoopCount = 0; int waitLoopCount = 0;
while(getNextMessageType(true) == nmtInvalid) while(getNextMessageType(true) == nmtInvalid)
{ {
if(!isConnected()) if(isConnected() == false)
{ {
//throw runtime_error("Disconnected"); //throw runtime_error("Disconnected");
sendTextMessage("Disconnected",-1); sendTextMessage("Server has Disconnected.",-1);
DisplayErrorMessage("Disconnected"); DisplayErrorMessage("Server has Disconnected.");
quit= true; quit= true;
close(); close();
return; return;

View File

@@ -25,7 +25,7 @@ namespace Glest{ namespace Game{
class GameSettings; class GameSettings;
enum NetworkMessageType{ enum NetworkMessageType {
nmtInvalid, nmtInvalid,
nmtIntro, nmtIntro,
nmtPing, nmtPing,
@@ -51,7 +51,7 @@ const int32 commandListHeaderSize = 6;
// class NetworkMessage // class NetworkMessage
// ===================================================== // =====================================================
class NetworkMessage{ class NetworkMessage {
public: public:
virtual ~NetworkMessage(){} virtual ~NetworkMessage(){}
virtual bool receive(Socket* socket)= 0; virtual bool receive(Socket* socket)= 0;

View File

@@ -280,6 +280,79 @@ void ServerInterface::updateKeyframe(int frameCount){
SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] broadcastMessage took %d msecs, networkMessageCommandList.getCommandCount() = %d, frameCount = %d\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),networkMessageCommandList.getCommandCount(),frameCount); SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] broadcastMessage took %d msecs, networkMessageCommandList.getCommandCount() = %d, frameCount = %d\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),networkMessageCommandList.getCommandCount(),frameCount);
} }
bool ServerInterface::shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,
ConnectionSlot* connectionSlot) {
bool discard = false;
if(connectionSlot != NULL) {
switch(networkMessageType) {
case nmtIntro:
{
discard = true;
NetworkMessageIntro msg = NetworkMessageIntro();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtLaunch:
{
discard = true;
NetworkMessageLaunch msg = NetworkMessageLaunch();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtText:
{
discard = true;
NetworkMessageText msg = NetworkMessageText();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtSynchNetworkGameData:
{
discard = true;
NetworkMessageSynchNetworkGameData msg = NetworkMessageSynchNetworkGameData();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtSynchNetworkGameDataStatus:
{
discard = true;
NetworkMessageSynchNetworkGameDataStatus msg = NetworkMessageSynchNetworkGameDataStatus();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtSynchNetworkGameDataFileCRCCheck:
{
discard = true;
NetworkMessageSynchNetworkGameDataFileCRCCheck msg = NetworkMessageSynchNetworkGameDataFileCRCCheck();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtSynchNetworkGameDataFileGet:
{
discard = true;
NetworkMessageSynchNetworkGameDataFileGet msg = NetworkMessageSynchNetworkGameDataFileGet();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtSwitchSetupRequest:
{
discard = true;
SwitchSetupRequest msg = SwitchSetupRequest();
connectionSlot->receiveMessage(&msg);
}
break;
case nmtPlayerIndexMessage:
{
discard = true;
PlayerIndexMessage msg = PlayerIndexMessage(0);
connectionSlot->receiveMessage(&msg);
}
break;
}
}
return discard;
}
void ServerInterface::waitUntilReady(Checksum* checksum){ void ServerInterface::waitUntilReady(Checksum* checksum){
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] START\n",__FUNCTION__); SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] START\n",__FUNCTION__);
@@ -293,44 +366,33 @@ void ServerInterface::waitUntilReady(Checksum* checksum){
chrono.start(); chrono.start();
//wait until we get a ready message from all clients //wait until we get a ready message from all clients
while(allReady == false) while(allReady == false) {
{
vector<string> waitingForHosts; vector<string> waitingForHosts;
allReady= true; allReady= true;
for(int i= 0; i<GameConstants::maxPlayers; ++i) for(int i= 0; i<GameConstants::maxPlayers; ++i) {
{
ConnectionSlot* connectionSlot= slots[i]; ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
if(connectionSlot != NULL && connectionSlot->isConnected() == true) if(connectionSlot->isReady() == false) {
{
if(connectionSlot->isReady() == false)
{
NetworkMessageType networkMessageType= connectionSlot->getNextMessageType(true); NetworkMessageType networkMessageType= connectionSlot->getNextMessageType(true);
NetworkMessageReady networkMessageReady;
// consume old messages from the setup // consume old messages from the lobby
while(networkMessageType == nmtSwitchSetupRequest) bool discarded = shouldDiscardNetworkMessage(networkMessageType,connectionSlot);
{ if(discarded == false) {
SwitchSetupRequest switchSetupRequest; NetworkMessageReady networkMessageReady;
connectionSlot->receiveMessage(&switchSetupRequest); if(networkMessageType == nmtReady &&
networkMessageType= connectionSlot->getNextMessageType(true); connectionSlot->receiveMessage(&networkMessageReady)) {
} SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] networkMessageType==nmtReady\n",__FUNCTION__);
if(networkMessageType == nmtReady &&
connectionSlot->receiveMessage(&networkMessageReady))
{
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] networkMessageType==nmtReady\n",__FUNCTION__);
connectionSlot->setReady(); connectionSlot->setReady();
}
else if(networkMessageType != nmtInvalid) {
//throw runtime_error("Unexpected network message: " + intToStr(networkMessageType));
string sErr = "Unexpected network message: " + intToStr(networkMessageType);
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
} }
else if(networkMessageType != nmtInvalid)
{
//throw runtime_error("Unexpected network message: " + intToStr(networkMessageType));
string sErr = "Unexpected network message: " + intToStr(networkMessageType);
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
//waitingForHosts.push_back(connectionSlot->getHostName()); //waitingForHosts.push_back(connectionSlot->getHostName());
waitingForHosts.push_back(connectionSlot->getName()); waitingForHosts.push_back(connectionSlot->getName());
@@ -340,25 +402,19 @@ void ServerInterface::waitUntilReady(Checksum* checksum){
} }
//check for timeout //check for timeout
if(allReady == false) if(allReady == false) {
{ if(chrono.getMillis() > readyWaitTimeout) {
if(chrono.getMillis() > readyWaitTimeout)
{
//throw runtime_error("Timeout waiting for clients"); //throw runtime_error("Timeout waiting for clients");
string sErr = "Timeout waiting for clients."; string sErr = "Timeout waiting for clients.";
sendTextMessage(sErr,-1); sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr); DisplayErrorMessage(sErr);
return; return;
} }
else else {
{ if(chrono.getMillis() % 1000 == 0) {
if(chrono.getMillis() % 1000 == 0)
{
string waitForHosts = ""; string waitForHosts = "";
for(int i = 0; i < waitingForHosts.size(); i++) for(int i = 0; i < waitingForHosts.size(); i++) {
{ if(waitForHosts != "") {
if(waitForHosts != "")
{
waitForHosts += ", "; waitForHosts += ", ";
} }
waitForHosts += waitingForHosts[i]; waitForHosts += waitingForHosts[i];
@@ -372,20 +428,16 @@ void ServerInterface::waitUntilReady(Checksum* checksum){
} }
} }
// FOR TESTING ONLY - delay to see the client count up while waiting // FOR TESTING ONLY - delay to see the client count up while waiting
//sleep(5000); //sleep(5000);
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] PART B (telling client we are ready!\n",__FUNCTION__); SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] PART B (telling client we are ready!\n",__FUNCTION__);
//send ready message after, so clients start delayed //send ready message after, so clients start delayed
for(int i= 0; i < GameConstants::maxPlayers; ++i) for(int i= 0; i < GameConstants::maxPlayers; ++i) {
{
NetworkMessageReady networkMessageReady(checksum->getSum());
ConnectionSlot* connectionSlot= slots[i]; ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot!=NULL) if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
{ NetworkMessageReady networkMessageReady(checksum->getSum());
connectionSlot->sendMessage(&networkMessageReady); connectionSlot->sendMessage(&networkMessageReady);
} }
} }

View File

@@ -82,6 +82,7 @@ private:
void broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot= -1); void broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot= -1);
void updateListen(); void updateListen();
void broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot = -1); void broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot = -1);
bool shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,ConnectionSlot* connectionSlot);
}; };
}}//end namespace }}//end namespace

View File

@@ -143,6 +143,28 @@ void deleteMapValues(T beginIt, T endIt){
} }
} }
template <typename T, typename U>
class create_map
{
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
}}//end namespace }}//end namespace
#endif #endif