Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

msg.c File Reference

#include "../game/q_shared.h"
#include "qcommon.h"

Include dependency graph for msg.c:

Include dependency graph

Go to the source code of this file.

Data Structures

struct  netField_t

Defines

#define CM_ANGLE1   (1<<0)
#define CM_ANGLE2   (1<<1)
#define CM_ANGLE3   (1<<2)
#define CM_BUTTONS   (1<<6)
#define CM_FORWARD   (1<<3)
#define CM_SIDE   (1<<4)
#define CM_UP   (1<<5)
#define CM_WEAPON   (1<<7)
#define FLOAT_INT_BIAS   (1<<(FLOAT_INT_BITS-1))
#define FLOAT_INT_BITS   13
#define LOG(x)   if( cl_shownet->integer == 4 ) { Com_Printf("%s ", x ); };
#define NETF(x)   #x,(int)&((entityState_t*)0)->x
#define PSF(x)   #x,(int)&((playerState_t*)0)->x

Functions

void MSG_BeginReading (msg_t *msg)
void MSG_BeginReadingOOB (msg_t *msg)
void MSG_Bitstream (msg_t *buf)
void MSG_Clear (msg_t *buf)
void MSG_Copy (msg_t *buf, byte *data, int length, msg_t *src)
void MSG_Init (msg_t *buf, byte *data, int length)
void MSG_initHuffman ()
void MSG_InitOOB (msg_t *buf, byte *data, int length)
float MSG_ReadAngle16 (msg_t *msg)
char * MSG_ReadBigString (msg_t *msg)
int MSG_ReadBits (msg_t *msg, int bits)
int MSG_ReadByte (msg_t *msg)
int MSG_ReadChar (msg_t *msg)
void MSG_ReadData (msg_t *msg, void *data, int len)
int MSG_ReadDelta (msg_t *msg, int oldV, int bits)
void MSG_ReadDeltaEntity (msg_t *msg, entityState_t *from, entityState_t *to, int number)
float MSG_ReadDeltaFloat (msg_t *msg, float oldV)
int MSG_ReadDeltaKey (msg_t *msg, int key, int oldV, int bits)
float MSG_ReadDeltaKeyFloat (msg_t *msg, int key, float oldV)
void MSG_ReadDeltaPlayerstate (msg_t *msg, playerState_t *from, playerState_t *to)
void MSG_ReadDeltaUsercmd (msg_t *msg, usercmd_t *from, usercmd_t *to)
void MSG_ReadDeltaUsercmdKey (msg_t *msg, int key, usercmd_t *from, usercmd_t *to)
float MSG_ReadFloat (msg_t *msg)
int MSG_ReadLong (msg_t *msg)
int MSG_ReadShort (msg_t *msg)
char * MSG_ReadString (msg_t *msg)
char * MSG_ReadStringLine (msg_t *msg)
void MSG_ReportChangeVectors_f (void)
void MSG_WriteAngle (msg_t *sb, float f)
void MSG_WriteAngle16 (msg_t *sb, float f)
void MSG_WriteBigString (msg_t *sb, const char *s)
void MSG_WriteBits (msg_t *msg, int value, int bits)
void MSG_WriteByte (msg_t *sb, int c)
void MSG_WriteChar (msg_t *sb, int c)
void MSG_WriteData (msg_t *buf, const void *data, int length)
void MSG_WriteDelta (msg_t *msg, int oldV, int newV, int bits)
void MSG_WriteDeltaEntity (msg_t *msg, struct entityState_s *from, struct entityState_s *to, qboolean force)
void MSG_WriteDeltaFloat (msg_t *msg, float oldV, float newV)
void MSG_WriteDeltaKey (msg_t *msg, int key, int oldV, int newV, int bits)
void MSG_WriteDeltaKeyFloat (msg_t *msg, int key, float oldV, float newV)
void MSG_WriteDeltaPlayerstate (msg_t *msg, struct playerState_s *from, struct playerState_s *to)
void MSG_WriteDeltaUsercmd (msg_t *msg, usercmd_t *from, usercmd_t *to)
void MSG_WriteDeltaUsercmdKey (msg_t *msg, int key, usercmd_t *from, usercmd_t *to)
void MSG_WriteFloat (msg_t *sb, float f)
void MSG_WriteLong (msg_t *sb, int c)
void MSG_WriteShort (msg_t *sb, int c)
void MSG_WriteString (msg_t *sb, const char *s)

