コード例 #1
0
ファイル: cl_input.c プロジェクト: n1ckn4m3/kmquake2megacoop
/*
=================
CL_SendCmd
=================
*/
void CL_SendCmd (void)
{
	sizebuf_t	buf;
	byte		data[128];
	int			i;
	usercmd_t	*cmd, *oldcmd;
	usercmd_t	nullcmd;
	int			checksumIndex;

	// clear buffer
	memset (&buf, 0, sizeof(buf));

	// build a command even if not connected

	// save this command off for prediction
	i = cls.netchan.outgoing_sequence & (CMD_BACKUP-1);
	cmd = &cl.cmds[i];
	cl.cmd_time[i] = cls.realtime;	// for netgraph ping calculation

	*cmd = CL_CreateCmd ();

	cl.cmd = *cmd;

	if (cls.state == ca_disconnected || cls.state == ca_connecting)
		return;

	if (cls.state == ca_connected)
	{
		if (cls.netchan.message.cursize	|| curtime - cls.netchan.last_sent > 1000 )
			Netchan_Transmit (&cls.netchan, 0, buf.data);	
		return;
	}

	// send a userinfo update if needed
	if (userinfo_modified)
	{
		CL_FixUpGender();
		userinfo_modified = false;
		MSG_WriteByte (&cls.netchan.message, clc_userinfo);
		MSG_WriteString (&cls.netchan.message, Cvar_Userinfo() );
	}

	SZ_Init (&buf, data, sizeof(data));

	// Knightmare- removed this, put ESC-only substitute in keys.c
	/*if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop 
		&& cls.realtime - cl.cinematictime > 1000)
	{	// skip the rest of the cinematic
		SCR_FinishCinematic ();
	}*/

	// begin a client move command
	MSG_WriteByte (&buf, clc_move);

	// save the position for a checksum byte
	checksumIndex = buf.cursize;
	MSG_WriteByte (&buf, 0);

	// let the server know what the last frame we
	// got was, so the next message can be delta compressed
	if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
		MSG_WriteLong (&buf, -1);	// no compression
	else
		MSG_WriteLong (&buf, cl.frame.serverframe);

	// send this and the previous cmds in the message, so
	// if the last packet was dropped, it can be recovered
	i = (cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1);
	cmd = &cl.cmds[i];
	memset (&nullcmd, 0, sizeof(nullcmd));
	MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
	oldcmd = cmd;

	i = (cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1);
	cmd = &cl.cmds[i];
	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
	oldcmd = cmd;

	i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP-1);
	cmd = &cl.cmds[i];
	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);

	// calculate a checksum over the move commands
	buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
		buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
		cls.netchan.outgoing_sequence);

	//
	// deliver the message
	//
	Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);	
}
コード例 #2
0
ファイル: cl_input.c プロジェクト: AAS/ezquake-source
void CL_SendCmd (void)
{
	sizebuf_t buf;
	byte data[128];
	usercmd_t *cmd, *oldcmd;
	int i, checksumIndex, lost;
	qbool dontdrop;
	static float pps_balance = 0;
	static int dropcount = 0;

	if (cls.demoplayback && !cls.mvdplayback)
		return; // sendcmds come from the demo

	#ifdef FTE_PEXT_CHUNKEDDOWNLOADS
	CL_SendChunkDownloadReq();
	#endif

	// save this command off for prediction
	i = cls.netchan.outgoing_sequence & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	cl.frames[i].senttime = cls.realtime;
	cl.frames[i].receivedtime = -1;		// we haven't gotten a reply yet

	// update network stats table
	i = cls.netchan.outgoing_sequence&NETWORK_STATS_MASK;
	network_stats[i].delta = 0;     // filled-in later
	network_stats[i].sentsize = 0;  // filled-in later
	network_stats[i].senttime = cls.realtime;
	network_stats[i].receivedtime = -1;

	// get basic movement from keyboard
	CL_BaseMove (cmd);

	// allow mice or other external controllers to add to the move

	if (cl_independentPhysics.value == 0 || (physframe && cl_independentPhysics.value != 0))
	{
		IN_Move(cmd);
	}

	// if we are spectator, try autocam
	if (cl.spectator)
		Cam_Track(cmd);

	CL_FinishMove(cmd);
	cmdtime_msec += cmd->msec;

	Cam_FinishMove(cmd);

	if (cls.mvdplayback)
	{
		CL_CalcPlayerFPS(&cl.players[cl.playernum], cmd->msec);
        cls.netchan.outgoing_sequence++;
		return;
	}

	SZ_Init (&buf, data, sizeof(data));

	SZ_Write (&buf, cls.cmdmsg.data, cls.cmdmsg.cursize);
	if (cls.cmdmsg.overflowed)
		Com_DPrintf("cls.cmdmsg overflowed\n");
	SZ_Clear (&cls.cmdmsg);

	// begin a client move command
	MSG_WriteByte (&buf, clc_move);

	// save the position for a checksum byte
	checksumIndex = buf.cursize;
	MSG_WriteByte (&buf, 0);

	// write our lossage percentage
	lost = CL_CalcNet();
	MSG_WriteByte (&buf, (byte)lost);

	// send this and the previous two cmds in the message, so if the last packet was dropped, it can be recovered
	dontdrop = false;

	i = (cls.netchan.outgoing_sequence - 2) & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	if (cl_c2sImpulseBackup.value >= 2)
		dontdrop = dontdrop || cmd->impulse;
	MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
	oldcmd = cmd;

	i = (cls.netchan.outgoing_sequence - 1) & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	if (cl_c2sImpulseBackup.value >= 3)
		dontdrop = dontdrop || cmd->impulse;
	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
	oldcmd = cmd;

	i = (cls.netchan.outgoing_sequence) & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	if (cl_c2sImpulseBackup.value >= 1)
		dontdrop = dontdrop || cmd->impulse;
	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);

	// calculate a checksum over the move commands
	buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
		buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
		cls.netchan.outgoing_sequence);

	// request delta compression of entities
	if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP - 1)
	{
		cl.validsequence = 0;
		cl.delta_sequence = 0;
	}

	if (cl.delta_sequence && !cl_nodelta.value && cls.state == ca_active)
	{
		cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK].delta_sequence = cl.delta_sequence;
		MSG_WriteByte (&buf, clc_delta);
		MSG_WriteByte (&buf, cl.delta_sequence & 255);

		// network stats table
		network_stats[cls.netchan.outgoing_sequence&NETWORK_STATS_MASK].delta = 1;
	}
	else
	{
		cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK].delta_sequence = -1;
	}

	if (cls.demorecording)
		CL_WriteDemoCmd (cmd);

	if (cl_c2spps.value)
	{
		pps_balance += cls.frametime;
		// never drop more than 2 messages in a row -- that'll cause PL
		// and don't drop if one of the last two movemessages have an impulse
		if (pps_balance > 0 || dropcount >= 2 || dontdrop)
		{
			float	pps;
			pps = cl_c2spps.value;
			if (pps < 10) pps = 10;
			if (pps > 72) pps = 72;
			pps_balance -= 1 / pps;
			// bound pps_balance. FIXME: is there a better way?
			if (pps_balance > 0.1) pps_balance = 0.1;
			if (pps_balance < -0.1) pps_balance = -0.1;
			dropcount = 0;
		}
		else
		{
			// don't count this message when calculating PL
			cl.frames[i].receivedtime = -3;
			// drop this message
			cls.netchan.outgoing_sequence++;
			dropcount++;
			return;
		}
	}
	else
	{
		pps_balance = 0;
		dropcount = 0;
	}

