diff --git a/src/global.h b/src/global.h index cb49542b..85fbec22 100755 --- a/src/global.h +++ b/src/global.h @@ -215,6 +215,13 @@ LED bar: lbr // defines the minimum time a server must run to be a permanent server #define SERVLIST_TIME_PERMSERV_MINUTES 1440 // minutes, 1440 = 60 min * 24 h +// registration response timeout +#define REGISTER_SERVER_TIME_OUT_MS 500 // ms + +// defines the maximum number of times to retry server registration +// when no response is received within the timeout (before reverting +// to SERVLIST_REGIST_INTERV_MINUTES) +#define REGISTER_SERVER_RETRY_LIMIT 5 // count // length of the moving average buffer for response time measurement #define TIME_MOV_AV_RESPONSE_SECONDS 30 // seconds diff --git a/src/protocol.cpp b/src/protocol.cpp index a33c6ca8..ed54bf2c 100755 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -289,6 +289,7 @@ CONNECTION LESS MESSAGES note: does not have any data -> n = 0 + - PROTMESSID_CLM_CHANNEL_LEVEL_LIST: The channel level list +----------------------------------+ @@ -304,13 +305,32 @@ CONNECTION LESS MESSAGES where an odd number of clients is connected, there will be four unused upper bits in the final byte, containing 0xF (which is out of range) - the server may compute them message when any client has used + the server may compute the message when any client has used PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST to opt in - the server may issue to message only to a client that has used + the server should issue the message only to a client that has used PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST to opt in +- PROTMESSID_CLM_REGISTER_SERVER_RESP: result of registration request + + +---------------+ + | 1 byte status | + +---------------+ + + - "status": + Values of ESvrRegResult: + 0 - success + 1 - failed due to central server list being full + + Note: the central server may send this message in response to a + PROTMESSID_CLM_REGISTER_SERVER request. + Where not received, the registering server may only retry up to + five times for one registration request at 500ms intervals. + Beyond this, it should "ping" every 15 minutes + (standard re-registration timeout). + + ****************************************************************************** * * This program is free software; you can redistribute it and/or modify it under @@ -676,6 +696,10 @@ if ( rand() < ( RAND_MAX / 2 ) ) return false; case PROTMESSID_CLM_CHANNEL_LEVEL_LIST: bRet = EvaluateCLChannelLevelListMes ( InetAddr, vecbyMesBodyData ); break; + + case PROTMESSID_CLM_REGISTER_SERVER_RESP: + bRet = EvaluateCLRegisterServerResp ( InetAddr, vecbyMesBodyData ); + break; } } else @@ -2090,6 +2114,39 @@ bool CProtocol::EvaluateCLChannelLevelListMes ( const CHostAddress& InetAdd return false; // no error } +void CProtocol::CreateCLRegisterServerResp ( const CHostAddress& InetAddr, + const ESvrRegResult eResult ) +{ + int iPos = 0; // init position pointer + CVector vecData( 1 ); + + PutValOnStream ( vecData, iPos, + static_cast ( eResult ), 1 ); + + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REGISTER_SERVER_RESP, + vecData, + InetAddr ); +} + +bool CProtocol::EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, + const CVector& vecData ) +{ + int iPos = 0; // init position pointer + const int iDataLen = vecData.Size(); + + if ( iDataLen != 1 ) + { + return true; + } + + ESvrRegResult eResult = static_cast ( + GetValFromStream ( vecData, iPos, 1 ) ); + + // invoke message action + emit CLRegisterServerResp ( InetAddr, eResult ); + + return false; // no error +} /******************************************************************************\ * Message generation and parsing * diff --git a/src/protocol.h b/src/protocol.h index 0470b24c..027c4284 100755 --- a/src/protocol.h +++ b/src/protocol.h @@ -73,6 +73,7 @@ #define PROTMESSID_CLM_CONN_CLIENTS_LIST 1013 // channel infos for connected clients #define PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST 1014 // request the connected clients list #define PROTMESSID_CLM_CHANNEL_LEVEL_LIST 1015 // channel level list +#define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request // lengths of message as defined in protocol.cpp file #define MESS_HEADER_LENGTH_BYTE 7 // TAG (2), ID (2), cnt (1), length (2) @@ -130,6 +131,8 @@ public: void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); + void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, + const ESvrRegResult eResult ); static bool ParseMessageFrame ( const CVector& vecbyData, const int iNumBytesIn, @@ -246,6 +249,8 @@ protected: bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, + const CVector& vecData ); int iOldRecID; int iOldRecCnt; @@ -305,4 +310,6 @@ signals: void CLReqConnClientsList ( CHostAddress InetAddr ); void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); + void CLRegisterServerResp ( CHostAddress InetAddr, + ESvrRegResult eStatus ); }; diff --git a/src/server.cpp b/src/server.cpp index 91f97809..da5edff8 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -438,6 +438,10 @@ CServer::CServer ( const int iNewMaxNumChan, SIGNAL ( CLReqServerList ( CHostAddress ) ), this, SLOT ( OnCLReqServerList ( CHostAddress ) ) ); + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLRegisterServerResp ( CHostAddress, ESvrRegResult ) ), + this, SLOT ( OnCLRegisterServerResp ( CHostAddress, ESvrRegResult ) ) ); + QObject::connect ( &ConnLessProtocol, SIGNAL ( CLSendEmptyMes ( CHostAddress ) ), this, SLOT ( OnCLSendEmptyMes ( CHostAddress ) ) ); @@ -454,6 +458,10 @@ CServer::CServer ( const int iNewMaxNumChan, SIGNAL ( CLReqConnClientsList ( CHostAddress ) ), this, SLOT ( OnCLReqConnClientsList ( CHostAddress ) ) ); + QObject::connect ( &ServerListManager, + SIGNAL ( SvrRegStatusChanged() ), + this, SLOT ( OnSvrRegStatusChanged() ) ); + // CODE TAG: MAX_NUM_CHANNELS_TAG // make sure we have MAX_NUM_CHANNELS connections!!! // send message diff --git a/src/server.h b/src/server.h index e8e6d225..ace2d57c 100755 --- a/src/server.h +++ b/src/server.h @@ -190,6 +190,7 @@ public: QLocale::Country GetServerCountry() { return ServerListManager.GetServerCountry(); } + ESvrRegStatus GetSvrRegStatus() { return ServerListManager.GetSvrRegStatus(); } // GUI settings ------------------------------------------------------------ void SetAutoRunMinimized ( const bool NAuRuMin ) { bAutoRunMinimized = NAuRuMin; } @@ -305,6 +306,7 @@ signals: void Started(); void Stopped(); void ClientDisconnected ( const int iChID ); + void SvrRegStatusChanged(); void AudioFrame ( const int iChID, const QString stChName, const CHostAddress RecHostAddr, @@ -373,6 +375,14 @@ public slots: ServerListManager.CentralServerRegisterServer ( InetAddr, LInetAddr, ServerInfo ); } + void OnCLRegisterServerResp ( CHostAddress /* unused */, + ESvrRegResult eResult ) + { + ServerListManager.StoreRegistrationResult ( eResult ); + } + + void OnSvrRegStatusChanged() { emit SvrRegStatusChanged(); } + void OnCLUnregisterServerReceived ( CHostAddress InetAddr ) { ServerListManager.CentralServerUnregisterServer ( InetAddr ); diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index 04564e3d..03da852f 100755 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -61,15 +61,20 @@ CServerDlg::CServerDlg ( CServer* pNServP, "Dialog: If enabled, a Creative Commons Licence dialog is shown " "each time a new user connects the server." ) ); - // register server flag - chbRegisterServer->setWhatsThis ( tr ( "Register Server Status: If " - "the register server check box is checked, this server registers " + // Make My Server Public flag + chbRegisterServer->setWhatsThis ( tr ( "Make My Server Public: If " + "the Make My Server Public check box is checked, this server registers " "itself at the central server so that all " ) + APP_NAME + tr ( " users can see the server in the connect dialog server list and " "connect to it. The registering of the server is renewed periodically " "to make sure that all servers in the connect dialog server list are " "actually available." ) ); + // register server status label + lblRegSvrStatus->setWhatsThis ( tr ( "Register Server Status: If " + "the Make My Server Public check box is checked, this will show " + "the success of registration with the central server." ) ); + // central server address QString strCentrServAddr = tr ( "Central Server Address: The " "Central server address is the IP address or URL of the central server " @@ -322,6 +327,9 @@ lvwClients->setMinimumHeight ( 140 ); QObject::connect ( pServer, SIGNAL ( Stopped() ), this, SLOT ( OnServerStopped() ) ); + QObject::connect ( pServer, SIGNAL ( SvrRegStatusChanged() ), + this, SLOT ( OnSvrRegStatusChanged() ) ); + QObject::connect ( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), this, SLOT ( OnAboutToQuit() ) ); @@ -507,6 +515,8 @@ void CServerDlg::UpdateGUIDependencies() const bool bCurUseDefCentServAddr = ( pServer->GetCentralServerAddressType() != AT_MANUAL ); + const ESvrRegStatus eSvrRegStatus = pServer->GetSvrRegStatus(); + // if register server is not enabled, we disable all the configuration // controls for the server list cbxCentServAddrType->setEnabled ( bCurSerListEnabled ); @@ -534,6 +544,34 @@ void CServerDlg::UpdateGUIDependencies() // server list is enabled and not the default address is used edtCentralServerAddress->setEnabled ( !bCurUseDefCentServAddr && bCurSerListEnabled ); + + QString strStatus; + switch ( eSvrRegStatus ) + { + case SRS_UNREGISTERED: + strStatus = "Unregistered"; + break; + case SRS_BAD_ADDRESS: + strStatus = "Bad address"; + break; + case SRS_REQUESTED: + strStatus = "Registration requested"; + break; + case SRS_TIME_OUT: + strStatus = "Using longer retries"; + break; + case SRS_UNKNOWN_RESP: + strStatus = "Check server version, retrying"; + break; + case SRS_REGISTERED: + strStatus = "Registered"; + break; + case SRS_CENTRAL_SVR_FULL: + strStatus = "Central Server full"; + break; + } + lblRegSvrStatus->setText( strStatus ); + } void CServerDlg::UpdateSystemTrayIcon ( const bool bIsActive ) diff --git a/src/serverdlg.h b/src/serverdlg.h index 50f40b27..febb44ef 100755 --- a/src/serverdlg.h +++ b/src/serverdlg.h @@ -94,6 +94,7 @@ public slots: void OnTimer(); void OnServerStarted() { UpdateSystemTrayIcon ( true ); } void OnServerStopped() { UpdateSystemTrayIcon ( false ); } + void OnSvrRegStatusChanged() { UpdateGUIDependencies(); } void OnSysTrayMenuOpen() { ShowWindowInForeground(); } void OnSysTrayMenuHide() { hide(); } void OnSysTrayMenuExit() { close(); } diff --git a/src/serverdlgbase.ui b/src/serverdlgbase.ui index aebc5b3a..03d5476d 100755 --- a/src/serverdlgbase.ui +++ b/src/serverdlgbase.ui @@ -61,11 +61,38 @@ - - - Make My Server Public (Register My Server in the Server List) - - + + + + + Make My Server Public (Register My Server in the Server List) + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + STATUS + + + + diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 38c67051..22948e5a 100755 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -34,7 +34,9 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, : iNumPredefinedServers ( 0 ), eCentralServerAddressType ( AT_MANUAL ), // must be AT_MANUAL for the "no GUI" case bCentServPingServerInList ( bNCentServPingServerInList ), - pConnLessProtocol ( pNConLProt ) + pConnLessProtocol ( pNConLProt ), + eSvrRegStatus ( SRS_UNREGISTERED ), + iSvrRegRetries ( 0 ) { // set the central server address SetCentralServerAddress ( sNCentServAddr ); @@ -177,6 +179,11 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, QObject::connect ( &TimerRegistering, SIGNAL ( timeout() ), this, SLOT ( OnTimerRegistering() ) ); + + TimerCLRegisterServerResp.setSingleShot ( true ); + TimerCLRegisterServerResp.setInterval ( REGISTER_SERVER_TIME_OUT_MS ); + QObject::connect ( &TimerCLRegisterServerResp, SIGNAL ( timeout() ), + this, SLOT ( OnTimerCLRegisterServerResp() ) ); } void CServerListManager::SetCentralServerAddress ( const QString sNCentServAddr ) @@ -239,6 +246,12 @@ void CServerListManager::Update() } locker.relock(); + // reset the retry counter to zero because update was called + iSvrRegRetries = 0; + + // start timer for registration timeout + TimerCLRegisterServerResp.start(); + // start timer for registering this server at the central server // 1 minute = 60 * 1000 ms TimerRegistering.start ( SERVLIST_REGIST_INTERV_MINUTES * 60000 ); @@ -266,6 +279,7 @@ void CServerListManager::Update() } else { + TimerCLRegisterServerResp.stop(); TimerRegistering.stop(); TimerPingCentralServer.stop(); } @@ -297,7 +311,7 @@ void CServerListManager::OnTimerPollList() // server entry) and the predefined servers if they are still valid. // Note that we have to use "ServerList.size()" function in the for loop // since we may remove elements from the server list inside the for loop. - for ( int iIdx = 1 + iNumPredefinedServers; iIdx < ServerList.size(); iIdx++ ) + for ( int iIdx = 1 + iNumPredefinedServers; iIdx < ServerList.size(); ) { // 1 minute = 60 * 1000 ms if ( ServerList[iIdx].RegisterTime.elapsed() > @@ -306,6 +320,11 @@ void CServerListManager::OnTimerPollList() // remove this list entry ServerList.removeAt ( iIdx ); } + else + { + // Move to the next entry (only on else) + iIdx++; + } } } @@ -346,6 +365,7 @@ void CServerListManager::CentralServerRegisterServer ( const CHostAddress& In { // create a new server list entry and init with received data ServerList.append ( CServerListEntry ( InetAddr, LInetAddr, ServerInfo ) ); + iSelIdx = iCurServerListSize; } } else @@ -364,6 +384,10 @@ void CServerListManager::CentralServerRegisterServer ( const CHostAddress& In ServerList[iSelIdx].UpdateRegistration(); } } + + pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr, iSelIdx == ciInvalidIdx + ? ESvrRegResult::SRR_CENTRAL_SVR_FULL + : ESvrRegResult::SRR_REGISTERED ); } } @@ -452,6 +476,30 @@ void CServerListManager::CentralServerQueryServerList ( const CHostAddress& Inet /* Slave server functionality *************************************************/ +void CServerListManager::StoreRegistrationResult ( ESvrRegResult eResult ) +{ + // we need the lock since the user might change the server properties at + // any time so another response could arrive + QMutexLocker locker ( &Mutex ); + + // We got some response, so stop the retry timer + TimerCLRegisterServerResp.stop(); + + eSvrRegStatus = ESvrRegStatus::SRS_UNKNOWN_RESP; + + switch ( eResult ) + { + case ESvrRegResult::SRR_REGISTERED: + eSvrRegStatus = ESvrRegStatus::SRS_REGISTERED; + break; + case ESvrRegResult::SRR_CENTRAL_SVR_FULL: + eSvrRegStatus = ESvrRegStatus::SRS_CENTRAL_SVR_FULL; + break; + } + + emit SvrRegStatusChanged(); +} + void CServerListManager::OnTimerPingCentralServer() { QMutexLocker locker ( &Mutex ); @@ -465,6 +513,32 @@ void CServerListManager::OnTimerPingCentralServer() } } +void CServerListManager::OnTimerCLRegisterServerResp() +{ + QMutexLocker locker ( &Mutex ); + + if ( eSvrRegStatus == SRS_REQUESTED ) + { + iSvrRegRetries++; + if ( iSvrRegRetries >= REGISTER_SERVER_RETRY_LIMIT ) + { + eSvrRegStatus = SRS_TIME_OUT; + emit SvrRegStatusChanged(); + } + else + { + locker.unlock(); + { + OnTimerRegistering(); + } + locker.relock(); + + // re-start timer for registration timeout + TimerCLRegisterServerResp.start(); + } + } +} + void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister ) { // we need the lock since the user might change the server properties at @@ -488,6 +562,9 @@ void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister ) if ( bIsRegister ) { // register server + eSvrRegStatus = SRS_REQUESTED; + emit SvrRegStatusChanged(); + pConnLessProtocol->CreateCLRegisterServerMes ( SlaveCurCentServerHostAddress, SlaveCurLocalHostAddress, ServerList[0] ); @@ -495,7 +572,15 @@ void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister ) else { // unregister server + eSvrRegStatus = SRS_UNREGISTERED; + emit SvrRegStatusChanged(); + pConnLessProtocol->CreateCLUnregisterServerMes ( SlaveCurCentServerHostAddress ); } } + else + { + eSvrRegStatus = SRS_BAD_ADDRESS; + emit SvrRegStatusChanged(); + } } diff --git a/src/serverlist.h b/src/serverlist.h index ace982b8..79a9b30a 100755 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -171,6 +171,10 @@ public: QLocale::Country GetServerCountry() { return ServerList[0].eCountry; } + ESvrRegStatus GetSvrRegStatus() { return eSvrRegStatus; } + + void StoreRegistrationResult ( ESvrRegResult eStatus ); + protected: void SlaveServerRegisterServer ( const bool bIsRegister ); @@ -178,6 +182,7 @@ protected: QTimer TimerRegistering; QTimer TimerPingServerInList; QTimer TimerPingCentralServer; + QTimer TimerCLRegisterServerResp; QMutex Mutex; @@ -195,10 +200,20 @@ protected: CProtocol* pConnLessProtocol; + // server registration status + ESvrRegStatus eSvrRegStatus; + + // count of registration retries + int iSvrRegRetries; + public slots: void OnTimerPollList(); void OnTimerPingServerInList(); void OnTimerPingCentralServer(); + void OnTimerCLRegisterServerResp(); void OnTimerRegistering() { SlaveServerRegisterServer ( true ); } void OnTimerIsPermanent() { ServerList[0].bPermanentOnline = true; } + +signals: + void SvrRegStatusChanged(); }; diff --git a/src/util.h b/src/util.h index ea060704..3b39523f 100755 --- a/src/util.h +++ b/src/util.h @@ -572,6 +572,27 @@ enum ECSAddType }; +// Slave server registration state --------------------------------------------- +enum ESvrRegStatus +{ + SRS_UNREGISTERED = 0, + SRS_BAD_ADDRESS = 1, + SRS_REQUESTED = 2, + SRS_TIME_OUT = 3, + SRS_UNKNOWN_RESP = 4, + SRS_REGISTERED = 5, + SRS_CENTRAL_SVR_FULL = 6 +}; + + +// Central server registration outcome ----------------------------------------- +enum ESvrRegResult +{ + SRR_REGISTERED = 0, + SRR_CENTRAL_SVR_FULL = 1 +}; + + // Skill level enum ------------------------------------------------------------ enum ESkillLevel {