Example #1
0
/*
* SV_ParseMoveCommand
*/
static void SV_ParseMoveCommand( client_t *client, msg_t *msg )
{
	unsigned int i, ucmdHead, ucmdFirst, ucmdCount;
	usercmd_t nullcmd;
	int lastframe;

	lastframe = MSG_ReadLong( msg );

	// read the id of the first ucmd we will receive
	ucmdHead = (unsigned int)MSG_ReadLong( msg );
	// read the number of ucmds we will receive
	ucmdCount = (unsigned int)MSG_ReadByte( msg );

	if( ucmdCount > CMD_MASK )
	{
		SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Ucmd overflow" );
		return;
	}

	ucmdFirst = ucmdHead > ucmdCount ? ucmdHead - ucmdCount : 0;
	client->UcmdReceived = ucmdHead < 1 ? 0 : ucmdHead - 1;

	// read the user commands
	for( i = ucmdFirst; i < ucmdHead; i++ )
	{
		if( i == ucmdFirst )
		{              // first one isn't delta compressed
			memset( &nullcmd, 0, sizeof( nullcmd ) );
			// jalfixme: check for too old overflood
			MSG_ReadDeltaUsercmd( msg, &nullcmd, &client->ucmds[i & CMD_MASK] );
		}
		else
		{
			MSG_ReadDeltaUsercmd( msg, &client->ucmds[( i-1 ) & CMD_MASK], &client->ucmds[i & CMD_MASK] );
		}
	}

	if( client->state != CS_SPAWNED )
	{
		client->lastframe = -1;
		return;
	}

	// calc ping
	if( lastframe != client->lastframe )
	{
		client->lastframe = lastframe;
		if( client->lastframe > 0 )
		{
			// FIXME: Medar: ping is in gametime, should be in realtime
			//client->frame_latency[client->lastframe&(LATENCY_COUNTS-1)] = svs.gametime - (client->frames[client->lastframe & UPDATE_MASK].sentTimeStamp;
			// this is more accurate. A little bit hackish, but more accurate
			client->frame_latency[client->lastframe&( LATENCY_COUNTS-1 )] = svs.gametime - ( client->ucmds[client->UcmdReceived & CMD_MASK].serverTimeStamp + svc.snapFrameTime );
		}
	}
}
Example #2
0
/*
=================
CL_ReadDemoUserCmd

read the demo usercmd for predicting
and smooth movement during playback the demo
=================
*/
void CL_ReadDemoUserCmd( qboolean discard )
{
	byte	data[1024];
	int	cmdnumber;
	int	outgoing_sequence;
	word	bytes;

	FS_Read( cls.demofile, &outgoing_sequence, sizeof( int ));
	FS_Read( cls.demofile, &cmdnumber, sizeof( int ));
	FS_Read( cls.demofile, &bytes, sizeof( short ));
	FS_Read( cls.demofile, data, bytes );

	if( !discard )
	{
		usercmd_t	nullcmd = {0};
		sizebuf_t	buf;

		BF_Init( &buf, "UserCmd", data, sizeof( data ));

		// always delta'ing from null
		cl.refdef.cmd = &cl.cmds[cmdnumber & CL_UPDATE_MASK ];

		MSG_ReadDeltaUsercmd( &buf, &nullcmd, cl.refdef.cmd );

		// NOTE: we need to have the current outgoing sequence correct
		// so we can do prediction correctly during playback
		cls.netchan.outgoing_sequence = outgoing_sequence;
	}
}
Example #3
0
/*
==================
SV_UserMove

The message usually contains all the movement commands 
that were in the last three packets, so that the information
in dropped packets can be recovered.

On very fast clients, there may be multiple usercmd packed into
each of the backup packets.
==================
*/
static void SV_UserMove( client_t *cl, msg_t *msg ) {
	int			i, start;
	int			cmdNum;
	int			firstNum;
	int			cmdCount;
	usercmd_t	nullcmd;
	usercmd_t	cmds[MAX_PACKET_USERCMDS];
	usercmd_t	*cmd, *oldcmd;
	int			clientTime;
	int			serverId;

	cl->reliableAcknowledge = MSG_ReadLong( msg );
	serverId = MSG_ReadLong( msg );
	clientTime = MSG_ReadLong( msg );
	cl->deltaMessage = MSG_ReadLong( msg );

	// cmdNum is the command number of the most recent included usercmd
	cmdNum = MSG_ReadLong( msg );
	cmdCount = MSG_ReadByte( msg );

	if ( cmdCount < 1 ) {
		Com_Printf( "cmdCount < 1\n" );
		return;
	}

	if ( cmdCount > MAX_PACKET_USERCMDS ) {
		Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" );
		return;
	}

	memset( &nullcmd, 0, sizeof(nullcmd) );
	oldcmd = &nullcmd;
	for ( i = 0 ; i < cmdCount ; i++ ) {
		cmd = &cmds[i];
		MSG_ReadDeltaUsercmd( msg, oldcmd, cmd );
		oldcmd = cmd;
	}

	// if this is a usercmd from a previous gamestate,
	// ignore it or retransmit the current gamestate
	if ( serverId != sv.serverId ) {
		// if we can tell that the client has dropped the last
		// gamestate we sent them, resend it
		if ( cl->netchan.incomingAcknowledged > cl->gamestateMessageNum ) {
			Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name );
			SV_SendClientGameState( cl );
		}
		return;
	}

	// if this is the first usercmd we have received
	// this gamestate, put the client into the world
	if ( cl->state == CS_PRIMED ) {

		SV_ClientEnterWorld( cl, &cmds[0], eSavedGameJustLoaded );
		if ( sv_mapname->string[0]!='_' )
		{
			char savename[MAX_QPATH];
			if ( eSavedGameJustLoaded == eNO )
			{
				SG_WriteSavegame("auto",qtrue);
				Com_sprintf (savename, sizeof(savename), "auto_%s",sv_mapname->string);
				SG_WriteSavegame(savename,qtrue);//can't use va becuase it's nested
			}
			else if ( qbLoadTransition == qtrue )
			{
				Com_sprintf (savename, sizeof(savename), "hub/%s", sv_mapname->string );
				SG_WriteSavegame( savename, qfalse );//save a full one
				SG_WriteSavegame( "auto", qfalse );//need a copy for auto, too
			}
		}
		eSavedGameJustLoaded = eNO;
		// the moves can be processed normaly
	}

	if ( cl->state != CS_ACTIVE ) {
		cl->deltaMessage = -1;
		return;
	}


	// if there is a time gap from the last packet to this packet,
	// fill in with the first command in the packet

	// with a packetdup of 0, firstNum == cmdNum
	firstNum = cmdNum - ( cmdCount - 1 );
	if ( cl->cmdNum < firstNum - 1 ) {
		cl->droppedCommands = qtrue; 
		if ( sv_showloss->integer ) {
			Com_Printf("Lost %i usercmds from %s\n", firstNum - 1 - cl->cmdNum,
				cl->name);
		}
		if ( cl->cmdNum < firstNum - 6 ) {
			cl->cmdNum = firstNum - 6;		// don't generate too many
		}
		while ( cl->cmdNum < firstNum - 1 ) {
			cl->cmdNum++;
			SV_ClientThink( cl, &cmds[0] );
		}
	}
	// skip over any usercmd_t we have already executed
	start = cl->cmdNum - ( firstNum - 1 );
	for ( i =  start ; i < cmdCount ; i++ ) {
		SV_ClientThink (cl, &cmds[ i ]);
	}
	cl->cmdNum = cmdNum;

}
Example #4
0
void CL_ParsePlayerinfo (void)
{
	int			msec;
	int			flags;
	player_info_t	*info;
	player_state_t	*state;
	int			num;
	int			i;

	num = MSG_ReadByte ();
	if (num > MAX_CLIENTS)
		Sys_Error ("CL_ParsePlayerinfo: bad num");

	info = &cl.players[num];

	state = &cl.frames[parsecountmod].playerstate[num];

	flags = state->flags = MSG_ReadShort ();

	state->messagenum = cl.parsecount;
	state->origin[0] = MSG_ReadCoord ();
	state->origin[1] = MSG_ReadCoord ();
	state->origin[2] = MSG_ReadCoord ();

	state->frame = MSG_ReadByte ();

	// the other player's last move was likely some time
	// before the packet was sent out, so accurately track
	// the exact time it was valid at
	if (flags & PF_MSEC)
	{
		msec = MSG_ReadByte ();
		state->state_time = parsecounttime - msec*0.001;
	}
	else
		state->state_time = parsecounttime;

	if (flags & PF_COMMAND)
		MSG_ReadDeltaUsercmd (&nullcmd, &state->command);

	for (i=0 ; i<3 ; i++)
	{
		if (flags & (PF_VELOCITY1<<i) )
			state->velocity[i] = MSG_ReadShort();
		else
			state->velocity[i] = 0;
	}
	if (flags & PF_MODEL)
		state->modelindex = MSG_ReadByte ();
	else
		state->modelindex = cl_playerindex;

	if (flags & PF_SKINNUM)
		state->skinnum = MSG_ReadByte ();
	else
		state->skinnum = 0;

	if (flags & PF_EFFECTS)
		state->effects = MSG_ReadByte ();
	else
		state->effects = 0;

	if (flags & PF_WEAPONFRAME)
		state->weaponframe = MSG_ReadByte ();
	else
		state->weaponframe = 0;

	// GAJA: Check if we are dead. We need this info to stop tracing
	// also we may use this :   cl.stats[STAT_HEALTH] <= 0    to check if we are dead  
	if (cl.playernum == num)
	{
		if ( (flags & PF_DEAD))
		{
			printf("I AM DEAD: trace_state = %d\n", trace_state);
			if ((trace_state >= start_trace) && (trace_state != dead_time))
				trace_state = stop_trace;
		}
	}

	VectorCopy (state->command.angles, state->viewangles);
}
Example #5
0
void Parse_Playerinfo (void){
	int num,flags, i;
	usercmd_t nullcmd,command;
	extern int gflags;

	player_info_t *p;

	num = MSG_ReadByte();



	p = &players[num];

	gflags = flags = MSG_ReadShort ();
	p->origin[0] = MSG_ReadCoord ();
	p->origin[1] = MSG_ReadCoord ();
	p->origin[2] = MSG_ReadCoord ();



	p->frame = MSG_ReadByte ();
	
	if (flags & PF_MSEC) {

		MSG_ReadByte ();

	} else {

	}

	if (flags & PF_COMMAND)
			MSG_ReadDeltaUsercmd (&nullcmd, &command, protoversion);


	for (i = 0; i < 3; i++) {
		if (flags & (PF_VELOCITY1 << i) )
			p->velocity[i] = MSG_ReadShort();
		else
			p->velocity[i] = 0;
	}
	if (flags & PF_MODEL)
		p->modelindex = MSG_ReadByte ();
	else
		p->modelindex = playermodel;
	if (flags & PF_SKINNUM)
		p->skinnum = MSG_ReadByte ();
	else
		p->skinnum = 0;
	if (flags & PF_EFFECTS)
		p->effects = MSG_ReadByte ();
	else
		p->effects = 0;
	if (flags & PF_WEAPONFRAME)
		p->weaponframe = MSG_ReadByte ();
	else
		p->weaponframe = 0;


		

}
Example #6
0
/*
==================
SV_UserMove

The message usually contains all the movement commands
that were in the last three packets, so that the information
in dropped packets can be recovered.

On very fast clients, there may be multiple usercmd packed into
each of the backup packets.
==================
*/
static void SV_UserMove( client_t *cl, msg_t *msg, bool delta )
{
	int       i;
	int       cmdCount;
	usercmd_t nullcmd;
	usercmd_t cmds[ MAX_PACKET_USERCMDS ];
	usercmd_t *cmd, *oldcmd;

	if ( delta )
	{
		cl->deltaMessage = cl->messageAcknowledge;
	}
	else
	{
		cl->deltaMessage = -1;
	}

	cmdCount = MSG_ReadByte( msg );

	if ( cmdCount < 1 )
	{
		Log::Notice( "cmdCount < 1\n" );
		return;
	}

	if ( cmdCount > MAX_PACKET_USERCMDS )
	{
		Log::Notice( "cmdCount > MAX_PACKET_USERCMDS\n" );
		return;
	}

	memset( &nullcmd, 0, sizeof( nullcmd ) );
	oldcmd = &nullcmd;

	for ( i = 0; i < cmdCount; i++ )
	{
		cmd = &cmds[ i ];
		MSG_ReadDeltaUsercmd( msg, oldcmd, cmd );
//      MSG_ReadDeltaUsercmd( msg, oldcmd, cmd );
		oldcmd = cmd;
	}

	// save time for ping calculation
	cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time;

	// if this is the first usercmd we have received
	// this gamestate, put the client into the world
	if ( cl->state == clientState_t::CS_PRIMED )
	{
		SV_ClientEnterWorld( cl, &cmds[ 0 ] );
		// the moves can be processed normaly
	}

	if ( cl->state != clientState_t::CS_ACTIVE )
	{
		cl->deltaMessage = -1;
		return;
	}

	// usually, the first couple commands will be duplicates
	// of ones we have previously received, but the servertimes
	// in the commands will cause them to be immediately discarded
	for ( i = 0; i < cmdCount; i++ )
	{
		// if this is a cmd from before a map_restart ignore it
		if ( cmds[ i ].serverTime > cmds[ cmdCount - 1 ].serverTime )
		{
			continue;
		}

		// extremely lagged or cmd from before a map_restart
		if ( cmds[ i ].serverTime <= cl->lastUsercmd.serverTime )
		{
			continue;
		}

		SV_ClientThink( cl, &cmds[ i ] );
	}
}
Example #7
0
/*
===================
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;
		}
	}
}
Example #8
0
void CL_ParsePlayerinfo (void)
{
	int			msec;
	int			flags;
	player_info_t	*info;
	player_state_t	*state;
	int			num;
	int			i;

	num = MSG_ReadByte ();
	if (num > MAX_CLIENTS)
		Sys_Error ("CL_ParsePlayerinfo: bad num");

	info = &cl.players[num];

	state = &cl.frames[parsecountmod].playerstate[num];

	flags = state->flags = MSG_ReadShort ();

	state->messagenum = cl.parsecount;
	state->origin[0] = MSG_ReadCoord ();
	state->origin[1] = MSG_ReadCoord ();
	state->origin[2] = MSG_ReadCoord ();

	state->frame = MSG_ReadByte ();

	// the other player's last move was likely some time
	// before the packet was sent out, so accurately track
	// the exact time it was valid at
	if (flags & PF_MSEC)
	{
		msec = MSG_ReadByte ();
		state->state_time = parsecounttime - msec*0.001;
	}
	else
		state->state_time = parsecounttime;

	if (flags & PF_COMMAND)
		MSG_ReadDeltaUsercmd (&nullcmd, &state->command);

	for (i=0 ; i<3 ; i++)
	{
		if (flags & (PF_VELOCITY1<<i) )
			state->velocity[i] = MSG_ReadShort();
		else
			state->velocity[i] = 0;
	}
	if (flags & PF_MODEL)
		state->modelindex = MSG_ReadByte ();
	else
		state->modelindex = cl_playerindex;

	if (flags & PF_SKINNUM)
		state->skinnum = MSG_ReadByte ();
	else
		state->skinnum = 0;

	if (flags & PF_EFFECTS)
		state->effects = MSG_ReadByte ();
	else
		state->effects = 0;

	if (flags & PF_WEAPONFRAME)
		state->weaponframe = MSG_ReadByte ();
	else
		state->weaponframe = 0;

	VectorCopy (state->command.angles, state->viewangles);
}
Example #9
0
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;

		}
	}
}