/* ================ CL_Netchan_Transmit ================ */ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { #if defined RTCW_SP // int i; MSG_WriteByte( msg, clc_EOF ); // for(i=CL_ENCODE_START;i<msg->cursize;i++) { // chksum[i-CL_ENCODE_START] = msg->data[i]; // } // Huff_Compress( msg, CL_ENCODE_START ); #if DO_NET_ENCODE CL_Netchan_Encode( msg ); #endif Netchan_Transmit( chan, msg->cursize, msg->data ); #elif defined RTCW_MP MSG_WriteByte( msg, clc_EOF ); CL_Netchan_Encode( msg ); Netchan_Transmit( chan, msg->cursize, msg->data ); #else MSG_WriteByte( msg, clc_EOF ); CL_WriteBinaryMessage( msg ); if ( !SV_GameIsSinglePlayer() ) { CL_Netchan_Encode( msg ); } Netchan_Transmit( chan, msg->cursize, msg->data ); #endif // RTCW_XX }
/* ================ CL_Netchan_Transmit ================ */ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { MSG_WriteByte( msg, clc_EOF ); CL_WriteBinaryMessage( msg ); if ( !SV_GameIsSinglePlayer() ) { CL_Netchan_Encode( msg ); } Netchan_Transmit( chan, msg->cursize, msg->data ); }
/* =================== 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 ); } }