// This function is called from the network thread, no acces to server // internal data should be made void NetManager::CheckLinkDead() { csTicks currenttime = csGetTicks(); csArray<uint32_t> checkedClients; Client *pClient = NULL; // Delete all clients marked for deletion already clients.SweepDelete(); while(true) { pClient = NULL; // Put the iterator in a limited scope so we don't hold on to the lock which may cause deadlock { ClientIterator i(clients); while(i.HasNext()) { Client *candidate = i.Next(); // Skip if already seen if(checkedClients.FindSortedKey(csArrayCmp<uint32_t, uint32_t> (candidate->GetClientNum())) != csArrayItemNotFound) continue; checkedClients.InsertSorted(candidate->GetClientNum()); pClient = candidate; break; } } // No more clients to check so break if(!pClient) break; // Shortcut here so zombies may immediately disconnect if(pClient->IsZombie() && pClient->ZombieAllowDisconnect()) { /* This simulates receipt of this message from the client ** without any network access, so that disconnection logic ** is all in one place. */ psDisconnectMessage discon(pClient->GetClientNum(), 0, "You should not see this."); if (discon.valid) { Connection* connection = pClient->GetConnection(); HandleCompletedMessage(discon.msg, connection, NULL,NULL); } else { Bug2("Failed to create valid psDisconnectMessage for client id %u.\n", pClient->GetClientNum()); } } else if (pClient->GetConnection()->lastRecvPacketTime+timeout < currenttime) { if (pClient->GetConnection()->heartbeat < 10 && pClient->GetConnection()->lastRecvPacketTime+timeout * 10 > currenttime) { psHeartBeatMsg ping(pClient->GetClientNum()); Broadcast(ping.msg, NetBase::BC_FINALPACKET); pClient->GetConnection()->heartbeat++; } else { if(!pClient->AllowDisconnect()) continue; char ipaddr[20]; pClient->GetIPAddress(ipaddr); csString status; status.Format("%s, %u, Client (%s) went linkdead.", ipaddr, pClient->GetClientNum(), pClient->GetName()); psserver->GetLogCSV()->Write(CSV_AUTHENT, status); /* This simulates receipt of this message from the client ** without any network access, so that disconnection logic ** is all in one place. */ psDisconnectMessage discon(pClient->GetClientNum(), 0, "You are linkdead."); if (discon.valid) { Connection* connection = pClient->GetConnection(); HandleCompletedMessage(discon.msg, connection, NULL,NULL); } else { Bug2("Failed to create valid psDisconnectMessage for client id %u.\n", pClient->GetClientNum()); } } } } }