#ifdef FTE_PEXT2_VOICECHAT
	S_Voip_Transmit(clc_voicechat, &buf);
#endif

	cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].sentsize = buf.cursize + 8;    // 8 = PACKET_HEADER
	// network stats table
	network_stats[cls.netchan.outgoing_sequence&NETWORK_STATS_MASK].sentsize = buf.cursize + 8;

	// deliver the message
	Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);
}
コード例 #3
0
ファイル: sv_user.c プロジェクト: luaman/qforge-2
/*
===================
SV_ExecuteClientMessage

The current net_message is parsed for the given client
===================
*/
void SV_ExecuteClientMessage(client_t *cl){
	int	c;
	char	*s;

	usercmd_t	nullcmd;
	usercmd_t	oldest, oldcmd, newcmd;
	int	net_drop;
	int	stringCmdCount;
	int	checksum, calculatedChecksum;
	int	checksumIndex;
	qboolean	move_issued;
	int	lastframe;

	sv_client = cl;
	sv_player = sv_client->edict;

	// only allow one move command
	move_issued = false;
	stringCmdCount = 0;

	while(1){
		if(net_message.readcount > net_message.cursize){
			Com_Printf("SV_ReadClientMessage: badread\n");
			SV_DropClient(cl);
			return;
		}

		c = MSG_ReadByte(&net_message);
		if(c == -1)
			break;

		switch(c){
			default:
				Com_Printf("SV_ReadClientMessage: unknown command char\n");
				SV_DropClient(cl);
				return;

			case clc_nop:
				break;

			case clc_userinfo:
				strncpy(cl->userinfo, MSG_ReadString(&net_message), sizeof(cl->userinfo) - 1);
				SV_UserinfoChanged(cl);
				break;

			case clc_move:
				if(move_issued)
					return;		// someone is trying to cheat...

				move_issued = true;
				checksumIndex = net_message.readcount;
				checksum = MSG_ReadByte(&net_message);
				lastframe = MSG_ReadLong(&net_message);
				if(lastframe != cl->lastframe){
					cl->lastframe = lastframe;
					if(cl->lastframe > 0){
						cl->frame_latency[cl->lastframe&(LATENCY_COUNTS - 1)] =
							svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
					}
				}

				memset(&nullcmd, 0, sizeof(nullcmd));
				MSG_ReadDeltaUsercmd(&net_message, &nullcmd, &oldest);
				MSG_ReadDeltaUsercmd(&net_message, &oldest, &oldcmd);
				MSG_ReadDeltaUsercmd(&net_message, &oldcmd, &newcmd);

				if(cl->state != cs_spawned){
					cl->lastframe = -1;
					break;
				}

				// if the checksum fails, ignore the rest of the packet
				calculatedChecksum = COM_BlockSequenceCRCByte(
										 net_message.data + checksumIndex + 1,
										 net_message.readcount - checksumIndex - 1,
										 cl->netchan.incoming_sequence);

				if(calculatedChecksum != checksum){
					Com_DPrintf("Failed command checksum for %s(%d != %d)/%d\n",
								 cl->name, calculatedChecksum, checksum,
								 cl->netchan.incoming_sequence);
					return;
				}

				if(!paused->value){
					net_drop = cl->netchan.dropped;
					if(net_drop < 20){
//						if(net_drop > 2)
//							Com_Printf("drop %i\n", net_drop);
						while(net_drop > 2){
							SV_ClientThink(cl, &cl->lastcmd);
							net_drop--;
						}
						if(net_drop > 1)
							SV_ClientThink(cl, &oldest);
						if(net_drop > 0)
							SV_ClientThink(cl, &oldcmd);
					}
					SV_ClientThink(cl, &newcmd);
				}

				cl->lastcmd = newcmd;
				break;

			case clc_stringcmd:
				s = MSG_ReadString(&net_message);

				// malicious users may try using too many string commands
				if(++stringCmdCount < MAX_STRINGCMDS)
					SV_ExecuteUserCommand(s);

				if(cl->state == cs_zombie)
					return;	// disconnect command
				break;
		}
	}
}
コード例 #4
0
ファイル: cl_input.c プロジェクト: Bad-ptr/q2pro
/*
=================
CL_SendDefaultCmd
=================
*/
static void CL_SendDefaultCmd( void ) {
    size_t cursize, checksumIndex;
    usercmd_t *cmd, *oldcmd;
    client_history_t *history;

    // archive this packet
    history = &cl.history[cls.netchan->outgoing_sequence & CMD_MASK];
    history->cmdNumber = cl.cmdNumber;
    history->sent = cls.realtime;    // for ping calculation
    history->rcvd = 0;

    cl.lastTransmitCmdNumber = cl.cmdNumber;

    // see if we are ready to send this packet
    if( !ready_to_send_hacked() ) {
        cls.netchan->outgoing_sequence++; // just drop the packet
        return;
    }

    cl.lastTransmitTime = cls.realtime;
    cl.lastTransmitCmdNumberReal = cl.cmdNumber;

    // begin a client move command
    MSG_WriteByte( clc_move );

    // save the position for a checksum byte
    checksumIndex = 0;
    if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) {
        checksumIndex = msg_write.cursize;
        SZ_GetSpace( &msg_write, 1 );
    }

    // let the server know what the last frame we
    // got was, so the next message can be delta compressed
    if( cl_nodelta->integer || !cl.frame.valid /*|| cls.demowaiting*/ ) {
        MSG_WriteLong( -1 ); // no compression
    } else {
        MSG_WriteLong( cl.frame.number );
    }

    // send this and the previous cmds in the message, so
    // if the last packet was dropped, it can be recovered
    cmd = &cl.cmds[( cl.cmdNumber - 2 ) & CMD_MASK];
    MSG_WriteDeltaUsercmd( NULL, cmd, cls.protocolVersion );
    MSG_WriteByte( cl.lightlevel );
    oldcmd = cmd;

    cmd = &cl.cmds[( cl.cmdNumber - 1 ) & CMD_MASK];
    MSG_WriteDeltaUsercmd( oldcmd, cmd, cls.protocolVersion );
    MSG_WriteByte( cl.lightlevel );
    oldcmd = cmd;

    cmd = &cl.cmds[cl.cmdNumber & CMD_MASK];
    MSG_WriteDeltaUsercmd( oldcmd, cmd, cls.protocolVersion );
    MSG_WriteByte( cl.lightlevel );

    if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) {
        // calculate a checksum over the move commands
        msg_write.data[checksumIndex] = COM_BlockSequenceCRCByte(
            msg_write.data + checksumIndex + 1,
            msg_write.cursize - checksumIndex - 1,
            cls.netchan->outgoing_sequence );
    }

    P_FRAMES++;

    //
    // deliver the message
    //
    cursize = cls.netchan->Transmit( cls.netchan, msg_write.cursize, msg_write.data, 1 );
