diff --git a/Engine/source/T3D/gameBase/extended/extendedGameProcess.cpp b/Engine/source/T3D/gameBase/extended/extendedGameProcess.cpp new file mode 100644 index 000000000..c3d35eba6 --- /dev/null +++ b/Engine/source/T3D/gameBase/extended/extendedGameProcess.cpp @@ -0,0 +1,379 @@ +#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 +} diff --git a/Engine/source/T3D/gameBase/extended/extendedGameProcess.h b/Engine/source/T3D/gameBase/extended/extendedGameProcess.h new file mode 100644 index 000000000..22d78488e --- /dev/null +++ b/Engine/source/T3D/gameBase/extended/extendedGameProcess.h @@ -0,0 +1,60 @@ +#ifndef _GAMEPROCESS_EXTENDED_H_ +#define _GAMEPROCESS_EXTENDED_H_ + +//#include "T3D/gameBase/processList.h" +#ifndef _GAMEPROCESS_H_ +#include "T3D/gameBase/gameProcess.h" +#endif + +class GameBase; +class GameConnection; +struct Move; + +//---------------------------------------------------------------------------- + +/// List to keep track of GameBases to process. +class ExtendedClientProcessList : public ClientProcessList +{ + typedef ClientProcessList Parent; + +protected: + + // ProcessList + void onTickObject(ProcessObject *); + void advanceObjects(); + void onAdvanceObjects(); + +public: + + ExtendedClientProcessList(); + + // ProcessList + bool advanceTime( SimTime timeDelta ); + + // ClientProcessList + void clientCatchup( GameConnection *conn ); + + static void init(); + static void shutdown(); +}; + +class ExtendedServerProcessList : public ServerProcessList +{ + typedef ServerProcessList Parent; + +protected: + + // ProcessList + void onPreTickObject( ProcessObject *pobj ); + void onTickObject( ProcessObject *pobj ); + void advanceObjects(); + +public: + + ExtendedServerProcessList(); + + static void init(); + static void shutdown(); +}; + +#endif // _GAMEPROCESS_EXTENDED_H_ diff --git a/Engine/source/T3D/gameBase/extended/extendedMove.cpp b/Engine/source/T3D/gameBase/extended/extendedMove.cpp new file mode 100644 index 000000000..0006318d0 --- /dev/null +++ b/Engine/source/T3D/gameBase/extended/extendedMove.cpp @@ -0,0 +1,258 @@ +#include "T3D/gameBase/extended/extendedMove.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "core/module.h" +#include "console/consoleTypes.h" +#include "core/strings/stringFunctions.h" + +MODULE_BEGIN( ExtendedMoveManager ) + + MODULE_INIT_AFTER( MoveManager ) + MODULE_INIT + { + ExtendedMoveManager::init(); + } + +MODULE_END; + +S32 ExtendedMoveManager::mPosX[ExtendedMove::MaxPositionsRotations] = { 0, }; +S32 ExtendedMoveManager::mPosY[ExtendedMove::MaxPositionsRotations] = { 0, }; +S32 ExtendedMoveManager::mPosZ[ExtendedMove::MaxPositionsRotations] = { 0, }; +F32 ExtendedMoveManager::mRotAX[ExtendedMove::MaxPositionsRotations] = { 0, }; +F32 ExtendedMoveManager::mRotAY[ExtendedMove::MaxPositionsRotations] = { 0, }; +F32 ExtendedMoveManager::mRotAZ[ExtendedMove::MaxPositionsRotations] = { 0, }; +F32 ExtendedMoveManager::mRotAA[ExtendedMove::MaxPositionsRotations] = { 1, }; + +void ExtendedMoveManager::init() +{ + for(U32 i = 0; i < ExtendedMove::MaxPositionsRotations; ++i) + { + char varName[256]; + + dSprintf(varName, sizeof(varName), "mvPosX%d", i); + Con::addVariable(varName, TypeS32, &mPosX[i], + "X position of controller in millimeters. Only 13 bits are networked.\n" + "@ingroup Game"); + + dSprintf(varName, sizeof(varName), "mvPosY%d", i); + Con::addVariable(varName, TypeS32, &mPosY[i], + "Y position of controller in millimeters. Only 13 bits are networked.\n" + "@ingroup Game"); + + dSprintf(varName, sizeof(varName), "mvPosZ%d", i); + Con::addVariable(varName, TypeS32, &mPosZ[i], + "Z position of controller in millimeters. Only 13 bits are networked.\n" + "@ingroup Game"); + + dSprintf(varName, sizeof(varName), "mvRotX%d", i); + Con::addVariable(varName, TypeF32, &mRotAX[i], + "X rotation vector component of controller.\n" + "@ingroup Game"); + + dSprintf(varName, sizeof(varName), "mvRotY%d", i); + Con::addVariable(varName, TypeF32, &mRotAY[i], + "Y rotation vector component of controller.\n" + "@ingroup Game"); + + dSprintf(varName, sizeof(varName), "mvRotZ%d", i); + Con::addVariable(varName, TypeF32, &mRotAZ[i], + "Z rotation vector component of controller.\n" + "@ingroup Game"); + + dSprintf(varName, sizeof(varName), "mvRotA%d", i); + Con::addVariable(varName, TypeF32, &mRotAA[i], + "Angle rotation (in degrees) component of controller.\n" + "@ingroup Game"); + } +} + +const ExtendedMove NullExtendedMove; + +#define CLAMPPOS(x) (x<0 ? -((-x) & (1<<(MaxPositionBits-1))-1) : (x & (1<<(MaxPositionBits-1))-1)) +#define CLAMPROT(f) ((S32)(((f + 1) * .5) * ((1 << MaxRotationBits) - 1)) & ((1<(basemove); + + bool extendedDifferent = false; + for(U32 i=0; iposX[i]) || + (posY[i] != extBaseMove->posY[i]) || + (posZ[i] != extBaseMove->posZ[i]) || + (rotX[i] != extBaseMove->rotX[i]) || + (rotY[i] != extBaseMove->rotY[i]) || + (rotZ[i] != extBaseMove->rotZ[i]) || + (rotW[i] != extBaseMove->rotW[i]); + + extendedDifferent = extendedDifferent || check; + } + + if (alwaysWriteAll || stream->writeFlag(extendedDifferent)) + { + for(U32 i=0; iwriteFlag(posX[i] != extBaseMove->posX[i])) + stream->writeSignedInt(posX[i], MaxPositionBits); + if(stream->writeFlag(posY[i] != extBaseMove->posY[i])) + stream->writeSignedInt(posY[i], MaxPositionBits); + if(stream->writeFlag(posZ[i] != extBaseMove->posZ[i])) + stream->writeSignedInt(posZ[i], MaxPositionBits); + + // Rotation + if(stream->writeFlag(rotX[i] != extBaseMove->rotX[i])) + stream->writeInt(crotX[i], MaxRotationBits); + if(stream->writeFlag(rotY[i] != extBaseMove->rotY[i])) + stream->writeInt(crotY[i], MaxRotationBits); + if(stream->writeFlag(rotZ[i] != extBaseMove->rotZ[i])) + stream->writeInt(crotZ[i], MaxRotationBits); + if(stream->writeFlag(rotW[i] != extBaseMove->rotW[i])) + stream->writeInt(crotW[i], MaxRotationBits); + } + } +} + +void ExtendedMove::unpack(BitStream *stream, const Move * basemove) +{ + bool alwaysReadAll = basemove!=NULL; + if (!basemove) + basemove=&NullExtendedMove; + + // Standard Move stuff + bool isBaseMove = !unpackMove(stream, basemove, alwaysReadAll); + + // ExtendedMove + const ExtendedMove* extBaseMove = static_cast(basemove); + + if (alwaysReadAll || stream->readFlag()) + { + isBaseMove = false; + + for(U32 i=0; ireadFlag()) + posX[i] = stream->readSignedInt(MaxPositionBits); + else + posX[i] = extBaseMove->posX[i]; + + if(stream->readFlag()) + posY[i] = stream->readSignedInt(MaxPositionBits); + else + posY[i] = extBaseMove->posY[i]; + + if(stream->readFlag()) + posZ[i] = stream->readSignedInt(MaxPositionBits); + else + posZ[i] = extBaseMove->posZ[i]; + + // Rotation + if(stream->readFlag()) + { + crotX[i] = stream->readInt(MaxRotationBits); + rotX[i] = UNCLAMPROT(crotX[i]); + } + else + { + rotX[i] = extBaseMove->rotX[i]; + } + + if(stream->readFlag()) + { + crotY[i] = stream->readInt(MaxRotationBits); + rotY[i] = UNCLAMPROT(crotY[i]); + } + else + { + rotY[i] = extBaseMove->rotY[i]; + } + + if(stream->readFlag()) + { + crotZ[i] = stream->readInt(MaxRotationBits); + rotZ[i] = UNCLAMPROT(crotZ[i]); + } + else + { + rotZ[i] = extBaseMove->rotZ[i]; + } + + if(stream->readFlag()) + { + crotW[i] = stream->readInt(MaxRotationBits); + rotW[i] = UNCLAMPROT(crotW[i]); + } + else + { + rotW[i] = extBaseMove->rotW[i]; + } + } + } + + if(isBaseMove) + { + *this = *extBaseMove; + } +} + +void ExtendedMove::clamp() +{ + // Clamp the values the same as for net traffic so the client matches the server + for(U32 i=0; i MaxMoveQueueSize ) + return false; + + // From MoveList + F32 pitchAdd = MoveManager::mPitchUpSpeed - MoveManager::mPitchDownSpeed; + F32 yawAdd = MoveManager::mYawLeftSpeed - MoveManager::mYawRightSpeed; + F32 rollAdd = MoveManager::mRollRightSpeed - MoveManager::mRollLeftSpeed; + + curMove.pitch = MoveManager::mPitch + pitchAdd; + curMove.yaw = MoveManager::mYaw + yawAdd; + curMove.roll = MoveManager::mRoll + rollAdd; + + MoveManager::mPitch = 0; + MoveManager::mYaw = 0; + MoveManager::mRoll = 0; + + curMove.x = MoveManager::mRightAction - MoveManager::mLeftAction + MoveManager::mXAxis_L; + curMove.y = MoveManager::mForwardAction - MoveManager::mBackwardAction + MoveManager::mYAxis_L; + curMove.z = MoveManager::mUpAction - MoveManager::mDownAction; + + curMove.freeLook = MoveManager::mFreeLook; + curMove.deviceIsKeyboardMouse = MoveManager::mDeviceIsKeyboardMouse; + + for(U32 i = 0; i < MaxTriggerKeys; i++) + { + curMove.trigger[i] = false; + if(MoveManager::mTriggerCount[i] & 1) + curMove.trigger[i] = true; + else if(!(MoveManager::mPrevTriggerCount[i] & 1) && MoveManager::mPrevTriggerCount[i] != MoveManager::mTriggerCount[i]) + curMove.trigger[i] = true; + MoveManager::mPrevTriggerCount[i] = MoveManager::mTriggerCount[i]; + } + + for(U32 i=0; igetControlObject()) + mConnection->getControlObject()->preprocessMove(&curMove); + + curMove.clamp(); // clamp for net traffic + return true; +} + +U32 ExtendedMoveList::getMoves(Move** movePtr,U32* numMoves) +{ + // We don't want to be here + AssertFatal(0, "getMoves() called"); + + numMoves = 0; + + return 0; +} + +U32 ExtendedMoveList::getExtMoves( ExtendedMove** movePtr, U32 *numMoves ) +{ + if (!mConnection->isConnectionToServer()) + { + if (mExtMoveVec.size() > mMoveCredit) + mExtMoveVec.setSize(mMoveCredit); + } + + // From MoveList but converted to use mExtMoveVec + if (mConnection->isConnectionToServer()) + { + // give back moves starting at the last client move... + + AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request"); + AssertFatal(mLastClientMove - mFirstMoveIndex <= mExtMoveVec.size(), "Desynched first and last move."); + *numMoves = mExtMoveVec.size() - mLastClientMove + mFirstMoveIndex; + *movePtr = mExtMoveVec.address() + mLastClientMove - mFirstMoveIndex; + } + else + { + // return the full list + *numMoves = mExtMoveVec.size(); + *movePtr = mExtMoveVec.begin(); + } + + return *numMoves; +} + +void ExtendedMoveList::collectMove() +{ + ExtendedMove mv; + if (mConnection) + { + if(!mConnection->isPlayingBack() && getNextExtMove(mv)) + { + mv.checksum=Move::ChecksumMismatch; + pushMove(mv); + mConnection->recordBlock(GameConnection::BlockTypeMove, sizeof(ExtendedMove), &mv); + } + } + else + { + if(getNextExtMove(mv)) + { + mv.checksum=Move::ChecksumMismatch; + pushMove(mv); + } + } +} + +void ExtendedMoveList::pushMove(const Move &mv) +{ + const ExtendedMove* extMove = dynamic_cast(&mv); + AssertFatal(extMove, "Regular Move struct passed to pushMove()"); + + pushExtMove(*extMove); +} + +void ExtendedMoveList::pushExtMove( const ExtendedMove &mv ) +{ + U32 id = mFirstMoveIndex + mExtMoveVec.size(); + U32 sz = mExtMoveVec.size(); + mExtMoveVec.push_back(mv); + mExtMoveVec[sz].id = id; + mExtMoveVec[sz].sendCount = 0; +} + +void ExtendedMoveList::clearMoves(U32 count) +{ + if (!mConnection->isConnectionToServer()) + { + count = mClamp(count,0,mMoveCredit); + mMoveCredit -= count; + } + + // From MoveList but converted to use mExtMoveVec + if (mConnection->isConnectionToServer()) + { + mLastClientMove += count; + AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request"); + AssertFatal(mLastClientMove - mFirstMoveIndex <= mExtMoveVec.size(), "Desynched first and last move."); + if (!mConnection) + // drop right away if no connection + ackMoves(count); + } + else + { + AssertFatal(count <= mExtMoveVec.size(),"GameConnection: Clearing too many moves"); + for (S32 i=0; iisConnectionToServer(), "Cannot inc move credit on the client."); + + // Game tick increment + mMoveCredit++; + if (mMoveCredit > MaxMoveCount) + mMoveCredit = MaxMoveCount; + + // Clear pending moves for the elapsed time if there + // is no control object. + if ( mConnection->getControlObject() == NULL ) + mExtMoveVec.clear(); +} + +void ExtendedMoveList::clientWriteMovePacket(BitStream *bstream) +{ + AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index."); + U32 count = mExtMoveVec.size(); + + ExtendedMove* extMove = mExtMoveVec.address(); + U32 start = mLastMoveAck; + U32 offset; + for(offset = 0; offset < count; offset++) + if(extMove[offset].sendCount < MAX_MOVE_PACKET_SENDS) + break; + if(offset == count && count != 0) + offset--; + + start += offset; + count -= offset; + + if (count > MaxMoveCount) + count = MaxMoveCount; + bstream->writeInt(start,32); + bstream->writeInt(count,MoveCountBits); + ExtendedMove* prevExtMove = NULL; + for (int i = 0; i < count; i++) + { + extMove[offset + i].sendCount++; + extMove[offset + i].pack(bstream,prevExtMove); + bstream->writeInt(extMove[offset + i].checksum & (~(0xFFFFFFFF << Move::ChecksumBits)),Move::ChecksumBits); + prevExtMove = &extMove[offset+i]; + } +} + +void ExtendedMoveList::serverReadMovePacket(BitStream *bstream) +{ + // Server side packet read. + U32 start = bstream->readInt(32); + U32 count = bstream->readInt(MoveCountBits); + + ExtendedMove* prevExtMove = NULL; + ExtendedMove prevExtMoveHolder; + + // Skip forward (must be starting up), or over the moves + // we already have. + int skip = mLastMoveAck - start; + if (skip < 0) + { + mLastMoveAck = start; + } + else + { + if (skip > count) + skip = count; + for (int i = 0; i < skip; i++) + { + prevExtMoveHolder.unpack(bstream,prevExtMove); + prevExtMoveHolder.checksum = bstream->readInt(Move::ChecksumBits); + prevExtMove = &prevExtMoveHolder; + S32 idx = mExtMoveVec.size()-skip+i; + if (idx>=0) + { +#ifdef TORQUE_DEBUG_NET_MOVES + if (mExtMoveVec[idx].checksum != prevExtMoveHolder.checksum) + Con::printf("updated checksum on move %i from %i to %i",mExtMoveVec[idx].id,mExtMoveVec[idx].checksum,prevExtMoveHolder.checksum); +#endif + mExtMoveVec[idx].checksum = prevExtMoveHolder.checksum; + } + } + start += skip; + count = count - skip; + } + + // Put the rest on the move list. + int index = mExtMoveVec.size(); + mExtMoveVec.increment(count); + while (index < mExtMoveVec.size()) + { + mExtMoveVec[index].unpack(bstream,prevExtMove); + mExtMoveVec[index].checksum = bstream->readInt(Move::ChecksumBits); + prevExtMove = &mExtMoveVec[index]; + mExtMoveVec[index].id = start++; + index ++; + } + + mLastMoveAck += count; +} + +void ExtendedMoveList::serverWriteMovePacket(BitStream * bstream) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("ack %i minus %i",mLastMoveAck,mExtMoveVec.size()); +#endif + + // acknowledge only those moves that have been ticked + bstream->writeInt(mLastMoveAck - mExtMoveVec.size(),32); +} + +void ExtendedMoveList::clientReadMovePacket(BitStream * bstream) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("pre move ack: %i", mLastMoveAck); +#endif + + mLastMoveAck = bstream->readInt(32); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("post move ack %i, first move %i, last move %i", mLastMoveAck, mFirstMoveIndex, mLastClientMove); +#endif + + if (mLastMoveAck < mFirstMoveIndex) + mLastMoveAck = mFirstMoveIndex; + + if(mLastMoveAck > mLastClientMove) + mLastClientMove = mLastMoveAck; + while(mFirstMoveIndex < mLastMoveAck) + { + if (mExtMoveVec.size()) + { + mExtMoveVec.pop_front(); + mFirstMoveIndex++; + } + else + { + AssertWarn(1, "Popping off too many moves!"); + mFirstMoveIndex = mLastMoveAck; + } + } +} + +bool ExtendedMoveList::isBacklogged() +{ + if ( !mConnection->isConnectionToServer() ) + return false; + + return mLastClientMove - mFirstMoveIndex == mExtMoveVec.size() && + mExtMoveVec.size() >= MaxMoveCount; +} + +bool ExtendedMoveList::areMovesPending() +{ + return mConnection->isConnectionToServer() ? + mExtMoveVec.size() - mLastClientMove + mFirstMoveIndex : + mExtMoveVec.size(); +} + +void ExtendedMoveList::ackMoves(U32 count) +{ + mLastMoveAck += count; + if(mLastMoveAck > mLastClientMove) + mLastClientMove = mLastMoveAck; + while(mFirstMoveIndex < mLastMoveAck) + { + if (mExtMoveVec.size()) + { + mExtMoveVec.pop_front(); + mFirstMoveIndex++; + } + else + { + AssertWarn(1, "Popping off too many moves!"); + mFirstMoveIndex = mLastMoveAck; + } + } +} diff --git a/Engine/source/T3D/gameBase/extended/extendedMoveList.h b/Engine/source/T3D/gameBase/extended/extendedMoveList.h new file mode 100644 index 000000000..fc7b891f1 --- /dev/null +++ b/Engine/source/T3D/gameBase/extended/extendedMoveList.h @@ -0,0 +1,55 @@ +#ifndef _EXTENDEDMOVELIST_H_ +#define _EXTENDEDMOVELIST_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MOVELIST_H_ +#include "T3D/gameBase/moveList.h" +#endif + +#include "T3D/gameBase/extended/extendedMove.h" + +class ExtendedMoveList : public MoveList +{ + typedef MoveList Parent; + +public: + + ExtendedMoveList(); + + void clientWriteMovePacket(BitStream *); + void clientReadMovePacket(BitStream *); + + void serverWriteMovePacket(BitStream *); + void serverReadMovePacket(BitStream *); + + void advanceMove(); + void onAdvanceObjects() {} + U32 getMoves(Move** movePtr,U32* numMoves); + U32 getExtMoves( ExtendedMove** movePtr, U32 *numMoves ); + + void collectMove(); + void pushMove( const Move &mv ); + void pushExtMove( const ExtendedMove &mv ); + void clearMoves( U32 count ); + + virtual void reset(); + + bool isBacklogged(); + + bool areMovesPending(); + + void ackMoves( U32 count ); + +protected: + + U32 mMoveCredit; + + Vector mExtMoveVec; + +protected: + bool getNextExtMove( ExtendedMove &curMove ); +}; + +#endif // _EXTENDEDMOVELIST_H_ diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp index afd1f9e25..e3f91e160 100644 --- a/Engine/source/T3D/gameBase/gameConnection.cpp +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -41,6 +41,8 @@ #ifdef TORQUE_HIFI_NET #include "T3D/gameBase/hifi/hifiMoveList.h" +#elif defined TORQUE_EXTENDED_MOVE + #include "T3D/gameBase/extended/extendedMoveList.h" #else #include "T3D/gameBase/std/stdMoveList.h" #endif @@ -175,6 +177,8 @@ GameConnection::GameConnection() #ifdef TORQUE_HIFI_NET mMoveList = new HifiMoveList(); +#elif defined TORQUE_EXTENDED_MOVE + mMoveList = new ExtendedMoveList(); #else mMoveList = new StdMoveList(); #endif diff --git a/Engine/source/T3D/gameBase/moveList.h b/Engine/source/T3D/gameBase/moveList.h index 5b96f7953..7c68c3a2d 100644 --- a/Engine/source/T3D/gameBase/moveList.h +++ b/Engine/source/T3D/gameBase/moveList.h @@ -76,8 +76,8 @@ public: /// Reset move list back to last acknowledged move. void resetCatchup() { mLastClientMove = mLastMoveAck; } - void collectMove(); - void pushMove( const Move &mv ); + virtual void collectMove(); + virtual void pushMove( const Move &mv ); virtual void clearMoves( U32 count ); virtual void markControlDirty() { mLastClientMove = mLastMoveAck; } @@ -85,15 +85,15 @@ public: void clearMismatch() { mControlMismatch = false; } /// Clear out all moves in the list and reset to initial state. - void reset(); + virtual void reset(); /// If there are no pending moves and the input queue is full, /// then the connection to the server must be clogged. - bool isBacklogged(); + virtual bool isBacklogged(); - bool areMovesPending(); + virtual bool areMovesPending(); - void ackMoves( U32 count ); + virtual void ackMoves( U32 count ); protected: diff --git a/Engine/source/T3D/gameBase/moveManager.cpp b/Engine/source/T3D/gameBase/moveManager.cpp index e9212149b..a95164ec7 100644 --- a/Engine/source/T3D/gameBase/moveManager.cpp +++ b/Engine/source/T3D/gameBase/moveManager.cpp @@ -66,20 +66,7 @@ F32 MoveManager::mYAxis_R = 0; U32 MoveManager::mTriggerCount[MaxTriggerKeys] = { 0, }; U32 MoveManager::mPrevTriggerCount[MaxTriggerKeys] = { 0, }; -const Move NullMove = -{ - /*px=*/16, /*py=*/16, /*pz=*/16, - /*pyaw=*/0, /*ppitch=*/0, /*proll=*/0, - /*x=*/0, /*y=*/0,/*z=*/0, - /*yaw=*/0, /*pitch=*/0, /*roll=*/0, - /*id=*/0, - /*sendCount=*/0, - - /*checksum=*/false, - /*deviceIsKeyboardMouse=*/false, - /*freeLook=*/false, - /*triggers=*/{false,false,false,false,false,false} -}; +const Move NullMove; void MoveManager::init() { @@ -161,6 +148,26 @@ void MoveManager::init() } } +Move::Move() +{ + px=16; py=16; pz=16; + pyaw=0; ppitch=0; proll=0; + x=0; y=0; z=0; + yaw=0; pitch=0; roll=0; + id=0; + sendCount=0; + + checksum = false; + deviceIsKeyboardMouse = false; + freeLook = false; + trigger[0] = false; + trigger[1] = false; + trigger[2] = false; + trigger[3] = false; + trigger[4] = false; + trigger[5] = false; +} + static inline F32 clampFloatWrap(F32 val) { return val - F32(S32(val)); @@ -235,6 +242,11 @@ void Move::pack(BitStream *stream, const Move * basemove) if (!basemove) basemove = &NullMove; + packMove(stream, basemove, alwaysWriteAll); +} + +bool Move::packMove(BitStream *stream, const Move* basemove, bool alwaysWriteAll) +{ S32 i; bool triggerDifferent = false; for (i=0; i < MaxTriggerKeys; i++) @@ -272,6 +284,8 @@ void Move::pack(BitStream *stream, const Move * basemove) for(i = 0; i < MaxTriggerKeys; i++) stream->writeFlag(trigger[i]); } + + return (triggerDifferent || somethingDifferent); } void Move::unpack(BitStream *stream, const Move * basemove) @@ -280,7 +294,20 @@ void Move::unpack(BitStream *stream, const Move * basemove) if (!basemove) basemove=&NullMove; - if (alwaysReadAll || stream->readFlag()) + bool readMove = unpackMove(stream, basemove, alwaysReadAll); + if(!readMove) + *this = *basemove; +} + +bool Move::unpackMove(BitStream *stream, const Move* basemove, bool alwaysReadAll) +{ + bool readMove = alwaysReadAll; + if(!readMove) + { + readMove = stream->readFlag(); + } + + if (readMove) { pyaw = stream->readFlag() ? stream->readInt(16) : basemove->pyaw; ppitch = stream->readFlag() ? stream->readInt(16) : basemove->ppitch; @@ -297,6 +324,6 @@ void Move::unpack(BitStream *stream, const Move * basemove) trigger[i] = triggersDiffer ? stream->readFlag() : basemove->trigger[i]; unclamp(); } - else - *this = *basemove; + + return readMove; } diff --git a/Engine/source/T3D/gameBase/moveManager.h b/Engine/source/T3D/gameBase/moveManager.h index 9ee6d111c..3b8dcbc08 100644 --- a/Engine/source/T3D/gameBase/moveManager.h +++ b/Engine/source/T3D/gameBase/moveManager.h @@ -51,10 +51,16 @@ struct Move bool freeLook; bool trigger[MaxTriggerKeys]; - void pack(BitStream *stream, const Move * move = NULL); - void unpack(BitStream *stream, const Move * move = NULL); - void clamp(); - void unclamp(); + Move(); + + virtual void pack(BitStream *stream, const Move * move = NULL); + virtual void unpack(BitStream *stream, const Move * move = NULL); + virtual void clamp(); + virtual void unclamp(); + +protected: + bool packMove(BitStream *stream, const Move* basemove, bool alwaysWriteAll); + bool unpackMove(BitStream *stream, const Move* basemove, bool alwaysReadAll); }; extern const Move NullMove; diff --git a/Templates/Empty PhysX/buildFiles/config/project.conf b/Templates/Empty PhysX/buildFiles/config/project.conf index b6c4bbe63..a8ea8fa1f 100644 --- a/Templates/Empty PhysX/buildFiles/config/project.conf +++ b/Templates/Empty PhysX/buildFiles/config/project.conf @@ -6,6 +6,11 @@ // a racing game. $TORQUE_HIFI_NET = false; +// Set this to true to enable the ExtendedMove class. This +// allows the passing of absolute position and rotation input +// device information from the client to the server. +$TORQUE_EXTENDED_MOVE = false; + // Configure Torque 3D Torque3D::beginConfig( "win32", "Empty PhysX" ); diff --git a/Templates/Empty/buildFiles/config/project.conf b/Templates/Empty/buildFiles/config/project.conf index a03a244d8..af9cbfd65 100644 --- a/Templates/Empty/buildFiles/config/project.conf +++ b/Templates/Empty/buildFiles/config/project.conf @@ -6,6 +6,11 @@ // a racing game. $TORQUE_HIFI_NET = false; +// Set this to true to enable the ExtendedMove class. This +// allows the passing of absolute position and rotation input +// device information from the client to the server. +$TORQUE_EXTENDED_MOVE = false; + // Configure Torque 3D Torque3D::beginConfig( "win32", "Empty" ); diff --git a/Templates/Full PhysX/buildFiles/config/project.conf b/Templates/Full PhysX/buildFiles/config/project.conf index 18e70cf54..3eb64cfe3 100644 --- a/Templates/Full PhysX/buildFiles/config/project.conf +++ b/Templates/Full PhysX/buildFiles/config/project.conf @@ -6,6 +6,11 @@ // a racing game. $TORQUE_HIFI_NET = false; +// Set this to true to enable the ExtendedMove class. This +// allows the passing of absolute position and rotation input +// device information from the client to the server. +$TORQUE_EXTENDED_MOVE = false; + // Configure Torque 3D Torque3D::beginConfig( "win32", "Full PhysX" ); diff --git a/Templates/Full/buildFiles/config/project.conf b/Templates/Full/buildFiles/config/project.conf index 46bfe3328..3ea967451 100644 --- a/Templates/Full/buildFiles/config/project.conf +++ b/Templates/Full/buildFiles/config/project.conf @@ -6,6 +6,11 @@ // a racing game. $TORQUE_HIFI_NET = false; +// Set this to true to enable the ExtendedMove class. This +// allows the passing of absolute position and rotation input +// device information from the client to the server. +$TORQUE_EXTENDED_MOVE = false; + // Configure Torque 3D Torque3D::beginConfig( "win32", "Full" ); diff --git a/Tools/projectGenerator/modules/T3D.inc b/Tools/projectGenerator/modules/T3D.inc index 5ec6be1c5..26bc8da1d 100644 --- a/Tools/projectGenerator/modules/T3D.inc +++ b/Tools/projectGenerator/modules/T3D.inc @@ -60,11 +60,17 @@ addEngineSrcDir('T3D/gameBase'); addEngineSrcDir('T3D/turret'); global $TORQUE_HIFI_NET; +global $TORQUE_EXTENDED_MOVE; if ( $TORQUE_HIFI_NET == true ) { addProjectDefines( 'TORQUE_HIFI_NET' ); addEngineSrcDir('T3D/gameBase/hifi'); } +elseif ( $TORQUE_EXTENDED_MOVE == true ) +{ + addProjectDefines( 'TORQUE_EXTENDED_MOVE' ); + addEngineSrcDir('T3D/gameBase/extended'); +} else addEngineSrcDir('T3D/gameBase/std');