/* ============== CL_ParseMuzzleFlash2 ============== */ void CL_ParseMuzzleFlash2(void){ int ent; vec3_t origin; int flash_number; cdlight_t *dl; vec3_t forward, right; char soundname[64]; ent = MSG_ReadShort(&net_message); if(ent < 1 || ent >= MAX_EDICTS) Com_Error(ERR_DROP, "CL_ParseMuzzleFlash2: bad entity"); flash_number = MSG_ReadByte(&net_message); // locate the origin AngleVectors(cl_entities[ent].current.angles, forward, right, NULL); origin[0] = cl_entities[ent].current.origin[0] + forward[0] * monster_flash_offset[flash_number][0] + right[0] * monster_flash_offset[flash_number][1]; origin[1] = cl_entities[ent].current.origin[1] + forward[1] * monster_flash_offset[flash_number][0] + right[1] * monster_flash_offset[flash_number][1]; origin[2] = cl_entities[ent].current.origin[2] + forward[2] * monster_flash_offset[flash_number][0] + right[2] * monster_flash_offset[flash_number][1] + monster_flash_offset[flash_number][2]; dl = CL_AllocDlight(ent); VectorCopy(origin, dl->origin); dl->radius = 200 +(rand() & 31); dl->minlight = 32; dl->die = cl.time; // + 0.1; switch(flash_number){ case MZ2_INFANTRY_MACHINEGUN_1: case MZ2_INFANTRY_MACHINEGUN_2: case MZ2_INFANTRY_MACHINEGUN_3: case MZ2_INFANTRY_MACHINEGUN_4: case MZ2_INFANTRY_MACHINEGUN_5: case MZ2_INFANTRY_MACHINEGUN_6: case MZ2_INFANTRY_MACHINEGUN_7: case MZ2_INFANTRY_MACHINEGUN_8: case MZ2_INFANTRY_MACHINEGUN_9: case MZ2_INFANTRY_MACHINEGUN_10: case MZ2_INFANTRY_MACHINEGUN_11: case MZ2_INFANTRY_MACHINEGUN_12: case MZ2_INFANTRY_MACHINEGUN_13: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_SOLDIER_MACHINEGUN_1: case MZ2_SOLDIER_MACHINEGUN_2: case MZ2_SOLDIER_MACHINEGUN_3: case MZ2_SOLDIER_MACHINEGUN_4: case MZ2_SOLDIER_MACHINEGUN_5: case MZ2_SOLDIER_MACHINEGUN_6: case MZ2_SOLDIER_MACHINEGUN_7: case MZ2_SOLDIER_MACHINEGUN_8: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_GUNNER_MACHINEGUN_1: case MZ2_GUNNER_MACHINEGUN_2: case MZ2_GUNNER_MACHINEGUN_3: case MZ2_GUNNER_MACHINEGUN_4: case MZ2_GUNNER_MACHINEGUN_5: case MZ2_GUNNER_MACHINEGUN_6: case MZ2_GUNNER_MACHINEGUN_7: case MZ2_GUNNER_MACHINEGUN_8: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0); break; case MZ2_ACTOR_MACHINEGUN_1: case MZ2_SUPERTANK_MACHINEGUN_1: case MZ2_SUPERTANK_MACHINEGUN_2: case MZ2_SUPERTANK_MACHINEGUN_3: case MZ2_SUPERTANK_MACHINEGUN_4: case MZ2_SUPERTANK_MACHINEGUN_5: case MZ2_SUPERTANK_MACHINEGUN_6: case MZ2_TURRET_MACHINEGUN: // PGM dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_BOSS2_MACHINEGUN_L1: case MZ2_BOSS2_MACHINEGUN_L2: case MZ2_BOSS2_MACHINEGUN_L3: case MZ2_BOSS2_MACHINEGUN_L4: case MZ2_BOSS2_MACHINEGUN_L5: case MZ2_CARRIER_MACHINEGUN_L1: // PMM case MZ2_CARRIER_MACHINEGUN_L2: // PMM dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0); break; case MZ2_SOLDIER_BLASTER_1: case MZ2_SOLDIER_BLASTER_2: case MZ2_SOLDIER_BLASTER_3: case MZ2_SOLDIER_BLASTER_4: case MZ2_SOLDIER_BLASTER_5: case MZ2_SOLDIER_BLASTER_6: case MZ2_SOLDIER_BLASTER_7: case MZ2_SOLDIER_BLASTER_8: case MZ2_TURRET_BLASTER: // PGM dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0); break; case MZ2_FLYER_BLASTER_1: case MZ2_FLYER_BLASTER_2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_MEDIC_BLASTER_1: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("medic/medatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_HOVER_BLASTER_1: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_FLOAT_BLASTER_1: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_SOLDIER_SHOTGUN_1: case MZ2_SOLDIER_SHOTGUN_2: case MZ2_SOLDIER_SHOTGUN_3: case MZ2_SOLDIER_SHOTGUN_4: case MZ2_SOLDIER_SHOTGUN_5: case MZ2_SOLDIER_SHOTGUN_6: case MZ2_SOLDIER_SHOTGUN_7: case MZ2_SOLDIER_SHOTGUN_8: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_TANK_BLASTER_1: case MZ2_TANK_BLASTER_2: case MZ2_TANK_BLASTER_3: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_TANK_MACHINEGUN_1: case MZ2_TANK_MACHINEGUN_2: case MZ2_TANK_MACHINEGUN_3: case MZ2_TANK_MACHINEGUN_4: case MZ2_TANK_MACHINEGUN_5: case MZ2_TANK_MACHINEGUN_6: case MZ2_TANK_MACHINEGUN_7: case MZ2_TANK_MACHINEGUN_8: case MZ2_TANK_MACHINEGUN_9: case MZ2_TANK_MACHINEGUN_10: case MZ2_TANK_MACHINEGUN_11: case MZ2_TANK_MACHINEGUN_12: case MZ2_TANK_MACHINEGUN_13: case MZ2_TANK_MACHINEGUN_14: case MZ2_TANK_MACHINEGUN_15: case MZ2_TANK_MACHINEGUN_16: case MZ2_TANK_MACHINEGUN_17: case MZ2_TANK_MACHINEGUN_18: case MZ2_TANK_MACHINEGUN_19: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); Com_sprintf(soundname, sizeof(soundname), "tank/tnkatk2%c.wav", 'a' + rand() % 5); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(soundname), 1, ATTN_NORM, 0); break; case MZ2_CHICK_ROCKET_1: case MZ2_TURRET_ROCKET: // PGM dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.2; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0); break; case MZ2_TANK_ROCKET_1: case MZ2_TANK_ROCKET_2: case MZ2_TANK_ROCKET_3: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.2; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_SUPERTANK_ROCKET_1: case MZ2_SUPERTANK_ROCKET_2: case MZ2_SUPERTANK_ROCKET_3: case MZ2_BOSS2_ROCKET_1: case MZ2_BOSS2_ROCKET_2: case MZ2_BOSS2_ROCKET_3: case MZ2_BOSS2_ROCKET_4: case MZ2_CARRIER_ROCKET_1: // case MZ2_CARRIER_ROCKET_2: // case MZ2_CARRIER_ROCKET_3: // case MZ2_CARRIER_ROCKET_4: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.2; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/rocket.wav"), 1, ATTN_NORM, 0); break; case MZ2_GUNNER_GRENADE_1: case MZ2_GUNNER_GRENADE_2: case MZ2_GUNNER_GRENADE_3: case MZ2_GUNNER_GRENADE_4: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_GLADIATOR_RAILGUN_1: // PMM case MZ2_CARRIER_RAILGUN: case MZ2_WIDOW_RAIL: // pmm dl->color[0] = 0.5; dl->color[1] = 0.5; dl->color[2] = 1.0; break; // --- Xian's shit starts --- case MZ2_MAKRON_BFG: dl->color[0] = 0.5; dl->color[1] = 1; dl->color[2] = 0.5; //S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/bfg_fire.wav"), 1, ATTN_NORM, 0); break; case MZ2_MAKRON_BLASTER_1: case MZ2_MAKRON_BLASTER_2: case MZ2_MAKRON_BLASTER_3: case MZ2_MAKRON_BLASTER_4: case MZ2_MAKRON_BLASTER_5: case MZ2_MAKRON_BLASTER_6: case MZ2_MAKRON_BLASTER_7: case MZ2_MAKRON_BLASTER_8: case MZ2_MAKRON_BLASTER_9: case MZ2_MAKRON_BLASTER_10: case MZ2_MAKRON_BLASTER_11: case MZ2_MAKRON_BLASTER_12: case MZ2_MAKRON_BLASTER_13: case MZ2_MAKRON_BLASTER_14: case MZ2_MAKRON_BLASTER_15: case MZ2_MAKRON_BLASTER_16: case MZ2_MAKRON_BLASTER_17: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/blaster.wav"), 1, ATTN_NORM, 0); break; case MZ2_JORG_MACHINEGUN_L1: case MZ2_JORG_MACHINEGUN_L2: case MZ2_JORG_MACHINEGUN_L3: case MZ2_JORG_MACHINEGUN_L4: case MZ2_JORG_MACHINEGUN_L5: case MZ2_JORG_MACHINEGUN_L6: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("boss3/xfire.wav"), 1, ATTN_NORM, 0); break; case MZ2_JORG_MACHINEGUN_R1: case MZ2_JORG_MACHINEGUN_R2: case MZ2_JORG_MACHINEGUN_R3: case MZ2_JORG_MACHINEGUN_R4: case MZ2_JORG_MACHINEGUN_R5: case MZ2_JORG_MACHINEGUN_R6: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); break; case MZ2_JORG_BFG_1: dl->color[0] = 0.5; dl->color[1] = 1; dl->color[2] = 0.5; break; case MZ2_BOSS2_MACHINEGUN_R1: case MZ2_BOSS2_MACHINEGUN_R2: case MZ2_BOSS2_MACHINEGUN_R3: case MZ2_BOSS2_MACHINEGUN_R4: case MZ2_BOSS2_MACHINEGUN_R5: case MZ2_CARRIER_MACHINEGUN_R1: // PMM case MZ2_CARRIER_MACHINEGUN_R2: // PMM dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); break; // ====== // ROGUE case MZ2_STALKER_BLASTER: case MZ2_DAEDALUS_BLASTER: case MZ2_MEDIC_BLASTER_2: case MZ2_WIDOW_BLASTER: case MZ2_WIDOW_BLASTER_SWEEP1: case MZ2_WIDOW_BLASTER_SWEEP2: case MZ2_WIDOW_BLASTER_SWEEP3: case MZ2_WIDOW_BLASTER_SWEEP4: case MZ2_WIDOW_BLASTER_SWEEP5: case MZ2_WIDOW_BLASTER_SWEEP6: case MZ2_WIDOW_BLASTER_SWEEP7: case MZ2_WIDOW_BLASTER_SWEEP8: case MZ2_WIDOW_BLASTER_SWEEP9: case MZ2_WIDOW_BLASTER_100: case MZ2_WIDOW_BLASTER_90: case MZ2_WIDOW_BLASTER_80: case MZ2_WIDOW_BLASTER_70: case MZ2_WIDOW_BLASTER_60: case MZ2_WIDOW_BLASTER_50: case MZ2_WIDOW_BLASTER_40: case MZ2_WIDOW_BLASTER_30: case MZ2_WIDOW_BLASTER_20: case MZ2_WIDOW_BLASTER_10: case MZ2_WIDOW_BLASTER_0: case MZ2_WIDOW_BLASTER_10L: case MZ2_WIDOW_BLASTER_20L: case MZ2_WIDOW_BLASTER_30L: case MZ2_WIDOW_BLASTER_40L: case MZ2_WIDOW_BLASTER_50L: case MZ2_WIDOW_BLASTER_60L: case MZ2_WIDOW_BLASTER_70L: case MZ2_WIDOW_RUN_1: case MZ2_WIDOW_RUN_2: case MZ2_WIDOW_RUN_3: case MZ2_WIDOW_RUN_4: case MZ2_WIDOW_RUN_5: case MZ2_WIDOW_RUN_6: case MZ2_WIDOW_RUN_7: case MZ2_WIDOW_RUN_8: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_WIDOW_DISRUPTOR: dl->color[0] = -1; dl->color[1] = -1; dl->color[2] = -1; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), 1, ATTN_NORM, 0); break; case MZ2_WIDOW_PLASMABEAM: case MZ2_WIDOW2_BEAMER_1: case MZ2_WIDOW2_BEAMER_2: case MZ2_WIDOW2_BEAMER_3: case MZ2_WIDOW2_BEAMER_4: case MZ2_WIDOW2_BEAMER_5: case MZ2_WIDOW2_BEAM_SWEEP_1: case MZ2_WIDOW2_BEAM_SWEEP_2: case MZ2_WIDOW2_BEAM_SWEEP_3: case MZ2_WIDOW2_BEAM_SWEEP_4: case MZ2_WIDOW2_BEAM_SWEEP_5: case MZ2_WIDOW2_BEAM_SWEEP_6: case MZ2_WIDOW2_BEAM_SWEEP_7: case MZ2_WIDOW2_BEAM_SWEEP_8: case MZ2_WIDOW2_BEAM_SWEEP_9: case MZ2_WIDOW2_BEAM_SWEEP_10: case MZ2_WIDOW2_BEAM_SWEEP_11: dl->radius = 300 +(rand() & 100); dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 200; break; // ROGUE // ====== // --- Xian's shit ends --- } }
/* ===================== CL_ParseVoip A VoIP message has been received from the server ===================== */ static void CL_ParseVoip ( msg_t *msg ) { static short decoded[4096]; // !!! FIXME: don't hardcode. const int sender = MSG_ReadShort(msg); const int generation = MSG_ReadByte(msg); const int sequence = MSG_ReadLong(msg); const int frames = MSG_ReadByte(msg); const int packetsize = MSG_ReadShort(msg); const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT); char encoded[1024]; int seqdiff; int written = 0; int i; Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender); if (sender < 0) return; // short/invalid packet, bail. else if (generation < 0) return; // short/invalid packet, bail. else if (sequence < 0) return; // short/invalid packet, bail. else if (frames < 0) return; // short/invalid packet, bail. else if (packetsize < 0) return; // short/invalid packet, bail. if (packetsize > sizeof (encoded)) { // overlarge packet? int bytesleft = packetsize; while (bytesleft) { int br = bytesleft; if (br > sizeof (encoded)) br = sizeof (encoded); MSG_ReadData(msg, encoded, br); bytesleft -= br; } return; // overlarge packet, bail. } if (!clc.speexInitialized) { MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // can't handle VoIP without libspeex! } else if (sender >= MAX_CLIENTS) { MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // bogus sender. } else if (CL_ShouldIgnoreVoipSender(sender)) { MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // Channel is muted, bail. } // !!! FIXME: make sure data is narrowband? Does decoder handle this? Com_DPrintf("VoIP: packet accepted!\n"); seqdiff = sequence - clc.voipIncomingSequence[sender]; // This is a new "generation" ... a new recording started, reset the bits. if (generation != clc.voipIncomingGeneration[sender]) { Com_DPrintf("VoIP: new generation %d!\n", generation); speex_bits_reset(&clc.speexDecoderBits[sender]); clc.voipIncomingGeneration[sender] = generation; seqdiff = 0; } else if (seqdiff < 0) { // we're ahead of the sequence?! // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[sender]); // reset the bits just in case. speex_bits_reset(&clc.speexDecoderBits[sender]); seqdiff = 0; } else if (seqdiff * clc.speexFrameSize * 2 >= sizeof (decoded)) { // dropped more than we can handle? // just start over. Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender); speex_bits_reset(&clc.speexDecoderBits[sender]); seqdiff = 0; } if (seqdiff != 0) { Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", seqdiff, sender); // tell speex that we're missing frames... for (i = 0; i < seqdiff; i++) { assert((written + clc.speexFrameSize) * 2 < sizeof (decoded)); speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written); written += clc.speexFrameSize; } } for (i = 0; i < frames; i++) { const int len = MSG_ReadByte(msg); if (len < 0) { Com_DPrintf("VoIP: Short packet!\n"); break; } MSG_ReadData(msg, encoded, len); // shouldn't happen, but just in case... if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) { Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i); CL_PlayVoip(sender, written, (const byte *) decoded, flags); written = 0; } speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len); speex_decode_int(clc.speexDecoder[sender], &clc.speexDecoderBits[sender], decoded + written); #if 0 static FILE *encio = NULL; if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } static FILE *decio = NULL; if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } #endif written += clc.speexFrameSize; } Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i); if(written > 0) CL_PlayVoip(sender, written, (const byte *) decoded, flags); clc.voipIncomingSequence[sender] = sequence + frames; }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( msg_t *msg ) { int cmd; if ( cl_shownet->integer == 1 ) { Com_Printf ("%i ",msg->cursize); } else if ( cl_shownet->integer >= 2 ) { Com_Printf ("------------------\n"); } MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong( msg ); // if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { clc.reliableAcknowledge = clc.reliableSequence; } // // parse the message // while ( 1 ) { if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message"); break; } cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); break; } if ( cl_shownet->integer >= 2 ) { if ( !svc_strings[cmd] ) { Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); } else { SHOWNET( msg, svc_strings[cmd] ); } } // other commands switch ( cmd ) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_serverCommand: CL_ParseCommandString( msg ); break; case svc_gamestate: CL_ParseGamestate( msg ); break; case svc_snapshot: CL_ParseSnapshot( msg ); break; case svc_download: CL_ParseDownload( msg ); break; case svc_mapchange: if (cgvm) { VM_Call( cgvm, CG_MAP_CHANGE ); } break; } } }
static int read_server_file(void) { char name[MAX_OSPATH], string[MAX_STRING_CHARS]; mapcmd_t cmd; size_t len; // errors like missing file, bad version, etc are // non-fatal and just return to the command handler if (read_binary_file("save/" SAVE_CURRENT "/server.ssv")) return -1; if (MSG_ReadLong() != SAVE_MAGIC1) return -1; if (MSG_ReadLong() != SAVE_VERSION) return -1; memset(&cmd, 0, sizeof(cmd)); // read the comment field MSG_ReadLong(); MSG_ReadLong(); if (MSG_ReadByte()) cmd.loadgame = 2; // autosave else cmd.loadgame = 1; // regular savegame MSG_ReadString(NULL, 0); // read the mapcmd len = MSG_ReadString(cmd.buffer, sizeof(cmd.buffer)); if (len >= sizeof(cmd.buffer)) return -1; // now try to load the map if (!SV_ParseMapCmd(&cmd)) return -1; // save pending CM to be freed later if ERR_DROP is thrown Com_AbortFunc(abort_func, &cmd.cm); // any error will drop from this point SV_Shutdown("Server restarted\n", ERR_RECONNECT); // the rest can't underflow msg_read.allowunderflow = qfalse; // read all CVAR_LATCH cvars // these will be things like coop, skill, deathmatch, etc while (1) { len = MSG_ReadString(name, MAX_QPATH); if (!len) break; if (len >= MAX_QPATH) Com_Error(ERR_DROP, "Savegame cvar name too long"); len = MSG_ReadString(string, sizeof(string)); if (len >= sizeof(string)) Com_Error(ERR_DROP, "Savegame cvar value too long"); Cvar_UserSet(name, string); } // start a new game fresh with new cvars SV_InitGame(MVD_SPAWN_DISABLED); // error out immediately if game doesn't support safe savegames if (!(g_features->integer & GMF_ENHANCED_SAVEGAMES)) Com_Error(ERR_DROP, "Game does not support enhanced savegames"); // read game state len = Q_snprintf(name, MAX_OSPATH, "%s/save/" SAVE_CURRENT "/game.ssv", fs_gamedir); if (len >= MAX_OSPATH) Com_Error(ERR_DROP, "Savegame path too long"); ge->ReadGame(name); // clear pending CM Com_AbortFunc(NULL, NULL); // go to the map SV_SpawnServer(&cmd); return 0; }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( msg_t *msg ) { int cmd; if ( cl_shownet->integer == 1 ) { Com_Printf ("%i ",msg->cursize); } else if ( cl_shownet->integer >= 2 ) { Com_Printf ("------------------\n"); } #ifdef ELITEFORCE if(!msg->compat) { #endif MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong( msg ); // if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { clc.reliableAcknowledge = clc.reliableSequence; } #ifdef ELITEFORCE } #endif // // parse the message // while ( 1 ) { if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message"); break; } cmd = MSG_ReadByte( msg ); #ifdef ELITEFORCE if ( cmd == svc_EOF || ( msg->compat && cmd == -1 ) ) #else if ( cmd == svc_EOF) #endif { SHOWNET( msg, "END OF MESSAGE" ); break; } if ( cl_shownet->integer >= 2 ) { if ( (cmd < 0) || (!svc_strings[cmd]) ) { Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); } else { SHOWNET( msg, svc_strings[cmd] ); } } // other commands switch ( cmd ) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message"); break; case svc_nop: break; case svc_serverCommand: CL_ParseCommandString( msg ); break; case svc_gamestate: CL_ParseGamestate( msg ); break; case svc_snapshot: CL_ParseSnapshot( msg ); break; case svc_download: CL_ParseDownload( msg ); break; case svc_voip: #ifdef USE_VOIP CL_ParseVoip( msg ); #endif break; } } }
/* ================= CL_ParseTEnt ================= */ void CL_ParseTEnt (void) { int type; vec3_t pos; dlight_t *dl; int rnd; int colorStart, colorLength; type = MSG_ReadByte (); switch (type) { case TE_WIZSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 20, 30); #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -20, 11); //qbism ftestain #endif S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); break; case TE_KNIGHTSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 226, 20); #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -20, 15); //qbism ftestain #endif S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); break; case TE_SPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 10); #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -30, 10); //qbism ftestain #endif if ( rand() % 5 ) { S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); } else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_SUPERSPIKE: // super spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20); #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -30, 10); //qbism ftestain #endif if ( rand() % 5 ) { S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); } else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_GUNSHOT: // bullet hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); #ifdef SUPPORTS_KUROK if (!kurok) #endif { R_RunParticleEffect (pos, vec3_origin, 0, 20); #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -40, 12); //qbism ftestain #endif } break; case TE_EXPLOSION: // rocket explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_ParticleExplosion (pos); dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; #ifdef SUPPORTS_KUROK if(kurok) { dl->color[0] = MSG_ReadCoord (); dl->color[1] = MSG_ReadCoord (); dl->color[2] = MSG_ReadCoord (); } #endif #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -30, 45); //qbism ftestain #endif S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_TAREXPLOSION: // tarbaby explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_BlobExplosion (pos); #ifdef SUPPORTS_KUROK if(kurok) { dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 150; dl->die = cl.time + 0.75; dl->decay = 200; dl->color[0] = MSG_ReadCoord (); dl->color[1] = MSG_ReadCoord (); dl->color[2] = MSG_ReadCoord (); } #endif #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -20, 60); //qbism ftestain #endif S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_LIGHTNING1: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); break; case TE_LIGHTNING2: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); break; case TE_LIGHTNING3: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); break; // PGM 01/21/97 case TE_BEAM: // grappling hook beam CL_ParseBeam (Mod_ForName("progs/beam.mdl", true)); break; // PGM 01/21/97 case TE_LAVASPLASH: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_LavaSplash (pos); break; case TE_TELEPORT: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_TeleportSplash (pos); break; case TE_EXPLOSION2: // color mapped explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); colorStart = MSG_ReadByte (); colorLength = MSG_ReadByte (); R_ParticleExplosion2 (pos, colorStart, colorLength); dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; #ifdef SUPPORTS_SOFTWARE_FTESTAIN R_AddStain(pos, -30, 50); //qbism ftestain #endif S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; default: Sys_Error ("CL_ParseTEnt: bad type"); } }
/* ================== 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, qboolean delta ) { int i, key; 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 ) { Com_Printf( "cmdCount < 1\n" ); return; } if ( cmdCount > MAX_PACKET_USERCMDS ) { Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" ); return; } // use the checksum feed in the key key = sv.checksumFeed; // also use the message acknowledge key ^= cl->messageAcknowledge; // also use the last acknowledged server command in the key key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32); Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation // With sv_pingFix enabled we store the time of the first acknowledge, instead of the last. And we use a time value that is not limited by sv_fps. if ( !sv_pingFix->integer || cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked == -1 ) cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = (sv_pingFix->integer ? Sys_Milliseconds() : svs.time); // if this is the first usercmd we have received // this gamestate, put the client into the world if ( cl->state == CS_PRIMED ) { SV_SendServerCommand(cl, "print \"^1[ ^7This server is running JK2MV ^1v^7" JK2MV_VERSION " ^1| ^7http://jk2mv.org ^1]\n\""); SV_ClientEnterWorld( cl, &cmds[0] ); // the moves can be processed normaly } // if (sv_pure->integer != 0 && cl->pureAuthentic == 0) { SV_DropClient( cl, "Cannot validate pure client!"); return; } if ( cl->state != 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 > sv.time + 3000 ) { // continue; //} // don't execute if this is an old cmd which is already executed // these old cmds are included when cl_packetdup > 0 if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) { continue; } SV_ClientThink (cl - svs.clients, &cmds[ i ]); } }
static void _Datagram_SearchForHosts(qboolean xmit) { int ret; int n; int i; struct qsockaddr readaddr; struct qsockaddr myaddr; int control; dfunc.GetSocketAddr(dfunc.controlSock, &myaddr); if (xmit) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); SZ_Clear(&net_message); } while ((ret = dfunc.Read(dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { if (ret < sizeof(int)) { continue; } net_message.cursize = ret; // don't answer our own query if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) { continue; } // is the cache full? if (hostCacheCount == HOSTCACHESIZE) { continue; } MSG_BeginReading(); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { continue; } if (MSG_ReadByte() != CCREP_SERVER_INFO) { continue; } dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); // search the cache for this server for (n = 0; n < hostCacheCount; n++) if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) { break; } // is it already there? if (n < hostCacheCount) { continue; } // add it hostCacheCount++; Q_strcpy(hostcache[n].name, MSG_ReadString()); Q_strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { Q_strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; Q_strcpy(hostcache[n].name, "*"); Q_strcat(hostcache[n].name, hostcache[n].cname); } Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) { continue; } if (Q_strcasecmp(hostcache[n].name, hostcache[i].name) == 0) { i = Q_strlen(hostcache[n].name); if (i < 15 && hostcache[n].name[i-1] > '8') { hostcache[n].name[i] = '0'; hostcache[n].name[i+1] = 0; } else { hostcache[n].name[i-1]++; } i = -1; } } } }
static qsocket_t *_Datagram_Connect(char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; int newsock; int ret; int reps; double start_time; int control; char *reason; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) == -1) { return NULL; } newsock = dfunc.OpenSocket(0); if (newsock == -1) { return NULL; } sock = NET_NewQSocket(); if (sock == NULL) { goto ErrorReturn2; } sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if (dfunc.Connect(newsock, &sendaddr) == -1) { goto ErrorReturn; } // send the connection request Con_Printf("trying...\n"); SCR_UpdateScreen(); start_time = net_time; for (reps = 0; reps < 3; reps++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(newsock, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); do { ret = dfunc.Read(newsock, net_message.data, net_message.maxsize, &readaddr); // if we got something, validate it if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) { #ifdef DEBUG Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s\n", StrAddr(&sendaddr)); Con_Printf("Received: %s\n", StrAddr(&readaddr)); SCR_UpdateScreen(); #endif ret = 0; continue; } if (ret < sizeof(int)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading(); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { ret = 0; continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { ret = 0; continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { ret = 0; continue; } } } while (ret == 0 && (SetNetTime() - start_time) < 2.5); if (ret) { break; } Con_Printf("still trying...\n"); SCR_UpdateScreen(); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf(reason); Q_strncpy(m_return_reason, reason, 31); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort(&sock->addr, MSG_ReadLong()); } else { reason = "Bad Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr(&sendaddr, sock->address); Con_Printf("Connection accepted\n"); sock->lastMessageTime = SetNetTime(); // switch the connection to the specified address if (dfunc.Connect(newsock, &sock->addr) == -1) { reason = "Connect to Game failed"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } m_return_onerror = false; return sock; ErrorReturn: NET_FreeQSocket(sock); ErrorReturn2: dfunc.CloseSocket(newsock); if (m_return_onerror) { key_dest = key_menu; m_state = m_return_state; m_return_onerror = false; } return NULL; }
static void CL_ParseDemoSnapShotSimple(msg_t *msg) { int len; clSnapshot_t *old; clSnapshot_t newSnap; int deltaNum; int oldMessageNum; int i, packetNum; memset(&newSnap, 0, sizeof(newSnap)); newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = MSG_ReadLong(msg); newSnap.messageNum = clc.serverMessageSequence; deltaNum = MSG_ReadByte(msg); if (!deltaNum) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = MSG_ReadByte(msg); if (newSnap.deltaNum <= 0) { newSnap.valid = qtrue; // uncompressed frame old = NULL; } else { old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK]; if (!old->valid) { // should never happen Com_FuncPrinf("Delta from invalid frame (not supposed to happen!).\n"); } else if (old->messageNum != newSnap.deltaNum) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_FuncDPrinf("Delta frame too old.\n"); } else if (cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES - 128) { Com_FuncDPrinf("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte(msg); if (len > sizeof(newSnap.areamask)) { Com_FuncDrop("Invalid size %d for areamask.", len); return; } MSG_ReadData(msg, &newSnap.areamask, len); if (old) { MSG_ReadDeltaPlayerstate(msg, &old->ps, &newSnap.ps); } else { MSG_ReadDeltaPlayerstate(msg, NULL, &newSnap.ps); } // read packet entities CL_ParsePacketEntities(msg, old, &newSnap); // if not valid, dump the entire thing now that it has // been properly read if (!newSnap.valid) { return; } // clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer oldMessageNum = cl.snap.messageNum + 1; if (newSnap.messageNum - oldMessageNum >= PACKET_BACKUP) { oldMessageNum = newSnap.messageNum - (PACKET_BACKUP - 1); } for (; oldMessageNum < newSnap.messageNum; oldMessageNum++) { cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; } // copy to the current good spot cl.snap = newSnap; cl.snap.ping = 999; // calculate ping time for (i = 0; i < PACKET_BACKUP; i++) { packetNum = (clc.netchan.outgoingSequence - 1 - i) & PACKET_MASK; if (cl.snap.ps.commandTime >= cl.outPackets[packetNum].p_serverTime) { cl.snap.ping = cls.realtime - cl.outPackets[packetNum].p_realtime; break; } } // save the frame off in the backup array for later delta comparisons cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap; cl.newSnapshots = qtrue; }
// Do very shallow parse of the demo (could be extended) just to get times and snapshot count static void CL_ParseDemo(void) { int tstart = 0; int demofile = 0; // Reset our demo data memset(&di, 0, sizeof(di)); // Parse start di.gameStartTime = -1; di.gameEndTime = -1; FS_Seek(clc.demofile, 0, FS_SEEK_SET); tstart = Sys_Milliseconds(); while (qtrue) { int r; msg_t buf; msg_t *msg; byte bufData[MAX_MSGLEN]; int s; int cmd; di.demoPos = FS_FTell(clc.demofile); // get the sequence number r = FS_Read(&s, 4, clc.demofile); if (r != 4) { CL_DemoCompleted(); return; } clc.serverMessageSequence = LittleLong(s); // init the message MSG_Init(&buf, bufData, sizeof(bufData)); // get the length r = FS_Read(&buf.cursize, 4, clc.demofile); if (r != 4) { break; } buf.cursize = LittleLong(buf.cursize); if (buf.cursize == -1) { break; } if (buf.cursize > buf.maxsize) { Com_FuncDPrinf("demoMsglen > MAX_MSGLEN"); break; } r = FS_Read(buf.data, buf.cursize, clc.demofile); if (r != buf.cursize) { Com_FuncDPrinf("Demo file was truncated.\n"); break; } clc.lastPacketTime = cls.realtime; buf.readcount = 0; // parse msg = &buf; MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong(msg); if (clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS) { clc.reliableAcknowledge = clc.reliableSequence; } // parse the message while (qtrue) { if (msg->readcount > msg->cursize) { Com_FuncDrop("read past end of server message"); break; } cmd = MSG_ReadByte(msg); if (cmd == svc_EOF) { break; } // other commands switch (cmd) { default: Com_FuncDrop("Illegible server message %d", cmd); break; case svc_nop: break; case svc_serverCommand: MSG_ReadLong(msg); MSG_ReadString(msg); break; case svc_gamestate: clc.serverCommandSequence = MSG_ReadLong(msg); cl.gameState.dataCount = 1; while (qtrue) { int cmd2 = MSG_ReadByte(msg); if (cmd2 == svc_EOF) { break; } if (cmd2 == svc_configstring) { MSG_ReadShort(msg); MSG_ReadBigString(msg); } else if (cmd2 == svc_baseline) { entityState_t s1, s2; memset(&s1, 0, sizeof(s1)); memset(&s2, 0, sizeof(s2)); MSG_ReadBits(msg, GENTITYNUM_BITS); MSG_ReadDeltaEntity(msg, &s1, &s2, 0); } else { Com_FuncDrop("bad command byte"); } } MSG_ReadLong(msg); MSG_ReadLong(msg); break; case svc_snapshot: CL_ParseDemoSnapShotSimple(msg); break; case svc_download: MSG_ReadShort(msg); break; } } if (!cl.snap.valid) { Com_FuncDPrinf("!cl.snap.valid\n"); continue; } if (cl.snap.serverTime < cl.oldFrameServerTime) { // ignore snapshots that don't have entities if (cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE) { continue; } cls.state = CA_ACTIVE; // set the timedelta so we are exactly on this first frame cl.serverTimeDelta = cl.snap.serverTime - cls.realtime; cl.oldServerTime = cl.snap.serverTime; clc.timeDemoBaseTime = cl.snap.serverTime; } cl.oldFrameServerTime = cl.snap.serverTime; if (cl.newSnapshots) { CL_AdjustTimeDelta(); } di.lastServerTime = cl.snap.serverTime; if (di.firstServerTime == 0) { di.firstServerTime = cl.snap.serverTime; Com_FuncPrinf("firstServerTime %d\n", di.firstServerTime); } di.snapsInDemo++; } Com_FuncDPrinf("Snaps in demo: %i\n", di.snapsInDemo); Com_FuncDPrinf("last serverTime %d total %f minutes\n", cl.snap.serverTime, (cl.snap.serverTime - di.firstServerTime) / 1000.0 / 60.0); Com_FuncDPrinf("parse time %f seconds\n", (float)(Sys_Milliseconds() - tstart) / 1000.0); FS_Seek(clc.demofile, 0, FS_SEEK_SET); clc.demoplaying = qfalse; demofile = clc.demofile; CL_ClearState(); Com_Memset(&clc, 0, sizeof(clc)); clc.demofile = demofile; cls.state = CA_DISCONNECTED; cl_connectedToPureServer = qfalse; dpi.firstTime = di.firstServerTime; dpi.lastTime = di.lastServerTime; }
qboolean CL_PeekSnapshot(int snapshotNumber, snapshot_t *snapshot) { clSnapshot_t *clSnap; clSnapshot_t csn; int i, count; int origPosition; int cmd; //char *s; char buffer[16]; qboolean success = qfalse; int r; msg_t buf; byte bufData[MAX_MSGLEN]; int j; int lastPacketTimeOrig; int parseEntitiesNumOrig; int currentSnapNum; //int serverMessageSequence; clSnap = &csn; if (!clc.demoplaying) { return qfalse; } if (snapshotNumber <= cl.snap.messageNum) { success = CL_GetSnapshot(snapshotNumber, snapshot); if (!success) { Com_FuncDPrinf("snapshot number outside of backup buffer\n"); return qfalse; } return qtrue; } if (snapshotNumber > cl.snap.messageNum + 1) { Com_FuncDPrinf("FIXME CL_PeekSnapshot %d > cl.snap.messageNum + 1 (%d)\n", snapshotNumber, cl.snap.messageNum); //return qfalse; } parseEntitiesNumOrig = cl.parseEntitiesNum; lastPacketTimeOrig = clc.lastPacketTime; // CL_ReadDemoMessage() origPosition = FS_FTell(clc.demofile); currentSnapNum = cl.snap.messageNum; for (j = 0; j < snapshotNumber - currentSnapNum; j++) { // get the sequence number memset(buffer, 0, sizeof(buffer)); r = FS_Read(&buffer, 4, clc.demofile); if (r != 4) { Com_FuncDPrinf("couldn't read sequence number\n"); FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; return qfalse; } //serverMessageSequence = LittleLong(*((int *)buffer)); // init the message memset(&buf, 0, sizeof(msg_t)); MSG_Init(&buf, bufData, sizeof(bufData)); // get the length r = FS_Read(&buf.cursize, 4, clc.demofile); if (r != 4) { Com_FuncDPrinf("couldn't get length\n"); FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; return qfalse; } buf.cursize = LittleLong(buf.cursize); if (buf.cursize == -1) { Com_FuncDPrinf("buf.cursize == -1\n"); FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; return qfalse; } if (buf.cursize > buf.maxsize) { Com_FuncDrop("demoMsglen > MAX_MSGLEN"); } r = FS_Read(buf.data, buf.cursize, clc.demofile); if (r != buf.cursize) { Com_FuncDPrinf("Demo file was truncated.\n"); FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; return qfalse; } clc.lastPacketTime = cls.realtime; buf.readcount = 0; MSG_Bitstream(&buf); // get the reliable sequence acknowledge number MSG_ReadLong(&buf); // parse the message while (qtrue) { if (buf.readcount > buf.cursize) { Com_FuncDrop("read past end of server message"); break; } cmd = MSG_ReadByte(&buf); if (cmd == svc_EOF) { break; } success = qfalse; switch (cmd) { default: Com_FuncDrop("Illegible server message"); break; case svc_nop: break; case svc_serverCommand: MSG_ReadLong(&buf); // seq //s = MSG_ReadString(&buf); MSG_ReadString(&buf); break; case svc_gamestate: Com_FuncDPrinf("FIXME gamestate\n"); goto alldone; break; case svc_snapshot: // TODO: changed this check if it works CL_ParseSnapshot(&buf); if (cl.snap.valid) { success = qtrue; } break; case svc_download: Com_FuncDPrinf("FIXME download\n"); goto alldone; break; } } alldone: if (!success) { Com_FuncDPrinf("failed\n"); FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; return success; } // FIXME other ents not supported yet // if the entities in the frame have fallen out of their // circular buffer, we can't return it if (cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES) { Com_FuncDPrinf("cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES"); FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; return qtrue; // FIXME if you fix other ents } // write the snapshot snapshot->snapFlags = clSnap->snapFlags; snapshot->serverCommandSequence = clSnap->serverCommandNum; snapshot->ping = clSnap->ping; snapshot->serverTime = clSnap->serverTime; Com_Memcpy(snapshot->areamask, clSnap->areamask, sizeof(snapshot->areamask)); snapshot->ps = clSnap->ps; count = clSnap->numEntities; if (count > MAX_ENTITIES_IN_SNAPSHOT) { Com_FuncDPrinf("truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT); count = MAX_ENTITIES_IN_SNAPSHOT; } snapshot->numEntities = count; for (i = 0; i < count; i++) { snapshot->entities[i] = cl.parseEntities[(clSnap->parseEntitiesNum + i) & (MAX_PARSE_ENTITIES - 1)]; } } FS_Seek(clc.demofile, origPosition, FS_SEEK_SET); clc.lastPacketTime = lastPacketTimeOrig; cl.parseEntitiesNum = parseEntitiesNumOrig; // TODO: configstring changes and server commands!!! return qtrue; }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage(msg_t *msg) { int cmd; if (cl_shownet->integer == 1) { Com_Printf("%i ", msg->cursize); } else if (cl_shownet->integer >= 2) { Com_Printf("------------------\n"); } MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong(msg); if (clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS) { clc.reliableAcknowledge = clc.reliableSequence; } // parse the message while (1) { if (msg->readcount > msg->cursize) { Com_Error(ERR_DROP, "CL_ParseServerMessage: read past end of server message"); break; } cmd = MSG_ReadByte(msg); if (cmd == svc_EOF) { SHOWNET(msg, "END OF MESSAGE"); break; } if (cl_shownet->integer >= 2) { if (cmd < 0 || cmd > svc_EOF) // MSG_ReadByte might return -1 and we can't access our svc_strings array ... { Com_Printf("%3i:BAD BYTE %i\n", msg->readcount - 1, cmd); // -> ERR_DROP } else { if (!svc_strings[cmd]) { Com_Printf("%3i:BAD CMD %i\n", msg->readcount - 1, cmd); } else { SHOWNET(msg, svc_strings[cmd]); } } } // other commands switch (cmd) { default: Com_Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message %d", cmd); break; case svc_nop: break; case svc_serverCommand: CL_ParseCommandString(msg); break; case svc_gamestate: CL_ParseGamestate(msg); break; case svc_snapshot: CL_ParseSnapshot(msg); break; case svc_download: CL_ParseDownload(msg); break; } } CL_ParseBinaryMessage(msg); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate(msg_t *msg) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; // Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong(msg); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while (1) { cmd = MSG_ReadByte(msg); if (cmd == svc_EOF) { break; } if (cmd == svc_configstring) { int len; i = MSG_ReadShort(msg); if (i < 0 || i >= MAX_CONFIGSTRINGS) { Com_Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS"); } s = MSG_ReadBigString(msg); len = strlen(s); if (len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS) { Com_Error(ERR_DROP, "MAX_GAMESTATE_CHARS exceeded"); } // append it to the gameState string buffer cl.gameState.stringOffsets[i] = cl.gameState.dataCount; memcpy(cl.gameState.stringData + cl.gameState.dataCount, s, len + 1); cl.gameState.dataCount += len + 1; } else if (cmd == svc_baseline) { newnum = MSG_ReadBits(msg, GENTITYNUM_BITS); if (newnum < 0 || newnum >= MAX_GENTITIES) { Com_Error(ERR_DROP, "Baseline number out of range: %i", newnum); } memset(&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[newnum]; MSG_ReadDeltaEntity(msg, &nullstate, es, newnum); } else { Com_Error(ERR_DROP, "CL_ParseGamestate: bad command byte"); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong(msg); // parse serverId and other cvars CL_SystemInfoChanged(); // Verify if we have all official pakfiles. As we won't // be downloading them, we should be kicked for not having them. if (cl_connectedToPureServer && !FS_VerifyOfficialPaks()) { Com_Error(ERR_DROP, "Couldn't load an official pak file; verify your installation and make sure it has been updated to the latest version."); } // reinitialize the filesystem if the game directory has changed FS_ConditionalRestart(clc.checksumFeed); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame Com_InitDownloads(); // make sure the game starts Cvar_Set("cl_paused", "0"); }
static void demoFrameUnpack( msg_t *msg, demoFrame_t *oldFrame, demoFrame_t *newFrame ) { int last; qboolean isDelta = MSG_ReadBits( msg, 1 ) ? qfalse : qtrue; if (!isDelta) oldFrame = 0; newFrame->serverTime = MSG_ReadLong( msg ); /* Read config strings */ newFrame->string.data[0] = 0; newFrame->string.used = 1; last = 0; /* Extract config strings */ while ( 1 ) { int i, num = MSG_ReadShort( msg ); if (!isDelta ) { for (i = last;i<num;i++) newFrame->string.offsets[i] = 0; } else { for (i = last;i<num;i++) demoFrameAddString( &newFrame->string, i, oldFrame->string.data + oldFrame->string.offsets[i] ); } if (num < MAX_CONFIGSTRINGS) { demoFrameAddString( &newFrame->string, num, MSG_ReadBigString( msg ) ); } else { break; } last = num + 1; } /* Extract player states */ Com_Memset( newFrame->clientData, 0, sizeof( newFrame->clientData )); last = MSG_ReadByte( msg ); while (last < MAX_CLIENTS) { playerState_t *oldPlayer, *newPlayer; newFrame->clientData[last] = 1; oldPlayer = isDelta && oldFrame->clientData[last] ? &oldFrame->clients[last] : &demoNullPlayerState; newPlayer = &newFrame->clients[last]; MSG_ReadDeltaPlayerstate( msg, oldPlayer, newPlayer ); last = MSG_ReadByte( msg ); } /* Extract entity states */ last = 0; while ( 1 ) { int i, num = MSG_ReadBits( msg, GENTITYNUM_BITS ); entityState_t *oldEntity, *newEntity; if ( isDelta ) { for (i = last;i<num;i++) if (oldFrame->entities[i].number == i) newFrame->entities[i] = oldFrame->entities[i]; else newFrame->entities[i].number = MAX_GENTITIES - 1; } else { for (i = last;i<num;i++) newFrame->entities[i].number = MAX_GENTITIES - 1; } if (num < MAX_GENTITIES - 1) { if (isDelta) { oldEntity = &oldFrame->entities[num]; if (oldEntity->number != num) oldEntity = &demoNullEntityState; } else { oldEntity = &demoNullEntityState; } newEntity = &newFrame->entities[i]; MSG_ReadDeltaEntity( msg, oldEntity, newEntity, num ); } else break; last = num + 1; } /* Read the area mask */ newFrame->areaUsed = MSG_ReadByte( msg ); MSG_ReadData( msg, newFrame->areamask, newFrame->areaUsed ); /* Read the command string data */ newFrame->commandUsed = MSG_ReadLong( msg ); MSG_ReadData( msg, newFrame->commandData, newFrame->commandUsed ); }
static void Test2_Poll(void) { struct qsockaddr clientaddr; int control; int len; char name[256]; char value[256]; net_landriverlevel = test2Driver; name[0] = 0; len = dfunc.Read(test2Socket, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) { goto Reschedule; } net_message.cursize = len; MSG_BeginReading(); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { goto Error; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { goto Error; } if ((control & NETFLAG_LENGTH_MASK) != len) { goto Error; } if (MSG_ReadByte() != CCREP_RULE_INFO) { goto Error; } Q_strcpy(name, MSG_ReadString()); if (name[0] == 0) { goto Done; } Q_strcpy(value, MSG_ReadString()); Con_Printf("%-16.16s %-16.16s\n", name, value); SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, name); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(test2Socket, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); Reschedule: SchedulePollProcedure(&test2PollProcedure, 0.05); return; Error: Con_Printf("Unexpected repsonse to Rule Info request\n"); Done: dfunc.CloseSocket(test2Socket); test2InProgress = false; return; }
/* ================= CL_ParseTEnt ================= */ void CL_ParseTEnt(void) { int type; vec3_t pos; dlight_t *dl; int rnd; explosion_t *ex; int cnt; type = MSG_ReadByte(); switch (type) { case TE_WIZSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 20, 30); S_StartSound(-1, 0, cl_sfx_wizhit, pos, 1, 1); break; case TE_KNIGHTSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 226, 20); S_StartSound(-1, 0, cl_sfx_knighthit, pos, 1, 1); break; case TE_SPIKE: // spike hitting wall pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 0, 10); if (rand() % 5) S_StartSound(-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound(-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound(-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound(-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_SUPERSPIKE: // super spike hitting wall pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 0, 20); if (rand() % 5) S_StartSound(-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound(-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound(-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound(-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_EXPLOSION: // rocket explosion // particles pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_ParticleExplosion(pos); // light dl = CL_AllocDlight(0); VectorCopy(pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; dl->color = dl_colors[DLIGHT_FLASH]; // sound S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1); // sprite ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->start = cl.time; ex->model = Mod_ForName("progs/s_explod.spr", true); break; case TE_TAREXPLOSION: // tarbaby explosion pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_BlobExplosion(pos); S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_LIGHTNING1: // lightning bolts CL_ParseBeam(Mod_ForName("progs/bolt.mdl", true)); break; case TE_LIGHTNING2: // lightning bolts CL_ParseBeam(Mod_ForName("progs/bolt2.mdl", true)); break; case TE_LIGHTNING3: // lightning bolts CL_ParseBeam(Mod_ForName("progs/bolt3.mdl", true)); break; case TE_LAVASPLASH: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_LavaSplash(pos); break; case TE_TELEPORT: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_TeleportSplash(pos); break; case TE_GUNSHOT: // bullet hitting wall cnt = MSG_ReadByte(); pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 0, 20 * cnt); break; case TE_BLOOD: // bullets hitting body cnt = MSG_ReadByte(); pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 73, 20 * cnt); break; case TE_LIGHTNINGBLOOD: // lightning hitting body pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); R_RunParticleEffect(pos, vec3_origin, 225, 50); break; default: Sys_Error("%s: bad type", __func__); } }
static qsocket_t *_Datagram_CheckNewConnections(void) { struct qsockaddr clientaddr; struct qsockaddr newaddr; int newsock; int acceptsock; qsocket_t *sock; qsocket_t *s; int len; int command; int control; int ret; acceptsock = dfunc.CheckNewConnections(); if (acceptsock == -1) { return NULL; } SZ_Clear(&net_message); len = dfunc.Read(acceptsock, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) { return NULL; } net_message.cursize = len; MSG_BeginReading(); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { return NULL; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { return NULL; } if ((control & NETFLAG_LENGTH_MASK) != len) { return NULL; } command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) { return NULL; } SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_SERVER_INFO); dfunc.GetSocketAddr(acceptsock, &newaddr); MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); MSG_WriteString(&net_message, hostname.string); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; int clientNumber; client_t *client; playerNumber = MSG_ReadByte(); activeNumber = -1; for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; if (activeNumber == playerNumber) { break; } } } if (clientNumber == svs.maxclients) { return NULL; } SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); MSG_WriteByte(&net_message, playerNumber); MSG_WriteString(&net_message, client->name); MSG_WriteLong(&net_message, client->colors); MSG_WriteLong(&net_message, (int)client->edict->v.frags); MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_RULE_INFO) { char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); if (*prevCvarName) { var = Cvar_FindVar(prevCvarName); if (!var) { return NULL; } var = var->next; } else { var = cvar_vars; } // search for the next server cvar while (var) { if (var->server) { break; } var = var->next; } // send the response SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_RULE_INFO); if (var) { MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command != CCREQ_CONNECT) { return NULL; } if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) { return NULL; } if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Incompatible version.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } #ifdef BAN_TEST // check for a ban if (clientaddr.sa_family == AF_INET) { unsigned long testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if ((testAddr & banMask) == banAddr) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "You have been banned.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } } #endif // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { if (s->driver != net_driverlevel) { continue; } ret = dfunc.AddrCompare(&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? if (ret == 0 && net_time - s->connecttime < 2.0) { // yes, so send a duplicate reply SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(s->socket, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in NET_Close(s); return NULL; } } // allocate a QSocket sock = NET_NewQSocket(); if (sock == NULL) { // no room; try to let him know SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Server is full.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // allocate a network socket newsock = dfunc.OpenSocket(0); if (newsock == -1) { NET_FreeQSocket(sock); return NULL; } // connect to the client if (dfunc.Connect(newsock, &clientaddr) == -1) { dfunc.CloseSocket(newsock); NET_FreeQSocket(sock); return NULL; } // everything is allocated, just fill in the details sock->socket = newsock; sock->landriver = net_landriverlevel; sock->addr = clientaddr; Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr)); // send him back the info about the server connection he has been allocated SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(newsock, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); // MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return sock; }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage(msg_t *msg) { int cmd; if (cl_shownet->integer == 1) { Com_Printf("%i ", msg->cursize); } else if (cl_shownet->integer >= 2) { Com_Printf("------------------\n"); } MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong(msg); // if (clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS) { clc.reliableAcknowledge = clc.reliableSequence; } // // parse the message // while (1) { if (msg->readcount > msg->cursize) { Com_Error(ERR_DROP, "CL_ParseServerMessage: read past end of server message"); break; } cmd = MSG_ReadByte(msg); // See if this is an extension command after the EOF, which means we // got data that a legacy client should ignore. if ((cmd == svc_EOF) && (MSG_LookaheadByte(msg) == svc_extension)) { SHOWNET(msg, "EXTENSION"); MSG_ReadByte(msg); // throw the svc_extension byte away. cmd = MSG_ReadByte(msg); // something legacy clients can't do! // sometimes you get a svc_extension at end of stream...dangling // bits in the huffman decoder giving a bogus value? if (cmd == -1) { cmd = svc_EOF; } } if (cmd == svc_EOF) { SHOWNET(msg, "END OF MESSAGE"); break; } if (cl_shownet->integer >= 2) { if ((cmd < 0) || (!svc_strings[cmd])) { Com_Printf("%3i:BAD CMD %i\n", msg->readcount - 1, cmd); } else { SHOWNET(msg, svc_strings[cmd]); } } // other commands switch (cmd) { default: Com_Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_serverCommand: CL_ParseCommandString(msg); break; case svc_gamestate: CL_ParseGamestate(msg); break; case svc_snapshot: CL_ParseSnapshot(msg); break; case svc_download: CL_ParseDownload(msg); break; case svc_voip: #ifdef USE_VOIP CL_ParseVoip(msg); #endif break; } } }
/* ================ CL_ParseSnapshot If the snapshot is parsed properly, it will be copied to cl.snap and saved in cl.snapshots[]. If the snapshot is invalid for any reason, no changes to the state will be made at all. ================ */ void CL_ParseSnapshot( msg_t *msg ) { int len; clSnapshot_t *old; clSnapshot_t newSnap; int deltaNum; int oldMessageNum; int i, packetNum; // get the reliable sequence acknowledge number // NOTE: now sent with all server to client messages //clc.reliableAcknowledge = MSG_ReadLong( msg ); // read in the new snapshot to a temporary buffer // we will only copy to cl.snap if it is valid Com_Memset( &newSnap, 0, sizeof( newSnap ) ); // we will have read any new server commands in this // message before we got to svc_snapshot newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = MSG_ReadLong( msg ); // if we were just unpaused, we can only *now* really let the // change come into effect or the client hangs. cl_paused->modified = 0; newSnap.messageNum = clc.serverMessageSequence; deltaNum = MSG_ReadByte( msg ); if ( !deltaNum ) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = MSG_ReadByte( msg ); // If the frame is delta compressed from data that we // no longer have available, we must suck up the rest of // the frame, but not use it, then ask for a non-compressed // message if ( newSnap.deltaNum <= 0 ) { newSnap.valid = true; // uncompressed frame old = nullptr; if ( clc.demorecording ) { clc.demowaiting = false; // we can start recording now // if(cl_autorecord->integer) { // Cvar_Set( "g_synchronousClients", "0" ); // } } else { if ( cl_autorecord->integer /*&& Cvar_VariableValue( "g_synchronousClients") */ ) { char name[ 256 ]; char mapname[ MAX_QPATH ]; char *period; qtime_t time; Com_RealTime( &time ); Q_strncpyz( mapname, cl.mapname, MAX_QPATH ); for ( period = mapname; *period; period++ ) { if ( *period == '.' ) { *period = '\0'; break; } } for ( period = mapname; *period; period++ ) { if ( *period == '/' ) { break; } } if ( *period ) { period++; } Com_sprintf( name, sizeof( name ), "demos/%s_%04i-%02i-%02i_%02i%02i%02i.dm_%d", period, 1900 + time.tm_year, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, PROTOCOL_VERSION ); CL_Record( name ); } } } else { old = &cl.snapshots[ newSnap.deltaNum & PACKET_MASK ]; if ( !old->valid ) { // should never happen Com_Printf( "Delta from invalid frame (not supposed to happen!).\n" ); } else if ( old->messageNum != newSnap.deltaNum ) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_DPrintf( "Delta frame too old.\n" ); } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES - 128 ) { Com_DPrintf( "Delta parseEntitiesNum too old.\n" ); } else { newSnap.valid = true; // valid delta parse } } // read areamask len = MSG_ReadByte( msg ); if ( len > (int) sizeof( newSnap.areamask ) ) { Com_Error( ERR_DROP, "CL_ParseSnapshot: Invalid size %d for areamask.", len ); } MSG_ReadData( msg, &newSnap.areamask, len ); // read playerinfo SHOWNET( msg, "playerstate" ); if ( old ) { MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); } else { MSG_ReadDeltaPlayerstate( msg, nullptr, &newSnap.ps ); } // read packet entities SHOWNET( msg, "packet entities" ); CL_ParsePacketEntities( msg, old, &newSnap ); // if not valid, dump the entire thing now that it has // been properly read if ( !newSnap.valid ) { return; } // clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer oldMessageNum = cl.snap.messageNum + 1; if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) { oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 ); } for ( ; oldMessageNum < newSnap.messageNum; oldMessageNum++ ) { cl.snapshots[ oldMessageNum & PACKET_MASK ].valid = false; } // copy to the current good spot cl.snap = newSnap; cl.snap.ping = 999; // calculate ping time for ( i = 0; i < PACKET_BACKUP; i++ ) { packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; break; } } // save the frame off in the backup array for later delta comparisons cl.snapshots[ cl.snap.messageNum & PACKET_MASK ] = cl.snap; if ( cl_shownet->integer == 3 ) { Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum, cl.snap.deltaNum, cl.snap.ping ); } cl.newSnapshots = true; }
/* =================== SV_ExecuteClientMessage Parse a client packet =================== */ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { int c; int serverId; MSG_Bitstream(msg); serverId = MSG_ReadLong( msg ); cl->messageAcknowledge = MSG_ReadLong( msg ); if (cl->messageAcknowledge < 0) { // usually only hackers create messages like this // it is more annoying for them to let them hanging //SV_DropClient( cl, "illegible client message" ); return; } cl->reliableAcknowledge = MSG_ReadLong( msg ); // NOTE: when the client message is fux0red the acknowledgement numbers // can be out of range, this could cause the server to send thousands of server // commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient if (cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS) { // usually only hackers create messages like this // it is more annoying for them to let them hanging //SV_DropClient( cl, "illegible client message" ); cl->reliableAcknowledge = cl->reliableSequence; return; } // if this is a usercmd from a previous gamestate, // ignore it or retransmit the current gamestate // // if the client was downloading, let it stay at whatever serverId and // gamestate it was at. This allows it to keep downloading even when // the gamestate changes. After the download is finished, we'll // notice and send it a new game state if ( serverId != sv.serverId && !*cl->downloadName ) { if ( serverId == sv.restartedServerId ) { // they just haven't caught the map_restart yet return; } // if we can tell that the client has dropped the last // gamestate we sent them, resend it if ( cl->messageAcknowledge > cl->gamestateMessageNum ) { Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name ); SV_SendClientGameState( cl ); } return; } // this client has acknowledged the new gamestate so it's // safe to start sending it the real time again if( cl->oldServerTime && serverId == sv.serverId ){ Com_DPrintf( "%s acknowledged gamestate\n", cl->name ); cl->oldServerTime = 0; } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); if ( c == clc_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg ) ) { return; // we couldn't execute it because of the flood protection } if (cl->state == CS_ZOMBIE) { return; // disconnect command } } while ( 1 ); // read the usercmd_t if ( c == clc_move ) { SV_UserMove( cl, msg, qtrue ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, qfalse ); } else if ( c != clc_EOF ) { Com_Printf( "WARNING: bad command byte for client %i\n", (int)(cl - svs.clients) ); } // if ( msg->readcount != msg->cursize ) { // Com_Printf( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients ); // } }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } const char* str = MSG_ReadBigString( msg ); std::string s = str; cl.gameState[i] = str; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } memset( &nullstate, 0, sizeof( nullstate ) ); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong( msg ); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // parse serverId and other cvars CL_SystemInfoChanged(); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
void CPostFunc::GTH_ProcessMessage_PostSystem_OpenWindow() { int PostIdx = 0; int PostCount = -1; enum tagGCPacket_POSTSYSTEM_OPEN::enumCode code = tagGCPacket_POSTSYSTEM_OPEN::enumCode::fail; code = static_cast<enum tagGCPacket_POSTSYSTEM_OPEN::enumCode>(MSG_ReadByte()); switch( code ) { case tagGCPacket_POSTSYSTEM_OPEN::enumCode::success: { PostCount = MSG_ReadLong(); g_cgv.myCharacterInfo->m_PostMng.InitPostSystem(); for ( PostIdx = 0; PostIdx < PostCount; PostIdx++) { CPostManager::PostPackage_t PostPackage; memset(&PostPackage, 0, sizeof(CPostManager::PostPackage_t) ); PostPackage.iPostIdx = MSG_ReadLong(); sstrncpy(PostPackage.szFromName, MSG_ReadString(), NAMESTRING); PostPackage.szFromName[NAMESTRING] = NULL; CTools::Replace_doubleQUOTATIONmark_by_singleQUOTATIONmark(PostPackage.szFromName); sstrncpy(PostPackage.szPostTitle, MSG_ReadString(), CPostManager::POST_TITLESIZE); PostPackage.szPostTitle[CPostManager::POST_TITLESIZE] = NULL; CTools::Replace_doubleQUOTATIONmark_by_singleQUOTATIONmark(PostPackage.szPostTitle); sstrncpy(PostPackage.szSendPostTime , MSG_ReadString(), CPostManager::POST_SENDTIME_INFO_STR_LEN); PostPackage.szSendPostTime[CPostManager::POST_SENDTIME_INFO_STR_LEN] = NULL; sstrncpy(PostPackage.szMailStr, MSG_ReadString(), CPostManager::POST_STRSIZE); PostPackage.szMailStr[CPostManager::POST_STRSIZE] = NULL; CTools::Replace_doubleQUOTATIONmark_by_singleQUOTATIONmark(PostPackage.szMailStr); int state = MSG_ReadLong(); if ( state == 0) PostPackage.MailState = CPostManager::enumPostPackageState::POSTPACKAGE_UNCHECK; else PostPackage.MailState = CPostManager::enumPostPackageState::POSTPACKAGE_CHECK; int nak = MSG_ReadLong(); PostPackage.Nak = nak; int SendType = MSG_ReadLong(); PostPackage.iRemainDays = MSG_ReadLong(); if ( SendType == 0) PostPackage.PostSendType = CPostManager::enumPostSendType::POST_SENDTYPE_WEB; else PostPackage.PostSendType = CPostManager::enumPostSendType::POST_SENDTYPE_CHAR; g_cgv.myCharacterInfo->m_PostMng.AddPostPackage(PostPackage); } g_ifMng->m_PostWin->InitIFPost(); g_ifMng->m_PostWin->SetDisplayMode(true); g_ifMng->m_chatWin->Enable( false ); g_ifMng->m_PostWin->Enable(1); g_ifMng->SetFocus( g_ifMng->m_PostWin ); } break; case tagGCPacket_POSTSYSTEM_OPEN::enumCode::fail: g_ifMng->SetMessage( g_LPACK.GetMassage(LPACK_TYPE_NORMAL, 322), g_LPACK.GetMassage(LPACK_TYPE_NORMAL2, 226) ); break; default: break; } }
/* ================== 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 ] ); } }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; char oldGame[MAX_QPATH]; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // save old gamedir Cvar_VariableStringBuffer("fs_game", oldGame, sizeof(oldGame)); // parse useful values out of CS_SERVERINFO CL_ParseServerInfo(); // parse serverId and other cvars CL_SystemInfoChanged(); // stop recording now so the demo won't have an unnecessary level load at the end. if(cl_autoRecordDemo->integer && clc.demorecording) CL_StopRecord_f(); // reinitialize the filesystem if the game directory has changed if(!cl_oldGameSet && (Cvar_Flags("fs_game") & CVAR_MODIFIED)) { cl_oldGameSet = qtrue; Q_strncpyz(cl_oldGame, oldGame, sizeof(cl_oldGame)); } FS_ConditionalRestart(clc.checksumFeed, qfalse); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
/* =================== SV_ExecuteClientMessage Parse a client packet =================== */ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { int c; int serverId; MSG_Bitstream( msg ); serverId = MSG_ReadLong( msg ); cl->messageAcknowledge = MSG_ReadLong( msg ); if ( cl->messageAcknowledge < 0 ) { // usually only hackers create messages like this // it is more annoying for them to let them hanging #ifndef NDEBUG SV_DropClient( cl, "DEBUG: illegible client message" ); #endif return; } cl->reliableAcknowledge = MSG_ReadLong( msg ); // NOTE: when the client message is fux0red the acknowledgement numbers // can be out of range, this could cause the server to send thousands of server // commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient if ( cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS ) { // usually only hackers create messages like this // it is more annoying for them to let them hanging #ifndef NDEBUG SV_DropClient( cl, "DEBUG: illegible client message" ); #endif cl->reliableAcknowledge = cl->reliableSequence; return; } // if this is a usercmd from a previous gamestate, // ignore it or retransmit the current gamestate // // if the client was downloading, let it stay at whatever serverId and // gamestate it was at. This allows it to keep downloading even when // the gamestate changes. After the download is finished, we'll // notice and send it a new game state // // show_bug.cgi?id=536 // don't drop as long as previous command was a nextdl, after a dl is done, downloadName is set back to "" // but we still need to read the next message to move to next download or send gamestate // I don't like this hack though, it must have been working fine at some point, suspecting the fix is somewhere else if ( serverId != sv.serverId && !*cl->downloadName && !strstr( cl->lastClientCommandString, "nextdl" ) ) { if ( serverId >= sv.restartedServerId && serverId < sv.serverId ) { // TTimo - use a comparison here to catch multiple map_restart // they just haven't caught the map_restart yet Log::Debug( "%s^7: ignoring pre map_restart / outdated client message", cl->name ); return; } // if we can tell that the client has dropped the last // gamestate we sent them, resend it if ( cl->messageAcknowledge > cl->gamestateMessageNum ) { Log::Debug( "%s^7: dropped gamestate, resending", cl->name ); SV_SendClientGameState( cl ); } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); if ( c == clc_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg, true ) ) { return; // we couldn't execute it because of the flood protection } if ( cl->state == clientState_t::CS_ZOMBIE ) { return; // disconnect command } } while ( 1 ); return; } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); if ( c == clc_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg, false ) ) { return; // we couldn't execute it because of the flood protection } if ( cl->state == clientState_t::CS_ZOMBIE ) { return; // disconnect command } } while ( 1 ); // read the usercmd_t if ( c == clc_move ) { SV_UserMove( cl, msg, true ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, false ); } else if ( c != clc_EOF ) { Log::Warn( "bad command byte for client %i\n", ( int )( cl - svs.clients ) ); } SV_ParseBinaryMessage( cl, msg ); // if ( msg->readcount != msg->cursize ) { // Log::Notice( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients ); // } }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); #ifdef _DONETPROFILE_ int startBytes,endBytes; startBytes=msg->readcount; #endif // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); #ifdef _DONETPROFILE_ endBytes=msg->readcount; // ClReadProf().AddField("svc_gamestate",endBytes-startBytes); #endif // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed if( FS_ConditionalRestart( clc.checksumFeed ) ) { // don't set to true because we yet have to start downloading // enabling this can cause double loading of a map when connecting to // a server which has a different game directory set //clc.downloadRestart = qtrue; } // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
void demoConvert( const char *oldName, const char *newBaseName, qboolean smoothen ) { fileHandle_t oldHandle = 0; fileHandle_t newHandle = 0; int temp; int oldSize; int msgSequence; msg_t oldMsg; byte oldData[ MAX_MSGLEN ]; int oldTime, nextTime, fullTime; int clientNum; demoFrame_t *workFrame; int parseEntitiesNum = 0; demoConvert_t *convert; char bigConfigString[BIG_INFO_STRING]; int bigConfigNumber; const char *s; clSnapshot_t *oldSnap = 0; clSnapshot_t *newSnap; int levelCount = 0; char newName[MAX_OSPATH]; oldSize = FS_FOpenFileRead( oldName, &oldHandle, qtrue ); if (!oldHandle) { Com_Printf("Failed to open %s for conversion.", oldName); return; } /* Alloc some memory */ convert = Z_Malloc( sizeof( demoConvert_t) ); /* Initialize the first workframe's strings */ while (oldSize > 0) { MSG_Init( &oldMsg, oldData, sizeof( oldData ) ); /* Read the sequence number */ if (FS_Read( &convert->messageNum, 4, oldHandle) != 4) goto conversionerror; convert->messageNum = LittleLong( convert->messageNum ); oldSize -= 4; /* Read the message size */ if (FS_Read( &oldMsg.cursize,4, oldHandle) != 4) goto conversionerror; oldSize -= 4; oldMsg.cursize = LittleLong( oldMsg.cursize ); /* Negative size signals end of demo */ if (oldMsg.cursize < 0) break; if ( oldMsg.cursize > oldMsg.maxsize ) goto conversionerror; /* Read the actual message */ if (FS_Read( oldMsg.data, oldMsg.cursize, oldHandle ) != oldMsg.cursize) goto conversionerror; oldSize -= oldMsg.cursize; // init the bitstream MSG_BeginReading( &oldMsg ); // Skip the reliable sequence acknowledge number MSG_ReadLong( &oldMsg ); // // parse the message // while ( 1 ) { byte cmd; if ( oldMsg.readcount > oldMsg.cursize ) { Com_Printf ("Demo conversion, read past end of server message.\n"); goto conversionerror; } cmd = MSG_ReadByte( &oldMsg ); if ( cmd == svc_EOF) { break; } workFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES ]; // other commands switch ( cmd ) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_serverCommand: temp = MSG_ReadLong( &oldMsg ); s = MSG_ReadString( &oldMsg ); if (temp<=msgSequence) break; // Com_Printf( " server command %s\n", s ); msgSequence = temp; Cmd_TokenizeString( s ); if ( !Q_stricmp( Cmd_Argv(0), "bcs0" ) ) { bigConfigNumber = atoi( Cmd_Argv(1) ); Q_strncpyz( bigConfigString, Cmd_Argv(2), sizeof( bigConfigString )); break; } if ( !Q_stricmp( Cmd_Argv(0), "bcs1" ) ) { Q_strcat( bigConfigString, sizeof( bigConfigString ), Cmd_Argv(2)); break; } if ( !Q_stricmp( Cmd_Argv(0), "bcs2" ) ) { Q_strcat( bigConfigString, sizeof( bigConfigString ), Cmd_Argv(2)); demoFrameAddString( &workFrame->string, bigConfigNumber, bigConfigString ); break; } if ( !Q_stricmp( Cmd_Argv(0), "cs" ) ) { int num = atoi( Cmd_Argv(1) ); s = Cmd_ArgsFrom( 2 ); demoFrameAddString( &workFrame->string, num, Cmd_ArgsFrom( 2 ) ); break; } if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { int len = strlen( s ) + 1; char *dst; if (workFrame->commandUsed + len + 1 > sizeof( workFrame->commandData)) { Com_Printf("Overflowed state command data.\n"); goto conversionerror; } dst = workFrame->commandData + workFrame->commandUsed; *dst = clientNum; Com_Memcpy( dst+1, s, len ); workFrame->commandUsed += len + 1; } break; case svc_gamestate: if (newHandle) { FS_FCloseFile( newHandle ); newHandle = 0; } if (levelCount) { Com_sprintf( newName, sizeof( newName ), "%s.%d.mme", newBaseName, levelCount ); } else { Com_sprintf( newName, sizeof( newName ), "%s.mme", newBaseName ); } fullTime = -1; clientNum = -1; oldTime = -1; Com_Memset( convert, 0, sizeof( *convert )); convert->frames[0].string.used = 1; levelCount++; newHandle = FS_FOpenFileWrite( newName ); if (!newHandle) { Com_Printf("Failed to open %s for target conversion target.\n", newName); goto conversionerror; return; } else { FS_Write ( demoHeader, strlen( demoHeader ), newHandle ); } Com_sprintf( newName, sizeof( newName ), "%s.txt", newBaseName ); workFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES ]; msgSequence = MSG_ReadLong( &oldMsg ); while( 1 ) { cmd = MSG_ReadByte( &oldMsg ); if (cmd == svc_EOF) break; if ( cmd == svc_configstring) { int num; const char *s; num = MSG_ReadShort( &oldMsg ); s = MSG_ReadBigString( &oldMsg ); demoFrameAddString( &workFrame->string, num, s ); } else if ( cmd == svc_baseline ) { int num = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); if ( num < 0 || num >= MAX_GENTITIES ) { Com_Printf( "Baseline number out of range: %i.\n", num ); goto conversionerror; } MSG_ReadDeltaEntity( &oldMsg, &demoNullEntityState, &convert->entityBaselines[num], num ); } else { Com_Printf( "Unknown block while converting demo gamestate.\n" ); goto conversionerror; } } clientNum = MSG_ReadLong( &oldMsg ); /* Skip the checksum feed */ MSG_ReadLong( &oldMsg ); break; case svc_snapshot: nextTime = MSG_ReadLong( &oldMsg ); /* Delta number, not needed */ newSnap = &convert->snapshots[convert->messageNum & PACKET_MASK]; Com_Memset (newSnap, 0, sizeof(*newSnap)); newSnap->deltaNum = MSG_ReadByte( &oldMsg ); newSnap->messageNum = convert->messageNum; if (!newSnap->deltaNum) { newSnap->deltaNum = -1; newSnap->valid = qtrue; // uncompressed frame oldSnap = NULL; } else { newSnap->deltaNum = newSnap->messageNum - newSnap->deltaNum; oldSnap = &convert->snapshots[newSnap->deltaNum & PACKET_MASK]; if (!oldSnap->valid) { Com_Printf( "Delta snapshot without base.\n" ); goto conversionerror; } else if (oldSnap->messageNum != newSnap->deltaNum) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_Printf ("Delta frame too old.\n"); } else if ( parseEntitiesNum - oldSnap->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) { Com_Printf ("Delta parseEntitiesNum too old.\n"); } else { newSnap->valid = qtrue; // valid delta parse } } /* Snapflags, not needed */ newSnap->snapFlags = MSG_ReadByte( &oldMsg ); // read areamask workFrame->areaUsed = MSG_ReadByte( &oldMsg ); MSG_ReadData( &oldMsg, workFrame->areamask, workFrame->areaUsed ); if (clientNum <0 || clientNum >= MAX_CLIENTS) { Com_Printf("Got snapshot with invalid client.\n"); goto conversionerror; } MSG_ReadDeltaPlayerstate( &oldMsg, oldSnap ? &oldSnap->ps : &demoNullPlayerState, &newSnap->ps ); /* Read the individual entities */ newSnap->parseEntitiesNum = parseEntitiesNum; newSnap->numEntities = 0; Com_Memset( workFrame->entityData, 0, sizeof( workFrame->entityData )); /* The beast that is entity parsing */ { int newnum; entityState_t *oldstate, *newstate; int oldindex = 0; int oldnum; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); while ( 1 ) { // read the entity index number if (oldSnap && oldindex < oldSnap->numEntities) { oldstate = &convert->parseEntities[(oldSnap->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } else { oldstate = 0; oldnum = 99999; } newstate = &convert->parseEntities[parseEntitiesNum]; if ( !oldstate && (newnum == (MAX_GENTITIES-1))) { break; } else if ( oldnum < newnum ) { *newstate = *oldstate; oldindex++; } else if (oldnum == newnum) { oldindex++; MSG_ReadDeltaEntity( &oldMsg, oldstate, newstate, newnum ); if ( newstate->number != MAX_GENTITIES-1) workFrame->entityData[ newstate->number ] = 1; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); } else if (oldnum > newnum) { MSG_ReadDeltaEntity( &oldMsg, &convert->entityBaselines[newnum], newstate , newnum ); if ( newstate->number != MAX_GENTITIES-1) workFrame->entityData[ newstate->number ] = 1; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); } if (newstate->number == MAX_GENTITIES-1) continue; parseEntitiesNum++; parseEntitiesNum &= (MAX_PARSE_ENTITIES-1); newSnap->numEntities++; }} /* Stop processing this further since it's an invalid snap due to lack of delta data */ if (!newSnap->valid) break; /* Skipped snapshots will be set invalid in the circular buffer */ if ( newSnap->messageNum - convert->lastMessageNum >= PACKET_BACKUP ) { convert->lastMessageNum = newSnap->messageNum - ( PACKET_BACKUP - 1 ); } for ( ; convert->lastMessageNum < newSnap->messageNum ; convert->lastMessageNum++ ) { convert->snapshots[convert->lastMessageNum & PACKET_MASK].valid = qfalse; } convert->lastMessageNum = newSnap->messageNum + 1; /* compress the frame into the new format */ if (nextTime > oldTime) { demoFrame_t *cleanFrame; int writeIndex; for (temp = 0;temp<newSnap->numEntities;temp++) { int p = (newSnap->parseEntitiesNum+temp) & (MAX_PARSE_ENTITIES-1); entityState_t *newState = &convert->parseEntities[p]; workFrame->entities[newState->number] = *newState; } workFrame->clientData[clientNum] = 1; workFrame->clients[clientNum] = newSnap->ps; workFrame->serverTime = nextTime; /* Which frame from the cache to save */ writeIndex = convert->frameIndex - (DEMOCONVERTFRAMES/2); if (writeIndex >= 0) { const demoFrame_t *newFrame; msg_t writeMsg; // init the message MSG_Init( &writeMsg, demoBuffer, sizeof (demoBuffer)); MSG_Clear( &writeMsg ); MSG_Bitstream( &writeMsg ); newFrame = &convert->frames[ writeIndex % DEMOCONVERTFRAMES]; if ( smoothen ) demoFrameInterpolate( convert->frames, DEMOCONVERTFRAMES, writeIndex ); if ( nextTime > fullTime || writeIndex <= 0 ) { /* Plan the next time for a full write */ fullTime = nextTime + 2000; demoFramePack( &writeMsg, newFrame, 0 ); } else { const demoFrame_t *oldFrame = &convert->frames[ ( writeIndex -1 ) % DEMOCONVERTFRAMES]; demoFramePack( &writeMsg, newFrame, oldFrame ); } /* Write away the new data in the msg queue */ temp = LittleLong( writeMsg.cursize ); FS_Write (&temp, 4, newHandle ); FS_Write ( writeMsg.data , writeMsg.cursize, newHandle ); } /* Clean up the upcoming frame for all new changes */ convert->frameIndex++; cleanFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES]; cleanFrame->serverTime = 0; for (temp = 0;temp<MAX_GENTITIES;temp++) cleanFrame->entities[temp].number = MAX_GENTITIES-1; Com_Memset( cleanFrame->clientData, 0, sizeof ( cleanFrame->clientData )); Com_Memcpy( cleanFrame->string.data, workFrame->string.data, workFrame->string.used ); Com_Memcpy( cleanFrame->string.offsets, workFrame->string.offsets, sizeof( workFrame->string.offsets )); cleanFrame->string.used = workFrame->string.used; cleanFrame->commandUsed = 0; /* keep track of this last frame's time */ oldTime = nextTime; } break; case svc_download: // read block number temp = MSG_ReadShort ( &oldMsg ); if (!temp) //0 block, read file size MSG_ReadLong( &oldMsg ); // read block size temp = MSG_ReadShort ( &oldMsg ); // read the data block for ( ;temp>0;temp--) MSG_ReadByte( &oldMsg ); break; } } } conversionerror: FS_FCloseFile( oldHandle ); FS_FCloseFile( newHandle ); Z_Free( convert ); return; }
/* ================ CL_ParseSnapshot If the snapshot is parsed properly, it will be copied to cl.snap and saved in cl.snapshots[]. If the snapshot is invalid for any reason, no changes to the state will be made at all. ================ */ void CL_ParseSnapshot( msg_t *msg ) { int len; clSnapshot_t *old; clSnapshot_t newSnap; int deltaNum; int oldMessageNum; int i, packetNum; // get the reliable sequence acknowledge number // NOTE: now sent with all server to client messages //clc.reliableAcknowledge = MSG_ReadLong( msg ); // read in the new snapshot to a temporary buffer // we will only copy to cl.snap if it is valid Com_Memset (&newSnap, 0, sizeof(newSnap)); // we will have read any new server commands in this // message before we got to svc_snapshot newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = MSG_ReadLong( msg ); // if we were just unpaused, we can only *now* really let the // change come into effect or the client hangs. cl_paused->modified = qfalse; newSnap.messageNum = clc.serverMessageSequence; deltaNum = MSG_ReadByte( msg ); if ( !deltaNum ) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = MSG_ReadByte( msg ); // If the frame is delta compressed from data that we // no longer have available, we must suck up the rest of // the frame, but not use it, then ask for a non-compressed // message if ( newSnap.deltaNum <= 0 ) { newSnap.valid = qtrue; // uncompressed frame old = NULL; clc.demowaiting = qfalse; // we can start recording now } else { old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK]; if ( !old->valid ) { // should never happen Com_Printf ("Delta from invalid frame (not supposed to happen!).\n"); } else if ( old->messageNum != newSnap.deltaNum ) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_Printf ("Delta frame too old.\n"); } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) { Com_DPrintf ("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte( msg ); if((unsigned)len > sizeof(newSnap.areamask)) { Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len); return; } MSG_ReadData( msg, &newSnap.areamask, len); // read playerinfo SHOWNET( msg, "playerstate" ); if ( old ) { MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); if (newSnap.ps.m_iVehicleNum) { //this means we must have written our vehicle's ps too MSG_ReadDeltaPlayerstate( msg, &old->vps, &newSnap.vps, qtrue ); } } else { MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); if (newSnap.ps.m_iVehicleNum) { //this means we must have written our vehicle's ps too MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.vps, qtrue ); } } // read packet entities SHOWNET( msg, "packet entities" ); CL_ParsePacketEntities( msg, old, &newSnap ); // if not valid, dump the entire thing now that it has // been properly read if ( !newSnap.valid ) { return; } // clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer oldMessageNum = cl.snap.messageNum + 1; if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) { oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 ); } for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) { cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; } // copy to the current good spot cl.snap = newSnap; cl.snap.ping = 999; // calculate ping time for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; break; } } // save the frame off in the backup array for later delta comparisons cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap; if (cl_shownet->integer == 3) { Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum, cl.snap.deltaNum, cl.snap.ping ); } cl.newSnapshots = qtrue; }
/* ============== CL_ParseMuzzleFlash ============== */ void CL_ParseMuzzleFlash(void){ vec3_t fv, rv; cdlight_t *dl; int i, weapon; centity_t *pl; int silenced; float volume; char soundname[64]; i = MSG_ReadShort(&net_message); if(i < 1 || i >= MAX_EDICTS) Com_Error(ERR_DROP, "CL_ParseMuzzleFlash: bad entity"); weapon = MSG_ReadByte(&net_message); silenced = weapon & MZ_SILENCED; weapon &= ~MZ_SILENCED; pl = &cl_entities[i]; dl = CL_AllocDlight(i); VectorCopy(pl->current.origin, dl->origin); AngleVectors(pl->current.angles, fv, rv, NULL); VectorMA(dl->origin, 18, fv, dl->origin); VectorMA(dl->origin, 16, rv, dl->origin); if(silenced) dl->radius = 100 +(rand() & 31); else dl->radius = 200 +(rand() & 31); dl->minlight = 32; dl->die = cl.time; // + 0.1; if(silenced) volume = 0.2; else volume = 1; switch(weapon){ case MZ_BLASTER: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_BLUEHYPERBLASTER: dl->color[0] = 0; dl->color[1] = 0; dl->color[2] = 1; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_HYPERBLASTER: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_MACHINEGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0); break; case MZ_SHOTGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0); S_StartSound(NULL, i, CHAN_AUTO, S_RegisterSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1); break; case MZ_SSHOTGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0); break; case MZ_CHAINGUN1: dl->radius = 200 +(rand() & 31); dl->color[0] = 1; dl->color[1] = 0.25; dl->color[2] = 0; Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0); break; case MZ_CHAINGUN2: dl->radius = 225 +(rand() & 31); dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0; dl->die = cl.time + 0.1; // long delay Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0); Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0.05); break; case MZ_CHAINGUN3: dl->radius = 250 +(rand() & 31); dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 0.1; // long delay Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0); Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0.033); Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav",(rand() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0.066); break; case MZ_RAILGUN: dl->color[0] = 0.5; dl->color[1] = 0.5; dl->color[2] = 1.0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_ROCKET: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.2; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0); S_StartSound(NULL, i, CHAN_AUTO, S_RegisterSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1); break; case MZ_GRENADE: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0); S_StartSound(NULL, i, CHAN_AUTO, S_RegisterSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1); break; case MZ_BFG: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0); break; case MZ_LOGIN: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 1.0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); CL_LogoutEffect(pl->current.origin, weapon); break; case MZ_LOGOUT: dl->color[0] = 1; dl->color[1] = 0; dl->color[2] = 0; dl->die = cl.time + 1.0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); CL_LogoutEffect(pl->current.origin, weapon); break; case MZ_RESPAWN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 1.0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); CL_LogoutEffect(pl->current.origin, weapon); break; // RAFAEL case MZ_PHALANX: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.5; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0); break; // RAFAEL case MZ_IONRIPPER: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.5; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0); break; // ====================== // PGM case MZ_ETF_RIFLE: dl->color[0] = 0.9; dl->color[1] = 0.7; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/nail1.wav"), volume, ATTN_NORM, 0); break; case MZ_SHOTGUN2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/shotg2.wav"), volume, ATTN_NORM, 0); break; case MZ_HEATBEAM: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 100; // S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/bfg__l1a.wav"), volume, ATTN_NORM, 0); break; case MZ_BLASTER2: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; // FIXME - different sound for blaster2 ?? S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_TRACKER: // negative flashes handled the same in gl/soft until CL_AddDLights dl->color[0] = -1; dl->color[1] = -1; dl->color[2] = -1; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), volume, ATTN_NORM, 0); break; case MZ_NUKE1: dl->color[0] = 1; dl->color[1] = 0; dl->color[2] = 0; dl->die = cl.time + 100; break; case MZ_NUKE2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 100; break; case MZ_NUKE4: dl->color[0] = 0; dl->color[1] = 0; dl->color[2] = 1; dl->die = cl.time + 100; break; case MZ_NUKE8: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 1; dl->die = cl.time + 100; break; // PGM // ====================== } }