/* ************************************************************************* 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 "nNetObject.h" #include "tLocale.h" //#include "nNet.h" #include "nSimulatePing.h" #include "tSysTime.h" #include "nObserver.h" // debug watchs #ifdef DEBUG int sn_WatchNetID = 383; extern nMessage* sn_WatchMessage; #endif tDEFINE_REFOBJ( nNetObject ); // max ping to equalize; int sn_pingCharityServer=100; // first, we need a mechanism to distribute the nNetObject id's // among the clients and the server. // only used on the server: the next free id. static unsigned short net_current_id=1; static unsigned short inc_id(){ unsigned short ret=net_current_id++; #ifdef DEBUG if ( net_current_id > 1000 ) { net_current_id = 0; } #endif if (net_current_id==0) net_current_id++; return ret; } // the number of ID's that is to be requested from the server #define ID_PREFETCH 50 // the requested id FIFO unsigned short net_reserved_id[ID_PREFETCH]; unsigned short distribute=0,request=0; // the reserved id's are stored here tArray sn_netObjectsOwner; // so the server knows who owns wich id // stored information needed when the objects are destroeyed static tArray sn_netObjects_AcceptClientSync; // and the function AcceptClientSync() is no longer valid (bummer.) tArray > sn_netObjects(1024); static const REAL nDeletedTimeout = 60.0f; struct nDeletedInfo { tJUST_CONTROLLED_PTR object_; // deleted object nTimeAbsolute time_; // time it was deleted nDeletedInfo() { this->UnSet(); } void Set( nNetObject* object ) { time_ = tSysTimeFloat(); object_ = object; } void UnSet( ) { time_ = tSysTimeFloat() - nDeletedTimeout * 2.0f; object_ = NULL; } }; static tArray< nDeletedInfo > sn_netObjectsDeleted(1024); static bool free_server( nNetObjectID id ) { if ( bool(sn_netObjectsOwner[id]) || bool(sn_netObjects[id]) ) { return false; } const nDeletedInfo& deleted = sn_netObjectsDeleted[ id ]; if ( deleted.time_ > tSysTimeFloat() - nDeletedTimeout ) { return false; } return true; } static unsigned short next_free_server_nokill(){ nNetObjectID start_id = net_current_id; if (sn_GetNetState()==nSERVER || sn_GetNetState()==nSTANDALONE) { do { inc_id(); } while ( !free_server( net_current_id ) && net_current_id != start_id ); if ( net_current_id != start_id ) { // no problem! return net_current_id; } else { // we ran out of IDs. return 0; } } else { tERR_ERROR("next_free_server is not available for clients."); return 0; } } // kills the guy with the most registered IDs static int kill_id_hog() { int i; int grabbedIDs[MAXCLIENTS+2]; int usedIDs[MAXCLIENTS+2]; for ( i = MAXCLIENTS+1; i>=0; --i ) { grabbedIDs[i] = 0; usedIDs[i] = 0; } // find out how many IDs a user has reserved with or without using them for ( i = sn_netObjectsOwner.Len()-1; i>=0; --i ) { int owner = sn_netObjectsOwner( i ); tASSERT( owner >= 0 && owner <= MAXCLIENTS ); if ( sn_netObjects[i] ) { usedIDs[ owner ] ++; } else if ( owner > 0 ) { grabbedIDs[ owner ] ++; } } // find the user with most used/grabbed IDs int maxGrabbed = ID_PREFETCH + 1; int maxUsed = 0; int maxGrabbedUser = -1; int maxUsedUser = -1; for ( i = MAXCLIENTS+1; i > 0; --i ) { if ( grabbedIDs[i] > maxGrabbed ) { maxGrabbedUser = i; maxGrabbed = grabbedIDs[i]; } if ( usedIDs[i] > maxUsed ) { maxUsedUser = i; maxUsed = usedIDs[i]; } } // kick the top grabber if ( maxGrabbedUser > 0 ) { con << "Killing top ID grabber.\n"; st_Breakpoint(); sn_KillUser( maxGrabbedUser, "$network_kill_maxidgrabber" ); return maxGrabbedUser; } // kick the top user else if ( maxUsedUser > 0 ) { con << "Killing top ID user.\n"; st_Breakpoint(); sn_KillUser( maxUsedUser, "$network_kill_maxiduser" ); return maxUsedUser; } // emergency else { con << "Emergency exit: ran out of IDs.\n"; st_Breakpoint(); exit(-1); } } static unsigned short next_free_server(){ nNetObjectID id = next_free_server_nokill(); if ( id > 0 ) { return id; } else { kill_id_hog(); id = next_free_server_nokill(); if ( id > 0 ) { return id; } else { con << "Emergency exit: desperately ran out of IDs.\n"; exit(-1); } } } void req_id_handler(nMessage &m){ unsigned short stop = distribute; if (distribute == 0) stop = ID_PREFETCH; if (sn_GetNetState()==nSERVER) Cheater(m.SenderID()); else{ while (!m.End()) { unsigned short id, count=1; m.Read(id); if (!m.End()) m.Read(count); for (unsigned short i=id + count - 1; i>= id && request+1 != stop; i--) { if (sn_netObjects[i]) { con << "Warning! Network id receive error on ID " << i << " belonging to client " << sn_netObjects[i]->Owner() << "\n"; con << "while recieving ID block " << id << "-" << id+count-1 << " from netmessage " << m.MessageID() << ".\n"; } else { net_reserved_id[request] = i; #ifdef DEBUG // con << "got id " << net_reserved_id[request] << '\n'; #endif request++; if (request>=ID_PREFETCH) request=0; } } } } } nDescriptor req_id(20,req_id_handler,"req_id"); void id_req_handler(nMessage &m){ // Add security: keep clients from fetching too many ids if (sn_GetNetState()==nSERVER && m.SenderID()<=MAXCLIENTS) { if (m.End()) { // old style request; send only one ID back. nMessage *rep=new nMessage(req_id); unsigned short id=next_free_server(); sn_netObjectsOwner[id]=m.SenderID(); // con << "Assigning ID " << id << "\n"; rep->Write(id); rep->Send(m.SenderID()); #ifdef DEBUG //con << "distributed id " << net_current_id-1 << " to user " << m.SenderID() << '\n'; #endif } else { // new style request: many IDs unsigned short num; m.Read(num); nMessage *rep=new nMessage(req_id); unsigned short begin_block=0; // begin of the block of currently assigned IDs unsigned short block_len=0; // and it's length for (int i = num-1; i>=0; i--) { nNetObjectID id = next_free_server_nokill(); if ( id <= 0 ) { int user = kill_id_hog(); id = next_free_server_nokill(); if ( id <= 0 ) { con << "Emergency exit: desperately ran out of IDs.\n"; exit(-1); } // we just kicked the user requesting the IDs if ( user == m.SenderID() ) { return; } } sn_netObjectsOwner[id]=m.SenderID(); if (begin_block + block_len == id) // RLE for allocated IDs block_len++; else { if (block_len > 0) { // con << "Assigning block " << begin_block << " - " << begin_block + block_len - 1 << "\n"; rep->Write(begin_block); rep->Write(block_len); } begin_block = id; block_len = 1; } } if (block_len > 0) { // con << "Assigning block " << begin_block << " - " << begin_block + block_len - 1 << "\n"; rep->Write(begin_block); rep->Write(block_len); } rep->Send(m.SenderID()); } } } nDescriptor id_req(21,id_req_handler,"id_req_handler"); unsigned short next_free(){ unsigned short ret=0; do{ if (sn_GetNetState()==nCLIENT){ unsigned short need_soon = request + ID_PREFETCH - distribute; if (need_soon > ID_PREFETCH) need_soon -= ID_PREFETCH; if (need_soon < (ID_PREFETCH >> 1)) { nMessage *m = new nMessage(id_req); m->Write(ID_PREFETCH >> 2); m->Send(0); } double timeout=tSysTimeFloat()+60; while(sn_Connections[0].socket>0 && distribute==request && tSysTimeFloat()=timeout) tERR_ERROR_INT("Not enough nNetObject IDs to distribute. Sorry!\n"); ret=net_reserved_id[distribute]; distribute++; if (distribute>=ID_PREFETCH) distribute=0; // con << "used id " << ret << '\n'; net_current_id=ret+1; } else { ret=next_free_server(); } if (sn_netObjects[ret]){ con << "Warning! Network id assignment error on ID " << ret << " belonging to client " << sn_netObjects[ret]->Owner() << "\n"; ret=0; } }while(ret==0 && sn_Connections[0].socket>0); return ret; } void first_fill_ids(){ if (sn_GetNetState()!=nCLIENT) tERR_ERROR("first_fill_ids is only for clients!"); distribute=request=0; // for (int i = 50; i>=0; i--) // { nMessage *m = new nMessage(id_req); m->Write(ID_PREFETCH - 10); m->Send(0); // } } void Cheater(int i) { con << "User " << i << " tried to cheat.\n"; // st_Breakpoint(); #ifdef DEBUG if (i==0) tERR_ERROR("HEY! The server does not cheat!"); #endif sn_KillUser(i, "$network_kill_cheater" ); } /* nDescriptor& nNetObject::CreatorDescriptor() const{ return nNetObject_initialisator.desc; } */ void nNetObject::AddRef(){ tASSERT ( this ); if ( this ) { tASSERT( refCtr_ >= 0 ); refCtr_++; tASSERT( refCtr_ >= 0 ); } } void nNetObject::ReleaseOwnership(){ if ( this->createdLocally ) { this->createdLocally = false; Release(); } } void nNetObject::TakeOwnership(){ if ( !this->createdLocally ) { this->createdLocally = true; // AddRef(); } } void nNetObject::Release(){ tASSERT( this ); if (this){ if (refCtr_>0) refCtr_--; else { #ifdef DEBUG tERR_ERROR("Negative recfcount!"); #else return; #endif } int extra=0; // account for the reference held by the creator of the object // only if the object is validly entered in our object-array if (id > 0 && static_cast(sn_netObjects[id])==this) { if ( createdLocally ) extra = -1; } // else // extra = -1; if ( refCtr_ + extra <= 0 ) { refCtr_ = -100; delete this; } } } // get refcount. Use only for Debgging purposes, never base any decisions on it. int nNetObject::GetRefcount() const { int extra=0; // account for the reference held by the creator of the object // only if the object is validly entered in our object-array if (id > 0 && static_cast(sn_netObjects[id])==this) { if ( createdLocally ) extra = -1; } // else // extra = -1; return this->refCtr_ + extra; } nObserver& nNetObject::GetObserver() const { if ( !this->observer_ ) { this->observer_ = tNEW( nObserver ); this->observer_->SetObject( this ); } return *this->observer_; } // dumps object stats void nNetObject::Dump( tConsole& con ) { tString str; this->PrintName( str ); con << str; } static unsigned short global_lastSync=0; bool sn_Update(unsigned short &old,unsigned short n){ unsigned short diff=old-n; if (diff>100){ old=n; return true; } else return false; } bool nNetObject::SyncIsNew(nMessage &m){ sn_Update(global_lastSync,m.MessageID()); return sn_Update(lastSyncID,m.MessageID()); } nNetObject::nNetObject(int own):lastSyncID(global_lastSync), id(0),refCtr_(0),owner(own){ #ifdef DEBUG //con << "Netobject " << id << " created.\n"; // if (id == 383) // st_Breakpoint(); #endif createdLocally = true; if (own<0) owner=::sn_myNetID; } static nNetObjectRegistrar* sn_Registrar = NULL; nNetObjectRegistrar::nNetObjectRegistrar() { sender = 100; id = 0; oldRegistrar = sn_Registrar; sn_Registrar = this; } nNetObjectRegistrar::~nNetObjectRegistrar() { tASSERT( sn_Registrar == this ); sn_Registrar = oldRegistrar; } // gegister with the object database void nNetObject::Register( const nNetObjectRegistrar& registrar ) { tASSERT( this == registrar.object ); tASSERT( id == 0 || id == registrar.id ); if ( this->id == registrar.id ) { return; } id = registrar.id; if (sn_netObjectsOwner[id]!= registrar.sender || sn_netObjects[id]){ #ifdef DEBUG con << "Netobject " << id << " is already reserved!\n"; #endif if (sn_netObjectsOwner[id]!=registrar.sender){ Cheater( registrar.sender ); nReadError(); } } else { sn_netObjects[id]=this; } if (sn_GetNetState()!=nCLIENT) owner=registrar.sender; // to make sure noone is given a nNetObject from // someone else. sn_netObjectsOwner[id]=owner; sn_netObjects_AcceptClientSync[id]=false; } nNetObject::nNetObject(nMessage &m):lastSyncID(m.MessageID()),refCtr_(0){ sn_Update(global_lastSync,lastSyncID); id = 0; owner = 0; tASSERT( sn_Registrar ); nNetObjectRegistrar& registrar = *sn_Registrar; createdLocally = false; m.Read( registrar.id ); #ifdef DEBUG //con << "Netobject " << id << " created on remote order.\n"; // if (id == 383) // st_Breakpoint(); #endif m.Read( owner ); registrar.object = this; registrar.sender = m.SenderID(); knowsAbout[m.SenderID()].knowsAboutExistence=true; #ifdef DEBUG // con << "Netobject " << id << " created (remote order).\n"; #endif } void nNetObject::DoBroadcastExistence(){ if (BroadcastExistence() && ( sn_GetNetState()!=nCLIENT || ( owner == ::sn_myNetID && AcceptClientSync() ) ) ) RequestSync(); } void nNetObject::InitAfterCreation(){ DoBroadcastExistence(); // con << "InitAfterCreation\n"; }; // after remote creation, struct nDestroyInfo { unsigned short id; unsigned short sender; }; static tArray< nDestroyInfo > sn_Destroyed; static void net_destroy_handler(nMessage &m){ //con << "destroy begin\n"; unsigned short id; //int count=0; while (!m.End()){ m.Read(id); nDestroyInfo& info = sn_Destroyed[ sn_Destroyed.Len() ]; info.id = id; info.sender = m.SenderID(); // notify object of pending deletion if (nNetObject *no=sn_netObjects[id]) no->ActionOnDelete(); #ifdef DEBUG //count ++; //con << count; //con << " destroying object " << id << " by remote order.\n"; #endif } //con << "destroy end.\n"; } static void sn_DoDestroy() { #ifdef DEBUG static bool recursion = false; tASSERT( !recursion ); recursion = true; #endif for ( int i = sn_Destroyed.Len()-1 ; i>=0; --i ) { const nDestroyInfo& info = sn_Destroyed( i ); unsigned short id = info.id; // destroy it! if (nNetObject *no=sn_netObjects[id]){ if (no->Owner()==info.sender || info.sender==0){ sn_netObjectsDeleted [ id ].Set( no ); // no->ActionOnDelete(); moved to destroy message handler sn_netObjects(id)=NULL; sn_netObjectsOwner(id)=0; } else Cheater(info.sender); } } sn_Destroyed.SetLen( 0 ); #ifdef DEBUG recursion = false; #endif } static nCallbackReveivedComplete sn_ReceivedComplete( sn_DoDestroy ); /* // tell the basic nNetObject constructor where to store itself void nNetObject::RegisterRegistrar( nNetObjectRegistrar& r ) { sn_Registrar = &r; } */ static nDescriptor net_destroy(22,net_destroy_handler,"net_destroy"); static nMessage *destroyers[MAXCLIENTS+2]; nNetObject::~nNetObject(){ // release observer if ( this->observer_ ) { this->observer_->SetObject( NULL ); } #ifdef DEBUG int extra=0; // account for the reference held by the creator of the object // only if the object is validly entered in our object-array if (id > 0 && static_cast(sn_netObjects[id])==this) { if ( createdLocally ) extra = -1; } if (refCtr_ + extra > 0) tERR_ERROR("Hey! There is stil someone interested in this nNetObject!\n"); //con << "Netobject " << id << " deleted.\n"; #endif // if this is called on the server, notify all clients if (id) sn_netObjectsOwner[id]=0; //sn_netObjects[id]=NULL; if (sn_GetNetState()==nSERVER || (id && sn_netObjects_AcceptClientSync[id] && owner==sn_myNetID)){ // con << "Destroying object " << id << '\n'; for(int user=MAXCLIENTS;user>=0;user--){ if(user!=sn_myNetID && knowsAbout[user].knowsAboutExistence || knowsAbout[user].acksPending){ if (destroyers[user]==NULL) destroyers[user]=new nMessage(net_destroy); destroyers[user]->Write(id); if (destroyers[user]->DataLen()>100){ destroyers[user]->Send(user); destroyers[user]=NULL; } #ifdef DEBUG //con << "remotely destroying object " << id << '\n'; #endif } } } refCtr_=100; if (id && (this == sn_netObjects[id])) { sn_netObjectsDeleted[id].Set( NULL ); sn_netObjects[id] = NULL; } refCtr_=-100; tCHECK_DEST; } nNetObject *nNetObject::Object(int i){ if (i==0) // the NULL nNetObject return NULL; // the last deleted object with specified ID nNetObject* deleted = sn_netObjectsDeleted[ i ].object_; nNetObject *ret=ObjectDangerous( i ); if ( ret ) { return ret; } double timeout=tSysTimeFloat()+20; while (sn_Connections[0].socket>0 && NULL==(ret=sn_netObjects[i]) && timeout >tSysTimeFloat()){ // wait until it is spawned if (tSysTimeFloat()>timeout-10){ con << "Waiting for nNetObject to spawn..\n"; if (sn_GetNetState()==nSERVER){ // in server mode, we cannot wait. // the deleted object with the same ID must have been meant if ( deleted ) { return deleted; } nReadError(); con << "Now we need to leave the\n" << "system in an undefined state. I hope this works...\n"; Cheater(owner); // kill this bastard return NULL; // pray that noone references this pointer } } usleep(10000); sn_Receive(); timeout--; } if (timeout<=0 || sn_Connections[0].socket<=0) tERR_ERROR("Netobject " << i << " requested, but was never spawned."); if ( ret ) return ret; else { return deleted; } } nNetObject *nNetObject::ObjectDangerous(int i ){ if (i==0) // the NULL nNetObject { return NULL; } else { nNetObject* ret = sn_netObjects[i]; if ( ret ) { return ret; } else { const nDeletedInfo& info = sn_netObjectsDeleted[ i ]; if ( info.time_ > tSysTimeFloat() - nDeletedTimeout ) { return info.object_; } } } return NULL; } void nNetObject::PrintName(tString &s) const { s << "Nameless NetObject nr. " << id; } bool nNetObject::HasBeenTransmitted(int user) const{ return (knowsAbout[user].knowsAboutExistence); } // we must not transmit an object that contains pointers // to non-transmitted objects. this function is supposed to check that. bool nNetObject::ClearToTransmit(int user) const{ return true; } void nNetObject::WriteSync(nMessage &m){ #ifdef DEBUG if (sn_GetNetState()!=nSERVER && !AcceptClientSync()) tERR_ERROR("WriteSync should only be called server-side!"); #endif } // nothing to do yet void nNetObject::ReadSync(nMessage &m){ if (sn_GetNetState()==nSERVER){ bool back=knowsAbout[m.SenderID()].syncReq; RequestSync(); // tell the others about it knowsAbout[m.SenderID()].syncReq=back; // but not the sender of the message; he // knows already. } } extern bool deb_net; // read and write id and owner void nNetObject::WriteCreate(nMessage &m){ m.Write(id); m.Write(owner); // store the info needed in the destructor sn_netObjects_AcceptClientSync[id]=this->AcceptClientSync(); if (deb_net) con << "Sending creation message for nNetObject " << id << "\n"; } void nNetObject::GetID() { if ( !id && GetRefcount() >= 0 ) { if ( bool( sn_Registrar ) && this == sn_Registrar->object ) { id = sn_Registrar->id; } else { id = next_free(); } if (sn_netObjects[id]) tERR_ERROR("Dublicate nNetObject id " << id); sn_netObjectsOwner[id]=owner; sn_netObjects_AcceptClientSync[id]=false; sn_netObjects[id]=this; } } // request a sync void nNetObject::RequestSync(int user,bool ack){ // only for a single user #ifdef nSIMULATE_PING ack=true; #endif this->GetID(); if (sn_GetNetState()==nSERVER || (AcceptClientSync() && owner==::sn_myNetID)){ knowsAbout[user].syncReq=true; knowsAbout[user].nextSyncAck |=ack; } #ifdef DEBUG else tERR_ERROR("RequestSync should only be called server-side!"); #endif } void nNetObject::RequestSync(bool ack){ this->GetID(); #ifdef nSIMULATE_PING ack=true; #endif #ifdef DEBUG if (sn_GetNetState()==nCLIENT && (!AcceptClientSync() || owner!=::sn_myNetID)) tERR_ERROR("RequestSync should only be called server-side!"); #endif for(int i=MAXCLIENTS;i>=0;i--){ knowsAbout[i].syncReq=true; knowsAbout[i].nextSyncAck |=ack; } } static void net_control_handler(nMessage &m){ //con << "control\n"; if (sn_GetNetState()==nSERVER){ unsigned short id; m.Read(id); nNetObject *o = sn_netObjects[id]; if ( o ){ if (m.SenderID()==o->Owner()) // only the owner is // allowed to control the object o->ReceiveControlNet(m); else Cheater(m.SenderID()); // another lame cheater. } } } static nDescriptor net_control(23,net_control_handler,"net_control"); void nNetObject::ReceiveControlNet(nMessage &){ #ifdef DEBUG if (sn_GetNetState()==nCLIENT) tERR_ERROR("rec_cont should not be called client-side!"); #endif // after control is received, we better sync this object with // the clients: RequestSync(); } // control functions: nMessage * nNetObject::NewControlMessage(){ nMessage *m=new nMessage(net_control); m->Write(id); return m; } class nWaitForAckSync: public nWaitForAck{ unsigned short netobj; public: nWaitForAckSync(nMessage* m,int rec,unsigned short obj) :nWaitForAck(m,rec),netobj(obj){ if(sn_netObjects(obj)->knowsAbout[rec].acksPending<15) { sn_netObjects(obj)->knowsAbout[rec].acksPending++; } else { st_Breakpoint(); } } virtual ~nWaitForAckSync(){tCHECK_DEST;} virtual void AckExtraAction() { nNetObject* obj = sn_netObjects[netobj]; if ( obj ) { if( obj->knowsAbout[receiver].acksPending) { obj->knowsAbout[receiver].acksPending--; } else { // st_Breakpoint(); } #ifdef DEBUG /* if ( !obj->knowsAbout[receiver].knowsAboutExistence ) { tString str; obj->PrintName( str ); con << "Received ack for object " << str << "\n"; } */ #endif obj->knowsAbout[receiver].knowsAboutExistence=true; } else { // st_Breakpoint(); } } }; static void net_sync_handler(nMessage &m){ unsigned short id; m.Read(id); if (sn_netObjects[id]){ if (sn_GetNetState()!=nCLIENT && (!sn_netObjects(id)->AcceptClientSync() || sn_netObjects(id)->Owner()!=m.SenderID()) ){ Cheater(m.SenderID()); #ifdef DEBUG tERR_ERROR("sync should only be called client-side!"); #endif } else if (sn_netObjects(id)->SyncIsNew(m)){ m.Reset(); m.Read(id); sn_netObjects(id)->ReadSync(m); } } } static nDescriptor net_sync(24,net_sync_handler,"net_sync"); bool nNetObject::AcceptClientSync() const{ return false; } // global functions: static int current_sync[MAXCLIENTS+2]; static bool is_ready_to_get_objects[MAXCLIENTS+2]; // from nNetwork.C //extern REAL planned_rate_control[MAXCLIENTS+2]; static bool s_DoPrintDebug = false; bool nNetObject::DoDebugPrint() { return s_DoPrintDebug; } void nNetObject::SyncAll(){ #ifdef DEBUG s_DoPrintDebug = false; static nTimeRolling debugtime = 0; nTimeRolling time = tSysTimeFloat(); if (time > debugtime && sn_GetNetState() == nSERVER) { debugtime = time+5; // s_DoPrintDebug = true; } #endif for(int user=MAXCLIENTS;user>=0;user--) if (is_ready_to_get_objects[user] && sn_Connections[user].socket>0 && sn_netObjects.Len()>0 && user!=sn_myNetID){ // send the destroy messages if (destroyers[user]) destroyers[user]->Send(user); destroyers[user]=NULL; if (current_sync[user]<0) current_sync[user]=0; if (current_sync[user]>=sn_netObjects.Len()) current_sync[user]=sn_netObjects.Len()-1; int stop_sync=-1; while(sn_Connections[user].socket>0 && sn_Connections[user].bandwidthControl_.CanSend() && sn_Connections[user].ackPendingClearToTransmit(user) && (sn_GetNetState()!=nCLIENT || nos->AcceptClientSync())) { if (// nos->knowsAbout[user].syncReq && !nos->knowsAbout[user].knowsAboutExistence) { if (!nos->knowsAbout[user].acksPending){ #ifdef DEBUG //con << "remotely creating object " << s << '\n'; #endif /* con << "creating object " << s << " at user " << user << " owned by " << sn_netObjects(s)->owner << '\n'; */ // send a creation message nMessage *m=new nMessage (nos->CreatorDescriptor()); #ifdef DEBUG if (s == sn_WatchNetID) sn_WatchMessage = m; #endif nos->WriteCreate(*m); nos->WriteSync(*m); new nWaitForAckSync(m,user,s); int id = m->MessageID(); m->SendImmediately(user, false); m->messageID = id; } #ifdef DEBUG else if (DoDebugPrint()) { tString s; s << "Not remotely creating object "; nos->PrintName(s); s << " on user " << user << " again because there is an Ack pending.\n"; con << s; } #endif } else if (nos->knowsAbout[user].syncReq && sn_Connections[user].bandwidthControl_.Control( nBandwidthControl::Usage_Planning ) >50 && nos->knowsAbout[user].acksPending<=1){ // send a sync nMessage *m=new nMessage(net_sync); m->Write(s); nos->WriteSync(*m); nos->knowsAbout[user].syncReq=false; if(nos->knowsAbout[user].nextSyncAck){ new nWaitForAckSync(m,user,s); nos->knowsAbout[user].nextSyncAck=false; } #ifndef nSIMULATE_PING int id = m->MessageID(); m->Send(user,0,false); m->messageID = id; #endif } } current_sync[user]++; if (current_sync[user]>=sn_netObjects.Len()) current_sync[user]=0; } #ifdef DEBUG bool inc=false; static int warn=0; if(sn_Connections[user].bandwidthControl_.Control( nBandwidthControl::Usage_Planning )<-100){ if ((warn%50)==0) con << "Warning! Network overflow: " << -100-sn_Connections[user].bandwidthControl_.Control( nBandwidthControl::Usage_Planning ) << "\n"; inc=true; } if(sn_Connections[user].ackPending>=sn_maxNoAck){ if ((warn%50)==25) std::cerr << "Warning! Too many acks pending: " << sn_Connections[user].ackPending << "\n"; inc=true; } if (inc) warn++; else warn=0; #endif } } static void ready_handler(nMessage &m) { is_ready_to_get_objects[m.SenderID()]=true; } static nDescriptor ready(25,ready_handler,"ready to get objects"); static void net_clear_handler(nMessage &m){ if (sn_GetNetState()!=nSERVER){ nNetObject::ClearAll(); first_fill_ids(); } } static nDescriptor net_clear(26,net_clear_handler,"net_clear"); void nNetObject::ClearAllDeleted() { for (int i=sn_netObjectsDeleted.Len()-1;i>=0;i--) { nDeletedInfo& info = sn_netObjectsDeleted[ i ]; info.UnSet(); } sn_netObjectsDeleted.SetLen(0); } void nNetObject::ClearAll(){ ClearAllDeleted();; //con << "WARNING! BAD DESIGN. nNetObject::clear all() called.\n"; for (int i=sn_netObjects.Len()-1;i>=0;i--) if (tJUST_CONTROLLED_PTR< nNetObject > no=sn_netObjects(i)){ sn_netObjects(i)=NULL; sn_netObjectsOwner(i)=0; no->id = 0; } sn_netObjects.SetLen(0); sn_netObjectsOwner.SetLen(0); (tNEW(nMessage)(net_clear))->BroadCast(); // just to make sure.. } void nNetObject::ClearKnows(int user, bool clear){ if (0<=user && user <=MAXCLIENTS){ is_ready_to_get_objects[user]=false; for(int i=sn_netObjects.Len()-1;i>=0;i--){ nNetObject *no=sn_netObjects(i); if (no){ no->knowsAbout[user].Reset(); no->DoBroadcastExistence(); // immediately transfer the thing if (clear){ if (no->owner==user && user!=sn_myNetID){ if (no->ActionOnQuit()) sn_netObjects(i)=NULL; // destroy it else{ no->owner=::sn_myNetID; // or make it mine. sn_netObjectsOwner(i)=::sn_myNetID; if (no->AcceptClientSync()){ tControlledPTR< nNetObject > bounce( no ); // destroy it, if noone wants it } } } } } } } } void ClearKnows(int user, bool clear){ nNetObject::ClearKnows(user, clear); if (clear) for(int i=sn_netObjectsOwner.Len()-1;i>=0;i--){ if(sn_netObjectsOwner(i)==user) sn_netObjectsOwner(i)=0; } } /* If we switch from standalone to client mode, all the sn_netObjects * need new id's. */ void nNetObject::RelabelOnConnect(){ if (sn_GetNetState()==nCLIENT){ tArray > sn_netObjects_old; // transfer the sn_netObjects to sn_netObjects_old: int i; for(i=sn_netObjects.Len()-1;i>=0;i--){ sn_netObjects_old[i]=sn_netObjects(i); sn_netObjects(i)=NULL; sn_netObjectsOwner[i]=0; } // assign new id's and transfer them back to sn_netObjects: for(i=sn_netObjects_old.Len()-1;i>=0;i--){ nNetObject *no = sn_netObjects_old(i); if (no){ unsigned short id=next_free(); #ifdef DEBUG //con << "object " <id=id; no->owner=::sn_myNetID; sn_netObjectsOwner[id]=::sn_myNetID; for(int j=MAXCLIENTS;j>=0;j--){ no->knowsAbout[j].Reset(); no->DoBroadcastExistence(); } if (sn_netObjects[id]) st_Breakpoint(); sn_netObjects[id]=no; sn_netObjects_old(i)=NULL; } } } (new nMessage(ready))->Send(0); is_ready_to_get_objects[0]=true; } static void login_callback(){ ClearKnows(nCallbackLoginLogout::User(), !nCallbackLoginLogout::Login()); if (nCallbackLoginLogout::User() == 0 && nCallbackLoginLogout::Login()) nNetObject::RelabelOnConnect(); } static nCallbackLoginLogout nlc(&login_callback); static bool sync_ack[MAXCLIENTS+2]; static unsigned short c_sync=0; static void sync_ack_handler(nMessage &m){ unsigned short id; m.Read(id); if (id==c_sync) sync_ack[m.SenderID()]=true; } static nDescriptor sync_ack_nd(27,sync_ack_handler,"sync_ack"); static void sync_msg_handler(nMessage &m); static nDescriptor sync_nd(28,sync_msg_handler,"sync_msg"); // from nNetwork.C void sn_Sync(REAL timeout,bool sync_sn_netObjects){ nTimeAbsolute endTime=timeout+tSysTimeFloat(); #ifdef DEBUG //con << "Start sync...\n"; #endif if (sn_GetNetState()==nCLIENT){ while (sn_Connections[0].socket>0 && (sn_Connections[0].ackPending>0 || sn_QueueLen(0)) && tSysTimeFloat()0;user--){ sync_ack[user]=false; if(sn_Connections[user].socket>0){ nMessage *m=new nMessage(sync_nd); *m << timeout; m->Write(sync_sn_netObjects); m->Write(c_sync); m->Send(user); } } bool goon=true; while(goon){ usleep(sn_defaultDelay); if (sync_sn_netObjects) nNetObject::SyncAll(); sn_Receive(); goon=false; for(int user=MAXCLIENTS;user>0;user--) { if(sn_Connections[user].socket>0 && (!sync_ack[user] || sn_Connections[user].ackPending>0 || sn_QueueLen(user))) { goon=true; } } if (tSysTimeFloat()>endTime) { goon=false; } } } #ifdef DEBUG //con << "Stop sync.\n"; #endif } static void sync_msg_handler(nMessage &m){ static bool recursion=false; if (!recursion){ recursion=true; if(sn_GetNetState()!=nSERVER){ REAL timeout; unsigned short sync_sn_netObjects; m >> timeout; m.Read(sync_sn_netObjects); m.Read(c_sync); sn_Sync(timeout+4,sync_sn_netObjects!=0); nMessage *m=new nMessage(sync_ack_nd); m->Write(c_sync); m->Send(0); } else nReadError(); recursion=false; } } void clear_owners(){ for(int i=sn_netObjectsOwner.Len()-1;i>=0;i--) sn_netObjectsOwner(i)=0; } #include "nPriorizing.h" // ********************************************************* // nBandwidthTaskSync/Create: object syncing bandwidth tasks // ********************************************************* nBandwidthTaskObject::nBandwidthTaskObject( nType type, nNetObject& object ) :nBandwidthTask( type ), object_( &object ) { } nBandwidthTaskObject::~nBandwidthTaskObject() { } // estimate bandwidth usage int nBandwidthTaskObject::DoEstimateSize() const { return 16; } // executes whatever it has to do void nBandwidthTaskSync::DoExecute( nSendBuffer& buffer, nBandwidthControl& control ) { tJUST_CONTROLLED_PTR< nMessage > message = tNEW( nMessage )( net_sync ); Object().WriteSync( *message ); buffer.AddMessage( *message, &control ); } // executes whatever it has to do void nBandwidthTaskCreate::DoExecute( nSendBuffer& buffer, nBandwidthControl& control ) { tJUST_CONTROLLED_PTR< nMessage > message = tNEW( nMessage )( Object().CreatorDescriptor() ); Object().WriteCreate( *message ); Object().WriteSync( *message ); buffer.AddMessage( *message, &control ); }