/* * TV_Downstream_SendClientsFragments */ qboolean TV_Downstream_SendClientsFragments( void ) { client_t *client; int i; qboolean remaining = qfalse; // send a message to each connected client for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ ) { if( client->state == CS_FREE || client->state == CS_ZOMBIE ) continue; if( !client->netchan.unsentFragments ) continue; if( !Netchan_TransmitNextFragment( &client->netchan ) ) { Com_Printf( "%s" S_COLOR_WHITE ": Error sending fragment: %s\n", client->name, NET_ErrorString() ); if( client->reliable ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error sending fragment: %s\n", NET_ErrorString() ); } continue; } if( client->netchan.unsentFragments ) remaining = qtrue; } return remaining; }
/* ================= SV_Netchan_TransmitNextFragment ================= */ void SV_Netchan_TransmitNextFragment(client_t *client) { Netchan_TransmitNextFragment(&client->netchan); while (!client->netchan.unsentFragments && client->netchan_start_queue) { // make sure the netchan queue has been properly initialized (you never know) //if (!client->netchan_end_queue) { // Com_Error(ERR_DROP, "netchan queue is not properly initialized in SV_Netchan_TransmitNextFragment"); //} // the last fragment was transmitted, check wether we have queued messages netchan_buffer_t *netbuf = client->netchan_start_queue; // pop from queue client->netchan_start_queue = netbuf->next; if (!client->netchan_start_queue) { client->netchan_end_queue = NULL; } SV_Netchan_Encode(client, &netbuf->msg, netbuf->lastClientCommandString); Netchan_Transmit(&client->netchan, netbuf->msg.cursize, netbuf->msg.data); Z_Free(netbuf); } }
/* ================= SV_Netchan_TransmitNextFragment ================= */ void SV_Netchan_TransmitNextFragment( client_t *client ) { Netchan_TransmitNextFragment( &client->netchan ); if (!client->netchan.unsentFragments) { // make sure the netchan queue has been properly initialized (you never know) if (!client->netchan_end_queue) { Com_Error(ERR_DROP, "netchan queue is not properly initialized in SV_Netchan_TransmitNextFragment\n"); } // the last fragment was transmitted, check wether we have queued messages if (client->netchan_start_queue) { netchan_buffer_t *netbuf; Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n"); netbuf = client->netchan_start_queue; SV_Netchan_Encode( client, &netbuf->msg ); Netchan_Transmit( &client->netchan, netbuf->msg.cursize, netbuf->msg.data ); // pop from queue client->netchan_start_queue = netbuf->next; if (!client->netchan_start_queue) { Com_DPrintf("#462 Netchan_TransmitNextFragment: emptied queue\n"); client->netchan_end_queue = &client->netchan_start_queue; } else Com_DPrintf("#462 Netchan_TransmitNextFragment: remaining queued message\n"); Z_Free(netbuf); } } }
/* * Netchan_Transmit * * Sends a message to a connection, fragmenting if necessary * A 0 length will still generate a packet. */ bool Netchan_Transmit( netchan_t *chan, msg_t *msg ) { msg_t send; uint8_t send_buf[MAX_PACKETLEN]; assert( msg ); if( msg->cursize > MAX_MSGLEN ) { Com_Error( ERR_DROP, "Netchan_Transmit: Excessive length = %i", msg->cursize ); return false; } chan->unsentFragmentStart = 0; chan->unsentIsCompressed = false; // fragment large reliable messages if( msg->cursize >= FRAGMENT_SIZE ) { chan->unsentFragments = true; chan->unsentLength = msg->cursize; chan->unsentIsCompressed = msg->compressed; memcpy( chan->unsentBuffer, msg->data, msg->cursize ); // only send the first fragment now return Netchan_TransmitNextFragment( chan ); } // write the packet header MSG_Init( &send, send_buf, sizeof( send_buf ) ); MSG_Clear( &send ); MSG_WriteLong( &send, chan->outgoingSequence ); // wsw : jal : by now our header sends incoming ack too (q3 doesn't) // wsw : jal : also add compressed information if it's compressed if( msg->compressed ) MSG_WriteLong( &send, chan->incomingSequence | FRAGMENT_BIT ); else MSG_WriteLong( &send, chan->incomingSequence ); chan->outgoingSequence++; // send the game port if we are a client if( !chan->socket->server ) MSG_WriteShort( &send, game_port ); MSG_CopyData( &send, msg->data, msg->cursize ); // send the datagram if( !NET_SendPacket( chan->socket, send.data, send.cursize, &chan->remoteAddress ) ) return false; if( showpackets->integer ) { Com_Printf( "%s send %4i : s=%i ack=%i\n", NET_SocketToString( chan->socket ), send.cursize, chan->outgoingSequence - 1, chan->incomingSequence ); } return true; }
/* =============== Netchan_Transmit Sends a message to a connection, fragmenting if necessary A 0 length will still generate a packet. ================ */ void Netchan_Transmit( netchan_t* chan, int length, const byte* data ) { msg_s send; byte send_buf[MAX_PACKETLEN]; if ( length > MAX_MSGLEN ) { Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length ); } chan->unsentFragmentStart = 0; // fragment large reliable messages if ( length >= FRAGMENT_SIZE ) { chan->unsentFragments = true; chan->unsentLength = length; memcpy( chan->unsentBuffer, data, length ); // only send the first fragment now Netchan_TransmitNextFragment( chan ); return; } // write the packet header MSG_InitOOB( &send, send_buf, sizeof( send_buf ) ); MSG_WriteLong( &send, chan->outgoingSequence ); // send the qport if we are a client if ( chan->sock == NS_CLIENT ) MSG_WriteShort( &send, qport->integer ); #ifdef LEGACY_PROTOCOL if ( !chan->compat ) #endif MSG_WriteLong( &send, NETCHAN_GENCHECKSUM( chan->challenge, chan->outgoingSequence ) ); chan->outgoingSequence++; MSG_WriteData( &send, data, length ); // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); // Store send time and size of this packet for rate control chan->lastSentTime = Sys_Milliseconds(); chan->lastSentSize = send.cursize; if ( showpackets->integer ) { Com_Printf( "%s send %4i : s=%i ack=%i\n" , netsrcString[ chan->sock ] , send.cursize , chan->outgoingSequence - 1 , chan->incomingSequence ); } }
/* ======================================================================================================================================= CL_Netchan_TransmitNextFragment ======================================================================================================================================= */ qboolean CL_Netchan_TransmitNextFragment(netchan_t *chan) { if (chan->unsentFragments) { Netchan_TransmitNextFragment(chan); return qtrue; } return qfalse; }
/* * Netchan_PushAllFragments * * Send all remaining fragments at once */ bool Netchan_PushAllFragments( netchan_t *chan ) { while( chan->unsentFragments ) { if( !Netchan_TransmitNextFragment( chan ) ) return false; } return true; }
/* ======================================================================================================================================= SV_Netchan_TransmitNextFragment ======================================================================================================================================= */ int SV_Netchan_TransmitNextFragment(client_t *client) { if (client->netchan.unsentFragments) { Netchan_TransmitNextFragment(&client->netchan); return SV_RateMsec(client); } else if (client->netchan_start_queue) { SV_Netchan_TransmitNextInQueue(client); return SV_RateMsec(client); } return -1; }
/* =============== Netchan_Transmit Sends a message to a connection, fragmenting if necessary A 0 length will still generate a packet. ================ */ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) { msg_t send; byte send_buf[ MAX_PACKETLEN ]; if ( length > MAX_MSGLEN ) { Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length ); } chan->unsentFragmentStart = 0; // fragment large reliable messages if ( length >= FRAGMENT_SIZE ) { chan->unsentFragments = qtrue; chan->unsentLength = length; Com_Memcpy( chan->unsentBuffer, data, length ); // only send the first fragment now Netchan_TransmitNextFragment( chan ); return; } // write the packet header MSG_InitOOB( &send, send_buf, sizeof( send_buf ) ); MSG_WriteLong( &send, chan->outgoingSequence ); chan->outgoingSequence++; // send the qport if we are a client if ( chan->sock == NS_CLIENT ) { MSG_WriteShort( &send, qport->integer ); } MSG_WriteData( &send, data, length ); // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); if ( showpackets->integer ) { Com_Printf( "%s send %4i : s=%i ack=%i\n" , netsrcString[ chan->sock ] , send.cursize , chan->outgoingSequence - 1 , chan->incomingSequence ); } }
/* =============== SV_Netchan_Transmit TTimo show_bug.cgi?id=462 if there are some unsent fragments (which may happen if the snapshots and the gamestate are fragmenting, and collide on send for instance) then buffer them and make sure they get sent in correct order ================ */ void SV_Netchan_Transmit( client_t *client, msg_t *msg ) { //int length, const byte *data ) { MSG_WriteByte( msg, svc_EOF ); SV_WriteBinaryMessage( msg, client ); if ( client->netchan.unsentFragments ) { netchan_buffer_t *netbuf; //Com_DPrintf("SV_Netchan_Transmit: there are unsent fragments remaining\n"); netbuf = ( netchan_buffer_t * ) Z_Malloc( sizeof( netchan_buffer_t ) ); // store the msg, we can't store it encoded, as the encoding depends on stuff we still have to finish sending MSG_Copy( &netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg ); // copy the command, since the command number used for encryption is // already compressed in the buffer, and receiving a new command would // otherwise lose the proper encryption key strcpy( netbuf->lastClientCommandString, client->lastClientCommandString ); // insert it in the queue, the message will be encoded and sent later //% *client->netchan_end_queue = netbuf; //% client->netchan_end_queue = &(*client->netchan_end_queue)->next; netbuf->next = NULL; if ( !client->netchan_start_queue ) { client->netchan_start_queue = netbuf; } else { client->netchan_end_queue->next = netbuf; } client->netchan_end_queue = netbuf; // emit the next fragment of the current message for now Netchan_TransmitNextFragment( &client->netchan ); } else { SV_Netchan_Encode( client, msg, client->lastClientCommandString ); Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } }
/* =============== SV_Netchan_Transmit TTimo show_bug.cgi?id=462 if there are some unsent fragments (which may happen if the snapshots and the gamestate are fragmenting, and collide on send for instance) then buffer them and make sure they get sent in correct order ================ */ void SV_Netchan_Transmit( client_t *client, msg_t *msg ) { //int length, const byte *data ) { MSG_WriteByte( msg, svc_EOF ); if ( client->netchan.unsentFragments ) { netchan_buffer_t *netbuf; //Com_DPrintf("SV_Netchan_Transmit: there are unsent fragments remaining\n"); netbuf = (netchan_buffer_t *)Z_Malloc( sizeof( netchan_buffer_t ) ); // store the msg, we can't store it encoded, as the encoding depends on stuff we still have to finish sending MSG_Copy( &netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg ); netbuf->next = NULL; // insert it in the queue, the message will be encoded and sent later *client->netchan_end_queue = netbuf; client->netchan_end_queue = &( *client->netchan_end_queue )->next; // emit the next fragment of the current message for now Netchan_TransmitNextFragment( &client->netchan ); } else { SV_Netchan_Encode( client, msg ); Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } }
/* =============== SV_Netchan_Transmit TTimo show_bug.cgi?id=462 if there are some unsent fragments (which may happen if the snapshots and the gamestate are fragmenting, and collide on send for instance) then buffer them and make sure they get sent in correct order ================ */ void SV_Netchan_Transmit( client_t *client, msg_t *msg ) { //int length, const byte *data ) { MSG_WriteByte( msg, svc_EOF ); SV_WriteBinaryMessage( msg, client ); if ( client->netchan.unsentFragments ) { netchan_buffer_t *netbuf; //Log::Debug("SV_Netchan_Transmit: there are unsent fragments remaining"); netbuf = ( netchan_buffer_t * ) Z_Malloc( sizeof( netchan_buffer_t ) ); MSG_Copy( &netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg ); // copy the command, since the command number used for encryption is // already compressed in the buffer, and receiving a new command would // otherwise lose the proper encryption key strcpy( netbuf->lastClientCommandString, client->lastClientCommandString ); netbuf->next = nullptr; if ( !client->netchan_start_queue ) { client->netchan_start_queue = netbuf; } else { client->netchan_end_queue->next = netbuf; } client->netchan_end_queue = netbuf; // emit the next fragment of the current message for now Netchan_TransmitNextFragment( &client->netchan ); } else { Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } }
/* ================= SV_Netchan_TransmitNextFragment ================= */ void SV_Netchan_TransmitNextFragment( netchan_t *chan ) { Netchan_TransmitNextFragment( chan ); }
/* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds A client packet will contain something like: 4 sequence number 2 qport 4 serverid 4 acknowledged sequence number 4 clc.serverCommandSequence <optional reliable commands> 1 clc_move or clc_moveNoDelta 1 command count <count * usercmds> =================== */ void CL_WritePacket() { msg_t buf; byte data[ MAX_MSGLEN ]; int i, j; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int packetNum; int oldPacketNum; int count; // don't send anything if playing back a demo if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { return; } memset( &nullcmd, 0, sizeof( nullcmd ) ); oldcmd = &nullcmd; MSG_Init( &buf, data, sizeof( data ) ); MSG_Bitstream( &buf ); // write the current serverId so the server // can tell if this is from the current gameState MSG_WriteLong( &buf, cl.serverId ); // write the last message we received, which can // be used for delta compression, and is also used // to tell if we dropped a gamestate MSG_WriteLong( &buf, clc.serverMessageSequence ); // write the last reliable message we received MSG_WriteLong( &buf, clc.serverCommandSequence ); // write any unacknowledged clientCommands // NOTE TTimo: if you verbose this, you will see that there are quite a few duplicates // typically several unacknowledged cp or userinfo commands stacked up for ( i = clc.reliableAcknowledge + 1; i <= clc.reliableSequence; i++ ) { MSG_WriteByte( &buf, clc_clientCommand ); MSG_WriteLong( &buf, i ); MSG_WriteString( &buf, clc.reliableCommands[ i & ( MAX_RELIABLE_COMMANDS - 1 ) ] ); } // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server if ( cl_packetdup->integer < 0 ) { Cvar_Set( "cl_packetdup", "0" ); } else if ( cl_packetdup->integer > 5 ) { Cvar_Set( "cl_packetdup", "5" ); } oldPacketNum = ( clc.netchan.outgoingSequence - 1 - cl_packetdup->integer ) & PACKET_MASK; count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; Com_Printf( "MAX_PACKET_USERCMDS" ); } #ifdef USE_VOIP if ( clc.voipOutgoingDataSize > 0 ) { if ( ( clc.voipFlags & VOIP_SPATIAL ) || Com_IsVoipTarget( clc.voipTargets, sizeof( clc.voipTargets ), -1 ) ) { MSG_WriteByte( &buf, clc_voip ); MSG_WriteByte( &buf, clc.voipOutgoingGeneration ); MSG_WriteLong( &buf, clc.voipOutgoingSequence ); MSG_WriteByte( &buf, clc.voipOutgoingDataFrames ); MSG_WriteData( &buf, clc.voipTargets, sizeof( clc.voipTargets ) ); MSG_WriteByte( &buf, clc.voipFlags ); MSG_WriteShort( &buf, clc.voipOutgoingDataSize ); MSG_WriteData( &buf, clc.voipOutgoingData, clc.voipOutgoingDataSize ); // If we're recording a demo, we have to fake a server packet with // this VoIP data so it gets to disk; the server doesn't send it // back to us, and we might as well eliminate concerns about dropped // and misordered packets here. if ( clc.demorecording && !clc.demowaiting ) { const int voipSize = clc.voipOutgoingDataSize; msg_t fakemsg; byte fakedata[ MAX_MSGLEN ]; MSG_Init( &fakemsg, fakedata, sizeof( fakedata ) ); MSG_Bitstream( &fakemsg ); MSG_WriteLong( &fakemsg, clc.reliableAcknowledge ); MSG_WriteByte( &fakemsg, svc_voip ); MSG_WriteShort( &fakemsg, clc.clientNum ); MSG_WriteByte( &fakemsg, clc.voipOutgoingGeneration ); MSG_WriteLong( &fakemsg, clc.voipOutgoingSequence ); MSG_WriteByte( &fakemsg, clc.voipOutgoingDataFrames ); MSG_WriteShort( &fakemsg, clc.voipOutgoingDataSize ); MSG_WriteBits( &fakemsg, clc.voipFlags, VOIP_FLAGCNT ); MSG_WriteData( &fakemsg, clc.voipOutgoingData, voipSize ); MSG_WriteByte( &fakemsg, svc_EOF ); CL_WriteDemoMessage( &fakemsg, 0 ); } clc.voipOutgoingSequence += clc.voipOutgoingDataFrames; clc.voipOutgoingDataSize = 0; clc.voipOutgoingDataFrames = 0; } else { // We have data, but no targets. Silently discard all data clc.voipOutgoingDataSize = 0; clc.voipOutgoingDataFrames = 0; } } #endif if ( count >= 1 ) { if ( cl_showSend->integer ) { Com_Printf( "(%i)", count ); } // begin a client move command if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting || clc.serverMessageSequence != cl.snap.messageNum ) { MSG_WriteByte( &buf, clc_moveNoDelta ); } else { MSG_WriteByte( &buf, clc_move ); } // write the command count MSG_WriteByte( &buf, count ); // write all the commands, including the predicted command for ( i = 0; i < count; i++ ) { j = ( cl.cmdNumber - count + i + 1 ) & CMD_MASK; cmd = &cl.cmds[ j ]; MSG_WriteDeltaUsercmd( &buf, oldcmd, cmd ); oldcmd = cmd; } } // // deliver the message // packetNum = clc.netchan.outgoingSequence & PACKET_MASK; cl.outPackets[ packetNum ].p_realtime = cls.realtime; cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime; cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber; clc.lastPacketSentTime = cls.realtime; if ( cl_showSend->integer ) { Com_Printf("%i ", buf.cursize ); } MSG_WriteByte( &buf, clc_EOF ); CL_WriteBinaryMessage( &buf ); Netchan_Transmit( &clc.netchan, buf.cursize, buf.data ); // clients never really should have messages large enough // to fragment, but in case they do, fire them all off // at once // TTimo: this causes a packet burst, which is bad karma for winsock // added a WARNING message, we'll see if there are legit situations where this happens while ( clc.netchan.unsentFragments ) { if ( cl_showSend->integer ) { Com_Printf( "WARNING: unsent fragments (not supposed to happen!)" ); } Netchan_TransmitNextFragment( &clc.netchan ); } }