1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300 |
- //-----------------------------------------------------------------------------
- // 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 0xF00DBAAD
- Signal<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;
- }
- else
- {
- objectId = 0;
- ghostIndex = 0;
- }
- object = NULL;
- validObject = false;
- }
- ~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--)
- {
- 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.
- }
|