Skip to content
2 changes: 1 addition & 1 deletion Core/GameEngine/Source/GameNetwork/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#include "../NGMP_include.h"

#if defined(GENERALS_ONLINE)
enum { MaxQuitFlushTime = 5000 }; // wait this many milliseconds at most to retry things before quitting
enum { MaxQuitFlushTime = 30000 }; // wait this many milliseconds at most to retry things before quitting
#else
enum { MaxQuitFlushTime = 30000 }; // wait this many milliseconds at most to retry things before quitting
#endif
Expand Down
171 changes: 110 additions & 61 deletions Core/GameEngine/Source/GameNetwork/ConnectionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1931,71 +1931,113 @@ PlayerLeaveCode ConnectionManager::disconnectPlayer(int64_t userID)
#endif

void ConnectionManager::quitGame() {
// Need to do the NetDisconnectPlayerCommandMsg creation and sending here.
NetDisconnectPlayerCommandMsg *disconnectMsg = newInstance(NetDisconnectPlayerCommandMsg);
disconnectMsg->setDisconnectSlot(m_localSlot);
disconnectMsg->setDisconnectFrame(TheGameLogic->getFrame());
disconnectMsg->setPlayerID(m_localSlot);
if (DoesCommandRequireACommandID(disconnectMsg->getNetCommandType())) {
disconnectMsg->setID(GenerateNextCommandID());
}
//DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - about to send disconnect command"));
sendLocalCommandDirect(disconnectMsg, 0xff ^ (1 << m_localSlot));

//DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - about to flush connections"));
flushConnections(); // need to do this so our packet actually gets sent before the connections are deleted.
//DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - done flushing connections"));

disconnectMsg->detach();
// Need to do the NetDisconnectPlayerCommandMsg creation and sending here.
NetDisconnectPlayerCommandMsg *disconnectMsg = newInstance(NetDisconnectPlayerCommandMsg);
disconnectMsg->setDisconnectSlot(m_localSlot);
disconnectMsg->setDisconnectFrame(TheGameLogic->getFrame());

// Ensure the message has a valid command ID before logging/sending.
if (DoesCommandRequireACommandID(disconnectMsg->getNetCommandType())) {
if (disconnectMsg->getID() == 0) {
disconnectMsg->setID(GenerateNextCommandID());
}
}

// Log after ID assignment so the log shows the real command id.
DEBUG_LOG(("DBG_DISCONNECT: CREATED %s:%d id=%d slot=%d setFrame=%u localFrame=%u",
__FILE__, __LINE__, disconnectMsg->getID(), disconnectMsg->getDisconnectSlot(),
disconnectMsg->getDisconnectFrame(), TheGameLogic->getFrame()));

disconnectMsg->setPlayerID(m_localSlot);

DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - about to send disconnect command"));
sendLocalCommandDirect(disconnectMsg, 0xff ^ (1 << m_localSlot));

// Temporary test: wait up to 500ms for the outgoing queues on each connection to drain.
DEBUG_LOG(("DBG_LOCALDISCONNECT: waiting for send queues to drain %s:%d", __FILE__, __LINE__));
const int kMaxWaitMs = 500;
int waited = 0;
while (waited < kMaxWaitMs) {
bool anyPending = false;

// Check each connection's outgoing queue. Use isQueueEmpty() which exists on Connection.
for (Int ci = 0; ci < MAX_SLOTS; ++ci) {
if (m_connections[ci] != NULL) {
if (!m_connections[ci]->isQueueEmpty()) {
anyPending = true;
break;
}
}
}

// If transport has a send buffer, give it a chance to flush as well.
if (m_transport != NULL) {
m_transport->doSend();
}

if (!anyPending) {
break;
}

Sleep(10); // short sleep to yield; replace with proper non-blocking wait in production.
waited += 10;
}
DEBUG_LOG(("DBG_LOCALDISCONNECT: done waiting, waited=%dms %s:%d", waited, __FILE__, __LINE__));

DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - about to flush connections"));
flushConnections(); // ensure transport flush as well
DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - done flushing connections"));

disconnectMsg->detach();

#if RTS_GENERALS
// if we get here, we hit Quit on the disconnect screen. Mark everyone as having disconnected from us
// so the online stats can give us appropriate feedback.
if (TheGameInfo)
{
for (Int i = 0; i < MAX_SLOTS; ++i)
{
GameSlot *gSlot = TheGameInfo->getSlot( i );
if (gSlot && !gSlot->lastFrameInGame())
{
gSlot->markAsDisconnected();
}
}
}
// if we get here, we hit Quit on the disconnect screen. Mark everyone as having disconnected from us
// so the online stats can give us appropriate feedback.
if (TheGameInfo)
{
for (Int i = 0; i < MAX_SLOTS; ++i)
{
GameSlot *gSlot = TheGameInfo->getSlot( i );
if (gSlot && !gSlot->lastFrameInGame())
{
gSlot->markAsDisconnected();
}
}
}
#endif

disconnectLocalPlayer();
disconnectLocalPlayer();
}