Variables

cvar_tcl_shownet
netField_t entityStateFields []
int kbitmask [32]
int msg_hData [256]
huffman_t msgHuff
qboolean msgInit = qfalse
int oldsize = 0
int overflows
int pcount [256]
netField_t playerStateFields []


Define Documentation

#define CM_ANGLE1   (1<<0)
 

Definition at line 628 of file msg.c.

#define CM_ANGLE2   (1<<1)
 

Definition at line 629 of file msg.c.

#define CM_ANGLE3   (1<<2)
 

Definition at line 630 of file msg.c.

#define CM_BUTTONS   (1<<6)
 

Definition at line 634 of file msg.c.

#define CM_FORWARD   (1<<3)
 

Definition at line 631 of file msg.c.

#define CM_SIDE   (1<<4)
 

Definition at line 632 of file msg.c.

#define CM_UP   (1<<5)
 

Definition at line 633 of file msg.c.

#define CM_WEAPON   (1<<7)
 

Definition at line 635 of file msg.c.

#define FLOAT_INT_BIAS   (1<<(FLOAT_INT_BITS-1))
 

Definition at line 845 of file msg.c.

Referenced by MSG_WriteDeltaEntity(), and MSG_WriteDeltaPlayerstate().

#define FLOAT_INT_BITS   13
 

Definition at line 844 of file msg.c.

Referenced by MSG_ReadDeltaEntity(), MSG_ReadDeltaPlayerstate(), MSG_WriteDeltaEntity(), and MSG_WriteDeltaPlayerstate().

#define LOG  )     if( cl_shownet->integer == 4 ) { Com_Printf("%s ", x ); };
 

Definition at line 527 of file msg.c.

Referenced by main(), and MSG_ReadDeltaPlayerstate().

#define NETF  )     #x,(int)&((entityState_t*)0)->x
 

Definition at line 784 of file msg.c.

#define PSF  )     #x,(int)&((playerState_t*)0)->x
 

Definition at line 1099 of file msg.c.


Function Documentation

void MSG_BeginReading msg_t msg  ) 
 

Definition at line 74 of file msg.c.

References msg_t::bit, msg_t::oob, and msg_t::readcount.

00074                                     {
00075     msg->readcount = 0;
00076     msg->bit = 0;
00077     msg->oob = qfalse;
00078 }

void MSG_BeginReadingOOB msg_t msg  ) 
 

Definition at line 80 of file msg.c.

References msg_t::bit, msg_t::oob, and msg_t::readcount.

Referenced by CL_ConnectionlessPacket(), Netchan_Process(), SV_ConnectionlessPacket(), and SV_PacketEvent().

00080                                        {
00081     msg->readcount = 0;
00082     msg->bit = 0;
00083     msg->oob = qtrue;
00084 }

void MSG_Bitstream msg_t buf  ) 
 

Definition at line 70 of file msg.c.

References msg_t::oob.

Referenced by CL_ParseServerMessage(), CL_Record_f(), CL_WritePacket(), and SV_ExecuteClientMessage().

00070                                  {
00071     buf->oob = qfalse;
00072 }

void MSG_Clear msg_t buf  ) 
 

Definition at line 63 of file msg.c.

References msg_t::bit, msg_t::cursize, and msg_t::overflowed.

Referenced by SV_SendClientSnapshot().

00063                              {
00064     buf->cursize = 0;
00065     buf->overflowed = qfalse;
00066     buf->bit = 0;                   //<- in bits
00067 }

void MSG_Copy msg_t buf,
byte data,
int  length,
msg_t src
 

