Pārlūkot izejas kodu

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.
DavidWyand-GG 12 gadi atpakaļ
vecāks
revīzija
35374f939e

+ 379 - 0
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<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
+}

+ 60 - 0
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_

+ 258 - 0
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<<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();
+}

+ 51 - 0
Engine/source/T3D/gameBase/extended/extendedMove.h

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

+ 369 - 0
Engine/source/T3D/gameBase/extended/extendedMoveList.cpp

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

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

+ 4 - 0
Engine/source/T3D/gameBase/gameConnection.cpp

@@ -41,6 +41,8 @@
 
 
 #ifdef TORQUE_HIFI_NET
 #ifdef TORQUE_HIFI_NET
    #include "T3D/gameBase/hifi/hifiMoveList.h"
    #include "T3D/gameBase/hifi/hifiMoveList.h"
+#elif defined TORQUE_EXTENDED_MOVE
+   #include "T3D/gameBase/extended/extendedMoveList.h"
 #else
 #else
    #include "T3D/gameBase/std/stdMoveList.h"
    #include "T3D/gameBase/std/stdMoveList.h"
 #endif
 #endif
@@ -175,6 +177,8 @@ GameConnection::GameConnection()
 
 
 #ifdef TORQUE_HIFI_NET
 #ifdef TORQUE_HIFI_NET
    mMoveList = new HifiMoveList();
    mMoveList = new HifiMoveList();
+#elif defined TORQUE_EXTENDED_MOVE
+   mMoveList = new ExtendedMoveList();
 #else
 #else
    mMoveList = new StdMoveList();
    mMoveList = new StdMoveList();
 #endif
 #endif

+ 6 - 6
Engine/source/T3D/gameBase/moveList.h

@@ -76,8 +76,8 @@ public:
    /// Reset move list back to last acknowledged move.
    /// Reset move list back to last acknowledged move.
    void resetCatchup() { mLastClientMove = mLastMoveAck; }
    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 clearMoves( U32 count );
 
 
    virtual void markControlDirty() { mLastClientMove = mLastMoveAck; }
    virtual void markControlDirty() { mLastClientMove = mLastMoveAck; }
@@ -85,15 +85,15 @@ public:
    void clearMismatch() { mControlMismatch = false; }
    void clearMismatch() { mControlMismatch = false; }
    
    
    /// Clear out all moves in the list and reset to initial state.
    /// 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,
    /// If there are no pending moves and the input queue is full,
    /// then the connection to the server must be clogged.
    /// 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:
 protected:
 
 

+ 44 - 17
Engine/source/T3D/gameBase/moveManager.cpp

@@ -66,20 +66,7 @@ F32 MoveManager::mYAxis_R = 0;
 U32 MoveManager::mTriggerCount[MaxTriggerKeys] = { 0, };
 U32 MoveManager::mTriggerCount[MaxTriggerKeys] = { 0, };
 U32 MoveManager::mPrevTriggerCount[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()
 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)
 static inline F32 clampFloatWrap(F32 val)
 {
 {
    return val - F32(S32(val));
    return val - F32(S32(val));
@@ -235,6 +242,11 @@ void Move::pack(BitStream *stream, const Move * basemove)
    if (!basemove)
    if (!basemove)
       basemove = &NullMove;
       basemove = &NullMove;
 
 
+   packMove(stream, basemove, alwaysWriteAll);
+}
+
+bool Move::packMove(BitStream *stream, const Move* basemove, bool alwaysWriteAll)
+{
    S32 i;
    S32 i;
    bool triggerDifferent = false;
    bool triggerDifferent = false;
    for (i=0; i < MaxTriggerKeys; i++)
    for (i=0; i < MaxTriggerKeys; i++)
@@ -272,6 +284,8 @@ void Move::pack(BitStream *stream, const Move * basemove)
          for(i = 0; i < MaxTriggerKeys; i++)
          for(i = 0; i < MaxTriggerKeys; i++)
       stream->writeFlag(trigger[i]);
       stream->writeFlag(trigger[i]);
    }
    }
