| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "core/dnet.h"#include "console/simBase.h"#include "sim/netConnection.h"#include "core/stream/bitStream.h"#include "sim/netObject.h"//#include "core/resManager.h"#include "console/console.h"#include "console/consoleTypes.h"#include "console/engineAPI.h"#define DebugChecksum 0xF00DBAADSignal<void()>    NetConnection::smGhostAlwaysDone;extern U32 gGhostUpdates;class GhostAlwaysObjectEvent : public NetEvent{   SimObjectId objectId;   U32 ghostIndex;   NetObject *object;   bool validObject;public:   typedef NetEvent Parent;   GhostAlwaysObjectEvent(NetObject *obj = NULL, U32 index = 0)   {      if(obj)      {         objectId = obj->getId();         ghostIndex = index;      }      object = NULL;   }   ~GhostAlwaysObjectEvent()      { delete object; }   void pack(NetConnection *ps, BitStream *bstream)   {      bstream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);      NetObject *obj = (NetObject *) Sim::findObject(objectId);      if(bstream->writeFlag(obj != NULL))      {         S32 classId = obj->getClassId(ps->getNetClassGroup());         bstream->writeClassId(classId, NetClassTypeObject, ps->getNetClassGroup());#ifdef TORQUE_NET_STATS         U32 beginSize = bstream->getBitPosition();#endif         U32 retMask = obj->packUpdate(ps, 0xFFFFFFFF, bstream);         if ( retMask != 0 ) obj->setMaskBits( retMask );#ifdef TORQUE_NET_STATS         obj->getClassRep()->updateNetStatPack(0xFFFFFFFF, bstream->getBitPosition() - beginSize);#endif      }   }   void write(NetConnection *ps, BitStream *bstream)   {      bstream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);      if(bstream->writeFlag(validObject))      {         S32 classId = object->getClassId(ps->getNetClassGroup());         bstream->writeClassId(classId, NetClassTypeObject, ps->getNetClassGroup());#ifdef TORQUE_NET_STATS         U32 beginSize = bstream->getBitPosition();#endif         U32 retMask = object->packUpdate(ps, 0xFFFFFFFF, bstream);         if ( retMask != 0 ) object->setMaskBits( retMask );#ifdef TORQUE_NET_STATS         object->getClassRep()->updateNetStatPack(0xFFFFFFFF, bstream->getBitPosition() - beginSize);#endif      }   }   void unpack(NetConnection *ps, BitStream *bstream)   {      ghostIndex = bstream->readInt(NetConnection::GhostIdBitSize);      if(bstream->readFlag())      {         S32 classId = bstream->readClassId(NetClassTypeObject, ps->getNetClassGroup());         if(classId == -1)         {            ps->setLastError("Invalid packet. (invalid ghost class id)");            return;         }         object = (NetObject *) ConsoleObject::create(ps->getNetClassGroup(), NetClassTypeObject, classId);         if(!object)         {            ps->setLastError("Invalid packet. (failed to created from class id)");            return;         }         object->mNetFlags = NetObject::IsGhost;         object->mNetIndex = ghostIndex;#ifdef TORQUE_NET_STATS         U32 beginSize = bstream->getBitPosition();#endif         object->unpackUpdate(ps, bstream);#ifdef TORQUE_NET_STATS         object->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize);#endif         validObject = true;      }      else      {         object = new NetObject;         validObject = false;      }   }   void process(NetConnection *ps)   {      Con::executef("onGhostAlwaysObjectReceived");      ps->setGhostAlwaysObject(object, ghostIndex);      object = NULL;   }   DECLARE_CONOBJECT(GhostAlwaysObjectEvent);};IMPLEMENT_CO_NETEVENT_V1(GhostAlwaysObjectEvent);ConsoleDocClass( GhostAlwaysObjectEvent,				"@brief Legacy or soon to be locked down object.\n\n"				"Not intended for game development, for editors or internal use only.\n\n "				"@internal");DefineEngineMethod( NetConnection, getGhostsActive, S32, (),,   "@brief Provides the number of active ghosts on the connection.\n\n"   "@returns The number of active ghosts.\n"   "@see @ref ghosting_scoping for a description of the ghosting system.\n\n"){	return object->getGhostsActive();}void NetConnection::setGhostTo(bool ghostTo){   if(mLocalGhosts) // if ghosting to this is already enabled, silently return      return;   if(ghostTo)   {      mLocalGhosts = new NetObject *[MaxGhostCount];      for(S32 i = 0; i < MaxGhostCount; i++)         mLocalGhosts[i] = NULL;   }}void NetConnection::setGhostFrom(bool ghostFrom){   if(mGhostArray)      return;   if(ghostFrom)   {      mGhostFreeIndex = mGhostZeroUpdateIndex = 0;      mGhostArray = new GhostInfo *[MaxGhostCount];      mGhostRefs = new GhostInfo[MaxGhostCount];      S32 i;      for(i = 0; i < MaxGhostCount; i++)      {         mGhostRefs[i].obj = NULL;         mGhostRefs[i].index = i;         mGhostRefs[i].updateMask = 0;      }      mGhostLookupTable = new GhostInfo *[GhostLookupTableSize];      for(i = 0; i < GhostLookupTableSize; i++)         mGhostLookupTable[i] = 0;   }}void NetConnection::ghostOnRemove(){   if(mGhostArray)      clearGhostInfo();}void NetConnection::ghostPacketDropped(PacketNotify *notify){   GhostRef *packRef = notify->ghostList;   // loop through all the packRefs in the packet   while(packRef)   {      GhostRef *temp = packRef->nextRef;      U32 orFlags = 0;      AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!");      // clear out the ref for this object, plus or together all      // flags from updates after this      GhostRef **walk = &(packRef->ghost->updateChain);      while(*walk != packRef)      {         orFlags |= (*walk)->mask;         walk = &((*walk)->nextUpdateChain);      }      *walk = 0;      // for any flags we haven't updated since this (dropped) packet      // or them into the mask so they'll get updated soon      orFlags = packRef->mask & ~orFlags;      if(orFlags)      {         if(!packRef->ghost->updateMask)         {            packRef->ghost->updateMask = orFlags;            ghostPushNonZero(packRef->ghost);         }         else            packRef->ghost->updateMask |= orFlags;      }      // if this packet was ghosting an object, set it      // to re ghost at it's earliest convenience      if(packRef->ghostInfoFlags & GhostInfo::Ghosting)      {         packRef->ghost->flags |= GhostInfo::NotYetGhosted;         packRef->ghost->flags &= ~GhostInfo::Ghosting;      }      // otherwise, if it was being deleted,      // set it to re-delete      else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost)      {         packRef->ghost->flags |= GhostInfo::KillGhost;         packRef->ghost->flags &= ~GhostInfo::KillingGhost;      }      delete packRef;      packRef = temp;   }}void NetConnection::ghostPacketReceived(PacketNotify *notify){   GhostRef *packRef = notify->ghostList;   // loop through all the notifies in this packet   while(packRef)   {      GhostRef *temp = packRef->nextRef;      AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!");      // clear this notify from the end of the object's notify      // chain      GhostRef **walk = &(packRef->ghost->updateChain);      while(*walk != packRef)         walk = &((*walk)->nextUpdateChain);      *walk = 0;      // if this object was ghosting , it is now ghosted      if(packRef->ghostInfoFlags & GhostInfo::Ghosting)         packRef->ghost->flags &= ~GhostInfo::Ghosting;      // otherwise, if it was dieing, free the ghost      else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost)         freeGhostInfo(packRef->ghost);      delete packRef;      packRef = temp;   }}struct UpdateQueueEntry{   F32 priority;   GhostInfo *obj;   UpdateQueueEntry(F32 in_priority, GhostInfo *in_obj)      { priority = in_priority; obj = in_obj; }};static S32 QSORT_CALLBACK UQECompare(const void *a,const void *b){   GhostInfo *ga = *((GhostInfo **) a);   GhostInfo *gb = *((GhostInfo **) b);   F32 ret = ga->priority - gb->priority;   return (ret < 0) ? -1 : ((ret > 0) ? 1 : 0);}void NetConnection::ghostWritePacket(BitStream *bstream, PacketNotify *notify){#ifdef    TORQUE_DEBUG_NET   bstream->writeInt(DebugChecksum, 32);#endif   notify->ghostList = NULL;   if(!isGhostingFrom())      return;   if(!bstream->writeFlag(mGhosting))      return;   // fill a packet (or two) with ghosting data   // first step is to check all our polled ghosts:   // 1. Scope query - find if any new objects have come into   //    scope and if any have gone out.   // 2. call scoped objects' priority functions if the flag set is nonzero   //    A removed ghost is assumed to have a high priority   // 3. call updates based on sorted priority until the packet is   //    full.  set flags to zero for all updated objects   CameraScopeQuery camInfo;   camInfo.camera = NULL;   camInfo.pos.set(0,0,0);   camInfo.orientation.set(0,1,0);   camInfo.visibleDistance = 1;   camInfo.fov = (F32)(3.1415f / 4.0f);   camInfo.sinFov = 0.7071f;   camInfo.cosFov = 0.7071f;   GhostInfo *walk;   // only need to worry about the ghosts that have update masks set...   S32 maxIndex = 0;   S32 i;   for(i = 0; i < mGhostZeroUpdateIndex; i++)   {      // increment the updateSkip for everyone... it's all good      walk = mGhostArray[i];      walk->updateSkipCount++;      if(!(walk->flags & (GhostInfo::ScopeAlways | GhostInfo::ScopeLocalAlways)))         walk->flags &= ~GhostInfo::InScope;   }   if( mScopeObject )      mScopeObject->onCameraScopeQuery( this, &camInfo );   doneScopingScene();   for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)   {      // [rene, 07-Mar-11] Killing ghosts depending on the camera scope queries      //    seems like a bad thing to me and something that definitely has the potential      //    of causing scoping to eat into bandwidth rather than preserve it.  As soon      //    as an object comes back into scope, it will have to completely retransmit its      //    full server-side state from scratch.      if(!(mGhostArray[i]->flags & GhostInfo::InScope))         detachObject(mGhostArray[i]);   }   for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)   {      walk = mGhostArray[i];      if(walk->index > maxIndex)         maxIndex = walk->index;      // clear out any kill objects that haven't been ghosted yet      if((walk->flags & GhostInfo::KillGhost) && (walk->flags & GhostInfo::NotYetGhosted))      {         freeGhostInfo(walk);         continue;      }      // don't do any ghost processing on objects that are being killed      // or in the process of ghosting      else if(!(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting)))      {         if(walk->flags & GhostInfo::KillGhost)            walk->priority = 10000;         else            walk->priority = walk->obj->getUpdatePriority(&camInfo, walk->updateMask, walk->updateSkipCount);      }      else         walk->priority = 0;   }   GhostRef *updateList = NULL;   dQsort(mGhostArray, mGhostZeroUpdateIndex, sizeof(GhostInfo *), UQECompare);   // reset the array indices...   for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)      mGhostArray[i]->arrayIndex = i;   S32 sendSize = 1;   while(maxIndex >>= 1)      sendSize++;   if(sendSize < 3)      sendSize = 3;   bstream->writeInt(sendSize - 3, GhostIndexBitSize);   U32 count = 0;   //   for(i = mGhostZeroUpdateIndex - 1; i >= 0 && !bstream->isFull(); i--)   {      GhostInfo *walk = mGhostArray[i];		if(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting))		   continue;		      bstream->writeFlag(true);      bstream->writeInt(walk->index, sendSize);      U32 updateMask = walk->updateMask;      GhostRef *upd = new GhostRef;      upd->nextRef = updateList;      updateList = upd;      upd->nextUpdateChain = walk->updateChain;      walk->updateChain = upd;      upd->ghost = walk;      upd->ghostInfoFlags = 0;      if(walk->flags & GhostInfo::KillGhost)      {         walk->flags &= ~GhostInfo::KillGhost;         walk->flags |= GhostInfo::KillingGhost;         walk->updateMask = 0;         upd->mask = updateMask;         ghostPushToZero(walk);         upd->ghostInfoFlags = GhostInfo::KillingGhost;         bstream->writeFlag(true); // killing ghost      }      else      {         bstream->writeFlag(false);#ifdef TORQUE_DEBUG_NET         U32 startPos = bstream->getCurPos();#endif         if(walk->flags & GhostInfo::NotYetGhosted)         {            S32 classId = walk->obj->getClassId(getNetClassGroup());            bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup());#ifdef TORQUE_DEBUG_NET            bstream->writeInt(classId ^ DebugChecksum, 32);#endif            walk->flags &= ~GhostInfo::NotYetGhosted;            walk->flags |= GhostInfo::Ghosting;            upd->ghostInfoFlags = GhostInfo::Ghosting;         }#ifdef TORQUE_DEBUG_NET         else {            S32 classId = walk->obj->getClassId(getNetClassGroup());            bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup());            bstream->writeInt(classId ^ DebugChecksum, 32);         }#endif         // update the object#ifdef TORQUE_NET_STATS         U32 beginSize = bstream->getBitPosition();#endif         U32 retMask = walk->obj->packUpdate(this, updateMask, bstream);#ifdef TORQUE_NET_STATS         walk->obj->getClassRep()->updateNetStatPack(updateMask, bstream->getBitPosition() - beginSize);#endif         DEBUG_LOG(("PKLOG %d GHOST %d: %s", getId(), bstream->getBitPosition() - 16 - startPos, walk->obj->getClassName()));         AssertFatal((retMask & (~updateMask)) == 0, "Cannot set new bits in packUpdate return");         ghostWriteExtra(walk->obj,bstream);         walk->updateMask = retMask;         if(!retMask)            ghostPushToZero(walk);         upd->mask = updateMask & ~retMask;         //PacketStream::getStats()->addBits(PacketStats::Send, bstream->getCurPos() - startPos, walk->obj->getPersistTag());#ifdef TORQUE_DEBUG_NET         bstream->writeInt(walk->index ^ DebugChecksum, 32);#endif      }      walk->updateSkipCount = 0;      count++;   }   //Con::printf("Ghosts updated: %d (%d remain)", count, mGhostZeroUpdateIndex);   // no more objects...   bstream->writeFlag(false);   notify->ghostList = updateList;}void NetConnection::ghostReadPacket(BitStream *bstream){#ifdef    TORQUE_DEBUG_NET   U32 sum = bstream->readInt(32);   AssertISV(sum == DebugChecksum, "Invalid checksum.");#endif   if(!isGhostingTo())      return;   if(!bstream->readFlag())      return;   S32 idSize;   idSize = bstream->readInt( GhostIndexBitSize);   idSize += 3;   // while there's an object waiting...   gGhostUpdates = 0;   while(bstream->readFlag())   {	  gGhostUpdates++;      U32 index;      //S32 startPos = bstream->getCurPos();      index = (U32) bstream->readInt(idSize);      if(bstream->readFlag()) // is this ghost being deleted?      {		 mGhostsActive--;         AssertFatal(mLocalGhosts[index] != NULL, "Error, NULL ghost encountered.");         mLocalGhosts[index]->deleteObject();         mLocalGhosts[index] = NULL;      }      else      {         if(!mLocalGhosts[index]) // it's a new ghost... cool         {			   mGhostsActive++;            S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup());            if(classId == -1)            {               setLastError("Invalid packet. (invalid new ghost class id)");               return;            }            NetObject *obj = (NetObject *) ConsoleObject::create(getNetClassGroup(), NetClassTypeObject, classId);            if(!obj)            {               setLastError("Invalid packet. (failed to create new ghost)");               return;            }            // remove all flags associated with netobject            obj->mNetFlags &= ~(BIT(NetObject::MaxNetFlagBit+1)-1);            // we're a ghost...            obj->mNetFlags |= NetObject::IsGhost;                        // object gets initial update before adding to the manager            obj->mNetIndex = index;            mLocalGhosts[index] = obj;#ifdef TORQUE_DEBUG_NET            U32 checksum = bstream->readInt(32);            S32 origId = checksum ^ DebugChecksum;            AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost.");            AssertISV(origId == mLocalGhosts[index]->getClassId(getNetClassGroup()),               avar("class id mismatch for dest class %s.",                  mLocalGhosts[index]->getClassName()) );#endif            // give derived classes a chance to prepare ghost for reading            ghostPreRead(mLocalGhosts[index],true);#ifdef TORQUE_NET_STATS            U32 beginSize = bstream->getBitPosition();#endif            mLocalGhosts[index]->unpackUpdate(this, bstream);#ifdef TORQUE_NET_STATS            mLocalGhosts[index]->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize);#endif            // Setup the remote object pointers before            // we register so that it can be used from onAdd.            if( mRemoteConnection )            {               obj->mServerObject = mRemoteConnection->resolveObjectFromGhostIndex(index);               if ( obj->mServerObject )               {                  obj->mServerObject->mClientObject = obj;                                    // Sync selection flag as otherwise the editor will end up setting only                  // server-side flags when selecting an object that hasn't been ghosted yet                  // (usually the case when creating new objects).                                    if( obj->mServerObject->isSelected() )                     obj->setSelected( true );               }            }            if(!obj->registerObject())            {               if(mErrorBuffer.isEmpty())                  setLastError("Invalid packet. (failed to register ghost)");               return;            }                     addObject(obj);            ghostReadExtra(mLocalGhosts[index],bstream,true);         }         else         {#ifdef TORQUE_DEBUG_NET            S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup());            U32 checksum = bstream->readInt(32);            S32 origId = checksum ^ DebugChecksum;            AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost.");            AssertISV(origId == mLocalGhosts[index]->getClassId(getNetClassGroup()),               avar("class id mismatch for dest class %s.",                  mLocalGhosts[index]->getClassName()) );#endif            // give derived classes a chance to prepare ghost for reading            ghostPreRead(mLocalGhosts[index],false);#ifdef TORQUE_NET_STATS            U32 beginSize = bstream->getBitPosition();#endif            mLocalGhosts[index]->unpackUpdate(this, bstream);#ifdef TORQUE_NET_STATS            mLocalGhosts[index]->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize);#endif            ghostReadExtra(mLocalGhosts[index],bstream,false);         }         //PacketStream::getStats()->addBits(PacketStats::Receive, bstream->getCurPos() - startPos, ghostRefs[index].localGhost->getPersistTag());#ifdef TORQUE_DEBUG_NET         U32 checksum = bstream->readInt(32);         S32 origIndex = checksum ^ DebugChecksum;         AssertISV(origIndex == index,            avar("unpackUpdate did not match packUpdate for object of class %s.",               mLocalGhosts[index]->getClassName()) );#endif         if(mErrorBuffer.isNotEmpty())            return;      }   }}//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------void NetConnection::setScopeObject(NetObject *obj){   if(((NetObject *) mScopeObject) == obj)      return;   mScopeObject = obj;}void NetConnection::detachObject(GhostInfo *info){   // mark it for ghost killin'   info->flags |= GhostInfo::KillGhost;   // if the mask is in the zero range, we've got to move it up...   if(!info->updateMask)   {      info->updateMask = 0xFFFFFFFF;      ghostPushNonZero(info);   }   if(info->obj)   {      if(info->prevObjectRef)         info->prevObjectRef->nextObjectRef = info->nextObjectRef;      else         info->obj->mFirstObjectRef = info->nextObjectRef;      if(info->nextObjectRef)         info->nextObjectRef->prevObjectRef = info->prevObjectRef;      // remove it from the lookup table      U32 id = info->obj->getId();      for(GhostInfo **walk = &mGhostLookupTable[id & (GhostLookupTableSize - 1)]; *walk; walk = &((*walk)->nextLookupInfo))      {         GhostInfo *temp = *walk;         if(temp == info)         {            *walk = temp->nextLookupInfo;            break;         }      }      info->prevObjectRef = info->nextObjectRef = NULL;      info->obj = NULL;   }}void NetConnection::freeGhostInfo(GhostInfo *ghost){   AssertFatal(ghost->arrayIndex < mGhostFreeIndex, "Ghost already freed.");   if(ghost->arrayIndex < mGhostZeroUpdateIndex)   {      AssertFatal(ghost->updateMask != 0, "Invalid ghost mask.");      ghost->updateMask = 0;      ghostPushToZero(ghost);   }   ghostPushZeroToFree(ghost);   AssertFatal(ghost->updateChain == NULL, "Ack!");}//-----------------------------------------------------------------------------void NetConnection::objectLocalScopeAlways(NetObject *obj){   if(!isGhostingFrom())      return;   objectInScope(obj);   for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo)   {      if(walk->obj != obj)         continue;      walk->flags |= GhostInfo::ScopeLocalAlways;      return;   }}void NetConnection::objectLocalClearAlways(NetObject *obj){   if(!isGhostingFrom())      return;   for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo)   {      if(walk->obj != obj)         continue;      walk->flags &= ~GhostInfo::ScopeLocalAlways;      return;   }}bool NetConnection::validateGhostArray(){   AssertFatal(mGhostZeroUpdateIndex >= 0 && mGhostZeroUpdateIndex <= mGhostFreeIndex, "Invalid update index range.");   AssertFatal(mGhostFreeIndex <= MaxGhostCount, "Invalid free index range.");   U32 i;   for(i = 0; i < mGhostZeroUpdateIndex; i ++)   {      AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");      AssertFatal(mGhostArray[i]->updateMask != 0, "Invalid ghost mask.");   }   for(; i < mGhostFreeIndex; i ++)   {      AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");      AssertFatal(mGhostArray[i]->updateMask == 0, "Invalid ghost mask.");   }   for(; i < MaxGhostCount; i++)   {      AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");   }   return true;}void NetConnection::objectInScope(NetObject *obj){   if (!mScoping || !isGhostingFrom())      return;	if (obj->isScopeLocal() && !isLocalConnection())		return;   S32 index = obj->getId() & (GhostLookupTableSize - 1);   // check if it's already in scope   // the object may have been cleared out without the lookupTable being cleared   // so validate that the object pointers are the same.   for(GhostInfo *walk = mGhostLookupTable[index ]; walk; walk = walk->nextLookupInfo)   {      if(walk->obj != obj)         continue;      walk->flags |= GhostInfo::InScope;      // Make sure scope always if reflected on the ghostinfo too      if (obj->mNetFlags.test(NetObject::ScopeAlways))         walk->flags |= GhostInfo::ScopeAlways;      return;   }   if (mGhostFreeIndex == MaxGhostCount)   {      AssertWarn(0,"NetConnection::objectInScope: too many ghosts");      return;   }   GhostInfo *giptr = mGhostArray[mGhostFreeIndex];   ghostPushFreeToZero(giptr);   giptr->updateMask = 0xFFFFFFFF;   ghostPushNonZero(giptr);   giptr->flags = GhostInfo::NotYetGhosted | GhostInfo::InScope;   if(obj->mNetFlags.test(NetObject::ScopeAlways))      giptr->flags |= GhostInfo::ScopeAlways;   giptr->obj = obj;   giptr->updateChain = NULL;   giptr->updateSkipCount = 0;   giptr->connection = this;   giptr->nextObjectRef = obj->mFirstObjectRef;   if(obj->mFirstObjectRef)      obj->mFirstObjectRef->prevObjectRef = giptr;   giptr->prevObjectRef = NULL;   obj->mFirstObjectRef = giptr;   giptr->nextLookupInfo = mGhostLookupTable[index];   mGhostLookupTable[index] = giptr;   //AssertFatal(validateGhostArray(), "Invalid ghost array!");}//-----------------------------------------------------------------------------void NetConnection::handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount){   if((  message == SendNextDownloadRequest      || message == FileDownloadSizeMessage      || message == GhostAlwaysStarting      || message == GhostAlwaysDone      || message == EndGhosting) && !isGhostingTo())   {      setLastError("Invalid packet. (not ghosting)");      return;   }   S32 i;   GhostSave sv;   switch(message)   {      case GhostAlwaysDone:         mGhostingSequence = sequence;         NetConnection::smGhostAlwaysDone.trigger();         // ok, all the ghost always objects are now on the client... but!         // it's possible that there were some file load errors...         // if so, we need to indicate to the server to restart ghosting, after         // we download all the files...         sv.ghost = NULL;         sv.index = -1;         mGhostAlwaysSaveList.push_back(sv);         if(mGhostAlwaysSaveList.size() == 1)            loadNextGhostAlwaysObject(true);         break;      case ReadyForNormalGhosts:         if(sequence != mGhostingSequence)            return;         Con::executef(this, "onGhostAlwaysObjectsReceived");         Con::printf("Ghost Always objects received.");         mGhosting = true;         for(i = 0; i < mGhostFreeIndex; i++)         {            if(mGhostArray[i]->flags & GhostInfo::ScopedEvent)               mGhostArray[i]->flags &= ~(GhostInfo::Ghosting | GhostInfo::ScopedEvent);         }         break;      case EndGhosting:         onEndGhosting();         // just delete all the local ghosts,         // and delete all the ghosts in the current save list         for(i = 0; i < MaxGhostCount; i++)         {            if(mLocalGhosts[i])            {               mLocalGhosts[i]->deleteObject();               mLocalGhosts[i] = NULL;            }         }         while(mGhostAlwaysSaveList.size())         {            delete mGhostAlwaysSaveList[0].ghost;            mGhostAlwaysSaveList.pop_front();         }         break;      case GhostAlwaysStarting:         Con::executef("onGhostAlwaysStarted", Con::getIntArg(ghostCount));         break;      case SendNextDownloadRequest:         sendNextFileDownloadRequest();         break;      case FileDownloadSizeMessage:         mCurrentFileBufferSize = sequence;         mCurrentFileBuffer = dRealloc(mCurrentFileBuffer, mCurrentFileBufferSize);         mCurrentFileBufferOffset = 0;         break;   }}void NetConnection::activateGhosting(){   if(!isGhostingFrom())      return;   mGhostingSequence++;   // iterate through the ghost always objects and InScope them...   // also post em all to the other side.   SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet();   SimSet::iterator i;   AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Error: ghosts in the ghost list before activate.");   U32 sz = ghostAlwaysSet->size();   S32 j;   for(j = 0; j < sz; j++)   {      U32 idx = MaxGhostCount - sz + j;      mGhostArray[j] = mGhostRefs + idx;      mGhostArray[j]->arrayIndex = j;   }   for(j = sz; j < MaxGhostCount; j++)   {      U32 idx = j - sz;      mGhostArray[j] = mGhostRefs + idx;      mGhostArray[j]->arrayIndex = j;   }   mScoping = true; // so that objectInScope will work   for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++)   {      AssertFatal(dynamic_cast<NetObject *>(*i) != NULL, avar("Non NetObject in GhostAlwaysSet: %s", (*i)->getClassName()));      NetObject *obj = (NetObject *)(*i);      if(obj->mNetFlags.test(NetObject::Ghostable))         objectInScope(obj);   }    // Send the initial ghosting connection message.    sendConnectionMessage(GhostAlwaysStarting, mGhostingSequence, ghostAlwaysSet->size());    // If this is the connection to the local client...    if (getLocalClientConnection() == this)    {        // Get a pointer to the local client.        NetConnection* pClient = NetConnection::getConnectionToServer();        Con::executef("onGhostAlwaysStarted", Con::getIntArg(mGhostZeroUpdateIndex));        // Set up a buffer for the object send.        U8 iBuffer[4096];        BitStream mStream(iBuffer, 4096);        // Iterate through the scope always objects...        for (j = mGhostZeroUpdateIndex - 1; j >= 0; j--)        {            AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "NetConnection::activateGhosting:  Non-scope always in the scope always list.")            // Clear the ghost update mask and flags appropriately.            mGhostArray[j]->updateMask = 0;            ghostPushToZero(mGhostArray[j]);            mGhostArray[j]->flags &= ~GhostInfo::NotYetGhosted;            mGhostArray[j]->flags |= GhostInfo::ScopedEvent;            // Set up a pointer to the new object.            NetObject* pObject = 0;            // If there's a valid ghost object...            if (mGhostArray[j]->obj)            {                // Pack the server object's update.                mStream.setPosition(0);                mStream.clearCompressionPoint();                U32 retMask = mGhostArray[j]->obj->packUpdate(this, 0xFFFFFFFF, &mStream);                if ( retMask != 0 )                    mGhostArray[j]->obj->setMaskBits( retMask );                // Create a new object instance for the client.                pObject = (NetObject*)ConsoleObject::create(pClient->getNetClassGroup(), NetClassTypeObject, mGhostArray[j]->obj->getClassId(getNetClassGroup()));                // Set the client object networking flags.                pObject->mNetFlags = NetObject::IsGhost;                pObject->mNetIndex = mGhostArray[j]->index;                // Unpack the client object's update.                mStream.setPosition(0);                mStream.clearCompressionPoint();                pObject->unpackUpdate(pClient, &mStream);            }            else            {                // Otherwise, create a new dummy netobject.                pObject = new NetObject;            }            // Execute the appropriate console callback.            Con::executef("onGhostAlwaysObjectReceived");            // Set the ghost always object for the client.            pClient->setGhostAlwaysObject(pObject, mGhostArray[j]->index);        }    }    else    {        // Iterate through the scope always objects...        for (j = mGhostZeroUpdateIndex - 1; j >= 0; j--)        {            AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "NetConnection::activateGhosting:  Non-scope always in the scope always list.")            // Clear the ghost update mask and flags appropriately.            mGhostArray[j]->updateMask = 0;            ghostPushToZero(mGhostArray[j]);            mGhostArray[j]->flags &= ~GhostInfo::NotYetGhosted;            mGhostArray[j]->flags |= GhostInfo::ScopedEvent;            // Post a network event to ghost the scope always object.            postNetEvent(new GhostAlwaysObjectEvent(mGhostArray[j]->obj, mGhostArray[j]->index));        }    }    // Send the ghosting always done message.    sendConnectionMessage(GhostAlwaysDone, mGhostingSequence);   //AssertFatal(validateGhostArray(), "Invalid ghost array!");}void NetConnection::clearGhostInfo(){   // gotta clear out the ghosts...   for(PacketNotify *walk = mNotifyQueueHead; walk; walk = walk->nextPacket)   {      ghostPacketReceived(walk);      walk->ghostList = NULL;   }   for(S32 i = 0; i < MaxGhostCount; i++)   {      if(mGhostRefs[i].arrayIndex < mGhostFreeIndex)      {         detachObject(&mGhostRefs[i]);         freeGhostInfo(&mGhostRefs[i]);      }   }   AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Invalid indices.");}void NetConnection::resetGhosting(){   if(!isGhostingFrom())      return;   // stop all ghosting activity   // send a message to the other side notifying of this   mGhosting = false;   mScoping = false;   sendConnectionMessage(EndGhosting, mGhostingSequence);   mGhostingSequence++;   clearGhostInfo();   //AssertFatal(validateGhostArray(), "Invalid ghost array!");}void NetConnection::setGhostAlwaysObject(NetObject *object, U32 index){   if(!isGhostingTo())   {      object->deleteObject();      setLastError("Invalid packet. (unexpected ghostalways)");      return;   }   object->mNetFlags = NetObject::IsGhost;   object->mNetIndex = index;   // while there's an object waiting...   if ( isLocalConnection() )    {      object->mServerObject = mRemoteConnection->resolveObjectFromGhostIndex(index);      if ( object->mServerObject )         object->mServerObject->mClientObject = object;   }   GhostSave sv;   sv.ghost = object;   sv.index = index;   mGhostAlwaysSaveList.push_back(sv);   // check if we are already downloading files for a previous object:   if(mGhostAlwaysSaveList.size() == 1)      loadNextGhostAlwaysObject(true);  // the initial call always has "new" files}void NetConnection::fileDownloadSegmentComplete(){   // this is called when a the file list has finished processing...   // at this point we can try again to add the object   // subclasses can override this to do, for example, datablock redos.   if(mGhostAlwaysSaveList.size())      loadNextGhostAlwaysObject(mNumDownloadedFiles != 0);}void NetConnection::loadNextGhostAlwaysObject(bool hadNewFiles){   if(!mGhostAlwaysSaveList.size())      return;   while(mGhostAlwaysSaveList.size())   {      if (isLocalConnection()) hadNewFiles = false;      // only check for new files if this is the first load, or if new      // files were downloaded from the server.//       if(hadNewFiles)//          gResourceManager->setMissingFileLogging(true);// //       gResourceManager->clearMissingFileList();      NetObject *object = mGhostAlwaysSaveList[0].ghost;      U32 index = mGhostAlwaysSaveList[0].index;      if(!object)      {         // a null object is used to signify that the last ghost in the list is down         mGhostAlwaysSaveList.pop_front();         AssertFatal(mGhostAlwaysSaveList.size() == 0, "Error! Ghost save list should be empty!");         sendConnectionMessage(ReadyForNormalGhosts, mGhostingSequence);//          gResourceManager->setMissingFileLogging(false);         return;      }      mFilesWereDownloaded = hadNewFiles;      if(!object->registerObject())      {         mFilesWereDownloaded = false;         // make sure there's an error message if necessary         if(mErrorBuffer.isEmpty())            setLastError("Invalid packet. (failed to register ghost always)");         // if there were no new files, make sure the error message         // is the one from the last time we tried to add this object         if(!hadNewFiles)         {            mErrorBuffer = mLastFileErrorBuffer;//             gResourceManager->setMissingFileLogging(false);            return;         }         // object failed to load, let's see if it had any missing files//          if(!gResourceManager->getMissingFileList(mMissingFileList))//          {//             // no missing files, must be an error//             // connection will automagically delete the ghost always list//             // when this error is reported.//             gResourceManager->setMissingFileLogging(false);//             return;//          }         // ok, copy the error buffer out to a scratch pad for now         mLastFileErrorBuffer = mErrorBuffer;         mErrorBuffer = String();         // request the missing files...         mNumDownloadedFiles = 0;         sendNextFileDownloadRequest();         break;      }      mFilesWereDownloaded = false;//       gResourceManager->setMissingFileLogging(false);      addObject(object);      mGhostAlwaysSaveList.pop_front();      AssertFatal(mLocalGhosts[index] == NULL, "Ghost already in table!");      mLocalGhosts[index] = object;      hadNewFiles = true;   }}//-----------------------------------------------------------------------------NetObject *NetConnection::resolveGhost(S32 id){   return mLocalGhosts[id];}NetObject *NetConnection::resolveObjectFromGhostIndex(S32 id){   return mGhostRefs[id].obj;}S32 NetConnection::getGhostIndex(NetObject *obj){   if(!isGhostingFrom())      return obj->mNetIndex;   S32 index = obj->getId() & (GhostLookupTableSize - 1);   for(GhostInfo *gptr = mGhostLookupTable[index]; gptr; gptr = gptr->nextLookupInfo)   {      if(gptr->obj == obj && (gptr->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting | GhostInfo::NotYetGhosted | GhostInfo::KillGhost)) == 0)            return gptr->index;   }   return -1;}//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------void NetConnection::ghostWriteStartBlock(ResizeBitStream *stream){   // Ok, writing the start block for the ghosts:   // here's how it goes.   //   // First we record out all the indices and class ids for all the objects   // This is so when the objects are read in, all the objects are instantiated   // before they are unpacked.  The unpack code may reference other   // existing ghosts, so we want to make sure that all the ghosts are in the   // table with the correct pointers before any of the unpacks are called.   stream->write(mGhostingSequence);   // first write out the indices and ids:   for(U32 i = 0; i < MaxGhostCount; i++)   {      if(mLocalGhosts[i])      {         stream->writeFlag(true);         stream->writeInt(i, GhostIdBitSize);         stream->writeClassId(mLocalGhosts[i]->getClassId(getNetClassGroup()), NetClassTypeObject, getNetClassGroup());         stream->validate();      }   }   // mark off the end of the ghost list:   // it would be more space efficient to write out a count of active ghosts followed   // by index run lengths, but hey, what's a few bits here and there?   stream->writeFlag(false);   // then, for each ghost written into the start block, write the full pack update   // into the start block.  For demos to work properly, packUpdate must   // be callable from client objects.   for(U32 i = 0; i < MaxGhostCount; i++)   {      if(mLocalGhosts[i])      {         U32 retMask = mLocalGhosts[i]->packUpdate(this, 0xFFFFFFFF, stream);         if ( retMask != 0 ) mLocalGhosts[i]->setMaskBits( retMask );         stream->validate();      }   }}void NetConnection::ghostReadStartBlock(BitStream *stream){   stream->read(&mGhostingSequence);   // read em back in.   // first, read in the index/class id, construct the object, and place it in mLocalGhosts[i]   while(stream->readFlag())   {      U32 index = stream->readInt(GhostIdBitSize);      S32 tag = stream->readClassId(NetClassTypeObject, getNetClassGroup());      NetObject *obj = (NetObject *) ConsoleObject::create(getNetClassGroup(), NetClassTypeObject, tag);      if(!obj)      {         setLastError("Invalid packet. (failed to create ghost from demo block)");         return;      }      obj->mNetFlags = NetObject::IsGhost;      obj->mNetIndex = index;      mLocalGhosts[index] = obj;   }   // now, all the ghosts are in the mLocalGhosts, so we loop   // through all non-null mLocalGhosts, unpacking the objects   // as we go:   for(U32 i = 0; i < MaxGhostCount; i++)   {      if(mLocalGhosts[i])      {         mLocalGhosts[i]->unpackUpdate(this, stream);         if(!mLocalGhosts[i]->registerObject())         {            if(mErrorBuffer.isEmpty())               setLastError("Invalid packet. (failed to register ghost from demo block)");            return;         }         addObject(mLocalGhosts[i]);      }   }   // MARKF - TODO - looks like we could have memory leaks here   // if there are errors.}
 |