void ConnectionManager::disconnectLocalPlayer() {
// kill the frame data and the connections for all the other players.
DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer()"));
for (Int i = 0; i < MAX_SLOTS; ++i) {
if (i != m_localSlot) {
disconnectPlayer(i);
}
}
// kill the frame data and the connections for all the other players.
DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer()"));
for (Int i = 0; i < MAX_SLOTS; ++i) {
if (i != m_localSlot) {
disconnectPlayer(i);
}
}
}

/**
* Takes all the commands that are ready to send and sends them right now.
*/
void ConnectionManager::flushConnections() {
for (Int i = 0; i < MAX_SLOTS; ++i) {
if (m_connections[i] != NULL) {
// DEBUG_LOG(("ConnectionManager::flushConnections - flushing connection to player %d", i));
/*
if (m_connections[i]->isQueueEmpty()) {
// DEBUG_LOG(("ConnectionManager::flushConnections - connection queue empty"));
}
*/
m_connections[i]->doSend();
}
}

if (m_transport != NULL) {
m_transport->doSend();
}
for (Int i = 0; i < MAX_SLOTS; ++i) {
if (m_connections[i] != NULL) {
// DEBUG_LOG(("ConnectionManager::flushConnections - flushing connection to player %d", i));
/*
if (m_connections[i]->isQueueEmpty()) {
// DEBUG_LOG(("ConnectionManager::flushConnections - connection queue empty"));
}
*/
m_connections[i]->doSend();
}
}

if (m_transport != NULL) {
m_transport->doSend();
}
}

void ConnectionManager::resendPendingCommands() {
Expand Down Expand Up @@ -2517,8 +2559,6 @@ void ConnectionManager::notifyOthersOfCurrentFrame(Int frame) {
m_disconnectManager->processDisconnectCommand(ref, this);
deleteInstance(ref);

msg->detach();

DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::notifyOthersOfCurrentFrame - start screen on debug stuff"));
#if defined(RTS_DEBUG)
debugPrintConnectionCommands();
Expand All @@ -2540,7 +2580,6 @@ void ConnectionManager::notifyOthersOfNewFrame(UnsignedInt frame) {
m_disconnectManager->processDisconnectCommand(ref, this);
deleteInstance(ref);

msg->detach();
}

void ConnectionManager::sendFrameDataToPlayer(UnsignedInt playerID, UnsignedInt startingFrame) {
Expand All @@ -2564,12 +2603,22 @@ void ConnectionManager::sendSingleFrameToPlayer(UnsignedInt playerID, UnsignedIn
if ((m_frameData[i] != NULL) && (i != playerID)) { // no need to send his own commands to him.
NetCommandList *list = m_frameData[i]->getFrameCommandList(frame);
if (list != NULL) {
NetCommandRef *ref = list->getFirstMessage();
while (ref != NULL) {
DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - sending command %d from player %d to player %d using relay 0x%x", ref->getCommand()->getID(), i, playerID, relay));
sendLocalCommandDirect(ref->getCommand(), relay);
ref = ref->getNext();
}
NetCommandRef *ref = list->getFirstMessage();
while (ref != NULL) {
NetCommandRef *nextRef = ref->getNext();

// Skip re-sending disconnect-frame commands to avoid duplicate disconnect messages
if (ref->getCommand()->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) {
ref = nextRef;
continue;
}

DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - sending command %d from player %d to player %d using relay 0x%x",
ref->getCommand()->getID(), i, playerID, relay));
sendLocalCommandDirect(ref->getCommand(), relay);

ref = nextRef;
}
}
UnsignedInt frameCommandCount = m_frameData[i]->getFrameCommandCount(frame);
NetFrameCommandMsg *msg = newInstance(NetFrameCommandMsg);
Expand Down
Loading
Loading