/* ************************************************************************* ArmageTron -- Just another Tron Lightcycle Game in 3D. Copyright (C) 2000 Manuel Moos (manuel@moosnet.de) ************************************************************************** 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 "tMemManager.h" #include "tSysTime.h" #include "tConsole.h" #include "tList.h" #include "tLocale.h" #include "tToDo.h" #include "tDirectories.h" #include "nServerInfo.h" #include "nNetObject.h" #ifdef KRAWALL_SERVER #include "nAuthentification.h" #endif #include "nNet.h" #include static nServerInfo* sn_FirstServer = NULL; static sn_ServerInfoCreator* sn_Creator = NULL; static nServerInfo* sn_Transmitting[MAXCLIENTS+2]; static unsigned int sn_LastKnown [MAXCLIENTS+2]; static bool sn_SendAll [MAXCLIENTS+2]; static bool sn_Requested [MAXCLIENTS+2]; static REAL sn_Timeout [MAXCLIENTS+2]; #ifdef KRAWALL_SERVER static bool sn_Auth [MAXCLIENTS+2]; #endif static nServerInfo* sn_Requesting=NULL; static unsigned int sn_NextTransactionNr = 0; static bool sn_AcceptingFromBroadcast = false; static bool sn_AcceptingFromMaster = false; static bool sn_IsMaster = false; static nServerInfo* sn_QuerySoon =NULL; static nTimeRolling sn_QueryTimeout = 0; static const REAL sn_queryDelay = 1.5f; // time delay between queries of the same server static const REAL sn_queryDelayGlobal = 0.1f; // time delay between all queries static const int sn_numQueries = 20; // number of queries per try static const int sn_TNALostContact = 2; // minimum TNA value to be considered contact loss static int sn_MaxUnreachable() { return sn_IsMaster ? 10 : 5; } static int sn_MaxTNA() { return sn_IsMaster ? 30 : 10; } static int sn_MaxTNATotal() { return sn_IsMaster ? 300 : 50; } static tList sn_Polling; static tString sn_LastLoaded; // stores the filename of the last loaded server list // make sure every new client gets a new server list static void login_callback(){ sn_Transmitting[nCallbackLoginLogout::User()] = NULL; sn_LastKnown [nCallbackLoginLogout::User()] = 0; sn_SendAll [nCallbackLoginLogout::User()] = true; sn_Requested [nCallbackLoginLogout::User()] = false; sn_Timeout [nCallbackLoginLogout::User()] = tSysTimeFloat() + 70; #ifdef KRAWALL_SERVER sn_Auth [nCallbackLoginLogout::User()] = false; #endif } // authentification stuff #ifdef KRAWALL_SERVER void ResultCallback(const tString& username, const tString& origUsername, int user, bool success) { if (success) { sn_Auth[user] = true; sn_Transmitting[user] = nServerInfo::GetFirstServer(); } else nAuthentification::RequestLogin(username, user, tOutput("$login_request_failed"), true); } #endif // KRAWALL_SERVER static nCallbackLoginLogout nlc(&login_callback); static nServerInfo *CreateServerInfo() { if (sn_Creator) return (*sn_Creator)(); else return tNEW(nServerInfo()); } nServerInfo::nServerInfo() :tListItem(sn_FirstServer), pollID(-1), transactionNr(sn_NextTransactionNr), connectionName(""), port(0), method(0), key(0), advancedInfoSet(false), advancedInfoSetEver(false), queried(0), timeQuerySent(0), ping(10), release_("pre_0.2.5"), login2_(true), timesNotAnswered(5), name(""), users(0), maxUsers_(MAXCLIENTS), score(-10000) { if (sn_IsMaster) { sn_NextTransactionNr++; if (0 == sn_NextTransactionNr) sn_NextTransactionNr++; } } nServerInfo::~nServerInfo() { sn_Polling.Remove(this, pollID); for (int i = MAXCLIENTS+1; i>=0; i--) if (sn_Transmitting[i] == this) sn_Transmitting[i] = sn_Transmitting[i]->Next(); if (sn_Requesting == this) sn_Requesting = sn_Requesting->Next(); if (sn_QuerySoon == this) sn_Requesting = NULL; } // calculates the score from other data void nServerInfo::CalcScore() { static int userScore[8] = { -100, 0, 100, 250, 300, 300, 250, 100 }; // do nothing if we are requerying if ( !this->advancedInfoSet && this->advancedInfoSetEver ) { return; } score = 100; if (ping > .1) score -= (ping - .1) * 300; if (users < 8 && users >= 0) score += userScore[users]; if (users >= maxUsers_ ) score -= 200; if ( Compat_Ok != this->Compatibility() ) { score -= 400; } score -= fabs( this->Version().Max() - sn_MyVersion().Max() ) * 10; } // read/write all the information a normal server will broadcast /* void nServerInfo::NetWrite(nMessage &m) { m << name; m << users; } */ void nServerInfo::NetRead (nMessage &m) { m >> name; m >> users; if ( !m.End() ) { m >> version_; m >> release_; login2_ = true; } else { login2_ = false; } if ( !m.End() ) { m >> maxUsers_; } if ( !m.End() ) { m >> userNames_; m >> options_; m >> url_; } else { userNames_ = "No Info\n"; options_ = "No Info\n"; url_ = "No Info\n"; } userNamesOneLine_.Clear(); for ( int i = 0; i < userNames_.Len()-2 ; ++i ) { char c = userNames_[i]; if ( c == '\n' ) userNamesOneLine_ << ", "; else userNamesOneLine_ << c; } timesNotAnswered = 0; if (!advancedInfoSet) { if ( sn_IsMaster && !advancedInfoSetEver ) { con << "Acknowledged server: " << this->connectionName << ":" << this->port << "\n"; Save(); } advancedInfoSet = true; advancedInfoSetEver = true; ping = tSysTimeFloat() - timeQuerySent; CalcScore(); } queried = 0; // queried = true; sn_Polling.Remove(this, pollID); } // the same for the information the master server is responsible for void nServerInfo::MasterNetWrite(nMessage &m) { m << transactionNr; m << connectionName; m << port; m << method; m << key; } /* void nServerInfo::MasterNetRead (nMessage &m) { m >> transactionNr; m >> connectionName; m >> port; m >> method; m >> key; } */ static const tString TRANSACTION("transaction"); static const tString CONNECTION ("connection"); static const tString PORT ("port"); static const tString METHOD ("method"); static const tString KEY ("key"); static const tString TNA ("tna"); static const tString NAME ("name"); static const tString VERSION ("version"); static const tString RELEASE ("release"); static const tString URL ("url"); static const tString END ("ServerEnd"); static const tString START ("ServerBegin"); void nServerInfo::Save(std::ostream &s) { s << CONNECTION << "\t" << connectionName << "\n"; s << PORT << "\t" << port << "\n"; s << METHOD << "\t" << method << "\n"; s << KEY << "\t" << key.Len() << " "; for (int i = key.Len()-1; i>=0; i--) s << "\t" << key(i); s << "\n"; s << TRANSACTION << "\t" << transactionNr << "\n"; s << VERSION << "\t" << version_ << "\n"; s << RELEASE << "\t" << release_ << "\n"; s << URL << "\t" << url_ << "\n"; s << TNA << "\t" << timesNotAnswered << "\n"; s << NAME << "\t" << name << "\n"; s << END << "\t" << "\n\n"; } void nServerInfo::Load(std::istream &s) { bool end = false; while (!end && s.good() && !s.eof()) { tString id; s >> id; int dummy; if (id == START) continue; else if (id == END) end = true; else if (id == METHOD) s >> dummy; else if (id == TRANSACTION) s >> transactionNr; else if ( id == VERSION ) s >> version_; else if ( id == RELEASE ) release_.ReadLine( s ); else if ( id == URL ) url_.ReadLine( s ); else if (id == CONNECTION) s >> connectionName; else if (id == PORT) s >> port; else if (id == KEY) { int len; s >> len; key.SetLen(len); for (int i=len-1; i>=0; i--) s >> key(i); } else if(id == TNA) s >> timesNotAnswered; else if (id == NAME) name.ReadLine(s); else con << "Warning: unknown tag " << id << " found in server config file.\n"; queried = 0; advancedInfoSet = false; advancedInfoSetEver = false; } } nServerInfo *nServerInfo::GetFirstServer() { return sn_FirstServer; } nServerInfo *nServerInfo::Prev() { if (reinterpret_cast(anchor) == &sn_FirstServer) return NULL; static nServerInfo& info = *this; // will not cause recursion since it is called only once. // uaaa. Pointer magic... // TODO: perhaps there is a better way using member pointers? return reinterpret_cast ( reinterpret_cast (anchor) + ( reinterpret_cast( &info ) - reinterpret_cast (&info.next) ) ); } void nServerInfo::Sort() // sort the servers by score { // insertion sort nServerInfo *run = GetFirstServer(); while (run) { nServerInfo* ascend = run; run = run->Next(); while (1) { nServerInfo *prev = ascend->Prev(); // check if ascend is well in place if (!prev) break; // if (prev->queried > ascend->queried) // break; int compare = 0; bool ppoll = prev->Polling(); bool punr = !prev->Reachable() && !ppoll; bool apoll = ascend->Polling(); bool aunr = !ascend->Reachable() && !apoll; if (punr) compare ++; if (aunr) compare --; if (0 == compare) { if (ppoll) compare++; if (apoll) compare--; } if (0 == compare) { if (ascend->score > prev->score) compare = 1; else compare = -1; } if (compare <= 0) break; // swap prev and ascend prev->Remove(); prev->Insert(ascend->next); } } } void nServerInfo::CalcScoreAll() // calculate the score for all servers { nServerInfo *run = GetFirstServer(); while (run) { run->CalcScore(); run = run->Next(); } } void nServerInfo::DeleteAll(bool autosave) // delete all server infos { if (autosave) { Save(); sn_LastLoaded.SetLen(0); } while (GetFirstServer()) delete GetFirstServer(); } // set the function that creates new server infos (so the server infos // generated by calls from the master server can be of a derived class). // returns the old function, so you can resore it later. sn_ServerInfoCreator* nServerInfo::SetCreator(sn_ServerInfoCreator* creator) { sn_ServerInfoCreator* ret = sn_Creator; sn_Creator = creator; return ret; } void nServerInfo::Save() { if ( sn_LastLoaded.Len() <= 1 ) { return; } Save( tDirectories::Var(), sn_LastLoaded ); } void nServerInfo::Save(const tPath& path, const char *filename) // save/load all server infos { std::ofstream s; if ( path.Open( s, filename ) ) { nServerInfo *run = GetFirstServer(); while (run && run->Next()) { run = run->Next(); } while (run) { s << START << "\n"; run->Save(s); run = run->Prev(); } } } void nServerInfo::Load(const tPath& path, const char *filename) { sn_LastLoaded = filename; std::ifstream s; path.Open( s, filename ); while (s.good() && !s.eof()) { tString id; s >> id; if (id == START) { nServerInfo *server = CreateServerInfo(); server->Load(s); // remove double servers bool IsDouble = 0; nServerInfo *run = GetFirstServer(); while(!IsDouble && run) { if (run != server && run->connectionName == server->connectionName && run->port == server->port) IsDouble = true; run = run->Next(); } if (IsDouble) delete server; } else break; } } // ********************************** // now the real network protocol part // ********************************** // the network handlers and descriptors used by the master protocol // used to transfer small server information (adress, port, public key) // from the master server or response to a broadcast to the client static nDescriptor SmallServerDescriptor(50,nServerInfo::GetSmallServerInfo,"small_server", true); // used to transfer the rest of the server info (name, number of players, etc) // from the server directly to the client static nDescriptor BigServerDescriptor(51,nServerInfo::GetBigServerInfo,"big_server", true); // request small server information from master server/broadcast static nDescriptor RequestSmallServerInfoDescriptor(52,nServerInfo::GiveSmallServerInfo,"small_request", true); // request big server information from master server/broadcast static nDescriptor RequestBigServerInfoDescriptor(53,nServerInfo::GiveBigServerInfo,"big_request", true); // used to transfer the rest of the server info (name, number of players, etc) // from the server directly to the client //atic nDescriptor ExtraServerDescriptor(54,nServerInfo::GetExtraServerInfo,"extra_server", true); // request big server information from master server/broadcast //atic nDescriptor RequestExtraServerInfoDescriptor(55,nServerInfo::GiveExtraServerInfo,"extra_request", true); static bool net_Accept() { return nCallbackAcceptPackedWithoutConnection::Descriptor()==SmallServerDescriptor.ID() || nCallbackAcceptPackedWithoutConnection::Descriptor()==BigServerDescriptor.ID(); } static nCallbackAcceptPackedWithoutConnection net_acc( &net_Accept ); static int sn_ServerCount = 0; static void ReadServerInfo(nMessage &m, unsigned int& port, tString& connectionName, bool acceptDirect, bool acceptMaster) { m >> port; // get the port m >> connectionName; // and the name if (connectionName.Len()<=1) // no valid name (must come directly from the server who does not know his own address) { sn_GetAdr(m.SenderID(), connectionName); // remove the port for (int i=connectionName.Len(); i>=0; i--) if (':' == connectionName[i]) { connectionName[i] = '\0'; connectionName.SetLen(i+1); } if (!acceptDirect && !acceptMaster) { // Cheater(m.SenderID()); return; } } else { if (!acceptMaster) { // Cheater(m.SenderID()); return; } } } static void WriteServerInfo(nMessage &m, unsigned int port, const tString connectionName) { m << port; if (connectionName.Len() > 3 && connectionName[0] == '1' && connectionName[1] == '2' && connectionName[2] == '7') m << tString(""); else m << connectionName; } static void WriteMyInfo(nMessage &m) { m << sn_GetServerPort(); m << tString(""); } //static const tString s_LocalName="192.168.0.2"; static const tString s_LocalName="127.0.0.2"; static const tString s_GlobalName="armagetron.kicks-ass.net"; static void S_GlobalizeName(tString &connectionName) { if ( sn_IsMaster ) { if ( connectionName == s_LocalName ) { connectionName = s_GlobalName; } } } static tString S_LocalizeName(const tString &connectionName) { // if ( !sn_IsMaster ) { if ( connectionName[0] < '0' || connectionName[0] > '9' ) { // get the IP adress struct sockaddr temp; ANET_GetAddrFromName (connectionName, &temp); tString connectionNameTemp = ANET_AddrToString (&temp); // remove the port part for(int pos = connectionNameTemp.Len()-1; pos>=0; pos--) if (':' == connectionNameTemp[pos]) { connectionNameTemp[pos]='\0'; connectionNameTemp.SetLen(pos+1); } return connectionNameTemp; } } return connectionName; } void nServerInfo::Alive() { // give it a new transaction number if it was down temporarily if ( sn_IsMaster && TimesNotAnswered() >= sn_TNALostContact ) { transactionNr = sn_NextTransactionNr++; if (!sn_NextTransactionNr) sn_NextTransactionNr++; } } void nServerInfo::GetSmallServerInfo(nMessage &m){ unsigned int port; tString connectionName; ReadServerInfo(m, port, connectionName, sn_AcceptingFromBroadcast, sn_AcceptingFromMaster); S_GlobalizeName( connectionName ); // master server should not listen to LAN games if ( sn_IsMaster && connectionName.Len() >= 3 && 0 == strncmp( connectionName, "192", 3 ) ) { return; } sn_ServerCount++; nServerInfo *n = NULL; // check if we already have that server lised nServerInfo *run = GetFirstServer(); int countSameAdr = 0; while(run) { if (run->connectionName == connectionName) { if (countSameAdr++ > 32) n = run; if (run->port == port) n = run; } run = run->Next(); } if (m.End()) return; // so far no objections have been found; create the new server info. if (!n) { n = CreateServerInfo(); n->timesNotAnswered = 0; if ( sn_IsMaster ) { con << "Received new server: " << connectionName << ":" << port << "\n"; } } else { n->Alive(); if ( sn_IsMaster ) { con << "Updated server: " << connectionName << ":" << port << "\n"; } } // n->timesNotAnswered = 1; n->connectionName = connectionName; if (n->name.Len() <= 1) n->name << connectionName << ":" << port; n->port = port; // n->advancedInfoSet = false; n->queried = 0; if (!sn_IsMaster) m >> n->transactionNr; else { n->timesNotAnswered = 5; if (sn_QuerySoon) sn_QuerySoon->QueryServer(); sn_QuerySoon = n; sn_QueryTimeout = tSysTimeFloat() + 5.0f; unsigned int dummy; m >> dummy; } if (sn_IsMaster) { Save(); } } void nServerInfo::GetBigServerInfo(nMessage &m) { unsigned int port; tString connectionName; ReadServerInfo(m, port, connectionName, true, false); // S_GlobalizeName( connectionName ); // find the server nServerInfo *server = GetFirstServer(); while(server && !(server->port == port && S_LocalizeName(server->connectionName) == connectionName)) server = server->Next(); if (!server) return; server->Alive(); server->NetRead(m); server->CalcScore(); // Sort(); } static bool TransIsNewer(unsigned int newTrans, unsigned int oldTrans) { int diff = newTrans - oldTrans; return (diff > 0); } void nServerInfo::GiveSmallServerInfo(nMessage &m) { // start transmitting the server list in master server mode if (sn_IsMaster) { con << "Giving server info to user " << m.SenderID() << "\n"; sn_Requested[m.SenderID()] = true; #ifdef KRAWALL_SERVER // one moment! check if we need authentification tString adr; unsigned int port = sn_GetPort(m.SenderID()); sn_GetAdr(m.SenderID(), adr); if (nKrawall::RequireMasterLogin(adr, port)) { nAuthentification::SetLoginResultCallback(&ResultCallback); nAuthentification::RequestLogin("", m.SenderID(), tOutput("$login_request_master")); } else { sn_Transmitting[m.SenderID()] = GetFirstServer(); sn_Auth[m.SenderID()] = true; } #else sn_Transmitting[m.SenderID()] = GetFirstServer(); #endif if (m.End()) sn_SendAll[m.SenderID()] = true; else { sn_SendAll[m.SenderID()] = false; m >> sn_LastKnown[m.SenderID()]; } // temporary fix: give out all server info. Always. sn_SendAll[m.SenderID()] = true; } else { // immediately respond with a small info tJUST_CONTROLLED_PTR< nMessage > ret = tNEW(nMessage)(SmallServerDescriptor); WriteMyInfo(*ret); unsigned int notrans = 0; *ret << notrans; ret->ClearMessageID(); ret->SendImmediately(m.SenderID(), false); nMessage::SendCollected(m.SenderID()); } } // from nNetwork.cpp int sn_NumRealUsers(); void nServerInfo::GiveBigServerInfo(nMessage &m) { if (sn_IsMaster) Cheater(m.SenderID()); nMessage *ret = tNEW(nMessage)(BigServerDescriptor); WriteMyInfo(*ret); *ret << sn_serverName; *ret << sn_NumRealUsers(); *ret << sn_CurrentVersion(); *ret << sn_programVersion; *ret << sn_MaxUsers(); if ( nServerInfoAdmin::GetAdmin() ) { *ret << nServerInfoAdmin::GetAdmin()->GetUsers(); *ret << nServerInfoAdmin::GetAdmin()->GetOptions(); *ret << nServerInfoAdmin::GetAdmin()->GetUrl(); } else { tString str("UNKNOWN"); *ret << str; *ret << str; *ret << str; } ret->ClearMessageID(); ret->SendImmediately(m.SenderID(), false); nMessage::SendCollected(m.SenderID()); } /* #define nUSERNAMES 0 #define nOPTIONS 1 #define nURL 2 void nServerInfo::GiveExtraServerInfo(nMessage &m) { if (sn_IsMaster) Cheater(m.SenderID()); unsigned short extraType; WriteMyInfo(m); m >> extraType; tJUST_CONTROLLED_PTR< nMessage > pm = tNEW( nMessage( ExtraServerDescriptor ) ); nMessage& mRet = *pm; mRet << extraType; tString ret="UNKNOWN"; if ( nServerInfoAdmin::GetAdmin() ) { switch ( extraType ) { case nUSERNAMES: ret = nServerInfoAdmin::GetAdmin()->GetUsers(); break; case nOPTIONS: ret = nServerInfoAdmin::GetAdmin()->GetOptions(); break; case nURL: ret = nServerInfoAdmin::GetAdmin()->GetUrl(); break; } } mRet << ret; mRet.Send( m.SenderID() ); mRet.ClearMessageID(); mRet.SendImmediately(m.SenderID(), false); nMessage::SendCollected(m.SenderID()); } void nServerInfo::GetExtraServerInfo(nMessage &m) { unsigned int port; tString connectionName; ReadServerInfo(m, port, connectionName, true, false); // S_GlobalizeName( connectionName ); // find the server nServerInfo *server = GetFirstServer(); while(server && !(server->port == port && S_LocalizeName(server->connectionName) == connectionName)) server = server->Next(); if (!server) return; unsigned short extraType; m >> extraType; tString value; m >> value; switch ( extraType ) { case nUSERNAMES: server->userNames_ = value; break; case nOPTIONS: server->options_ = value; break; case nURL: server->url_ = value; break; } server->Alive(); server->CalcScore(); } // queries extra informationn bool nServerInfo::QueryExtraInfo() { static float lastTime = 0.0f; static const float Interval } RequestExtraServerInfoDescriptor */ nConnectError nServerInfo::Connect() { unsigned int portBack = sn_clientPort; sn_clientPort = port; nConnectError error = sn_Connect(connectionName, login2_); sn_clientPort = portBack; return error; } tString MasterFile() { tString ret = "frommaster.srv"; return ret; } void nServerInfo::GetFromMaster(nServerInfo *masterInfo) { sn_AcceptingFromMaster = true; if (!masterInfo) masterInfo = GetDefaultMaster(); DeleteAll(); // load all the servers we know Load( tDirectories::Var(), MasterFile() ); // find the latest server we know about unsigned int latest=0; nServerInfo *run = GetFirstServer(); if (run) { latest = run->TransactionNr(); run = run->Next(); while (run) { if (TransIsNewer(run->TransactionNr(), latest)) latest = run->TransactionNr(); run = run->Next(); } } // connect to the master server con << tOutput("$network_master_connecting"); switch(masterInfo->Connect()) { case nOK: break; case nTIMEOUT: tConsole::Message("$network_master_timeout_title", "$network_master_timeout_inter", 20); return; break; case nDENIED: tConsole::Message("$network_master_denied_title", "$network_master_denied_inter", 20); return; break; } // send the server list request message con << tOutput("$network_master_reqlist"); nMessage *m=tNEW(nMessage)(RequestSmallServerInfoDescriptor); if (GetFirstServer()) *m << latest; m->BroadCast(); sn_ServerCount = 0; int lastReported = 10; // just wait for the data to pour in REAL timeout = tSysTimeFloat() + 60; while(sn_GetNetState() == nCLIENT && timeout > tSysTimeFloat()) { sn_Receive(); usleep(1000); st_DoToDo(); if (sn_ServerCount > lastReported) { tOutput o; o.SetTemplateParameter(1, lastReported); o << "$network_master_status"; con << o; lastReported = (sn_ServerCount/10) * 10; } } tOutput o; o.SetTemplateParameter(1, sn_ServerCount); o << "$network_master_finish"; con << o; Save(tDirectories::Var(), MasterFile()); sn_SetNetState(nSTANDALONE); sn_AcceptingFromMaster = false; tSysTimeFloat(true); } void nServerInfo::GetFromLAN(unsigned int pollBeginPort, unsigned int pollEndPort) { sn_AcceptingFromBroadcast = true; sn_LastLoaded.SetLen(0); // enter client state if (sn_GetNetState() != nCLIENT) sn_SetNetState(nCLIENT); // prepare the request message and broadcast is con << tOutput("$network_master_reqlist"); for (unsigned int port = pollBeginPort; port <= pollEndPort; port++) { nMessage *m=tNEW(nMessage)(RequestSmallServerInfoDescriptor); m->ClearMessageID(); m->SendImmediately(0, false); usleep(1000); nMessage::BroadcastCollected(0, port); } sn_ServerCount = 0; int lastReported = 10; // and just wait a bit for the answers to arrive REAL timeout = tSysTimeFloat() + 1.5f; while(sn_GetNetState() == nCLIENT && timeout > tSysTimeFloat()) { sn_Receive(); usleep(1000); if (sn_ServerCount > lastReported) { tOutput o; o.SetTemplateParameter(1, lastReported); o << "$network_master_status"; con << 0; lastReported = (sn_ServerCount/10) * 10; } } tOutput o; o.SetTemplateParameter(1, sn_ServerCount); o << "$network_master_finish"; con << o; sn_AcceptingFromBroadcast = false; sn_SetNetState(nSTANDALONE); } void nServerInfo::GetFromLANContinuously(unsigned int pollBeginPort, unsigned int pollEndPort) { sn_AcceptingFromBroadcast = true; sn_LastLoaded.SetLen(0); // enter client state if (sn_GetNetState() != nCLIENT) sn_SetNetState(nCLIENT); // prepare the request message and broadcast it for (unsigned int port = pollBeginPort; port <= pollEndPort; port++) { nMessage *m=tNEW(nMessage)(RequestSmallServerInfoDescriptor); m->ClearMessageID(); m->SendImmediately(0, false); usleep(1000); nMessage::BroadcastCollected(0, port); } } void nServerInfo::GetFromLANContinuouslyStop() { sn_AcceptingFromBroadcast = false; sn_SetNetState(nSTANDALONE); } void nServerInfo::TellMasterAboutMe(nServerInfo *masterInfo) { static unsigned int lastPort = 0; // enter server state so we know our true port number sn_SetNetState(nSERVER); unsigned int port = sn_GetServerPort(); if (port == lastPort) return; // the master already knows about us lastPort = port; // send the server descriptor message nMessage *m=tNEW(nMessage)(SmallServerDescriptor); WriteMyInfo(*m); unsigned int dummy = 0; *m << dummy; sn_SetNetState(nSTANDALONE); if (!masterInfo) masterInfo = GetDefaultMaster(); con << tOutput("$network_master_connecting"); masterInfo->Connect(); con << tOutput("$network_master_send"); m->BroadCast(); sn_Receive(); // wait for the data to be accepted nTimeRolling timeout = tSysTimeFloat() + 20; while(sn_GetNetState() == nCLIENT && timeout > tSysTimeFloat() && sn_Connections[0].ackPending > 0) { sn_Receive(); usleep(10000); } sn_SetNetState(nSTANDALONE); } void nServerInfo::QueryServer() // start to get advanced info from this server itself { sn_Polling.Add(this, pollID); #ifdef DEBUG if ( sn_IsMaster ) { con << "Querying server " << connectionName << ":" << port << "\n"; } #endif if (timesNotAnswered > sn_MaxTNA() ) { // server was inactive too long. Delete it if possible. // check if this server is the one with the highest TAN unsigned int latest=0; nServerInfo *run = GetFirstServer(); nServerInfo *best = NULL; while (run == this) run = run->Next(); if (run) { latest = run->TransactionNr(); best = run; run = run->Next(); while (run) { if ((run != this) && TransIsNewer(run->TransactionNr(), latest)) { latest = run->TransactionNr(); best = run; } run = run->Next(); } } // now, best points to the latest (except this) server and // latest is its TAN. // continue if this server is the only one available if (best) { // if THIS server has the latest TAN, simpy transfer it to the second latest. if (TransIsNewer(TransactionNr(), latest)) best->transactionNr = TransactionNr(); timesNotAnswered = 1000; if ( sn_IsMaster ) { con << "Deleted unreachable server: " << this->connectionName << ":" << this->port << "\n"; delete this; } return; } } sn_Bend(connectionName, port); tJUST_CONTROLLED_PTR< nMessage > req = tNEW(nMessage)(RequestBigServerInfoDescriptor); req->ClearMessageID(); req->SendImmediately(0, false); nMessage::SendCollected(0); timeQuerySent = tSysTimeFloat(); if ( queried == 1 ) { if ( ++timesNotAnswered == sn_TNALostContact && sn_IsMaster ) { con << "Lost contact with server: " << this->connectionName << ":" << this->port << "\n"; } } queried++; if ( !this->advancedInfoSetEver ) { score = -1E+32f; } } void GetSenderData(const nMessage &m,tString& name, int& port) { sn_GetAdr(m.SenderID(),name); port = sn_GetPort(m.SenderID()); } void nServerInfo::StartQueryAll() // start querying the advanced info of each of the servers in our list { sn_Requesting = GetFirstServer(); while (sn_Polling.Len()) sn_Polling.Remove(sn_Polling(0), sn_Polling(0)->pollID); nServerInfo *run = GetFirstServer(); while(run) { run->queried = 0; run->advancedInfoSet = 0; int TNA = run->TimesNotAnswered(); // remove known status if ( TNA > 0 ) { run->advancedInfoSetEver = false; } run = run->Next(); } int totalTNAMax = sn_MaxTNATotal(); int maxUnreachable = sn_MaxUnreachable(); int totalTNA = totalTNAMax + 1; int lastTNA = totalTNA + 1; int unreachableCount = 0; while ( ( totalTNA > totalTNAMax || unreachableCount > maxUnreachable ) && lastTNA != totalTNA ) { lastTNA = totalTNA; totalTNA = 0; unreachableCount = 0; nServerInfo *kickOut = NULL; int maxTNA = 0; int minTNA = 100; run = GetFirstServer(); while(run) { int TNA = run->TimesNotAnswered(); // sum up TNAs and determine server with maximum TNA if ( TNA > 0 && TNA <= sn_MaxTNA() ) { unreachableCount++; totalTNA += TNA; if ( TNA > maxTNA ) { maxTNA = TNA; kickOut = run; } } if ( TNA < minTNA ) { minTNA = TNA; } run = run->Next(); } if ( minTNA > 0 ) { // no server was reachable at all! Ehternet cable is probably pulled. return; } // mark worst server for kickout if total TNA is too high if ( kickOut && ( ( totalTNA > totalTNAMax && maxTNA >= sn_TNALostContact ) || unreachableCount > maxUnreachable ) ) { // just delete the bastard! delete kickOut; // kickOut->timesNotAnswered = sn_MaxTNA() + 100; } } } bool nServerInfo::DoQueryAll(int simultaneous) // continue querying the advanced info of each of the servers in our list; return value: do we need to go on with this? { REAL time = tSysTimeFloat(); static REAL globalTimeout = time; if ( time < globalTimeout ) { return true; } globalTimeout = time + sn_queryDelayGlobal; for (int i=sn_Polling.Len()-1; i>=0; i--) { nServerInfo* poll = sn_Polling(i); if (poll->timeQuerySent + sn_queryDelay < time) sn_Polling.Remove(poll, poll->pollID); } if (sn_Requesting && sn_Polling.Len() < simultaneous) { nServerInfo* next = sn_Requesting->Next(); if (!sn_Requesting->advancedInfoSet && sn_Requesting->pollID < 0 && sn_Requesting->queried <= sn_numQueries) { sn_Requesting->QueryServer(); } #ifdef DEBUG else { int x; x = 1; } #endif sn_Requesting = next; } sn_Receive(); if (sn_Requesting) return true; else { bool ret = false; nServerInfo *run = GetFirstServer(); while(run && !ret) { if (!run->advancedInfoSet && (run->queried <= sn_numQueries-1 || run->pollID >= 0)) ret = true; run = run->Next(); } if (ret) { sn_Requesting = GetFirstServer(); Save(); } else Save(); return ret; } } void nServerInfo::RunMaster() { sn_IsMaster = true; sn_AcceptingFromBroadcast = true; nTimeRolling time = tSysTimeFloat(); if (time > sn_QueryTimeout && sn_QuerySoon) { sn_QuerySoon->QueryServer(); sn_QuerySoon = NULL; std::cout.flush(); std::cerr.flush(); } if (sn_NextTransactionNr == 0) // find the latest server we know about { unsigned int latest=0; nServerInfo *run = GetFirstServer(); if (run) { latest = run->TransactionNr(); run = run->Next(); while (run) { if (TransIsNewer(run->TransactionNr(), latest)) latest = run->TransactionNr(); run = run->Next(); } } latest++; if (latest == 0) latest ++; sn_NextTransactionNr = latest; } for (int i=MAXCLIENTS; i>0; i--) { if(sn_Connections[i].socket > 0) { // kick the user soon when the transfer is completed if ((sn_Requested[i] && !sn_Transmitting[i] #ifdef KRAWALL_SERVER && sn_Auth[i] #endif && sn_MessagesPending(i) == 0)) { if (sn_Timeout[i] > tSysTimeFloat() + .2f) sn_Timeout[i] = tSysTimeFloat() + .2f; } // defend against DOS attacks: Kill idle clients if(sn_Timeout[i] < tSysTimeFloat()) sn_KillUser(i, "$network_kill_timeout"); if (!sn_Requested[i] && sn_Timeout[i] < tSysTimeFloat() + 60.0f) sn_KillUser(i, "$network_kill_timeout"); } if (sn_Transmitting[i] && sn_MessagesPending(i) < 3) { for (int j = 10-sn_MessagesPending(i); j>=0 && sn_Transmitting[i]; j--) { // skip known servers if (!sn_SendAll[i]) { while (sn_Transmitting[i] && !TransIsNewer(sn_Transmitting[i]->TransactionNr(), sn_LastKnown[i])) sn_Transmitting[i] = sn_Transmitting[i]->Next(); } if (!sn_Transmitting[i]) continue; if (sn_Transmitting[i]->TimesNotAnswered() < sn_TNALostContact ) { // tell user i about server sn_Transmitting[i] nMessage *m = tNEW(nMessage)(SmallServerDescriptor); WriteServerInfo(*m, sn_Transmitting[i]->Port(), sn_Transmitting[i]->ConnectionName()); *m << sn_Transmitting[i]->TransactionNr(); m->Send(i); } sn_Transmitting[i] = sn_Transmitting[i]->Next(); } } } sn_Receive(); } bool nServerInfo::Reachable() const { return advancedInfoSetEver && TimesNotAnswered() < sn_TNALostContact; } bool nServerInfo::Polling() const { return (!advancedInfoSetEver && queried <= 3 && TimesNotAnswered() < sn_TNALostContact );// || ( !advancedInfoSet && queried > 1 && queried <= 3 ); } class nMasterServerInfo: public nServerInfo { public: nMasterServerInfo() { Remove(); // the master server should not appear on any list // automatically load the master server config file tString f; std::ifstream s; tDirectories::Config().Open( s, "master.srv" ); Load(s); } }; static nMasterServerInfo* sn_DefaultMaster = NULL; nServerInfo *nServerInfo::GetDefaultMaster() { if (!sn_DefaultMaster) sn_DefaultMaster = tNEW(nMasterServerInfo); return sn_DefaultMaster; } class DeleteMaster { public: ~DeleteMaster() { if (sn_DefaultMaster) delete sn_DefaultMaster; sn_DefaultMaster = NULL; } }; static DeleteMaster sn_delm; nServerInfo::Compat nServerInfo::Compatibility() const { if ( sn_MyVersion().Min() > version_.Max() ) { return Compat_Downgrade; } if ( sn_MyVersion().Max() < version_.Min() ) { return Compat_Upgrade; } return Compat_Ok; } static nServerInfoAdmin* sn_serverInfoAdmin = NULL; nServerInfoAdmin::nServerInfoAdmin() { tASSERT( NULL == sn_serverInfoAdmin ); sn_serverInfoAdmin = this; } nServerInfoAdmin::~nServerInfoAdmin() { sn_serverInfoAdmin = NULL; } nServerInfoAdmin* nServerInfoAdmin::GetAdmin() { return sn_serverInfoAdmin; }