#ifdef _DEBUG
    if( cl_showpackets->integer ) {
        Com_Printf( "%"PRIz" ", cursize );
    }
#endif

    SZ_Clear( &msg_write );
}
コード例 #5
0
ファイル: cl_input.c プロジェクト: DaneTheory/quake
/*
=================
CL_SendCmd
=================
*/
void CL_SendCmd (void)
{
	sizebuf_t	buf;
	byte		data[128];
	int			i;
	usercmd_t	*cmd, *oldcmd;
	int			checksumIndex;
	int			lost;
	int			seq_hash;

	if (cls.demoplayback)
		return; // sendcmds come from the demo

	// save this command off for prediction
	i = cls.netchan.outgoing_sequence & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	cl.frames[i].senttime = realtime;
	cl.frames[i].receivedtime = -1;		// we haven't gotten a reply yet

//	seq_hash = (cls.netchan.outgoing_sequence & 0xffff) ; // ^ QW_CHECK_HASH;
	seq_hash = cls.netchan.outgoing_sequence;

	// get basic movement from keyboard
	CL_BaseMove (cmd);

	// allow mice or other external controllers to add to the move
	IN_Move (cmd);

	// if we are spectator, try autocam
	if (cl.spectator)
		Cam_Track(cmd);

	CL_FinishMove(cmd);

	Cam_FinishMove(cmd);

// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
	buf.maxsize = 128;
	buf.cursize = 0;
	buf.data = data;

	MSG_WriteByte (&buf, clc_move);

	// save the position for a checksum byte
	checksumIndex = buf.cursize;
	MSG_WriteByte (&buf, 0);

	// write our lossage percentage
	lost = CL_CalcNet();
	MSG_WriteByte (&buf, (byte)lost);

	i = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
	oldcmd = cmd;

	i = (cls.netchan.outgoing_sequence-1) & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
	oldcmd = cmd;

	i = (cls.netchan.outgoing_sequence) & UPDATE_MASK;
	cmd = &cl.frames[i].cmd;
	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);

	// calculate a checksum over the move commands
	buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
		buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
		seq_hash);

	// request delta compression of entities
	if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1)
		cl.validsequence = 0;

	if (cl.validsequence && !cl_nodelta.value && cls.state == ca_active &&
		!cls.demorecording)
	{
		cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = cl.validsequence;
		MSG_WriteByte (&buf, clc_delta);
		MSG_WriteByte (&buf, cl.validsequence&255);
	}
	else
		cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = -1;

	if (cls.demorecording)
		CL_WriteDemoCmd(cmd);

