tge/engine/game/gameProcess.cc
2025-02-17 23:17:30 -06:00

246 lines
7.6 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "core/dnet.h"
#include "game/gameConnection.h"
#include "game/gameBase.h"
#include "game/shapeBase.h"
#include "platform/profiler.h"
#include "console/consoleTypes.h"
//----------------------------------------------------------------------------
ProcessList gClientProcessList(false);
ProcessList gServerProcessList(true);
bool ProcessList::mDebugControlSync = false;
ProcessList::ProcessList(bool isServer)
{
mDirty = false;
mCurrentTag = 0;
mLastTick = 0;
mLastTime = 0;
mLastDelta = 0;
mIsServer = isServer;
// Con::addVariable("debugControlSync",TypeBool, &mDebugControlSync);
}
//----------------------------------------------------------------------------
void ProcessList::orderList()
{
// GameBase tags are intialized to 0, so current tag
// should never be 0.
if (!++mCurrentTag)
mCurrentTag++;
// Install a temporary head node
GameBase list;
list.plLinkBefore(head.mProcessLink.next);
head.plUnlink();
// Reverse topological sort into the orignal head node
while (list.mProcessLink.next != &list) {
GameBase* ptr = list.mProcessLink.next;
ptr->mProcessTag = mCurrentTag;
ptr->plUnlink();
if (ptr->mAfterObject) {
// Build chain "stack" of dependant objects and patch
// it to the end of the current list.
while (bool(ptr->mAfterObject) &&
ptr->mAfterObject->mProcessTag != mCurrentTag) {
ptr->mAfterObject->mProcessTag = mCurrentTag;
ptr->mAfterObject->plUnlink();
ptr->mAfterObject->plLinkBefore(ptr);
ptr = ptr->mAfterObject;
}
ptr->plJoin(&head);
}
else
ptr->plLinkBefore(&head);
}
mDirty = false;
}
//----------------------------------------------------------------------------
bool ProcessList::advanceServerTime(SimTime timeDelta)
{
PROFILE_START(AdvanceServerTime);
if (mDirty) orderList();
SimTime targetTime = mLastTime + timeDelta;
SimTime targetTick = targetTime & ~TickMask;
SimTime tickCount = (targetTick - mLastTick) >> TickShift;
bool ret = mLastTick != targetTick;
// Advance all the objects
for (; mLastTick != targetTick; mLastTick += TickMs)
advanceObjects();
// Credit all the connections with the elapsed ticks.
SimGroup *g = Sim::getClientGroup();
for (SimGroup::iterator i = g->begin(); i != g->end(); i++)
if (GameConnection *t = dynamic_cast<GameConnection *>(*i))
t->incMoveCredit(tickCount);
mLastTime = targetTime;
PROFILE_END();
return ret;
}
//----------------------------------------------------------------------------
bool ProcessList::advanceClientTime(SimTime timeDelta)
{
PROFILE_START(AdvanceClientTime);
if (mDirty) orderList();
SimTime targetTime = mLastTime + timeDelta;
SimTime targetTick = (targetTime + TickMask) & ~TickMask;
SimTime tickCount = (targetTick - mLastTick) >> TickShift;
// See if the control object has pending moves.
GameBase* control = 0;
GameConnection* connection = GameConnection::getConnectionToServer();
if (connection)
{
// If the connection to the server is backlogged
// the simulation is frozen.
if (connection->isBacklogged()) {
mLastTime = targetTime;
mLastTick = targetTick;
PROFILE_END();
return false;
}
if (connection->areMovesPending())
control = connection->getControlObject();
}
// If we are going to tick, or have moves pending for the
// control object, we need to reset everyone back to their
// last full tick pos.
if (mLastDelta && (tickCount || control))
for (GameBase* obj = head.mProcessLink.next; obj != &head;
obj = obj->mProcessLink.next)
if (obj->mProcessTick)
obj->interpolateTick(0);
// Produce new moves and advance all the objects
if (tickCount)
{
for (; mLastTick != targetTick; mLastTick += TickMs)
{
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)
{
PROFILE_END();
return true;
}
} while(blockType != GameConnection::BlockTypeMove);
}
connection->collectMove(mLastTick);
}
advanceObjects();
}
}
else
if (control)
{
// Sync up the control object with the latest client moves.
Move* movePtr;
U32 m = 0, numMoves;
connection->getMoveList(&movePtr, &numMoves);
while (m < numMoves)
{
control->processTick(&movePtr[m++]);
}
connection->clearMoves(m);
}
mLastDelta = (TickMs - (targetTime & TickMask)) & TickMask;
F32 dt = mLastDelta / F32(TickMs);
for (GameBase* obj = head.mProcessLink.next; obj != &head;
obj = obj->mProcessLink.next)
if (obj->mProcessTick)
obj->interpolateTick(dt);
// Inform objects of total elapsed delta so they can advance
// client side animations.
dt = F32(timeDelta) / 1000;
for (GameBase* obj = head.mProcessLink.next; obj != &head;
obj = obj->mProcessLink.next)
obj->advanceTime(dt);
mLastTime = targetTime;
PROFILE_END();
return tickCount != 0;
}
//----------------------------------------------------------------------------
void ProcessList::advanceObjects()
{
PROFILE_START(AdvanceObjects);
// A little link list shuffling is done here to avoid problems
// with objects being deleted from within the process method.
GameBase list;
GameBase* obj;
list.plLinkBefore(head.mProcessLink.next);
head.plUnlink();
while ((obj = list.mProcessLink.next) != &list) {
obj->plUnlink();
obj->plLinkBefore(&head);
// Each object is either advanced a single tick, or if it's
// being controlled by a client, ticked once for each pending move.
if (obj->mTypeMask & GameBaseObjectType) {
GameBase *pGB = static_cast<GameBase *>(obj);
GameConnection* con = pGB->getControllingClient();
if (con && con->getControlObject() == pGB) {
Move* movePtr;
U32 m, numMoves;
con->getMoveList(&movePtr, &numMoves);
for (m = 0; m < numMoves && pGB->getControllingClient() == con; )
obj->processTick(&movePtr[m++]);
con->clearMoves(m);
continue;
}
}
if (obj->mProcessTick)
obj->processTick(0);
}
PROFILE_END();
}