diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index 1737c6d9..bf7e74db 100755 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -345,7 +345,7 @@ void CAudioMixerBoard::SetServerName ( const QString& strNewServerName ) void CAudioMixerBoard::SetGUIDesign ( const EGUIDesign eNewDesign ) { // apply GUI design to child GUI controls - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { vecpChanFader[i]->SetGUIDesign ( eNewDesign ); } @@ -354,7 +354,7 @@ void CAudioMixerBoard::SetGUIDesign ( const EGUIDesign eNewDesign ) void CAudioMixerBoard::HideAll() { // make all controls invisible - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { vecpChanFader[i]->Hide(); } @@ -370,7 +370,7 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecCh // search for channels with are already present and preserver their gain // setting, for all other channels, reset gain - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { bool bFaderIsUsed = false; @@ -422,7 +422,7 @@ void CAudioMixerBoard::OnChSoloStateChanged ( const int iChannelIdx, ( static_cast ( iValue ) == Qt::Checked ); // apply "other solo state" for all other channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { if ( i != iChannelIdx ) { diff --git a/src/buffer.cpp b/src/buffer.cpp index 79dd0ff1..90fd49a4 100755 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -35,7 +35,7 @@ void CNetBuf::Init ( const int iNewBlockSize, // store block size value iBlockSize = iNewBlockSize; - // total size -> size of one block times number of blocks + // total size -> size of one block times the number of blocks CBufferBase::Init ( iNewBlockSize * iNewNumBlocks ); // use the "get" flag to make sure the buffer is cleared @@ -46,7 +46,7 @@ void CNetBuf::Init ( const int iNewBlockSize, } bool CNetBuf::Put ( const CVector& vecbyData, - const int iInSize ) + const int iInSize ) { bool bPutOK = true; diff --git a/src/buffer.h b/src/buffer.h index e597f64e..f48a1c87 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -54,7 +54,8 @@ public: eBufState = CBufferBase::BS_EMPTY; } - virtual bool Put ( const CVector& vecData, const int iInSize ) + virtual bool Put ( const CVector& vecData, + const int iInSize ) { // copy new data in internal buffer int iCurPos = 0; @@ -188,6 +189,58 @@ public: protected: enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; + void PutSimulation ( const int iInSize ) + { + // in this simulation only the buffer pointers and the buffer state + // is updated, no actual data is transferred + if ( iPutPos + iInSize > iMemSize ) + { + // data must be written in two steps because of wrap around + iPutPos += iInSize - iMemSize - 1; + } + else + { + // data can be written in one step + iPutPos += iInSize - 1; + } + + // set buffer state flag + if ( iPutPos == iGetPos ) + { + eBufState = CBufferBase::BS_FULL; + } + else + { + eBufState = CBufferBase::BS_OK; + } + } + + void GetSimulation ( const int iInSize ) + { + // in this simulation only the buffer pointers and the buffer state + // is updated, no actual data is transferred + if ( iGetPos + iInSize > iMemSize ) + { + // data must be read in two steps because of wrap around + iGetPos += iInSize - iMemSize - 1; + } + else + { + // data can be read in one step + iGetPos += iInSize - 1; + } + + // set buffer state flag + if ( iPutPos == iGetPos ) + { + eBufState = CBufferBase::BS_EMPTY; + } + else + { + eBufState = CBufferBase::BS_OK; + } + } + CVector vecMemory; int iMemSize; int iGetPos, iPutPos; diff --git a/src/channel.cpp b/src/channel.cpp index d5243067..553737d1 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -27,7 +27,7 @@ // CChannel implementation ***************************************************** CChannel::CChannel ( const bool bNIsServer ) : - vecdGains ( USED_NUM_CHANNELS, (double) 1.0 ), + vecdGains ( MAX_NUM_CHANNELS, (double) 1.0 ), bIsEnabled ( false ), bIsServer ( bNIsServer ), iNetwFrameSizeFact ( FRAME_SIZE_FACTOR_PREFERRED ), @@ -189,7 +189,7 @@ void CChannel::SetGain ( const int iChanID, const double dNewGain ) QMutexLocker locker ( &Mutex ); // set value (make sure channel ID is in range) - if ( ( iChanID >= 0 ) && ( iChanID < USED_NUM_CHANNELS ) ) + if ( ( iChanID >= 0 ) && ( iChanID < MAX_NUM_CHANNELS ) ) { vecdGains[iChanID] = dNewGain; } @@ -200,7 +200,7 @@ double CChannel::GetGain ( const int iChanID ) QMutexLocker locker ( &Mutex ); // get value (make sure channel ID is in range) - if ( ( iChanID >= 0 ) && ( iChanID < USED_NUM_CHANNELS ) ) + if ( ( iChanID >= 0 ) && ( iChanID < MAX_NUM_CHANNELS ) ) { return vecdGains[iChanID]; } diff --git a/src/global.h b/src/global.h index 2d8b9d84..3c5dc8b0 100755 --- a/src/global.h +++ b/src/global.h @@ -148,7 +148,7 @@ LED bar: lbr // actual number of used channels in the server // this parameter can safely be changed from 1 to MAX_NUM_CHANNELS // without any other changes in the code -#define USED_NUM_CHANNELS 6 // used number channels for server +#define DEFAULT_USED_NUM_CHANNELS 6 // default used number channels for server // maximum number of servers registered in the server list #define MAX_NUM_SERVERS_IN_SERVER_LIST 100 diff --git a/src/llconserverdlg.cpp b/src/llconserverdlg.cpp index 1b7d0f1c..e0157331 100755 --- a/src/llconserverdlg.cpp +++ b/src/llconserverdlg.cpp @@ -165,8 +165,8 @@ lvwClients->setMinimumHeight ( 140 ); // insert items in reverse order because in Windows all of them are // always visible -> put first item on the top - vecpListViewItems.Init ( USED_NUM_CHANNELS ); - for ( int i = USED_NUM_CHANNELS - 1; i >= 0; i-- ) + vecpListViewItems.Init ( MAX_NUM_CHANNELS ); + for ( int i = MAX_NUM_CHANNELS - 1; i >= 0; i-- ) { vecpListViewItems[i] = new CServerListViewItem ( lvwClients ); vecpListViewItems[i]->setHidden ( true ); @@ -435,8 +435,11 @@ void CLlconServerDlg::OnTimer() veciJitBufNumFrames, veciNetwFrameSizeFact ); + // we assume that all vectors have the same length + const int iNumChannels = vecHostAddresses.Size(); + // fill list with connected clients - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( !( vecHostAddresses[i].InetAddr == QHostAddress ( (quint32) 0 ) ) ) { diff --git a/src/main.cpp b/src/main.cpp index a8dc5022..2e9a287b 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,6 +58,7 @@ int main ( int argc, char** argv ) bool bStartMinimized = false; bool bConnectOnStartup = false; bool bDisalbeLEDs = false; + int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS; quint16 iPortNumber = LLCON_DEFAULT_PORT_NUMBER; QString strIniFileName = ""; QString strHTMLStatusFileName = ""; @@ -96,6 +97,27 @@ int main ( int argc, char** argv ) } + // Maximum number of channels ------------------------------------------ + if ( GetNumericArgument ( tsConsole, + argc, + argv, + i, + "-u", + "--numchannels", + 1, + MAX_NUM_CHANNELS, + rDbleArgument ) ) + { + iNumServerChannels = static_cast ( rDbleArgument ); + + tsConsole << "- maximum number of channels: " + << iNumServerChannels << endl; + + continue; + } + + + // Start minimized ----------------------------------------------------- if ( GetFlagArgument ( argv, i, @@ -348,7 +370,8 @@ int main ( int argc, char** argv ) { // Server: // actual server object - CServer Server ( strLoggingFileName, + CServer Server ( iNumServerChannels, + strLoggingFileName, iPortNumber, strHTMLStatusFileName, strHistoryFileName, @@ -433,6 +456,7 @@ QString UsageArguments ( char **argv ) "\nRecognized options:\n" " -s, --server start server\n" " -n, --nogui disable GUI (server only)\n" + " -u, --numchannels maximum number of channels (server only)\n" " -z, --startminimized start minimizied (server only)\n" " -l, --log enable logging, set file name\n" " -i, --inifile initialization file name (client only)\n" diff --git a/src/server.cpp b/src/server.cpp index a6fb689f..a6655217 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -164,27 +164,30 @@ void CHighPrecisionTimer::OnTimer() // CServer implementation ****************************************************** -CServer::CServer ( const QString& strLoggingFileName, +CServer::CServer ( const int iNewNumChan, + const QString& strLoggingFileName, const quint16 iPortNumber, const QString& strHTMLStatusFileName, const QString& strHistoryFileName, const QString& strServerNameForHTMLStatusFile, const QString& strCentralServer, const QString& strServerInfo ) : - Socket ( this, iPortNumber ), + iNumChannels ( iNewNumChan ), + Socket ( this, iPortNumber ), bWriteStatusHTMLFile ( false ), - ServerListManager ( iPortNumber, - strCentralServer, - strServerInfo, - &ConnLessProtocol ), - bAutoRunMinimized ( false ) + ServerListManager ( iPortNumber, + strCentralServer, + strServerInfo, + iNewNumChan, + &ConnLessProtocol ), + bAutoRunMinimized ( false ) { int i; // create CELT encoder/decoder for each channel (must be done before // enabling the channels), create a mono and stereo encoder/decoder // for each channel - for ( i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( i = 0; i < iNumChannels; i++ ) { // init audio endocder/decoder (mono) CeltModeMono[i] = celt_mode_create ( @@ -268,7 +271,7 @@ CServer::CServer ( const QString& strLoggingFileName, // enable all channels (for the server all channel must be enabled the // entire life time of the software) - for ( i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( i = 0; i < iNumChannels; i++ ) { vecChannels[i].SetEnable ( true ); } @@ -459,7 +462,7 @@ void CServer::OnTimer() { // first, get number and IDs of connected channels vecChanID.Init ( 0 ); - for ( i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -785,7 +788,7 @@ CVector CServer::CreateChannelList() CVector vecChanInfo ( 0 ); // look for free channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -806,7 +809,7 @@ void CServer::CreateAndSendChanListForAllConChannels() CVector vecChanInfo ( CreateChannelList() ); // now send connected channels list to all connected clients - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -854,7 +857,7 @@ void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID // Send chat text to all connected clients --------------------------------- - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -867,7 +870,7 @@ void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID int CServer::GetFreeChan() { // look for a free channel - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( !vecChannels[i].IsConnected() ) { @@ -884,7 +887,7 @@ int CServer::GetNumberOfConnectedClients() int iNumConnClients = 0; // check all possible channels for connection status - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -901,7 +904,7 @@ int CServer::CheckAddr ( const CHostAddress& Addr ) CHostAddress InetAddr; // check for all possible channels if IP is already in use - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -956,7 +959,7 @@ bool CServer::PutData ( const CVector& vecbyRecBuf, // reset the channel gains of current channel, at the same // time reset gains of this channel ID for all other channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { vecChannels[iCurChanID].SetGain ( i, (double) 1.0 ); @@ -1062,13 +1065,13 @@ void CServer::GetConCliParam ( CVector& vecHostAddresses, CHostAddress InetAddr; // init return values - vecHostAddresses.Init ( USED_NUM_CHANNELS ); - vecsName.Init ( USED_NUM_CHANNELS ); - veciJitBufNumFrames.Init ( USED_NUM_CHANNELS ); - veciNetwFrameSizeFact.Init ( USED_NUM_CHANNELS ); + vecHostAddresses.Init ( iNumChannels ); + vecsName.Init ( iNumChannels ); + veciJitBufNumFrames.Init ( iNumChannels ); + veciNetwFrameSizeFact.Init ( iNumChannels ); // check all possible channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].GetAddress ( InetAddr ) ) { @@ -1112,7 +1115,7 @@ void CServer::WriteHTMLChannelList() // get the number of connected clients int iNumConnClients = 0; - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -1129,7 +1132,7 @@ void CServer::WriteHTMLChannelList() else { // write entry for each connected client - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + for ( int i = 0; i < iNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { diff --git a/src/server.h b/src/server.h index d2c5eea4..bc6af7a3 100755 --- a/src/server.h +++ b/src/server.h @@ -103,7 +103,8 @@ class CServer : public QObject Q_OBJECT public: - CServer ( const QString& strLoggingFileName, + CServer ( const int iNewNumChan, + const QString& strLoggingFileName, const quint16 iPortNumber, const QString& strHTMLStatusFileName, const QString& strHistoryFileName, @@ -200,6 +201,7 @@ protected: // do not use the vector class since CChannel does not have appropriate // copy constructor/operator CChannel vecChannels[MAX_NUM_CHANNELS]; + int iNumChannels; CProtocol ConnLessProtocol; QMutex Mutex; diff --git a/src/serverlist.cpp b/src/serverlist.cpp index e08f526c..6ac80ae6 100755 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -1,428 +1,429 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "serverlist.h" - - -/* Implementation *************************************************************/ -CServerListManager::CServerListManager ( const quint16 iNPortNum, - const QString& sNCentServAddr, - const QString& strServerInfo, - CProtocol* pNConLProt ) - : iPortNumber ( iNPortNum ), - iNumPredefinedServers ( 0 ), - bUseDefaultCentralServerAddress ( false ), - pConnLessProtocol ( pNConLProt ) -{ - // set the central server address - SetCentralServerAddress ( sNCentServAddr ); - - // prepare the server info information - QStringList slServInfoSeparateParams; - int iServInfoNumSplitItems = 0; - - if ( !strServerInfo.isEmpty() ) - { - // split the different parameter strings - slServInfoSeparateParams = strServerInfo.split ( ";" ); - - // get the number of items in the split list - iServInfoNumSplitItems = slServInfoSeparateParams.count(); - } - - // per definition, the very first entry is this server and this entry will - // never be deleted - ServerList.clear(); - - // init server list entry (server info for this server) with defaults, per - // definition the client substitudes the IP address of the central server - // itself for his server list - CServerListEntry ThisServerListEntry ( - CHostAddress(), - iPortNumber, - "", - "", - QLocale::system().country(), - "", - USED_NUM_CHANNELS, - true ); - - // parse the server info string according to definition: - // [this server name];[this server city]; ... - // [this server country as QLocale ID]; ... - // per definition, we expect at least three parameters - if ( iServInfoNumSplitItems >= 3 ) - { - // [this server name] - ThisServerListEntry.strName = slServInfoSeparateParams[0]; - - // [this server city] - ThisServerListEntry.strCity = slServInfoSeparateParams[1]; - - // [this server country as QLocale ID] - const int iCountry = slServInfoSeparateParams[2].toInt(); - if ( ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) ) - { - ThisServerListEntry.eCountry = static_cast ( - iCountry ); - } - } - - // per definition, the first entry in the server list it the own server - ServerList.append ( ThisServerListEntry ); - - // parse the predefined server infos (if any) according to definition: - // [server1 address];[server1 name];[server1 city]; ... - // [server1 country as QLocale ID]; ... - // [server2 address];[server2 name];[server2 city]; ... - // [server2 country as QLocale ID]; ... - // ... - int iCurUsedServInfoSplitItems = 3; // three items are used for this server - - // we always expect four items per new server, also check for maximum - // allowed number of servers in the server list - while ( ( iServInfoNumSplitItems - iCurUsedServInfoSplitItems >= 4 ) && - ( iNumPredefinedServers <= MAX_NUM_SERVERS_IN_SERVER_LIST ) ) - { - // create a new server list entry - CServerListEntry NewServerListEntry ( - CHostAddress(), - 0, // port number not used - "", - "", - QLocale::AnyCountry, - "", - USED_NUM_CHANNELS, - true ); - - // [server n address] - LlconNetwUtil().ParseNetworkAddress ( - slServInfoSeparateParams[iCurUsedServInfoSplitItems], - NewServerListEntry.HostAddr ); - - // [server n name] - NewServerListEntry.strName = - slServInfoSeparateParams[iCurUsedServInfoSplitItems + 1]; - - // [server n city] - NewServerListEntry.strCity = - slServInfoSeparateParams[iCurUsedServInfoSplitItems + 2]; - - // [server n country as QLocale ID] - const int iCountry = - slServInfoSeparateParams[iCurUsedServInfoSplitItems + 3].toInt(); - - if ( ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) ) - { - NewServerListEntry.eCountry = static_cast ( - iCountry ); - } - - // add the new server to the server list - ServerList.append ( NewServerListEntry ); - - // we have used four items and have created one predefined server - // (adjust counters) - iCurUsedServInfoSplitItems += 4; - iNumPredefinedServers++; - } - - - // Connections ------------------------------------------------------------- - QObject::connect ( &TimerPollList, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPollList() ) ); - - QObject::connect ( &TimerRegistering, SIGNAL ( timeout() ), - this, SLOT ( OnTimerRegistering() ) ); -} - -void CServerListManager::SetCentralServerAddress ( const QString sNCentServAddr ) -{ - QMutexLocker locker ( &Mutex ); - - strCentralServerAddress = sNCentServAddr; - - // per definition: If the central server address is empty, the server list - // is disabled. - // per definition: If we are in server mode and the central server address - // is the localhost address, we are in central server mode. For the central - // server, the server list is always enabled. - if ( !strCentralServerAddress.isEmpty() ) - { - bIsCentralServer = - ( !strCentralServerAddress.toLower().compare ( "localhost" ) || - !strCentralServerAddress.compare ( "127.0.0.1" ) ); - - bEnabled = true; - } - else - { - bIsCentralServer = false; - bEnabled = true; - } -} - -void CServerListManager::Update() -{ - QMutexLocker locker ( &Mutex ); - - if ( bEnabled ) - { - if ( bIsCentralServer ) - { - // start timer for polling the server list if enabled - // 1 minute = 60 * 1000 ms - TimerPollList.start ( SERVLIST_POLL_TIME_MINUTES * 60000 ); - } - else - { - // initiate registration right away so that we do not have to wait - // for the first time out of the timer until the slave server gets - // registered at the central server, note that we have to unlock - // the mutex before calling the function since inside this function - // the mutex is locked, too - locker.unlock(); - { - OnTimerRegistering(); - } - locker.relock(); - - // start timer for registering this server at the central server - // 1 minute = 60 * 1000 ms - TimerRegistering.start ( SERVLIST_REGIST_INTERV_MINUTES * 60000 ); - } - } - else - { - // disable service -> stop timer - if ( bIsCentralServer ) - { - TimerPollList.stop(); - } - else - { - TimerRegistering.stop(); - } - } -} - - -/* Central server functionality ***********************************************/ -void CServerListManager::OnTimerPollList() -{ - QMutexLocker locker ( &Mutex ); - - // check all list entries except of the very first one (which is the central - // server entry) and the predefined servers if they are still valid - for ( int iIdx = 1 + iNumPredefinedServers; iIdx < ServerList.size(); iIdx++ ) - { - // 1 minute = 60 * 1000 ms - if ( ServerList[iIdx].RegisterTime.elapsed() > - ( SERVLIST_TIME_OUT_MINUTES * 60000 ) ) - { - // remove this list entry - ServerList.removeAt ( iIdx ); - } - } -} - -void CServerListManager::CentralServerRegisterServer ( const CHostAddress& InetAddr, - const CServerCoreInfo& ServerInfo ) -{ - QMutexLocker locker ( &Mutex ); - - if ( bIsCentralServer && bEnabled ) - { - const int iCurServerListSize = ServerList.size(); - - // check for maximum allowed number of servers in the server list - if ( iCurServerListSize < MAX_NUM_SERVERS_IN_SERVER_LIST ) - { - // define invalid index used as a flag - const int ciInvalidIdx = -1; - - // Check if server is already registered. Use address to identify - // a server. The very first list entry must not be checked since - // this is per definition the central server (i.e., this server) - int iSelIdx = ciInvalidIdx; // initialize with an illegal value - for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) - { - if ( ServerList[iIdx].HostAddr == InetAddr ) - { - // store entry index - iSelIdx = iIdx; - - // entry found, leave for-loop - continue; - } - } - - // if server is not yet registered, we have to create a new entry - if ( iSelIdx == ciInvalidIdx ) - { - // create a new server list entry and init with received data - ServerList.append ( CServerListEntry ( InetAddr, ServerInfo ) ); - } - else - { - // do not update the information in the predefined servers - if ( iSelIdx > iNumPredefinedServers ) - { - // update all data and call update registration function - ServerList[iSelIdx].iLocalPortNumber = ServerInfo.iLocalPortNumber; - ServerList[iSelIdx].strName = ServerInfo.strName; - ServerList[iSelIdx].strTopic = ServerInfo.strTopic; - ServerList[iSelIdx].eCountry = ServerInfo.eCountry; - ServerList[iSelIdx].strCity = ServerInfo.strCity; - ServerList[iSelIdx].iMaxNumClients = ServerInfo.iMaxNumClients; - ServerList[iSelIdx].bPermanentOnline = ServerInfo.bPermanentOnline; - - ServerList[iSelIdx].UpdateRegistration(); - } - } - } - } -} - -void CServerListManager::CentralServerUnregisterServer ( const CHostAddress& InetAddr ) -{ - QMutexLocker locker ( &Mutex ); - - if ( bIsCentralServer && bEnabled ) - { - const int iCurServerListSize = ServerList.size(); - - // Find the server to unregister in the list. The very first list entry - // must not be checked since this is per definition the central server - // (i.e., this server), also the predefined servers must not be checked - for ( int iIdx = 1 + iNumPredefinedServers; iIdx < iCurServerListSize; iIdx++ ) - { - if ( ServerList[iIdx].HostAddr == InetAddr ) - { - // remove this list entry - ServerList.removeAt ( iIdx ); - - // entry found, leave for-loop - continue; - } - } - } -} - -void CServerListManager::CentralServerQueryServerList ( const CHostAddress& InetAddr ) -{ - QMutexLocker locker ( &Mutex ); - - if ( bIsCentralServer && bEnabled ) - { - const int iCurServerListSize = ServerList.size(); - - // allocate memory for the entire list - CVector vecServerInfo ( iCurServerListSize ); - - // copy the list (we have to copy it since the message requires - // a vector but the list is actually stored in a QList object and - // not in a vector object - for ( int iIdx = 0; iIdx < iCurServerListSize; iIdx++ ) - { - // copy list item - vecServerInfo[iIdx] = ServerList[iIdx]; - - if ( iIdx > 0 ) - { - // check if the address of the client which is requesting the - // list is the same address as one server in the list -> in this - // case he has to connect to the local host address - if ( vecServerInfo[iIdx].HostAddr.InetAddr == InetAddr.InetAddr ) - { - vecServerInfo[iIdx].HostAddr.InetAddr = - QHostAddress ( QHostAddress::LocalHost ); - - // take the local port number instead of the received port - // number since some NAT (network address translation) might - // have changed the port, note that the predefined servers - // are treated differently, for these we assume that the - // received port number is the same as the actual port - // number - if ( iIdx > iNumPredefinedServers ) - { - vecServerInfo[iIdx].HostAddr.iPort = - ServerList[iIdx].iLocalPortNumber; - } - } - else - { - // create "send empty message" for all registered servers - // (except of the very first list entry since this is this - // server (central server) per definition) and also it is - // not required to send this message, if the server is on - // the same computer - pConnLessProtocol->CreateCLSendEmptyMesMes ( - vecServerInfo[iIdx].HostAddr, - InetAddr ); - } - } - } - - // send the server list to the client - pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo ); - } -} - - -/* Slave server functionality *************************************************/ -void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister ) -{ - // we need the lock since the user might change the server properties at - // any time - QMutexLocker locker ( &Mutex ); - - // get the correct central server address +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "serverlist.h" + + +/* Implementation *************************************************************/ +CServerListManager::CServerListManager ( const quint16 iNPortNum, + const QString& sNCentServAddr, + const QString& strServerInfo, + const int iNumChannels, + CProtocol* pNConLProt ) + : iPortNumber ( iNPortNum ), + iNumPredefinedServers ( 0 ), + bUseDefaultCentralServerAddress ( false ), + pConnLessProtocol ( pNConLProt ) +{ + // set the central server address + SetCentralServerAddress ( sNCentServAddr ); + + // prepare the server info information + QStringList slServInfoSeparateParams; + int iServInfoNumSplitItems = 0; + + if ( !strServerInfo.isEmpty() ) + { + // split the different parameter strings + slServInfoSeparateParams = strServerInfo.split ( ";" ); + + // get the number of items in the split list + iServInfoNumSplitItems = slServInfoSeparateParams.count(); + } + + // per definition, the very first entry is this server and this entry will + // never be deleted + ServerList.clear(); + + // init server list entry (server info for this server) with defaults, per + // definition the client substitudes the IP address of the central server + // itself for his server list + CServerListEntry ThisServerListEntry ( + CHostAddress(), + iPortNumber, + "", + "", + QLocale::system().country(), + "", + iNumChannels, + true ); + + // parse the server info string according to definition: + // [this server name];[this server city]; ... + // [this server country as QLocale ID]; ... + // per definition, we expect at least three parameters + if ( iServInfoNumSplitItems >= 3 ) + { + // [this server name] + ThisServerListEntry.strName = slServInfoSeparateParams[0]; + + // [this server city] + ThisServerListEntry.strCity = slServInfoSeparateParams[1]; + + // [this server country as QLocale ID] + const int iCountry = slServInfoSeparateParams[2].toInt(); + if ( ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) ) + { + ThisServerListEntry.eCountry = static_cast ( + iCountry ); + } + } + + // per definition, the first entry in the server list it the own server + ServerList.append ( ThisServerListEntry ); + + // parse the predefined server infos (if any) according to definition: + // [server1 address];[server1 name];[server1 city]; ... + // [server1 country as QLocale ID]; ... + // [server2 address];[server2 name];[server2 city]; ... + // [server2 country as QLocale ID]; ... + // ... + int iCurUsedServInfoSplitItems = 3; // three items are used for this server + + // we always expect four items per new server, also check for maximum + // allowed number of servers in the server list + while ( ( iServInfoNumSplitItems - iCurUsedServInfoSplitItems >= 4 ) && + ( iNumPredefinedServers <= MAX_NUM_SERVERS_IN_SERVER_LIST ) ) + { + // create a new server list entry + CServerListEntry NewServerListEntry ( + CHostAddress(), + 0, // port number not used + "", + "", + QLocale::AnyCountry, + "", + iNumChannels, + true ); + + // [server n address] + LlconNetwUtil().ParseNetworkAddress ( + slServInfoSeparateParams[iCurUsedServInfoSplitItems], + NewServerListEntry.HostAddr ); + + // [server n name] + NewServerListEntry.strName = + slServInfoSeparateParams[iCurUsedServInfoSplitItems + 1]; + + // [server n city] + NewServerListEntry.strCity = + slServInfoSeparateParams[iCurUsedServInfoSplitItems + 2]; + + // [server n country as QLocale ID] + const int iCountry = + slServInfoSeparateParams[iCurUsedServInfoSplitItems + 3].toInt(); + + if ( ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) ) + { + NewServerListEntry.eCountry = static_cast ( + iCountry ); + } + + // add the new server to the server list + ServerList.append ( NewServerListEntry ); + + // we have used four items and have created one predefined server + // (adjust counters) + iCurUsedServInfoSplitItems += 4; + iNumPredefinedServers++; + } + + + // Connections ------------------------------------------------------------- + QObject::connect ( &TimerPollList, SIGNAL ( timeout() ), + this, SLOT ( OnTimerPollList() ) ); + + QObject::connect ( &TimerRegistering, SIGNAL ( timeout() ), + this, SLOT ( OnTimerRegistering() ) ); +} + +void CServerListManager::SetCentralServerAddress ( const QString sNCentServAddr ) +{ + QMutexLocker locker ( &Mutex ); + + strCentralServerAddress = sNCentServAddr; + + // per definition: If the central server address is empty, the server list + // is disabled. + // per definition: If we are in server mode and the central server address + // is the localhost address, we are in central server mode. For the central + // server, the server list is always enabled. + if ( !strCentralServerAddress.isEmpty() ) + { + bIsCentralServer = + ( !strCentralServerAddress.toLower().compare ( "localhost" ) || + !strCentralServerAddress.compare ( "127.0.0.1" ) ); + + bEnabled = true; + } + else + { + bIsCentralServer = false; + bEnabled = true; + } +} + +void CServerListManager::Update() +{ + QMutexLocker locker ( &Mutex ); + + if ( bEnabled ) + { + if ( bIsCentralServer ) + { + // start timer for polling the server list if enabled + // 1 minute = 60 * 1000 ms + TimerPollList.start ( SERVLIST_POLL_TIME_MINUTES * 60000 ); + } + else + { + // initiate registration right away so that we do not have to wait + // for the first time out of the timer until the slave server gets + // registered at the central server, note that we have to unlock + // the mutex before calling the function since inside this function + // the mutex is locked, too + locker.unlock(); + { + OnTimerRegistering(); + } + locker.relock(); + + // start timer for registering this server at the central server + // 1 minute = 60 * 1000 ms + TimerRegistering.start ( SERVLIST_REGIST_INTERV_MINUTES * 60000 ); + } + } + else + { + // disable service -> stop timer + if ( bIsCentralServer ) + { + TimerPollList.stop(); + } + else + { + TimerRegistering.stop(); + } + } +} + + +/* Central server functionality ***********************************************/ +void CServerListManager::OnTimerPollList() +{ + QMutexLocker locker ( &Mutex ); + + // check all list entries except of the very first one (which is the central + // server entry) and the predefined servers if they are still valid + for ( int iIdx = 1 + iNumPredefinedServers; iIdx < ServerList.size(); iIdx++ ) + { + // 1 minute = 60 * 1000 ms + if ( ServerList[iIdx].RegisterTime.elapsed() > + ( SERVLIST_TIME_OUT_MINUTES * 60000 ) ) + { + // remove this list entry + ServerList.removeAt ( iIdx ); + } + } +} + +void CServerListManager::CentralServerRegisterServer ( const CHostAddress& InetAddr, + const CServerCoreInfo& ServerInfo ) +{ + QMutexLocker locker ( &Mutex ); + + if ( bIsCentralServer && bEnabled ) + { + const int iCurServerListSize = ServerList.size(); + + // check for maximum allowed number of servers in the server list + if ( iCurServerListSize < MAX_NUM_SERVERS_IN_SERVER_LIST ) + { + // define invalid index used as a flag + const int ciInvalidIdx = -1; + + // Check if server is already registered. Use address to identify + // a server. The very first list entry must not be checked since + // this is per definition the central server (i.e., this server) + int iSelIdx = ciInvalidIdx; // initialize with an illegal value + for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) + { + if ( ServerList[iIdx].HostAddr == InetAddr ) + { + // store entry index + iSelIdx = iIdx; + + // entry found, leave for-loop + continue; + } + } + + // if server is not yet registered, we have to create a new entry + if ( iSelIdx == ciInvalidIdx ) + { + // create a new server list entry and init with received data + ServerList.append ( CServerListEntry ( InetAddr, ServerInfo ) ); + } + else + { + // do not update the information in the predefined servers + if ( iSelIdx > iNumPredefinedServers ) + { + // update all data and call update registration function + ServerList[iSelIdx].iLocalPortNumber = ServerInfo.iLocalPortNumber; + ServerList[iSelIdx].strName = ServerInfo.strName; + ServerList[iSelIdx].strTopic = ServerInfo.strTopic; + ServerList[iSelIdx].eCountry = ServerInfo.eCountry; + ServerList[iSelIdx].strCity = ServerInfo.strCity; + ServerList[iSelIdx].iMaxNumClients = ServerInfo.iMaxNumClients; + ServerList[iSelIdx].bPermanentOnline = ServerInfo.bPermanentOnline; + + ServerList[iSelIdx].UpdateRegistration(); + } + } + } + } +} + +void CServerListManager::CentralServerUnregisterServer ( const CHostAddress& InetAddr ) +{ + QMutexLocker locker ( &Mutex ); + + if ( bIsCentralServer && bEnabled ) + { + const int iCurServerListSize = ServerList.size(); + + // Find the server to unregister in the list. The very first list entry + // must not be checked since this is per definition the central server + // (i.e., this server), also the predefined servers must not be checked + for ( int iIdx = 1 + iNumPredefinedServers; iIdx < iCurServerListSize; iIdx++ ) + { + if ( ServerList[iIdx].HostAddr == InetAddr ) + { + // remove this list entry + ServerList.removeAt ( iIdx ); + + // entry found, leave for-loop + continue; + } + } + } +} + +void CServerListManager::CentralServerQueryServerList ( const CHostAddress& InetAddr ) +{ + QMutexLocker locker ( &Mutex ); + + if ( bIsCentralServer && bEnabled ) + { + const int iCurServerListSize = ServerList.size(); + + // allocate memory for the entire list + CVector vecServerInfo ( iCurServerListSize ); + + // copy the list (we have to copy it since the message requires + // a vector but the list is actually stored in a QList object and + // not in a vector object + for ( int iIdx = 0; iIdx < iCurServerListSize; iIdx++ ) + { + // copy list item + vecServerInfo[iIdx] = ServerList[iIdx]; + + if ( iIdx > 0 ) + { + // check if the address of the client which is requesting the + // list is the same address as one server in the list -> in this + // case he has to connect to the local host address + if ( vecServerInfo[iIdx].HostAddr.InetAddr == InetAddr.InetAddr ) + { + vecServerInfo[iIdx].HostAddr.InetAddr = + QHostAddress ( QHostAddress::LocalHost ); + + // take the local port number instead of the received port + // number since some NAT (network address translation) might + // have changed the port, note that the predefined servers + // are treated differently, for these we assume that the + // received port number is the same as the actual port + // number + if ( iIdx > iNumPredefinedServers ) + { + vecServerInfo[iIdx].HostAddr.iPort = + ServerList[iIdx].iLocalPortNumber; + } + } + else + { + // create "send empty message" for all registered servers + // (except of the very first list entry since this is this + // server (central server) per definition) and also it is + // not required to send this message, if the server is on + // the same computer + pConnLessProtocol->CreateCLSendEmptyMesMes ( + vecServerInfo[iIdx].HostAddr, + InetAddr ); + } + } + } + + // send the server list to the client + pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo ); + } +} + + +/* Slave server functionality *************************************************/ +void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister ) +{ + // we need the lock since the user might change the server properties at + // any time + QMutexLocker locker ( &Mutex ); + + // get the correct central server address const QString strCurCentrServAddr = SELECT_SERVER_ADDRESS ( bUseDefaultCentralServerAddress, strCentralServerAddress ); - - // For the slave server, the slave server properties are store in the - // very first item in the server list (which is actually no server list - // but just one item long for the slave server). - // Note that we always have to parse the server address again since if - // it is an URL of a dynamic IP address, the IP address might have - // changed in the meanwhile. - CHostAddress HostAddress; - if ( LlconNetwUtil().ParseNetworkAddress ( strCurCentrServAddr, - HostAddress ) ) - { - if ( bIsRegister ) - { - // register server - pConnLessProtocol->CreateCLRegisterServerMes ( HostAddress, - ServerList[0] ); - } - else - { - // unregister server - pConnLessProtocol->CreateCLUnregisterServerMes ( HostAddress ); - } - } -} + + // For the slave server, the slave server properties are store in the + // very first item in the server list (which is actually no server list + // but just one item long for the slave server). + // Note that we always have to parse the server address again since if + // it is an URL of a dynamic IP address, the IP address might have + // changed in the meanwhile. + CHostAddress HostAddress; + if ( LlconNetwUtil().ParseNetworkAddress ( strCurCentrServAddr, + HostAddress ) ) + { + if ( bIsRegister ) + { + // register server + pConnLessProtocol->CreateCLRegisterServerMes ( HostAddress, + ServerList[0] ); + } + else + { + // unregister server + pConnLessProtocol->CreateCLUnregisterServerMes ( HostAddress ); + } + } +} diff --git a/src/serverlist.h b/src/serverlist.h index c6e7f777..43d8013b 100755 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -128,6 +128,7 @@ public: CServerListManager ( const quint16 iNPortNum, const QString& sNCentServAddr, const QString& strServerInfo, + const int iNumChannels, CProtocol* pNConLProt ); // the update has to be called if any change to the server list