//
// deliver the message
//
	Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);	
}
コード例 #6
0
ファイル: cl_input.c プロジェクト: greck2908/qengine
void CL_SendCmd(void)
{
  sizebuf_t buf;
  byte data[128];
  int i;
  usercmd_t *cmd, *oldcmd;
  usercmd_t nullcmd;
  int checksumIndex;

  memset(&buf, 0, sizeof(buf));

  /* save this command off for prediction */
  i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1);
  cmd = &cl.cmds[i];
  cl.cmd_time[i] = cls.realtime; /* for netgraph ping calculation */

  CL_FinalizeCmd();

  cl.cmd = *cmd;

  if ((cls.state == ca_disconnected) || (cls.state == ca_connecting)) {
    return;
  }

  if (cls.state == ca_connected) {
    if (cls.netchan.message.cursize || (curtime - cls.netchan.last_sent > 1000)) {
      Netchan_Transmit(&cls.netchan, 0, buf.data);
    }

    return;
  }

  /* send a userinfo update if needed */
  if (userinfo_modified) {
    CL_FixUpGender();
    userinfo_modified = false;
    MSG_WriteByte(&cls.netchan.message, clc_userinfo);
    MSG_WriteString(&cls.netchan.message, Cvar_Userinfo());
  }

  SZ_Init(&buf, data, sizeof(data));

  /* begin a client move command */
  MSG_WriteByte(&buf, clc_move);

  /* save the position for a checksum byte */
  checksumIndex = buf.cursize;
  MSG_WriteByte(&buf, 0);

  /* let the server know what the last frame we
     got was, so the next message can be delta
     compressed */
  if (cl_nodelta->value || !cl.frame.valid) {
    MSG_WriteLong(&buf, -1); /* no compression */
  } else {
    MSG_WriteLong(&buf, cl.frame.serverframe);
  }

  /* send this and the previous cmds in the message, so
     if the last packet was dropped, it can be recovered */
  i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1);
  cmd = &cl.cmds[i];
  memset(&nullcmd, 0, sizeof(nullcmd));
  MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
  oldcmd = cmd;

  i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1);
  cmd = &cl.cmds[i];
  MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
  oldcmd = cmd;

  i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1);
  cmd = &cl.cmds[i];
  MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);

  /* calculate a checksum over the move commands */
  buf.data[checksumIndex] = COM_BlockSequenceCRCByte(buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
                                                     cls.netchan.outgoing_sequence);

  /* deliver the message */
  Netchan_Transmit(&cls.netchan, buf.cursize, buf.data);

  /* Reinit the current cmd buffer */
  cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
  memset(cmd, 0, sizeof(*cmd));
}
コード例 #7
0
ファイル: sv_user.c プロジェクト: flwh/Alcatel_OT_985_kernel
void SV_ExecuteClientMessage (client_t *cl)
{
	int		c;
	char	*s;
	usercmd_t	oldest, oldcmd, newcmd;
	client_frame_t	*frame;
	vec3_t o;
	qboolean	move_issued = false; //only allow one move command
	int		checksumIndex;
	byte	checksum, calculatedChecksum;
	int		seq_hash;

	// calc ping time
	frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];
	frame->ping_time = realtime - frame->senttime;

	// make sure the reply sequence number matches the incoming
	// sequence number 
	if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence)
		cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence;
	else
		cl->send_message = false;	// don't reply, sequences have slipped		

	// save time for ping calculations
	cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime;
	cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1;

	host_client = cl;
	sv_player = host_client->edict;