Definition at line 86 of file msg.c.

References byte, Com_Error(), Com_Memcpy(), msg_t::data, ERR_DROP, length(), and src.

Referenced by SV_Netchan_Transmit().

00087 {
00088     if (length<src->cursize) {
00089         Com_Error( ERR_DROP, "MSG_Copy: can't copy into a smaller msg_t buffer");
00090     }
00091     Com_Memcpy(buf, src, sizeof(msg_t));
00092     buf->data = data;
00093     Com_Memcpy(buf->data, src->data, src->cursize);
00094 }

Here is the call graph for this function:

void MSG_Init msg_t buf,
byte data,
int  length
 

Definition at line 44 of file msg.c.

References byte, Com_Memset(), and MSG_initHuffman().

Referenced by CL_ReadDemoMessage(), CL_Record_f(), CL_WritePacket(), Com_EventLoop(), SV_SendClientGameState(), SV_SendClientSnapshot(), and Sys_GetEvent().

00044                                                     {
00045     if (!msgInit) {
00046         MSG_initHuffman();
00047     }
00048     Com_Memset (buf, 0, sizeof(*buf));
00049     buf->data = data;
00050     buf->maxsize = length;
00051 }

Here is the call graph for this function:

void MSG_initHuffman  ) 
 

Definition at line 1709 of file msg.c.

References byte, huffman_t::compressor, huffman_t::decompressor, Huff_addRef(), Huff_Init(), i, j, msg_hData, msgHuff, and msgInit.

Referenced by MSG_Init(), and MSG_InitOOB().

01709                        {
01710     int i,j;
01711 
01712     msgInit = qtrue;
01713     Huff_Init(&msgHuff);
01714     for(i=0;i<256;i++) {
01715         for (j=0;j<msg_hData[i];j++) {
01716             Huff_addRef(&msgHuff.compressor,    (byte)i);           // Do update
01717             Huff_addRef(&msgHuff.decompressor,  (byte)i);           // Do update
01718         }
01719     }
01720 }

Here is the call graph for this function:

void MSG_InitOOB msg_t buf,
byte data,
int  length
 

Definition at line 53 of file msg.c.

References byte, Com_Memset(), and MSG_initHuffman().

Referenced by Netchan_Transmit(), and Netchan_TransmitNextFragment().

00053                                                        {
00054     if (!msgInit) {
00055         MSG_initHuffman();
00056     }
00057     Com_Memset (buf, 0, sizeof(*buf));
00058     buf->data = data;
00059     buf->maxsize = length;
00060     buf->oob = qtrue;
00061 }

Here is the call graph for this function:

float MSG_ReadAngle16 msg_t msg  ) 
 

Definition at line 504 of file msg.c.

References MSG_ReadShort(), and SHORT2ANGLE.

00504                                     {
00505     return SHORT2ANGLE(MSG_ReadShort(msg));
00506 }

Here is the call graph for this function:

char* MSG_ReadBigString msg_t msg  ) 
 

Definition at line 457 of file msg.c.

References c, l, MSG_ReadByte(), and string().

Referenced by CL_ParseGamestate().

00457                                       {
00458     static char string[BIG_INFO_STRING];
00459     int     l,c;
00460     
00461     l = 0;
00462     do {
00463         c = MSG_ReadByte(msg);      // use ReadByte so -1 is out of bounds
00464         if ( c == -1 || c == 0 ) {
00465             break;
00466         }
00467         // translate all fmt spec to avoid crash bugs
00468         if ( c == '%' ) {
00469             c = '.';
00470         }
00471 
00472         string[l] = c;
00473         l++;
00474     } while (l < sizeof(string)-1);
00475     
00476     string[l] = 0;
00477     
00478     return string;
00479 }

Here is the call graph for this function:

int MSG_ReadBits msg_t msg,
int  bits
 

Definition at line 184 of file msg.c.

