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.
This commit is contained in:
DavidWyand-GG 2013-01-23 01:52:46 -05:00
parent 404cffc11a
commit 35374f939e
15 changed files with 1262 additions and 27 deletions

View file

@ -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<SceneObject> safePtr = static_cast<SceneObject*>( 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<ExtendedMoveList*>(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<GameBase> control = connection->getControlObject();
if ( control )
{
ExtendedMove * extMovePtr;
U32 numMoves;
U32 m = 0;
ExtendedMoveList* extMoveList = static_cast<ExtendedMoveList*>(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<GameBase> 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<ExtendedMoveList*>(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<GameBase> obj = getGameBase( pobj );
ExtendedMove* extMovePtr;
U32 m, numMoves;
ExtendedMoveList* extMoveList = static_cast<ExtendedMoveList*>(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<GameConnection *>(*i))
{
con->mMoveList->advanceMove();
}
}
#ifdef TORQUE_DEBUG_NET_MOVES
Con::printf("---------");
#endif
}

View file

@ -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_

View file

@ -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<<MaxRotationBits)-1))
#define UNCLAMPROT(x) ((F32)(x * 2 / F32((1 << MaxRotationBits) - 1) - 1.0f))
ExtendedMove::ExtendedMove() : Move()
{
for(U32 i=0; i<MaxPositionsRotations; ++i)
{
posX[i] = 0;
posY[i] = 0;
posZ[i] = 0;
rotX[i] = 0;
rotY[i] = 0;
rotZ[i] = 0;
rotW[i] = 1;
}
}
void ExtendedMove::pack(BitStream *stream, const Move * basemove)
{
bool alwaysWriteAll = basemove!=NULL;
if (!basemove)
basemove = &NullExtendedMove;
// Write the standard Move stuff
packMove(stream, basemove, alwaysWriteAll);
// Extended Move
const ExtendedMove* extBaseMove = static_cast<const ExtendedMove*>(basemove);
bool extendedDifferent = false;
for(U32 i=0; i<MaxPositionsRotations; ++i)
{
bool check = (posX[i] != extBaseMove->posX[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; i<MaxPositionsRotations; ++i)
{
// Position
if(stream->writeFlag(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<const ExtendedMove*>(basemove);
if (alwaysReadAll || stream->readFlag())
{
isBaseMove = false;
for(U32 i=0; i<MaxPositionsRotations; ++i)
{
// Position
if(stream->readFlag())
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<MaxPositionsRotations; ++i)
{
// Positions
posX[i] = CLAMPPOS(posX[i]);
posY[i] = CLAMPPOS(posY[i]);
posZ[i] = CLAMPPOS(posZ[i]);
// Rotations
crotX[i] = CLAMPROT(rotX[i]);
crotY[i] = CLAMPROT(rotY[i]);
crotZ[i] = CLAMPROT(rotZ[i]);
crotW[i] = CLAMPROT(rotW[i]);
}
// Perform the standard Move clamp
Parent::clamp();
}
void ExtendedMove::unclamp()
{
// Unclamp the values the same as for net traffic so the client matches the server
for(U32 i=0; i<MaxPositionsRotations; ++i)
{
// Rotations
rotX[i] = UNCLAMPROT(crotX[i]);
rotY[i] = UNCLAMPROT(crotY[i]);
rotZ[i] = UNCLAMPROT(crotZ[i]);
rotW[i] = UNCLAMPROT(crotW[i]);
}
// Perform the standard Move unclamp
Parent::unclamp();
}

View file

@ -0,0 +1,51 @@
#ifndef _EXTENDEDMOVE_H_
#define _EXTENDEDMOVE_H_
#include "T3D/gameBase/moveManager.h"
#include "math/mQuat.h"
struct ExtendedMove : public Move
{
typedef Move Parent;
enum Constants {
MaxPositionsRotations = 2,
MaxPositionBits = 13,
MaxRotationBits = 11,
};
// Position is in millimeters
S32 posX[MaxPositionsRotations], posY[MaxPositionsRotations], posZ[MaxPositionsRotations];
F32 rotX[MaxPositionsRotations], rotY[MaxPositionsRotations], rotZ[MaxPositionsRotations], rotW[MaxPositionsRotations];
// Network clamped rotation
S32 crotX[MaxPositionsRotations], crotY[MaxPositionsRotations], crotZ[MaxPositionsRotations], crotW[MaxPositionsRotations];
ExtendedMove();
virtual void pack(BitStream *stream, const Move * move = NULL);
virtual void unpack(BitStream *stream, const Move * move = NULL);
virtual void clamp();
virtual void unclamp();
};
extern const ExtendedMove NullExtendedMove;
class ExtendedMoveManager
{
public:
static S32 mPosX[ExtendedMove::MaxPositionsRotations];
static S32 mPosY[ExtendedMove::MaxPositionsRotations];
static S32 mPosZ[ExtendedMove::MaxPositionsRotations];
static F32 mRotAX[ExtendedMove::MaxPositionsRotations];
static F32 mRotAY[ExtendedMove::MaxPositionsRotations];
static F32 mRotAZ[ExtendedMove::MaxPositionsRotations];
static F32 mRotAA[ExtendedMove::MaxPositionsRotations];
static void init();
};
#endif // _EXTENDEDMOVE_H_

View file

@ -0,0 +1,369 @@
#include "platform/platform.h"
#include "T3D/gameBase/extended/extendedMoveList.h"
#include "T3D/gameBase/gameConnection.h"
#include "core/stream/bitStream.h"
#define MAX_MOVE_PACKET_SENDS 4
ExtendedMoveList::ExtendedMoveList()
{
mMoveCredit = MaxMoveCount;
mControlMismatch = false;
reset();
}
void ExtendedMoveList::reset()
{
mLastMoveAck = 0;
mLastClientMove = 0;
mFirstMoveIndex = 0;
mExtMoveVec.clear();
}
bool ExtendedMoveList::getNextExtMove( ExtendedMove &curMove )
{
if ( mExtMoveVec.size() > 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; i<ExtendedMove::MaxPositionsRotations; ++i)
{
// Process position
curMove.posX[i] = ExtendedMoveManager::mPosX[i];
curMove.posY[i] = ExtendedMoveManager::mPosY[i];
curMove.posZ[i] = ExtendedMoveManager::mPosZ[i];
// Process rotation. Rotation is passed in as an Angle Axis in degrees.
// We need to convert this into a Quat.
QuatF q(Point3F(ExtendedMoveManager::mRotAX[i], ExtendedMoveManager::mRotAY[i], ExtendedMoveManager::mRotAZ[i]), mDegToRad(ExtendedMoveManager::mRotAA[i]));
curMove.rotX[i] = q.x;
curMove.rotY[i] = q.y;
curMove.rotZ[i] = q.z;
curMove.rotW[i] = q.w;
}
if (mConnection->getControlObject())
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<const ExtendedMove*>(&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; i<count; i++)
if (mExtMoveVec[i].checksum == Move::ChecksumMismatch)
mControlMismatch = true;
else
mControlMismatch = false;
if (count == mExtMoveVec.size())
mExtMoveVec.clear();
else
while (count--)
mExtMoveVec.pop_front();
}
}
void ExtendedMoveList::advanceMove()
{
AssertFatal(!mConnection->isConnectionToServer(), "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;
}
}
}

View file

@ -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<ExtendedMove> mExtMoveVec;
protected:
bool getNextExtMove( ExtendedMove &curMove );
};
#endif // _EXTENDEDMOVELIST_H_

View file

@ -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

View file

@ -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:

View file

@ -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;
}

View file

@ -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;

View file

@ -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" );

View file

@ -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" );

View file

@ -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" );

View file

@ -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" );

View file

@ -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');