//	seq_hash = (cl->netchan.incoming_sequence & 0xffff) ; // ^ QW_CHECK_HASH;
	seq_hash = cl->netchan.incoming_sequence;
	
	// mark time so clients will know how much to predict
	// other players
 	cl->localtime = sv.time;
	cl->delta_sequence = -1;	// no delta unless requested
	while (1)
	{
		if (msg_badread)
		{
			Con_Printf ("SV_ReadClientMessage: badread\n");
			SV_DropClient (cl);
			return;
		}	

		c = MSG_ReadByte ();
		if (c == -1)
			break;
				
		switch (c)
		{
		default:
			Con_Printf ("SV_ReadClientMessage: unknown command char\n");
			SV_DropClient (cl);
			return;
						
		case clc_nop:
			break;

		case clc_delta:
			cl->delta_sequence = MSG_ReadByte ();
			break;

		case clc_move:
			if (move_issued)
				return;		// someone is trying to cheat...

			move_issued = true;

			checksumIndex = MSG_GetReadCount();
			checksum = (byte)MSG_ReadByte ();

			// read loss percentage
			cl->lossage = MSG_ReadByte();

			MSG_ReadDeltaUsercmd (&nullcmd, &oldest);
			MSG_ReadDeltaUsercmd (&oldest, &oldcmd);
			MSG_ReadDeltaUsercmd (&oldcmd, &newcmd);

			if ( cl->state != cs_spawned )
				break;

			// if the checksum fails, ignore the rest of the packet
			calculatedChecksum = COM_BlockSequenceCRCByte(
				net_message.data + checksumIndex + 1,
				MSG_GetReadCount() - checksumIndex - 1,
				seq_hash);

			if (calculatedChecksum != checksum)
			{
				Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", 
					cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum);
				return;
			}

			if (!sv.paused) {
				SV_PreRunCmd();

				if (net_drop < 20)
				{
					while (net_drop > 2)
					{
						SV_RunCmd (&cl->lastcmd);
						net_drop--;
					}
					if (net_drop > 1)
						SV_RunCmd (&oldest);
					if (net_drop > 0)
						SV_RunCmd (&oldcmd);
				}
				SV_RunCmd (&newcmd);

				SV_PostRunCmd();
			}

			cl->lastcmd = newcmd;
			cl->lastcmd.buttons = 0; // avoid multiple fires on lag
			break;


		case clc_stringcmd:	
			s = MSG_ReadString ();
			SV_ExecuteUserCommand (s);
			break;

		case clc_tmove:
			o[0] = MSG_ReadCoord();
			o[1] = MSG_ReadCoord();
			o[2] = MSG_ReadCoord();
			// only allowed by spectators
			if (host_client->spectator) {
				VectorCopy(o, sv_player->v.origin);
				SV_LinkEdict(sv_player, false);
			}
			break;

		case clc_upload:
			SV_NextUpload();
			break;

		}
	}
}