+
+   return (triggerDifferent || somethingDifferent);
 }
 }
 
 
 void Move::unpack(BitStream *stream, const Move * basemove)
 void Move::unpack(BitStream *stream, const Move * basemove)
@@ -280,7 +294,20 @@ void Move::unpack(BitStream *stream, const Move * basemove)
    if (!basemove)
    if (!basemove)
       basemove=&NullMove;
       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;
       pyaw = stream->readFlag() ? stream->readInt(16) : basemove->pyaw;
       ppitch = stream->readFlag() ? stream->readInt(16) : basemove->ppitch;
       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];
          trigger[i] = triggersDiffer ? stream->readFlag() : basemove->trigger[i];
       unclamp();
       unclamp();
    }
    }
-   else
-      *this = *basemove;
+
+   return readMove;
 }
 }

+ 10 - 4
Engine/source/T3D/gameBase/moveManager.h

@@ -51,10 +51,16 @@ struct Move
    bool freeLook;
    bool freeLook;
    bool trigger[MaxTriggerKeys];
    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;
 extern const Move NullMove;

+ 5 - 0
Templates/Empty PhysX/buildFiles/config/project.conf

@@ -6,6 +6,11 @@
 // a racing game.
 // a racing game.
 $TORQUE_HIFI_NET = false;
 $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
 // Configure Torque 3D
 Torque3D::beginConfig( "win32", "Empty PhysX" );
 Torque3D::beginConfig( "win32", "Empty PhysX" );
 
 

+ 5 - 0
Templates/Empty/buildFiles/config/project.conf

@@ -6,6 +6,11 @@
 // a racing game.
 // a racing game.
 $TORQUE_HIFI_NET = false;
 $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
 // Configure Torque 3D
 Torque3D::beginConfig( "win32", "Empty" );
 Torque3D::beginConfig( "win32", "Empty" );
 
 

+ 5 - 0
Templates/Full PhysX/buildFiles/config/project.conf

@@ -6,6 +6,11 @@
 // a racing game.
 // a racing game.
 $TORQUE_HIFI_NET = false;
 $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
 // Configure Torque 3D
 Torque3D::beginConfig( "win32", "Full PhysX" );
 Torque3D::beginConfig( "win32", "Full PhysX" );
 
 

+ 5 - 0
Templates/Full/buildFiles/config/project.conf

@@ -6,6 +6,11 @@
 // a racing game.
 // a racing game.
 $TORQUE_HIFI_NET = false;
 $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
 // Configure Torque 3D
 Torque3D::beginConfig( "win32", "Full" );
 Torque3D::beginConfig( "win32", "Full" );
 
 

+ 6 - 0
Tools/projectGenerator/modules/T3D.inc

@@ -60,11 +60,17 @@ addEngineSrcDir('T3D/gameBase');
 addEngineSrcDir('T3D/turret');
 addEngineSrcDir('T3D/turret');
 
 
 global $TORQUE_HIFI_NET;
 global $TORQUE_HIFI_NET;
+global $TORQUE_EXTENDED_MOVE;
 if ( $TORQUE_HIFI_NET == true )
 if ( $TORQUE_HIFI_NET == true )
 {
 {
    addProjectDefines( 'TORQUE_HIFI_NET' );
    addProjectDefines( 'TORQUE_HIFI_NET' );
    addEngineSrcDir('T3D/gameBase/hifi');
    addEngineSrcDir('T3D/gameBase/hifi');
 }
 }
+elseif ( $TORQUE_EXTENDED_MOVE == true )
+{
+   addProjectDefines( 'TORQUE_EXTENDED_MOVE' );
+   addEngineSrcDir('T3D/gameBase/extended');
+}
 else
 else
 	addEngineSrcDir('T3D/gameBase/std');
 	addEngineSrcDir('T3D/gameBase/std');