/* Copyright (C) 1997-2001 Id Software, Inc. 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 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "shared/shared.h" #include "common/msg.h" #include "common/protocol.h" #include "common/sizebuf.h" #include "common/math.h" /* ============================================================================== MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors ============================================================================== */ sizebuf_t msg_write; byte msg_write_buffer[MAX_MSGLEN]; sizebuf_t msg_read; byte msg_read_buffer[MAX_MSGLEN]; const entity_packed_t nullEntityState; const player_packed_t nullPlayerState; const usercmd_t nullUserCmd; /* ============= MSG_Init Initialize default buffers, clearing allow overflow/underflow flags. This is the only place where writing buffer is initialized. Writing buffer is never allowed to overflow. Reading buffer is reinitialized in many other places. Reinitializing will set the allow underflow flag as appropriate. ============= */ void MSG_Init(void) { SZ_TagInit(&msg_read, msg_read_buffer, MAX_MSGLEN, SZ_MSG_READ); SZ_TagInit(&msg_write, msg_write_buffer, MAX_MSGLEN, SZ_MSG_WRITE); } /* ============================================================================== WRITING ============================================================================== */ /* ============= MSG_BeginWriting ============= */ void MSG_BeginWriting(void) { msg_write.cursize = 0; msg_write.bitpos = 0; msg_write.overflowed = qfalse; } /* ============= MSG_WriteChar ============= */ void MSG_WriteChar(int c) { byte *buf; #ifdef PARANOID if (c < -128 || c > 127) Com_Error(ERR_FATAL, "MSG_WriteChar: range error"); #endif buf = SZ_GetSpace(&msg_write, 1); buf[0] = c; } /* ============= MSG_WriteByte ============= */ void MSG_WriteByte(int c) { byte *buf; #ifdef PARANOID if (c < 0 || c > 255) Com_Error(ERR_FATAL, "MSG_WriteByte: range error"); #endif buf = SZ_GetSpace(&msg_write, 1); buf[0] = c; } /* ============= MSG_WriteShort ============= */ void MSG_WriteShort(int c) { byte *buf; #ifdef PARANOID if (c < ((short)0x8000) || c > (short)0x7fff) Com_Error(ERR_FATAL, "MSG_WriteShort: range error"); #endif buf = SZ_GetSpace(&msg_write, 2); buf[0] = c & 0xff; buf[1] = c >> 8; } /* ============= MSG_WriteLong ============= */ void MSG_WriteLong(int c) { byte *buf; buf = SZ_GetSpace(&msg_write, 4); buf[0] = c & 0xff; buf[1] = (c >> 8) & 0xff; buf[2] = (c >> 16) & 0xff; buf[3] = c >> 24; } /* ============= MSG_WriteString ============= */ void MSG_WriteString(const char *string) { size_t length; if (!string) { MSG_WriteByte(0); return; } length = strlen(string); if (length >= MAX_NET_STRING) { Com_WPrintf("%s: overflow: %"PRIz" chars", __func__, length); MSG_WriteByte(0); return; } MSG_WriteData(string, length + 1); } /* ============= MSG_WriteCoord ============= */ #define COORD2SHORT(x) ((int)((x)*8.0f)) #define SHORT2COORD(x) ((x)*(1.0f/8)) static inline void MSG_WriteCoord(float f) { MSG_WriteShort(COORD2SHORT(f)); } /* ============= MSG_WritePos ============= */ void MSG_WritePos(const vec3_t pos) { MSG_WriteCoord(pos[0]); MSG_WriteCoord(pos[1]); MSG_WriteCoord(pos[2]); } /* ============= MSG_WriteAngle ============= */ #define ANGLE2BYTE(x) ((int)((x)*256.0f/360)&255) #define BYTE2ANGLE(x) ((x)*(360.0f/256)) #define U8_ANGLE (360.0/256) #define S16_ANGLE (360.0/65536) #define S16_COORD (1.0f/8) void MSG_WriteAngle(float f) { MSG_WriteByte(ANGLE2BYTE(f)); } #define MSG_WriteU8(_c) MSG_WriteByte(_c) #define MSG_WriteU16(_c) MSG_WriteShort(_c) #define MSG_WriteS8(_c) MSG_WriteChar(_c) #define MSG_WriteS16(_c) MSG_WriteShort(_c) #define MSG_WriteS32(_c) MSG_WriteLong(_c) #define MSG_ReadU8() MSG_ReadByte() #define MSG_ReadU16() MSG_ReadWord() #define MSG_ReadS8() MSG_ReadChar() #define MSG_ReadS16() MSG_ReadShort() #define MSG_ReadS32() MSG_ReadLong() #define var_field_len(_mask) \ (((bits & (_mask##8)) != 0) | (((bits & (_mask##16)) != 0) << 1)) /* input macros: */ #define read_varia(_field, _mask) \ do { \ switch (var_field_len(_mask)) { \ case 1: \ to->_field = MSG_ReadByte(); \ break; \ case 2: \ to->_field = MSG_ReadShort(); \ break; \ case 3: \ to->_field = MSG_ReadLong(); \ break; \ } \ } while (0) #define __read_field(_field, _type, _scale) \ do { \ if (_scale) \ to->_field = MSG_Read##_type() * (_scale); \ else \ to->_field = MSG_Read##_type(); \ } while (0) #define read_field(_field, _mask, _type, _scale) \ do { \ if (bits & _mask) \ __read_field(_field, _type, _scale); \ } while (0) #define read_array(_field, _mask, _type, _scale, _nr) \ do { \ if (bits & _mask) \ for (unsigned _i = 0; _i < (_nr); _i++) \ __read_field(_field[_i], _type, _scale); \ } while (0) /* output macros: */ #define write_field(_field, _mask, _type, _scale) \ do { \ if (bits & _mask) \ MSG_Write##_type(to->_field); \ } while (0) #define write_varia(_field, _mask) \ do { \ switch (var_field_len(_mask)) { \ case 1: \ MSG_WriteByte(to->_field); \ break; \ case 2: \ MSG_WriteShort(to->_field); \ break; \ case 3: \ MSG_WriteLong(to->_field); \ break; \ } \ } while (0) #define write_array(_field, _mask, _type, _scale, _nr) \ do { \ if (bits & _mask) \ for (unsigned _i = 0; _i < (_nr); _i++) \ MSG_Write##_type(to->_field[_i]); \ } while (0) /* diff macros: */ #define diff_field(_field, _mask, _type, _scale) \ do { \ if (to->_field != from->_field) \ bits |= _mask; \ } while (0) #define diff_varia(_field, _mask) \ do { \ if (to->_field != from->_field) { \ if (sizeof(to->_field) > 2 && to->_field & mask) \ bits |= _mask##8 | _mask##16; \ else if (to->_field & 0x0000ff00) \ bits |= _mask##16; \ else \ bits |= _mask##8; \ } \ } while (0) #define diff_array(_field, _mask, _type, _scale, _nr) \ do { \ for (unsigned _i = 0; _i < (_nr); _i++) \ if (to->_field[_i] != from->_field[_i]) \ bits |= _mask; \ } while (0) /* pack macros: */ #define pack_field(_field, _mask, _type, _scale) \ do { \ if (_scale) \ out->_field = in->_field * (1 / ((_scale) ?: 1)); \ else \ out->_field = in->_field; \ } while (0) #define pack_array(_field, _mask, _type, _scale, _nr) \ do { \ for (unsigned _i = 0; _i < (_nr); _i++) \ pack_field(_field[_i], _mask, _type, _scale); \ } while (0) #define PLAYERSTATE_DEFAULT() \ _field(pmove.pm_type, PS_M_TYPE, U8, 0); \ _array(pmove.origin, PS_M_ORIGIN, S16, 0, 3); \ _array(pmove.velocity, PS_M_VELOCITY, S16, 0, 3); \ _field(pmove.pm_time, PS_M_TIME, U8, 0); \ _field(pmove.pm_flags, PS_M_FLAGS, U8, 0); \ _field(pmove.gravity, PS_M_GRAVITY, S16, 0); \ _array(pmove.delta_angles, PS_M_DELTA_ANGLES, S16, 0, 3); \ _array(viewoffset, PS_VIEWOFFSET, S8, 0.25f, 3); \ _array(viewangles, PS_VIEWANGLES, S16, S16_ANGLE, 3); \ _array(kick_angles, PS_KICKANGLES, S8, 0.25f, 3); \ _field(gunindex, PS_WEAPONINDEX, U8, 0); \ _field(gunindex, PS_WEAPONFRAME, U8, 0); \ _array(gunoffset, PS_WEAPONFRAME, S8, 0.25f, 3); \ _array(gunangles, PS_WEAPONFRAME, S8, 0.25f, 3); \ _array(blend, PS_BLEND, U8, (1 / 255.0f), 4);\ _field(fov, PS_FOV, U8, 0); \ _field(rdflags, PS_RDFLAGS, U8, 0) #define PLAYERSTATE_ENHANCED() \ _field(pmove.pm_type, PS_M_TYPE, U8, 0); \ _array(pmove.origin, PS_M_ORIGIN, S16, 0, 2); \ _field(pmove.origin[2], EPS_M_ORIGIN2, S16, 0); \ _array(pmove.velocity, PS_M_VELOCITY, S16, 0, 2); \ _field(pmove.velocity[2], EPS_M_VELOCITY2, S16, 0); \ _field(pmove.pm_time, PS_M_TIME, U8, 0); \ _field(pmove.pm_flags, PS_M_FLAGS, U8, 0); \ _field(pmove.gravity, PS_M_GRAVITY, S16, 0); \ _array(pmove.delta_angles, PS_M_DELTA_ANGLES, S16, 0, 3); \ _array(viewoffset, PS_VIEWOFFSET, S8, 0.25f, 3); \ _array(viewangles, PS_VIEWANGLES, S16, S16_ANGLE, 2); \ _field(viewangles[2], EPS_VIEWANGLE2, S16, S16_ANGLE); \ _array(kick_angles, PS_KICKANGLES, S8, 0.25f, 3); \ _field(gunindex, PS_WEAPONINDEX, U8, 0); \ _field(gunframe, PS_WEAPONFRAME, U8, 0); \ _array(gunoffset, EPS_GUNOFFSET, S8, 0.25f, 3); \ _array(gunangles, EPS_GUNANGLES, S8, 0.25f, 3); \ _array(blend, PS_BLEND, U8, (1 / 255.0f), 4);\ _field(fov, PS_FOV, U8, 0); \ _field(rdflags, PS_RDFLAGS, U8, 0) #define PLAYERSTATE_PACKET() \ _field(pmove.pm_type, PPS_M_TYPE, U8, 0); \ _array(pmove.origin, PPS_M_ORIGIN, S16, 0, 2); \ _field(pmove.origin[2], PPS_M_ORIGIN2, S16, 0); \ _array(viewoffset, PPS_VIEWOFFSET, S8, 0.25f, 3); \ _array(viewangles, PPS_VIEWANGLES, S16, S16_ANGLE, 2); \ _field(viewangles[2], PPS_VIEWANGLE2, S16, S16_ANGLE); \ _array(kick_angles, PPS_KICKANGLES, S8, 0.25f, 3); \ _field(gunindex, PPS_WEAPONINDEX, U8, 0); \ _field(gunframe, PPS_WEAPONFRAME, U8, 0); \ _array(gunoffset, PPS_GUNOFFSET, S8, 0.25f, 3); \ _array(gunangles, PPS_GUNANGLES, S8, 0.25f, 3); \ _array(blend, PPS_BLEND, U8, (1 / 255.0f), 4);\ _field(fov, PPS_FOV, U8, 0); \ _field(rdflags, PPS_RDFLAGS, U8, 0); \ #if USE_CLIENT /* ============= MSG_WriteDeltaUsercmd ============= */ int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int version) { int bits, buttons = cmd->buttons & BUTTON_MASK; if (!from) { from = &nullUserCmd; } // // send the movement message // bits = 0; if (cmd->angles[0] != from->angles[0]) bits |= CM_ANGLE1; if (cmd->angles[1] != from->angles[1]) bits |= CM_ANGLE2; if (cmd->angles[2] != from->angles[2]) bits |= CM_ANGLE3; if (cmd->forwardmove != from->forwardmove) bits |= CM_FORWARD; if (cmd->sidemove != from->sidemove) bits |= CM_SIDE; if (cmd->upmove != from->upmove) bits |= CM_UP; if (cmd->buttons != from->buttons) bits |= CM_BUTTONS; if (cmd->impulse != from->impulse) bits |= CM_IMPULSE; MSG_WriteByte(bits); if (version >= PROTOCOL_VERSION_R1Q2_UCMD) { if (bits & CM_BUTTONS) { if ((bits & CM_FORWARD) && !(cmd->forwardmove % 5)) { buttons |= BUTTON_FORWARD; } if ((bits & CM_SIDE) && !(cmd->sidemove % 5)) { buttons |= BUTTON_SIDE; } if ((bits & CM_UP) && !(cmd->upmove % 5)) { buttons |= BUTTON_UP; } MSG_WriteByte(buttons); } } if (bits & CM_ANGLE1) MSG_WriteShort(cmd->angles[0]); if (bits & CM_ANGLE2) MSG_WriteShort(cmd->angles[1]); if (bits & CM_ANGLE3) MSG_WriteShort(cmd->angles[2]); if (bits & CM_FORWARD) { if (buttons & BUTTON_FORWARD) { MSG_WriteChar(cmd->forwardmove / 5); } else { MSG_WriteShort(cmd->forwardmove); } } if (bits & CM_SIDE) { if (buttons & BUTTON_SIDE) { MSG_WriteChar(cmd->sidemove / 5); } else { MSG_WriteShort(cmd->sidemove); } } if (bits & CM_UP) { if (buttons & BUTTON_UP) { MSG_WriteChar(cmd->upmove / 5); } else { MSG_WriteShort(cmd->upmove); } } if (version < PROTOCOL_VERSION_R1Q2_UCMD) { if (bits & CM_BUTTONS) MSG_WriteByte(cmd->buttons); } if (bits & CM_IMPULSE) MSG_WriteByte(cmd->impulse); MSG_WriteByte(cmd->msec); return bits; } /* ============= MSG_WriteBits ============= */ void MSG_WriteBits(int value, int bits) { int i; size_t bitpos; if (bits == 0 || bits < -31 || bits > 32) { Com_Error(ERR_FATAL, "MSG_WriteBits: bad bits: %d", bits); } if (msg_write.maxsize - msg_write.cursize < 4) { Com_Error(ERR_FATAL, "MSG_WriteBits: overflow"); } if (bits < 0) { bits = -bits; } bitpos = msg_write.bitpos; if ((bitpos & 7) == 0) { // optimized case switch (bits) { case 8: MSG_WriteByte(value); return; case 16: MSG_WriteShort(value); return; case 32: MSG_WriteLong(value); return; default: break; } } for (i = 0; i < bits; i++, bitpos++) { if ((bitpos & 7) == 0) { msg_write.data[bitpos >> 3] = 0; } msg_write.data[bitpos >> 3] |= (value & 1) << (bitpos & 7); value >>= 1; } msg_write.bitpos = bitpos; msg_write.cursize = (bitpos + 7) >> 3; } /* ============= MSG_WriteDeltaUsercmd_Enhanced ============= */ int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, const usercmd_t *cmd, int version) { int bits, delta, count; if (!from) { from = &nullUserCmd; } // // send the movement message // bits = 0; if (cmd->angles[0] != from->angles[0]) bits |= CM_ANGLE1; if (cmd->angles[1] != from->angles[1]) bits |= CM_ANGLE2; if (cmd->angles[2] != from->angles[2]) bits |= CM_ANGLE3; if (cmd->forwardmove != from->forwardmove) bits |= CM_FORWARD; if (cmd->sidemove != from->sidemove) bits |= CM_SIDE; if (cmd->upmove != from->upmove) bits |= CM_UP; if (cmd->buttons != from->buttons) bits |= CM_BUTTONS; if (cmd->msec != from->msec) bits |= CM_IMPULSE; if (!bits) { MSG_WriteBits(0, 1); return 0; } MSG_WriteBits(1, 1); MSG_WriteBits(bits, 8); if (bits & CM_ANGLE1) { delta = cmd->angles[0] - from->angles[0]; if (delta >= -128 && delta <= 127) { MSG_WriteBits(1, 1); MSG_WriteBits(delta, -8); } else { MSG_WriteBits(0, 1); MSG_WriteBits(cmd->angles[0], -16); } } if (bits & CM_ANGLE2) { delta = cmd->angles[1] - from->angles[1]; if (delta >= -128 && delta <= 127) { MSG_WriteBits(1, 1); MSG_WriteBits(delta, -8); } else { MSG_WriteBits(0, 1); MSG_WriteBits(cmd->angles[1], -16); } } if (bits & CM_ANGLE3) { MSG_WriteBits(cmd->angles[2], -16); } if (version >= PROTOCOL_VERSION_Q2PRO_UCMD) { count = -10; } else { count = -16; } if (bits & CM_FORWARD) { MSG_WriteBits(cmd->forwardmove, count); } if (bits & CM_SIDE) { MSG_WriteBits(cmd->sidemove, count); } if (bits & CM_UP) { MSG_WriteBits(cmd->upmove, count); } if (bits & CM_BUTTONS) { int buttons = (cmd->buttons & 3) | (cmd->buttons >> 5); MSG_WriteBits(buttons, 3); } if (bits & CM_IMPULSE) { MSG_WriteBits(cmd->msec, 8); } return bits; } #endif // USE_CLIENT void MSG_WriteDir(const vec3_t dir) { int best; best = DirToByte(dir); MSG_WriteByte(best); } void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, qboolean short_angles) { // allow 0 to accomodate empty baselines if (in->number < 0 || in->number >= MAX_EDICTS) Com_Error(ERR_DROP, "%s: bad number: %d", __func__, in->number); out->number = in->number; out->origin[0] = COORD2SHORT(in->origin[0]); out->origin[1] = COORD2SHORT(in->origin[1]); out->origin[2] = COORD2SHORT(in->origin[2]); if (short_angles) { out->angles[0] = ANGLE2SHORT(in->angles[0]); out->angles[1] = ANGLE2SHORT(in->angles[1]); out->angles[2] = ANGLE2SHORT(in->angles[2]); } else { // pack angles8 akin to angles16 to make delta compression happy when // precision suddenly changes between entity updates out->angles[0] = ANGLE2BYTE(in->angles[0]) << 8; out->angles[1] = ANGLE2BYTE(in->angles[1]) << 8; out->angles[2] = ANGLE2BYTE(in->angles[2]) << 8; } out->old_origin[0] = COORD2SHORT(in->old_origin[0]); out->old_origin[1] = COORD2SHORT(in->old_origin[1]); out->old_origin[2] = COORD2SHORT(in->old_origin[2]); out->modelindex = in->modelindex; out->modelindex2 = in->modelindex2; out->modelindex3 = in->modelindex3; out->modelindex4 = in->modelindex4; out->skinnum = in->skinnum; out->effects = in->effects; out->renderfx = in->renderfx; out->solid = in->solid; out->frame = in->frame; out->sound = in->sound; out->event = in->event; } void MSG_WriteDeltaEntity(const entity_packed_t *from, const entity_packed_t *to, msgEsFlags_t flags) { uint32_t bits, mask; if (flags & MSG_ES_UMASK) mask = 0xffff0000; else mask = 0xffff8000; // don't confuse old clients if (!to) { if (!from) Com_Error(ERR_DROP, "%s: NULL", __func__); if (from->number < 1 || from->number >= MAX_EDICTS) Com_Error(ERR_DROP, "%s: bad number: %d", __func__, from->number); bits = U_REMOVE; if (from->number & 0xff00) bits |= U_NUMBER16 | U_MOREBITS1; MSG_WriteByte(bits & 255); if (bits & 0x0000ff00) MSG_WriteByte((bits >> 8) & 255); if (bits & U_NUMBER16) MSG_WriteShort(from->number); else MSG_WriteByte(from->number); return; // remove entity } if (to->number < 1 || to->number >= MAX_EDICTS) Com_Error(ERR_DROP, "%s: bad number: %d", __func__, to->number); if (!from) from = &nullEntityState; // send an update bits = 0; diff_field(modelindex, U_MODEL, U8, 0); diff_field(modelindex2, U_MODEL2, U8, 0); diff_field(modelindex3, U_MODEL3, U8, 0); diff_field(modelindex4, U_MODEL4, U8, 0); diff_varia(frame, U_FRAME); diff_varia(skinnum, U_SKIN); diff_varia(effects, U_EFFECTS); diff_varia(renderfx, U_RENDERFX); if (!(flags & MSG_ES_FIRSTPERSON)) { diff_field(origin[0], U_ORIGIN1, S16, 0); diff_field(origin[1], U_ORIGIN2, S16, 0); diff_field(origin[2], U_ORIGIN3, S16, 0); diff_field(angles[0], U_ANGLE1, S16, 0); diff_field(angles[1], U_ANGLE2, S16, 0); diff_field(angles[2], U_ANGLE3, S16, 0); if ((flags & MSG_ES_SHORTANGLES) && (bits & (U_ANGLE1|U_ANGLE2|U_ANGLE3))) bits |= U_ANGLE16; if (flags & MSG_ES_NEWENTITY) diff_array(old_origin, U_OLDORIGIN, S16, 0, 3); } if (to->renderfx & RF_FRAMELERP) { bits |= U_OLDORIGIN; } else if (to->renderfx & RF_BEAM) { if (flags & MSG_ES_BEAMORIGIN) diff_array(old_origin, U_OLDORIGIN, S16, 0, 3); else bits |= U_OLDORIGIN; } diff_field(sound, U_SOUND, U8, 0); diff_field(event, U_EVENT, U8, 0); diff_field(solid, U_SOLID, _, 0); // // write the message // if (!bits && !(flags & MSG_ES_FORCE)) return; // nothing to send! if (flags & MSG_ES_REMOVE) bits |= U_REMOVE; // used for MVD stream only //---------- if (to->number & 0xff00) bits |= U_NUMBER16; // number8 is implicit otherwise if (bits & 0xff000000) bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1; else if (bits & 0x00ff0000) bits |= U_MOREBITS2 | U_MOREBITS1; else if (bits & 0x0000ff00) bits |= U_MOREBITS1; MSG_WriteByte(bits & 255); if (bits & 0xff000000) { MSG_WriteByte((bits >> 8) & 255); MSG_WriteByte((bits >> 16) & 255); MSG_WriteByte((bits >> 24) & 255); } else if (bits & 0x00ff0000) { MSG_WriteByte((bits >> 8) & 255); MSG_WriteByte((bits >> 16) & 255); } else if (bits & 0x0000ff00) { MSG_WriteByte((bits >> 8) & 255); } //---------- if (bits & U_NUMBER16) MSG_WriteShort(to->number); else MSG_WriteByte(to->number); write_field(modelindex, U_MODEL, U8, 0); write_field(modelindex2, U_MODEL2, U8, 0); write_field(modelindex3, U_MODEL3, U8, 0); write_field(modelindex4, U_MODEL4, U8, 0); write_varia(frame, U_FRAME); write_varia(skinnum, U_SKIN); write_varia(effects, U_EFFECTS); write_varia(renderfx, U_RENDERFX); write_field(origin[0], U_ORIGIN1, S16, 0); write_field(origin[1], U_ORIGIN2, S16, 0); write_field(origin[2], U_ORIGIN3, S16, 0); if ((flags & MSG_ES_SHORTANGLES) && (bits & U_ANGLE16)) { write_field(angles[0], U_ANGLE1, S16, 0); write_field(angles[1], U_ANGLE2, S16, 0); write_field(angles[2], U_ANGLE3, S16, 0); } else { if (bits & U_ANGLE1) MSG_WriteByte(to->angles[0] >> 8); if (bits & U_ANGLE2) MSG_WriteByte(to->angles[1] >> 8); if (bits & U_ANGLE3) MSG_WriteByte(to->angles[2] >> 8); } write_array(old_origin, U_OLDORIGIN, S16, 0, 3); write_field(sound, U_SOUND, U8, 0); write_field(event, U_EVENT, U8, 0); if (bits & U_SOLID) { if (flags & MSG_ES_LONGSOLID) MSG_WriteLong(to->solid); else MSG_WriteShort(to->solid); } } void MSG_PackPlayer(player_packed_t *out, const player_state_t *in) { int i; #define _field(_f, _m, _t, _s) pack_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) pack_array(_f, _m, _t, _s, _n) PLAYERSTATE_DEFAULT(); #undef _field #undef _array for (i = 0; i < MAX_STATS; i++) out->stats[i] = in->stats[i]; } void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player_packed_t *to) { int i; int bits; int statbits; if (!to) Com_Error(ERR_DROP, "%s: NULL", __func__); if (!from) from = &nullPlayerState; // // determine what needs to be sent // bits = 0; #define _field(_f, _m, _t, _s) diff_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) diff_array(_f, _m, _t, _s, _n) PLAYERSTATE_DEFAULT(); #undef _field #undef _array // // write it // MSG_WriteShort(bits); #define _field(_f, _m, _t, _s) write_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) write_array(_f, _m, _t, _s, _n) PLAYERSTATE_DEFAULT(); #undef _field #undef _array // send stats statbits = 0; for (i = 0; i < MAX_STATS; i++) if (to->stats[i] != from->stats[i]) statbits |= 1 << i; MSG_WriteLong(statbits); for (i = 0; i < MAX_STATS; i++) if (statbits & (1 << i)) MSG_WriteShort(to->stats[i]); } int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, player_packed_t *to, msgPsFlags_t flags) { int i; int bits; int statbits; if (!to) Com_Error(ERR_DROP, "%s: NULL", __func__); if (!from) from = &nullPlayerState; // save previous state if (flags & MSG_PS_IGNORE_GUNINDEX) to->gunindex = from->gunindex; if (flags & MSG_PS_IGNORE_GUNFRAMES) { to->gunframe = from->gunframe; to->gunoffset[0] = from->gunoffset[0]; to->gunoffset[1] = from->gunoffset[1]; to->gunoffset[2] = from->gunoffset[2]; to->gunangles[0] = from->gunangles[0]; to->gunangles[1] = from->gunangles[1]; to->gunangles[2] = from->gunangles[2]; } if (flags & MSG_PS_IGNORE_BLEND) { to->blend[0] = from->blend[0]; to->blend[1] = from->blend[1]; to->blend[2] = from->blend[2]; to->blend[3] = from->blend[3]; } if (flags & MSG_PS_IGNORE_VIEWANGLES) { to->viewangles[0] = from->viewangles[0]; to->viewangles[1] = from->viewangles[1]; to->viewangles[2] = from->viewangles[2]; } if (flags & MSG_PS_IGNORE_DELTAANGLES) VectorCopy(from->pmove.delta_angles, to->pmove.delta_angles); if (flags & MSG_PS_IGNORE_PREDICTION) { VectorCopy(from->pmove.velocity, to->pmove.velocity); to->pmove.pm_time = from->pmove.pm_time; to->pmove.pm_flags = from->pmove.pm_flags; to->pmove.gravity = from->pmove.gravity; } // // determine what needs to be sent // bits = 0; #define _field(_f, _m, _t, _s) diff_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) diff_array(_f, _m, _t, _s, _n) PLAYERSTATE_ENHANCED(); #undef _field #undef _array statbits = 0; for (i = 0; i < MAX_STATS; i++) if (to->stats[i] != from->stats[i]) statbits |= 1 << i; if (statbits) bits |= EPS_STATS; // // write it // MSG_WriteShort(bits & PS_MASK); #define _field(_f, _m, _t, _s) write_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) write_array(_f, _m, _t, _s, _n) PLAYERSTATE_ENHANCED(); #undef _field #undef _array // send stats if (bits & EPS_STATS) { MSG_WriteLong(statbits); for (i = 0; i < MAX_STATS; i++) if (statbits & (1 << i)) MSG_WriteShort(to->stats[i]); } return bits; } #if USE_MVD_SERVER || USE_MVD_CLIENT /* ================== MSG_WriteDeltaPlayerstate_Packet Throws away most of the pmove_state_t fields as they are used only for client prediction, and are not needed in MVDs. ================== */ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, const player_packed_t *to, int number, msgPsFlags_t flags) { int i; int bits; int statbits; if (number < 0 || number >= MAX_CLIENTS) Com_Error(ERR_DROP, "%s: bad number: %d", __func__, number); if (!to) { MSG_WriteByte(number); MSG_WriteShort(PPS_REMOVE); return; } if (!from) from = &nullPlayerState; // // determine what needs to be sent // bits = 0; diff_field(pmove.pm_type, PPS_M_TYPE, U8, 0); diff_array(pmove.origin, PPS_M_ORIGIN, S16, 0, 2); diff_field(pmove.origin[2], PPS_M_ORIGIN2, S16, 0); diff_array(viewoffset, PPS_VIEWOFFSET, S8, 0, 3); diff_array(viewangles, PPS_VIEWANGLES, S16, 0, 2); diff_field(viewangles[2], PPS_VIEWANGLE2, S16, 0); diff_array(kick_angles, PPS_KICKANGLES, S8, 0, 3); if (!(flags & MSG_PS_IGNORE_GUNINDEX)) diff_field(gunindex, PPS_WEAPONINDEX, U8, 0); if (!(flags & MSG_PS_IGNORE_GUNFRAMES)) { diff_field(gunframe, PPS_WEAPONFRAME, U8, 0); diff_array(gunoffset, PPS_GUNOFFSET, S8, 0, 3); diff_array(gunangles, PPS_GUNANGLES, S8, 0, 3); } if (!(flags & MSG_PS_IGNORE_BLEND)) diff_array(blend, PPS_BLEND, U8, 0, 4); diff_field(fov, PPS_FOV, U8, 0); diff_field(rdflags, PPS_RDFLAGS, U8, 0); statbits = 0; for (i = 0; i < MAX_STATS; i++) if (to->stats[i] != from->stats[i]) statbits |= 1 << i; if (statbits) bits |= PPS_STATS; if (!bits && !(flags & MSG_PS_FORCE)) return; if (flags & MSG_PS_REMOVE) bits |= PPS_REMOVE; // used for MVD stream only // // write it // MSG_WriteByte(number); MSG_WriteShort(bits); #define _field(_f, _m, _t, _s) write_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) write_array(_f, _m, _t, _s, _n) PLAYERSTATE_PACKET(); #undef _field #undef _array // send stats if (bits & PPS_STATS) { MSG_WriteLong(statbits); for (i = 0; i < MAX_STATS; i++) if (statbits & (1 << i)) MSG_WriteShort(to->stats[i]); } } #endif // USE_MVD_SERVER || USE_MVD_CLIENT /* ============================================================================== READING ============================================================================== */ void MSG_BeginReading(void) { msg_read.readcount = 0; msg_read.bitpos = 0; } byte *MSG_ReadData(size_t len) { byte *buf = msg_read.data + msg_read.readcount; msg_read.readcount += len; msg_read.bitpos = msg_read.readcount << 3; if (msg_read.readcount > msg_read.cursize) { if (!msg_read.allowunderflow) { Com_Error(ERR_DROP, "%s: read past end of message", __func__); } return NULL; } return buf; } // returns -1 if no more characters are available int MSG_ReadChar(void) { byte *buf = MSG_ReadData(1); int c; if (!buf) { c = -1; } else { c = (signed char)buf[0]; } return c; } int MSG_ReadByte(void) { byte *buf = MSG_ReadData(1); int c; if (!buf) { c = -1; } else { c = (unsigned char)buf[0]; } return c; } int MSG_ReadShort(void) { byte *buf = MSG_ReadData(2); int c; if (!buf) { c = -1; } else { c = (signed short)LittleShortMem(buf); } return c; } int MSG_ReadWord(void) { byte *buf = MSG_ReadData(2); int c; if (!buf) { c = -1; } else { c = (unsigned short)LittleShortMem(buf); } return c; } int MSG_ReadLong(void) { byte *buf = MSG_ReadData(4); int c; if (!buf) { c = -1; } else { c = LittleLongMem(buf); } return c; } size_t MSG_ReadString(char *dest, size_t size) { int c; size_t len = 0; while (1) { c = MSG_ReadByte(); if (c == -1 || c == 0) { break; } if (len + 1 < size) { *dest++ = c; } len++; } if (size) { *dest = 0; } return len; } size_t MSG_ReadStringLine(char *dest, size_t size) { int c; size_t len = 0; while (1) { c = MSG_ReadByte(); if (c == -1 || c == 0 || c == '\n') { break; } if (len + 1 < size) { *dest++ = c; } len++; } if (size) { *dest = 0; } return len; } static inline float MSG_ReadCoord(void) { return SHORT2COORD(MSG_ReadShort()); } #if !USE_CLIENT static inline #endif void MSG_ReadPos(vec3_t pos) { pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); } static inline float MSG_ReadAngle(void) { return BYTE2ANGLE(MSG_ReadChar()); } static inline float MSG_ReadAngle16(void) { return SHORT2ANGLE(MSG_ReadShort()); } #if USE_CLIENT void MSG_ReadDir(vec3_t dir) { int b; b = MSG_ReadByte(); if (b < 0 || b >= NUMVERTEXNORMALS) Com_Error(ERR_DROP, "MSG_ReadDir: out of range"); VectorCopy(bytedirs[b], dir); } #endif void MSG_ReadDeltaUsercmd(const usercmd_t *from, usercmd_t *to) { int bits; if (from) { memcpy(to, from, sizeof(*to)); } else { memset(to, 0, sizeof(*to)); } bits = MSG_ReadByte(); // read current angles if (bits & CM_ANGLE1) to->angles[0] = MSG_ReadShort(); if (bits & CM_ANGLE2) to->angles[1] = MSG_ReadShort(); if (bits & CM_ANGLE3) to->angles[2] = MSG_ReadShort(); // read movement if (bits & CM_FORWARD) to->forwardmove = MSG_ReadShort(); if (bits & CM_SIDE) to->sidemove = MSG_ReadShort(); if (bits & CM_UP) to->upmove = MSG_ReadShort(); // read buttons if (bits & CM_BUTTONS) to->buttons = MSG_ReadByte(); if (bits & CM_IMPULSE) to->impulse = MSG_ReadByte(); // read time to run command to->msec = MSG_ReadByte(); // read the light level to->lightlevel = MSG_ReadByte(); } void MSG_ReadDeltaUsercmd_Hacked(const usercmd_t *from, usercmd_t *to) { int bits, buttons = 0; if (from) { memcpy(to, from, sizeof(*to)); } else { memset(to, 0, sizeof(*to)); } bits = MSG_ReadByte(); // read buttons if (bits & CM_BUTTONS) { buttons = MSG_ReadByte(); to->buttons = buttons & BUTTON_MASK; } // read current angles if (bits & CM_ANGLE1) { if (buttons & BUTTON_ANGLE1) { to->angles[0] = MSG_ReadChar() * 64; } else { to->angles[0] = MSG_ReadShort(); } } if (bits & CM_ANGLE2) { if (buttons & BUTTON_ANGLE2) { to->angles[1] = MSG_ReadChar() * 256; } else { to->angles[1] = MSG_ReadShort(); } } if (bits & CM_ANGLE3) to->angles[2] = MSG_ReadShort(); // read movement if (bits & CM_FORWARD) { if (buttons & BUTTON_FORWARD) { to->forwardmove = MSG_ReadChar() * 5; } else { to->forwardmove = MSG_ReadShort(); } } if (bits & CM_SIDE) { if (buttons & BUTTON_SIDE) { to->sidemove = MSG_ReadChar() * 5; } else { to->sidemove = MSG_ReadShort(); } } if (bits & CM_UP) { if (buttons & BUTTON_UP) { to->upmove = MSG_ReadChar() * 5; } else { to->upmove = MSG_ReadShort(); } } if (bits & CM_IMPULSE) to->impulse = MSG_ReadByte(); // read time to run command to->msec = MSG_ReadByte(); // read the light level to->lightlevel = MSG_ReadByte(); } int MSG_ReadBits(int bits) { int i, get; size_t bitpos; qboolean sgn; int value; if (bits == 0 || bits < -31 || bits > 32) { Com_Error(ERR_FATAL, "MSG_ReadBits: bad bits: %d", bits); } bitpos = msg_read.bitpos; if ((bitpos & 7) == 0) { // optimized case switch (bits) { case -8: value = MSG_ReadChar(); return value; case 8: value = MSG_ReadByte(); return value; case -16: value = MSG_ReadShort(); return value; case 32: value = MSG_ReadLong(); return value; default: break; } } sgn = qfalse; if (bits < 0) { bits = -bits; sgn = qtrue; } value = 0; for (i = 0; i < bits; i++, bitpos++) { get = (msg_read.data[bitpos >> 3] >> (bitpos & 7)) & 1; value |= get << i; } msg_read.bitpos = bitpos; msg_read.readcount = (bitpos + 7) >> 3; if (sgn) { if (value & (1 << (bits - 1))) { value |= -1 ^((1 << bits) - 1); } } return value; } void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, usercmd_t *to, int version) { int bits, count; if (from) { memcpy(to, from, sizeof(*to)); } else { memset(to, 0, sizeof(*to)); } if (!MSG_ReadBits(1)) { return; } bits = MSG_ReadBits(8); // read current angles if (bits & CM_ANGLE1) { if (MSG_ReadBits(1)) { to->angles[0] += MSG_ReadBits(-8); } else { to->angles[0] = MSG_ReadBits(-16); } } if (bits & CM_ANGLE2) { if (MSG_ReadBits(1)) { to->angles[1] += MSG_ReadBits(-8); } else { to->angles[1] = MSG_ReadBits(-16); } } if (bits & CM_ANGLE3) { to->angles[2] = MSG_ReadBits(-16); } // read movement if (version >= PROTOCOL_VERSION_Q2PRO_UCMD) { count = -10; } else { count = -16; } if (bits & CM_FORWARD) { to->forwardmove = MSG_ReadBits(count); } if (bits & CM_SIDE) { to->sidemove = MSG_ReadBits(count); } if (bits & CM_UP) { to->upmove = MSG_ReadBits(count); } // read buttons if (bits & CM_BUTTONS) { int buttons = MSG_ReadBits(3); to->buttons = (buttons & 3) | ((buttons & 4) << 5); } // read time to run command if (bits & CM_IMPULSE) { to->msec = MSG_ReadBits(8); } } #if USE_CLIENT || USE_MVD_CLIENT /* ================= MSG_ParseEntityBits Returns the entity number and the header bits ================= */ int MSG_ParseEntityBits(int *bits) { int b, total; int number; total = MSG_ReadByte(); if (total & U_MOREBITS1) { b = MSG_ReadByte(); total |= b << 8; } if (total & U_MOREBITS2) { b = MSG_ReadByte(); total |= b << 16; } if (total & U_MOREBITS3) { b = MSG_ReadByte(); total |= b << 24; } if (total & U_NUMBER16) number = MSG_ReadShort(); else number = MSG_ReadByte(); *bits = total; return number; } /* ================== MSG_ParseDeltaEntity Can go from either a baseline or a previous packet_entity ================== */ void MSG_ParseDeltaEntity(const entity_state_t *from, entity_state_t *to, int number, int bits, msgEsFlags_t flags) { if (!to) { Com_Error(ERR_DROP, "%s: NULL", __func__); } if (number < 1 || number >= MAX_EDICTS) { Com_Error(ERR_DROP, "%s: bad entity number: %d", __func__, number); } // set everything to the state we are delta'ing from if (!from) { memset(to, 0, sizeof(*to)); } else if (to != from) { memcpy(to, from, sizeof(*to)); } to->number = number; to->event = 0; if (!bits) { return; } read_field(modelindex, U_MODEL, U8, 0); read_field(modelindex2, U_MODEL2, U8, 0); read_field(modelindex3, U_MODEL3, U8, 0); read_field(modelindex4, U_MODEL4, U8, 0); read_varia(frame, U_FRAME); read_varia(skinnum, U_SKIN); read_varia(effects, U_EFFECTS); read_varia(renderfx, U_RENDERFX); read_field(origin[0], U_ORIGIN1, S16, S16_COORD); read_field(origin[1], U_ORIGIN2, S16, S16_COORD); read_field(origin[2], U_ORIGIN3, S16, S16_COORD); if ((flags & MSG_ES_SHORTANGLES) && (bits & U_ANGLE16)) { read_field(angles[0], U_ANGLE1, S16, S16_ANGLE); read_field(angles[1], U_ANGLE2, S16, S16_ANGLE); read_field(angles[2], U_ANGLE3, S16, S16_ANGLE); } else { read_field(angles[0], U_ANGLE1, U8, U8_ANGLE); read_field(angles[1], U_ANGLE2, U8, U8_ANGLE); read_field(angles[2], U_ANGLE3, U8, U8_ANGLE); } read_array(old_origin, U_OLDORIGIN, S16, S16_COORD, 3); read_field(sound, U_SOUND, U8, 0); read_field(event, U_EVENT, U8, 0); if (bits & U_SOLID) { if (flags & MSG_ES_LONGSOLID) { to->solid = MSG_ReadLong(); } else { to->solid = MSG_ReadWord(); } } } #endif // USE_CLIENT || USE_MVD_CLIENT #if USE_CLIENT /* =================== MSG_ParseDeltaPlayerstate_Default =================== */ void MSG_ParseDeltaPlayerstate_Default(const player_state_t *from, player_state_t *to, int bits) { int i; int statbits; if (!to) { Com_Error(ERR_DROP, "%s: NULL", __func__); } // clear to old value before delta parsing if (!from) { memset(to, 0, sizeof(*to)); } else if (to != from) { memcpy(to, from, sizeof(*to)); } #define _field(_f, _m, _t, _s) read_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) read_array(_f, _m, _t, _s, _n) PLAYERSTATE_DEFAULT(); #undef _field #undef _array // parse stats statbits = MSG_ReadLong(); for (i = 0; i < MAX_STATS; i++) if (statbits & (1 << i)) to->stats[i] = MSG_ReadShort(); } /* =================== MSG_ParseDeltaPlayerstate_Default =================== */ void MSG_ParseDeltaPlayerstate_Enhanced(const player_state_t *from, player_state_t *to, int bits_lo, int bits_hi) { int i; int statbits; int bits = bits_lo | (bits_hi << EPS_OFFSET); if (!to) { Com_Error(ERR_DROP, "%s: NULL", __func__); } // clear to old value before delta parsing if (!from) { memset(to, 0, sizeof(*to)); } else if (to != from) { memcpy(to, from, sizeof(*to)); } #define _field(_f, _m, _t, _s) read_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) read_array(_f, _m, _t, _s, _n) PLAYERSTATE_ENHANCED(); #undef _field #undef _array // parse stats if (bits & EPS_STATS) { statbits = MSG_ReadLong(); for (i = 0; i < MAX_STATS; i++) { if (statbits & (1 << i)) { to->stats[i] = MSG_ReadShort(); } } } } #endif // USE_CLIENT #if USE_MVD_CLIENT /* =================== MSG_ParseDeltaPlayerstate_Packet =================== */ void MSG_ParseDeltaPlayerstate_Packet(const player_state_t *from, player_state_t *to, int bits) { int i; int statbits; if (!to) { Com_Error(ERR_DROP, "%s: NULL", __func__); } // clear to old value before delta parsing if (!from) { memset(to, 0, sizeof(*to)); } else if (to != from) { memcpy(to, from, sizeof(*to)); } #define _field(_f, _m, _t, _s) read_field(_f, _m, _t, _s) #define _array(_f, _m, _t, _s, _n) read_array(_f, _m, _t, _s, _n) PLAYERSTATE_PACKET(); #undef _field #undef _array // parse stats if (bits & PPS_STATS) { statbits = MSG_ReadLong(); for (i = 0; i < MAX_STATS; i++) { if (statbits & (1 << i)) { to->stats[i] = MSG_ReadShort(); } } } } #endif // USE_MVD_CLIENT /* ============================================================================== DEBUGGING STUFF ============================================================================== */ #ifdef _DEBUG #define SHOWBITS(x) Com_LPrintf(PRINT_DEVELOPER, x " ") #if USE_CLIENT void MSG_ShowDeltaPlayerstateBits_Default(int flags) { #define S(b,s) if(flags&PS_##b) SHOWBITS(s) S(M_TYPE, "pmove.pm_type"); S(M_ORIGIN, "pmove.origin"); S(M_VELOCITY, "pmove.velocity"); S(M_TIME, "pmove.pm_time"); S(M_FLAGS, "pmove.pm_flags"); S(M_GRAVITY, "pmove.gravity"); S(M_DELTA_ANGLES, "pmove.delta_angles"); S(VIEWOFFSET, "viewoffset"); S(VIEWANGLES, "viewangles"); S(KICKANGLES, "kick_angles"); S(WEAPONINDEX, "gunindex"); S(WEAPONFRAME, "gunframe"); S(BLEND, "blend"); S(FOV, "fov"); S(RDFLAGS, "rdflags"); #undef S } void MSG_ShowDeltaPlayerstateBits_Enhanced(int flags_lo, int flags_hi) { int flags = flags_lo | (flags_hi << EPS_OFFSET); #define SP(b,s) if(flags&PS_##b) SHOWBITS(s) #define SE(b,s) if(flags&EPS_##b) SHOWBITS(s) SP(M_TYPE, "pmove.pm_type"); SP(M_ORIGIN, "pmove.origin[0,1]"); SE(M_ORIGIN2, "pmove.origin[2]"); SP(M_VELOCITY, "pmove.velocity[0,1]"); SE(M_VELOCITY2, "pmove.velocity[2]"); SP(M_TIME, "pmove.pm_time"); SP(M_FLAGS, "pmove.pm_flags"); SP(M_GRAVITY, "pmove.gravity"); SP(M_DELTA_ANGLES, "pmove.delta_angles"); SP(VIEWOFFSET, "viewoffset"); SP(VIEWANGLES, "viewangles[0,1]"); SE(VIEWANGLE2, "viewangles[2]"); SP(KICKANGLES, "kick_angles"); SP(WEAPONINDEX, "gunindex"); SP(WEAPONFRAME, "gunframe"); SE(GUNOFFSET, "gunoffset"); SE(GUNANGLES, "gunangles"); SP(BLEND, "blend"); SP(FOV, "fov"); SP(RDFLAGS, "rdflags"); SE(STATS, "stats"); #undef SP #undef SE } void MSG_ShowDeltaUsercmdBits_Enhanced(int bits) { if (!bits) { SHOWBITS(""); return; } #define S(b,s) if(bits&CM_##b) SHOWBITS(s) S(ANGLE1, "angle1"); S(ANGLE2, "angle2"); S(ANGLE3, "angle3"); S(FORWARD, "forward"); S(SIDE, "side"); S(UP, "up"); S(BUTTONS, "buttons"); S(IMPULSE, "msec"); #undef S } #endif // USE_CLIENT #if USE_CLIENT || USE_MVD_CLIENT void MSG_ShowDeltaEntityBits(int bits) { #define S(b,s) if(bits&U_##b) SHOWBITS(s) S(MODEL, "modelindex"); S(MODEL2, "modelindex2"); S(MODEL3, "modelindex3"); S(MODEL4, "modelindex4"); if (bits & U_FRAME8) SHOWBITS("frame8"); if (bits & U_FRAME16) SHOWBITS("frame16"); if ((bits & (U_SKIN8 | U_SKIN16)) == (U_SKIN8 | U_SKIN16)) SHOWBITS("skinnum32"); else if (bits & U_SKIN8) SHOWBITS("skinnum8"); else if (bits & U_SKIN16) SHOWBITS("skinnum16"); if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) SHOWBITS("effects32"); else if (bits & U_EFFECTS8) SHOWBITS("effects8"); else if (bits & U_EFFECTS16) SHOWBITS("effects16"); if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) SHOWBITS("renderfx32"); else if (bits & U_RENDERFX8) SHOWBITS("renderfx8"); else if (bits & U_RENDERFX16) SHOWBITS("renderfx16"); S(ORIGIN1, "origin[0]"); S(ORIGIN2, "origin[1]"); S(ORIGIN3, "origin[2]"); S(ANGLE1, "angles[0]"); S(ANGLE2, "angles[1]"); S(ANGLE3, "angles[2]"); S(OLDORIGIN, "old_origin"); S(SOUND, "sound"); S(EVENT, "event"); S(SOLID, "solid"); #undef S } void MSG_ShowDeltaPlayerstateBits_Packet(int flags) { #define S(b,s) if(flags&PPS_##b) SHOWBITS(s) S(M_TYPE, "pmove.pm_type"); S(M_ORIGIN, "pmove.origin[0,1]"); S(M_ORIGIN2, "pmove.origin[2]"); S(VIEWOFFSET, "viewoffset"); S(VIEWANGLES, "viewangles[0,1]"); S(VIEWANGLE2, "viewangles[2]"); S(KICKANGLES, "kick_angles"); S(WEAPONINDEX, "gunindex"); S(WEAPONFRAME, "gunframe"); S(GUNOFFSET, "gunoffset"); S(GUNANGLES, "gunangles"); S(BLEND, "blend"); S(FOV, "fov"); S(RDFLAGS, "rdflags"); S(STATS, "stats"); #undef S } const char *MSG_ServerCommandString(int cmd) { switch (cmd) { case -1: return "END OF MESSAGE"; default: return "UNKNOWN COMMAND"; #define S(x) case svc_##x: return "svc_" #x; S(bad) S(muzzleflash) S(muzzleflash2) S(temp_entity) S(layout) S(inventory) S(nop) S(disconnect) S(reconnect) S(sound) S(print) S(stufftext) S(serverdata) S(configstring) S(spawnbaseline) S(centerprint) S(download) S(playerinfo) S(packetentities) S(deltapacketentities) S(frame) S(zpacket) S(zdownload) S(gamestate) #undef S } } #endif // USE_CLIENT || USE_MVD_CLIENT #endif // _DEBUG