310 lines
7.0 KiB
C++
Executable File
310 lines
7.0 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|