mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 09:04:38 +00:00
Merge pull request #221 from DavidWyand-GG/ExtendedMove
ExtendedMove class and support
This commit is contained in:
commit
2112878f30
15 changed files with 1262 additions and 27 deletions
379
Engine/source/T3D/gameBase/extended/extendedGameProcess.cpp
Normal file
379
Engine/source/T3D/gameBase/extended/extendedGameProcess.cpp
Normal 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
|
||||||
|
}
|
||||||
60
Engine/source/T3D/gameBase/extended/extendedGameProcess.h
Normal file
60
Engine/source/T3D/gameBase/extended/extendedGameProcess.h
Normal 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_
|
||||||
258
Engine/source/T3D/gameBase/extended/extendedMove.cpp
Normal file
258
Engine/source/T3D/gameBase/extended/extendedMove.cpp
Normal 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();
|
||||||
|
}
|
||||||
51
Engine/source/T3D/gameBase/extended/extendedMove.h
Normal file
51
Engine/source/T3D/gameBase/extended/extendedMove.h
Normal 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_
|
||||||
369
Engine/source/T3D/gameBase/extended/extendedMoveList.cpp
Normal file
369
Engine/source/T3D/gameBase/extended/extendedMoveList.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
Engine/source/T3D/gameBase/extended/extendedMoveList.h
Normal file
55
Engine/source/T3D/gameBase/extended/extendedMoveList.h
Normal 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_
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
virtual void collectMove();
|
||||||
void pushMove( const Move &mv );
|
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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
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}
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,16 @@ struct Move
|
||||||
bool freeLook;
|
bool freeLook;
|
||||||
bool trigger[MaxTriggerKeys];
|
bool trigger[MaxTriggerKeys];
|
||||||
|
|
||||||
void pack(BitStream *stream, const Move * move = NULL);
|
Move();
|
||||||
void unpack(BitStream *stream, const Move * move = NULL);
|
|
||||||
void clamp();
|
virtual void pack(BitStream *stream, const Move * move = NULL);
|
||||||
void unclamp();
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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" );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue