Initial commit
This commit is contained in:
309
engine/game/net/httpObject.cc
Executable file
309
engine/game/net/httpObject.cc
Executable file
@ -0,0 +1,309 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/net/httpObject.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/event.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(HTTPObject);
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
HTTPObject::HTTPObject()
|
||||
{
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
mPost = 0;
|
||||
mBufferSave = 0;
|
||||
}
|
||||
|
||||
HTTPObject::~HTTPObject()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
dFree(mPost);
|
||||
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
mPost = 0;
|
||||
dFree(mBufferSave);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
//--------------------------------------
|
||||
void HTTPObject::get(const char *host, const char *path, const char *query)
|
||||
{
|
||||
if(mHostName)
|
||||
dFree(mHostName);
|
||||
if(mPath)
|
||||
dFree(mPath);
|
||||
if(mQuery)
|
||||
dFree(mQuery);
|
||||
if(mPost)
|
||||
dFree(mPost);
|
||||
if(mBufferSave)
|
||||
dFree(mBufferSave);
|
||||
|
||||
mBufferSave = 0;
|
||||
mHostName = dStrdup(host);
|
||||
mPath = dStrdup(path);
|
||||
if(query)
|
||||
mQuery = dStrdup(query);
|
||||
else
|
||||
mQuery = NULL;
|
||||
mPost = NULL;
|
||||
|
||||
connect(host);
|
||||
}
|
||||
|
||||
void HTTPObject::post(const char *host, const char *path, const char *query, const char *post)
|
||||
{
|
||||
if(mHostName)
|
||||
dFree(mHostName);
|
||||
if(mPath)
|
||||
dFree(mPath);
|
||||
if(mQuery)
|
||||
dFree(mQuery);
|
||||
if(mPost)
|
||||
dFree(mPost);
|
||||
if(mBufferSave)
|
||||
dFree(mBufferSave);
|
||||
|
||||
mBufferSave = 0;
|
||||
mHostName = dStrdup(host);
|
||||
mPath = dStrdup(path);
|
||||
if(query && query[0])
|
||||
mQuery = dStrdup(query);
|
||||
else
|
||||
mQuery = NULL;
|
||||
mPost = dStrdup(post);
|
||||
connect(host);
|
||||
}
|
||||
|
||||
static char getHex(char c)
|
||||
{
|
||||
if(c <= 9)
|
||||
return c + '0';
|
||||
return c - 10 + 'A';
|
||||
}
|
||||
|
||||
static S32 getHexVal(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if(c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 10;
|
||||
else if(c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void HTTPObject::expandPath(char *dest, const char *path, U32 destSize)
|
||||
{
|
||||
static bool asciiEscapeTableBuilt = false;
|
||||
static bool asciiEscapeTable[256];
|
||||
if(!asciiEscapeTableBuilt)
|
||||
{
|
||||
asciiEscapeTableBuilt = true;
|
||||
U32 i;
|
||||
for(i = 0; i <= ' '; i++)
|
||||
asciiEscapeTable[i] = true;
|
||||
for(;i <= 0x7F; i++)
|
||||
asciiEscapeTable[i] = false;
|
||||
for(;i <= 0xFF; i++)
|
||||
asciiEscapeTable[i] = true;
|
||||
asciiEscapeTable['\"'] = true;
|
||||
asciiEscapeTable['_'] = true;
|
||||
asciiEscapeTable['\''] = true;
|
||||
asciiEscapeTable['#'] = true;
|
||||
asciiEscapeTable['$'] = true;
|
||||
asciiEscapeTable['%'] = true;
|
||||
asciiEscapeTable['&'] = true;
|
||||
asciiEscapeTable['+'] = true;
|
||||
asciiEscapeTable['-'] = true;
|
||||
asciiEscapeTable['~'] = true;
|
||||
}
|
||||
|
||||
U32 destIndex = 0;
|
||||
U32 srcIndex = 0;
|
||||
while(path[srcIndex] && destIndex < destSize - 3)
|
||||
{
|
||||
char c = path[srcIndex++];
|
||||
if(asciiEscapeTable[c])
|
||||
{
|
||||
dest[destIndex++] = '%';
|
||||
dest[destIndex++] = getHex((c >> 4) & 0xF);
|
||||
dest[destIndex++] = getHex(c & 0xF);
|
||||
}
|
||||
else
|
||||
dest[destIndex++] = c;
|
||||
}
|
||||
dest[destIndex] = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void HTTPObject::onConnected()
|
||||
{
|
||||
Parent::onConnected();
|
||||
char expPath[8192];
|
||||
char buffer[8192];
|
||||
|
||||
if(mQuery)
|
||||
{
|
||||
dSprintf(buffer, sizeof(buffer), "%s?%s", mPath, mQuery);
|
||||
expandPath(expPath, buffer, sizeof(expPath));
|
||||
}
|
||||
else
|
||||
expandPath(expPath, mPath, sizeof(expPath));
|
||||
|
||||
char *pt = dStrchr(mHostName, ':');
|
||||
if(pt)
|
||||
*pt = 0;
|
||||
dSprintf(buffer, sizeof(buffer), "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", expPath, mHostName);
|
||||
if(pt)
|
||||
*pt = ':';
|
||||
|
||||
send((U8*)buffer, dStrlen(buffer));
|
||||
mParseState = ParsingStatusLine;
|
||||
mChunkedEncoding = false;
|
||||
}
|
||||
|
||||
void HTTPObject::onConnectFailed()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
Parent::onConnectFailed();
|
||||
}
|
||||
|
||||
|
||||
void HTTPObject::onDisconnect()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
Parent::onDisconnect();
|
||||
}
|
||||
|
||||
bool HTTPObject::processLine(U8 *line)
|
||||
{
|
||||
if(mParseState == ParsingStatusLine)
|
||||
{
|
||||
mParseState = ParsingHeader;
|
||||
}
|
||||
else if(mParseState == ParsingHeader)
|
||||
{
|
||||
if(!dStricmp((char *) line, "transfer-encoding: chunked"))
|
||||
mChunkedEncoding = true;
|
||||
if(line[0] == 0)
|
||||
{
|
||||
if(mChunkedEncoding)
|
||||
mParseState = ParsingChunkHeader;
|
||||
else
|
||||
mParseState = ProcessingBody;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(mParseState == ParsingChunkHeader)
|
||||
{
|
||||
if(line[0]) // strip off the crlf if necessary
|
||||
{
|
||||
mChunkSize = 0;
|
||||
S32 hexVal;
|
||||
while((hexVal = getHexVal(*line++)) != -1)
|
||||
{
|
||||
mChunkSize *= 16;
|
||||
mChunkSize += hexVal;
|
||||
}
|
||||
if(mBufferSave)
|
||||
{
|
||||
mBuffer = mBufferSave;
|
||||
mBufferSize = mBufferSaveSize;
|
||||
mBufferSave = 0;
|
||||
}
|
||||
if(mChunkSize)
|
||||
mParseState = ProcessingBody;
|
||||
else
|
||||
{
|
||||
mParseState = ProcessingDone;
|
||||
finishLastLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Parent::processLine(line);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 HTTPObject::onDataReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 HTTPObject::onReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
if(mParseState == ProcessingBody)
|
||||
{
|
||||
if(mChunkedEncoding && bufferLen >= mChunkSize)
|
||||
{
|
||||
U32 ret = onDataReceive(buffer, mChunkSize);
|
||||
mChunkSize -= ret;
|
||||
if(mChunkSize == 0)
|
||||
{
|
||||
if(mBuffer)
|
||||
{
|
||||
mBufferSaveSize = mBufferSize;
|
||||
mBufferSave = mBuffer;
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
}
|
||||
mParseState = ParsingChunkHeader;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
U32 ret = onDataReceive(buffer, bufferLen);
|
||||
mChunkSize -= ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else if(mParseState != ProcessingDone)
|
||||
{
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
return bufferLen;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
ConsoleMethod( HTTPObject, get, void, 4, 5, "(TransportAddress addr, string requirstURI, string query=NULL)")
|
||||
{
|
||||
object->get(argv[2], argv[3], argc == 4 ? NULL : argv[4]);
|
||||
}
|
||||
|
||||
ConsoleMethod( HTTPObject, post, void, 6, 6, "(TransportAddress addr, string requestURI, string query, string post)")
|
||||
{
|
||||
object->post(argv[2], argv[3], argv[4], argv[5]);
|
||||
}
|
64
engine/game/net/httpObject.h
Executable file
64
engine/game/net/httpObject.h
Executable file
@ -0,0 +1,64 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _HTTPOBJECT_H_
|
||||
#define _HTTPOBJECT_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _TCPOBJECT_H_
|
||||
#include "game/net/tcpObject.h"
|
||||
#endif
|
||||
|
||||
class HTTPObject : public TCPObject
|
||||
{
|
||||
private:
|
||||
typedef TCPObject Parent;
|
||||
protected:
|
||||
enum ParseState {
|
||||
ParsingStatusLine,
|
||||
ParsingHeader,
|
||||
ParsingChunkHeader,
|
||||
ProcessingBody,
|
||||
ProcessingDone,
|
||||
};
|
||||
ParseState mParseState;
|
||||
U32 mTotalBytes;
|
||||
U32 mBytesRemaining;
|
||||
public:
|
||||
U32 mStatus;
|
||||
F32 mVersion;
|
||||
U32 mContentLength;
|
||||
bool mChunkedEncoding;
|
||||
U32 mChunkSize;
|
||||
const char *mContentType;
|
||||
char *mHostName;
|
||||
char *mPath;
|
||||
char *mQuery;
|
||||
char *mPost;
|
||||
U8 *mBufferSave;
|
||||
U32 mBufferSaveSize;
|
||||
public:
|
||||
static void expandPath(char *dest, const char *path, U32 destSize);
|
||||
void get(const char *hostName, const char *urlName, const char *query);
|
||||
void post(const char *host, const char *path, const char *query, const char *post);
|
||||
HTTPObject();
|
||||
~HTTPObject();
|
||||
|
||||
//static HTTPObject *find(U32 tag);
|
||||
|
||||
virtual U32 onDataReceive(U8 *buffer, U32 bufferLen);
|
||||
virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data
|
||||
virtual void onConnected();
|
||||
virtual void onConnectFailed();
|
||||
virtual void onDisconnect();
|
||||
bool processLine(U8 *line);
|
||||
|
||||
DECLARE_CONOBJECT(HTTPObject);
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_HTTPOBJECT_
|
277
engine/game/net/net.cc
Executable file
277
engine/game/net/net.cc
Executable file
@ -0,0 +1,277 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/dnet.h"
|
||||
#include "core/idGenerator.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "sim/netObject.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/net/serverQuery.h"
|
||||
|
||||
StringTableEntry gMasterAddress;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// remote procedure call console functions
|
||||
//----------------------------------------------------------------
|
||||
|
||||
class RemoteCommandEvent : public NetEvent
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
MaxRemoteCommandArgs = 20,
|
||||
CommandArgsBits = 5
|
||||
};
|
||||
|
||||
private:
|
||||
S32 mArgc;
|
||||
char *mArgv[MaxRemoteCommandArgs + 1];
|
||||
StringHandle mTagv[MaxRemoteCommandArgs + 1];
|
||||
static char mBuf[1024];
|
||||
public:
|
||||
RemoteCommandEvent(S32 argc=0, const char **argv=NULL, NetConnection *conn = NULL)
|
||||
{
|
||||
mArgc = argc;
|
||||
for(S32 i = 0; i < argc; i++)
|
||||
{
|
||||
if(argv[i][0] == StringTagPrefixByte)
|
||||
{
|
||||
char buffer[256];
|
||||
mTagv[i+1] = StringHandle(dAtoi(argv[i]+1));
|
||||
if(conn)
|
||||
{
|
||||
dSprintf(buffer + 1, sizeof(buffer) - 1, "%d", conn->getNetSendId(mTagv[i+1]));
|
||||
buffer[0] = StringTagPrefixByte;
|
||||
mArgv[i+1] = dStrdup(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
mArgv[i+1] = dStrdup(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET
|
||||
const char *getDebugName()
|
||||
{
|
||||
static char buffer[256];
|
||||
dSprintf(buffer, sizeof(buffer), "%s [%s]", getClassName(), gNetStringTable->lookupString(dAtoi(mArgv[1] + 1)) );
|
||||
return buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
~RemoteCommandEvent()
|
||||
{
|
||||
for(S32 i = 0; i < mArgc; i++)
|
||||
dFree(mArgv[i+1]);
|
||||
}
|
||||
|
||||
virtual void pack(NetConnection* conn, BitStream *bstream)
|
||||
{
|
||||
bstream->writeInt(mArgc, CommandArgsBits);
|
||||
// write it out reversed... why?
|
||||
// automatic string substitution with later arguments -
|
||||
// handled automatically by the system.
|
||||
|
||||
for(S32 i = 0; i < mArgc; i++)
|
||||
conn->packString(bstream, mArgv[i+1]);
|
||||
}
|
||||
|
||||
virtual void write(NetConnection* conn, BitStream *bstream)
|
||||
{
|
||||
pack(conn, bstream);
|
||||
}
|
||||
|
||||
virtual void unpack(NetConnection* conn, BitStream *bstream)
|
||||
{
|
||||
|
||||
mArgc = bstream->readInt(CommandArgsBits);
|
||||
// read it out backwards
|
||||
for(S32 i = 0; i < mArgc; i++)
|
||||
{
|
||||
conn->unpackString(bstream, mBuf);
|
||||
mArgv[i+1] = dStrdup(mBuf);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void process(NetConnection *conn)
|
||||
{
|
||||
static char idBuf[10];
|
||||
|
||||
// de-tag the command name
|
||||
|
||||
for(S32 i = mArgc - 1; i >= 0; i--)
|
||||
{
|
||||
char *arg = mArgv[i+1];
|
||||
if(*arg == StringTagPrefixByte)
|
||||
{
|
||||
// it's a tag:
|
||||
U32 localTag = dAtoi(arg + 1);
|
||||
StringHandle tag = conn->translateRemoteStringId(localTag);
|
||||
NetStringTable::expandString( tag,
|
||||
mBuf,
|
||||
sizeof(mBuf),
|
||||
(mArgc - 1) - i,
|
||||
(const char**)(mArgv + i + 2) );
|
||||
dFree(mArgv[i+1]);
|
||||
mArgv[i+1] = dStrdup(mBuf);
|
||||
}
|
||||
}
|
||||
const char *rmtCommandName = dStrchr(mArgv[1], ' ') + 1;
|
||||
if(conn->isConnectionToServer())
|
||||
{
|
||||
dStrcpy(mBuf, "clientCmd");
|
||||
dStrcat(mBuf, rmtCommandName);
|
||||
|
||||
char *temp = mArgv[1];
|
||||
mArgv[1] = mBuf;
|
||||
|
||||
Con::execute(mArgc, (const char **) mArgv+1);
|
||||
mArgv[1] = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
dStrcpy(mBuf, "serverCmd");
|
||||
dStrcat(mBuf, rmtCommandName);
|
||||
char *temp = mArgv[1];
|
||||
|
||||
dSprintf(idBuf, sizeof(idBuf), "%d", conn->getId());
|
||||
mArgv[0] = mBuf;
|
||||
mArgv[1] = idBuf;
|
||||
|
||||
Con::execute(mArgc+1, (const char **) mArgv);
|
||||
mArgv[1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_CONOBJECT(RemoteCommandEvent);
|
||||
};
|
||||
char RemoteCommandEvent::mBuf[1024];
|
||||
|
||||
IMPLEMENT_CO_NETEVENT_V1(RemoteCommandEvent);
|
||||
|
||||
static void sendRemoteCommand(NetConnection *conn, S32 argc, const char **argv)
|
||||
{
|
||||
if(U8(argv[0][0]) != StringTagPrefixByte)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::Script, "Remote Command Error - command must be a tag.");
|
||||
return;
|
||||
}
|
||||
S32 i;
|
||||
for(i = argc - 1; i >= 0; i--)
|
||||
{
|
||||
if(argv[i][0] != 0)
|
||||
break;
|
||||
argc = i;
|
||||
}
|
||||
for(i = 0; i < argc; i++)
|
||||
conn->validateSendString(argv[i]);
|
||||
RemoteCommandEvent *cevt = new RemoteCommandEvent(argc, argv, conn);
|
||||
conn->postNetEvent(cevt);
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupBegin( Net, "Functions for use with the network; tagged strings and remote commands.");
|
||||
|
||||
ConsoleFunction( commandToServer, void, 2, RemoteCommandEvent::MaxRemoteCommandArgs + 1, "(string func, ...)"
|
||||
"Send a command to the server.")
|
||||
{
|
||||
NetConnection *conn = NetConnection::getConnectionToServer();
|
||||
if(!conn)
|
||||
return;
|
||||
sendRemoteCommand(conn, argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
ConsoleFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(NetConnection client, string func, ...)")
|
||||
{
|
||||
NetConnection *conn;
|
||||
if(!Sim::findObject(argv[1], conn))
|
||||
return;
|
||||
sendRemoteCommand(conn, argc - 2, argv + 2);
|
||||
}
|
||||
|
||||
ConsoleFunction(removeTaggedString, void, 2, 2, "(int tag)")
|
||||
{
|
||||
gNetStringTable->removeString(dAtoi(argv[1]+1), true);
|
||||
}
|
||||
|
||||
ConsoleFunction( addTaggedString, const char*, 2, 2, "(string str)")
|
||||
{
|
||||
StringHandle s(argv[1]);
|
||||
gNetStringTable->incStringRefScript(s.getIndex());
|
||||
|
||||
char *ret = Con::getReturnBuffer(10);
|
||||
ret[0] = StringTagPrefixByte;
|
||||
dSprintf(ret + 1, 9, "%d", s.getIndex());
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConsoleFunction( getTaggedString, const char*, 2, 2, "(int tag)")
|
||||
{
|
||||
const char *indexPtr = argv[1];
|
||||
if (*indexPtr == StringTagPrefixByte)
|
||||
indexPtr++;
|
||||
return gNetStringTable->lookupString(dAtoi(indexPtr));
|
||||
}
|
||||
|
||||
ConsoleFunction( buildTaggedString, const char*, 2, 11, "(string format, ...)")
|
||||
{
|
||||
const char *indexPtr = argv[1];
|
||||
if (*indexPtr == StringTagPrefixByte)
|
||||
indexPtr++;
|
||||
const char *fmtString = gNetStringTable->lookupString(dAtoi(indexPtr));
|
||||
char *strBuffer = Con::getReturnBuffer(512);
|
||||
const char *fmtStrPtr = fmtString;
|
||||
char *strBufPtr = strBuffer;
|
||||
S32 strMaxLength = 511;
|
||||
if (!fmtString)
|
||||
goto done;
|
||||
|
||||
//build the string
|
||||
while (*fmtStrPtr)
|
||||
{
|
||||
//look for an argument tag
|
||||
if (*fmtStrPtr == '%')
|
||||
{
|
||||
if (fmtStrPtr[1] >= '1' && fmtStrPtr[1] <= '9')
|
||||
{
|
||||
S32 argIndex = S32(fmtStrPtr[1] - '0') + 1;
|
||||
if (argIndex >= argc)
|
||||
goto done;
|
||||
const char *argStr = argv[argIndex];
|
||||
if (!argStr)
|
||||
goto done;
|
||||
S32 strLength = dStrlen(argStr);
|
||||
if (strLength > strMaxLength)
|
||||
goto done;
|
||||
dStrcpy(strBufPtr, argStr);
|
||||
strBufPtr += strLength;
|
||||
strMaxLength -= strLength;
|
||||
fmtStrPtr += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//if we don't continue, just copy the character
|
||||
if (strMaxLength <= 0)
|
||||
goto done;
|
||||
*strBufPtr++ = *fmtStrPtr++;
|
||||
strMaxLength--;
|
||||
}
|
||||
|
||||
done:
|
||||
*strBufPtr = '\0';
|
||||
return strBuffer;
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( Net );
|
||||
|
||||
void netInit()
|
||||
{
|
||||
gMasterAddress = "";
|
||||
Con::addVariable( "MasterServerAddress", TypeString, &gMasterAddress );
|
||||
}
|
84
engine/game/net/netTest.cc
Executable file
84
engine/game/net/netTest.cc
Executable file
@ -0,0 +1,84 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/simBase.h"
|
||||
#include "platform/event.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "sim/netObject.h"
|
||||
|
||||
class SimpleMessageEvent : public NetEvent
|
||||
{
|
||||
typedef NetEvent Parent;
|
||||
char *msg;
|
||||
public:
|
||||
SimpleMessageEvent(const char *message = NULL)
|
||||
{
|
||||
if(message)
|
||||
msg = dStrdup(message);
|
||||
else
|
||||
msg = NULL;
|
||||
}
|
||||
~SimpleMessageEvent()
|
||||
{ dFree(msg); }
|
||||
|
||||
virtual void pack(NetConnection* /*ps*/, BitStream *bstream)
|
||||
{ bstream->writeString(msg); }
|
||||
virtual void write(NetConnection*, BitStream *bstream)
|
||||
{ bstream->writeString(msg); }
|
||||
virtual void unpack(NetConnection* /*ps*/, BitStream *bstream)
|
||||
{ char buf[256]; bstream->readString(buf); msg = dStrdup(buf); }
|
||||
virtual void process(NetConnection *)
|
||||
{ Con::printf("RMSG %d %s", mSourceId, msg); }
|
||||
|
||||
DECLARE_CONOBJECT(SimpleMessageEvent);
|
||||
};
|
||||
|
||||
IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent);
|
||||
|
||||
class SimpleNetObject : public NetObject
|
||||
{
|
||||
typedef NetObject Parent;
|
||||
public:
|
||||
char message[256];
|
||||
SimpleNetObject()
|
||||
{
|
||||
mNetFlags.set(ScopeAlways | Ghostable);
|
||||
dStrcpy(message, "Hello World!");
|
||||
}
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
|
||||
{
|
||||
stream->writeString(message);
|
||||
return 0;
|
||||
}
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
stream->readString(message);
|
||||
Con::printf("Got message: %s", message);
|
||||
}
|
||||
void setMessage(const char *msg)
|
||||
{
|
||||
setMaskBits(1);
|
||||
dStrcpy(message, msg);
|
||||
}
|
||||
|
||||
DECLARE_CONOBJECT(SimpleNetObject);
|
||||
};
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject);
|
||||
|
||||
ConsoleMethod( SimpleNetObject, setMessage, void, 3, 3, "(string msg)")
|
||||
{
|
||||
object->setMessage(argv[2]);
|
||||
}
|
||||
|
||||
ConsoleFunction( msg, void, 3, 3, "(NetConnection id, string message)"
|
||||
"Send a SimpleNetObject message to the specified connection.")
|
||||
{
|
||||
NetConnection *con = (NetConnection *) Sim::findObject(argv[1]);
|
||||
if(con)
|
||||
con->postNetEvent(new SimpleMessageEvent(argv[2]));
|
||||
}
|
2051
engine/game/net/serverQuery.cc
Executable file
2051
engine/game/net/serverQuery.cc
Executable file
File diff suppressed because it is too large
Load Diff
125
engine/game/net/serverQuery.h
Executable file
125
engine/game/net/serverQuery.h
Executable file
@ -0,0 +1,125 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SERVERQUERY_H_
|
||||
#define _SERVERQUERY_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _EVENT_H_
|
||||
#include "platform/event.h"
|
||||
#endif
|
||||
#ifndef _BITSET_H_
|
||||
#include "core/bitSet.h"
|
||||
#endif
|
||||
|
||||
#include "sim/netConnection.h"
|
||||
#include "sim/netInterface.h"
|
||||
//-----------------------------------------------------------------------------
|
||||
// Master server information
|
||||
|
||||
class DemoNetInterface : public NetInterface
|
||||
{
|
||||
public:
|
||||
void handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream);
|
||||
};
|
||||
|
||||
struct MasterInfo
|
||||
{
|
||||
NetAddress address;
|
||||
U32 region;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game Server Information
|
||||
|
||||
struct ServerInfo
|
||||
{
|
||||
enum StatusFlags
|
||||
{
|
||||
// Info flags (0-7):
|
||||
Status_Dedicated = BIT(0),
|
||||
Status_Passworded = BIT(1),
|
||||
Status_Linux = BIT(2),
|
||||
|
||||
// Status flags:
|
||||
Status_New = 0,
|
||||
Status_Querying = BIT(28),
|
||||
Status_Updating = BIT(29),
|
||||
Status_Responded = BIT(30),
|
||||
Status_TimedOut = BIT(31),
|
||||
};
|
||||
|
||||
U8 numPlayers;
|
||||
U8 maxPlayers;
|
||||
U8 numBots;
|
||||
char* name;
|
||||
char* gameType;
|
||||
char* missionName;
|
||||
char* missionType;
|
||||
char* statusString;
|
||||
char* infoString;
|
||||
NetAddress address;
|
||||
U32 version;
|
||||
U32 ping;
|
||||
U32 cpuSpeed;
|
||||
bool isFavorite;
|
||||
BitSet32 status;
|
||||
|
||||
ServerInfo()
|
||||
{
|
||||
numPlayers = 0;
|
||||
maxPlayers = 0;
|
||||
numBots = 0;
|
||||
name = NULL;
|
||||
gameType = NULL;
|
||||
missionType = NULL;
|
||||
missionName = NULL;
|
||||
statusString = NULL;
|
||||
infoString = NULL;
|
||||
version = 0;
|
||||
ping = 0;
|
||||
cpuSpeed = 0;
|
||||
isFavorite = false;
|
||||
status = Status_New;
|
||||
}
|
||||
~ServerInfo();
|
||||
|
||||
bool isNew() { return( status == Status_New ); }
|
||||
bool isQuerying() { return( status.test( Status_Querying ) ); }
|
||||
bool isUpdating() { return( status.test( Status_Updating ) ); }
|
||||
bool hasResponded() { return( status.test( Status_Responded ) ); }
|
||||
bool isTimedOut() { return( status.test( Status_TimedOut ) ); }
|
||||
|
||||
bool isDedicated() { return( status.test( Status_Dedicated ) ); }
|
||||
bool isPassworded() { return( status.test( Status_Passworded ) ); }
|
||||
bool isLinux() { return( status.test( Status_Linux ) ); }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern Vector<ServerInfo> gServerList;
|
||||
extern bool gServerBrowserDirty;
|
||||
extern void clearServerList();
|
||||
extern void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType,
|
||||
U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
|
||||
U8 filterFlags);
|
||||
extern void queryMasterGameTypes();
|
||||
extern void queryMasterServer(U8 flags, const char* gameType, const char* missionType,
|
||||
U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
|
||||
U8 filterFlags, U8 buddyCount, U32* buddyList );
|
||||
extern void queryFavoriteServers( U8 flags );
|
||||
extern void querySingleServer(const NetAddress* addr, U8 flags);
|
||||
extern void startHeartbeat();
|
||||
extern void sendHeartbeat( U8 flags );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
extern void addFakeServers( S32 howMany );
|
||||
#endif // DEBUG
|
||||
|
||||
#endif
|
309
engine/game/net/tcpObject.cc
Executable file
309
engine/game/net/tcpObject.cc
Executable file
@ -0,0 +1,309 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/net/tcpObject.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/event.h"
|
||||
#include "platform/gameInterface.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "game/demoGame.h"
|
||||
|
||||
TCPObject *TCPObject::table[TCPObject::TableSize] = {0, };
|
||||
|
||||
IMPLEMENT_CONOBJECT(TCPObject);
|
||||
|
||||
TCPObject *TCPObject::find(NetSocket tag)
|
||||
{
|
||||
for(TCPObject *walk = table[U32(tag) & TableMask]; walk; walk = walk->mNext)
|
||||
if(walk->mTag == tag)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TCPObject::addToTable(NetSocket newTag)
|
||||
{
|
||||
removeFromTable();
|
||||
mTag = newTag;
|
||||
mNext = table[U32(mTag) & TableMask];
|
||||
table[U32(mTag) & TableMask] = this;
|
||||
}
|
||||
|
||||
void TCPObject::removeFromTable()
|
||||
{
|
||||
for(TCPObject **walk = &table[U32(mTag) & TableMask]; *walk; walk = &((*walk)->mNext))
|
||||
{
|
||||
if(*walk == this)
|
||||
{
|
||||
*walk = mNext;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TCPObject::TCPObject()
|
||||
{
|
||||
mBuffer = NULL;
|
||||
mBufferSize = 0;
|
||||
mPort = 0;
|
||||
mTag = InvalidSocket;
|
||||
mNext = NULL;
|
||||
mState = Disconnected;
|
||||
}
|
||||
|
||||
TCPObject::~TCPObject()
|
||||
{
|
||||
disconnect();
|
||||
dFree(mBuffer);
|
||||
}
|
||||
|
||||
bool TCPObject::processArguments(S32 argc, const char **argv)
|
||||
{
|
||||
if(argc == 0)
|
||||
return true;
|
||||
else if(argc == 1)
|
||||
{
|
||||
addToTable(U32(dAtoi(argv[0])));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TCPObject::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
const char *name = getName();
|
||||
|
||||
if(name && name[0] && getClassRep())
|
||||
{
|
||||
Namespace *parent = getClassRep()->getNameSpace();
|
||||
Con::linkNamespaces(parent->mName, name);
|
||||
mNameSpace = Con::lookupNamespace(name);
|
||||
|
||||
}
|
||||
|
||||
Sim::getTCPGroup()->addObject(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 TCPObject::onReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
// we got a raw buffer event
|
||||
// default action is to split the buffer into lines of text
|
||||
// and call processLine on each
|
||||
// for any incomplete lines we have mBuffer
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
|
||||
void TCPObject::parseLine(U8 *buffer, U32 *start, U32 bufferLen)
|
||||
{
|
||||
// find the first \n in buffer
|
||||
U32 i;
|
||||
U8 *line = buffer + *start;
|
||||
|
||||
for(i = *start; i < bufferLen; i++)
|
||||
if(buffer[i] == '\n' || buffer[i] == 0)
|
||||
break;
|
||||
U32 len = i - *start;
|
||||
|
||||
if(i == bufferLen || mBuffer)
|
||||
{
|
||||
// we've hit the end with no newline
|
||||
mBuffer = (U8 *) dRealloc(mBuffer, mBufferSize + len + 1);
|
||||
dMemcpy(mBuffer + mBufferSize, line, len);
|
||||
mBufferSize += len;
|
||||
*start = i;
|
||||
|
||||
// process the line
|
||||
if(i != bufferLen)
|
||||
{
|
||||
mBuffer[mBufferSize] = 0;
|
||||
if(mBufferSize && mBuffer[mBufferSize-1] == '\r')
|
||||
mBuffer[mBufferSize - 1] = 0;
|
||||
U8 *temp = mBuffer;
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
|
||||
processLine(temp);
|
||||
dFree(temp);
|
||||
}
|
||||
}
|
||||
else if(i != bufferLen)
|
||||
{
|
||||
line[len] = 0;
|
||||
if(len && line[len-1] == '\r')
|
||||
line[len-1] = 0;
|
||||
processLine(line);
|
||||
}
|
||||
if(i != bufferLen)
|
||||
*start = i + 1;
|
||||
}
|
||||
|
||||
void TCPObject::onConnectionRequest(const NetAddress *addr, U32 connectId)
|
||||
{
|
||||
char idBuf[16];
|
||||
char addrBuf[256];
|
||||
Net::addressToString(addr, addrBuf);
|
||||
dSprintf(idBuf, sizeof(idBuf), "%d", connectId);
|
||||
Con::executef(this, 3, "onConnectRequest", addrBuf, idBuf);
|
||||
}
|
||||
|
||||
bool TCPObject::processLine(U8 *line)
|
||||
{
|
||||
Con::executef(this, 2, "onLine", line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPObject::onDNSResolved()
|
||||
{
|
||||
mState = DNSResolved;
|
||||
Con::executef(this, 1, "onDNSResolved");
|
||||
}
|
||||
|
||||
void TCPObject::onDNSFailed()
|
||||
{
|
||||
mState = Disconnected;
|
||||
Con::executef(this, 1, "onDNSFailed");
|
||||
}
|
||||
|
||||
void TCPObject::onConnected()
|
||||
{
|
||||
mState = Connected;
|
||||
Con::executef(this, 1, "onConnected");
|
||||
}
|
||||
|
||||
void TCPObject::onConnectFailed()
|
||||
{
|
||||
mState = Disconnected;
|
||||
Con::executef(this, 1, "onConnectFailed");
|
||||
}
|
||||
|
||||
void TCPObject::finishLastLine()
|
||||
{
|
||||
if(mBufferSize)
|
||||
{
|
||||
mBuffer[mBufferSize] = 0;
|
||||
processLine(mBuffer);
|
||||
dFree(mBuffer);
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPObject::onDisconnect()
|
||||
{
|
||||
finishLastLine();
|
||||
mState = Disconnected;
|
||||
Con::executef(this, 1, "onDisconnect");
|
||||
}
|
||||
|
||||
void TCPObject::listen(U16 port)
|
||||
{
|
||||
mState = Listening;
|
||||
U32 newTag = Net::openListenPort(port);
|
||||
addToTable(newTag);
|
||||
}
|
||||
|
||||
void TCPObject::connect(const char *address)
|
||||
{
|
||||
NetSocket newTag = Net::openConnectTo(address);
|
||||
addToTable(newTag);
|
||||
}
|
||||
|
||||
void TCPObject::disconnect()
|
||||
{
|
||||
if( mTag != InvalidSocket ) {
|
||||
Net::closeConnectTo(mTag);
|
||||
}
|
||||
removeFromTable();
|
||||
}
|
||||
|
||||
void TCPObject::send(const U8 *buffer, U32 len)
|
||||
{
|
||||
Net::sendtoSocket(mTag, buffer, S32(len));
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, send, void, 3, 0, "(...)"
|
||||
"Parameters are transmitted as strings, one at a time.")
|
||||
{
|
||||
for(S32 i = 2; i < argc; i++)
|
||||
object->send((const U8 *) argv[i], dStrlen(argv[i]));
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, listen, void, 3, 3, "(int port)"
|
||||
"Start listening on the specified ports for connections.")
|
||||
{
|
||||
object->listen(U32(dAtoi(argv[2])));
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, connect, void, 3, 3, "(string addr)"
|
||||
"Connect to the given address.")
|
||||
{
|
||||
object->connect(argv[2]);
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, disconnect, void, 2, 2, "Disconnect from whatever we're connected to, if anything.")
|
||||
{
|
||||
object->disconnect();
|
||||
}
|
||||
|
||||
void DemoGame::processConnectedReceiveEvent(ConnectedReceiveEvent* event )
|
||||
{
|
||||
TCPObject *tcpo = TCPObject::find(event->tag);
|
||||
if(!tcpo)
|
||||
{
|
||||
Con::printf("Got bad connected receive event.");
|
||||
return;
|
||||
}
|
||||
U32 size = U32(event->size - ConnectedReceiveEventHeaderSize);
|
||||
U8 *buffer = event->data;
|
||||
|
||||
while(size)
|
||||
{
|
||||
U32 ret = tcpo->onReceive(buffer, size);
|
||||
AssertFatal(ret <= size, "Invalid return size");
|
||||
size -= ret;
|
||||
buffer += ret;
|
||||
}
|
||||
}
|
||||
|
||||
void DemoGame::processConnectedAcceptEvent( ConnectedAcceptEvent* event )
|
||||
{
|
||||
TCPObject *tcpo = TCPObject::find(event->portTag);
|
||||
if(!tcpo)
|
||||
return;
|
||||
tcpo->onConnectionRequest(&event->address, event->connectionTag);
|
||||
}
|
||||
|
||||
void DemoGame::processConnectedNotifyEvent( ConnectedNotifyEvent* event )
|
||||
{
|
||||
TCPObject *tcpo = TCPObject::find(event->tag);
|
||||
if(!tcpo)
|
||||
return;
|
||||
switch(event->state)
|
||||
{
|
||||
case ConnectedNotifyEvent::DNSResolved:
|
||||
tcpo->onDNSResolved();
|
||||
break;
|
||||
case ConnectedNotifyEvent::DNSFailed:
|
||||
tcpo->onDNSFailed();
|
||||
break;
|
||||
case ConnectedNotifyEvent::Connected:
|
||||
tcpo->onConnected();
|
||||
break;
|
||||
case ConnectedNotifyEvent::ConnectFailed:
|
||||
tcpo->onConnectFailed();
|
||||
break;
|
||||
case ConnectedNotifyEvent::Disconnected:
|
||||
tcpo->onDisconnect();
|
||||
break;
|
||||
}
|
||||
}
|
69
engine/game/net/tcpObject.h
Executable file
69
engine/game/net/tcpObject.h
Executable file
@ -0,0 +1,69 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TCPOBJECT_H_
|
||||
#define _TCPOBJECT_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
class TCPObject : public SimObject
|
||||
{
|
||||
public:
|
||||
enum State {Disconnected, DNSResolved, Connected, Listening };
|
||||
|
||||
private:
|
||||
NetSocket mTag;
|
||||
TCPObject *mNext;
|
||||
enum { TableSize = 256, TableMask = 0xFF };
|
||||
static TCPObject *table[TableSize];
|
||||
State mState;
|
||||
|
||||
protected:
|
||||
typedef SimObject Parent;
|
||||
U8 *mBuffer;
|
||||
U32 mBufferSize;
|
||||
U16 mPort;
|
||||
|
||||
public:
|
||||
TCPObject();
|
||||
virtual ~TCPObject();
|
||||
|
||||
void parseLine(U8 *buffer, U32 *start, U32 bufferLen);
|
||||
void finishLastLine();
|
||||
|
||||
static TCPObject *find(NetSocket tag);
|
||||
|
||||
// onReceive gets called continuously until all bytes are processed
|
||||
// return # of bytes processed each time.
|
||||
virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data
|
||||
virtual bool processLine(U8 *line); // process a complete line of text... default action is to call into script
|
||||
virtual void onDNSResolved();
|
||||
virtual void onDNSFailed();
|
||||
virtual void onConnected();
|
||||
virtual void onConnectFailed();
|
||||
virtual void onConnectionRequest(const NetAddress *addr, U32 connectId);
|
||||
virtual void onDisconnect();
|
||||
void connect(const char *address);
|
||||
void listen(U16 port);
|
||||
void disconnect();
|
||||
State getState() { return mState; }
|
||||
|
||||
bool processArguments(S32 argc, const char **argv);
|
||||
void send(const U8 *buffer, U32 bufferLen);
|
||||
void addToTable(NetSocket newTag);
|
||||
void removeFromTable();
|
||||
|
||||
void setPort(U16 port) { mPort = port; }
|
||||
|
||||
bool onAdd();
|
||||
|
||||
DECLARE_CONOBJECT(TCPObject);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_TCPOBJECT_
|
Reference in New Issue
Block a user