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

View File

@ -25,7 +25,7 @@ namespace Glest{ namespace Game{
class GameSettings;
enum NetworkMessageType{
enum NetworkMessageType {
nmtInvalid,
nmtIntro,
nmtPing,
@ -51,7 +51,7 @@ const int32 commandListHeaderSize = 6;
// class NetworkMessage
// =====================================================
class NetworkMessage{
class NetworkMessage {
public:
virtual ~NetworkMessage(){}
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);
}
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){
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] START\n",__FUNCTION__);
@ -293,44 +366,33 @@ void ServerInterface::waitUntilReady(Checksum* checksum){
chrono.start();
//wait until we get a ready message from all clients
while(allReady == false)
{
while(allReady == false) {
vector<string> waitingForHosts;
allReady= true;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
for(int i= 0; i<GameConstants::maxPlayers; ++i) {
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot != NULL && connectionSlot->isConnected() == true)
{
if(connectionSlot->isReady() == false)
{
if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
if(connectionSlot->isReady() == false) {
NetworkMessageType networkMessageType= connectionSlot->getNextMessageType(true);
NetworkMessageReady networkMessageReady;
// consume old messages from the setup
while(networkMessageType == nmtSwitchSetupRequest)
{
SwitchSetupRequest switchSetupRequest;
connectionSlot->receiveMessage(&switchSetupRequest);
networkMessageType= connectionSlot->getNextMessageType(true);
}
if(networkMessageType == nmtReady &&
connectionSlot->receiveMessage(&networkMessageReady))
{
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] networkMessageType==nmtReady\n",__FUNCTION__);
// consume old messages from the lobby
bool discarded = shouldDiscardNetworkMessage(networkMessageType,connectionSlot);
if(discarded == false) {
NetworkMessageReady networkMessageReady;
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->getName());
@ -340,25 +402,19 @@ void ServerInterface::waitUntilReady(Checksum* checksum){
}
//check for timeout
if(allReady == false)
{
if(chrono.getMillis() > readyWaitTimeout)
{
if(allReady == false) {
if(chrono.getMillis() > readyWaitTimeout) {
//throw runtime_error("Timeout waiting for clients");
string sErr = "Timeout waiting for clients.";
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
else
{
if(chrono.getMillis() % 1000 == 0)
{
else {
if(chrono.getMillis() % 1000 == 0) {
string waitForHosts = "";
for(int i = 0; i < waitingForHosts.size(); i++)
{
if(waitForHosts != "")
{
for(int i = 0; i < waitingForHosts.size(); i++) {
if(waitForHosts != "") {
waitForHosts += ", ";
}
waitForHosts += waitingForHosts[i];
@ -372,20 +428,16 @@ void ServerInterface::waitUntilReady(Checksum* checksum){
}
}
// FOR TESTING ONLY - delay to see the client count up while waiting
//sleep(5000);
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] PART B (telling client we are ready!\n",__FUNCTION__);
//send ready message after, so clients start delayed
for(int i= 0; i < GameConstants::maxPlayers; ++i)
{
NetworkMessageReady networkMessageReady(checksum->getSum());
for(int i= 0; i < GameConstants::maxPlayers; ++i) {
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot!=NULL)
{
if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
NetworkMessageReady networkMessageReady(checksum->getSum());
connectionSlot->sendMessage(&networkMessageReady);
}
}

View File

@ -82,6 +82,7 @@ private:
void broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot= -1);
void updateListen();
void broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot = -1);
bool shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,ConnectionSlot* connectionSlot);
};
}}//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
#endif