mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
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.
380 lines
11 KiB
C++
380 lines
11 KiB
C++
#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
|
|
}
|