References msg_t::bit, bits, Com_Error(), msg_t::data, huffman_t::decompressor, ERR_DROP, Huff_getBit(), Huff_offsetReceive(), i, LittleLong(), LittleShort(), msgHuff, msg_t::oob, qboolean, msg_t::readcount, huff_t::tree, and value.

Referenced by CL_ParseGamestate(), CL_ParsePacketEntities(), MSG_ReadByte(), MSG_ReadChar(), MSG_ReadDelta(), MSG_ReadDeltaEntity(), MSG_ReadDeltaFloat(), MSG_ReadDeltaKey(), MSG_ReadDeltaKeyFloat(), MSG_ReadDeltaPlayerstate(), MSG_ReadDeltaUsercmd(), MSG_ReadDeltaUsercmdKey(), MSG_ReadFloat(), MSG_ReadLong(), and MSG_ReadShort().

00184                                          {
00185     int         value;
00186     int         get;
00187     qboolean    sgn;
00188     int         i, nbits;
00189 //  FILE*   fp;
00190 
00191     value = 0;
00192 
00193     if ( bits < 0 ) {
00194         bits = -bits;
00195         sgn = qtrue;
00196     } else {
00197         sgn = qfalse;
00198     }
00199 
00200     if (msg->oob) {
00201         if (bits==8) {
00202             value = msg->data[msg->readcount];
00203             msg->readcount += 1;
00204             msg->bit += 8;
00205         } else if (bits==16) {
00206             unsigned short *sp = (unsigned short *)&msg->data[msg->readcount];
00207             value = LittleShort(*sp);
00208             msg->readcount += 2;
00209             msg->bit += 16;
00210         } else if (bits==32) {
00211             unsigned int *ip = (unsigned int *)&msg->data[msg->readcount];
00212             value = LittleLong(*ip);
00213             msg->readcount += 4;
00214             msg->bit += 32;
00215         } else {
00216             Com_Error(ERR_DROP, "can't read %d bits\n", bits);
00217         }
00218     } else {
00219         nbits = 0;
00220         if (bits&7) {
00221             nbits = bits&7;
00222             for(i=0;i<nbits;i++) {
00223                 value |= (Huff_getBit(msg->data, &msg->bit)<<i);
00224             }
00225             bits = bits - nbits;
00226         }
00227         if (bits) {
00228 //          fp = fopen("c:\\netchan.bin", "a");
00229             for(i=0;i<bits;i+=8) {
00230                 Huff_offsetReceive (msgHuff.decompressor.tree, &get, msg->data, &msg->bit);
00231 //              fwrite(&get, 1, 1, fp);
00232                 value |= (get<<(i+nbits));
00233             }
00234 //          fclose(fp);
00235         }
00236         msg->readcount = (msg->bit>>3)+1;
00237     }
00238     if ( sgn ) {
00239         if ( value & ( 1 << ( bits - 1 ) ) ) {
00240             value |= -1 ^ ( ( 1 << bits ) - 1 );
00241         }
00242     }
00243 
00244     return value;
00245 }

Here is the call graph for this function:

int MSG_ReadByte msg_t msg  ) 
 

Definition at line 382 of file msg.c.

References c, msg_t::cursize, MSG_ReadBits(), and msg_t::readcount.

Referenced by CL_ParseGamestate(), CL_ParseServerMessage(), CL_ParseSnapshot(), MSG_ReadBigString(), MSG_ReadData(), MSG_ReadDeltaEntity(), MSG_ReadDeltaPlayerstate(), MSG_ReadString(), MSG_ReadStringLine(), SV_ExecuteClientMessage(), and SV_UserMove().

00382                                {
00383     int c;
00384     
00385     c = (unsigned char)MSG_ReadBits( msg, 8 );
00386     if ( msg->readcount > msg->cursize ) {
00387         c = -1;
00388     }   
00389     return c;
00390 }

Here is the call graph for this function:

int MSG_ReadChar msg_t msg  ) 
 

Definition at line 371 of file msg.c.

References c, msg_t::cursize, MSG_ReadBits(), and msg_t::readcount.

