#include "platform/platform.h" #include "T3D/gameBase/extended/extendedGameProcess.h" #include "T3D/gameBase/extended/extendedMoveList.h" #include "platform/profiler.h" #include "console/consoleTypes.h" #include "core/dnet.h" #include "core/stream/bitStream.h" #include "core/frameAllocator.h" #include "core/util/refBase.h" #include "math/mPoint3.h" #include "math/mMatrix.h" #include "math/mathUtils.h" #include "T3D/gameBase/gameBase.h" #include "T3D/gameBase/gameConnection.h" #include "T3D/fx/cameraFXMgr.h" MODULE_BEGIN( ProcessList ) MODULE_INIT { ExtendedServerProcessList::init(); ExtendedClientProcessList::init(); } MODULE_SHUTDOWN { ExtendedServerProcessList::shutdown(); ExtendedClientProcessList::shutdown(); } MODULE_END; void ExtendedServerProcessList::init() { smServerProcessList = new ExtendedServerProcessList(); } void ExtendedServerProcessList::shutdown() { delete smServerProcessList; } void ExtendedClientProcessList::init() { smClientProcessList = new ExtendedClientProcessList(); } void ExtendedClientProcessList::shutdown() { delete smClientProcessList; } //---------------------------------------------------------------------------- namespace { // local work class struct GameBaseListNode { GameBaseListNode() { mPrev=this; mNext=this; mObject=NULL; } GameBaseListNode * mPrev; GameBaseListNode * mNext; GameBase * mObject; void linkBefore(GameBaseListNode * obj) { // Link this before obj mNext = obj; mPrev = obj->mPrev; obj->mPrev = this; mPrev->mNext = this; } }; } // namespace //-------------------------------------------------------------------------- // ExtendedClientProcessList //-------------------------------------------------------------------------- ExtendedClientProcessList::ExtendedClientProcessList() { } bool ExtendedClientProcessList::advanceTime( SimTime timeDelta ) { PROFILE_SCOPE( ExtendedClientProcessList_AdvanceTime ); if ( doBacklogged( timeDelta ) ) return false; bool ret = Parent::advanceTime( timeDelta ); ProcessObject *obj = NULL; AssertFatal( mLastDelta >= 0.0f && mLastDelta <= 1.0f, "mLastDelta is not zero to one."); obj = mHead.mProcessLink.next; while ( obj != &mHead ) { if ( obj->isTicking() ) obj->interpolateTick( mLastDelta ); obj = obj->mProcessLink.next; } // Inform objects of total elapsed delta so they can advance // client side animations. F32 dt = F32(timeDelta) / 1000; // Update camera FX. gCamFXMgr.update( dt ); obj = mHead.mProcessLink.next; while ( obj != &mHead ) { obj->advanceTime( dt ); obj = obj->mProcessLink.next; } return ret; } //---------------------------------------------------------------------------- void ExtendedClientProcessList::onAdvanceObjects() { PROFILE_SCOPE( ExtendedClientProcessList_OnAdvanceObjects ); GameConnection* connection = GameConnection::getConnectionToServer(); if ( connection ) { // process any demo blocks that are NOT moves, and exactly one move // we advance time in the demo stream by a move inserted on // each tick. So before doing the tick processing we advance // the demo stream until a move is ready if ( connection->isPlayingBack() ) { U32 blockType; do { blockType = connection->getNextBlockType(); bool res = connection->processNextBlock(); // if there are no more blocks, exit out of this function, // as no more client time needs to process right now - we'll // get it all on the next advanceClientTime() if(!res) return; } while ( blockType != GameConnection::BlockTypeMove ); } connection->mMoveList->collectMove(); advanceObjects(); } else advanceObjects(); } void ExtendedClientProcessList::onTickObject( ProcessObject *obj ) { PROFILE_SCOPE( ExtendedClientProcessList_OnTickObject ); // In case the object deletes itself during its processTick. SimObjectPtr safePtr = static_cast( obj ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. ExtendedMove* extMovePtr; U32 numMoves; GameConnection* con = obj->getControllingClient(); if ( con && con->getControlObject() == obj ) { ExtendedMoveList* extMoveList = static_cast(con->mMoveList); extMoveList->getExtMoves( &extMovePtr, &numMoves ); if ( numMoves ) { // Note: should only have a single move at this point AssertFatal(numMoves==1,"ClientProccessList::onTickObject: more than one move in queue"); #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); #endif if ( obj->isTicking() ) obj->processTick( extMovePtr ); if ( bool(safePtr) && obj->getControllingClient() ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // set checksum if not set or check against stored value if set extMovePtr->checksum = newsum; #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("move checksum: %i, (start %i), (move %f %f %f)", movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z); #endif } con->mMoveList->clearMoves( 1 ); } } else if ( obj->isTicking() ) obj->processTick( 0 ); } void ExtendedClientProcessList::advanceObjects() { PROFILE_SCOPE( ExtendedClientProcessList_AdvanceObjects ); #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("Advance client time..."); #endif Parent::advanceObjects(); #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("---------"); #endif } void ExtendedClientProcessList::clientCatchup( GameConnection * connection ) { SimObjectPtr control = connection->getControlObject(); if ( control ) { ExtendedMove * extMovePtr; U32 numMoves; U32 m = 0; ExtendedMoveList* extMoveList = static_cast(connection->mMoveList); extMoveList->getExtMoves( &extMovePtr, &numMoves ); #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("client catching up... (%i)", numMoves); #endif preTickSignal().trigger(); if ( control->isTicking() ) for ( m = 0; m < numMoves; m++ ) control->processTick( extMovePtr++ ); extMoveList->clearMoves( m ); } #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("---------"); #endif } //-------------------------------------------------------------------------- // ServerProcessList //-------------------------------------------------------------------------- ExtendedServerProcessList::ExtendedServerProcessList() { } void ExtendedServerProcessList::onPreTickObject( ProcessObject *pobj ) { if ( pobj->mIsGameBase ) { SimObjectPtr obj = getGameBase( pobj ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. GameConnection *con = obj->getControllingClient(); if ( con && con->getControlObject() == obj ) { ExtendedMove* extMovePtr; U32 numMoves; ExtendedMoveList* extMoveList = static_cast(con->mMoveList); extMoveList->getExtMoves( &extMovePtr, &numMoves ); if ( numMoves == 0 ) { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("no moves on object %i, skip tick",obj->getId()); #endif return; } } } Parent::onPreTickObject (pobj ); } void ExtendedServerProcessList::onTickObject( ProcessObject *pobj ) { PROFILE_SCOPE( ExtendedServerProcessList_OnTickObject ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. GameConnection *con = pobj->getControllingClient(); if ( pobj->mIsGameBase && con && con->getControlObject() == pobj ) { // In case the object is deleted during its own tick. SimObjectPtr obj = getGameBase( pobj ); ExtendedMove* extMovePtr; U32 m, numMoves; ExtendedMoveList* extMoveList = static_cast(con->mMoveList); extMoveList->getExtMoves( &extMovePtr, &numMoves ); // For debugging it can be useful to know when this happens. //if ( numMoves > 1 ) // Con::printf( "numMoves: %i", numMoves ); // Do we really need to test the control object each iteration? Does it change? for ( m = 0; m < numMoves && con && con->getControlObject() == obj; m++, extMovePtr++ ) { #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); #endif if ( obj->isTicking() ) obj->processTick( extMovePtr ); if ( con && con->getControlObject() == obj ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // check move checksum if ( extMovePtr->checksum != newsum ) { #ifdef TORQUE_DEBUG_NET_MOVES if( !obj->isAIControlled() ) Con::printf("move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)", extMovePtr->id, extMovePtr->checksum,newsum,sum,extMovePtr->yaw,extMovePtr->y,extMovePtr->z); #endif extMovePtr->checksum = Move::ChecksumMismatch; } else { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("move %i checksum agree: %i == %i, (start %i), (move %f %f %f)", extMovePtr->id, extMovePtr->checksum,newsum,sum,extMovePtr->yaw,extMovePtr->y,extMovePtr->z); #endif } } } extMoveList->clearMoves( m ); } else if ( pobj->isTicking() ) pobj->processTick( 0 ); } void ExtendedServerProcessList::advanceObjects() { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("Advance server time..."); #endif Parent::advanceObjects(); // Credit all connections with the elapsed tick SimGroup *clientGroup = Sim::getClientGroup(); for(SimGroup::iterator i = clientGroup->begin(); i != clientGroup->end(); i++) { if (GameConnection *con = dynamic_cast(*i)) { con->mMoveList->advanceMove(); } } #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("---------"); #endif }