void HifiClientProcessList::ageTickCache(S32 numToAge, S32 len) { for (ProcessObject * pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) { GameBase *obj = getGameBase(pobj); if ( obj && obj->getTypeMask() & GameBaseHiFiObjectType ) obj->getTickCache().ageCache(numToAge,len); } }
void HifiClientProcessList::clientCatchup(GameConnection * connection) { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("client catching up... (%i)%s", mCatchup, mForceHifiReset ? " reset" : ""); #endif if (connection->getControlObject() && connection->getControlObject()->isGhostUpdated()) // if control object is reset, make sure moves are reset too connection->mMoveList->resetCatchup(); const F32 maxVel = mSqrt(gMaxHiFiVelSq) * 1.25f; F32 dt = F32(mCatchup+1) * TickSec; Point3F bigDelta(maxVel*dt,maxVel*dt,maxVel*dt); // walk through all process objects looking for ones which were updated // -- during first pass merely collect neighbors which need to be reset and updated in unison ProcessObject * pobj; if (mCatchup && !mForceHifiReset) { for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) { GameBase *obj = getGameBase( pobj ); static SimpleQueryList nearby; nearby.mList.clear(); // check for nearby objects which need to be reset and then caught up // note the funky loop logic -- first time through obj is us, then // we start iterating through nearby list (to look for objects nearby // the nearby objects), which is why index starts at -1 // [objects nearby the nearby objects also get added to the nearby list] for (S32 i=-1; obj; obj = ++i<nearby.mList.size() ? (GameBase*)nearby.mList[i] : NULL) { if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType) && !obj->isNetNearbyAdded()) { Point3F start = obj->getWorldSphere().center; Point3F end = start + 1.1f * dt * obj->getVelocity(); F32 rad = 1.5f * obj->getWorldSphere().radius; // find nearby items not updated but are hi fi, mark them as updated (and restore old loc) // check to see if added items have neighbors that need updating Box3F box; Point3F rads(rad,rad,rad); box.minExtents = box.maxExtents = start; box.minExtents -= bigDelta + rads; box.maxExtents += bigDelta + rads; // CodeReview - this is left in for MBU, but also so we can deal with the issue later. // add marble blast hack so hifi networking can see hidden objects // (since hidden is under control of hifi networking) // gForceNotHidden = true; S32 j = nearby.mList.size(); gClientContainer.findObjects(box, GameBaseHiFiObjectType, SimpleQueryList::insertionCallback, &nearby); // CodeReview - this is left in for MBU, but also so we can deal with the issue later. // disable above hack // gForceNotHidden = false; // drop anyone not heading toward us or already checked for (; j<nearby.mList.size(); j++) { GameBase * obj2 = (GameBase*)nearby.mList[j]; // if both passive, these guys don't interact with each other bool passive = obj->isHifiPassive() && obj2->isHifiPassive(); if (!obj2->isGhostUpdated() && !passive) { // compare swept spheres of obj and obj2 // if collide, reset obj2, setGhostUpdated(true), and continue Point3F end2 = obj2->getWorldSphere().center; Point3F start2 = end2 - 1.1f * dt * obj2->getVelocity(); F32 rad2 = 1.5f * obj->getWorldSphere().radius; if (MathUtils::capsuleCapsuleOverlap(start,end,rad,start2,end2,rad2)) { // better add obj2 obj2->getTickCache().beginCacheList(); TickCacheEntry * tce = obj2->getTickCache().incCacheList(); BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); obj2->readPacketData(connection,&bs); obj2->setGhostUpdated(true); // continue so we later add the neighbors too continue; } } // didn't pass above test...so don't add it or nearby objects nearby.mList[j] = nearby.mList.last(); nearby.mList.decrement(); j--; } obj->setNetNearbyAdded(true); } } } } // save water mark -- for game base list FrameAllocatorMarker mark; // build ordered list of client objects which need to be caught up GameBaseListNode list; for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) { GameBase *obj = getGameBase( pobj ); //GameBase *obj = dynamic_cast<GameBase*>( pobj ); //GameBase *obj = (GameBase*)pobj; // Not a GameBase object so nothing to do. if ( !obj ) continue; if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType)) { // construct process object and add it to the list // hold pointer to our object in mAfterObject GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode)); po->mObject = obj; po->linkBefore(&list); // begin iterating through tick list (skip first tick since that is the state we've been reset to) obj->getTickCache().beginCacheList(); obj->getTickCache().incCacheList(); } else if (mForceHifiReset && (obj->getTypeMask() & GameBaseHiFiObjectType)) { // add all hifi objects obj->getTickCache().beginCacheList(); TickCacheEntry * tce = obj->getTickCache().incCacheList(); BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); obj->readPacketData(connection,&bs); obj->setGhostUpdated(true); // construct process object and add it to the list // hold pointer to our object in mAfterObject GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode)); po->mObject = obj; po->linkBefore(&list); } else if (obj == connection->getControlObject() && obj->isGhostUpdated()) { // construct process object and add it to the list // hold pointer to our object in mAfterObject // .. but this is not a hi fi object, so don't mess with tick cache GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode)); po->mObject = obj; po->linkBefore(&list); } else if (obj->isGhostUpdated()) { // not hifi but we were updated, so perform net smooth now obj->computeNetSmooth(mLastDelta); } // clear out work flags obj->setNetNearbyAdded(false); obj->setGhostUpdated(false); } // run through all the moves in the move list so we can play them with our control object Move* movePtr; U32 numMoves; connection->mMoveList->resetClientMoves(); connection->mMoveList->getMoves(&movePtr, &numMoves); AssertFatal(mCatchup<=numMoves,"doh"); // tick catchup time for (U32 m=0; m<mCatchup; m++) { for (GameBaseListNode * walk = list.mNext; walk != &list; walk = walk->mNext) { // note that we get object from after object not getGameBase function // this is because we are an on the fly linked list which uses mAfterObject // rather than the linked list embedded in GameBase (clean this up?) GameBase * obj = walk->mObject; // it's possible for a non-hifi object to get in here, but // only if it is a control object...make sure we don't do any // of the tick cache stuff if we are not hifi. bool hifi = obj->getTypeMask() & GameBaseHiFiObjectType; TickCacheEntry * tce = hifi ? obj->getTickCache().incCacheList() : NULL; // tick object if (obj==connection->getControlObject()) { obj->processTick(movePtr); movePtr->checksum = obj->getPacketDataChecksum(connection); movePtr++; } else { AssertFatal(tce && hifi,"Should not get in here unless a hi fi object!!!"); obj->processTick(tce->move); } if (hifi) { BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); obj->writePacketData(connection,&bs); } } if (connection->getControlObject() == NULL) movePtr++; } connection->mMoveList->clearMoves(mCatchup); // Handle network error smoothing here...but only for control object GameBase * control = connection->getControlObject(); if (control && !control->isNewGhost()) { control->computeNetSmooth(mLastDelta); control->setNewGhost(false); } if (moveSync.doAction() && moveSync.moveDiff>0) { S32 moveDiff = moveSync.moveDiff; #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("client timewarping to catchup %i moves",moveDiff); #endif while (moveDiff--) advanceObjects(); moveSync.reset(); } #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("---------"); #endif // all caught up mCatchup = 0; }