dnet.cpp
Public Enumerations
enum
NetPacketType { DataPacket PingPacket AckPacket InvalidPacketType }
Public Variables
Public Functions
DefineEngineFunction(DNetSetLogging , void , (bool enabled) , "(bool enabled)" "@brief Enables logging of the connection <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">protocols\n\n</a>" "When enabled <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lot of network debugging information is sent <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param enabled True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> enable, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">disable\n</a>" " @ingroup Networking" )
Detailed Description
Public Enumerations
NetPacketType
Enumerator
- DataPacket
- PingPacket
- AckPacket
- InvalidPacketType
Public Variables
bool gLogToConsole
S32 gNetBitsReceived
const char * packetTypeNames []
Public Functions
DefineEngineFunction(DNetSetLogging , void , (bool enabled) , "(bool enabled)" "@brief Enables logging of the connection <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">protocols\n\n</a>" "When enabled <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lot of network debugging information is sent <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param enabled True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> enable, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">disable\n</a>" " @ingroup Networking" )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "core/stream/bitStream.h" 25#include "core/dnet.h" 26#include "core/strings/stringFunctions.h" 27 28#include "console/consoleTypes.h" 29#include "console/engineAPI.h" 30 31 32bool gLogToConsole = false; 33 34S32 gNetBitsReceived = 0; 35 36enum NetPacketType 37{ 38 DataPacket, 39 PingPacket, 40 AckPacket, 41 InvalidPacketType, 42}; 43 44static const char *packetTypeNames[] = 45{ 46 "DataPacket", 47 "PingPacket", 48 "AckPacket", 49}; 50 51//----------------------------------------------------------------- 52//----------------------------------------------------------------- 53//----------------------------------------------------------------- 54DefineEngineFunction( DNetSetLogging, void, (bool enabled), , "(bool enabled)" 55 "@brief Enables logging of the connection protocols\n\n" 56 "When enabled a lot of network debugging information is sent to the console.\n" 57 "@param enabled True to enable, false to disable\n" 58 "@ingroup Networking") 59{ 60 gLogToConsole = enabled; 61} 62 63ConnectionProtocol::ConnectionProtocol() 64{ 65 mLastSeqRecvd = 0; 66 mHighestAckedSeq = 0; 67 mLastSendSeq = 0; // start sending at 1 68 mAckMask = 0; 69 mLastRecvAckAck = 0; 70 mConnectionEstablished = false; 71} 72void ConnectionProtocol::buildSendPacketHeader(BitStream *stream, S32 packetType) 73{ 74 S32 ackByteCount = ((mLastSeqRecvd - mLastRecvAckAck + 7) >> 3); 75 AssertFatal(ackByteCount <= 4, "Too few ack bytes!"); 76 77 // S32 headerSize = 3 + ackByteCount; 78 79 if(packetType == DataPacket) 80 mLastSendSeq++; 81 82 stream->writeFlag(true); 83 stream->writeInt(mConnectSequence & 1, 1); 84 stream->writeInt(mLastSendSeq & 0x1FF, 9); 85 stream->writeInt(mLastSeqRecvd & 0x1FF, 9); 86 stream->writeInt(packetType & 0x3, 2); 87 stream->writeInt(ackByteCount & 0x7, 3); 88 U32 bitmask = ~(0xFFFFFFFF << (ackByteCount*8)); 89 if(ackByteCount == 4) 90 { 91 // Performing a bit shift that is the same size as the variable being shifted 92 // is undefined in the C/C++ standard. Handle that exception here when 93 // ackByteCount*8 == 4*8 == 32 94 bitmask = 0xFFFFFFFF; 95 } 96 stream->writeInt(mAckMask & bitmask, ackByteCount * 8); 97 98 // if we're resending this header, we can't advance the 99 // sequence recieved (in case this packet drops and the prev one 100 // goes through) 101 102 if(gLogToConsole) 103 Con::printf("build hdr %d %d", mLastSendSeq, packetType); 104 105 if(packetType == DataPacket) 106 mLastSeqRecvdAtSend[mLastSendSeq & 0x1F] = mLastSeqRecvd; 107} 108 109void ConnectionProtocol::sendPingPacket() 110{ 111 U8 buffer[16]; 112 BitStream bs(buffer, 16); 113 buildSendPacketHeader(&bs, PingPacket); 114 if(gLogToConsole) 115 Con::printf("send ping %d", mLastSendSeq); 116 117 sendPacket(&bs); 118} 119 120void ConnectionProtocol::sendAckPacket() 121{ 122 U8 buffer[16]; 123 BitStream bs(buffer, 16); 124 buildSendPacketHeader(&bs, AckPacket); 125 if(gLogToConsole) 126 Con::printf("send ack %d", mLastSendSeq); 127 128 sendPacket(&bs); 129} 130 131// packets are read directly into the data portion of 132// connection notify packets... makes the events easier to post into 133// the system. 134 135void ConnectionProtocol::processRawPacket(BitStream *pstream) 136{ 137 // read in the packet header: 138 139 // Fixed packet header: 3 bytes 140 // 141 // 1 bit game packet flag 142 // 1 bit connect sequence 143 // 9 bits packet seq number 144 // 9 bits ackstart seq number 145 // 2 bits packet type 146 // 2 bits ack byte count 147 // 148 // type is: 149 // 00 data packet 150 // 01 ping packet 151 // 02 ack packet 152 153 // next 1-4 bytes are ack flags 154 // 155 // header len is 4-9 bytes 156 // average case 4 byte header 157 158 gNetBitsReceived = pstream->getStreamSize(); 159 160 pstream->readFlag(); // get rid of the game info packet bit 161 U32 pkConnectSeqBit = pstream->readInt(1); 162 U32 pkSequenceNumber = pstream->readInt(9); 163 U32 pkHighestAck = pstream->readInt(9); 164 U32 pkPacketType = pstream->readInt(2); 165 S32 pkAckByteCount = pstream->readInt(3); 166 167 // check connection sequence bit 168 if(pkConnectSeqBit != (mConnectSequence & 1)) 169 return; 170 171 if(pkAckByteCount > 4 || pkPacketType >= InvalidPacketType) 172 return; 173 174 S32 pkAckMask = pstream->readInt(8 * pkAckByteCount); 175 176 // verify packet ordering and acking and stuff 177 // check if the 9-bit sequence is within the packet window 178 // (within 31 packets of the last received sequence number). 179 180 pkSequenceNumber |= (mLastSeqRecvd & 0xFFFFFE00); 181 // account for wrap around 182 if(pkSequenceNumber < mLastSeqRecvd) 183 pkSequenceNumber += 0x200; 184 185 if(pkSequenceNumber > mLastSeqRecvd + 31) 186 { 187 // the sequence number is outside the window... must be out of order 188 // discard. 189 return; 190 } 191 192 pkHighestAck |= (mHighestAckedSeq & 0xFFFFFE00); 193 // account for wrap around 194 195 if(pkHighestAck < mHighestAckedSeq) 196 pkHighestAck += 0x200; 197 198 if(pkHighestAck > mLastSendSeq) 199 { 200 // the ack number is outside the window... must be an out of order 201 // packet, discard. 202 return; 203 } 204 205 if(gLogToConsole) 206 { 207 for(U32 i = mLastSeqRecvd+1; i < pkSequenceNumber; i++) 208 Con::printf("Not recv %d", i); 209 Con::printf("Recv %d %s", pkSequenceNumber, packetTypeNames[pkPacketType]); 210 } 211 212 // shift up the ack mask by the packet difference 213 // this essentially nacks all the packets dropped 214 215 mAckMask <<= pkSequenceNumber - mLastSeqRecvd; 216 217 // if this packet is a data packet (i.e. not a ping packet or an ack packet), ack it 218 if(pkPacketType == DataPacket) 219 mAckMask |= 1; 220 221 // do all the notifies... 222 for(U32 i = mHighestAckedSeq+1; i <= pkHighestAck; i++) 223 { 224 bool packetTransmitSuccess = pkAckMask & (1 << (pkHighestAck - i)); 225 handleNotify(packetTransmitSuccess); 226 if(gLogToConsole) 227 Con::printf("Ack %d %d", i, packetTransmitSuccess); 228 229 if(packetTransmitSuccess) 230 { 231 mLastRecvAckAck = mLastSeqRecvdAtSend[i & 0x1F]; 232 if(!mConnectionEstablished) 233 { 234 mConnectionEstablished = true; 235 handleConnectionEstablished(); 236 } 237 } 238 } 239 240 // the other side knows more about its window than we do. 241 if(pkSequenceNumber - mLastRecvAckAck > 32) 242 mLastRecvAckAck = pkSequenceNumber - 32; 243 244 mHighestAckedSeq = pkHighestAck; 245 246 // first things first... 247 // ackback any pings or accept connects 248 249 if(pkPacketType == PingPacket) 250 { 251 // send an ack to the other side 252 // the ack will have the same packet sequence as our last sent packet 253 // if the last packet we sent was the connection accepted packet 254 // we must resend that packet 255 sendAckPacket(); 256 } 257 keepAlive(); // notification that the connection is ok 258 259 // note: handlePacket() may delete the connection if an error occurs. 260 if(mLastSeqRecvd != pkSequenceNumber) 261 { 262 mLastSeqRecvd = pkSequenceNumber; 263 if(pkPacketType == DataPacket) 264 handlePacket(pstream); 265 } 266} 267 268bool ConnectionProtocol::windowFull() 269{ 270 return mLastSendSeq - mHighestAckedSeq >= 30; 271} 272 273void ConnectionProtocol::writeDemoStartBlock(ResizeBitStream *stream) 274{ 275 for(U32 i = 0; i < 32; i++) 276 stream->write(mLastSeqRecvdAtSend[i]); 277 stream->write(mLastSeqRecvd); 278 stream->write(mHighestAckedSeq); 279 stream->write(mLastSendSeq); 280 stream->write(mAckMask); 281 stream->write(mConnectSequence); 282 stream->write(mLastRecvAckAck); 283 stream->write(mConnectionEstablished); 284} 285 286bool ConnectionProtocol::readDemoStartBlock(BitStream *stream) 287{ 288 for(U32 i = 0; i < 32; i++) 289 stream->read(&mLastSeqRecvdAtSend[i]); 290 stream->read(&mLastSeqRecvd); 291 stream->read(&mHighestAckedSeq); 292 stream->read(&mLastSendSeq); 293 stream->read(&mAckMask); 294 stream->read(&mConnectSequence); 295 stream->read(&mLastRecvAckAck); 296 stream->read(&mConnectionEstablished); 297 return true; 298} 299