From 0267a2ba8f8444f7a73c0b2e24f9020bb6a59219 Mon Sep 17 00:00:00 2001 From: jsalmon3 <> Date: Sun, 16 Feb 2003 22:37:31 +0000 Subject: [PATCH] Fixed bug #561204: 3 player game can end too soon --- doc/ChangeLog.html | 1 + src/network/network.cpp | 231 +++++++++++++++++++++++++++++----------- 2 files changed, 172 insertions(+), 60 deletions(-) diff --git a/doc/ChangeLog.html b/doc/ChangeLog.html index c59723ed3..e8a01c6c7 100644 --- a/doc/ChangeLog.html +++ b/doc/ChangeLog.html @@ -1072,6 +1072,7 @@ <LI>Implemented feature request #631875: No MP menu pause (from Jimmy Salmon). <LI>Fixed bug #678580: Dragon Breath impact too long (from Jimmy Salmon). + <LI>Fixed bug #561204: 3 player game can end too soon (from Jimmy Salmon). <LI>+++ </UL> </UL> diff --git a/src/network/network.cpp b/src/network/network.cpp index 63078505a..21d6df97f 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -271,6 +271,8 @@ global int NetworkInSync = 1; /// Network is in sync global int NetworkUpdates = 5; /// Network update each # game cycles global int NetworkLag = 10; /// Network lag in # game cycles global unsigned long NetworkStatus[PlayerMax]; /// Network status +global unsigned long NetworkLastFrame[PlayerMax]; /// Last frame received packet +global int NetworkTimeout = 45; /// Number of seconds until player times out local char NetMsgBuf[128][PlayerMax]; /// Chat message buffers local int NetMsgBufLen[PlayerMax]; /// Stored chat message length @@ -297,6 +299,8 @@ local int NetworkSendPackets; /// Packets send packets local int NetworkSendResend; /// Packets send to resend #endif +local int PlayerQuit[PlayerMax]; /// Player quit + /**@name api */ //@{ @@ -376,7 +380,7 @@ local void NetworkSendPacket(const NetworkCommandQueue *ncq) // if (0 || !(rand() & 15)) NetworkBroadcast(&packet, sizeof(packet)); - if( HostsCount<3 ) { // enough bandwidth to send twice :) + if (HostsCount < 3) { // enough bandwidth to send twice :) NetworkBroadcast(&packet, sizeof(packet)); } } @@ -386,11 +390,12 @@ local void NetworkSendPacket(const NetworkCommandQueue *ncq) //---------------------------------------------------------------------------- /** -** Initialise network part 1. +** Initialize network part 1. */ global void InitNetwork1(void) { - int i, port; + int i; + int port; DebugLevel0Fn("\n"); @@ -401,9 +406,9 @@ global void InitNetwork1(void) NetworkInSync = 1; NetworkNumInterfaces = 0; - NetInit(); // machine dependend setup + NetInit(); // machine dependent setup - for (i = 0; i < PlayerMax; i++) { + for (i = 0; i < PlayerMax; ++i) { NetMsgBufLen[i] = 0; } @@ -416,15 +421,15 @@ global void InitNetwork1(void) // Our communication port port = NetworkPort; - for( i=0; i<10; ++i ) { - NetworkFildes = NetOpenUDP(port+i); + for (i = 0; i < 10; ++i) { + NetworkFildes = NetOpenUDP(port + i); if (NetworkFildes != -1) { break; } - if( i==9 ) { + if (i == 9) { fprintf(stderr,"NETWORK: No free ports %d-%d available, aborting\n", - port, port+i); - NetExit(); // machine dependend network exit + port, port + i); + NetExit(); // machine dependent network exit return; } } @@ -432,7 +437,7 @@ global void InitNetwork1(void) NetworkNumInterfaces = NetSocketAddr(NetworkFildes); if (NetworkNumInterfaces) { DebugLevel0Fn("Num IP: %d\n" _C_ NetworkNumInterfaces); - for (i = 0; i < NetworkNumInterfaces; i++) { + for (i = 0; i < NetworkNumInterfaces; ++i) { DebugLevel0Fn("IP: %d.%d.%d.%d\n" _C_ NIPQUAD(ntohl(NetLocalAddrs[i]))); } } else { @@ -474,7 +479,7 @@ global void ExitNetwork1(void) #endif NetCloseUDP(NetworkFildes); - NetExit(); // machine dependend setup + NetExit(); // machine dependent setup NetworkFildes = -1; NetworkInSync = 1; NetPlayers = 0; @@ -482,11 +487,12 @@ global void ExitNetwork1(void) } /** -** Initialise network part 2. +** Initialize network part 2. */ global void InitNetwork2(void) { - int i, n; + int i; + int n; NetworkConnectSetupGame(); @@ -496,7 +502,7 @@ global void InitNetwork2(void) // // Prepare first time without syncs. // - memset(NetworkIn,0,sizeof(NetworkIn)); + memset(NetworkIn, 0, sizeof(NetworkIn)); for (i = 0; i <= NetworkLag; i += NetworkUpdates) { for (n = 0; n < HostsCount; ++n) { NetworkIn[i][Hosts[n].PlyNr].Time = i; @@ -504,6 +510,12 @@ global void InitNetwork2(void) NetworkIn[i][Hosts[n].PlyNr].Data.Type = MessageSync; } } + + memset(NetworkSyncSeeds, 0, sizeof(NetworkSyncSeeds)); + memset(NetworkSyncHashs, 0, sizeof(NetworkSyncHashs)); + memset(PlayerQuit, 0, sizeof(PlayerQuit)); + memset(NetworkStatus, 0, sizeof(NetworkStatus)); + memset(NetworkLastFrame, 0, sizeof(NetworkLastFrame)); } //---------------------------------------------------------------------------- @@ -599,6 +611,47 @@ global void NetworkSendExtendedCommand(int command,int arg1,int arg2,int arg3, nec->Arg4 = htons(arg4); } +/** +** Remove a player from the game. +** +** @param player Player number +*/ +local void NetworkRemovePlayer(int player) +{ + int i; + + if (Players[player].TotalNumUnits != 0) { + // Set player to neutral, remove allied/enemy/shared vision status + Players[player].Type = PlayerNeutral; + for (i = 0; i < NumPlayers; ++i) { + if (i == player) { + continue; + } + Players[i].Allied &= ~(1 << player); + Players[i].Enemy &= ~(1 << player); + Players[i].SharedVision &= ~(1 << player); + Players[player].Allied &= ~(1 << i); + Players[player].Enemy &= ~(1 << i); + Players[player].SharedVision &= ~(1 << i); + } + SetMessage("Player \"%s\" has left the game", Players[player].Name); + } else { + SetMessage("Player \"%s\" has been killed", Players[player].Name); + } + + // Remove player from Hosts and clear NetworkIn + for (i = 0; i < HostsCount; ++i) { + if (Hosts[i].PlyNr == player) { + Hosts[i] = Hosts[HostsCount-1]; + --HostsCount; + break; + } + } + for (i = 0; i < 256; ++i) { + NetworkIn[i][player].Time = 0; + } +} + /** ** Called if message for the network is ready. ** (by WaitEventsOneFrame) @@ -632,14 +685,15 @@ global void NetworkEvent(void) return; } packet = (NetworkPacket *)buf; - IfDebug( NetworkReceivedPackets++ ); + IfDebug( ++NetworkReceivedPackets ); // // Setup messages // if (NetConnectRunning) { - if (NetworkParseSetupEvent(buf, i)) + if (NetworkParseSetupEvent(buf, i)) { return; + } } // @@ -651,13 +705,14 @@ global void NetworkEvent(void) } for (i = 0; i < HostsCount; ++i) { - if (Hosts[i].Host == NetLastHost && Hosts[i].Port == NetLastPort) { + if (Hosts[i].Host == NetLastHost && Hosts[i].Port == NetLastPort && + !PlayerQuit[i]) { break; } } if (i == HostsCount) { DebugLevel0Fn("Not a host in play: %d.%d.%d.%d:%d\n" _C_ - NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort)); + NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort)); return; } player = Hosts[i].PlyNr; @@ -674,17 +729,12 @@ global void NetworkEvent(void) // Handle some messages. // if (nc->Type == MessageQuit) { - DebugLevel0("Got quit from network.\n"); - // FIXME: should only remove the player who have send the quit - if (GameResult == GameNoResult) { - GameResult=GameVictory; - } - GameRunning=0; - continue; + PlayerQuit[nc->X] = 1; } if (nc->Type == MessageResend) { const NetworkCommandQueue *ncq; + int j; // Destination cycle (time to execute). n = ((GameCycle + 128) & ~0xFF) | nc->Cycle; @@ -693,33 +743,18 @@ global void NetworkEvent(void) n -= 0x100; } - // FIXME: not neccessary to send this packet multiple!!!! - // other side send re-send until its gets an answer. + // FIXME: not neccessary to send this packet multiple times!!!! + // other side sends re-send until it gets an answer. DebugLevel2Fn("Resend for %lu got\n" _C_ n); // // Find the commands to resend // -#if 0 - // Both directions are same fast/slow - ncq = (NetworkCommandQueue *)(CommandsOut->last); - while (ncq->List->prev) { - DebugLevel3Fn("resend %d? %d\n" _C_ ncq->Time _C_ n); - if (ncq->Time == n) { - NetworkSendPacket(ncq); - break; - } - - ncq = (NetworkCommandQueue *)(ncq->List->prev); - } - if (!ncq->List->prev) { - DebugLevel2Fn("no packets for resend\n"); - } -#else ncq = (NetworkCommandQueue *)(CommandsOut->first); while (ncq->List->next) { DebugLevel3Fn("resend %d? %d\n" _C_ ncq->Time _C_ n); if (ncq->Time == n) { + // FIXME: don't need to broadcast to everyone NetworkSendPacket(ncq); break; } @@ -729,7 +764,23 @@ global void NetworkEvent(void) if (!ncq->List->next) { DebugLevel3Fn("no packets for resend\n"); } -#endif + + // Check if a player quit this cycle + for (j = 0; j < HostsCount; ++j) { + ncq = &NetworkIn[n & 0xFF][Hosts[j].PlyNr]; + if (ncq->Time && ncq->Data.Type == MessageQuit) { + NetworkPacket np; + int k; + + for (k = 0; k < NetworkDups; ++k) { + np.Commands[k] = ncq->Data; + } + + // FIXME: don't need to broadcast to everyone + NetworkBroadcast(&np, sizeof(np)); + } + } + continue; } @@ -750,19 +801,21 @@ global void NetworkEvent(void) } // Receive statistic - if( n>NetworkStatus[player] ) { + if (n > NetworkStatus[player]) { NetworkStatus[player] = n; } + NetworkLastFrame[player] = FrameCounter; + #ifdef DEBUG - if( i ) { + if (i) { // Not in first slot and not got packet lost! // FIXME: if more than 1 in sequence is lost, I count one to much! - if( NetworkIn[nc->Cycle][player].Time != n ) { + if (NetworkIn[nc->Cycle][player].Time != n) { ++NetworkReceivedLost; } } else { // FIXME: more network statistic - if( NetworkIn[nc->Cycle][player].Time == n ) { + if (NetworkIn[nc->Cycle][player].Time == n) { ++NetworkReceivedDups; } } @@ -778,7 +831,7 @@ global void NetworkEvent(void) // if (!NetworkInSync) { NetworkInSync = 1; - n = ((GameCycle) / NetworkUpdates) * NetworkUpdates + NetworkUpdates; + n = (GameCycle / NetworkUpdates) * NetworkUpdates + NetworkUpdates; DebugLevel3Fn("wait for %d - " _C_ n); for (player = 0; player < HostsCount; ++player) { if (NetworkIn[n & 0xFF][Hosts[player].PlyNr].Time != n) { @@ -795,17 +848,26 @@ global void NetworkEvent(void) */ global void NetworkQuit(void) { - NetworkCommand nc; + NetworkCommandQueue *ncq; - nc.Type = MessageQuit; - nc.Cycle = GameCycle & 0xFF; - NetworkBroadcast(&nc, sizeof(NetworkCommand)); + if (!ThisPlayer) { + return; + } - // FIXME: if lost? Need an acknowledge for QuitMessages. + ncq = malloc(sizeof(NetworkCommandQueue)); + dl_insert_first(CommandsOut, ncq->List); + + ncq->Time = (GameCycle + NetworkUpdates) / NetworkUpdates * NetworkUpdates + + NetworkLag; + ncq->Data.Cycle = ncq->Time & 0xFF; + ncq->Data.Type = MessageQuit; + ncq->Data.X = ThisPlayer->Player; + + NetworkSendPacket(ncq); } /** -** Send chat message. (Message is send with low priority) +** Send chat message. (Message is sent with low priority) ** ** @param msg Text message to send. */ @@ -847,7 +909,7 @@ local void ParseNetworkCommand(const NetworkCommandQueue *ncq) { int ply; - switch (ncq->Data.Type&0x7F) { + switch (ncq->Data.Type & 0x7F) { case MessageSync: ply = ntohs(ncq->Data.X) << 16; ply |= ntohs(ncq->Data.Y); @@ -878,11 +940,14 @@ local void ParseNetworkCommand(const NetworkCommandQueue *ncq) } } break; + case MessageQuit: + NetworkRemovePlayer(ncq->Data.X); + break; case MessageExtendedCommand: { const NetworkExtendedCommand *nec; nec = (NetworkExtendedCommand *)(&ncq->Data); - ParseExtendedCommand(nec->ExtendedType,(nec->Type&0x80)>>7, + ParseExtendedCommand(nec->ExtendedType, (nec->Type & 0x80) >> 7, nec->Arg1, ntohs(nec->Arg2), ntohs(nec->Arg3), ntohs(nec->Arg4)); } @@ -1069,7 +1134,7 @@ local void NetworkSyncCommands(void) if (ncq->Time != n) { NetworkInSync = 0; NetworkDelay = FrameCounter + NetworkUpdates; - // FIXME: should send a resent request. + // FIXME: should send a resend request. DebugLevel3Fn("%lu not in sync %d\n" _C_ GameCycle _C_ n); break; } @@ -1100,9 +1165,55 @@ global void NetworkCommands(void) */ global void NetworkRecover(void) { - // Got no message just resent our oldest messages + int i; + if (FrameCounter > NetworkDelay) { NetworkDelay += NetworkUpdates; + + // Check for players that timed out + for (i = 0; i < HostsCount; ++i) { + int secs; + + if (!NetworkLastFrame[Hosts[i].PlyNr]) { + continue; + } + + secs = (FrameCounter - NetworkLastFrame[Hosts[i].PlyNr]) / + (FRAMES_PER_SECOND * VideoSyncSpeed / 100); + // FIXME: display a menu while we wait + if (secs >= 3 && secs < NetworkTimeout) { + if (FrameCounter % FRAMES_PER_SECOND < (unsigned long)NetworkUpdates) { + SetMessage("Waiting for player \"%s\": %d:%02d", Hosts[i].PlyName, + (NetworkTimeout - secs) / 60, (NetworkTimeout - secs) % 60); + } + } + if (secs >= NetworkTimeout) { + NetworkCommand nc; + const NetworkCommandQueue *ncq; + unsigned long n; + NetworkPacket np; + int j; + + n = GameCycle + NetworkUpdates; + nc.Cycle = n & 0xFF; + nc.Type = MessageQuit; + nc.X = Hosts[i].PlyNr; + NetworkIn[n & 0xFF][Hosts[i].PlyNr].Time = n; + NetworkIn[n & 0xFF][Hosts[i].PlyNr].Data = nc; + PlayerQuit[Hosts[i].PlyNr] = 1; + SetMessage("Timed out"); + + ncq = &NetworkIn[n & 0xFF][Hosts[i].PlyNr]; + for (j = 0; j < NetworkDups; ++j) { + np.Commands[j] = ncq->Data; + } + NetworkBroadcast(&np, sizeof(np)); + + NetworkSyncCommands(); + } + } + + // Resend old commands if (!dl_empty(CommandsOut)) { DebugLevel3Fn("cycle %lu vi %d\n" _C_ GameCycle _C_ VideoInterrupts); NetworkResendCommands();