1485 lines
41 KiB
C++
Executable File
1485 lines
41 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platform.h"
|
|
#include "core/dnet.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "console/simBase.h"
|
|
#include "core/bitStream.h"
|
|
#include "sim/pathManager.h"
|
|
#include "sceneGraph/sceneGraph.h"
|
|
#include "sceneGraph/sceneLighting.h"
|
|
#include "audio/audioDataBlock.h"
|
|
#include "game/game.h"
|
|
#include "game/shapeBase.h"
|
|
#include "game/gameConnection.h"
|
|
#include "game/gameConnectionEvents.h"
|
|
#include "game/auth.h"
|
|
#include "util/safeDelete.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
#define MAX_MOVE_PACKET_SENDS 4
|
|
|
|
#define ControlRequestTime 5000
|
|
|
|
const U32 GameConnection::CurrentProtocolVersion = 12;
|
|
const U32 GameConnection::MinRequiredProtocolVersion = 12;
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_CONOBJECT(GameConnection);
|
|
S32 GameConnection::mLagThresholdMS = 0;
|
|
|
|
//----------------------------------------------------------------------------
|
|
GameConnection::GameConnection()
|
|
{
|
|
mLagging = false;
|
|
mControlObject = NULL;
|
|
mCameraObject = NULL;
|
|
mLastMoveAck = 0;
|
|
mLastClientMove = 0;
|
|
mFirstMoveIndex = 0;
|
|
mMoveCredit = MaxMoveCount;
|
|
mDataBlockModifiedKey = 0;
|
|
mMaxDataBlockModifiedKey = 0;
|
|
mAuthInfo = NULL;
|
|
mLastControlObjectChecksum = 0;
|
|
mConnectArgc = 0;
|
|
for(U32 i = 0; i < MaxConnectArgs; i++)
|
|
mConnectArgv[i] = 0;
|
|
|
|
mJoinPassword = NULL;
|
|
|
|
mMissionCRC = 0xffffffff;
|
|
|
|
mDamageFlash = mWhiteOut = 0;
|
|
|
|
mCameraPos = 0;
|
|
mCameraSpeed = 10;
|
|
|
|
mCameraFov = 90.f;
|
|
mUpdateCameraFov = false;
|
|
|
|
mAIControlled = false;
|
|
|
|
mDisconnectReason[0] = 0;
|
|
|
|
//blackout vars
|
|
mBlackOut = 0.0f;
|
|
mBlackOutTimeMS = 0;
|
|
mBlackOutStartTimeMS = 0;
|
|
mFadeToBlack = false;
|
|
|
|
// first person
|
|
mFirstPerson = true;
|
|
mUpdateFirstPerson = false;
|
|
}
|
|
|
|
GameConnection::~GameConnection()
|
|
{
|
|
delete mAuthInfo;
|
|
for(U32 i = 0; i < mConnectArgc; i++)
|
|
dFree(mConnectArgv[i]);
|
|
dFree(mJoinPassword);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool GameConnection::canRemoteCreate()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void GameConnection::setConnectArgs(U32 argc, const char **argv)
|
|
{
|
|
if(argc > MaxConnectArgs)
|
|
argc = MaxConnectArgs;
|
|
mConnectArgc = argc;
|
|
for(U32 i = 0; i < mConnectArgc; i++)
|
|
mConnectArgv[i] = dStrdup(argv[i]);
|
|
}
|
|
|
|
void GameConnection::setJoinPassword(const char *password)
|
|
{
|
|
mJoinPassword = dStrdup(password);
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, setJoinPassword, void, 3, 3, "")
|
|
{
|
|
object->setJoinPassword(argv[2]);
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, setConnectArgs, void, 3, 17, "")
|
|
{
|
|
object->setConnectArgs(argc - 2, argv + 2);
|
|
}
|
|
|
|
void GameConnection::onTimedOut()
|
|
{
|
|
if(isConnectionToServer())
|
|
{
|
|
Con::printf("Connection to server timed out");
|
|
Con::executef(this, 1, "onConnectionTimedOut");
|
|
}
|
|
else
|
|
{
|
|
Con::printf("Client %d timed out.", getId());
|
|
setDisconnectReason("TimedOut");
|
|
}
|
|
|
|
}
|
|
|
|
void GameConnection::onConnectionEstablished(bool isInitiator)
|
|
{
|
|
if(isInitiator)
|
|
{
|
|
setGhostFrom(false);
|
|
setGhostTo(true);
|
|
setSendingEvents(true);
|
|
setTranslatesStrings(true);
|
|
setIsConnectionToServer();
|
|
mServerConnection = this;
|
|
Con::printf("Connection established %d", getId());
|
|
Con::executef(this, 1, "onConnectionAccepted");
|
|
}
|
|
else
|
|
{
|
|
setGhostFrom(true);
|
|
setGhostTo(false);
|
|
setSendingEvents(true);
|
|
setTranslatesStrings(true);
|
|
Sim::getClientGroup()->addObject(this);
|
|
|
|
const char *argv[MaxConnectArgs + 2];
|
|
argv[0] = "onConnect";
|
|
for(U32 i = 0; i < mConnectArgc; i++)
|
|
argv[i + 2] = mConnectArgv[i];
|
|
Con::execute(this, mConnectArgc + 2, argv);
|
|
}
|
|
}
|
|
|
|
void GameConnection::onConnectTimedOut()
|
|
{
|
|
Con::executef(this, 1, "onConnectRequestTimedOut");
|
|
}
|
|
|
|
void GameConnection::onDisconnect(const char *reason)
|
|
{
|
|
if(isConnectionToServer())
|
|
{
|
|
Con::printf("Connection with server lost.");
|
|
Con::executef(this, 2, "onConnectionDropped", reason);
|
|
}
|
|
else
|
|
{
|
|
Con::printf("Client %d disconnected.", getId());
|
|
setDisconnectReason(reason);
|
|
}
|
|
}
|
|
|
|
void GameConnection::onConnectionRejected(const char *reason)
|
|
{
|
|
Con::executef(this, 2, "onConnectRequestRejected", reason);
|
|
}
|
|
|
|
void GameConnection::handleStartupError(const char *errorString)
|
|
{
|
|
Con::executef(this, 2, "onConnectRequestRejected", errorString);
|
|
}
|
|
|
|
void GameConnection::writeConnectAccept(BitStream *stream)
|
|
{
|
|
Parent::writeConnectAccept(stream);
|
|
stream->write(getProtocolVersion());
|
|
}
|
|
|
|
bool GameConnection::readConnectAccept(BitStream *stream, const char **errorString)
|
|
{
|
|
if(!Parent::readConnectAccept(stream, errorString))
|
|
return false;
|
|
|
|
U32 protocolVersion;
|
|
stream->read(&protocolVersion);
|
|
if(protocolVersion < MinRequiredProtocolVersion || protocolVersion > CurrentProtocolVersion)
|
|
{
|
|
*errorString = "CHR_PROTOCOL"; // this should never happen unless someone is faking us out.
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void GameConnection::writeConnectRequest(BitStream *stream)
|
|
{
|
|
Parent::writeConnectRequest(stream);
|
|
stream->writeString(GameString);
|
|
stream->write(CurrentProtocolVersion);
|
|
stream->write(MinRequiredProtocolVersion);
|
|
stream->writeString(mJoinPassword);
|
|
|
|
stream->write(mConnectArgc);
|
|
for(U32 i = 0; i < mConnectArgc; i++)
|
|
stream->writeString(mConnectArgv[i]);
|
|
}
|
|
|
|
bool GameConnection::readConnectRequest(BitStream *stream, const char **errorString)
|
|
{
|
|
if(!Parent::readConnectRequest(stream, errorString))
|
|
return false;
|
|
U32 currentProtocol, minProtocol;
|
|
char gameString[256];
|
|
stream->readString(gameString);
|
|
if(dStrcmp(gameString, GameString))
|
|
{
|
|
*errorString = "CHR_GAME";
|
|
return false;
|
|
}
|
|
|
|
stream->read(¤tProtocol);
|
|
stream->read(&minProtocol);
|
|
|
|
char joinPassword[256];
|
|
stream->readString(joinPassword);
|
|
|
|
if(currentProtocol < MinRequiredProtocolVersion)
|
|
{
|
|
*errorString = "CHR_PROTOCOL_LESS";
|
|
return false;
|
|
}
|
|
if(minProtocol > CurrentProtocolVersion)
|
|
{
|
|
*errorString = "CHR_PROTOCOL_GREATER";
|
|
return false;
|
|
}
|
|
setProtocolVersion(currentProtocol < CurrentProtocolVersion ? currentProtocol : CurrentProtocolVersion);
|
|
|
|
const char *serverPassword = Con::getVariable("Pref::Server::Password");
|
|
if(serverPassword[0])
|
|
{
|
|
if(dStrcmp(joinPassword, serverPassword))
|
|
{
|
|
*errorString = "CHR_PASSWORD";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
stream->read(&mConnectArgc);
|
|
if(mConnectArgc > MaxConnectArgs)
|
|
{
|
|
*errorString = "CR_INVALID_ARGS";
|
|
return false;
|
|
}
|
|
const char *connectArgv[MaxConnectArgs + 3];
|
|
for(U32 i = 0; i < mConnectArgc; i++)
|
|
{
|
|
char argString[256];
|
|
stream->readString(argString);
|
|
mConnectArgv[i] = dStrdup(argString);
|
|
connectArgv[i + 3] = mConnectArgv[i];
|
|
}
|
|
connectArgv[0] = "onConnectRequest";
|
|
char buffer[256];
|
|
Net::addressToString(getNetAddress(), buffer);
|
|
connectArgv[2] = buffer;
|
|
|
|
const char *ret = Con::execute(this, mConnectArgc + 3, connectArgv);
|
|
if(ret[0])
|
|
{
|
|
*errorString = ret;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------
|
|
void GameConnection::connectionError(const char *errorString)
|
|
{
|
|
if(isConnectionToServer())
|
|
{
|
|
Con::printf("Connection error: %s.", errorString);
|
|
Con::executef(this, 2, "onConnectionError", errorString);
|
|
}
|
|
else
|
|
{
|
|
Con::printf("Client %d packet error: %s.", getId(), errorString);
|
|
setDisconnectReason("Packet Error.");
|
|
}
|
|
deleteObject();
|
|
}
|
|
|
|
|
|
void GameConnection::setAuthInfo(const AuthInfo *info)
|
|
{
|
|
mAuthInfo = new AuthInfo;
|
|
*mAuthInfo = *info;
|
|
}
|
|
|
|
const AuthInfo *GameConnection::getAuthInfo()
|
|
{
|
|
return mAuthInfo;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void GameConnection::setControlObject(ShapeBase *obj)
|
|
{
|
|
if(mControlObject == obj)
|
|
return;
|
|
|
|
if(mControlObject && mControlObject != mCameraObject)
|
|
mControlObject->setControllingClient(0);
|
|
|
|
if(obj)
|
|
{
|
|
// Nothing else is permitted to control this object.
|
|
if (ShapeBase* coo = obj->getControllingObject())
|
|
coo->setControlObject(0);
|
|
if (GameConnection *con = obj->getControllingClient())
|
|
{
|
|
if(this != con)
|
|
{
|
|
// was it controlled via camera or control
|
|
if(con->getControlObject() == obj)
|
|
con->setControlObject(0);
|
|
else
|
|
con->setCameraObject(0);
|
|
}
|
|
}
|
|
|
|
// We are now the controlling client of this object.
|
|
obj->setControllingClient(this);
|
|
}
|
|
|
|
// Okay, set our control object.
|
|
mControlObject = obj;
|
|
if(mCameraObject.isNull())
|
|
setScopeObject(mControlObject);
|
|
}
|
|
|
|
void GameConnection::setCameraObject(ShapeBase *obj)
|
|
{
|
|
if(mCameraObject == obj)
|
|
return;
|
|
|
|
if(mCameraObject && mCameraObject != mControlObject)
|
|
mCameraObject->setControllingClient(0);
|
|
|
|
if(obj)
|
|
{
|
|
// nothing else is permitted to control this object
|
|
if(ShapeBase *coo = obj->getControllingObject())
|
|
coo->setControlObject(0);
|
|
|
|
if(GameConnection *con = obj->getControllingClient())
|
|
{
|
|
if(this != con)
|
|
{
|
|
// was it controlled via camera or control
|
|
if(con->getControlObject() == obj)
|
|
con->setControlObject(0);
|
|
else
|
|
con->setCameraObject(0);
|
|
}
|
|
}
|
|
|
|
// we are now the controlling client of this object
|
|
obj->setControllingClient(this);
|
|
}
|
|
|
|
// Okay, set our camera object.
|
|
mCameraObject = obj;
|
|
|
|
if(mCameraObject.isNull())
|
|
setScopeObject(mControlObject);
|
|
else
|
|
{
|
|
setScopeObject(mCameraObject);
|
|
|
|
// if this is a client then set the fov and active image
|
|
if(isConnectionToServer())
|
|
{
|
|
F32 fov = mCameraObject->getDefaultCameraFov();
|
|
GameSetCameraFov(fov);
|
|
}
|
|
}
|
|
}
|
|
|
|
ShapeBase* GameConnection::getCameraObject()
|
|
{
|
|
// If there is no camera object, or if we're first person, return
|
|
// the control object.
|
|
if( !mControlObject.isNull() && (mCameraObject.isNull() || mFirstPerson))
|
|
return mControlObject;
|
|
return mCameraObject;
|
|
}
|
|
|
|
static S32 sChaseQueueSize = 0;
|
|
static MatrixF* sChaseQueue = 0;
|
|
static S32 sChaseQueueHead = 0;
|
|
static S32 sChaseQueueTail = 0;
|
|
|
|
bool GameConnection::getControlCameraTransform(F32 dt, MatrixF* mat)
|
|
{
|
|
ShapeBase* obj = getCameraObject();
|
|
if(!obj)
|
|
return false;
|
|
|
|
ShapeBase* cObj = obj;
|
|
while((cObj = cObj->getControlObject()) != 0)
|
|
{
|
|
if(cObj->useObjsEyePoint())
|
|
obj = cObj;
|
|
}
|
|
|
|
if (dt)
|
|
{
|
|
if (mFirstPerson || obj->onlyFirstPerson())
|
|
{
|
|
if (mCameraPos > 0)
|
|
if ((mCameraPos -= mCameraSpeed * dt) <= 0)
|
|
mCameraPos = 0;
|
|
}
|
|
else
|
|
{
|
|
if (mCameraPos < 1)
|
|
if ((mCameraPos += mCameraSpeed * dt) > 1)
|
|
mCameraPos = 1;
|
|
}
|
|
}
|
|
|
|
if (!sChaseQueueSize || mFirstPerson || obj->onlyFirstPerson())
|
|
obj->getCameraTransform(&mCameraPos,mat);
|
|
else
|
|
{
|
|
MatrixF& hm = sChaseQueue[sChaseQueueHead];
|
|
MatrixF& tm = sChaseQueue[sChaseQueueTail];
|
|
obj->getCameraTransform(&mCameraPos,&hm);
|
|
*mat = tm;
|
|
if (dt)
|
|
{
|
|
if ((sChaseQueueHead += 1) >= sChaseQueueSize)
|
|
sChaseQueueHead = 0;
|
|
if (sChaseQueueHead == sChaseQueueTail)
|
|
if ((sChaseQueueTail += 1) >= sChaseQueueSize)
|
|
sChaseQueueTail = 0;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GameConnection::getControlCameraFov(F32 * fov)
|
|
{
|
|
//find the last control object in the chain (client->player->turret->whatever...)
|
|
ShapeBase *obj = getCameraObject();
|
|
ShapeBase *cObj = NULL;
|
|
while (obj)
|
|
{
|
|
cObj = obj;
|
|
obj = obj->getControlObject();
|
|
}
|
|
if (cObj)
|
|
{
|
|
*fov = cObj->getCameraFov();
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
bool GameConnection::isValidControlCameraFov(F32 fov)
|
|
{
|
|
//find the last control object in the chain (client->player->turret->whatever...)
|
|
ShapeBase *obj = getCameraObject();
|
|
ShapeBase *cObj = NULL;
|
|
while (obj)
|
|
{
|
|
cObj = obj;
|
|
obj = obj->getControlObject();
|
|
}
|
|
|
|
return cObj ? cObj->isValidCameraFov(fov) : NULL;
|
|
}
|
|
|
|
bool GameConnection::setControlCameraFov(F32 fov)
|
|
{
|
|
//find the last control object in the chain (client->player->turret->whatever...)
|
|
ShapeBase *obj = getCameraObject();
|
|
ShapeBase *cObj = NULL;
|
|
while (obj)
|
|
{
|
|
cObj = obj;
|
|
obj = obj->getControlObject();
|
|
}
|
|
if (cObj)
|
|
{
|
|
// allow shapebase to clamp fov to its datablock values
|
|
cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
|
|
fov = cObj->getCameraFov();
|
|
|
|
// server fov of client has 1degree resolution
|
|
if(S32(fov) != S32(mCameraFov))
|
|
mUpdateCameraFov = true;
|
|
|
|
mCameraFov = fov;
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
bool GameConnection::getControlCameraVelocity(Point3F *vel)
|
|
{
|
|
if (ShapeBase* obj = getCameraObject()) {
|
|
*vel = obj->getVelocity();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GameConnection::setFirstPerson(bool firstPerson)
|
|
{
|
|
mFirstPerson = firstPerson;
|
|
mUpdateFirstPerson = true;
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool GameConnection::onAdd()
|
|
{
|
|
if (!Parent::onAdd())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void GameConnection::onRemove()
|
|
{
|
|
if(isNetworkConnection())
|
|
{
|
|
sendDisconnectPacket(mDisconnectReason);
|
|
}
|
|
if(!isConnectionToServer())
|
|
Con::executef(this, 2, "onDrop", mDisconnectReason);
|
|
|
|
if (mControlObject)
|
|
mControlObject->setControllingClient(0);
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void GameConnection::setDisconnectReason(const char *str)
|
|
{
|
|
dStrncpy(mDisconnectReason, str, sizeof(mDisconnectReason) - 1);
|
|
mDisconnectReason[sizeof(mDisconnectReason) - 1] = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void GameConnection::handleRecordedBlock(U32 type, U32 size, void *data)
|
|
{
|
|
switch(type)
|
|
{
|
|
case BlockTypeMove:
|
|
pushMove(*((Move *) data));
|
|
if(isRecording()) // put it back into the stream
|
|
recordBlock(type, size, data);
|
|
break;
|
|
default:
|
|
Parent::handleRecordedBlock(type, size, data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GameConnection::writeDemoStartBlock(ResizeBitStream *stream)
|
|
{
|
|
// write all the data blocks to the stream:
|
|
|
|
for(SimObjectId i = DataBlockObjectIdFirst; i <= DataBlockObjectIdLast; i++)
|
|
{
|
|
SimDataBlock *data;
|
|
if(Sim::findObject(i, data))
|
|
{
|
|
stream->writeFlag(true);
|
|
SimDataBlockEvent evt(data);
|
|
evt.pack(this, stream);
|
|
stream->validate();
|
|
}
|
|
}
|
|
stream->writeFlag(false);
|
|
stream->write(mFirstPerson);
|
|
stream->write(mCameraPos);
|
|
stream->write(mCameraSpeed);
|
|
stream->write(mLastMoveAck);
|
|
stream->write(mLastClientMove);
|
|
stream->write(mFirstMoveIndex);
|
|
|
|
stream->writeString(Con::getVariable("$Client::MissionFile"));
|
|
|
|
stream->write(U32(mMoveList.size()));
|
|
for(U32 j = 0; j < mMoveList.size(); j++)
|
|
mMoveList[j].pack(stream);
|
|
|
|
// dump all the "demo" vars associated with this connection:
|
|
SimFieldDictionaryIterator itr(getFieldDictionary());
|
|
|
|
SimFieldDictionary::Entry *entry;
|
|
while((entry = *itr) != NULL)
|
|
{
|
|
if(!dStrnicmp(entry->slotName, "demo", 4))
|
|
{
|
|
stream->writeFlag(true);
|
|
stream->writeString(entry->slotName + 4);
|
|
stream->writeString(entry->value);
|
|
stream->validate();
|
|
}
|
|
++itr;
|
|
}
|
|
stream->writeFlag(false);
|
|
Parent::writeDemoStartBlock(stream);
|
|
|
|
stream->validate();
|
|
|
|
// dump out the control object ghost id
|
|
S32 idx = mControlObject ? getGhostIndex(mControlObject) : -1;
|
|
stream->write(idx);
|
|
if(mControlObject)
|
|
mControlObject->writePacketData(this, stream);
|
|
idx = mCameraObject ? getGhostIndex(mCameraObject) : -1;
|
|
stream->write(idx);
|
|
if(mCameraObject && mCameraObject != mControlObject)
|
|
mCameraObject->writePacketData(this, stream);
|
|
mLastControlRequestTime = Platform::getVirtualMilliseconds();
|
|
}
|
|
|
|
bool GameConnection::readDemoStartBlock(BitStream *stream)
|
|
{
|
|
while(stream->readFlag())
|
|
{
|
|
SimDataBlockEvent evt;
|
|
evt.unpack(this, stream);
|
|
evt.process(this);
|
|
}
|
|
|
|
while(mDataBlockLoadList.size())
|
|
{
|
|
preloadNextDataBlock(false);
|
|
if(mErrorBuffer[0])
|
|
return false;
|
|
}
|
|
|
|
stream->read(&mFirstPerson);
|
|
stream->read(&mCameraPos);
|
|
stream->read(&mCameraSpeed);
|
|
stream->read(&mLastMoveAck);
|
|
stream->read(&mLastClientMove);
|
|
stream->read(&mFirstMoveIndex);
|
|
|
|
char buf[256];
|
|
stream->readString(buf);
|
|
Con::setVariable("$Client::MissionFile",buf);
|
|
|
|
U32 size;
|
|
Move mv;
|
|
stream->read(&size);
|
|
mMoveList.clear();
|
|
while(size--)
|
|
{
|
|
mv.unpack(stream);
|
|
pushMove(mv);
|
|
}
|
|
|
|
// read in all the demo vars associated with this recording
|
|
// they are all tagged on to the object and start with the
|
|
// string "demo"
|
|
|
|
while(stream->readFlag())
|
|
{
|
|
StringTableEntry slotName = StringTable->insert("demo");
|
|
char array[256];
|
|
char value[256];
|
|
stream->readString(array);
|
|
stream->readString(value);
|
|
setDataField(slotName, array, value);
|
|
}
|
|
bool ret = Parent::readDemoStartBlock(stream);
|
|
// grab the control object
|
|
S32 idx;
|
|
stream->read(&idx);
|
|
|
|
ShapeBase * obj = 0;
|
|
if(idx != -1)
|
|
{
|
|
obj = dynamic_cast<ShapeBase*>(resolveGhost(idx));
|
|
setControlObject(obj);
|
|
obj->readPacketData(this, stream);
|
|
}
|
|
|
|
// Get the camera object, and read it in if it's different
|
|
S32 idx2;
|
|
stream->read(&idx2);
|
|
obj = 0;
|
|
if(idx2 != -1 && idx2 != idx)
|
|
{
|
|
obj = dynamic_cast<ShapeBase*>(resolveGhost(idx2));
|
|
setCameraObject(obj);
|
|
obj->readPacketData(this, stream);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void GameConnection::demoPlaybackComplete()
|
|
{
|
|
static const char *demoPlaybackArgv[1] = { "demoPlaybackComplete" };
|
|
Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(1, demoPlaybackArgv, false));
|
|
Parent::demoPlaybackComplete();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void GameConnection::readPacket(BitStream *bstream)
|
|
{
|
|
char stringBuf[256];
|
|
stringBuf[0] = 0;
|
|
bstream->setStringBuffer(stringBuf);
|
|
|
|
bstream->clearCompressionPoint();
|
|
if (isConnectionToServer())
|
|
{
|
|
mLastMoveAck = bstream->readInt(32);
|
|
if (mLastMoveAck < mFirstMoveIndex)
|
|
mLastMoveAck = mFirstMoveIndex;
|
|
if(mLastMoveAck > mLastClientMove)
|
|
mLastClientMove = mLastMoveAck;
|
|
while(mFirstMoveIndex < mLastMoveAck)
|
|
{
|
|
AssertFatal(mMoveList.size(), "Popping off too many moves!");
|
|
mMoveList.pop_front();
|
|
mFirstMoveIndex++;
|
|
}
|
|
|
|
mDamageFlash = 0;
|
|
mWhiteOut = 0;
|
|
if(bstream->readFlag())
|
|
{
|
|
if(bstream->readFlag())
|
|
mDamageFlash = bstream->readFloat(7);
|
|
if(bstream->readFlag())
|
|
mWhiteOut = bstream->readFloat(7) * 1.5;
|
|
}
|
|
|
|
if (bstream->readFlag())
|
|
{
|
|
if(bstream->readFlag())
|
|
{
|
|
// the control object is dirty...
|
|
// so we get an update:
|
|
mLastClientMove = mLastMoveAck;
|
|
bool callScript = false;
|
|
if(mControlObject.isNull())
|
|
callScript = true;
|
|
|
|
S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
|
|
ShapeBase* obj = static_cast<ShapeBase*>(resolveGhost(gIndex));
|
|
if (mControlObject != obj)
|
|
setControlObject(obj);
|
|
obj->readPacketData(this, bstream);
|
|
|
|
if(callScript)
|
|
Con::executef(this, 2, "initialControlSet");
|
|
}
|
|
else
|
|
{
|
|
// read out the compression point
|
|
Point3F pos;
|
|
bstream->read(&pos.x);
|
|
bstream->read(&pos.y);
|
|
bstream->read(&pos.z);
|
|
bstream->setCompressionPoint(pos);
|
|
}
|
|
}
|
|
|
|
if (bstream->readFlag())
|
|
{
|
|
S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
|
|
ShapeBase* obj = static_cast<ShapeBase*>(resolveGhost(gIndex));
|
|
setCameraObject(obj);
|
|
obj->readPacketData(this, bstream);
|
|
}
|
|
else
|
|
setCameraObject(0);
|
|
|
|
// server changed first person
|
|
if(bstream->readFlag()) {
|
|
setFirstPerson(bstream->readFlag());
|
|
mUpdateFirstPerson = false;
|
|
}
|
|
|
|
// server forcing a fov change?
|
|
if(bstream->readFlag())
|
|
{
|
|
S32 fov = bstream->readInt(8);
|
|
setControlCameraFov(fov);
|
|
|
|
// don't bother telling the server if we were able to set the fov
|
|
F32 setFov;
|
|
if(getControlCameraFov(&setFov) && (S32(setFov) == fov))
|
|
mUpdateCameraFov = false;
|
|
|
|
// update the games fov info
|
|
GameSetCameraFov(fov);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool fp = bstream->readFlag();
|
|
if(fp)
|
|
mCameraPos = 0;
|
|
else
|
|
mCameraPos = 1;
|
|
|
|
bstream->read(&mLastControlObjectChecksum);
|
|
moveReadPacket(bstream);
|
|
|
|
// client changed first person
|
|
if(bstream->readFlag()) {
|
|
setFirstPerson(bstream->readFlag());
|
|
mUpdateFirstPerson = false;
|
|
}
|
|
|
|
// check fov change.. 1degree granularity on server
|
|
if(bstream->readFlag())
|
|
{
|
|
S32 fov = mClamp(bstream->readInt(8), S32(MinCameraFov), S32(MaxCameraFov));
|
|
setControlCameraFov(fov);
|
|
|
|
// may need to force client back to a valid fov
|
|
F32 setFov;
|
|
if(getControlCameraFov(&setFov) && (S32(setFov) == fov))
|
|
mUpdateCameraFov = false;
|
|
}
|
|
}
|
|
|
|
Parent::readPacket(bstream);
|
|
bstream->clearCompressionPoint();
|
|
bstream->setStringBuffer(NULL);
|
|
}
|
|
|
|
void GameConnection::writePacket(BitStream *bstream, PacketNotify *note)
|
|
{
|
|
char stringBuf[256];
|
|
bstream->clearCompressionPoint();
|
|
stringBuf[0] = 0;
|
|
bstream->setStringBuffer(stringBuf);
|
|
|
|
GamePacketNotify *gnote = (GamePacketNotify *) note;
|
|
|
|
U32 startPos = bstream->getCurPos();
|
|
if (isConnectionToServer())
|
|
{
|
|
bstream->writeFlag(mCameraPos == 0);
|
|
U32 sum = 0;
|
|
if(mControlObject)
|
|
{
|
|
mControlObject->interpolateTick(0);
|
|
sum = mControlObject->getPacketDataChecksum(this);
|
|
mControlObject->interpolateTick(gClientProcessList.getLastInterpDelta());
|
|
}
|
|
// if we're recording, we want to make sure that we get periodic updates of the
|
|
// control object "just in case" - ie if the math copro is different between the
|
|
// recording machine (SIMD vs FPU), we get periodic corrections
|
|
|
|
if(isRecording())
|
|
{
|
|
U32 currentTime = Platform::getVirtualMilliseconds();
|
|
if(currentTime - mLastControlRequestTime > ControlRequestTime)
|
|
{
|
|
mLastControlRequestTime = currentTime;
|
|
sum = 0;
|
|
}
|
|
}
|
|
bstream->write(sum);
|
|
|
|
moveWritePacket(bstream);
|
|
|
|
// first person changed?
|
|
if(bstream->writeFlag(mUpdateFirstPerson))
|
|
{
|
|
bstream->writeFlag(mFirstPerson);
|
|
mUpdateFirstPerson = false;
|
|
}
|
|
|
|
// camera fov changed? (server fov resolution is 1 degree)
|
|
if(bstream->writeFlag(mUpdateCameraFov))
|
|
{
|
|
bstream->writeInt(mClamp(S32(mCameraFov), S32(MinCameraFov), S32(MaxCameraFov)), 8);
|
|
mUpdateCameraFov = false;
|
|
}
|
|
DEBUG_LOG(("PKLOG %d CLIENTMOVES: %d", getId(), bstream->getCurPos() - startPos));
|
|
}
|
|
else
|
|
{
|
|
// The only time mMoveList will not be empty at this
|
|
// point is during a change in control object.
|
|
|
|
bstream->writeInt(mLastMoveAck - mMoveList.size(),32);
|
|
|
|
S32 gIndex = -1;
|
|
|
|
// get the ghost index of the control object, and write out
|
|
// all the damage flash & white out
|
|
|
|
if (!mControlObject.isNull())
|
|
{
|
|
gIndex = getGhostIndex(mControlObject);
|
|
|
|
F32 flash = mControlObject->getDamageFlash();
|
|
F32 whiteOut = mControlObject->getWhiteOut();
|
|
if(bstream->writeFlag(flash != 0 || whiteOut != 0))
|
|
{
|
|
if(bstream->writeFlag(flash != 0))
|
|
bstream->writeFloat(flash, 7);
|
|
if(bstream->writeFlag(whiteOut != 0))
|
|
bstream->writeFloat(whiteOut/1.5, 7);
|
|
}
|
|
}
|
|
else
|
|
bstream->writeFlag(false);
|
|
|
|
if (bstream->writeFlag(gIndex != -1))
|
|
{
|
|
// assume that the control object will write in a compression point
|
|
if(bstream->writeFlag(mControlObject->getPacketDataChecksum(this) != mLastControlObjectChecksum))
|
|
{
|
|
#ifdef TORQUE_DEBUG_NET
|
|
Con::printf("packetDataChecksum disagree!");
|
|
#endif
|
|
bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);
|
|
mControlObject->writePacketData(this, bstream);
|
|
}
|
|
else
|
|
{
|
|
// we'll have to use the control object's position as the compression point
|
|
// should make this lower res for better space usage:
|
|
Point3F coPos = mControlObject->getPosition();
|
|
bstream->write(coPos.x);
|
|
bstream->write(coPos.y);
|
|
bstream->write(coPos.z);
|
|
bstream->setCompressionPoint(coPos);
|
|
}
|
|
}
|
|
DEBUG_LOG(("PKLOG %d CONTROLOBJECTSTATE: %d", getId(), bstream->getCurPos() - startPos));
|
|
startPos = bstream->getCurPos();
|
|
|
|
if (!mCameraObject.isNull() && mCameraObject != mControlObject)
|
|
{
|
|
gIndex = getGhostIndex(mCameraObject);
|
|
if (bstream->writeFlag(gIndex != -1))
|
|
{
|
|
bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);
|
|
mCameraObject->writePacketData(this, bstream);
|
|
}
|
|
}
|
|
else
|
|
bstream->writeFlag( false );
|
|
|
|
// first person changed?
|
|
if(bstream->writeFlag(mUpdateFirstPerson)) {
|
|
bstream->writeFlag(mFirstPerson);
|
|
mUpdateFirstPerson = false;
|
|
}
|
|
|
|
// server forcing client fov?
|
|
gnote->cameraFov = -1;
|
|
if(bstream->writeFlag(mUpdateCameraFov))
|
|
{
|
|
gnote->cameraFov = mClamp(S32(mCameraFov), S32(MinCameraFov), S32(MaxCameraFov));
|
|
bstream->writeInt(gnote->cameraFov, 8);
|
|
mUpdateCameraFov = false;
|
|
}
|
|
DEBUG_LOG(("PKLOG %d PINGCAMSTATE: %d", getId(), bstream->getCurPos() - startPos));
|
|
}
|
|
|
|
Parent::writePacket(bstream, note);
|
|
bstream->clearCompressionPoint();
|
|
bstream->setStringBuffer(NULL);
|
|
}
|
|
|
|
|
|
void GameConnection::detectLag()
|
|
{
|
|
//see if we're lagging...
|
|
S32 curTime = Sim::getCurrentTime();
|
|
if (curTime - mLastPacketTime > mLagThresholdMS)
|
|
{
|
|
if (!mLagging)
|
|
{
|
|
mLagging = true;
|
|
Con::executef(this, 2, "setLagIcon", "true");
|
|
}
|
|
}
|
|
else if (mLagging)
|
|
{
|
|
mLagging = false;
|
|
Con::executef(this, 2, "setLagIcon", "false");
|
|
}
|
|
}
|
|
|
|
GameConnection::GamePacketNotify::GamePacketNotify()
|
|
{
|
|
// need to fill in empty notifes for demo start block
|
|
cameraFov = 0;
|
|
}
|
|
|
|
NetConnection::PacketNotify *GameConnection::allocNotify()
|
|
{
|
|
return new GamePacketNotify;
|
|
}
|
|
|
|
void GameConnection::packetReceived(PacketNotify *note)
|
|
{
|
|
//record the time so we can tell if we're lagging...
|
|
mLastPacketTime = Sim::getCurrentTime();
|
|
GamePacketNotify *gnote = (GamePacketNotify *) note;
|
|
|
|
Parent::packetReceived(note);
|
|
}
|
|
|
|
void GameConnection::packetDropped(PacketNotify *note)
|
|
{
|
|
Parent::packetDropped(note);
|
|
GamePacketNotify *gnote = (GamePacketNotify *) note;
|
|
if(gnote->cameraFov != -1)
|
|
mUpdateCameraFov = true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void GameConnection::play2D(const AudioProfile* profile)
|
|
{
|
|
postNetEvent(new Sim2DAudioEvent(profile));
|
|
}
|
|
|
|
void GameConnection::play3D(const AudioProfile* profile, const MatrixF *transform)
|
|
{
|
|
if (transform)
|
|
{
|
|
if (mControlObject)
|
|
{
|
|
// Only post the event if it's within audible range
|
|
// of the control object.
|
|
Point3F ear,pos;
|
|
transform->getColumn(3,&pos);
|
|
mControlObject->getTransform().getColumn(3,&ear);
|
|
if ((ear - pos).len() < profile->mDescriptionObject->mDescription.mMaxDistance)
|
|
postNetEvent(new Sim3DAudioEvent(profile,transform));
|
|
}
|
|
else
|
|
postNetEvent(new Sim3DAudioEvent(profile,transform));
|
|
}
|
|
else
|
|
play2D(profile);
|
|
}
|
|
|
|
void GameConnection::doneScopingScene()
|
|
{
|
|
// Could add special post-scene scoping here, such as scoping
|
|
// objects not visible to the camera, but visible to sensors.
|
|
}
|
|
|
|
void GameConnection::preloadDataBlock(SimDataBlock *db)
|
|
{
|
|
mDataBlockLoadList.push_back(db);
|
|
if(mDataBlockLoadList.size() == 1)
|
|
preloadNextDataBlock(true);
|
|
}
|
|
|
|
void GameConnection::fileDownloadSegmentComplete()
|
|
{
|
|
// this is called when a the file list has finished processing...
|
|
// at this point we can try again to add the object
|
|
// subclasses can override this to do, for example, datablock redos.
|
|
if(mDataBlockLoadList.size())
|
|
preloadNextDataBlock(mNumDownloadedFiles != 0);
|
|
Parent::fileDownloadSegmentComplete();
|
|
}
|
|
|
|
void GameConnection::preloadNextDataBlock(bool hadNewFiles)
|
|
{
|
|
if(!mDataBlockLoadList.size())
|
|
return;
|
|
while(mDataBlockLoadList.size())
|
|
{
|
|
// only check for new files if this is the first load, or if new
|
|
// files were downloaded from the server.
|
|
if(hadNewFiles)
|
|
ResourceManager->setMissingFileLogging(true);
|
|
ResourceManager->clearMissingFileList();
|
|
SimDataBlock *object = mDataBlockLoadList[0];
|
|
if(!object)
|
|
{
|
|
// a null object is used to signify that the last ghost in the list is down
|
|
mDataBlockLoadList.pop_front();
|
|
AssertFatal(mDataBlockLoadList.size() == 0, "Error! Datablock save list should be empty!");
|
|
sendConnectionMessage(DataBlocksDownloadDone, mDataBlockSequence);
|
|
|
|
ResourceManager->setMissingFileLogging(false);
|
|
return;
|
|
}
|
|
mFilesWereDownloaded = hadNewFiles;
|
|
if(!object->preload(false, mErrorBuffer))
|
|
{
|
|
mFilesWereDownloaded = false;
|
|
// make sure there's an error message if necessary
|
|
if(!mErrorBuffer[0])
|
|
setLastError("Invalid packet.");
|
|
|
|
// if there were no new files, make sure the error message
|
|
// is the one from the last time we tried to add this object
|
|
if(!hadNewFiles)
|
|
{
|
|
dStrcpy(mErrorBuffer, mLastFileErrorBuffer);
|
|
ResourceManager->setMissingFileLogging(false);
|
|
return;
|
|
}
|
|
// object failed to load, let's see if it had any missing files
|
|
|
|
if(!ResourceManager->getMissingFileList(mMissingFileList))
|
|
{
|
|
// no missing files, must be an error
|
|
// connection will automagically delete the ghost always list
|
|
// when this error is reported.
|
|
ResourceManager->setMissingFileLogging(false);
|
|
return;
|
|
}
|
|
|
|
// ok, copy the error buffer out to a scratch pad for now
|
|
dStrcpy(mLastFileErrorBuffer, mErrorBuffer);
|
|
mErrorBuffer[0] = 0;
|
|
|
|
// request the missing files...
|
|
mNumDownloadedFiles = 0;
|
|
sendNextFileDownloadRequest();
|
|
break;
|
|
}
|
|
mFilesWereDownloaded = false;
|
|
ResourceManager->setMissingFileLogging(false);
|
|
mDataBlockLoadList.pop_front();
|
|
hadNewFiles = true;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//localconnection only blackout functions
|
|
void GameConnection::setBlackOut(bool fadeToBlack, S32 timeMS)
|
|
{
|
|
mFadeToBlack = fadeToBlack;
|
|
mBlackOutStartTimeMS = Sim::getCurrentTime();
|
|
mBlackOutTimeMS = timeMS;
|
|
|
|
//if timeMS <= 0 set the value instantly
|
|
if (mBlackOutTimeMS <= 0)
|
|
mBlackOut = (mFadeToBlack ? 1.0f : 0.0f);
|
|
}
|
|
|
|
F32 GameConnection::getBlackOut()
|
|
{
|
|
S32 curTime = Sim::getCurrentTime();
|
|
|
|
//see if we're in the middle of a black out
|
|
if (curTime < mBlackOutStartTimeMS + mBlackOutTimeMS)
|
|
{
|
|
S32 elapsedTime = curTime - mBlackOutStartTimeMS;
|
|
F32 timePercent = F32(elapsedTime) / F32(mBlackOutTimeMS);
|
|
mBlackOut = (mFadeToBlack ? timePercent : 1.0f - timePercent);
|
|
}
|
|
else
|
|
mBlackOut = (mFadeToBlack ? 1.0f : 0.0f);
|
|
|
|
//return the blackout time
|
|
return mBlackOut;
|
|
}
|
|
|
|
void GameConnection::handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount)
|
|
{
|
|
if(isConnectionToServer())
|
|
{
|
|
if(message == DataBlocksDone)
|
|
{
|
|
mDataBlockLoadList.push_back(NULL);
|
|
mDataBlockSequence = sequence;
|
|
if(mDataBlockLoadList.size() == 1)
|
|
preloadNextDataBlock(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(message == DataBlocksDownloadDone)
|
|
{
|
|
if(getDataBlockSequence() == sequence)
|
|
Con::executef(this, 2, "onDataBlocksDone", Con::getIntArg(getDataBlockSequence()));
|
|
}
|
|
}
|
|
Parent::handleConnectionMessage(message, sequence, ghostCount);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
ConsoleMethod( GameConnection, transmitDataBlocks, void, 3, 3, "(int sequence)")
|
|
{
|
|
object->setDataBlockSequence(dAtoi(argv[2]));
|
|
SimDataBlockGroup *g = Sim::getDataBlockGroup();
|
|
|
|
// find the first one we haven't sent:
|
|
U32 i, groupCount = g->size();
|
|
S32 key = object->getDataBlockModifiedKey();
|
|
for(i = 0; i < groupCount; i++)
|
|
if(( (SimDataBlock *)(*g)[i])->getModifiedKey() > key)
|
|
break;
|
|
if (i == groupCount) {
|
|
object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
|
|
return;
|
|
}
|
|
object->setMaxDataBlockModifiedKey(key);
|
|
|
|
// Ship the rest off...
|
|
U32 max = getMin(i + DataBlockQueueCount, groupCount);
|
|
for (;i < max; i++) {
|
|
SimDataBlock *data = (SimDataBlock *)(*g)[i];
|
|
object->postNetEvent(new SimDataBlockEvent(data, i, groupCount, object->getDataBlockSequence()));
|
|
}
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, activateGhosting, void, 2, 2, "")
|
|
{
|
|
object->activateGhosting();
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, resetGhosting, void, 2, 2, "")
|
|
{
|
|
object->resetGhosting();
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, setControlObject, bool, 3, 3, "(ShapeBase object)")
|
|
{
|
|
ShapeBase *gb;
|
|
if(!Sim::findObject(argv[2], gb))
|
|
return false;
|
|
|
|
object->setControlObject(gb);
|
|
return true;
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, getControlObject, S32, 2, 2, "")
|
|
{
|
|
argv;
|
|
SimObject* cp = object->getControlObject();
|
|
return cp? cp->getId(): 0;
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, isAIControlled, bool, 2, 2, "")
|
|
{
|
|
return object->isAIControlled();
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, play2D, bool, 3, 3, "(AudioProfile ap)")
|
|
{
|
|
AudioProfile *profile;
|
|
if(!Sim::findObject(argv[2], profile))
|
|
return false;
|
|
object->play2D(profile);
|
|
return true;
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, play3D, bool, 4, 4, "(AudioProfile ap, Transform pos)")
|
|
{
|
|
AudioProfile *profile;
|
|
if(!Sim::findObject(argv[2], profile))
|
|
return false;
|
|
|
|
Point3F pos(0,0,0);
|
|
AngAxisF aa;
|
|
aa.axis.set(0,0,1);
|
|
aa.angle = 0;
|
|
dSscanf(argv[3],"%g %g %g %g %g %g %g",
|
|
&pos.x,&pos.y,&pos.z,&aa.axis.x,&aa.axis.y,&aa.axis.z,&aa.angle);
|
|
MatrixF mat;
|
|
aa.setMatrix(&mat);
|
|
mat.setColumn(3,pos);
|
|
|
|
object->play3D(profile,&mat);
|
|
return true;
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, chaseCam, bool, 3, 3, "(int size)")
|
|
{
|
|
S32 size = dAtoi(argv[2]);
|
|
if (size != sChaseQueueSize)
|
|
{
|
|
SAFE_DELETE_ARRAY(sChaseQueue);
|
|
|
|
sChaseQueueSize = size;
|
|
sChaseQueueHead = sChaseQueueTail = 0;
|
|
|
|
if (size)
|
|
{
|
|
sChaseQueue = new MatrixF[size];
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, setControlCameraFov, void, 3, 3, "(int newFOV)"
|
|
"Set new FOV in degrees.")
|
|
{
|
|
object->setControlCameraFov(dAtoi(argv[2]));
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, getControlCameraFov, F32, 2, 2, "")
|
|
{
|
|
F32 fov = 0.f;
|
|
if(!object->getControlCameraFov(&fov))
|
|
return(0.f);
|
|
return(fov);
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, setBlackOut, void, 4, 4, "(bool doFade, int timeMS)")
|
|
{
|
|
object->setBlackOut(dAtob(argv[2]), dAtoi(argv[3]));
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, setMissionCRC, void, 3, 3, "(int CRC)")
|
|
{
|
|
if(object->isConnectionToServer())
|
|
return;
|
|
|
|
object->postNetEvent(new SetMissionCRCEvent(dAtoi(argv[2])));
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, delete, void, 2, 3, "(string reason=NULL) Disconnect a client; reason is sent as part of the disconnect packet.")
|
|
{
|
|
if (argc == 3)
|
|
object->setDisconnectReason(argv[2]);
|
|
object->deleteObject();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
void GameConnection::consoleInit()
|
|
{
|
|
Con::addVariable("Pref::Net::LagThreshold", TypeS32, &mLagThresholdMS);
|
|
Con::addVariable("specialFog", TypeBool, &SceneGraph::useSpecial);
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, startRecording, void, 3, 3, "(string fileName)records the network connection to a demo file.")
|
|
{
|
|
char fileName[1024];
|
|
Con::expandScriptFilename(fileName, sizeof(fileName), argv[2]);
|
|
object->startDemoRecord(fileName);
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, stopRecording, void, 2, 2, "()stops the demo recording.")
|
|
{
|
|
object->stopRecording();
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, playDemo, bool, 3, 3, "(string demoFileName)plays a previously recorded demo.")
|
|
{
|
|
char filename[1024];
|
|
Con::expandScriptFilename(filename, sizeof(filename), argv[2]);
|
|
|
|
// Note that calling onConnectionEstablished will change the values in argv!
|
|
object->onConnectionEstablished(true);
|
|
object->setEstablished();
|
|
|
|
if(!object->replayDemoRecord(filename))
|
|
{
|
|
Con::printf("Unable to open demo file %s.", filename);
|
|
object->deleteObject();
|
|
return false;
|
|
}
|
|
|
|
// After demo has loaded, execute the scene re-light the scene
|
|
SceneLighting::lightScene(0, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, isDemoPlaying, bool, 2, 2, "isDemoPlaying();")
|
|
{
|
|
argc;
|
|
argv;
|
|
return object->isPlayingBack();
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, isDemoRecording, bool, 2, 2, "()")
|
|
{
|
|
argc;
|
|
argv;
|
|
return object->isRecording();
|
|
}
|
|
|
|
ConsoleMethod( GameConnection, listClassIDs, void, 2, 2, "() List all of the "
|
|
"classes that this connection knows about, and what their IDs "
|
|
"are. Useful for debugging network problems.")
|
|
{
|
|
Con::printf("--------------- Class ID Listing ----------------");
|
|
Con::printf(" id | name");
|
|
|
|
for(AbstractClassRep *rep = AbstractClassRep::getClassList();
|
|
rep;
|
|
rep = rep->getNextClass())
|
|
{
|
|
ConsoleObject *obj = rep->create();
|
|
if(obj && rep->getClassId(object->getNetClassGroup()) >= 0)
|
|
Con::printf("%7.7d| %s", rep->getClassId(object->getNetClassGroup()), rep->getClassName());
|
|
delete obj;
|
|
}
|
|
}
|
|
|
|
ConsoleStaticMethod(GameConnection, getServerConnection, S32, 1, 2, "() Get the server connection if any.")
|
|
{
|
|
if(GameConnection::getConnectionToServer())
|
|
return GameConnection::getConnectionToServer()->getId();
|
|
else
|
|
{
|
|
Con::errorf("GameConnection::getServerConnection - no connection available.");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, setCameraObject, S32, 3, 3, "")
|
|
{
|
|
ShapeBase *obj;
|
|
if(!Sim::findObject(argv[2], obj))
|
|
return false;
|
|
|
|
object->setCameraObject(obj);
|
|
return true;
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, getCameraObject, S32, 2, 2, "")
|
|
{
|
|
SimObject *obj = object->getCameraObject();
|
|
return obj ? obj->getId() : 0;
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, clearCameraObject, void, 2, 2, "")
|
|
{
|
|
object->setCameraObject(NULL);
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, isFirstPerson, bool, 2, 2, "() True if this connection is in first person mode.")
|
|
{
|
|
// Note: Transition to first person occurs over time via mCameraPos, so this
|
|
// won't immediately return true after a set.
|
|
return object->isFirstPerson();
|
|
}
|
|
|
|
ConsoleMethod(GameConnection, setFirstPerson, void, 3, 3, "(bool firstPerson) Sets this connection into or out of first person mode.")
|
|
{
|
|
object->setFirstPerson(dAtob(argv[2]));
|
|
} |