/* sv_parse.c quakeworld protocol parsing Copyright (C) 2005 Bill Currie Author: Bill Currie Date: 2005/05/06 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include "QF/cmd.h" #include "QF/console.h" #include "QF/dstring.h" #include "QF/hash.h" #include "QF/idparse.h" #include "QF/info.h" #include "QF/msg.h" #include "QF/qendian.h" #include "QF/sys.h" #include "QF/va.h" #include "qw/bothdefs.h" #include "qw/msg_ucmd.h" #include "qw/protocol.h" #include "client.h" #include "connection.h" #include "qtv.h" #include "server.h" static void sv_serverdata (server_t *sv, qmsg_t *msg) { const char *str; int i; for (i = 0; i < MAX_SOUNDS; i++) { if (sv->soundlist[i]) { free (sv->soundlist[i]); sv->soundlist[i] = 0; } } for (i = 0; i < MAX_MODELS; i++) { if (sv->modellist[i]) { free (sv->modellist[i]); sv->modellist[i] = 0; } } sv->ver = MSG_ReadLong (msg); sv->spawncount = MSG_ReadLong (msg); sv->gamedir = strdup (MSG_ReadString (msg)); sv->message = strdup (MSG_ReadString (msg)); sv->movevars.gravity = MSG_ReadFloat (msg); sv->movevars.stopspeed = MSG_ReadFloat (msg); sv->movevars.maxspeed = MSG_ReadFloat (msg); sv->movevars.spectatormaxspeed = MSG_ReadFloat (msg); sv->movevars.accelerate = MSG_ReadFloat (msg); sv->movevars.airaccelerate = MSG_ReadFloat (msg); sv->movevars.wateraccelerate = MSG_ReadFloat (msg); sv->movevars.friction = MSG_ReadFloat (msg); sv->movevars.waterfriction = MSG_ReadFloat (msg); sv->movevars.entgravity = MSG_ReadFloat (msg); sv->cdtrack = MSG_ReadByte (msg); sv->sounds = MSG_ReadByte (msg); COM_TokenizeString (MSG_ReadString (msg), qtv_args); cmd_args = qtv_args; Info_Destroy (sv->info); sv->info = Info_ParseString (Cmd_Argv (1), MAX_SERVERINFO_STRING, 0); str = Info_ValueForKey (sv->info, "hostname"); if (strcmp (str, "unnamed")) qtv_printf ("%s: %s\n", sv->name, str); str = Info_ValueForKey (sv->info, "*version"); qtv_printf ("%s: QW %s\n", sv->name, str); str = Info_ValueForKey (sv->info, "*qf_version"); if (str[0]) qtv_printf ("%s: QuakeForge %s\n", sv->name, str); qtv_printf ("%s: gamedir: %s\n", sv->name, sv->gamedir); str = Info_ValueForKey (sv->info, "map"); qtv_printf ("%s: (%s) %s\n", sv->name, str, sv->message); MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, va ("soundlist %i %i", sv->spawncount, 0)); sv->next_run = realtime; } static void sv_soundlist (server_t *sv, qmsg_t *msg) { int numsounds = MSG_ReadByte (msg); int n; const char *str; for (;;) { str = MSG_ReadString (msg); if (!str[0]) break; //qtv_printf ("%s\n", str); numsounds++; if (numsounds == MAX_SOUNDS) { while (str[0]) str = MSG_ReadString (msg); MSG_ReadByte (msg); return; } sv->soundlist[numsounds - 1] = strdup (str); } n = MSG_ReadByte (msg); if (n) { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, va ("soundlist %d %d", sv->spawncount, n)); } else { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, va ("modellist %d %d", sv->spawncount, 0)); } sv->next_run = realtime; } static void sv_modellist (server_t *sv, qmsg_t *msg) { int nummodels = MSG_ReadByte (msg); int n; const char *str; for (;;) { str = MSG_ReadString (msg); if (!str[0]) break; //qtv_printf ("%s\n", str); n = nummodels++; if (nummodels == MAX_MODELS) { while (str[0]) str = MSG_ReadString (msg); MSG_ReadByte (msg); return; } sv->modellist[n] = strdup (str); if (!strcmp (sv->modellist[n], "progs/player.mdl")) sv->playermodel = n; } n = MSG_ReadByte (msg); if (n) { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, va ("modellist %d %d", sv->spawncount, n)); } else { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, va ("prespawn %d 0 0", sv->spawncount)); sv->signon = 1; } sv->next_run = realtime; } static void sv_cmd_f (server_t *sv) { if (Cmd_Argc () > 1) { if (!strcmp (Cmd_Argv(1), "spawn")) sv->signon = 0; MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); SZ_Print (&sv->netchan.message, Cmd_Args (1)); } sv->next_run = realtime; } static void sv_skins_f (server_t *sv) { int i; // we don't actually bother checking skins here, but this is a good way // to get everything ready at the last miniute before we start getting // actual in-game update messages MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, va ("begin %d", sv->spawncount)); sv->next_run = realtime; sv->connected = 2; sv->delta = -1; for (i = 0; i < UPDATE_BACKUP; i++) { sv->frames[i].entities.entities = sv->entity_states[i]; sv->frames[i].players.players = sv->player_states[i]; } Server_BroadcastCommand (sv, "reconnect\n"); } static void sv_changing_f (server_t *sv) { client_t *cl; sv->connected = 1; sv->num_signon_buffers = 0; qtv_printf ("Changing map...\n"); MSG_WriteByte (&sv->netchan.message, qtv_nop); sv->next_run = realtime; Server_BroadcastCommand (sv, "changing\n"); for (cl = sv->clients; cl; cl = cl->next) { cl->connected = 0; Client_SendMessages (cl); } } static void sv_reconnect_f (server_t *sv) { sizebuf_t *msg = &sv->netchan.message; qtv_printf ("Reconnecting...\n"); MSG_WriteByte (msg, qtv_stringcmd); MSG_WriteString (msg, "new"); sv->next_run = realtime; } typedef struct { const char *name; void (*func) (server_t *sv); } svcmd_t; svcmd_t svcmds[] = { {"cmd", sv_cmd_f}, {"skins", sv_skins_f}, {"changing", sv_changing_f}, {"reconnect", sv_reconnect_f}, {0, 0}, }; void sv_stringcmd (server_t *sv, qmsg_t *msg) { svcmd_t *c; const char *name; COM_TokenizeString (MSG_ReadString (msg), qtv_args); cmd_args = qtv_args; name = Cmd_Argv (0); for (c = svcmds; c->name; c++) if (strcmp (c->name, name) == 0) break; if (!c->name) { qtv_printf ("Bad QTV command: %s\n", name); return; } c->func (sv); } static void sv_parse_delta (qmsg_t *msg, int bits, entity_state_t *ent) { ent->number = bits & 511; bits &= ~511; if (bits & U_MOREBITS) bits |= MSG_ReadByte (msg); if (bits & U_EXTEND1) { bits |= MSG_ReadByte (msg) << 16; if (bits & U_EXTEND2) bits |= MSG_ReadByte (msg) << 24; } if (bits & U_MODEL) ent->modelindex = MSG_ReadByte (msg); if (bits & U_FRAME) ent->frame = (ent->frame & 0xff00) | MSG_ReadByte (msg); if (bits & U_COLORMAP) ent->colormap = MSG_ReadByte (msg); if (bits & U_SKIN) ent->skinnum = MSG_ReadByte (msg); if (bits & U_EFFECTS) ent->effects = (ent->effects & 0xff00) | MSG_ReadByte (msg); if (bits & U_ORIGIN1) ent->origin[0] = MSG_ReadCoord (msg); if (bits & U_ANGLE1) ent->angles[0] = MSG_ReadAngle (msg); if (bits & U_ORIGIN2) ent->origin[1] = MSG_ReadCoord (msg); if (bits & U_ANGLE2) ent->angles[1] = MSG_ReadAngle (msg); if (bits & U_ORIGIN3) ent->origin[2] = MSG_ReadCoord (msg); if (bits & U_ANGLE3) ent->angles[2] = MSG_ReadAngle (msg); if (bits & U_SOLID) { // FIXME } if (!(bits & U_EXTEND1)) return; if (bits & U_ALPHA) ent->alpha = MSG_ReadByte (msg); if (bits & U_SCALE) ent->scale = MSG_ReadByte (msg); if (bits & U_EFFECTS2) ent->effects = (ent->effects & 0x00ff) | (MSG_ReadByte (msg) << 8); if (bits & U_GLOWSIZE) ent->glow_size = MSG_ReadByte (msg); if (bits & U_GLOWCOLOR) ent->glow_color = MSG_ReadByte (msg); if (bits & U_COLORMOD) ent->colormod = MSG_ReadByte (msg); if (!(bits & U_EXTEND1)) return; if (bits & U_FRAME2) ent->frame = (ent->frame & 0x00ff) | (MSG_ReadByte (msg) << 8); } static void flush_entity_packet (server_t *sv, qmsg_t *msg) { entity_state_t dummy; int word; memset (&dummy, 0, sizeof (dummy)); sv->frames[sv->netchan.incoming_sequence & UPDATE_MASK].invalid = true; while (1) { word = MSG_ReadShort (msg); if (msg->badread) { qtv_printf ("msg_badread in packetentities\n"); return; } if (!word) break; sv_parse_delta (msg, word, &dummy); } } static void sv_packetentities (server_t *sv, qmsg_t *msg, int delta) { unsigned short word; int newnum, oldnum, from, num; int newindex, oldindex; int newpacket, oldpacket; int full; packet_entities_t *oldp, *newp, dummy; if (sv->connected < 2) { flush_entity_packet (sv, msg); return; } newpacket = sv->netchan.incoming_sequence & UPDATE_MASK; newp = &sv->frames[newpacket].entities; sv->frames[newpacket].invalid = false; if (delta) { from = MSG_ReadByte (msg); oldpacket = sv->frames[newpacket].delta_sequence; if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) qtv_printf ("WARNING: from mismatch\n"); } else { oldpacket = -1; } full = 0; if (oldpacket != -1) { if (sv->netchan.outgoing_sequence - oldpacket > UPDATE_BACKUP) { flush_entity_packet (sv, msg); return; } oldp = &sv->frames[oldpacket & UPDATE_MASK].entities; sv->validsequence = sv->netchan.incoming_sequence; } else { oldp = &dummy; dummy.num_entities = 0; sv->validsequence = sv->netchan.incoming_sequence; full = 1; memset (sv->ent_valid, 0, sizeof (sv->ent_valid)); } //qtv_printf ("newp = %-5d oldp = %d\n", newpacket, oldpacket & UPDATE_MASK); sv->delta = sv->netchan.incoming_sequence; newindex = oldindex = 0; newp->num_entities = 0; while (1) { word = MSG_ReadShort (msg); if (msg->badread) { // something didn't parse right... qtv_printf ("msg_badread in packetentities\n"); return; } //qtv_printf ("word = %04x new = %-3d old = %-3d\n", word, newindex, oldindex); if (!word) { // copy rest of ents from old packet while (oldindex < oldp->num_entities) { if (newindex >= MAX_DEMO_PACKET_ENTITIES) { qtv_printf ("A too many packet entities\n"); Sys_Quit (); flush_entity_packet (sv, msg); return; } newp->entities[newindex] = oldp->entities[oldindex]; num = newp->entities[newindex].number; sv->entities[num] = newp->entities[newindex]; sv->ent_valid[num] = 1; newindex++; oldindex++; } break; } newnum = word & 511; oldnum = 9999; if (oldindex < oldp->num_entities) oldnum = oldp->entities[oldindex].number; //qtv_printf (" %-3d %-4d %3d\n", newnum, oldnum, oldp->num_entities); while (newnum > oldnum) { if (full) { qtv_printf ("WARNING: oldcopy on full update\n"); flush_entity_packet (sv, msg); return; } if (newindex >= MAX_DEMO_PACKET_ENTITIES) { qtv_printf ("B too many packet entities\n"); Sys_Quit (); flush_entity_packet (sv, msg); return; } newp->entities[newindex] = oldp->entities[oldindex]; num = newp->entities[newindex].number; sv->entities[num] = newp->entities[newindex]; sv->ent_valid[num] = 1; newindex++; oldindex++; oldnum = 9999; if (oldindex < oldp->num_entities) oldnum = oldp->entities[oldindex].number; //qtv_printf (" %-3d %-4d %3d\n", newnum, oldnum, oldp->num_entities); } if (newnum < oldnum) { if (word & U_REMOVE) { if (full) { qtv_printf ("WARNING: U_REMOVE on full update\n"); flush_entity_packet (sv, msg); return; } continue; } if (newindex >= MAX_DEMO_PACKET_ENTITIES) { qtv_printf ("C too many packet entities\n"); Sys_Quit (); flush_entity_packet (sv, msg); return; } newp->entities[newindex] = sv->baselines[newnum]; sv_parse_delta (msg, word, &newp->entities[newindex]); sv->entities[newnum] = newp->entities[newindex]; newindex++; continue; } if (newnum == oldnum) { if (full) { sv->validsequence = 0; qtv_printf ("WARNING: delta on full update\n"); } if (word & U_REMOVE) { sv->ent_valid[newnum] = 0; oldindex++; continue; } newp->entities[newindex] = oldp->entities[oldindex]; sv_parse_delta (msg, word, &newp->entities[newindex]); sv->entities[newnum] = newp->entities[newindex]; sv->ent_valid[newnum] = 1; newindex++; oldindex++; } } newp->num_entities = newindex; } static void parse_player_delta (qmsg_t *msg, plent_state_t *from, plent_state_t *to) { int i; int flags; flags = to->flags = MSG_ReadShort (msg); MSG_ReadCoordV (msg, to->origin); to->frame = (to->frame & 0xff00) | MSG_ReadByte (msg); if (flags & PF_MSEC) to->msec = MSG_ReadByte (msg); // qtv_printf ("%02x\n", msg->message->data[msg->readcount]); if (flags & PF_COMMAND) MSG_ReadDeltaUsercmd (msg, &from->cmd, &to->cmd); for (i = 0; i < 3; i++) { if (flags & (PF_VELOCITY1 << i)) to->velocity[i] = MSG_ReadShort (msg); } if (flags & PF_MODEL) to->modelindex = MSG_ReadByte (msg); if (flags & PF_SKINNUM) to->skinnum = MSG_ReadByte (msg); if (flags & PF_EFFECTS) to->effects = (to->effects & 0xff00) | MSG_ReadByte (msg);; if (flags & PF_WEAPONFRAME) to->weaponframe = MSG_ReadByte (msg); if (flags & PF_QF) { int bits; bits = MSG_ReadByte (msg); if (bits & PF_ALPHA) to->alpha = MSG_ReadByte (msg); if (bits & PF_SCALE) to->scale = MSG_ReadByte (msg); if (bits & PF_EFFECTS2) to->effects = (to->effects & 0x00ff) | (MSG_ReadByte (msg) << 8); if (bits & PF_GLOWSIZE) to->glow_size = MSG_ReadByte (msg); if (bits & PF_GLOWCOLOR) to->glow_color = MSG_ReadByte (msg); if (bits & PF_COLORMOD) to->colormod = MSG_ReadByte (msg); if (bits & PF_FRAME2) to->frame = (to->frame & 0xff) | (MSG_ReadByte (msg) << 8); } } static void sv_playerinfo (server_t *sv, qmsg_t *msg) { plent_state_t dummy; // for bad player indices plent_state_t *ent; plent_state_t *from, *to; int num; int fromind, toind; static plent_state_t null_player_state; if (!null_player_state.alpha) { null_player_state.alpha = 255; null_player_state.scale = 16; null_player_state.glow_size = 0; null_player_state.glow_color = 254; null_player_state.colormod = 255; } fromind = MSG_ReadByte (msg); toind = sv->netchan.incoming_sequence & UPDATE_MASK; num = MSG_ReadByte (msg); if (num > MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", num); ent = from = to = &dummy; } else { ent = &sv->players[num].ent; from = &null_player_state; if (fromind != 255) from = &sv->player_states[fromind & UPDATE_MASK][num]; to = &sv->player_states[toind][num]; *to = *from; } parse_player_delta (msg, from, to); // qtv_printf ("%3d %g %g %g %d\n", fromind, to->cmd.angles[0], to->cmd.angles[1], to->cmd.angles[2], to->modelindex); *ent = *to; } static void sv_serverinfo (server_t *sv, qmsg_t *msg) { dstring_t *key = dstring_newstr (); dstring_t *value = dstring_newstr (); dstring_copystr (key, MSG_ReadString (msg)); dstring_copystr (value, MSG_ReadString (msg)); Info_SetValueForKey (sv->info, key->str, value->str, 0); dstring_delete (key); dstring_delete (value); } static void sv_setinfo (server_t *sv, qmsg_t *msg) { int slot; dstring_t *key = dstring_newstr (); dstring_t *value = dstring_newstr (); player_t *pl; slot = MSG_ReadByte (msg); dstring_copystr (key, MSG_ReadString (msg)); dstring_copystr (value, MSG_ReadString (msg)); if (slot >= MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", slot); } else { pl = sv->players + slot; if (!pl->info) pl->info = Info_ParseString ("", MAX_INFO_STRING, 0); Info_SetValueForKey (pl->info, key->str, value->str, 0); } dstring_delete (key); dstring_delete (value); } static void sv_updateuserinfo (server_t *sv, qmsg_t *msg) { int slot, uid; const char *info; player_t *pl; slot = MSG_ReadByte (msg); uid = MSG_ReadLong (msg); info = MSG_ReadString (msg); if (slot >= MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", slot); return; } pl = sv->players + slot; if (pl->info) Info_Destroy (pl->info); if (info) { pl->info = Info_ParseString (info, MAX_INFO_STRING, 0); pl->uid = uid; } } static void sv_updatestat (server_t *sv, qmsg_t *msg, int islong) { int stat, val; player_t *pl; stat = MSG_ReadByte (msg); if (!islong) val = MSG_ReadByte (msg); else val = MSG_ReadLong (msg); for (pl = sv->players; pl; pl = pl->next) pl->stats[stat] = val; } static void sv_update_net (server_t *sv, qmsg_t *msg, int ping) { int slot, val; player_t *pl; slot = MSG_ReadByte (msg); if (ping) val = MSG_ReadShort (msg); else val = MSG_ReadByte (msg); if (slot >= MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", slot); return; } pl = sv->players + slot; if (ping) pl->ping = val; else pl->pl = val; } static void sv_sound (server_t *sv, qmsg_t *msg, int stop) { int c; vec3_t v; if (stop) { MSG_ReadShort (msg); } else { c = MSG_ReadShort (msg); if (c & SND_VOLUME) MSG_ReadByte (msg); if (c & SND_ATTENUATION) MSG_ReadByte (msg); MSG_ReadByte (msg); MSG_ReadCoordV (msg, v); } } static void sv_setangle (server_t *sv, qmsg_t *msg) { int slot; player_t *pl; vec3_t ang; slot = MSG_ReadByte (msg); MSG_ReadAngleV (msg, ang); if (slot >= MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", slot); return; } pl = sv->players + slot; VectorCopy (ang, pl->ent.cmd.angles); } static void sv_updatefrags (server_t *sv, qmsg_t *msg) { int slot, frags; player_t *pl; slot = MSG_ReadByte (msg); frags = MSG_ReadShort (msg); if (slot >= MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", slot); return; } pl = sv->players + slot; pl->frags = frags; } static void parse_baseline (qmsg_t *msg, entity_state_t *ent) { ent->modelindex = MSG_ReadByte (msg); ent->frame = MSG_ReadByte (msg); ent->colormap = MSG_ReadByte (msg); ent->skinnum = MSG_ReadByte (msg); MSG_ReadCoordAngleV (msg, ent->origin, ent->angles); ent->colormod = 255; ent->alpha = 255; ent->scale = 16; ent->glow_size = 254; ent->glow_color = 254; } static void sv_spawnbaseline (server_t *sv, qmsg_t *msg) { int i; i = MSG_ReadShort (msg) % MAX_SV_ENTITIES; sv->baselines[i].number = i; parse_baseline (msg, &sv->baselines[i]); } static void sv_spawnstatic (server_t *sv, qmsg_t *msg) { entity_state_t ent; parse_baseline (msg, &ent); } static void parse_beam (qmsg_t *msg) { vec3_t start, end; MSG_ReadShort (msg); MSG_ReadCoordV (msg, start); MSG_ReadCoordV (msg, end); } static void sv_temp_entity (server_t *sv, qmsg_t *msg) { vec3_t pos; int type; type = MSG_ReadByte (msg); switch (type) { case TE_WIZSPIKE: MSG_ReadCoordV (msg, pos); break; case TE_KNIGHTSPIKE: MSG_ReadCoordV (msg, pos); break; case TE_SPIKE: MSG_ReadCoordV (msg, pos); break; case TE_SUPERSPIKE: MSG_ReadCoordV (msg, pos); break; case TE_EXPLOSION: MSG_ReadCoordV (msg, pos); break; case TE_TAREXPLOSION: MSG_ReadCoordV (msg, pos); break; case TE_LIGHTNING1: case TE_LIGHTNING2: case TE_LIGHTNING3: case TE_BEAM: parse_beam (msg); break; case TE_LAVASPLASH: MSG_ReadCoordV (msg, pos); break; case TE_TELEPORT: MSG_ReadCoordV (msg, pos); break; case TE_EXPLOSION2: MSG_ReadCoordV (msg, pos); MSG_ReadByte (msg); MSG_ReadByte (msg); break; case TE_GUNSHOT: MSG_ReadByte (msg); MSG_ReadCoordV (msg, pos); break; case TE_BLOOD: MSG_ReadByte (msg); MSG_ReadCoordV (msg, pos); break; case TE_LIGHTNINGBLOOD: MSG_ReadCoordV (msg, pos); break; } } static void sv_nails (server_t *sv, qmsg_t *msg, int nails2) { int c, i; byte bits[6]; c = MSG_ReadByte (msg); for (i = 0; i < c; i++) { if (nails2) MSG_ReadByte (msg); MSG_ReadBytes (msg, bits, 6); } } static void sv_print (server_t *sv, qmsg_t *msg) { MSG_ReadByte (msg); qtv_printf ("%s", MSG_ReadString (msg)); } static void sv_lightstyle (server_t *sv, qmsg_t *msg) { int ind = MSG_ReadByte (msg); const char *style = MSG_ReadString (msg); if (ind > MAX_LIGHTSTYLES) return; if (sv->lightstyles[ind]) free (sv->lightstyles[ind]); sv->lightstyles[ind] = strdup (style); } static void sv_updateentertime (server_t *sv, qmsg_t *msg) { int slot = MSG_ReadByte (msg); int time = MSG_ReadFloat (msg); player_t *pl; if (slot >= MAX_SV_PLAYERS) { qtv_printf ("bogus player: %d\n", slot); return; } pl = sv->players + slot; pl->time = time; } void sv_parse (server_t *sv, qmsg_t *msg, int reliable) { int svc; vec3_t v; player_t *pl; while (1) { int bc_len = msg->readcount; byte *bc_data = msg->message->data + bc_len; int send = 1; svc = MSG_ReadByte (msg); if (svc == -1) break; //qtv_printf ("sv_parse: svc: %d\n", svc); switch (svc) { default: qtv_printf ("sv_parse: unknown svc: %d\n", svc); Sys_Quit (); return; case svc_nop: send = 0; break; //case svc_setview: // break; case svc_sound: sv_sound (sv, msg, 0); break; case svc_print: sv_print (sv, msg); break; case svc_setangle: sv_setangle (sv, msg); send = 0; break; case svc_updatefrags: sv_updatefrags (sv, msg); break; case svc_stopsound: sv_sound (sv, msg, 1); break; case svc_damage: //XXX MSG_ReadByte (msg); MSG_ReadByte (msg); MSG_ReadCoordV (msg, v); send = 0; break; case svc_temp_entity: sv_temp_entity (sv, msg); //XXX break; case svc_setpause: //XXX MSG_ReadByte (msg); break; case svc_centerprint: //XXX MSG_ReadString (msg); break; case svc_killedmonster: for (pl = sv->players; pl; pl = pl->next) pl->stats[STAT_MONSTERS]++; break; case svc_foundsecret: for (pl = sv->players; pl; pl = pl->next) pl->stats[STAT_SECRETS]++; break; case svc_intermission: //XXX MSG_ReadCoordV (msg, v); MSG_ReadAngleV (msg, v); break; case svc_finale: //XXX MSG_ReadString (msg); break; case svc_cdtrack: //XXX MSG_ReadByte (msg); break; case svc_sellscreen: //ignore break; case svc_smallkick: //XXX break; case svc_bigkick: //XXX break; case svc_updateentertime: sv_updateentertime (sv, msg); break; case svc_updatestat: case svc_updatestatlong: sv_updatestat (sv, msg, svc == svc_updatestatlong); break; case svc_muzzleflash: //XXX MSG_ReadShort (msg); break; case svc_updateuserinfo: sv_updateuserinfo (sv, msg); break; case svc_playerinfo: sv_playerinfo (sv, msg); send = 0; break; case svc_nails: case svc_nails2: sv_nails (sv, msg, svc == svc_nails2); send = 0; break; case svc_packetentities: sv_packetentities (sv, msg, 0); send = 0; break; case svc_deltapacketentities: sv_packetentities (sv, msg, 1); send = 0; break; case svc_maxspeed: //XXX MSG_ReadFloat (msg); break; case svc_entgravity: //XXX MSG_ReadFloat (msg); break; case svc_setinfo: sv_setinfo (sv, msg); break; case svc_serverinfo: sv_serverinfo (sv, msg); break; case svc_updatepl: sv_update_net (sv, msg, 0); break; case svc_updateping: sv_update_net (sv, msg, 1); break; case svc_chokecount: //XXX MSG_ReadByte (msg); send = 0; break; case svc_serverdata: sv_serverdata (sv, msg); send = 0; break; case svc_stufftext: sv_stringcmd (sv, msg); send = 0; break; case svc_soundlist: sv_soundlist (sv, msg); send = 0; break; case svc_modellist: sv_modellist (sv, msg); send = 0; break; case svc_spawnstaticsound: //XXX MSG_ReadCoordV (msg, v); MSG_ReadByte (msg); MSG_ReadByte (msg); MSG_ReadByte (msg); send = 0; break; case svc_spawnbaseline: sv_spawnbaseline (sv, msg); send = 0; break; case svc_spawnstatic: sv_spawnstatic (sv, msg); send = 0; break; case svc_lightstyle: sv_lightstyle (sv, msg); break; } if (send) { bc_len = msg->readcount - bc_len; Server_Broadcast (sv, reliable, 0, bc_data, bc_len); } } }