00371                                {
00372     int c;
00373     
00374     c = (signed char)MSG_ReadBits( msg, 8 );
00375     if ( msg->readcount > msg->cursize ) {
00376         c = -1;
00377     }   
00378     
00379     return c;
00380 }

Here is the call graph for this function:

void MSG_ReadData msg_t msg,
void *  data,
int  len
 

Definition at line 508 of file msg.c.

References byte, i, and MSG_ReadByte().

Referenced by CL_ParseDownload(), and CL_ParseSnapshot().

00508                                                      {
00509     int     i;
00510 
00511     for (i=0 ; i<len ; i++) {
00512         ((byte *)data)[i] = MSG_ReadByte (msg);
00513     }
00514 }

Here is the call graph for this function:

int MSG_ReadDelta msg_t msg,
int  oldV,
int  bits
 

Definition at line 538 of file msg.c.

References bits, and MSG_ReadBits().

Referenced by MSG_ReadDeltaUsercmd().

00538                                                     {
00539     if ( MSG_ReadBits( msg, 1 ) ) {
00540         return MSG_ReadBits( msg, bits );
00541     }
00542     return oldV;
00543 }

Here is the call graph for this function:

void MSG_ReadDeltaEntity msg_t msg,
entityState_t from,
entityState_t to,
int  number
 

Definition at line 977 of file msg.c.

References msg_t::bit, byte, cl_shownet, Com_Error(), Com_Memset(), Com_Printf(), entityState_t, entityStateFields, ERR_DROP, field(), FLOAT_INT_BITS, i, cvar_s::integer, MAX_GENTITIES, MSG_ReadBits(), MSG_ReadByte(), number, entityState_s::number, print(), and msg_t::readcount.

Referenced by CL_DeltaEntity(), and CL_ParseGamestate().

00978                                      {
00979     int         i, lc;
00980     int         numFields;
00981     netField_t  *field;
00982     int         *fromF, *toF;
00983     int         print;
00984     int         trunc;
00985     int         startBit, endBit;
00986 
00987     if ( number < 0 || number >= MAX_GENTITIES) {
00988         Com_Error( ERR_DROP, "Bad delta entity number: %i", number );
00989     }
00990 
00991     if ( msg->bit == 0 ) {
00992         startBit = msg->readcount * 8 - GENTITYNUM_BITS;
00993     } else {
00994         startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS;
00995     }
00996 
00997     // check for a remove
00998     if ( MSG_ReadBits( msg, 1 ) == 1 ) {
00999         Com_Memset( to, 0, sizeof( *to ) ); 
01000         to->number = MAX_GENTITIES - 1;
01001         if ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) {
01002             Com_Printf( "%3i: #%-3i remove\n", msg->readcount, number );
01003         }
01004         return;
01005     }
01006 
01007     // check for no delta
01008     if ( MSG_ReadBits( msg, 1 ) == 0 ) {
01009         *to = *from;
01010         to->number = number;
01011         return;
01012     }
01013 
01014     numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]);
01015     lc = MSG_ReadByte(msg);
01016 
01017     // shownet 2/3 will interleave with other printed info, -1 will
01018     // just print the delta records`
01019     if ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) {
01020         print = 1;
01021         Com_Printf( "%3i: #%-3i ", msg->readcount, to->number );
01022     } else {
01023         print = 0;
01024     }
01025 
01026     to->number = number;
01027 
01028     for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) {
01029         fromF = (int *)( (byte *)from + field->offset );
01030         toF = (int *)( (byte *)to + field->offset );
01031 
01032         if ( ! MSG_ReadBits( msg, 1 ) ) {
01033             // no change
01034             *toF = *fromF;
01035         } else {
01036             if ( field->bits == 0 ) {
01037                 // float
01038                 if ( MSG_ReadBits( msg, 1 ) == 0 ) {
01039                         *(float *)toF = 0.0f; 
01040                 } else {
01041                     if ( MSG_ReadBits( msg, 1 ) == 0 ) {
01042                         // integral float
01043                         trunc = MSG_ReadBits( msg, FLOAT_INT_BITS );
01044                         // bias to allow equal parts positive and negative
01045                         trunc -= FLOAT_INT_BIAS;
01046                         *(float *)toF = trunc; 
01047                         if ( print ) {
01048                             Com_Printf( "%s:%i ", field->name, trunc );
01049                         }
01050                     } else {
01051                         // full floating point value
01052                         *toF = MSG_ReadBits( msg, 32 );
01053                         if ( print ) {
01054                             Com_Printf( "%s:%f ", field->name, *(float *)toF );
01055                         }
01056                     }
01057                 }
01058             } else {
01059                 if ( MSG_ReadBits( msg, 1 ) == 0 ) {
01060                     *toF = 0;
01061                 } else {
01062                     // integer
01063                     *toF = MSG_ReadBits( msg, field->bits );
01064                     if ( print ) {
01065                         Com_Printf( "%s:%i ", field->name, *toF );
01066                     }
01067                 }
01068             }
01069 //          pcount[i]++;
01070         }
01071     }
01072     for ( i = lc, field = &entityStateFields[lc] ; i < numFields ; i++, field++ ) {
01073         fromF = (int *)( (byte *)from + field->offset );
01074         toF = (int *)( (byte *)to + field->offset );
01075         // no change
01076         *toF = *fromF;
01077     }
01078 
01079     if ( print ) {
01080         if ( msg->bit == 0 ) {
01081             endBit = msg->readcount * 8 - GENTITYNUM_BITS;
01082         } else {
01083             endBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS;
01084         }
01085         Com_Printf( " (%i bits)\n", endBit - startBit  );
01086     }
01087 }

