void SV_RunBots (void) { int i; client_t *cl; edict_t *ent; usercmd_t cmd; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->bot) continue; // FIXME, we get an infinite loop in COM_HullPointContents // if we spawn the bot in one of the first two SV_Physics calls // Why? if (cl->state == cs_connected && sv.state == ss_active) { Bot_Spawn_And_Begin (cl); continue; } if (cl->state != cs_spawned) continue; ent = cl->edict; // create a fake client move command for prediction's sake cmd = nullcmd; VectorCopy (ent->v.v_angle, cmd.angles); cmd.msec = min ((svs.realtime - cl->cmdtime) * 1000, 255); cl->lastcmd = cmd; cl->cmdtime = svs.realtime; // update bogus network stuff cl->netchan.last_received = curtime; SZ_Clear (&cl->datagram); // don't overflow SV_ClearReliable (cl); // don't overflow // // think and run physics // if (sv_paused.value) continue; sv_client = cl; sv_player = ent; SV_PreRunCmd (); SV_RunCmd (&cmd); SV_PostRunCmd (); } }
/* =========== SV_RunCmd =========== */ void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed ) { usercmd_t lastcmd; edict_t *clent, *touch; double frametime; int i, oldmsec; pmtrace_t *pmtrace; trace_t trace; vec3_t oldvel; clent = cl->edict; if( cl->next_movetime > host.realtime ) { cl->last_movetime += ( ucmd->msec * 0.001f ); return; } cl->next_movetime = 0.0; // chop up very long commands if( ucmd->msec > 50 ) { lastcmd = *ucmd; oldmsec = ucmd->msec; lastcmd.msec = oldmsec / 2; SV_RunCmd( cl, &lastcmd, random_seed ); lastcmd.msec = oldmsec / 2 + (oldmsec & 1); // give them back thier msec. lastcmd.impulse = 0; SV_RunCmd( cl, &lastcmd, random_seed ); return; } if( !cl->fakeclient ) { SV_SetupMoveInterpolant( cl ); } lastcmd = *ucmd; svgame.dllFuncs.pfnCmdStart( cl->edict, ucmd, random_seed ); frametime = ucmd->msec * 0.001; cl->timebase += frametime; cl->last_movetime += frametime; PM_CheckMovingGround( clent, frametime ); VectorCopy( clent->v.v_angle, svgame.pmove->oldangles ); // save oldangles if( !clent->v.fixangle ) VectorCopy( ucmd->viewangles, clent->v.v_angle ); VectorClear( clent->v.clbasevelocity ); // copy player buttons clent->v.button = ucmd->buttons; if( ucmd->impulse ) clent->v.impulse = ucmd->impulse; if( ucmd->impulse == 204 ) { // force client.dll update SV_RefreshUserinfo(); } svgame.globals->time = cl->timebase; svgame.dllFuncs.pfnPlayerPreThink( clent ); SV_PlayerRunThink( clent, frametime, cl->timebase ); // If conveyor, or think, set basevelocity, then send to client asap too. if( !VectorIsNull( clent->v.basevelocity )) VectorCopy( clent->v.basevelocity, clent->v.clbasevelocity ); // setup playermove state SV_SetupPMove( svgame.pmove, cl, ucmd, cl->physinfo ); // motor! svgame.dllFuncs.pfnPM_Move( svgame.pmove, true ); // copy results back to client SV_FinishPMove( svgame.pmove, cl ); // link into place and touch triggers SV_LinkEdict( clent, true ); VectorCopy( clent->v.velocity, oldvel ); // save velocity // touch other objects for( i = 0; i < svgame.pmove->numtouch; i++ ) { // never touch the objects when "playersonly" is active if( i == MAX_PHYSENTS || ( sv.hostflags & SVF_PLAYERSONLY )) break; pmtrace = &svgame.pmove->touchindex[i]; touch = EDICT_NUM( svgame.pmove->physents[pmtrace->ent].info ); if( touch == clent ) continue; VectorCopy( pmtrace->deltavelocity, clent->v.velocity ); SV_ConvertPMTrace( &trace, pmtrace, touch ); SV_Impact( touch, clent, &trace ); } // restore velocity VectorCopy( oldvel, clent->v.velocity ); svgame.pmove->numtouch = 0; svgame.globals->time = cl->timebase; svgame.globals->frametime = frametime; // run post-think svgame.dllFuncs.pfnPlayerPostThink( clent ); svgame.dllFuncs.pfnCmdEnd( clent ); if( !cl->fakeclient ) { SV_RestoreMoveInterpolant( cl ); } }
/* =================== SV_ExecuteClientMessage The current net_message is parsed for the given client =================== */ void SV_ExecuteClientMessage (client_t *cl) { int c; const char *s; usercmd_t oldest, oldcmd, newcmd; client_frame_t *frame; vec3_t o; // 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; // 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 ("%s: badread\n", __thisfunc__); SV_DropClient (cl); return; } c = MSG_ReadByte (); if (c == -1) break; switch (c) { default: Con_Printf ("%s: unknown command char\n", __thisfunc__); SV_DropClient (cl); return; case clc_nop: break; case clc_delta: cl->delta_sequence = MSG_ReadByte (); break; case clc_move: MSG_ReadUsercmd (&oldest, false); MSG_ReadUsercmd (&oldcmd, false); MSG_ReadUsercmd (&newcmd, true); if ( cl->state != cs_spawned ) break; 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_inv_select: cl->edict->v.inventory = MSG_ReadByte(); break; case clc_get_effect: c = MSG_ReadByte(); if (sv.Effects[c].type) { Con_Printf("Getting effect %d\n",(int)c); SV_SendEffect(&host_client->netchan.message, c); } break; } } }
/* =========== SV_RunCmd =========== */ static void SV_RunCmd (usercmd_t *ucmd) { edict_t *ent; int i, n; int oldmsec; cmd = *ucmd; // chop up very long commands if (cmd.msec > 50) { oldmsec = ucmd->msec; cmd.msec = oldmsec/2; SV_RunCmd (&cmd); cmd.msec = oldmsec/2; cmd.impulse = 0; SV_RunCmd (&cmd); return; } if (!sv_player->v.fixangle) VectorCopy (ucmd->angles, sv_player->v.v_angle); sv_player->v.button0 = ucmd->buttons & 1; sv_player->v.button2 = (ucmd->buttons & 2)>>1; if (ucmd->buttons & 4 || sv_player->v.playerclass == CLASS_DWARF) // crouched? sv_player->v.flags2 = ((int)sv_player->v.flags2) | FL2_CROUCHED; else sv_player->v.flags2 = ((int)sv_player->v.flags2) & (~FL2_CROUCHED); if (ucmd->impulse) sv_player->v.impulse = ucmd->impulse; // // angles // show 1/3 the pitch angle and all the roll angle if (sv_player->v.health > 0) { if (!sv_player->v.fixangle) { sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; } sv_player->v.angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; } host_frametime = ucmd->msec * 0.001; if (host_frametime > HX_FRAME_TIME) host_frametime = HX_FRAME_TIME; if (!host_client->spectator) { pr_global_struct->frametime = host_frametime; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); SV_RunThink (sv_player); } for (i = 0; i < 3; i++) pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); VectorCopy (sv_player->v.velocity, pmove.velocity); VectorCopy (sv_player->v.v_angle, pmove.angles); pmove.spectator = host_client->spectator; // pmove.waterjumptime = sv_player->v.teleport_time; pmove.numphysent = 1; pmove.physents[0].model = sv.worldmodel; pmove.cmd = *ucmd; pmove.dead = sv_player->v.health <= 0; pmove.oldbuttons = host_client->oldbuttons; pmove.hasted = sv_player->v.hasted; pmove.movetype = sv_player->v.movetype; pmove.crouched = (sv_player->v.hull == HULL_CROUCH); pmove.teleport_time = realtime + (sv_player->v.teleport_time - sv.time); // movevars.entgravity = host_client->entgravity; movevars.entgravity = sv_player->v.gravity; movevars.maxspeed = host_client->maxspeed; for (i = 0; i < 3; i++) { pmove_mins[i] = pmove.origin[i] - 256; pmove_maxs[i] = pmove.origin[i] + 256; } #if 1 AddLinksToPmove ( sv_areanodes ); #else AddAllEntsToPmove (); #endif #if 0 { int before, after; before = PM_TestPlayerPosition (pmove.origin); PlayerMove (); after = PM_TestPlayerPosition (pmove.origin); if (sv_player->v.health > 0 && before && !after ) Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); } #else PlayerMove (); #endif host_client->oldbuttons = pmove.oldbuttons; // sv_player->v.teleport_time = pmove.waterjumptime; sv_player->v.waterlevel = waterlevel; sv_player->v.watertype = watertype; if (onground != -1) { sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); } else sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; for (i = 0; i < 3; i++) sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); #if 0 // truncate velocity the same way the net protocol will for (i = 0; i < 3; i++) sv_player->v.velocity[i] = (int)pmove.velocity[i]; #else VectorCopy (pmove.velocity, sv_player->v.velocity); #endif VectorCopy (pmove.angles, sv_player->v.v_angle); if (!host_client->spectator) { // link into place and touch triggers SV_LinkEdict (sv_player, true); // touch other objects for (i = 0; i < pmove.numtouch; i++) { n = pmove.physents[pmove.touchindex[i]].info; ent = EDICT_NUM(n); // Why not just do an SV_Impact here? // SV_Impact(sv_player,ent); if (sv_player->v.touch) { pr_global_struct->self = EDICT_TO_PROG(sv_player); pr_global_struct->other = EDICT_TO_PROG(ent); PR_ExecuteProgram (sv_player->v.touch); } if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) continue; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (ent->v.touch); playertouch[n/8] |= 1 << (n%8); } } }
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; } } }