Initial commit
This commit is contained in:
263
Torque/SDK/engine/core/dnet.cc
Normal file
263
Torque/SDK/engine/core/dnet.cc
Normal file
@@ -0,0 +1,263 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/dnet.h"
|
||||
#include "console/console.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
bool gLogToConsole = false;
|
||||
|
||||
S32 gNetBitsReceived = 0;
|
||||
|
||||
enum NetPacketType
|
||||
{
|
||||
DataPacket,
|
||||
PingPacket,
|
||||
AckPacket,
|
||||
InvalidPacketType,
|
||||
};
|
||||
|
||||
static const char *packetTypeNames[] =
|
||||
{
|
||||
"DataPacket",
|
||||
"PingPacket",
|
||||
"AckPacket",
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//-----------------------------------------------------------------
|
||||
//-----------------------------------------------------------------
|
||||
ConsoleFunction(DNetSetLogging, void, 2, 2, "(bool enabled)")
|
||||
{
|
||||
argc;
|
||||
gLogToConsole = dAtob(argv[1]);
|
||||
}
|
||||
|
||||
ConnectionProtocol::ConnectionProtocol()
|
||||
{
|
||||
mLastSeqRecvd = 0;
|
||||
mHighestAckedSeq = 0;
|
||||
mLastSendSeq = 0; // start sending at 1
|
||||
mAckMask = 0;
|
||||
mLastRecvAckAck = 0;
|
||||
}
|
||||
void ConnectionProtocol::buildSendPacketHeader(BitStream *stream, S32 packetType)
|
||||
{
|
||||
S32 ackByteCount = ((mLastSeqRecvd - mLastRecvAckAck + 7) >> 3);
|
||||
AssertFatal(ackByteCount <= 4, "Too few ack bytes!");
|
||||
|
||||
S32 headerSize = 3 + ackByteCount;
|
||||
S32 datalen = 0;
|
||||
|
||||
if(packetType == DataPacket)
|
||||
mLastSendSeq++;
|
||||
|
||||
stream->writeFlag(true);
|
||||
stream->writeInt(mConnectSequence & 1, 1);
|
||||
stream->writeInt(mLastSendSeq, 9);
|
||||
stream->writeInt(mLastSeqRecvd, 9);
|
||||
stream->writeInt(packetType, 2);
|
||||
stream->writeInt(ackByteCount, 3);
|
||||
stream->writeInt(mAckMask, ackByteCount * 8);
|
||||
|
||||
// if we're resending this header, we can't advance the
|
||||
// sequence recieved (in case this packet drops and the prev one
|
||||
// goes through)
|
||||
|
||||
if(gLogToConsole)
|
||||
Con::printf("build hdr %d %d", mLastSendSeq, packetType);
|
||||
|
||||
if(packetType == DataPacket)
|
||||
mLastSeqRecvdAtSend[mLastSendSeq & 0x1F] = mLastSeqRecvd;
|
||||
}
|
||||
|
||||
void ConnectionProtocol::sendPingPacket()
|
||||
{
|
||||
U8 buffer[16];
|
||||
BitStream bs(buffer, 16);
|
||||
buildSendPacketHeader(&bs, PingPacket);
|
||||
if(gLogToConsole)
|
||||
Con::printf("send ping %d", mLastSendSeq);
|
||||
|
||||
sendPacket(&bs);
|
||||
}
|
||||
|
||||
void ConnectionProtocol::sendAckPacket()
|
||||
{
|
||||
U8 buffer[16];
|
||||
BitStream bs(buffer, 16);
|
||||
buildSendPacketHeader(&bs, AckPacket);
|
||||
if(gLogToConsole)
|
||||
Con::printf("send ack %d", mLastSendSeq);
|
||||
|
||||
sendPacket(&bs);
|
||||
}
|
||||
|
||||
// packets are read directly into the data portion of
|
||||
// connection notify packets... makes the events easier to post into
|
||||
// the system.
|
||||
|
||||
void ConnectionProtocol::processRawPacket(BitStream *pstream)
|
||||
{
|
||||
// read in the packet header:
|
||||
|
||||
// Fixed packet header: 3 bytes
|
||||
//
|
||||
// 1 bit game packet flag
|
||||
// 1 bit connect sequence
|
||||
// 9 bits packet seq number
|
||||
// 9 bits ackstart seq number
|
||||
// 2 bits packet type
|
||||
// 2 bits ack byte count
|
||||
//
|
||||
// type is:
|
||||
// 00 data packet
|
||||
// 01 ping packet
|
||||
// 02 ack packet
|
||||
|
||||
// next 1-4 bytes are ack flags
|
||||
//
|
||||
// header len is 4-9 bytes
|
||||
// average case 4 byte header
|
||||
|
||||
gNetBitsReceived = pstream->getStreamSize();
|
||||
|
||||
pstream->readFlag(); // get rid of the game info packet bit
|
||||
U32 pkConnectSeqBit = pstream->readInt(1);
|
||||
U32 pkSequenceNumber = pstream->readInt(9);
|
||||
U32 pkHighestAck = pstream->readInt(9);
|
||||
U32 pkPacketType = pstream->readInt(2);
|
||||
S32 pkAckByteCount = pstream->readInt(3);
|
||||
|
||||
// check connection sequence bit
|
||||
if(pkConnectSeqBit != (mConnectSequence & 1))
|
||||
return;
|
||||
|
||||
if(pkAckByteCount > 4 || pkPacketType >= InvalidPacketType)
|
||||
return;
|
||||
|
||||
S32 pkAckMask = pstream->readInt(8 * pkAckByteCount);
|
||||
|
||||
// verify packet ordering and acking and stuff
|
||||
// check if the 9-bit sequence is within the packet window
|
||||
// (within 31 packets of the last received sequence number).
|
||||
|
||||
pkSequenceNumber |= (mLastSeqRecvd & 0xFFFFFE00);
|
||||
// account for wrap around
|
||||
if(pkSequenceNumber < mLastSeqRecvd)
|
||||
pkSequenceNumber += 0x200;
|
||||
|
||||
if(pkSequenceNumber > mLastSeqRecvd + 31)
|
||||
{
|
||||
// the sequence number is outside the window... must be out of order
|
||||
// discard.
|
||||
return;
|
||||
}
|
||||
|
||||
pkHighestAck |= (mHighestAckedSeq & 0xFFFFFE00);
|
||||
// account for wrap around
|
||||
|
||||
if(pkHighestAck < mHighestAckedSeq)
|
||||
pkHighestAck += 0x200;
|
||||
|
||||
if(pkHighestAck > mLastSendSeq)
|
||||
{
|
||||
// the ack number is outside the window... must be an out of order
|
||||
// packet, discard.
|
||||
return;
|
||||
}
|
||||
|
||||
if(gLogToConsole)
|
||||
{
|
||||
for(U32 i = mLastSeqRecvd+1; i < pkSequenceNumber; i++)
|
||||
Con::printf("Not recv %d", i);
|
||||
Con::printf("Recv %d %s", pkSequenceNumber, packetTypeNames[pkPacketType]);
|
||||
}
|
||||
|
||||
// shift up the ack mask by the packet difference
|
||||
// this essentially nacks all the packets dropped
|
||||
|
||||
mAckMask <<= pkSequenceNumber - mLastSeqRecvd;
|
||||
|
||||
// if this packet is a data packet (i.e. not a ping packet or an ack packet), ack it
|
||||
if(pkPacketType == DataPacket)
|
||||
mAckMask |= 1;
|
||||
|
||||
// do all the notifies...
|
||||
for(U32 i = mHighestAckedSeq+1; i <= pkHighestAck; i++)
|
||||
{
|
||||
bool packetTransmitSuccess = pkAckMask & (1 << (pkHighestAck - i));
|
||||
handleNotify(packetTransmitSuccess);
|
||||
if(gLogToConsole)
|
||||
Con::printf("Ack %d %d", i, packetTransmitSuccess);
|
||||
|
||||
if(packetTransmitSuccess)
|
||||
{
|
||||
mLastRecvAckAck = mLastSeqRecvdAtSend[i & 0x1F];
|
||||
if(!mConnectionEstablished)
|
||||
{
|
||||
mConnectionEstablished = true;
|
||||
handleConnectionEstablished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the other side knows more about its window than we do.
|
||||
if(pkSequenceNumber - mLastRecvAckAck > 32)
|
||||
mLastRecvAckAck = pkSequenceNumber - 32;
|
||||
|
||||
mHighestAckedSeq = pkHighestAck;
|
||||
|
||||
// first things first...
|
||||
// ackback any pings or accept connects
|
||||
|
||||
if(pkPacketType == PingPacket)
|
||||
{
|
||||
// send an ack to the other side
|
||||
// the ack will have the same packet sequence as our last sent packet
|
||||
// if the last packet we sent was the connection accepted packet
|
||||
// we must resend that packet
|
||||
sendAckPacket();
|
||||
}
|
||||
keepAlive(); // notification that the connection is ok
|
||||
|
||||
if(mLastSeqRecvd != pkSequenceNumber && pkPacketType == DataPacket)
|
||||
handlePacket(pstream);
|
||||
|
||||
mLastSeqRecvd = pkSequenceNumber;
|
||||
}
|
||||
|
||||
bool ConnectionProtocol::windowFull()
|
||||
{
|
||||
return mLastSendSeq - mHighestAckedSeq >= 30;
|
||||
}
|
||||
|
||||
void ConnectionProtocol::writeDemoStartBlock(ResizeBitStream *stream)
|
||||
{
|
||||
for(U32 i = 0; i < 32; i++)
|
||||
stream->write(mLastSeqRecvdAtSend[i]);
|
||||
stream->write(mLastSeqRecvd);
|
||||
stream->write(mHighestAckedSeq);
|
||||
stream->write(mLastSendSeq);
|
||||
stream->write(mAckMask);
|
||||
stream->write(mConnectSequence);
|
||||
stream->write(mLastRecvAckAck);
|
||||
stream->write(mConnectionEstablished);
|
||||
}
|
||||
|
||||
bool ConnectionProtocol::readDemoStartBlock(BitStream *stream)
|
||||
{
|
||||
for(U32 i = 0; i < 32; i++)
|
||||
stream->read(&mLastSeqRecvdAtSend[i]);
|
||||
stream->read(&mLastSeqRecvd);
|
||||
stream->read(&mHighestAckedSeq);
|
||||
stream->read(&mLastSendSeq);
|
||||
stream->read(&mAckMask);
|
||||
stream->read(&mConnectSequence);
|
||||
stream->read(&mLastRecvAckAck);
|
||||
stream->read(&mConnectionEstablished);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user