Here is the call graph for this function:

float MSG_ReadDeltaFloat msg_t msg,
float  oldV
 

Definition at line 554 of file msg.c.

References MSG_ReadBits().

00554                                                    {
00555     if ( MSG_ReadBits( msg, 1 ) ) {
00556         float   newV;
00557 
00558         *(int *)&newV = MSG_ReadBits( msg, 32 );
00559         return newV;
00560     }
00561     return oldV;
00562 }

Here is the call graph for this function:

int MSG_ReadDeltaKey msg_t msg,
int  key,
int  oldV,
int  bits
 

Definition at line 592 of file msg.c.

References bits, kbitmask, and MSG_ReadBits().

Referenced by MSG_ReadDeltaUsercmdKey().

00592                                                                 {
00593     if ( MSG_ReadBits( msg, 1 ) ) {
00594         return MSG_ReadBits( msg, bits ) ^ (key & kbitmask[bits]);
00595     }
00596     return oldV;
00597 }

Here is the call graph for this function:

float MSG_ReadDeltaKeyFloat msg_t msg,
int  key,
float  oldV
 

Definition at line 608 of file msg.c.

References MSG_ReadBits().

00608                                                                {
00609     if ( MSG_ReadBits( msg, 1 ) ) {
00610         float   newV;
00611 
00612         *(int *)&newV = MSG_ReadBits( msg, 32 ) ^ key;
00613         return newV;
00614     }
00615     return oldV;
00616 }

Here is the call graph for this function:

void MSG_ReadDeltaPlayerstate msg_t msg,
playerState_t from,
playerState_t to
 

Definition at line 1315 of file msg.c.

References playerState_s::ammo, msg_t::bit, bits, byte, cl_shownet, Com_Memset(), Com_Printf(), field(), FLOAT_INT_BITS, i, cvar_s::integer, LOG, MSG_ReadBits(), MSG_ReadByte(), MSG_ReadLong(), MSG_ReadShort(), playerState_s::persistant, playerState_t, playerStateFields, playerState_s::powerups, print(), msg_t::readcount, and playerState_s::stats.

