tge/engine/game/net/tcpObject.cc
2017-04-17 06:17:10 -06:00

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;
}
}