From 35374f939e4d4ccb685a2cb3b4094c95ab1f9ba3 Mon Sep 17 00:00:00 2001 From: DavidWyand-GG Date: Wed, 23 Jan 2013 01:52:46 -0500 Subject: [PATCH] ExtendedMove class and support The ExtendedMove class can optionally replace the standard Move class to allow the passing of absolute position and rotation information from the client's input device to the server. It is enabled by changing $TORQUE_EXTENDED_MOVE to true in buildFiles/config/project.conf and re-running the project generator. --- .../gameBase/extended/extendedGameProcess.cpp | 379 ++++++++++++++++++ .../gameBase/extended/extendedGameProcess.h | 60 +++ .../T3D/gameBase/extended/extendedMove.cpp | 258 ++++++++++++ .../T3D/gameBase/extended/extendedMove.h | 51 +++ .../gameBase/extended/extendedMoveList.cpp | 369 +++++++++++++++++ .../T3D/gameBase/extended/extendedMoveList.h | 55 +++ Engine/source/T3D/gameBase/gameConnection.cpp | 4 + Engine/source/T3D/gameBase/moveList.h | 12 +- Engine/source/T3D/gameBase/moveManager.cpp | 61 ++- Engine/source/T3D/gameBase/moveManager.h | 14 +- .../buildFiles/config/project.conf | 5 + .../Empty/buildFiles/config/project.conf | 5 + .../Full PhysX/buildFiles/config/project.conf | 5 + Templates/Full/buildFiles/config/project.conf | 5 + Tools/projectGenerator/modules/T3D.inc | 6 + 15 files changed, 1262 insertions(+), 27 deletions(-) create mode 100644 Engine/source/T3D/gameBase/extended/extendedGameProcess.cpp create mode 100644 Engine/source/T3D/gameBase/extended/extendedGameProcess.h create mode 100644 Engine/source/T3D/gameBase/extended/extendedMove.cpp create mode 100644 Engine/source/T3D/gameBase/extended/extendedMove.h create mode 100644 Engine/source/T3D/gameBase/extended/extendedMoveList.cpp create mode 100644 Engine/source/T3D/gameBase/extended/extendedMoveList.h 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');