01315                                                                                     {
01316     int         i, lc;
01317     int         bits;
01318     netField_t  *field;
01319     int         numFields;
01320     int         startBit, endBit;
01321     int         print;
01322     int         *fromF, *toF;
01323     int         trunc;
01324     playerState_t   dummy;
01325 
01326     if ( !from ) {
01327         from = &dummy;
01328         Com_Memset( &dummy, 0, sizeof( dummy ) );
01329     }
01330     *to = *from;
01331 
01332     if ( msg->bit == 0 ) {
01333         startBit = msg->readcount * 8 - GENTITYNUM_BITS;
01334     } else {
01335         startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS;
01336     }
01337 
01338     // shownet 2/3 will interleave with other printed info, -2 will
01339     // just print the delta records
01340     if ( cl_shownet->integer >= 2 || cl_shownet->integer == -2 ) {
01341         print = 1;
01342         Com_Printf( "%3i: playerstate ", msg->readcount );
01343     } else {
01344         print = 0;
01345     }
01346 
01347     numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] );
01348     lc = MSG_ReadByte(msg);
01349 
01350     for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) {
01351         fromF = (int *)( (byte *)from + field->offset );
01352         toF = (int *)( (byte *)to + field->offset );
01353 
01354         if ( ! MSG_ReadBits( msg, 1 ) ) {
01355             // no change
01356             *toF = *fromF;
01357         } else {
01358             if ( field->bits == 0 ) {
01359                 // float
01360                 if ( MSG_ReadBits( msg, 1 ) == 0 ) {
01361                     // integral float
01362                     trunc = MSG_ReadBits( msg, FLOAT_INT_BITS );
01363                     // bias to allow equal parts positive and negative
01364                     trunc -= FLOAT_INT_BIAS;
01365                     *(float *)toF = trunc; 
01366                     if ( print ) {
01367                         Com_Printf( "%s:%i ", field->name, trunc );
01368                     }
01369                 } else {
01370                     // full floating point value
01371                     *toF = MSG_ReadBits( msg, 32 );
01372                     if ( print ) {
01373                         Com_Printf( "%s:%f ", field->name, *(float *)toF );
01374                     }
01375                 }
01376             } else {
01377                 // integer
01378                 *toF = MSG_ReadBits( msg, field->bits );
01379                 if ( print ) {
01380                     Com_Printf( "%s:%i ", field->name, *toF );
01381                 }
01382             }
01383         }
01384     }
01385     for ( i=lc,field = &playerStateFields[lc];i<numFields; i++, field++) {
01386         fromF = (int *)( (byte *)from + field->offset );
01387         toF = (int *)( (byte *)to + field->offset );
01388         // no change
01389         *toF = *fromF;
01390     }
01391 
01392 
01393     // read the arrays
01394     if (MSG_ReadBits( msg, 1 ) ) {
01395         // parse stats
01396         if ( MSG_ReadBits( msg, 1 ) ) {
01397             LOG("PS_STATS");
01398             bits = MSG_ReadShort (msg);
01399             for (i=0 ; i<16 ; i++) {
01400                 if (bits & (1<<i) ) {
01401                     to->stats[i] = MSG_ReadShort(msg);
01402                 }
01403             }
01404         }
01405 
01406         // parse persistant stats
01407         if ( MSG_ReadBits( msg, 1 ) ) {
01408             LOG("PS_PERSISTANT");
01409             bits = MSG_ReadShort (msg);
01410             for (i=0 ; i<16 ; i++) {
01411                 if (bits & (1<<i) ) {
01412                     to->persistant[i] = MSG_ReadShort(msg);
01413                 }
01414             }
01415         }
01416 
01417         // parse ammo
01418         if ( MSG_ReadBits( msg, 1 ) ) {
01419             LOG("PS_AMMO");
01420             bits = MSG_ReadShort (msg);
01421             for (i=0 ; i<16 ; i++) {
01422                 if (bits & (1<<i) ) {
01423                     to->ammo[i] = MSG_ReadShort(msg);
01424                 }
01425             }
01426         }
01427 
01428         // parse powerups
01429         if ( MSG_ReadBits( msg, 1 ) ) {
01430             LOG("PS_POWERUPS");
01431             bits =