Example #1
0
/*
==============
CL_Netchan_Encode

	// first 12 bytes of the data are always:
	long serverId;
	long messageAcknowledge;
	long reliableAcknowledge;

==============
*/
static void CL_Netchan_Encode(msg_t * msg)
{
	int             serverId, messageAcknowledge, reliableAcknowledge;
	int             i, index, srdc, sbit, soob;
	byte            key, *string;

	if(msg->cursize <= CL_ENCODE_START)
	{
		return;
	}

	srdc = msg->readcount;
	sbit = msg->bit;
	soob = msg->oob;

	msg->bit = 0;
	msg->readcount = 0;
	msg->oob = qfalse;

	serverId = MSG_ReadLong(msg);
	messageAcknowledge = MSG_ReadLong(msg);
	reliableAcknowledge = MSG_ReadLong(msg);

	msg->oob = (qboolean)soob;
	msg->bit = sbit;
	msg->readcount = srdc;

	string = (byte *)CL_GetReliableServerCommand( reliableAcknowledge );
	index = 0;
	//
	key = clc.challenge ^ serverId ^ messageAcknowledge;
	for(i = CL_ENCODE_START; i < msg->cursize; i++)
	{
		// modify the key with the last received now acknowledged server command
		if(!string[index])
		{
			index = 0;
		}
		if(string[index] > 127 || string[index] == '%')
		{
			key ^= '.' << (i & 1);
		}
		else
		{
			key ^= string[index] << (i & 1);
		}
		index++;
		// encode the data with this key
		*(msg->data + i) = (*(msg->data + i)) ^ key;
	}
}
Example #2
0
/*
===================
CL_WritePacket

Create and send the command packet to the server
Including both the reliable commands and the usercmds

During normal gameplay, 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(void)
{
    msg_t           buf;
    byte            data[MAX_MSGLEN];
    int             i, j;
    usercmd_t      *cmd, *oldcmd;
    usercmd_t       nullcmd;
    int             packetNum;
    int             oldPacketNum;
    int             count, key;

    // 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, CL_GetReliableCommand( i ) );
    }

    // 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\n");
    }

#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_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);

        // use the checksum feed in the key
        key = clc.checksumFeed;
        // also use the message acknowledge
        key ^= clc.serverMessageSequence;
        // also use the last acknowledged server command in the key
        key ^= Com_HashKey( CL_GetReliableServerCommand( clc.serverCommandSequence ), 32);

        // 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_WriteDeltaUsercmdKey(&buf, key, 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);
    }
    CL_Netchan_Transmit(&clc.netchan, &buf);

    // 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!)\n");
        }
        CL_Netchan_TransmitNextFragment(&clc.netchan);
    }
}
Example #3
0
/*
===================
CL_GetServerCommand

Set up argc/argv for the given command
===================
*/
qboolean CL_GetServerCommand( int serverCommandNumber ) {
	char	*s;
	char	*cmd;
	static char bigConfigString[BIG_INFO_STRING];
	int argc;

	// if we have irretrievably lost a reliable command, drop the connection
	if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) {
		// when a demo record was started after the client got a whole bunch of
		// reliable commands then the client never got those first reliable commands
		if ( clc.demoplaying )
			return qfalse;
		Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" );
		return qfalse;
	}

	if ( serverCommandNumber > clc.serverCommandSequence ) {
		Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" );
		return qfalse;
	}

	s = CL_GetReliableServerCommand( serverCommandNumber );
	clc.lastExecutedServerCommand = serverCommandNumber;

	Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s );

rescan:
	Cmd_TokenizeString( s );
	cmd = Cmd_Argv(0);
	argc = Cmd_Argc();

	if ( !strcmp( cmd, "disconnect" ) ) {
		// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552
		// allow server to indicate why they were disconnected
		if ( argc >= 2 )
			Com_Error (ERR_SERVERDISCONNECT, va( "Server Disconnected - %s", Cmd_Argv( 1 ) ) );
		else
			Com_Error (ERR_SERVERDISCONNECT,"Server disconnected\n");
	}

	if ( !strcmp( cmd, "bcs0" ) ) {
		Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) );
		return qfalse;
	}

	if ( !strcmp( cmd, "bcs1" ) ) {
		s = Cmd_Argv(2);
		if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) {
			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
		}
		strcat( bigConfigString, s );
		return qfalse;
	}

	if ( !strcmp( cmd, "bcs2" ) ) {
		s = Cmd_Argv(2);
		if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) {
			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
		}
		strcat( bigConfigString, s );
		strcat( bigConfigString, "\"" );
		s = bigConfigString;
		goto rescan;
	}

	if ( !strcmp( cmd, "cs" ) ) {
		CL_ConfigstringModified();
		// reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString()
		Cmd_TokenizeString( s );
		return qtrue;
	}

	if ( !strcmp( cmd, "up" ) ) {
		if( serverCommandNumber <= clc.lastSnapServerCmdNum )
			return qtrue;

		CL_TableUpdateRow();
		return qtrue;
	}

	if ( !strcmp( cmd, "de" ) ) {
		if( serverCommandNumber <= clc.lastSnapServerCmdNum )
			return qtrue;

		CL_TableDeleteRow();
		return qtrue;
	}

	if ( !strcmp( cmd, "in" ) ) {
		if( serverCommandNumber <= clc.lastSnapServerCmdNum )
			return qtrue;

		CL_TableInsertRow();
		return qtrue;
	}

	if ( !strcmp( cmd, "map_restart" ) ) {
		// clear notify lines and outgoing commands before passing
		// the restart to the cgame
		Con_ClearNotify();
		Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) );
		return qtrue;
	}

	// the clientLevelShot command is used during development
	// to generate 128*128 screenshots from the intermission
	// point of levels for the menu system to use
	// we pass it along to the cgame to make apropriate adjustments,
	// but we also clear the console and notify lines here
	if ( !strcmp( cmd, "clientLevelShot" ) ) {
		// don't do it if we aren't running the server locally,
		// otherwise malicious remote servers could overwrite
		// the existing thumbnails
		if ( !com_sv_running->integer ) {
			return qfalse;
		}
		// close the console
		Con_Close();
		// take a special screenshot next frame
		Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" );
		return qtrue;
	}

	// we may want to put a "connect to other server" command here

	// cgame can now act on the command
	return qtrue;
}