tge/engine/core/dnet.cc
2017-04-17 06:17:10 -06:00

264 lines
7.3 KiB
C++
Executable File

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