void C4Network2ClientList::UpdateClientActivity() { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) if (pClient->isActivated()) if (::Players.GetAtClient(pClient->getID())) pClient->SetLastActivity(Game.FrameCounter); }
C4Network2Stats::C4Network2Stats() : pSec1Timer(NULL) { // set self (needed in CreateGraph-fns) Game.pNetworkStatistics = this; // init callback timer pSec1Timer = new C4Sec1TimerCallback<C4Network2Stats>(this); SecondCounter = 0; ControlCounter = 0; // init graphs statObjCount.SetTitle(LoadResStr("IDS_MSG_OBJCOUNT")); statFPS.SetTitle(LoadResStr("IDS_MSG_FPS")); statNetI.SetTitle(LoadResStr("IDS_NET_INPUT")); statNetI.SetColorDw(0x00ff00); statNetO.SetTitle(LoadResStr("IDS_NET_OUTPUT")); statNetO.SetColorDw(0xff0000); graphNetIO.AddGraph(&statNetI); graphNetIO.AddGraph(&statNetO); statControls.SetTitle(LoadResStr("IDS_NET_CONTROL")); statControls.SetAverageTime(100); statActions.SetTitle(LoadResStr("IDS_NET_APM")); statActions.SetAverageTime(100); for (C4Player *pPlr = Game.Players.First; pPlr; pPlr = pPlr->Next) pPlr->CreateGraphs(); C4Network2Client *pClient = NULL; while (pClient = Game.Network.Clients.GetNextClient(pClient)) pClient->CreateGraphs(); }
C4Network2Client *C4Network2ClientList::GetClient(const C4ClientCore &CCore, int32_t iMaxDiffLevel) { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext) if (pClient->getCore().getDiffLevel(CCore) <= iMaxDiffLevel) return pClient; return NULL; }
void C4Network2Players::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn) { if (!pConn) return; // find associated client C4Network2Client *pClient = ::Network.Clients.GetClient(pConn); if (!pClient) pClient = ::Network.Clients.GetClientByID(pConn->getClientID()); #define GETPKT(type, name) \ assert(pPacket); const type &name = \ static_cast<const type &>(*pPacket); // player join request? if (cStatus == PID_PlayerInfoUpdReq) { GETPKT(C4PacketPlayerInfoUpdRequest, pkPlrInfo); // this packet is sent to the host only, and thus cannot have been sent from the host if (!::Network.isHost()) return; // handle this packet HandlePlayerInfoUpdRequest(&pkPlrInfo.Info, false); } else if (cStatus == PID_LeagueRoundResults) { GETPKT(C4PacketLeagueRoundResults, pkLeagueInfo); // accepted from the host only if (!pClient || !pClient->isHost()) return; // process Game.RoundResults.EvaluateLeague(pkLeagueInfo.sResultsString.getData(), pkLeagueInfo.fSuccess, pkLeagueInfo.Players); } #undef GETPKT }
C4Network2Client *C4Network2ClientList::GetClient(C4Network2IOConnection *pConn) const { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext) if (pClient->hasConn(pConn)) return pClient; return NULL; }
C4Network2Client *C4Network2ClientList::GetClientByID(int32_t iID) const { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext) if (pClient->getID() == iID) return pClient; return NULL; }
C4Network2Client *C4Network2ClientList::GetClient(const char *szName) const { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext) if (SEqual(pClient->getName(), szName)) return pClient; return NULL; }
bool C4Network2ClientList::AllClientsReady() const { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) if (!pClient->isLocal() && pClient->isWaitedFor() && !pClient->isReady()) return false; return true; }
void C4Network2ClientList::HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn) { // find associated client C4Network2Client *pClient = GetClient(pConn); if (!pClient) return; #define GETPKT(type, name) \ assert(pBasePkt); \ const type &name = /*dynamic_cast*/ static_cast<const type &>(*pBasePkt); switch (cStatus) { case PID_Addr: // address propagation { GETPKT(C4PacketAddr, rPkt) // find client pClient = GetClientByID(rPkt.getClientID()); if (pClient) { C4Network2Address addr = rPkt.getAddr(); // IP zero? Set to IP from where the packet came if (addr.isIPNull()) addr.SetIP(pConn->getPeerAddr().sin_addr); // add (no announce) if (pClient->AddAddr(addr, true)) // new address? Try to connect pClient->DoConnectAttempt(pIO); } } break; } #undef GETPKT }
C4Network2Client *C4Network2ClientList::RegClient(C4Client *pClient) { // security if (pClient->getNetClient()) return pClient->getNetClient(); // find insert position C4Network2Client *pPos = pFirst, *pLast = NULL; for (; pPos; pLast = pPos, pPos = pPos->getNext()) if (pPos->getID() > pClient->getID()) break; assert(!pLast || pLast->getID() != pClient->getID()); // create new client C4Network2Client *pNetClient = new C4Network2Client(pClient); // add to list pNetClient->pNext = pPos; (pLast ? pLast->pNext : pFirst) = pNetClient; pNetClient->pParent = this; // local? if (pClient->isLocal()) pLocal = pNetClient; else // set auto-accept pIO->AddAutoAccept(pClient->getCore()); // add return pNetClient; }
void C4Network2ClientListBox::ConnectionListItem::Update() { C4Network2IOConnection *pConn = GetConnection(); if (!pConn) { // No connection: Shouldn't happen pDesc->SetText("???"); pPing->SetText("???"); return; } // update connection ping int iPing = pConn->getLag(); pPing->SetText(FormatString("%d ms", iPing).getData()); // update description // get connection usage const char *szConnType; C4Network2Client *pNetClient = ::Network.Clients.GetClientByID(iClientID); if (pNetClient->getDataConn() == pNetClient->getMsgConn()) szConnType = "Data/Msg"; else if (iConnID) szConnType = "Msg"; else szConnType = "Data"; // display info pDesc->SetText(FormatString("%s: %s (%s l%d)", szConnType, ::Network.NetIO.getNetIOName(pConn->getNetClass()), pConn->getPeerAddr().ToString().getData(), pConn->getPacketLoss()).getData()); }
C4Network2Stats::~C4Network2Stats() { for (C4Player *pPlr = Game.Players.First; pPlr; pPlr = pPlr->Next) pPlr->ClearGraphs(); C4Network2Client *pClient = NULL; while (pClient = Game.Network.Clients.GetNextClient(pClient)) pClient->ClearGraphs(); pSec1Timer->Release(); }
bool C4Network2ClientList::SendMsgToHost(C4NetIOPacket rPkt) { // find host C4Network2Client *pHost = GetHost(); if (!pHost) return false; // send message if (!pHost->getMsgConn()) return false; return pHost->SendMsg(rPkt); }
C4Network2Client *C4Network2ClientList::GetNextClientAfterID( int32_t iSmallerClientID) const { // return client with smallest ID > iSmallerClientID C4Network2Client *pBest = NULL; for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext) if (pClient->getID() > iSmallerClientID) if (!pBest || pBest->getID() > pClient->getID()) pBest = pClient; return pBest; }
C4Network2IOConnection *C4Network2ClientListBox::ConnectionListItem::GetConnection() const { // get connection by connection ID C4Network2Client *pNetClient = ::Network.Clients.GetClientByID(iClientID); if (!pNetClient) return nullptr; if (iConnID == 0) return pNetClient->getDataConn(); if (iConnID == 1) return pNetClient->getMsgConn(); return nullptr; }
void C4Network2IO::OnPunch(C4NetIO::addr_t addr) { // Sanity check assert (addr.sin_family == AF_INET); if (addr.sin_family != AF_INET) return; ZeroMem(addr.sin_zero, sizeof(addr.sin_zero)); // Add for local client C4Network2Client *pLocal = ::Network.Clients.GetLocal(); if (pLocal) if (pLocal->AddAddr(C4Network2Address(addr, P_UDP), true)) ::Network.InvalidateReference(); }
bool C4Network2ClientList::SendMsgToClient(int32_t iClient, C4NetIOPacket RREF rPkt) { // find client C4Network2Client *pClient = GetClientByID(iClient); if (!pClient) return false; // connected? send directly if (pClient->isConnected()) return pClient->SendMsg(rPkt); // forward C4PacketFwd Fwd; Fwd.SetListType(false); Fwd.AddClient(iClient); Fwd.SetData(rPkt); return SendMsgToHost(MkC4NetIOPacket(PID_FwdReq, Fwd)); }
void C4Network2ClientList::DeleteClient(C4Network2Client *pClient) { // close connections pClient->CloseConns("removing client"); // remove from list if (pClient == pFirst) pFirst = pClient->getNext(); else { C4Network2Client *pPrev; for (pPrev = pFirst; pPrev && pPrev->getNext(); pPrev = pPrev->getNext()) if (pPrev->getNext() == pClient) break; if (pPrev && pPrev->getNext() == pClient) pPrev->pNext = pClient->getNext(); } // remove auto-accept pIO->RemoveAutoAccept(pClient->getCore()); // delete delete pClient; }
bool C4Network2ClientList::BroadcastMsgToConnClients(const C4NetIOPacket &rPkt) { // Send a msg to all clients that are currently directly reachable. // lock pIO->BeginBroadcast(false); // select connections for broadcast for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) if (pClient->isConnected()) pClient->getMsgConn()->SetBroadcastTarget(true); // broadcast bool fSuccess = pIO->Broadcast(rPkt); // unlock pIO->EndBroadcast(); // finished return fSuccess; }
void C4Network2Stats::ExecuteSecond() { statFPS.RecordValue(C4Graph::ValueType(Game.FPS)); statNetI.RecordValue( C4Graph::ValueType(Game.Network.NetIO.getProtIRate(P_TCP) + Game.Network.NetIO.getProtIRate(P_UDP))); statNetO.RecordValue( C4Graph::ValueType(Game.Network.NetIO.getProtORate(P_TCP) + Game.Network.NetIO.getProtORate(P_UDP))); // pings for all clients C4Network2Client *pClient = NULL; while (pClient = Game.Network.Clients.GetNextClient(pClient)) if (pClient->getStatPing()) { int iPing = 0; C4Network2IOConnection *pConn = pClient->getMsgConn(); if (pConn) iPing = pConn->getLag(); pClient->getStatPing()->RecordValue(C4Graph::ValueType(iPing)); } ++SecondCounter; }
bool C4Network2ClientList::BroadcastMsgToClients(const C4NetIOPacket &rPkt) { // Send a msg to all clients, including clients that are not connected to // this computer (will get forwarded by host). C4PacketFwd Fwd; Fwd.SetListType(true); // lock pIO->BeginBroadcast(false); // select connections for broadcast for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) if (!pClient->isHost()) if (pClient->isConnected()) { pClient->getMsgConn()->SetBroadcastTarget(true); Fwd.AddClient(pClient->getID()); } // broadcast bool fSuccess = pIO->Broadcast(rPkt); // unlock pIO->EndBroadcast(); // clients: send forward request to host if (!fHost) { Fwd.SetData(rPkt); fSuccess &= SendMsgToHost(MkC4NetIOPacket(PID_FwdReq, Fwd)); } return fSuccess; }
void C4Network2ClientList::DoConnectAttempts() { // check interval time_t t; time(&t); for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) if (!pClient->isLocal() && !pClient->isRemoved() && pClient->getNextConnAttempt() && pClient->getNextConnAttempt() <= t) // attempt connect pClient->DoConnectAttempt(pIO); }
void C4Network2ClientListBox::Update() { // sync with client list ListItem *pItem = static_cast<ListItem *>(pClientWindow->GetFirst()), *pNext; const C4Client *pClient = nullptr; while ((pClient = Game.Clients.getClient(pClient))) { // skip host in startup board if (IsStartup() && pClient->isHost()) continue; // deleted client(s) present? this will also delete unneeded client connections of previous client while (pItem && (pItem->GetClientID() < pClient->getID())) { pNext = static_cast<ListItem *>(pItem->GetNext()); delete pItem; pItem = pNext; } // same present for update? // need not check for connection ID, because a client item will always be placed before the corresponding connection items if (pItem && pItem->GetClientID() == pClient->getID()) { pItem->Update(); pItem = static_cast<ListItem *>(pItem->GetNext()); } else // not present: insert (or add if pItem=nullptr) InsertElement(new ClientListItem(this, pClient->getID()), pItem); // update connections for client // but no connections in startup board if (IsStartup()) continue; // enumerate client connections C4Network2Client *pNetClient = pClient->getNetClient(); if (!pNetClient) continue; // local client does not have connections C4Network2IOConnection *pLastConn = nullptr; for (int i = 0; i<2; ++i) { C4Network2IOConnection *pConn = i ? pNetClient->getMsgConn() : pNetClient->getDataConn(); if (!pConn) continue; if (pConn == pLastConn) continue; // combined connection: Display only one pLastConn = pConn; // del leading items while (pItem && ((pItem->GetClientID() < pClient->getID()) || ((pItem->GetClientID() == pClient->getID()) && (pItem->GetConnectionID() < i)))) { pNext = static_cast<ListItem *>(pItem->GetNext()); delete pItem; pItem = pNext; } // update connection item if (pItem && pItem->GetClientID() == pClient->getID() && pItem->GetConnectionID() == i) { pItem->Update(); pItem = static_cast<ListItem *>(pItem->GetNext()); } else { // new connection: create it InsertElement(new ConnectionListItem(this, pClient->getID(), i), pItem); } } } // del trailing items while (pItem) { pNext = static_cast<ListItem *>(pItem->GetNext()); delete pItem; pItem = pNext; } }
void C4Network2ClientList::ResetReady() { for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) if (pClient->isWaitedFor()) pClient->SetStatus(NCS_NotReady); }
void C4Network2ClientList::SendAddresses(C4Network2IOConnection *pConn) { // send all client addresses known for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext()) pClient->SendAddresses(pConn); }
void C4Network2ClientDlg::UpdateText() { // begin updating (clears previous text) BeginUpdateText(); // get core const C4Client *pClient = Game.Clients.getClientByID(iClientID); if (!pClient) { // client ID unknown AddLineFmt(LoadResStr("IDS_NET_CLIENT_INFO_UNKNOWNID"), iClientID); } else { // get client (may be nullptr for local info) C4Network2Client *pNetClient = pClient->getNetClient(); // show some info StdCopyStrBuf strInfo; if (!pClient->isActivated()) { strInfo.Append(LoadResStr("IDS_MSG_INACTIVE")); strInfo.Append(" "); } if (pClient->isLocal()) { strInfo.Append(LoadResStr("IDS_MSG_LOCAL")); strInfo.Append(" "); } strInfo.AppendFormat("%s %s (ID #%d)%s", LoadResStr(pClient->isHost() ? "IDS_MSG_HOST" : "IDS_MSG_CLIENT"), pClient->getName(), iClientID, ::Network.isHost() && pNetClient && !pNetClient->isReady() ? " (!ack)" : ""); AddLine(strInfo.getData()); // show addresses int iCnt; if ((iCnt=pNetClient->getAddrCnt())) { AddLine(LoadResStr("IDS_NET_CLIENT_INFO_ADDRESSES")); for (int i=0; i<iCnt; ++i) { C4Network2Address addr = pNetClient->getAddr(i); AddLineFmt(" %d: %s", i, // adress index addr.toString().getData()); } } else AddLine(LoadResStr("IDS_NET_CLIENT_INFO_NOADDRESSES")); // show connection if (pNetClient) { // connections if (pNetClient->isConnected()) { AddLineFmt(LoadResStr("IDS_NET_CLIENT_INFO_CONNECTIONS"), pNetClient->getMsgConn() == pNetClient->getDataConn() ? "Msg/Data" : "Msg", ::Network.NetIO.getNetIOName(pNetClient->getMsgConn()->getNetClass()), pNetClient->getMsgConn()->getPeerAddr().ToString().getData(), pNetClient->getMsgConn()->getPingTime()); if (pNetClient->getMsgConn() != pNetClient->getDataConn()) AddLineFmt(LoadResStr("IDS_NET_CLIENT_INFO_CONNDATA"), ::Network.NetIO.getNetIOName(pNetClient->getDataConn()->getNetClass()), pNetClient->getDataConn()->getPeerAddr().ToString().getData(), pNetClient->getDataConn()->getPingTime()); } else AddLine(LoadResStr("IDS_NET_CLIENT_INFO_NOCONNECTIONS")); } } // update done EndUpdateText(); }
void C4Network2ClientListBox::ClientListItem::Update() { // update wait label if (pPing) { int iWait = ::Control.Network.ClientPerfStat(iClientID); pPing->SetText(FormatString("%d ms", iWait).getData()); pPing->SetColor(C4RGB( Clamp(255-Abs(iWait)*5, 0, 255), Clamp(255-iWait*5, 0, 255), Clamp(255+iWait*5, 0, 255))); } // update activation status const C4Client *pClient = GetClient(); if (!pClient) return; bool fIsActive = pClient->isActivated(); if (fIsActive != fShownActive) { fShownActive = fIsActive; if (!pClient->isHost()) pStatusIcon->SetIcon(fIsActive ? C4GUI::Ico_Client : C4GUI::Ico_ObserverClient); if (pActivateBtn) { pActivateBtn->SetIcon(fIsActive ? C4GUI::Ico_Active : C4GUI::Ico_Inactive); pActivateBtn->SetToolTip(LoadResStrNoAmp(fIsActive ? "IDS_NET_DEACTIVATECLIENT" : "IDS_NET_ACTIVATECLIENT")); } } // update players in tooltip StdStrBuf sCltPlrs(Game.PlayerInfos.GetActivePlayerNames(false, iClientID)); pName->SetToolTip(sCltPlrs.getData()); // update icon: Network status C4GUI::Icons icoStatus = C4GUI::Ico_UnknownClient; C4Network2Client *pClt = pClient->getNetClient(); if (pClt) { switch (pClt->getStatus()) { case NCS_Joining: // waiting for join data case NCS_Chasing: // client is behind (status not acknowledged, isn't waited for) case NCS_NotReady: // client is behind (status not acknowledged) icoStatus = C4GUI::Ico_Loading; break; case NCS_Ready: // client acknowledged network status icoStatus = C4GUI::Ico_Ready; break; case NCS_Remove: // client is to be removed icoStatus = C4GUI::Ico_Kick; break; default: // whatever assert(false); icoStatus = C4GUI::Ico_Loading; break; } } // sound icon? if (last_sound_time) { time_t dt = time(nullptr) - last_sound_time; if (dt >= SoundIconShowTime) { // stop showing sound icon last_sound_time = 0; } else { // time not up yet: show sound icon icoStatus = C4GUI::Ico_Sound; } } // network OK - control ready? if (!pForDlg->IsStartup() && (icoStatus == C4GUI::Ico_Ready)) { if (!::Control.Network.ClientReady(iClientID, ::Control.ControlTick)) { // control not ready icoStatus = C4GUI::Ico_NetWait; } } // set new icon pStatusIcon->SetIcon(icoStatus); }