/* ************************************************************************* 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 "tInitExit.h" #include "nSimulatePing.h" #include "nConfig.h" #include "nNetwork.h" #include "tConsole.h" #include "tDirectories.h" #include "nNet.h" #include "tSysTime.h" #include #include #include #ifndef WIN32 #include #else #include #endif // debug watchs #ifdef DEBUG nMessage* sn_WatchMessage = NULL; #endif #define NO_ACK tString sn_bigBrotherString; tString sn_greeting[5]; //made 4 = 5 (lol i broke the laws of maths. subby), k's bug fix tString sn_programVersion= #include "../../version.h" ; tString sn_serverName ="Unnamed Server"; unsigned int sn_serverPort = 4534; unsigned int sn_clientPort = 4534; bool big_brother=true; static tConfItem sn_bb("BIG_BROTHER",big_brother); static tConfItemLine sn_sn("SERVER_NAME", sn_serverName); static tConfItem sn_sport("SERVER_PORT",reinterpret_cast(sn_serverPort)); static tConfItem sn_cport("CLIENT_PORT",reinterpret_cast(sn_clientPort)); int sn_defaultDelay=10000; int sn_maxRateIn=8; // maximum data rate in kb/s int sn_maxRateOut=8; // maximum output data rate in kb/s static nConnectError sn_Error = nOK; //tArray send_buffer[MAXCLIENTS+2]; //REAL planned_rate_control[MAXCLIENTS+2]; //REAL rate_control[MAXCLIENTS+2]; //unsigned short rate[MAXCLIENTS+2]; // from gGame.C //extern unsigned short client_gamestate[MAXCLIENTS+2]; bool deb_net=false; static REAL maxTimeout=1; // the maximal timeout in seconds static REAL minTimeout=.01; // the minimal timeout in seconds static REAL pingTimeout=2; // the normal timeout in multiples of the ping static REAL zeroTimeout=.01; // additional timeout #ifndef DEBUG static REAL killTimeout=30; #else static REAL killTimeout=30; #endif static const int kickOnDemandTimeout = 10; static bool send_again_warn=false; #ifdef DEBUG static int simulate_loss=0; #else //static int simulate_loss=0; #endif int sn_maxNoAck=100; // the maximum number of not ack messages // before more are send //int sn_ackPending[MAXCLIENTS+2]; // int sn_ackAckPending[MAXCLIENTS+2]; //static nMessage * ack_mess[MAXCLIENTS+2]; static nNetState current_state; //int sn_sockets[MAXCLIENTS+2]; // server mode: // elements 1...MAXCLIENTS are the incoming connections, // client mode: element 0 connects to the server. // element MAXCLIENTS+1: currently logging in nConnectionInfo sn_Connections[MAXCLIENTS+2]; static sockaddr peers[MAXCLIENTS+2]; // the same logic for the peer adresses. static int timeouts[MAXCLIENTS+2]; #define ACKBACK 1000 static unsigned short lastacks[MAXCLIENTS+2][ACKBACK]; static unsigned short lastackPos[MAXCLIENTS+2]; static unsigned short highest_ack[MAXCLIENTS+2]; //******************************************************** // Version control //******************************************************** static int sn_MaxBackwardsCompatibility = 1000; static tSettingItem sn_mxc("BACKWARD_COMPATIBILITY",sn_MaxBackwardsCompatibility); static int sn_newFeatureDelay = 0; static tSettingItem sn_nfd("NEW_FEATURE_DELAY",sn_newFeatureDelay); static const int sn_currentProtocolVersion = 4; // the current version number of the network protocol static const int sn_backwardCompatibleProtocolVersion = 0; // the smallest version of the network protocol this program is compatible with static const nVersion sn_myVersion( sn_backwardCompatibleProtocolVersion, sn_currentProtocolVersion); static nVersion sn_currentVersion( sn_myVersion ); const nVersion& sn_MyVersion() // the version this progam maximally supports { return sn_myVersion; } const nVersion& sn_CurrentVersion() // the version currently supported { return sn_currentVersion; } nVersion::nVersion() { min_=0; max_=0; } nVersion::nVersion( int min, int max ) { tASSERT( min <= max ); min_=min; max_=max; } bool nVersion::Supported( int version ) const // check if a particular version is supported { tASSERT( min_ <= max_ ); return version >= min_ && version <= max_; } bool nVersion::Merge( const nVersion& a, const nVersion& b) // merges two versions to one; the new version supports only features both versions understand. false is returned if no common denominator could be found { int min = a.min_; if ( min < b.min_ ) { min = b.min_; } int max = a.max_; if ( max > b.max_ ) { max = b.max_; } if ( min <= max ) { min_ = min; max_ = max; return true; } else { return false; } } bool nVersion::operator == ( const nVersion& other ) { return this->max_ == other.max_ && this->min_ == other.min_; } nVersion& nVersion::operator = ( const nVersion& other ) { this->min_ = other.min_; this->max_ = other.max_; return *this; } nMessage& operator >> ( nMessage& m, nVersion& ver ) { int min,max; m >> min; m >> max; ver = nVersion( min, max ); return m; } nMessage& operator << ( nMessage& m, const nVersion& ver ) { m << ver.Min(); m << ver.Max(); return m; } std::istream& operator >> ( std::istream& s, nVersion& ver ) { int min,max; s >> min; s >> max; ver = nVersion( min, max ); return s; } std::ostream& operator << ( std::ostream& s, const nVersion& ver ) { s << ver.Min() << " "; s << ver.Max(); return s; } nVersionFeature::nVersionFeature( int min, int max ) // creates a feature that is supported from version min to max; values of -1 indicate no border { tASSERT( min_ >= sn_MyVersion().Min() ); tASSERT( max < 0 || max <= sn_MyVersion().Max() ); min_ = min; max_ = max; } bool nVersionFeature::Supported() { return ( min_ < 0 || sn_CurrentVersion().Max() >= min_ ) && ( max_ < 0 || sn_CurrentVersion().Min() <= max_ ); } void handle_version_control( nMessage& m ) { if ( sn_GetNetState() == nCLIENT ) { m >> sn_currentVersion; } } nDescriptor versionControl(10, handle_version_control,"version" ); static void sn_UpdateCurrentVerion() { // update the current version from the native version and the versions of all attached clients // allow maximally sn_MaxBackwardsCompatibility old versions to connect int min = sn_myVersion.Max() - sn_MaxBackwardsCompatibility; if ( min < sn_myVersion.Min() ) min = sn_myVersion.Min(); // disable features that are too new int max = sn_myVersion.Max() - sn_newFeatureDelay; if ( max < min ) max = min; nVersion version( min, max ); nVersion maxVersion = version; if ( sn_GetNetState() == nCLIENT ) { sn_currentVersion = version; return; } for ( int i = MAXCLIENTS; i>0; --i ) { const nConnectionInfo& info = sn_Connections[i]; if ( info.socket > 0 ) { if ( ! version.Merge( version, info.version ) ) { // emergency fallback con << "Emergency version fallback: we should never have gotten here!"; version = maxVersion; } } } if ( version != sn_currentVersion ) { sn_currentVersion = version; nMessage* m = tNEW( nMessage )( versionControl ); (*m) << version; m->BroadCast(); } } //******************************************************** nConnectError sn_GetLastError() { nConnectError ret = sn_Error; sn_Error = nOK; return ret; } // REAL sn_ping[MAXCLIENTS+2]; static void reset_last_acks(int i){ for(int j=ACKBACK-1;j>=0;j--) lastacks[i][j]=0; lastackPos[i]=0; highest_ack[i]=0; } //#ifndef DEBUG int sn_maxClients=MAXCLIENTS; static tSettingItem< int > sn_maxClientsConf( "MAX_CLIENTS", sn_maxClients ); int sn_allowSameIPCountSoft=4; static tSettingItem< int > sn_allowSameIPCountSoftConf( "MAX_CLIENTS_SAME_IP_SOFT", sn_allowSameIPCountSoft ); int sn_allowSameIPCountHard=8; static tSettingItem< int > sn_allowSameIPCountHardConf( "MAX_CLIENTS_SAME_IP_HARD", sn_allowSameIPCountHard ); //#else //int maxclients=1; //#endif int sn_myNetID=0; // our network identification: 0: server // 1..MAXCLIENTS: client #define IDS_RESERVED 16 // number of message IDs reserved for special purposes: id 0 is reserved for no-ack messages. unsigned short current_id=1; // current running network number // the classes that are responsible for the queuing of network send tEvents: class planned_send:public tHeapElement{ protected: int peer; public: planned_send(REAL priority,int peer); ~planned_send(); virtual tHeapBase *Heap() const; // in wich heap are we? // change our priority: void add_to_priority(REAL diff); // what is to be done if the sceduled tEvent is executed? virtual void execute()=0; }; class nMessage_planned_send:public planned_send{ tCONTROLLED_PTR(nMessage) m; bool ack; public: nMessage_planned_send(nMessage *m,REAL priority,bool ack,int peer); ~nMessage_planned_send(); virtual void execute(); }; // ************************************************************* unsigned short nDescriptor::s_nextID(1); #define MAXDESCRIPTORS 400 static nDescriptor* descriptors[MAXDESCRIPTORS]; static nDescriptor* nDescriptor_anchor; nDescriptor::nDescriptor(unsigned short identification, nHandler *handle,const char *Name, bool awl) :tListItem(nDescriptor_anchor), id(identification),handler(handle),name(Name), acceptWithoutLogin(awl) { #ifdef DEBUG #ifndef WIN32 // con << "Descriptor " << id << ": " << name << '\n'; #endif #endif if (MAXDESCRIPTORS<=id || descriptors[id]!=NULL){ con << "Descriptor " << id << " already used!\n"; exit(-1); } s_nextID=id+1; descriptors[id]=this; } /* nDescriptor::nDescriptor(nHandler *handle,const char *Name) :id(s_nextID++),handler(handle),name(Name) { #ifdef DEBUG con << "Descriptor " << id << ": " << name << '\n'; #endif if (descriptors.Len()>id && descriptors[id]!=NULL){ con << "Descriptor " << id << " already used!\n"; exit(-1); } descriptors[id]=this; } */ int nCurrentSenderID::currentSenderID_ = 0; void nDescriptor::HandleMessage(nMessage &message){ static tArray warned; // store sender ID for console nCurrentSenderID currentSender( message.SenderID() ); #ifdef DEBUG_X if (message.descriptor>1) con << "RMT " << message.descriptor << "\n"; #endif #ifndef NOEXCEPT try{ #endif nDescriptor *nd = 0; // z-man: security check ( thanks, Luigi Auriemma! ) if ( message.descriptor < MAXDESCRIPTORS ) nd=descriptors[message.descriptor]; if (nd){ if ((message.SenderID() <= MAXCLIENTS) || nd->acceptWithoutLogin) nd->handler(message); } else if (!warned[message.Descriptor()]){ tOutput warn; warn.SetTemplateParameter(1, message.Descriptor()); warn << "$network_warn_unknowndescriptor"; con << warn; warned[message.Descriptor()]=true; } #ifndef NOEXCEPT } catch(nKillHim){ // st_Breakpoint(); con << tOutput("$network_error"); sn_KillUser(message.SenderID(), "$network_kill_error" ); } #endif } // ************************************************************* void ack_handler(nMessage &m){ while (!m.End()){ unsigned short ack; m.Read(ack); //con << "Got ack:" << ack << ":" << m.SenderID() << '\n'; nWaitForAck::Ackt(ack,m.SenderID()); } } static nDescriptor s_Acknowledge(1,ack_handler,"ack"); class nWaitForAck; static tList sn_pendingAcks; //static eTimer netTimer; static nTimeRolling netTime; #ifdef NET_DEBUG static int acks=0; static int max_acks=0; #endif nWaitForAck::nWaitForAck(nMessage* m,int rec) :id(-1),message(m),receiver(rec){ if (!message) tERR_ERROR("Null ack!"); if (message->Descriptor()!=s_Acknowledge.ID()) sn_Connections[receiver].ackPending++; else tASSERT(false); // sn_ackAckPending[receiver]++; #ifdef NET_DEBUG acks++; #endif timeFirstSent=::netTime; timeLastSent=::netTime; timeouts=0; REAL timeout=sn_Connections[rec].ping*pingTimeout; if (timeoutmaxTimeout) timeout=maxTimeout; #ifdef nSIMULATE_PING timeSendAgain=::netTime + nSIMULATE_PING; #ifndef WIN32 timeSendAgain+=(nSIMULATE_PING_VARIANT*random())/RAND_MAX; #endif #else timeSendAgain=::netTime + timeout+zeroTimeout; #endif sn_pendingAcks.Add(this,id); } nWaitForAck::~nWaitForAck(){ #ifdef NET_DEBUG acks--; if (acks>max_acks){ max_acks=acks; // con << "MA=" << max_acks << '\n'; } #endif if (bool(message) && message->Descriptor()!=s_Acknowledge.ID()) sn_Connections[receiver].ackPending--; else tASSERT(false); // sn_ackAckPending[receiver]--; sn_pendingAcks.Remove(this,id); tCHECK_DEST; } void nWaitForAck::Ackt(unsigned short id,unsigned short peer){ int success=0; for(int i=sn_pendingAcks.Len()-1;i>=0;i--){ if (sn_pendingAcks(i)->message->messageID==id && sn_pendingAcks(i)->receiver==peer){ success=1; #ifdef DEBUG // if (sn_pendingAcks(i)->message == sn_WatchMessage) // st_Breakpoint(); #endif #ifdef DEBUG_X if (sn_pendingAcks(i)->message->descriptor>1) con << "AT " << sn_pendingAcks(i)->message->descriptor << '\n'; #endif // calculate ping #define PING_AVG 6.0 REAL ping_avg=PING_AVG; REAL thisping=netTime-sn_pendingAcks(i)->timeFirstSent; /* if (sn_pendingAcks(i]->timeouts) // if we had packet loss, ping_avg*=3+sn_pendingAcks(i]->timeouts; // be a little less strict if (thispingsn_Connections[peer].ping*2.5) ping_avg*=100; /* if (netTime-sn_pendingAcks(i]->timeouts) ping_avg*=100; */ sn_Connections[peer].ping+=thisping/ping_avg; // make some average sn_Connections[peer].ping/=(1+1/ping_avg); sn_pendingAcks(i)->AckExtraAction(); delete sn_pendingAcks(i); ::timeouts[peer]=0; if (i=0;i--){ con << i << "\t:" << sn_pendingAcks(i]->message->messageID << ":" << sn_pendingAcks(i]->receiver << '\n'; } } */ #endif } void nWaitForAck::AckAllPeer(unsigned short peer){ for(int i=sn_pendingAcks.Len()-1;i>=0;i--){ if (sn_pendingAcks(i)->receiver==peer){ delete sn_pendingAcks(i); if (i=0;i--){ nWaitForAck* pendingAck = sn_pendingAcks(i); if (pendingAck->timeSendAgain<=netTime){ //con << net_ticks-sn_pendingAcks[i]->ticks_first_sent << '\n'; // update timeout counters ::timeouts[pendingAck->receiver]++; pendingAck->timeouts++; if(netTime - pendingAck->timeFirstSent > killTimeout && ::timeouts[pendingAck->receiver] > 20){ // total timeout. Kill connection. if (pendingAck->receiver<=MAXCLIENTS){ tOutput o; o.SetTemplateParameter(1, pendingAck->receiver); o << "$network_error_timeout"; con << o; sn_KillUser(pendingAck->receiver, "$network_kill_timeout" ); sn_Error = nTIMEOUT; if (i>=sn_pendingAcks.Len()) i=sn_pendingAcks.Len()-1; } else // it is just in the login slot. Ignore it. delete pendingAck; } else{ #ifdef DEBUG //if (pendingAck->message == sn_WatchMessage) //st_Breakpoint(); #endif if (sn_Connections[pendingAck->receiver].socket > 0){ // if(sn_Connections[].rateControlPlanned[pendingAck->receiver]>-1000) { REAL timeout=sn_Connections[pendingAck->receiver].ping*pingTimeout; if (timeoutmaxTimeout) timeout=maxTimeout; timeout *= pendingAck->timeouts; pendingAck->timeSendAgain=netTime+timeout; pendingAck->timeLastSent=netTime; if (send_again_warn){ con << "sending packet again: " ; deb_net=true; } pendingAck->message->SendImmediately (pendingAck->receiver,false); deb_net=false; } } else delete pendingAck; } } } } // defined in netobjec.C void ClearKnows(int user); #ifdef NET_DEBUG static int nMessages=0; static int max_nMessages=0; #endif nMessage::nMessage(unsigned short*& buffer,short sn_myNetID, int lenLeft ) :descriptor(ntohs(*(buffer++))),messageID(ntohs(*(buffer++))), senderID(sn_myNetID),readOut(0){ #ifdef NET_DEBUG nMessages++; #endif unsigned short len=ntohs(*(buffer++)); lenLeft--; if ( len > lenLeft ) { len = lenLeft; #ifndef NOEXCEPT throw nKillHim(); #endif } for(int i=0;imax_nMessages){ max_nMessages=nMessages; con << "MN=" << max_nMessages <<'\n'; } #endif #ifdef DEBUG_X if (descriptor>1) con << "DMT " << descriptor << "\n"; #endif tCHECK_DEST; } void nMessage::BroadCast(bool ack){ tControlledPTR< nMessage > keep( this ); if (sn_GetNetState()==nCLIENT) Send(0,ack); if (sn_GetNetState()==nSERVER){ for(int i=MAXCLIENTS;i>0;i--){ if (sn_Connections[i].socket>0) Send(i,ack); } } } static nVersionFeature sn_ZeroMessageCrashfix( 1 ); nMessage& nMessage::operator << (const tString &s){ if ( !sn_ZeroMessageCrashfix.Supported() && s.Len() <= 0 ) { return this->operator<<( s + " " ); } unsigned short len=s.Len(); Write(len); for(int i=0;i> (tString &s){ s.Clear(); unsigned short w,len; Read(len); if ( len > 0 ) { s[len-1] = 0; for(int i=0;i> 8; } } return *this; } #define MANT 26 #define EXP (32-MANT) #define MS (MANT-1) typedef struct{ int mant:MANT; unsigned int exp:EXP; } myfloat; nMessage& nMessage::operator<<(const REAL &x){ #ifdef DEBUG // con << "write x= " << x; if(sizeof(myfloat)!=sizeof(int)) tERR_ERROR_INT("floating ePoint format does not work!"); #endif /* REAL nachkomma=x-floor(x); Write(short(x-nachkomma)); Write(60000*nachkomma); */ // no fuss. Read and write floats in binary format. // will likely cause problems for systems other than i386. //Write(((short *)&x)[0]); //Write(((short *)&x)[1]); // right. Caused severe problems with the AIX port. // new way: own floating ePoint format that is not good with small numbers // (we do not need them anyway) REAL y=x; unsigned int negative=0; if (y<0){ y=-y; negative=1; } unsigned int exp=0; while ( fabs(y)>=64 && exp < (1<=1 && exp < (1<((1<(1<0) mant=(1<(trans)); #ifdef DEBUG /* con << "mant: " << mant << ", exp: " << exp << ", negative: " << negative; */ unsigned int mant2=trans & ((1 << MS)-1); unsigned int negative2=(trans >> MS) & 1; unsigned int nt=trans-mant-(negative << MS); unsigned int exp2=nt >> MANT; if (mant2!=mant || negative2!=negative || exp2!=exp) tERR_ERROR_INT("Floating ePoint tranfer failure!"); /* con << ", x: " << x; con << ", mant: " << mant << ", exp: " << exp << ", negative: " << negative; */ // check: REAL z=REAL(mant)/(1<=6){ exp-=6; z*=64; } while (exp>0){ exp--; z*=2; } if (fabs(z-x)>(fabs(x)+1)*.001) tERR_ERROR_INT("Floating ePoint tranfer failure!"); //con << ", z: " << z << '\n'; #endif return *this; } nMessage& nMessage::operator>>(REAL &x){ /* short vorkomma; unsigned short nachkomma; Read((unsigned short &)vorkomma); Read(nachkomma); x=vorkomma+nachkomma/60000.0; Read(((unsigned short *)&x)[0]); Read(((unsigned short *)&x)[1]); */ unsigned int trans; operator>>(reinterpret_cast(trans)); int mant=trans & ((1 << MS)-1); unsigned int negative=(trans >> MS) & 1; unsigned int exp=(trans-mant-(negative << MS)) >> MANT; x=REAL(mant)/(1<=6){ exp-=6; x*=64; } while (exp>0){ exp--; x*=2; } #ifdef DEBUG #ifndef WIN32 if (!finite(x)) st_Breakpoint(); // con << " , x= " << x << '\n'; #endif #endif return *this; } nMessage& nMessage::operator<< (const short &x){ Write((reinterpret_cast(&x))[0]); return *this; } nMessage& nMessage::operator>> (short &x){ Read(reinterpret_cast(&x)[0]); return *this; } nMessage& nMessage::operator<< (const int &x){ unsigned short a=x & (0xFFFF); short b=(x-a) >> 16; Write(a); operator<<(b); return *this; } nMessage& nMessage::operator>> (int &x){ unsigned short a; short b; Read(a); operator>>(b); x=(b << 16)+a; return *this; } nMessage& nMessage::operator<< (const bool &x){ if (x) Write(1); else Write(0); return *this; } nMessage& nMessage::operator>> (bool &x){ unsigned short y; Read(y); x= (y!=0); return *this; } void nMessage::Read(unsigned short &x){ if (End()){ tOutput o; st_Breakpoint(); o.SetTemplateParameter(1, senderID); o << "$network_error_shortmessage"; con << o; sn_KillUser(senderID, "$network_kill_error"); nReadError(); } else x=data(readOut++); } // ********************************************** // Basic communication classes: login // ********************************************** static bool login_failed=false; static bool login_succeeded=false; static nHandler *real_req_info_handler=NULL; void req_info_handler(nMessage &m){ if (real_req_info_handler) (*real_req_info_handler)(m); if (m.SenderID()==MAXCLIENTS+1) sn_KillUser(MAXCLIENTS+1, "$network_kill_logout"); } static nDescriptor req_info(2,req_info_handler,"req_info"); void RequestInfoHandler(nHandler *handle){ real_req_info_handler=handle; } void login_deny_handler(nMessage &m){ if ( !m.End() ) { // tOutput output; // m >> output; // sn_DenyReason = output; m >> sn_DenyReason; } else { sn_DenyReason = tOutput( "$network_kill_unknown" ); } if (!login_failed) con << tOutput("$network_login_denial"); if (sn_GetNetState()!=nSERVER){ login_failed=true; login_succeeded=false; sn_SetNetState(nSTANDALONE); } } static nDescriptor login_deny(3,login_deny_handler,"login_deny"); void login_handler_1( nMessage&m ); void login_handler_2( nMessage&m ); void logout_handler( nMessage&m ); nDescriptor login(6,login_handler_1,"login1", true); nDescriptor login_2(11,login_handler_2,"login2", true); nDescriptor logout(7,logout_handler,"logout"); tString sn_DenyReason; void login_ignore_handler(nMessage &m){ if (sn_GetNetState()!=nSERVER && !login_succeeded){ /* login_failed=true; login_succeeded=false; // kicking the one who uses our place // (he is probably timing out anyway..) nMessage *lo=new nMessage(logout); lo->Write((unsigned short)sn_myNetID); lo->Send(0); sn_Sync(10); (new nMessage(login))->Send(0); */ } } static nDescriptor login_ignore(4,login_ignore_handler,"login_ignore"); void first_fill_ids(); void login_accept_handler(nMessage &m){ if (sn_GetNetState()!=nSERVER){ login_succeeded=true; unsigned short id=0; m.Read(id); sn_myNetID=id; first_fill_ids(); } } static nDescriptor login_accept(5,login_accept_handler,"login_accept", true); //static short new_id=0; // counts the number of logins with the same IP int CountSameIP( int user, bool reset=false ) { static int sameIP[ MAXCLIENTS+2 ]; tASSERT( user >= 0 && user <= MAXCLIENTS+1 ); if ( reset ) { int count = 0; for(int user2=1;user2<=sn_maxClients;++user2) { if(sn_Connections[user].socket<=0) continue; if ( user2 != user && ANET_AddrCompare( &peers[user], &peers[user2] ) >=0 ) { count++; } } sameIP[user] = count; } return sameIP[user]; } // counts the number of logins with the same Connection int CountSameConnection( int user ) { int count = 0; for(int user2=1;user2<=sn_maxClients;++user2) { if(sn_Connections[user].socket<=0) continue; if ( user2 != user && ANET_AddrCompare( &peers[user], &peers[user2] ) == 0 ) { count++; } } return count; } // determine a free connection slot or at least one where the user won't be missed int GetFreeSlot() { int user; // level 1: look for free slot for(user=1;user<=sn_maxClients;++user) { // look for empty slot if(sn_Connections[user].socket<=0) { return user; } } int best = -1; int bestTimeout = kickOnDemandTimeout; // level 2: look for slot timing out anyway for(user=1;user<=sn_maxClients;++user) { int timeout = timeouts[user]; if ( timeout > bestTimeout ) { bestTimeout = timeout; best = user; } } if ( best > 0 ) return best; int bestCount = sn_allowSameIPCountSoft-1; // level 3: look for dublicate IPs for(user=1;user<=sn_maxClients;++user) { int count = CountSameIP( user ); if ( count > bestCount ) { bestCount = count; best = user; } } if ( best > 0 ) return best; return -1; } int login_handler(const nMessage &m, const nVersion& version, unsigned short rate ){ nCurrentSenderID senderID; // ignore multiple logins if( CountSameConnection( m.SenderID() ) > 0 ) return -1; bool success=false; int new_id = -1; // test // sn_KillUser(m.SenderID(), "$network_kill_incompatible"); // return -1; nVersion mergedVersion; if ( !mergedVersion.Merge( version, sn_CurrentVersion() ) ) { sn_KillUser(m.SenderID(), "$network_kill_incompatible"); } if (m.SenderID()!=MAXCLIENTS+1) { //con << "Ignoring second login from " << m.SenderID() << ".\n"; (new nMessage(login_ignore))->Send(m.SenderID()); } else if (sn_Connections[m.SenderID()].socket>0) { if ( sn_maxClients > MAXCLIENTS ) sn_maxClients = MAXCLIENTS; // count doublicate IPs if ( CountSameIP( m.SenderID(), true ) < sn_allowSameIPCountHard ) { // find new free ( or freeable ) ID new_id = GetFreeSlot(); if ( new_id > 0 ) { if(sn_Connections[new_id].socket>0) sn_KillUser( new_id, "$network_kill_full" ); success = true; senderID.SetID( new_id ); sn_Connections [ new_id ].socket = sn_Connections[MAXCLIENTS+1].socket; // the new connection has number MC+1 peers [ new_id ] = peers[MAXCLIENTS+1]; timeouts [ new_id ] = kickOnDemandTimeout/2; sn_Connections [ MAXCLIENTS+1 ].socket = -1; peers [ MAXCLIENTS+1 ].sa_family = 0; // nCallbackLoginLogout::UserLoggedIn(i); // recount doublicate IPs CountSameIP( new_id, true ); } } // log login to console tOutput o; o.SetTemplateParameter(1, ANET_AddrToString(&peers[m.SenderID()])); o << "$network_server_login"; con << o; } if (success) { tOutput o; o.SetTemplateParameter(1, new_id); o << "$network_server_login_success"; con << o; // tString s; // s << "User " << new_id << " logged in.\n"; nCallbackLoginLogout::UserLoggedIn(new_id); sn_Connections[new_id].ping=1; sn_Connections[new_id].bandwidthControl_.Reset(); reset_last_acks(new_id); if (rate>sn_maxRateOut) rate=sn_maxRateOut; sn_Connections[new_id].bandwidthControl_.SetRate( rate ); sn_Connections[new_id].version = version; nWaitForAck::AckAllPeer(MAXCLIENTS+1); reset_last_acks(MAXCLIENTS+1); if (sn_Connections[MAXCLIENTS+1].ackMess) { sn_Connections[MAXCLIENTS+1].ackMess=NULL; } nMessage *rep=new nMessage(login_accept); rep->Write(new_id); rep->Send(new_id); nConfItemBase::s_SendConfig(true, new_id); // ANET_Listen(false); // ANET_Listen(true); } else if (m.SenderID()==MAXCLIENTS+1) { sn_KillUser(MAXCLIENTS+1, "$network_kill_full"); } sn_UpdateCurrentVerion(); return new_id; } void login_handler_1(nMessage& m) { nVersion version; unsigned short rate; m.Read( rate ); int new_id = login_handler( m, version, rate ); if ( !m.End() && new_id > 0 ){ // we get a big brother message tString rem_bb; m >> rem_bb; // ignore it, it is worthless. // std::ofstream s; // if ( tDirectories::Var().Open(s, "big_brother",std::ios::app) ) // s << rem_bb << '\n'; } } void login_handler_2(nMessage& m) { unsigned short rate; unsigned short bb; m.Read( rate ); m.Read( bb ); tString rem_bb; if ( bb ) { // we get a big brother message m >> rem_bb; } nVersion ver; m >> ver; int new_ID = login_handler( m, ver , rate ); if ( new_ID > 0 ) { nMessage* m = tNEW( nMessage )( versionControl ); (*m) << sn_currentVersion; m->Send( new_ID ); if ( bb ) { std::ofstream s; if ( tDirectories::Var().Open(s, "big_brother",std::ios::app) ) s << rem_bb << '\n'; } } } void logout_handler(nMessage &m){ unsigned short id = m.SenderID(); //m.Read(id); if (sn_Connections[id].socket>0) { tOutput o; o.SetTemplateParameter(1, id); o << "$network_logout_server"; con << o; } nWaitForAck::AckAllPeer(id); if (0Use( nBandwidthControl::Usage_Planning, message.DataLen() * 2 ); } } // send the contents of the buffer to a specific socket void nSendBuffer::Send ( int socket , const sockaddr& peer ,nBandwidthControl* control ) { if (sendBuffer_.Len()){ sn_SentPackets++; sn_SentBytes += sendBuffer_.Len() * 2 + OVERHEAD; // store our id sendBuffer_[sendBuffer_.Len()]=htons(::sn_myNetID); ANET_Write(socket,reinterpret_cast(&(sendBuffer_[0])), 2*sendBuffer_.Len(),&peer); this->Clear(); if ( control ) { control->Use( nBandwidthControl::Usage_Execution, 2*sendBuffer_.Len() + OVERHEAD ); } } } // broadcast the contents of the buffer void nSendBuffer::Broadcast ( int socket , int port , nBandwidthControl* control ) { if (sendBuffer_.Len()){ sn_SentPackets++; sn_SentBytes += sendBuffer_.Len() * 2 + OVERHEAD; // store our id sendBuffer_[sendBuffer_.Len()]=htons(::sn_myNetID); ANET_Broadcast( socket,reinterpret_cast(&(sendBuffer_[0])), 2*sendBuffer_.Len(), port); Clear(); if ( control ) { control->Use( nBandwidthControl::Usage_Execution, 2*sendBuffer_.Len() + OVERHEAD ); } } } // clears the buffer void nSendBuffer::Clear() { for(int i=sendBuffer_.Len()-1;i>=0;i--) sendBuffer_(i)=0; sendBuffer_.SetLen( 0 ); } nBandwidthControl::nBandwidthControl( nBandwidthControl* parent ) { #ifdef DEBUG if ( parent ) parent->numChildren_ ++; numChildren_ = 0; #endif parent_ = parent; Reset(); } nBandwidthControl::~nBandwidthControl() { #ifdef DEBUG if ( parent_ ) parent_->numChildren_ --; tASSERT( numChildren_ == 0 ); #endif } void nBandwidthControl::Reset() { rateControlPlanned_ = rateControl_ = 20.0f; rate_ = 8; } void nBandwidthControl::Update( REAL ts) { rateControl_ += ( rate_ * 1000 ) * ts; if ( rateControl_ > 1000.0f ) { rateControl_ = 1000.0f; } rateControlPlanned_ = rateControl_; } void nMessage::SendCollected(int peer) { if (peer<0 || peer > MAXCLIENTS+1) tERR_ERROR("Invalid peer!"); sn_Connections[peer].sendBuffer_.Send( sn_Connections[peer].socket, peers[peer], &sn_Connections[peer].bandwidthControl_ ); } void nMessage::BroadcastCollected(int peer, unsigned int port){ if (peer<0 || peer > MAXCLIENTS+1) tERR_ERROR("Invalid peer!"); sn_Connections[peer].sendBuffer_.Broadcast( sn_Connections[peer].socket, port, &sn_Connections[peer].bandwidthControl_ ); } // TODO_NOACK void nMessage::SendImmediately(int peer,bool ack){ if (ack) { #ifdef NO_ACK tASSERT(messageID); #endif new nWaitForAck(this,peer); } #ifdef DEBUG /* if (descriptor>1) con << "SMT " << descriptor << "\n"; */ /* #ifdef DEBUG if (sn_Connections[].rate_control[peer]<-2000) tERR_ERROR("Heavy network overflow."); #endif */ // if (peer==0 && sn_GetNetState()==nSERVER) // tERR_ERROR("talking to yourself, eh?"); if (peer==MAXCLIENTS+1){ #ifdef DEBUG if(descriptor==login_deny.id) con << "Sending login_deny to login slot.\n"; else if(descriptor==s_Acknowledge.id) con << "Sending ack to login slot.\n"; #endif // else if (descriptor // tERR_ERROR("the last user only should receive denials or acks."); } #endif if (sn_Connections[peer].sendBuffer_.Len()+data.Len()+3 > MAX_MESS_LEN/2){ SendCollected(peer); //con << "Overflow packets sent to " << peer << '\n'; } if (sn_Connections[peer].socket>0) { sn_Connections[peer].sendBuffer_.AddMessage( *this, &sn_Connections[peer].bandwidthControl_ ); /* if (sn_Connections[].rate_control[peer]>0) send_collected(peer); unsigned short *b=new (unsigned short)[data.Len()+3]; b[0]=htons(descriptor); b[1]=htons(messageID); b[2]=htons(data.Len()); int len=data.Len(); for(int i=0;i bounce( this ); // delete this message if nobody is interested in it any more } REAL sent_per_messid[MAXDESCRIPTORS+100]; void nMessage::Send(int peer,REAL priority,bool ack){ #ifdef NO_ACK if (!ack) messageID = 0; #endif #ifdef DEBUG_X if (descriptor>1) con << "PMT " << descriptor << "\n"; #endif sn_Connections[peer].bandwidthControl_.Use( nBandwidthControl::Usage_Planning, 2*(data.Len()+3) ); sent_per_messid[descriptor]+=2*(data.Len()+3); tASSERT(Descriptor()!=s_Acknowledge.ID() || !ack); new nMessage_planned_send(this,priority+sn_OrderPriority,ack,peer); sn_OrderPriority += .0001; // to roughly keep the relative order of netmessages } // receive and s_Acknowledge the recently reveived network messages static void rec_peer(unsigned int peer){ // temporary storage for received messages static tArray< tJUST_CONTROLLED_PTR< nMessage > > receivedMessages; #define maxrec 2000 unsigned short buff[maxrec]; if (sn_Connections[peer].socket>0){ int count=0; int len=1; while (len>=0 && sn_Connections[peer].socket>0) { len=ANET_Read(sn_Connections[peer].socket,reinterpret_cast(buff),maxrec*2,&peers[peer]); unsigned short *b=buff; unsigned short *bend=buff+(len/2-1); if (len>0){ sn_ReceivedPackets++; sn_ReceivedBytes += len + OVERHEAD; unsigned short claim_id=ntohs(*bend); // z-man: security check ( thanks, Luigi Auriemma! ) if ( claim_id > MAXCLIENTS+1 ) continue; // drop packet /* std::cerr << "Received " << len << " bytes"; con << " from user " << claim_id << '\n'; */ count ++; unsigned int id=peer; // for(unsigned int i=1;i<=(unsigned int)maxclients;i++) int comp=ANET_AddrCompare(&peers[peer],&peers[claim_id]); if ( comp == 0 || claim_id == MAXCLIENTS+1 ) { // everything seems allright. accept the id. id = claim_id; } else if (-1 != comp ) { id=claim_id; if ( peer != MAXCLIENTS+1 ) { if (ANET_GetSocketPort(&peers[claim_id]) != ANET_GetSocketPort(&peers[peer])) { con << "changing port of user " << id << " from " << ANET_GetSocketPort(&peers[claim_id]) << " to " << ANET_GetSocketPort(&peers[peer]) << ".\n"; peers[claim_id]=peers[peer]; } } } else // look for a match { // packets from unknown senders are treated as login packets id = MAXCLIENTS+1; for(int i=MAXCLIENTS;i > 0;i--) { if (ANET_AddrCompare(&peers[peer],&peers[i])!=-1 && ANET_GetSocketPort(&peers[peer]) == ANET_GetSocketPort(&peers[i])) id=i; } if (id != MAXCLIENTS+1) { static int warn = 0; static int nextwarn = 1; if ( ++warn >= nextwarn ) { nextwarn <<= 1; con << comp << ":Peer claims to be user " << claim_id << " (" << tString(ANET_AddrToString(&peers[claim_id])) << "), but is user " << id << " (" << tString(ANET_AddrToString(&peers[id])) << ").\n"; } } } // if (peer!=id) // con << "Changed incoming address.\n"; int lenleft = bend - b; #ifndef NOEXCEPT try { #endif while( lenleft > 0 ){ tJUST_CONTROLLED_PTR< nMessage > pmess; pmess = tNEW( nMessage )(b,id,lenleft); nMessage& mess = *pmess; lenleft = bend - b; bool mess_is_new=true; // see if we have got this packet before unsigned short mess_id=mess.MessageID(); #ifdef DEBUG if ( (simulate_loss && rand()%simulate_loss==0)){ // simulate packet loss con << "Losing packet " << mess_id << ":" << id << ".\n"; }else #endif if(// (id==MAXCLIENTS+1 && !nCallbackAcceptPackedWithoutConnection::Accept( mess ) ) || // do not accept normal packages from the login // slot; just login and information packets are allowed. ( sn_GetNetState() != nSERVER && !login_succeeded && !nCallbackAcceptPackedWithoutConnection::Accept( mess ) ) // if we are not yet logged in, accept only login and login_deny. ) { // con << "Ignoring packet " << mess_id << ":" << id << ".\n"; } else { if (id <= MAXCLIENTS && mess_id != 0) // messages with ID 0 are non-ack messages and come really often. they are always new. { unsigned short diff=mess_id-highest_ack[id]; if (diff>0 && diff<10000 || (( mess.Descriptor() == login_accept.ID() || mess.Descriptor() == login_deny.ID() || mess.Descriptor() == login.ID() ) && highest_ack[id] == 0) ){ // the message has a more recent id than anything before. // it is surely new. highest_ack[id]=mess_id; } else{ // do a better check for(int i=ACKBACK-1;i>=0;i--) if (mess_id==lastacks[id][i]) mess_is_new=false; } } // acknowledge the message, even if it was old (the sender // then thinks it got lost the first time) // special situation: logout. Do not sent ack any more. if ((sn_Connections[id].socket<0)) { sn_Connections[id].ackMess=NULL; } else if ( #ifdef NO_ACK (mess.MessageID()) && #endif (mess.Descriptor()!=login_ignore.ID() || login_succeeded)){ // do not ack the login_ignore packet that did not let you in. if(sn_Connections[id].ackMess==NULL) sn_Connections[id].ackMess=new nMessage(s_Acknowledge); sn_Connections[id].ackMess->Write(mess.MessageID()); if (sn_Connections[id].ackMess->DataLen()>100){ sn_Connections[id].ackMess->Send(id, 0, false); sn_Connections[id].ackMess=NULL; } } if (mess_is_new){ // mark the message as old if (mess_id > 0) { lastacks[id][lastackPos[id]]=mess_id; if(++lastackPos[id]>=ACKBACK) lastackPos[id]=0; } /* // special situation: login. Change peer number permanently if (peer==MAXCLIENTS+1 && new_id>0){ id=peer=new_id; } */ if (sn_GetNetState() != nSTANDALONE) { // store the message for later processing receivedMessages[ receivedMessages.Len() ] = pmess; } } //else //con << "Message " << mess_id << ":" << id << " was not new.\n"; } } #ifndef NOEXCEPT } catch(nKillHim) { con << "nKillHim signal caught.\n"; sn_KillUser(peer, "$network_kill_error"); } #endif } #ifndef NOEXCEPT try { #endif static int recursionCount = 0; ++recursionCount; // handle messages while ( receivedMessages.Len() > 0 ) { int index = receivedMessages.Len()-1; tJUST_CONTROLLED_PTR< nMessage > mess = receivedMessages( index ); receivedMessages( index ) = NULL; receivedMessages.SetLen( index ); nDescriptor::HandleMessage( *mess ); } if ( --recursionCount <= 0 ) { nCallbackReveivedComplete::ReceivedComplete(); } #ifndef NOEXCEPT } catch(nKillHim) { con << "nKillHim signal caught.\n"; sn_KillUser(peer, "$network_kill_error"); } #endif } } } void sn_Receive(){ /* static bool reentry=false; if (reentry) return; reentry=true; */ netTime=tSysTimeFloat(); // new_id=0; sn_Connections[MAXCLIENTS+1].ping=1; // create the ack messages int i; for(i=0;i<=MAXCLIENTS+1;i++) if(sn_Connections[i].ackMess==NULL) sn_Connections[i].ackMess=new nMessage(s_Acknowledge); switch (current_state){ case nSERVER:{ peers[MAXCLIENTS+1].sa_family=0; for(int i=13;i>=0;i--) peers[MAXCLIENTS+1].sa_data[i]=0; if((sn_Connections[MAXCLIENTS+1].socket = ANET_CheckNewConnections())>0) rec_peer(MAXCLIENTS+1); } // break; case nCLIENT: rec_peer(0); break; case nSTANDALONE: default: break; } nWaitForAck::Resend(); sn_SendPlanned(); // send the acks for(i=0;i<=MAXCLIENTS+1;i++) if(sn_Connections[i].socket>0 && sn_Connections[i].ackMess && !sn_Connections[i].ackMess->End() // && sn_ackAckPending[i] <= 1+sn_Connections[].ackMess[i]->DataLen() && sn_Connections[i].bandwidthControl_.CanSend() ){ sn_Connections[i].ackMess->Send(i,0, false); sn_Connections[i].ackMess=NULL; } //reentry=false; } nNetState sn_GetNetState(){ return current_state; } void clear_owners(); void sn_SetNetState(nNetState x){ static bool reentry=false; if(!reentry && x!=current_state){ sn_UpdateCurrentVerion(); if (x == nSERVER) net_hostport = sn_serverPort; reentry=true; if (x!=nSTANDALONE) { if (x==nCLIENT) { for(int i=MAXCLIENTS+1;i>0;i--) { if(sn_Connections[i].socket>0) { sn_KillUser(i, "$network_kill_shutdown"); } } nCallbackLoginLogout::UserLoggedOut(0); } else sn_myNetID=0; if (sn_Connections[0].socket<=0) sn_Connections[0].socket=ANET_Init(); ANET_Listen(x==nSERVER); if (x == nSERVER) { // try some other ports while (net_acceptsocket < 0 && net_hostport < sn_serverPort + 100) { if (net_hostport >= 5000 ) con << tOutput("$network_toomanyservers"); net_hostport++; ANET_Listen(true); } } } else { clear_owners(); for(int i=MAXCLIENTS+1;i>=0;i--){ if(sn_Connections[i].socket>0){ if (i==0 && current_state!=nSERVER) { // logout con << tOutput("$network_logout_process"); for(int j=3;j>=0;j--){ // just to make sure nMessage *lo=new nMessage(logout); lo->Write(static_cast(sn_myNetID)); lo->SendImmediately(0, false); sn_Receive(); usleep(1000); } // wait for logout return /* Naaa. you have to stop making checks somewhere. double timeout=tSysTimeFloat()+5; while(sn_Connections[].socket[0]>0 && login_succeeded && tSysTimeFloat() mess; if ( login2 ) { mess=new nMessage(login_2); mess->Write(sn_maxRateIn); unsigned short bb = big_brother; mess->Write( bb ); if ( bb ){ (*mess) << sn_bigBrotherString; big_brother=false; } (*mess) << sn_MyVersion(); } else { mess=new nMessage(login); mess->Write(sn_maxRateIn); if (big_brother){ (*mess) << sn_bigBrotherString; big_brother=false; } } mess->Send(0); con << tOutput("$network_login_process"); login_failed=false; login_succeeded=false; nTimeRolling timeout=tSysTimeFloat()+5; while(sn_GetNetState()==nCLIENT && tSysTimeFloat()=timeout || sn_GetNetState()!=nCLIENT){ if ( login2 ) { return sn_Connect( server, false ); } else { con << tOutput("$network_login_failed_timeout"); sn_SetNetState(nSTANDALONE); return nTIMEOUT; } } else{ nCallbackLoginLogout::UserLoggedIn(0); tOutput mess; mess.SetTemplateParameter(1, sn_myNetID); mess << "$network_login_success"; con << mess; con << tOutput("$network_login_sync"); sn_Sync(40); con << tOutput("$network_login_relabeling"); con << tOutput("$network_login_sync2"); sn_Sync(40,true); con << tOutput("$network_login_done"); return nOK; } } void nReadError() { // st_Breakpoint(); #ifndef NOEXCEPT throw nKillHim(); #else con << "\nI told you not to use PGCC! Now we need to leave the\n" << "system in an undefined state. The progam will crash now.\n" << "\n\n"; #endif } #ifdef DEDICATED static short sn_decorateID = true; static tConfItem< short > sn_decorateIDConf( "CONSOLE_DECORATE_ID", sn_decorateID ); static short sn_decorateIP = false; static tConfItem< short > sn_decorateIPConf( "CONSOLE_DECORATE_IP", sn_decorateIP ); // console with filter for better machine readable log format class nConsoleFilter:public tConsoleFilter{ private: virtual void DoFilterLine( tString &line ) { if ( sn_decorateID ) { tString orig = line; int id = nCurrentSenderID::GetID(); bool printIP = ( id > 0 && sn_decorateIP ); line = ""; line << "["; if ( sn_decorateID ) line << id; if ( sn_decorateID && printIP ) line << " "; if ( printIP ) { // get IP from id tString IP; sn_GetAdr( id, IP ); line << "IP=" << IP; } line << "] " << orig; } } }; static nConsoleFilter sn_consoleFilter; #endif static void sn_ConsoleOut_handler(nMessage &m){ if (sn_GetNetState()!=nSERVER){ tString s; m >> s; con << s; } } static nDescriptor sn_ConsoleOut_nd(8,sn_ConsoleOut_handler,"sn_ConsoleOut"); // causes the connected clients to print a message nMessage* sn_ConsoleOutMessage( const tOutput& o ) { tString message(o); message << "0xffffff"; nMessage* m=new nMessage(sn_ConsoleOut_nd); *m << message; return m; } void sn_ConsoleOut(const tOutput& o,int client){ // tString message(o); tJUST_CONTROLLED_PTR< nMessage > m = sn_ConsoleOutMessage( o ); if (client<0){ m->BroadCast(); con << o; } else if (client==sn_myNetID) { con << o; } else m->Send(client); } static void client_cen_handler(nMessage &m){ if (sn_GetNetState()!=nSERVER){ tString s; m >> s; con.CenterDisplay(s); } } static nDescriptor client_cen_nd(9,client_cen_handler,"client_cen"); // causes the connected clients to print a message in the center of the screeen void sn_CenterMessage(const tOutput &o,int client){ tString message(o); nMessage *m=new nMessage(client_cen_nd); *m << message; if (client<0){ m->BroadCast(); con.CenterDisplay(message); } else if (client==sn_myNetID) con.CenterDisplay(message); else m->Send(client); } static void ConsoleOut_conf(std::istream &s) { // read the message tString message; message.ReadLine( s ); message += "\n"; // display it sn_ConsoleOut( message ); } static tConfItemFunc ConsoleOut_c("CONSOLE_MESSAGE",&ConsoleOut_conf); static void CeterMessage_conf(std::istream &s) { // read the message tString message; message.ReadLine( s ); // display it sn_CenterMessage( message ); } static tConfItemFunc CenterMessage_c("CENTER_MESSAGE",&CeterMessage_conf); // **************************************************************** // Send Queue // **************************************************************** // the network stuff planned to send: tHeap send_queue[MAXCLIENTS+2]; planned_send::planned_send(REAL priority,int Peer){ peer=Peer; SetVal( priority, send_queue[peer] ); } planned_send::~planned_send(){ RemoveFromHeap(); } tHeapBase *planned_send::Heap() const { return &send_queue[peer]; } // change our priority: void planned_send::add_to_priority(REAL diff) { SetVal( Val() + diff, send_queue[peer] ); } // ********************************************** nMessage_planned_send::nMessage_planned_send (nMessage *M,REAL priority,bool Ack,int Peer) :planned_send(priority,Peer),m(M),ack(Ack){ //if (m) } nMessage_planned_send::~nMessage_planned_send(){ } void nMessage_planned_send::execute(){ if ( Val() < -killTimeout-10){ tOutput mess; mess.SetTemplateParameter(1, peer); mess << "$network_error_overflow"; con << mess; st_Breakpoint(); sn_KillUser(peer, "$network_kill_overflow"); } else if (m) m->SendImmediately(peer,ack); } // ********************************************** void sn_SendPlanned(){ sn_OrderPriority = 0; // if possible, send waiting messages static double lastTime=-1; nTimeAbsolute time=tSysTimeFloat(); if (lastTime<0) lastTime=time; if (timelastTime+1000) #ifdef DEBUG tERR_ERROR("Timer hickup!"); #else { tERR_WARN("Timer hickup!"); lastTime=time; } #endif //for(int i=MAXCLIENTS+1;i>=0;i--){ for(int i=0;i<=MAXCLIENTS+1;i++){ sn_Connections[i].bandwidthControl_.Update(time-lastTime); while (sn_Connections[i].ackPendingexecute(); if (send_queue[i].Len()) delete send_queue[i](0); } // make everything a little more urgent: for(int j=send_queue[i].Len()-1;j>=0;j--) send_queue[i](j)->add_to_priority(lastTime-time); if (sn_Connections[i].sendBuffer_.Len()>0 && sn_Connections[i].bandwidthControl_.CanSend() ) nMessage::SendCollected(i); } lastTime=time; } void sn_KillUser(int i, const tOutput& reason ){ nCurrentSenderID senderID( i ); // don't be daft and kill yourself, server! if ( i == 0 && sn_GetNetState() == nSERVER ) return; nWaitForAck::AckAllPeer(i); // don't kill the server! // if (sn_GetNetState()==nSERVER && i==0) // return; static bool reentry=false; if (reentry) return; reentry=true; bool printMessage = false; // is it worth printing a message for this event? if (sn_Connections[i].socket>0) { nMessage::SendCollected(i); printMessage = true; } // to make sure... if (i!=0){ for(int j=2;j>=0;j--){ nMessage* mess = (new nMessage(login_deny)); *mess << tString( reason ); mess->SendImmediately(i, false); nMessage::SendCollected(i); } } nWaitForAck::AckAllPeer(i); sn_Connections[i].ackMess=NULL; if (i==0 && sn_GetNetState()==nCLIENT) sn_SetNetState(nSTANDALONE); reset_last_acks(i); peers[i].sa_family=0; sn_Connections[i].socket=-1; sn_Connections[i].ackPending=0; // sn_ackAckPending[i]=0; while (send_queue[i].Len()) delete (send_queue[i](0)); nCallbackLoginLogout::UserLoggedOut(i); sn_Connections[i].sendBuffer_.Clear(); sn_Connections[i].Clear(); reentry=false; sn_UpdateCurrentVerion(); if ( printMessage ) { tOutput o; o.SetTemplateParameter(1, i); o.SetTemplateParameter(2, sn_Connections[i].ping); o << "$network_killuser"; con << o; } } int sn_QueueLen(int user){ return send_queue[user].Len(); } static tCallback* s_loginoutAnchor=NULL; int nCallbackLoginLogout::user; bool nCallbackLoginLogout::login; nCallbackLoginLogout::nCallbackLoginLogout(VOIDFUNC *f) :tCallback(s_loginoutAnchor,f){} void nCallbackLoginLogout::UserLoggedIn(int u){ login = true; user = u; Exec(s_loginoutAnchor); } void nCallbackLoginLogout::UserLoggedOut(int u){ login = false; user = u; Exec(s_loginoutAnchor); } unsigned short nCallbackAcceptPackedWithoutConnection::descriptor=0; // the descriptor of the incoming packet static tCallbackOr* s_AcceptAnchor=NULL; nCallbackAcceptPackedWithoutConnection::nCallbackAcceptPackedWithoutConnection(BOOLRETFUNC *f) : tCallbackOr( s_AcceptAnchor, f ) { } bool nCallbackAcceptPackedWithoutConnection::Accept( const nMessage& m ) { descriptor=m.Descriptor(); return Exec( s_AcceptAnchor ); } static tCallback* s_ReceivedCompleteAnchor=NULL; nCallbackReveivedComplete::nCallbackReveivedComplete(VOIDFUNC *f) : tCallback( s_ReceivedCompleteAnchor, f ) { } void nCallbackReveivedComplete::ReceivedComplete( ) { Exec( s_ReceivedCompleteAnchor ); } static bool net_Accept() { return nCallbackAcceptPackedWithoutConnection::Descriptor()==login_accept.ID() || nCallbackAcceptPackedWithoutConnection::Descriptor()==login_deny.ID(); } static nCallbackAcceptPackedWithoutConnection net_acc( &net_Accept ); static void net_exit(){ for (int i=MAXCLIENTS+1;i>=0;i--) { sn_Connections[i].ackMess = NULL; while (send_queue[i].Len()) delete send_queue[i].Remove(0); } } static tInitExit net_ie(NULL, &net_exit); void sn_Statistics() { nTimeRolling time = tSysTimeFloat(); REAL dt = time - sn_StatsTime; sn_StatsTime = time; if (dt > 0 && (sn_SentPackets || sn_SentBytes)) { tOutput o; o.SetTemplateParameter(1,dt); o.SetTemplateParameter(2,sn_SentBytes); o.SetTemplateParameter(3,sn_SentPackets); o.SetTemplateParameter(4,sn_SentBytes/dt); o.SetTemplateParameter(5,sn_ReceivedBytes); o.SetTemplateParameter(6,sn_ReceivedPackets); o.SetTemplateParameter(7,sn_ReceivedBytes/dt); o << "$network_statistics1"; o << "$network_statistics2"; o << "$network_statistics3"; con << o; } sn_SentPackets = 0; sn_SentBytes = 0; sn_ReceivedPackets = 0; sn_ReceivedBytes = 0; } nConnectionInfo::nConnectionInfo(){} nConnectionInfo::~nConnectionInfo(){} void nConnectionInfo::Clear(){ socket = -1; ackPending = 0; ping = 10; crypt = NULL; sendBuffer_.Clear(); bandwidthControl_.Reset(); ackMess = NULL; userName.SetLen(0); } void sn_GetAdr(int user, tString& name) { name = ANET_AddrToString(&peers[user]); } unsigned int sn_GetPort(int user) { return ANET_GetSocketPort(&peers[user]); } unsigned int sn_GetServerPort() { return net_hostport; } int sn_NumUsers( bool all ) { int ret = 0; for (int i=MAXCLIENTS; i>0; i--) if (sn_Connections[i].socket > 0 && ( all || ( timeouts[i] < kickOnDemandTimeout && sn_allowSameIPCountSoft > CountSameIP( i ) ) ) ) ret++; #ifndef DEDICATED ret++; #endif return ret; } int sn_NumUsers() { return sn_NumUsers( true ); } int sn_NumRealUsers() { return sn_NumUsers( false ); } int sn_MaxUsers() { return sn_maxClients; } int sn_MessagesPending(int user) { return sn_Connections[user].ackPending + send_queue[user].Len(); }