/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "com_local.h" #include "protocol.h" #include "q_msg.h" sizebuf_t msg_write; byte msg_write_buffer[MAX_MSGLEN]; sizebuf_t msg_read; byte msg_read_buffer[MAX_MSGLEN]; /* ============================================================================== MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors ============================================================================== */ const entity_state_t nullEntityState; const player_state_t nullPlayerState; const usercmd_t nullUserCmd; /* ============= MSG_Init ============= */ void MSG_Init( void ) { // initialize default buffers // don't allow them to overflow 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 functions // /* ============= 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)) void MSG_WriteAngle( float f ) { MSG_WriteByte( ANGLE2BYTE( f ) ); } /* ============= MSG_WriteAngle16 ============= */ static inline void MSG_WriteAngle16( float f ) { MSG_WriteShort( ANGLE2SHORT( f ) ); } #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 ); } // values transmitted over network are discrete, so // we use special macros to check for delta conditions #define delta_angle(a,b) (ANGLE2BYTE(b)!=ANGLE2BYTE(a)) #define delta_angle16(a,b) (ANGLE2SHORT(b)!=ANGLE2SHORT(a)) #define delta_angle16_v(a,b) \ (delta_angle16(a[0],b[0])|| \ delta_angle16(a[1],b[1])|| \ delta_angle16(a[2],b[2])) #define delta_coord(a,b) (COORD2SHORT(b)!=COORD2SHORT(a)) #define delta_pos_v(a,b) \ (delta_coord(a[0],b[0])|| \ delta_coord(a[1],b[1])|| \ delta_coord(a[2],b[2])) #define delta_ofs(a,b) ((int)((b)*4)!=(int)((a)*4)) #define delta_ofs_v(a,b) \ (delta_ofs(a[0],b[0])|| \ delta_ofs(a[1],b[1])|| \ delta_ofs(a[2],b[2])) #define delta_blend(a,b) ((int)((b)*255)!=(int)((a)*255)) #define delta_blend_v(a,b) \ (delta_blend(a[0],b[0])|| \ delta_blend(a[1],b[1])|| \ delta_blend(a[2],b[2])|| \ delta_blend(a[3],b[3])) #define delta_fov(a,b) ((int)(b)!=(int)(a)) /* ================== MSG_WriteDeltaEntity Writes part of a packetentities message. Can delta from either a baseline or a previous packet_entity ================== */ void MSG_WriteDeltaEntity( const entity_state_t *from, const entity_state_t *to, msgEsFlags_t flags ) { unsigned bits, mask; 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 >= 256 ) { 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; if( !( flags & MSG_ES_FIRSTPERSON ) ) { if( delta_coord( to->origin[0], from->origin[0] ) ) bits |= U_ORIGIN1; if( delta_coord( to->origin[1], from->origin[1] ) ) bits |= U_ORIGIN2; if( delta_coord( to->origin[2], from->origin[2] ) ) bits |= U_ORIGIN3; if( delta_angle( to->angles[0], from->angles[0] ) ) bits |= U_ANGLE1; if( delta_angle( to->angles[1], from->angles[1] ) ) bits |= U_ANGLE2; if( delta_angle( to->angles[2], from->angles[2] ) ) bits |= U_ANGLE3; if( flags & MSG_ES_NEWENTITY ) { if( delta_pos_v( to->old_origin, from->old_origin ) ) { bits |= U_OLDORIGIN; } } } if( flags & MSG_ES_UMASK ) { mask = 0xffff0000; } else { mask = 0xffff8000; // don't confuse old clients } if( to->skinnum != from->skinnum ) { if( to->skinnum & mask ) { bits |= U_SKIN8|U_SKIN16; } else if( to->skinnum & 0x0000ff00 ) { bits |= U_SKIN16; } else { bits |= U_SKIN8; } } if( to->frame != from->frame ) { if( to->frame < 256 ) bits |= U_FRAME8; else bits |= U_FRAME16; } if( to->effects != from->effects ) { if( to->effects & mask ) { bits |= U_EFFECTS8|U_EFFECTS16; } else if( to->effects & 0x0000ff00 ) { bits |= U_EFFECTS16; } else { bits |= U_EFFECTS8; } } if( to->renderfx != from->renderfx ) { if( to->renderfx & mask ) { bits |= U_RENDERFX8|U_RENDERFX16; } else if( to->renderfx & 0x0000ff00 ) { bits |= U_RENDERFX16; } else { bits |= U_RENDERFX8; } } if( to->solid != from->solid ) bits |= U_SOLID; // event is not delta compressed, just 0 compressed if( to->event ) bits |= U_EVENT; if( to->modelindex != from->modelindex ) bits |= U_MODEL; if( to->modelindex2 != from->modelindex2 ) bits |= U_MODEL2; if( to->modelindex3 != from->modelindex3 ) bits |= U_MODEL3; if( to->modelindex4 != from->modelindex4 ) bits |= U_MODEL4; if( to->sound != from->sound ) bits |= U_SOUND; if( ( to->renderfx & RF_BEAM ) ) bits |= U_OLDORIGIN; // // 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 >= 256) 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); if (bits & U_MODEL) MSG_WriteByte (to->modelindex); if (bits & U_MODEL2) MSG_WriteByte (to->modelindex2); if (bits & U_MODEL3) MSG_WriteByte (to->modelindex3); if (bits & U_MODEL4) MSG_WriteByte (to->modelindex4); if (bits & U_FRAME8) MSG_WriteByte (to->frame); else if (bits & U_FRAME16) MSG_WriteShort (to->frame); if ((bits & (U_SKIN8|U_SKIN16)) == (U_SKIN8|U_SKIN16) ) //used for laser colors MSG_WriteLong (to->skinnum); else if (bits & U_SKIN8) MSG_WriteByte (to->skinnum); else if (bits & U_SKIN16) MSG_WriteShort (to->skinnum); if ( (bits & (U_EFFECTS8|U_EFFECTS16)) == (U_EFFECTS8|U_EFFECTS16) ) MSG_WriteLong (to->effects); else if (bits & U_EFFECTS8) MSG_WriteByte (to->effects); else if (bits & U_EFFECTS16) MSG_WriteShort (to->effects); if ( (bits & (U_RENDERFX8|U_RENDERFX16)) == (U_RENDERFX8|U_RENDERFX16) ) MSG_WriteLong (to->renderfx); else if (bits & U_RENDERFX8) MSG_WriteByte (to->renderfx); else if (bits & U_RENDERFX16) MSG_WriteShort (to->renderfx); if (bits & U_ORIGIN1) MSG_WriteCoord (to->origin[0]); if (bits & U_ORIGIN2) MSG_WriteCoord (to->origin[1]); if (bits & U_ORIGIN3) MSG_WriteCoord (to->origin[2]); if (bits & U_ANGLE1) MSG_WriteAngle(to->angles[0]); if (bits & U_ANGLE2) MSG_WriteAngle(to->angles[1]); if (bits & U_ANGLE3) MSG_WriteAngle(to->angles[2]); if (bits & U_OLDORIGIN) MSG_WritePos (to->old_origin); if (bits & U_SOUND) MSG_WriteByte (to->sound); if (bits & U_EVENT) MSG_WriteByte (to->event); if (bits & U_SOLID) { if( flags & MSG_ES_LONGSOLID ) { MSG_WriteLong (to->solid); } else { MSG_WriteShort (to->solid); } } } /* ================== MSG_WriteDeltaPlayerstate_Default ================== */ void MSG_WriteDeltaPlayerstate_Default( const player_state_t *from, const player_state_t *to ) { int i; int pflags; int statbits; if( !to ) { Com_Error( ERR_DROP, "%s: NULL", __func__ ); } if( !from ) { from = &nullPlayerState; } // // determine what needs to be sent // pflags = 0; if( to->pmove.pm_type != from->pmove.pm_type ) pflags |= PS_M_TYPE; if( to->pmove.origin[0] != from->pmove.origin[0] || to->pmove.origin[1] != from->pmove.origin[1] || to->pmove.origin[2] != from->pmove.origin[2] ) { pflags |= PS_M_ORIGIN; } if( to->pmove.velocity[0] != from->pmove.velocity[0] || to->pmove.velocity[1] != from->pmove.velocity[1] || to->pmove.velocity[2] != from->pmove.velocity[2] ) { pflags |= PS_M_VELOCITY; } if( to->pmove.pm_time != from->pmove.pm_time ) pflags |= PS_M_TIME; if( to->pmove.pm_flags != from->pmove.pm_flags ) pflags |= PS_M_FLAGS; if( to->pmove.gravity != from->pmove.gravity ) pflags |= PS_M_GRAVITY; if( to->pmove.delta_angles[0] != from->pmove.delta_angles[0] || to->pmove.delta_angles[1] != from->pmove.delta_angles[1] || to->pmove.delta_angles[2] != from->pmove.delta_angles[2] ) { pflags |= PS_M_DELTA_ANGLES; } if( delta_ofs_v( to->viewoffset, from->viewoffset ) ) { pflags |= PS_VIEWOFFSET; } if( delta_angle16_v( to->viewangles, from->viewangles ) ) { pflags |= PS_VIEWANGLES; } if( delta_ofs_v( to->kick_angles, from->kick_angles ) ) { pflags |= PS_KICKANGLES; } if( delta_blend_v( to->blend, from->blend ) ) { pflags |= PS_BLEND; } if( delta_fov( to->fov, from->fov ) ) pflags |= PS_FOV; if( to->rdflags != from->rdflags ) pflags |= PS_RDFLAGS; if( to->gunframe != from->gunframe || delta_ofs_v( to->gunoffset, from->gunoffset ) || delta_ofs_v( to->gunangles, from->gunangles ) ) { pflags |= PS_WEAPONFRAME; } if( to->gunindex != from->gunindex ) pflags |= PS_WEAPONINDEX; // // write it // MSG_WriteShort( pflags ); // // write the pmove_state_t // if( pflags & PS_M_TYPE ) MSG_WriteByte( to->pmove.pm_type ); if( pflags & PS_M_ORIGIN ) { MSG_WriteShort( to->pmove.origin[0] ); MSG_WriteShort( to->pmove.origin[1] ); MSG_WriteShort( to->pmove.origin[2] ); } if( pflags & PS_M_VELOCITY ) { MSG_WriteShort( to->pmove.velocity[0] ); MSG_WriteShort( to->pmove.velocity[1] ); MSG_WriteShort( to->pmove.velocity[2] ); } if( pflags & PS_M_TIME ) MSG_WriteByte( to->pmove.pm_time ); if( pflags & PS_M_FLAGS ) MSG_WriteByte( to->pmove.pm_flags ); if( pflags & PS_M_GRAVITY ) MSG_WriteShort( to->pmove.gravity ); if( pflags & PS_M_DELTA_ANGLES ) { MSG_WriteShort( to->pmove.delta_angles[0] ); MSG_WriteShort( to->pmove.delta_angles[1] ); MSG_WriteShort( to->pmove.delta_angles[2] ); } // // write the rest of the player_state_t // if( pflags & PS_VIEWOFFSET ) { MSG_WriteChar( to->viewoffset[0] * 4 ); MSG_WriteChar( to->viewoffset[1] * 4 ); MSG_WriteChar( to->viewoffset[2] * 4 ); } if( pflags & PS_VIEWANGLES ) { MSG_WriteAngle16( to->viewangles[0] ); MSG_WriteAngle16( to->viewangles[1] ); MSG_WriteAngle16( to->viewangles[2] ); } if( pflags & PS_KICKANGLES ) { MSG_WriteChar( to->kick_angles[0] * 4 ); MSG_WriteChar( to->kick_angles[1] * 4 ); MSG_WriteChar( to->kick_angles[2] * 4 ); } if( pflags & PS_WEAPONINDEX ) { MSG_WriteByte( to->gunindex ); } if( pflags & PS_WEAPONFRAME ) { MSG_WriteByte( to->gunframe ); MSG_WriteChar( to->gunoffset[0] * 4 ); MSG_WriteChar( to->gunoffset[1] * 4 ); MSG_WriteChar( to->gunoffset[2] * 4 ); MSG_WriteChar( to->gunangles[0] * 4 ); MSG_WriteChar( to->gunangles[1] * 4 ); MSG_WriteChar( to->gunangles[2] * 4 ); } if( pflags & PS_BLEND ) { MSG_WriteByte( to->blend[0] * 255 ); MSG_WriteByte( to->blend[1] * 255 ); MSG_WriteByte( to->blend[2] * 255 ); MSG_WriteByte( to->blend[3] * 255 ); } if( pflags & PS_FOV ) MSG_WriteByte( to->fov ); if( pflags & PS_RDFLAGS ) MSG_WriteByte( to->rdflags ); // 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] ); } /* ================== MSG_WriteDeltaPlayerstate_Enhanced ================== */ int MSG_WriteDeltaPlayerstate_Enhanced( const player_state_t *from, player_state_t *to, msgPsFlags_t flags ) { int i; int pflags, extraflags; int statbits; if( !to ) { Com_Error( ERR_DROP, "%s: NULL", __func__ ); } if( !from ) { from = &nullPlayerState; } // // determine what needs to be sent // pflags = 0; extraflags = 0; if( to->pmove.pm_type != from->pmove.pm_type ) pflags |= PS_M_TYPE; if( to->pmove.origin[0] != from->pmove.origin[0] || to->pmove.origin[1] != from->pmove.origin[1] ) { pflags |= PS_M_ORIGIN; } if( to->pmove.origin[2] != from->pmove.origin[2] ) { extraflags |= EPS_M_ORIGIN2; } if( !( flags & MSG_PS_IGNORE_PREDICTION ) ) { if( to->pmove.velocity[0] != from->pmove.velocity[0] || to->pmove.velocity[1] != from->pmove.velocity[1] ) { pflags |= PS_M_VELOCITY; } if( to->pmove.velocity[2] != from->pmove.velocity[2] ) { extraflags |= EPS_M_VELOCITY2; } if( to->pmove.pm_time != from->pmove.pm_time ) pflags |= PS_M_TIME; if( to->pmove.pm_flags != from->pmove.pm_flags ) pflags |= PS_M_FLAGS; if( to->pmove.gravity != from->pmove.gravity ) pflags |= PS_M_GRAVITY; } else { // save previous state 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; } if( !( flags & MSG_PS_IGNORE_DELTAANGLES ) ) { if( to->pmove.delta_angles[0] != from->pmove.delta_angles[0] || to->pmove.delta_angles[1] != from->pmove.delta_angles[1] || to->pmove.delta_angles[2] != from->pmove.delta_angles[2] ) { pflags |= PS_M_DELTA_ANGLES; } } else { // save previous state VectorCopy( from->pmove.delta_angles, to->pmove.delta_angles ); } if( delta_ofs_v( from->viewoffset, to->viewoffset ) ) { pflags |= PS_VIEWOFFSET; } if( !( flags & MSG_PS_IGNORE_VIEWANGLES ) ) { if( delta_angle16( from->viewangles[0], to->viewangles[0] ) || delta_angle16( from->viewangles[1], to->viewangles[1] ) ) { pflags |= PS_VIEWANGLES; } if( delta_angle16( from->viewangles[2], to->viewangles[2] ) ) { extraflags |= EPS_VIEWANGLE2; } } else { // save previous state to->viewangles[0] = from->viewangles[0]; to->viewangles[1] = from->viewangles[1]; to->viewangles[2] = from->viewangles[2]; } if( delta_ofs_v( from->kick_angles, to->kick_angles ) ) { pflags |= PS_KICKANGLES; } if( !( flags & MSG_PS_IGNORE_BLEND ) ) { if( delta_blend_v( from->blend, to->blend ) ) { pflags |= PS_BLEND; } } else { // save previous state 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( delta_fov( from->fov, to->fov ) ) pflags |= PS_FOV; if( to->rdflags != from->rdflags ) pflags |= PS_RDFLAGS; if( !( flags & MSG_PS_IGNORE_GUNINDEX ) ) { if( to->gunindex != from->gunindex ) pflags |= PS_WEAPONINDEX; } else { // save previous state to->gunindex = from->gunindex; } if( !( flags & MSG_PS_IGNORE_GUNFRAMES ) ) { if( to->gunframe != from->gunframe ) pflags |= PS_WEAPONFRAME; if( delta_ofs_v( from->gunoffset, to->gunoffset ) ) { extraflags |= EPS_GUNOFFSET; } if( delta_ofs_v( from->gunangles, to->gunangles ) ) { extraflags |= EPS_GUNANGLES; } } else { // save previous state 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]; } statbits = 0; for( i = 0; i < MAX_STATS; i++ ) { if( to->stats[i] != from->stats[i] ) { statbits |= 1 << i; } } if( statbits ) { extraflags |= EPS_STATS; } // // write it // MSG_WriteShort( pflags ); // // write the pmove_state_t // if( pflags & PS_M_TYPE ) MSG_WriteByte( to->pmove.pm_type ); if( pflags & PS_M_ORIGIN ) { MSG_WriteShort( to->pmove.origin[0] ); MSG_WriteShort( to->pmove.origin[1] ); } if( extraflags & EPS_M_ORIGIN2 ) { MSG_WriteShort( to->pmove.origin[2] ); } if( pflags & PS_M_VELOCITY ) { MSG_WriteShort( to->pmove.velocity[0] ); MSG_WriteShort( to->pmove.velocity[1] ); } if( extraflags & EPS_M_VELOCITY2 ) { MSG_WriteShort( to->pmove.velocity[2] ); } if( pflags & PS_M_TIME ) { MSG_WriteByte( to->pmove.pm_time ); } if( pflags & PS_M_FLAGS ) { MSG_WriteByte( to->pmove.pm_flags ); } if( pflags & PS_M_GRAVITY ) { MSG_WriteShort( to->pmove.gravity ); } if( pflags & PS_M_DELTA_ANGLES ) { MSG_WriteShort( to->pmove.delta_angles[0] ); MSG_WriteShort( to->pmove.delta_angles[1] ); MSG_WriteShort( to->pmove.delta_angles[2] ); } // // write the rest of the player_state_t // if( pflags & PS_VIEWOFFSET ) { MSG_WriteChar( to->viewoffset[0] * 4 ); MSG_WriteChar( to->viewoffset[1] * 4 ); MSG_WriteChar( to->viewoffset[2] * 4 ); } if( pflags & PS_VIEWANGLES ) { MSG_WriteAngle16( to->viewangles[0] ); MSG_WriteAngle16( to->viewangles[1] ); } if( extraflags & EPS_VIEWANGLE2 ) { MSG_WriteAngle16( to->viewangles[2] ); } if( pflags & PS_KICKANGLES ) { MSG_WriteChar( to->kick_angles[0] * 4 ); MSG_WriteChar( to->kick_angles[1] * 4 ); MSG_WriteChar( to->kick_angles[2] * 4 ); } if( pflags & PS_WEAPONINDEX ) { MSG_WriteByte( to->gunindex ); } if( pflags & PS_WEAPONFRAME ) { MSG_WriteByte( to->gunframe ); } if( extraflags & EPS_GUNOFFSET ) { MSG_WriteChar( to->gunoffset[0] * 4 ); MSG_WriteChar( to->gunoffset[1] * 4 ); MSG_WriteChar( to->gunoffset[2] * 4 ); } if( extraflags & EPS_GUNANGLES ) { MSG_WriteChar( to->gunangles[0] * 4 ); MSG_WriteChar( to->gunangles[1] * 4 ); MSG_WriteChar( to->gunangles[2] * 4 ); } if( pflags & PS_BLEND ) { MSG_WriteByte( to->blend[0] * 255 ); MSG_WriteByte( to->blend[1] * 255 ); MSG_WriteByte( to->blend[2] * 255 ); MSG_WriteByte( to->blend[3] * 255 ); } if( pflags & PS_FOV ) MSG_WriteByte( to->fov ); if( pflags & PS_RDFLAGS ) MSG_WriteByte( to->rdflags ); // send stats if( extraflags & EPS_STATS ) { MSG_WriteLong( statbits ); for( i = 0; i < MAX_STATS; i++ ) { if( statbits & ( 1 << i ) ) { MSG_WriteShort( to->stats[i] ); } } } return extraflags; } #if USE_MVD_SERVER || USE_MVD_CLIENT /* ================== MSG_WriteDeltaPlayerstate_Packet Throw away most of the pmove_state_t fields as they are used only for client prediction, and not needed in MVDs. ================== */ void MSG_WriteDeltaPlayerstate_Packet( const player_state_t *from, const player_state_t *to, int number, msgPsFlags_t flags ) { int i; int pflags; 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 // pflags = 0; if( to->pmove.pm_type != from->pmove.pm_type ) pflags |= PPS_M_TYPE; if( to->pmove.origin[0] != from->pmove.origin[0] || to->pmove.origin[1] != from->pmove.origin[1] ) { pflags |= PPS_M_ORIGIN; } if( to->pmove.origin[2] != from->pmove.origin[2] ) { pflags |= PPS_M_ORIGIN2; } if( delta_ofs_v( from->viewoffset, to->viewoffset ) ) { pflags |= PPS_VIEWOFFSET; } if( delta_angle16( from->viewangles[0], to->viewangles[0] ) || delta_angle16( from->viewangles[1], to->viewangles[1] ) ) { pflags |= PPS_VIEWANGLES; } if( delta_angle16( from->viewangles[2], to->viewangles[2] ) ) { pflags |= PPS_VIEWANGLE2; } if( delta_ofs_v( from->kick_angles, to->kick_angles ) ) { pflags |= PPS_KICKANGLES; } if( !( flags & MSG_PS_IGNORE_BLEND ) ) { if( delta_blend_v( from->blend, to->blend ) ) { pflags |= PPS_BLEND; } } if( delta_fov( from->fov, to->fov ) ) pflags |= PPS_FOV; if( to->rdflags != from->rdflags ) pflags |= PPS_RDFLAGS; if( !( flags & MSG_PS_IGNORE_GUNINDEX ) ) { if( to->gunindex != from->gunindex ) pflags |= PPS_WEAPONINDEX; } if( !( flags & MSG_PS_IGNORE_GUNFRAMES ) ) { if( to->gunframe != from->gunframe ) pflags |= PPS_WEAPONFRAME; if( delta_ofs_v( from->gunoffset, to->gunoffset ) ) { pflags |= PPS_GUNOFFSET; } if( delta_ofs_v( from->gunangles, to->gunangles ) ) { pflags |= PPS_GUNANGLES; } } statbits = 0; for( i = 0; i < MAX_STATS; i++ ) { if( to->stats[i] != from->stats[i] ) { statbits |= 1 << i; pflags |= PPS_STATS; } } if( !pflags && !( flags & MSG_PS_FORCE ) ) { return; } if( flags & MSG_PS_REMOVE ) { pflags |= PPS_REMOVE; // used for MVD stream only } // // write it // MSG_WriteByte( number ); MSG_WriteShort( pflags ); // // write some part of the pmove_state_t // if( pflags & PPS_M_TYPE ) MSG_WriteByte( to->pmove.pm_type ); if( pflags & PPS_M_ORIGIN ) { MSG_WriteShort( to->pmove.origin[0] ); MSG_WriteShort( to->pmove.origin[1] ); } if( pflags & PPS_M_ORIGIN2 ) { MSG_WriteShort( to->pmove.origin[2] ); } // // write the rest of the player_state_t // if( pflags & PPS_VIEWOFFSET ) { MSG_WriteChar( to->viewoffset[0] * 4 ); MSG_WriteChar( to->viewoffset[1] * 4 ); MSG_WriteChar( to->viewoffset[2] * 4 ); } if( pflags & PPS_VIEWANGLES ) { MSG_WriteAngle16( to->viewangles[0] ); MSG_WriteAngle16( to->viewangles[1] ); } if( pflags & PPS_VIEWANGLE2 ) { MSG_WriteAngle16( to->viewangles[2] ); } if( pflags & PPS_KICKANGLES ) { MSG_WriteChar( to->kick_angles[0] * 4 ); MSG_WriteChar( to->kick_angles[1] * 4 ); MSG_WriteChar( to->kick_angles[2] * 4 ); } if( pflags & PPS_WEAPONINDEX ) { MSG_WriteByte( to->gunindex ); } if( pflags & PPS_WEAPONFRAME ) { MSG_WriteByte( to->gunframe ); } if( pflags & PPS_GUNOFFSET ) { MSG_WriteChar( to->gunoffset[0] * 4 ); MSG_WriteChar( to->gunoffset[1] * 4 ); MSG_WriteChar( to->gunoffset[2] * 4 ); } if( pflags & PPS_GUNANGLES ) { MSG_WriteChar( to->gunangles[0] * 4 ); MSG_WriteChar( to->gunangles[1] * 4 ); MSG_WriteChar( to->gunangles[2] * 4 ); } if( pflags & PPS_BLEND ) { MSG_WriteByte( to->blend[0] * 255 ); MSG_WriteByte( to->blend[1] * 255 ); MSG_WriteByte( to->blend[2] * 255 ); MSG_WriteByte( to->blend[3] * 255 ); } if( pflags & PPS_FOV ) MSG_WriteByte( to->fov ); if( pflags & PPS_RDFLAGS ) MSG_WriteByte( to->rdflags ); // send stats if( pflags & 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 /* ============= MSG_FlushTo ============= */ void MSG_FlushTo( sizebuf_t *dest ) { memcpy( SZ_GetSpace( dest, msg_write.cursize ), msg_write.data, msg_write.cursize ); SZ_Clear( &msg_write ); } #if 0 // NOTE: does not NUL-terminate the string void MSG_Printf( const char *fmt, ... ) { char buffer[MAX_STRING_CHARS]; va_list argptr; size_t len; va_start( argptr, fmt ); len = Q_vsnprintf( buffer, sizeof( buffer ), fmt, argptr ); va_end( argptr ); if( len >= sizeof( buffer ) ) { Com_WPrintf( "%s: overflow\n", __func__ ); return; } MSG_WriteData( buffer, len ); } #endif //============================================================ // // reading functions // 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 ) { if( to != from ) { memcpy( to, from, sizeof( *to ) ); } VectorCopy( from->origin, to->old_origin ); } else { memset( to, 0, sizeof( *to ) ); from = &nullEntityState; } to->number = number; to->event = 0; if( !bits ) { return; } if( bits & U_MODEL ) { to->modelindex = MSG_ReadByte(); } if( bits & U_MODEL2 ) { to->modelindex2 = MSG_ReadByte(); } if( bits & U_MODEL3 ) { to->modelindex3 = MSG_ReadByte(); } if( bits & U_MODEL4 ) { to->modelindex4 = MSG_ReadByte(); } if( bits & U_FRAME8 ) to->frame = MSG_ReadByte(); if( bits & U_FRAME16 ) to->frame = MSG_ReadShort(); if( (bits & (U_SKIN8|U_SKIN16)) == (U_SKIN8|U_SKIN16) ) //used for laser colors to->skinnum = MSG_ReadLong(); else if( bits & U_SKIN8 ) to->skinnum = MSG_ReadByte(); else if( bits & U_SKIN16 ) to->skinnum = MSG_ReadWord(); if( (bits & (U_EFFECTS8|U_EFFECTS16)) == (U_EFFECTS8|U_EFFECTS16) ) to->effects = MSG_ReadLong(); else if( bits & U_EFFECTS8 ) to->effects = MSG_ReadByte(); else if( bits & U_EFFECTS16 ) to->effects = MSG_ReadWord(); if( (bits & (U_RENDERFX8|U_RENDERFX16)) == (U_RENDERFX8|U_RENDERFX16) ) to->renderfx = MSG_ReadLong(); else if( bits & U_RENDERFX8 ) to->renderfx = MSG_ReadByte(); else if( bits & U_RENDERFX16 ) to->renderfx = MSG_ReadWord(); if( bits & U_ORIGIN1 ) { to->origin[0] = MSG_ReadCoord(); } if( bits & U_ORIGIN2 ) { to->origin[1] = MSG_ReadCoord(); } if( bits & U_ORIGIN3 ) { to->origin[2] = MSG_ReadCoord(); } if( bits & U_ANGLE1 ) { to->angles[0] = MSG_ReadAngle(); } if( bits & U_ANGLE2 ) { to->angles[1] = MSG_ReadAngle(); } if( bits & U_ANGLE3 ) { to->angles[2] = MSG_ReadAngle(); } if( bits & U_OLDORIGIN ) { MSG_ReadPos( to->old_origin ); } if( bits & U_SOUND ) { to->sound = MSG_ReadByte(); } if( bits & U_EVENT ) { to->event = MSG_ReadByte(); } 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 flags ) { int i; int statbits; if( !to ) { Com_Error( ERR_DROP, "%s: NULL", __func__ ); } // clear to old value before delta parsing if( from ) { memcpy( to, from, sizeof( *to ) ); } else { memset( to, 0, sizeof( *to ) ); } // // parse the pmove_state_t // if( flags & PS_M_TYPE ) to->pmove.pm_type = MSG_ReadByte(); if( flags & PS_M_ORIGIN ) { to->pmove.origin[0] = MSG_ReadShort(); to->pmove.origin[1] = MSG_ReadShort(); to->pmove.origin[2] = MSG_ReadShort(); } if( flags & PS_M_VELOCITY ) { to->pmove.velocity[0] = MSG_ReadShort(); to->pmove.velocity[1] = MSG_ReadShort(); to->pmove.velocity[2] = MSG_ReadShort(); } if( flags & PS_M_TIME ) to->pmove.pm_time = MSG_ReadByte(); if( flags & PS_M_FLAGS ) to->pmove.pm_flags = MSG_ReadByte(); if( flags & PS_M_GRAVITY ) to->pmove.gravity = MSG_ReadShort(); if( flags & PS_M_DELTA_ANGLES ) { to->pmove.delta_angles[0] = MSG_ReadShort(); to->pmove.delta_angles[1] = MSG_ReadShort(); to->pmove.delta_angles[2] = MSG_ReadShort(); } // // parse the rest of the player_state_t // if( flags & PS_VIEWOFFSET ) { to->viewoffset[0] = MSG_ReadChar() * 0.25f; to->viewoffset[1] = MSG_ReadChar() * 0.25f; to->viewoffset[2] = MSG_ReadChar() * 0.25f; } if( flags & PS_VIEWANGLES ) { to->viewangles[0] = MSG_ReadAngle16(); to->viewangles[1] = MSG_ReadAngle16(); to->viewangles[2] = MSG_ReadAngle16(); } if( flags & PS_KICKANGLES ) { to->kick_angles[0] = MSG_ReadChar() * 0.25f; to->kick_angles[1] = MSG_ReadChar() * 0.25f; to->kick_angles[2] = MSG_ReadChar() * 0.25f; } if( flags & PS_WEAPONINDEX ) { to->gunindex = MSG_ReadByte(); } if( flags & PS_WEAPONFRAME ) { to->gunframe = MSG_ReadByte(); to->gunoffset[0] = MSG_ReadChar() * 0.25f; to->gunoffset[1] = MSG_ReadChar() * 0.25f; to->gunoffset[2] = MSG_ReadChar() * 0.25f; to->gunangles[0] = MSG_ReadChar() * 0.25f; to->gunangles[1] = MSG_ReadChar() * 0.25f; to->gunangles[2] = MSG_ReadChar() * 0.25f; } if( flags & PS_BLEND ) { to->blend[0] = MSG_ReadByte() / 255.0f; to->blend[1] = MSG_ReadByte() / 255.0f; to->blend[2] = MSG_ReadByte() / 255.0f; to->blend[3] = MSG_ReadByte() / 255.0f; } if( flags & PS_FOV ) to->fov = MSG_ReadByte(); if( flags & PS_RDFLAGS ) to->rdflags = MSG_ReadByte(); // 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 flags, int extraflags ) { int i; int statbits; if( !to ) { Com_Error( ERR_DROP, "%s: NULL", __func__ ); } // clear to old value before delta parsing if( from ) { memcpy( to, from, sizeof( *to ) ); } else { memset( to, 0, sizeof( *to ) ); } // // parse the pmove_state_t // if( flags & PS_M_TYPE ) to->pmove.pm_type = MSG_ReadByte(); if( flags & PS_M_ORIGIN ) { to->pmove.origin[0] = MSG_ReadShort(); to->pmove.origin[1] = MSG_ReadShort(); } if( extraflags & EPS_M_ORIGIN2 ) { to->pmove.origin[2] = MSG_ReadShort(); } if( flags & PS_M_VELOCITY ) { to->pmove.velocity[0] = MSG_ReadShort(); to->pmove.velocity[1] = MSG_ReadShort(); } if( extraflags & EPS_M_VELOCITY2 ) { to->pmove.velocity[2] = MSG_ReadShort(); } if( flags & PS_M_TIME ) to->pmove.pm_time = MSG_ReadByte(); if( flags & PS_M_FLAGS ) to->pmove.pm_flags = MSG_ReadByte(); if( flags & PS_M_GRAVITY ) to->pmove.gravity = MSG_ReadShort(); if( flags & PS_M_DELTA_ANGLES ) { to->pmove.delta_angles[0] = MSG_ReadShort(); to->pmove.delta_angles[1] = MSG_ReadShort(); to->pmove.delta_angles[2] = MSG_ReadShort(); } // // parse the rest of the player_state_t // if( flags & PS_VIEWOFFSET ) { to->viewoffset[0] = MSG_ReadChar() * 0.25f; to->viewoffset[1] = MSG_ReadChar() * 0.25f; to->viewoffset[2] = MSG_ReadChar() * 0.25f; } if( flags & PS_VIEWANGLES ) { to->viewangles[0] = MSG_ReadAngle16(); to->viewangles[1] = MSG_ReadAngle16(); } if( extraflags & EPS_VIEWANGLE2 ) { to->viewangles[2] = MSG_ReadAngle16(); } if( flags & PS_KICKANGLES ) { to->kick_angles[0] = MSG_ReadChar() * 0.25f; to->kick_angles[1] = MSG_ReadChar() * 0.25f; to->kick_angles[2] = MSG_ReadChar() * 0.25f; } if( flags & PS_WEAPONINDEX ) { to->gunindex = MSG_ReadByte(); } if( flags & PS_WEAPONFRAME ) { to->gunframe = MSG_ReadByte(); } if( extraflags & EPS_GUNOFFSET ) { to->gunoffset[0] = MSG_ReadChar() * 0.25f; to->gunoffset[1] = MSG_ReadChar() * 0.25f; to->gunoffset[2] = MSG_ReadChar() * 0.25f; } if( extraflags & EPS_GUNANGLES ) { to->gunangles[0] = MSG_ReadChar() * 0.25f; to->gunangles[1] = MSG_ReadChar() * 0.25f; to->gunangles[2] = MSG_ReadChar() * 0.25f; } if( flags & PS_BLEND ) { to->blend[0] = MSG_ReadByte() / 255.0f; to->blend[1] = MSG_ReadByte() / 255.0f; to->blend[2] = MSG_ReadByte() / 255.0f; to->blend[3] = MSG_ReadByte() / 255.0f; } if( flags & PS_FOV ) to->fov = MSG_ReadByte(); if( flags & PS_RDFLAGS ) to->rdflags = MSG_ReadByte(); // parse stats if( extraflags & 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 flags ) { int i; int statbits; if( !to ) { Com_Error( ERR_DROP, "%s: NULL", __func__ ); } // clear to old value before delta parsing if( from ) { memcpy( to, from, sizeof( *to ) ); } else { memset( to, 0, sizeof( *to ) ); } // // parse the pmove_state_t // if( flags & PPS_M_TYPE ) to->pmove.pm_type = MSG_ReadByte(); if( flags & PPS_M_ORIGIN ) { to->pmove.origin[0] = MSG_ReadShort(); to->pmove.origin[1] = MSG_ReadShort(); } if( flags & PPS_M_ORIGIN2 ) { to->pmove.origin[2] = MSG_ReadShort(); } // // parse the rest of the player_state_t // if( flags & PPS_VIEWOFFSET ) { to->viewoffset[0] = MSG_ReadChar() * 0.25f; to->viewoffset[1] = MSG_ReadChar() * 0.25f; to->viewoffset[2] = MSG_ReadChar() * 0.25f; } if( flags & PPS_VIEWANGLES ) { to->viewangles[0] = MSG_ReadAngle16(); to->viewangles[1] = MSG_ReadAngle16(); } if( flags & PPS_VIEWANGLE2 ) { to->viewangles[2] = MSG_ReadAngle16(); } if( flags & PPS_KICKANGLES ) { to->kick_angles[0] = MSG_ReadChar() * 0.25f; to->kick_angles[1] = MSG_ReadChar() * 0.25f; to->kick_angles[2] = MSG_ReadChar() * 0.25f; } if( flags & PPS_WEAPONINDEX ) { to->gunindex = MSG_ReadByte(); } if( flags & PPS_WEAPONFRAME ) { to->gunframe = MSG_ReadByte(); } if( flags & PPS_GUNOFFSET ) { to->gunoffset[0] = MSG_ReadChar() * 0.25f; to->gunoffset[1] = MSG_ReadChar() * 0.25f; to->gunoffset[2] = MSG_ReadChar() * 0.25f; } if( flags & PPS_GUNANGLES ) { to->gunangles[0] = MSG_ReadChar() * 0.25f; to->gunangles[1] = MSG_ReadChar() * 0.25f; to->gunangles[2] = MSG_ReadChar() * 0.25f; } if( flags & PPS_BLEND ) { to->blend[0] = MSG_ReadByte() / 255.0f; to->blend[1] = MSG_ReadByte() / 255.0f; to->blend[2] = MSG_ReadByte() / 255.0f; to->blend[3] = MSG_ReadByte() / 255.0f; } if( flags & PPS_FOV ) to->fov = MSG_ReadByte(); if( flags & PPS_RDFLAGS ) to->rdflags = MSG_ReadByte(); // parse stats if( flags & 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 #ifdef _DEBUG #define SHOWBITS( data ) \ do { Com_Printf( "%s ", data ); } while( 0 ) #if USE_CLIENT void MSG_ShowDeltaPlayerstateBits_Default( int flags ) { if( flags & PS_M_TYPE ) { SHOWBITS( "pmove.pm_type" ); } if( flags & PS_M_ORIGIN ) { SHOWBITS( "pmove.origin" ); } if( flags & PS_M_VELOCITY ) { SHOWBITS( "pmove.velocity" ); } if( flags & PS_M_TIME ) { SHOWBITS( "pmove.pm_time" ); } if( flags & PS_M_FLAGS ) { SHOWBITS( "pmove.pm_flags" ); } if( flags & PS_M_GRAVITY ) { SHOWBITS( "pmove.gravity" ); } if( flags & PS_M_DELTA_ANGLES ) { SHOWBITS( "pmove.delta_angles" ); } if( flags & PS_VIEWOFFSET ) { SHOWBITS( "viewoffset" ); } if( flags & PS_VIEWANGLES ) { SHOWBITS( "viewangles" ); } if( flags & PS_KICKANGLES ) { SHOWBITS( "kick_angles" ); } if( flags & PS_WEAPONINDEX ) { SHOWBITS( "gunindex" ); } if( flags & PS_WEAPONFRAME ) { SHOWBITS( "gunframe" ); } if( flags & PS_BLEND ) { SHOWBITS( "blend" ); } if( flags & PS_FOV ) { SHOWBITS( "fov" ); } if( flags & PS_RDFLAGS ) { SHOWBITS( "rdflags" ); } } void MSG_ShowDeltaPlayerstateBits_Enhanced( int flags ) { int extraflags; extraflags = flags >> PS_BITS; flags &= PS_MASK; if( flags & PS_M_TYPE ) { SHOWBITS( "pmove.pm_type" ); } if( flags & PS_M_ORIGIN ) { SHOWBITS( "pmove.origin[0,1]" ); } if( extraflags & EPS_M_ORIGIN2 ) { SHOWBITS( "pmove.origin[2]" ); } if( flags & PS_M_VELOCITY ) { SHOWBITS( "pmove.velocity[0,1]" ); } if( extraflags & EPS_M_VELOCITY2 ) { SHOWBITS( "pmove.velocity[2]" ); } if( flags & PS_M_TIME ) { SHOWBITS( "pmove.pm_time" ); } if( flags & PS_M_FLAGS ) { SHOWBITS( "pmove.pm_flags" ); } if( flags & PS_M_GRAVITY ) { SHOWBITS( "pmove.gravity" ); } if( flags & PS_M_DELTA_ANGLES ) { SHOWBITS( "pmove.delta_angles" ); } if( flags & PS_VIEWOFFSET ) { SHOWBITS( "viewoffset" ); } if( flags & PS_VIEWANGLES ) { SHOWBITS( "viewangles[0,1]" ); } if( extraflags & EPS_VIEWANGLE2 ) { SHOWBITS( "viewangles[2]" ); } if( flags & PS_KICKANGLES ) { SHOWBITS( "kick_angles" ); } if( flags & PS_WEAPONINDEX ) { SHOWBITS( "gunindex" ); } if( flags & PS_WEAPONFRAME ) { SHOWBITS( "gunframe" ); } if( extraflags & EPS_GUNOFFSET ) { SHOWBITS( "gunoffset" ); } if( extraflags & EPS_GUNANGLES ) { SHOWBITS( "gunangles" ); } if( flags & PS_BLEND ) { SHOWBITS( "blend" ); } if( flags & PS_FOV ) { SHOWBITS( "fov" ); } if( flags & PS_RDFLAGS ) { SHOWBITS( "rdflags" ); } if( extraflags & EPS_STATS ) { SHOWBITS( "stats" ); } } void MSG_ShowDeltaUsercmdBits_Enhanced( int bits ) { if( !bits ) { SHOWBITS( "" ); return; } if( bits & CM_ANGLE1 ) SHOWBITS( "angle1" ); if( bits & CM_ANGLE2 ) SHOWBITS( "angle2" ); if( bits & CM_ANGLE3 ) SHOWBITS( "angle3" ); if( bits & CM_FORWARD ) SHOWBITS( "forward" ); if( bits & CM_SIDE ) SHOWBITS( "side" ); if( bits & CM_UP ) SHOWBITS( "up" ); if( bits & CM_BUTTONS ) SHOWBITS( "buttons" ); if( bits & CM_IMPULSE ) SHOWBITS( "msec" ); } #endif // USE_CLIENT #if USE_CLIENT || USE_MVD_CLIENT void MSG_ShowDeltaEntityBits( int bits ) { if( bits & U_MODEL ) { SHOWBITS( "modelindex" ); } if( bits & U_MODEL2 ) { SHOWBITS( "modelindex2" ); } if( bits & U_MODEL3 ) { SHOWBITS( "modelindex3" ); } if( bits & U_MODEL4 ) { SHOWBITS( "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" ); if( bits & U_ORIGIN1 ) { SHOWBITS( "origin[0]" ); } if( bits & U_ORIGIN2 ) { SHOWBITS( "origin[1]" ); } if( bits & U_ORIGIN3 ) { SHOWBITS( "origin[2]" ); } if( bits & U_ANGLE1 ) { SHOWBITS( "angles[0]" ); } if( bits & U_ANGLE2 ) { SHOWBITS( "angles[2]" ); } if( bits & U_ANGLE3 ) { SHOWBITS( "angles[3]" ); } if( bits & U_OLDORIGIN ) { SHOWBITS( "old_origin" ); } if( bits & U_SOUND ) { SHOWBITS( "sound" ); } if( bits & U_EVENT ) { SHOWBITS( "event" ); } if( bits & U_SOLID ) { SHOWBITS( "solid" ); } } void MSG_ShowDeltaPlayerstateBits_Packet( int flags ) { if( flags & PPS_M_TYPE ) { SHOWBITS( "pmove.pm_type" ); } if( flags & PPS_M_ORIGIN ) { SHOWBITS( "pmove.origin[0,1]" ); } if( flags & PPS_M_ORIGIN2 ) { SHOWBITS( "pmove.origin[2]" ); } if( flags & PPS_VIEWOFFSET ) { SHOWBITS( "viewoffset" ); } if( flags & PPS_VIEWANGLES ) { SHOWBITS( "viewangles[0,1]" ); } if( flags & PPS_VIEWANGLE2 ) { SHOWBITS( "viewangles[2]" ); } if( flags & PPS_KICKANGLES ) { SHOWBITS( "kick_angles" ); } if( flags & PPS_WEAPONINDEX ) { SHOWBITS( "gunindex" ); } if( flags & PPS_WEAPONFRAME ) { SHOWBITS( "gunframe" ); } if( flags & PPS_GUNOFFSET ) { SHOWBITS( "gunoffset" ); } if( flags & PPS_GUNANGLES ) { SHOWBITS( "gunangles" ); } if( flags & PPS_BLEND ) { SHOWBITS( "blend" ); } if( flags & PPS_FOV ) { SHOWBITS( "fov" ); } if( flags & PPS_RDFLAGS ) { SHOWBITS( "rdflags" ); } if( flags & PPS_STATS ) { SHOWBITS( "stats" ); } } 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) } } #endif // USE_CLIENT || USE_MVD_CLIENT #endif // _DEBUG //=========================================================================== void SZ_TagInit( sizebuf_t *buf, void *data, size_t length, uint32_t tag ) { memset( buf, 0, sizeof( *buf ) ); buf->data = data; buf->maxsize = length; buf->tag = tag; } void SZ_Init( sizebuf_t *buf, void *data, size_t length ) { memset( buf, 0, sizeof( *buf ) ); buf->data = data; buf->maxsize = length; buf->allowoverflow = qtrue; buf->allowunderflow = qtrue; } void SZ_Clear( sizebuf_t *buf ) { buf->cursize = 0; buf->readcount = 0; buf->bitpos = 0; buf->overflowed = qfalse; } void *SZ_GetSpace( sizebuf_t *buf, size_t length ) { void *data; if( buf->cursize + length > buf->maxsize ) { if( !buf->allowoverflow ) { Com_Error( ERR_FATAL, "%s: %#x: overflow without allowoverflow set", __func__, buf->tag ); } if( length > buf->maxsize ) { Com_Error( ERR_FATAL, "%s: %#x: %"PRIz" is > full buffer size %"PRIz"", __func__, buf->tag, length, buf->maxsize ); } //Com_DPrintf( "%s: %#x: overflow\n", __func__, buf->tag ); SZ_Clear( buf ); buf->overflowed = qtrue; } data = buf->data + buf->cursize; buf->cursize += length; buf->bitpos = buf->cursize << 3; return data; } void SZ_WriteByte( sizebuf_t *sb, int c ) { byte *buf; buf = SZ_GetSpace( sb, 1 ); buf[0] = c; } void SZ_WriteShort( sizebuf_t *sb, int c ) { byte *buf; buf = SZ_GetSpace( sb, 2 ); buf[0] = c & 0xff; buf[1] = c >> 8; } void SZ_WriteLong( sizebuf_t *sb, int c ) { byte *buf; buf = SZ_GetSpace( sb, 4 ); buf[0] = c & 0xff; buf[1] = ( c >> 8 ) & 0xff; buf[2] = ( c >> 16 ) & 0xff; buf[3] = c >> 24; } #if USE_MVD_SERVER void SZ_WriteString( sizebuf_t *sb, const char *string ) { size_t length; if( !string ) { SZ_WriteByte( sb, 0 ); return; } length = strlen( string ); if( length >= MAX_NET_STRING ) { Com_WPrintf( "%s: overflow: %"PRIz" chars", __func__, length ); SZ_WriteByte( sb, 0 ); return; } SZ_Write( sb, string, length + 1 ); } #endif