void NetChannel::TransmitOutgoing() { byte send_buf[NET_MAX_MESSAGE]; BitBuffer data(send_buf, sizeof(send_buf)); bool send_reliable; bool send_reliable_fragment; bool send_resending = false; unsigned w1, w2; int i, j; if (IsFakeChannel()) { m_outgoing_sequence++; m_last_send = m_System->GetTime(); m_cleartime = m_last_send + m_send_interval; m_reliableStream.FastClear(); m_unreliableStream.FastClear(); FakeAcknowledgement(); return; } // check for reliable message overflow if (m_reliableStream.IsOverflowed()) { m_System->DPrintf("Transmit:Outgoing m_reliableStream overflow (%s)\n", m_remote_address.ToString()); m_reliableStream.Clear(); return; } // check for unreliable message overflow if (m_unreliableStream.IsOverflowed()) { m_System->DPrintf("Transmit:Outgoing m_unreliableStream overflow (%s)\n", m_remote_address.ToString()); m_unreliableStream.Clear(); } // if the remote side dropped the last reliable message, resend it send_reliable = false; if (m_incoming_acknowledged > m_last_reliable_sequence && m_incoming_reliable_acknowledged != m_reliable_sequence) { send_reliable = true; send_resending = true; } // A packet can have "reliable payload + frag payload + unreliable payload // frag payload can be a file chunk, if so, it needs to be parsed on the receiving end and reliable payload + unreliable payload need // to be passed on to the message queue. The processing routine needs to be able to handle the case where a message comes in and a file // transfer completes // // if the reliable transmit buffer is empty, copy the current message out if (!m_reliableOutSize) { bool send_frag = false; fragbuf_t *pbuf; // Will be true if we are active and should let chan->message get some bandwidth int send_from_frag[MAX_STREAMS] = { 0, 0 }; int send_from_regular = 0; // If we have data in the waiting list(s) and we have cleared the current queue(s), then // push the m_waitlist(s) into the current queue(s) FragSend(); // Sending regular payload send_from_regular = m_reliableStream.CurrentSize() ? 1 : 0; // Check to see if we are sending a frag payload for (i = 0; i < MAX_STREAMS; i++) { if (m_fragbufs[i]) { send_from_frag[i] = 1; } } /*if (m_reliableStream.CurrentSize() > sizeof(send_buf)) { CreateFragmentsFromBuffer(m_reliableStream.GetData(), m_reliableStream.CurrentSize(), FRAG_NORMAL_STREAM); m_reliableStream.FastClear(); }*/ // Stall reliable payloads if sending from frag buffer if (send_from_regular && (send_from_frag[FRAG_NORMAL_STREAM])) { send_from_regular = false; // If the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer if (m_reliableStream.CurrentSize() > MAX_RELIABLE_PAYLOAD) { CreateFragmentsFromBuffer(m_reliableStream.GetData(), m_reliableStream.CurrentSize(), FRAG_NORMAL_STREAM); m_reliableStream.FastClear(); } } // Startpos will be zero if there is no regular payload for (i = 0; i < MAX_STREAMS; i++) { m_frag_startpos[i] = 0; // Assume no fragment is being sent m_reliable_fragment[i] = 0; m_reliable_fragid[i] = 0; m_frag_length[i] = 0; if (send_from_frag[i]) { send_frag = true; } } if (send_from_regular || send_frag) { m_reliable_sequence ^= 1u; send_reliable = true; } if (send_from_regular) { memcpy(m_reliableOutBuffer, m_reliableStream.GetData(), m_reliableStream.CurrentSize()); m_reliableOutSize = m_reliableStream.CurrentSize(); m_reliableStream.FastClear(); // If we send fragments, this is where they'll start for (i = 0; i < MAX_STREAMS; i++) { m_frag_startpos[i] = m_reliableOutSize; } } for (i = 0; i < MAX_STREAMS; i++) { int fragment_size = 0; // Is there something in the fragbuf? pbuf = m_fragbufs[i]; if (pbuf) { fragment_size = pbuf->size; // Files set size a bit differently. if (pbuf->isfile && !pbuf->isbuffer) { fragment_size = pbuf->size; } } // Make sure we have enought space left if (send_from_frag[i] && pbuf && (m_reliableOutSize + fragment_size) < MAX_RELIABLE_PAYLOAD) { m_reliable_fragid[i] = MAKE_FRAGID(pbuf->bufferId, m_fragbufcount[i]); // Which buffer are we sending? // If it's not in-memory, then we'll need to copy it in frame the file handle. if (pbuf->isfile && !pbuf->isbuffer) { m_System->Printf("TODO! NetChannel::Transmit: system file support\n"); } memcpy(m_reliableOutBuffer + m_reliableOutSize, pbuf->data, pbuf->size); m_reliableOutSize += pbuf->size; m_frag_length[i] = pbuf->size; // Unlink pbuf UnlinkFragment(pbuf, i); m_reliable_fragment[i] = 1; // Offset the rest of the starting positions for (j = i + 1; j < MAX_STREAMS; j++) { m_frag_startpos[j] += m_frag_length[i]; } } } } // Prepare the packet header w1 = m_outgoing_sequence | (send_reliable << 31); w2 = m_incoming_sequence | (m_incoming_reliable_sequence << 31); send_reliable_fragment = false; for (i = 0; i < MAX_STREAMS; i++) { if (m_reliable_fragment[i]) { send_reliable_fragment = true; break; } } if (send_reliable && send_reliable_fragment) { w1 |= (1 << 30); } m_outgoing_sequence++; data.Clear(); data.WriteLong(w1); data.WriteLong(w2); if (send_reliable && send_reliable_fragment) { for (i = 0; i < MAX_STREAMS; i++) { if (m_reliable_fragment[i]) { data.WriteByte(1); data.WriteLong(m_reliable_fragid[i]); data.WriteShort(m_frag_startpos[i]); data.WriteShort(m_frag_length[i]); } else { data.WriteByte(0); } } } // Copy the reliable message to the packet first if (send_reliable) { data.WriteBuf(m_reliableOutBuffer, m_reliableOutSize); m_last_reliable_sequence = m_outgoing_sequence - 1; } // Is there room for the unreliable payload? int max_send_size = send_resending ? MAX_ROUTEABLE_PACKET : NET_MAX_MESSAGE; if ((max_send_size - data.CurrentSize()) >= m_unreliableStream.CurrentSize()) { data.ConcatBuffer(&m_unreliableStream); } else { m_System->DPrintf("WARNING! TransmitOutgoing: Unreliable would overfow, ignoring.\n"); } m_unreliableStream.FastClear(); // Deal with packets that are too small for some networks // Packet too small for some networks if (data.CurrentSize() < 16) { // Go ahead and pad a full 16 extra bytes -- this only happens during authentication / signon for (int i = data.CurrentSize(); i < 16; i++) { // Note that the server can parse svc_nop, too. data.WriteByte(svc_nop); } } int statId = m_flow[FLOW_OUTGOING].current & 0x1f; m_flow[FLOW_OUTGOING].stats[statId].size = data.CurrentSize() + UDP_HEADER_SIZE; m_flow[FLOW_OUTGOING].stats[statId].time = m_System->GetTime(); m_flow[FLOW_OUTGOING].current++; COM_Munge2(data.GetData() + 8, data.CurrentSize() - 8, (unsigned char)(m_outgoing_sequence - 1)); if (m_Socket) { m_Socket->SendPacket(&m_remote_address, data.GetData(), data.CurrentSize()); } m_last_send = m_System->GetTime(); m_cleartime = max(m_send_interval, (data.CurrentSize() + UDP_HEADER_SIZE) * (1.0 / m_max_bandwidth_rate)) + m_last_send; }
void Netchan_Transmit(netchan_t *chan, int length, byte *data) { #ifdef REHLDS_FIXES byte send_buf[MAX_UDP_PACKET]; #else byte send_buf[NET_MAX_MESSAGE]; #endif qboolean send_reliable; qboolean send_reliable_fragment; qboolean send_resending = false; unsigned w1, w2; int i, j; float fRate; sizebuf_t sb_send; sb_send.data = send_buf; sb_send.buffername = "Netchan_Transmit"; sb_send.maxsize = sizeof(send_buf); sb_send.flags = 0; sb_send.cursize = 0; // check for message overflow if (chan->message.flags & 2) { Con_Printf("%s:Outgoing message overflow\n", NET_AdrToString(chan->remote_address)); return; } // if the remote side dropped the last reliable message, resend it send_reliable = false; if (chan->incoming_acknowledged > chan->last_reliable_sequence && chan->incoming_reliable_acknowledged != chan->reliable_sequence) { send_reliable = true; send_resending = true; } // // A packet can have "reliable payload + frag payload + unreliable payload // frag payload can be a file chunk, if so, it needs to be parsed on the receiving end and reliable payload + unreliable payload need // to be passed on to the message queue. The processing routine needs to be able to handle the case where a message comes in and a file // transfer completes // // // if the reliable transmit buffer is empty, copy the current message out if (!chan->reliable_length) { qboolean send_frag = false; fragbuf_t *pbuf; // Will be true if we are active and should let chan->message get some bandwidth int send_from_frag[MAX_STREAMS] = { 0, 0 }; int send_from_regular = 0; #ifdef REHLDS_FIXES if (chan->message.cursize > MAX_MSGLEN) { Netchan_CreateFragments_(chan == &g_pcls.netchan ? 1 : 0, chan, &chan->message); SZ_Clear(&chan->message); } #endif // If we have data in the waiting list(s) and we have cleared the current queue(s), then // push the waitlist(s) into the current queue(s) Netchan_FragSend(chan); // Sending regular payload send_from_regular = (chan->message.cursize) ? 1 : 0; // Check to see if we are sending a frag payload // for (i = 0; i < MAX_STREAMS; i++) { if (chan->fragbufs[i]) { send_from_frag[i] = 1; } } // Stall reliable payloads if sending from frag buffer if (send_from_regular && (send_from_frag[FRAG_NORMAL_STREAM])) { send_from_regular = false; // If the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer // if (chan->message.cursize > MAX_RELIABLE_PAYLOAD) { Netchan_CreateFragments_(chan == &g_pcls.netchan ? 1 : 0, chan, &chan->message); SZ_Clear(&chan->message); } } // Startpos will be zero if there is no regular payload for (i = 0; i < MAX_STREAMS; i++) { chan->frag_startpos[i] = 0; // Assume no fragment is being sent chan->reliable_fragment[i] = 0; chan->reliable_fragid[i] = 0; chan->frag_length[i] = 0; if (send_from_frag[i]) { send_frag = true; } } if (send_from_regular || send_frag) { chan->reliable_sequence ^= 1; send_reliable = true; } if (send_from_regular) { #ifdef REHLDS_FIXES Q_memcpy(chan->reliable_buf, chan->message.data, chan->message.cursize); #else Q_memcpy(chan->reliable_buf, chan->message_buf, chan->message.cursize); #endif chan->reliable_length = chan->message.cursize; SZ_Clear(&chan->message); // If we send fragments, this is where they'll start for (i = 0; i < MAX_STREAMS; i++) { chan->frag_startpos[i] = chan->reliable_length; } } for (i = 0; i < MAX_STREAMS; i++) { int fragment_size; // Is there something in the fragbuf? pbuf = chan->fragbufs[i]; fragment_size = 0; // Compiler warning. if (pbuf) { fragment_size = pbuf->frag_message.cursize; // Files set size a bit differently. if (pbuf->isfile && !pbuf->isbuffer) { fragment_size = pbuf->size; } } // Make sure we have enought space left if (send_from_frag[i] && pbuf && ((chan->reliable_length + fragment_size) < MAX_RELIABLE_PAYLOAD)) { chan->reliable_fragid[i] = MAKE_FRAGID(pbuf->bufferid, chan->fragbufcount[i]); // Which buffer are we sending? // If it's not in-memory, then we'll need to copy it in frame the file handle. if (pbuf->isfile && !pbuf->isbuffer) { char compressedfilename[MAX_PATH]; FileHandle_t hfile; if (pbuf->iscompressed) { Q_snprintf(compressedfilename, sizeof(compressedfilename), "%s.ztmp", pbuf->filename); hfile = FS_Open(compressedfilename, "rb"); } else { hfile = FS_Open(pbuf->filename, "rb"); } FS_Seek(hfile, pbuf->foffset, FILESYSTEM_SEEK_HEAD); FS_Read(&pbuf->frag_message.data[pbuf->frag_message.cursize], pbuf->size, 1, hfile); pbuf->frag_message.cursize += pbuf->size; FS_Close(hfile); } Q_memcpy(chan->reliable_buf + chan->reliable_length, pbuf->frag_message.data, pbuf->frag_message.cursize); chan->reliable_length += pbuf->frag_message.cursize; chan->frag_length[i] = pbuf->frag_message.cursize; // Unlink pbuf Netchan_UnlinkFragment(pbuf, &chan->fragbufs[i]); chan->reliable_fragment[i] = 1; // Offset the rest of the starting positions for (j = i + 1; j < MAX_STREAMS; j++) { chan->frag_startpos[j] += chan->frag_length[i]; } } } } // Prepare the packet header w1 = chan->outgoing_sequence | (send_reliable << 31); w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence << 31); send_reliable_fragment = false; for (i = 0; i < MAX_STREAMS; i++) { if (chan->reliable_fragment[i]) { send_reliable_fragment = true; break; } } if (send_reliable && send_reliable_fragment) { w1 |= (1 << 30); } chan->outgoing_sequence++; MSG_WriteLong(&sb_send, w1); MSG_WriteLong(&sb_send, w2); if (send_reliable && send_reliable_fragment) { for (i = 0; i < MAX_STREAMS; i++) { if (chan->reliable_fragment[i]) { MSG_WriteByte(&sb_send, 1); MSG_WriteLong(&sb_send, chan->reliable_fragid[i]); MSG_WriteShort(&sb_send, chan->frag_startpos[i]); MSG_WriteShort(&sb_send, chan->frag_length[i]); } else { MSG_WriteByte(&sb_send, 0); } } } // Copy the reliable message to the packet first if (send_reliable) { SZ_Write(&sb_send, chan->reliable_buf, chan->reliable_length); chan->last_reliable_sequence = chan->outgoing_sequence - 1; } // Is there room for the unreliable payload? int max_send_size = MAX_ROUTEABLE_PACKET; if (!send_resending) max_send_size = sb_send.maxsize; if ((max_send_size - sb_send.cursize) >= length) { SZ_Write(&sb_send, data, length); } else { Con_DPrintf("Netchan_Transmit: Unreliable would overfow, ignoring\n"); } // Deal with packets that are too small for some networks if (sb_send.cursize < 16) // Packet too small for some networks { // Go ahead and pad a full 16 extra bytes -- this only happens during authentication / signon for (int i = sb_send.cursize; i < 16; i++) { // Note that the server can parse svc_nop, too. MSG_WriteByte(&sb_send, svc_nop); } } int statId = chan->flow[FLOW_OUTGOING].current & 0x1F; chan->flow[FLOW_OUTGOING].stats[statId].size = sb_send.cursize + UDP_HEADER_SIZE; chan->flow[FLOW_OUTGOING].stats[statId].time = realtime; chan->flow[FLOW_OUTGOING].current++; Netchan_UpdateFlow(chan); if (!g_pcls.demoplayback) { COM_Munge2(sb_send.data + 8, sb_send.cursize - 8, (unsigned char)(chan->outgoing_sequence - 1)); if (g_modfuncs.m_pfnProcessOutgoingNet) g_modfuncs.m_pfnProcessOutgoingNet(chan, &sb_send); NET_SendPacket(chan->sock, sb_send.cursize, sb_send.data, chan->remote_address); } if (g_psv.active && sv_lan.value != 0.0f && sv_lan_rate.value > MIN_RATE) fRate = 1.0 / sv_lan_rate.value; else fRate = 1.0 / chan->rate; if (chan->cleartime < realtime) { chan->cleartime = realtime; } chan->cleartime += (sb_send.cursize + UDP_HEADER_SIZE) * fRate; if (net_showpackets.value != 0.0f && net_showpackets.value != 2.0f) { char c = (chan == &g_pcls.netchan) ? 'c' : 's'; Con_Printf(" %c --> sz=%i seq=%i ack=%i rel=%i tm=%f\n" , c , sb_send.cursize , chan->outgoing_sequence - 1 , chan->incoming_sequence , send_reliable ? 1 : 0 , (float)(chan == &g_pcls.netchan ? g_pcl.time : g_psv.time)); } }
/* =============== Netchan_TransmitBits tries to send an unreliable message to a connection, and handles the transmition / retransmition of the reliable messages. A 0 length will still generate a packet and deal with the reliable messages. ================ */ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) { sizebuf_t send; byte send_buf[NET_MAX_MESSAGE]; qboolean send_reliable_fragment; qboolean send_resending = false; qboolean send_reliable; size_t size1, size2; uint w1, w2, hdr_size; int i, j; float fRate; // check for message overflow // check for message overflow if( BF_CheckOverflow( &chan->message )) { MsgDev( D_ERROR, "%s:outgoing message overflow\n", NET_AdrToString( chan->remote_address )); return; } // if the remote side dropped the last reliable message, resend it send_reliable = false; if( chan->incoming_acknowledged > chan->last_reliable_sequence && chan->incoming_reliable_acknowledged != chan->reliable_sequence ) { send_reliable = true; send_resending = true; } // A packet can have "reliable payload + frag payload + unreliable payload // frag payload can be a file chunk, if so, it needs to be parsed on the receiving end and reliable payload + unreliable payload need // to be passed on to the message queue. The processing routine needs to be able to handle the case where a message comes in and a file // transfer completes // if the reliable transmit buffer is empty, copy the current message out if( !chan->reliable_length ) { qboolean send_frag = false; fragbuf_t *pbuf; // will be true if we are active and should let chan->message get some bandwidth int send_from_frag[MAX_STREAMS] = { 0, 0 }; int send_from_regular = false; // if we have data in the waiting list(s) and we have cleared the current queue(s), then // push the waitlist(s) into the current queue(s) Netchan_FragSend( chan ); // sending regular payload send_from_regular = BF_GetNumBytesWritten( &chan->message ) ? 1 : 0; // check to see if we are sending a frag payload for( i = 0; i < MAX_STREAMS; i++ ) { if( chan->fragbufs[i] ) { send_from_frag[i] = 1; } } // stall reliable payloads if sending from frag buffer if( send_from_regular && ( send_from_frag[FRAG_NORMAL_STREAM] )) { send_from_regular = false; // if the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer if( BF_GetNumBytesWritten( &chan->message ) > MAX_RELIABLE_PAYLOAD ) { Netchan_CreateFragments(( chan->sock == NS_SERVER ), chan, &chan->message ); BF_Clear( &chan->message ); } } // startpos will be zero if there is no regular payload for( i = 0; i < MAX_STREAMS; i++ ) { chan->frag_startpos[i] = 0; // assume no fragment is being sent chan->reliable_fragment[i] = 0; chan->reliable_fragid[i] = 0; chan->frag_length[i] = 0; if( send_from_frag[i] ) { send_frag = true; } } if( send_from_regular || send_frag ) { chan->reliable_sequence ^= 1; send_reliable = true; } if( send_from_regular ) { Q_memcpy( chan->reliable_buf, chan->message_buf, BF_GetNumBytesWritten( &chan->message )); chan->reliable_length = BF_GetNumBitsWritten( &chan->message ); BF_Clear( &chan->message ); // if we send fragments, this is where they'll start for( i = 0; i < MAX_STREAMS; i++ ) { chan->frag_startpos[i] = chan->reliable_length; } } for( i = 0; i < MAX_STREAMS; i++ ) { int fragment_size; int newpayloadsize; // is there someting in the fragbuf? pbuf = chan->fragbufs[i]; fragment_size = 0; if( pbuf ) { fragment_size = BF_GetNumBytesWritten( &pbuf->frag_message ); // files set size a bit differently. if( pbuf->isfile && !pbuf->isbuffer ) { fragment_size = pbuf->size; } } newpayloadsize = (( chan->reliable_length + ( fragment_size << 3 )) + 7 ) >> 3; // make sure we have enought space left if( send_from_frag[i] && pbuf && ( newpayloadsize < MAX_RELIABLE_PAYLOAD )) { sizebuf_t temp; // which buffer are we sending ? chan->reliable_fragid[i] = MAKE_FRAGID( pbuf->bufferid, chan->fragbufcount[i] ); // if it's not in-memory, then we'll need to copy it in frame the file handle. if( pbuf->isfile && !pbuf->isbuffer ) { byte filebuffer[2048]; file_t *hfile; hfile = FS_Open( pbuf->filename, "rb", false ); FS_Seek( hfile, pbuf->foffset, SEEK_SET ); FS_Read( hfile, filebuffer, pbuf->size ); BF_WriteBits( &pbuf->frag_message, filebuffer, pbuf->size << 3 ); FS_Close( hfile ); } // copy frag stuff on top of current buffer BF_StartWriting( &temp, chan->reliable_buf, sizeof( chan->reliable_buf ), chan->reliable_length, -1 ); BF_WriteBits( &temp, BF_GetData( &pbuf->frag_message ), BF_GetNumBitsWritten( &pbuf->frag_message )); chan->reliable_length += BF_GetNumBitsWritten( &pbuf->frag_message ); chan->frag_length[i] = BF_GetNumBitsWritten( &pbuf->frag_message ); // unlink pbuf Netchan_UnlinkFragment( pbuf, &chan->fragbufs[i] ); chan->reliable_fragment[i] = 1; // offset the rest of the starting positions for( j = i + 1; j < MAX_STREAMS; j++ ) { chan->frag_startpos[j] += chan->frag_length[i]; } } } }