/* ================= 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); }
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); }
/* =================== 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; } } }
/* ================= 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 ); }
/* ================= 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); }
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)); }
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; } } }