641 lines
16 KiB
C
641 lines
16 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// net_msg.c - network messages
|
|
//=======================================================================
|
|
|
|
#include "common.h"
|
|
#include "byteorder.h"
|
|
#include "mathlib.h"
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
SZ BUFFER (io functions)
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
=======================
|
|
MSG_Init
|
|
|
|
init new buffer
|
|
=======================
|
|
*/
|
|
void MSG_Init( sizebuf_t *buf, byte *data, size_t length )
|
|
{
|
|
memset( buf, 0, sizeof(*buf));
|
|
buf->data = data;
|
|
buf->maxsize = length;
|
|
Huff_Init();
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
MSG_GetSpace
|
|
|
|
get some space for write
|
|
=======================
|
|
*/
|
|
void *MSG_GetSpace( sizebuf_t *msg, size_t length )
|
|
{
|
|
void *data;
|
|
|
|
if( msg->cursize + length > msg->maxsize )
|
|
{
|
|
if( length > msg->maxsize )
|
|
Host_Error("MSG_GetSpace: length[%i] > buffer maxsize [%i]\n", length, msg->maxsize );
|
|
MsgDev( D_WARN, "MSG_GetSpace: overflow\n", msg->cursize + length, msg->maxsize );
|
|
MSG_Clear( msg );
|
|
msg->overflowed = true;
|
|
}
|
|
data = msg->data + msg->cursize;
|
|
msg->cursize += length;
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
MSG_Print
|
|
|
|
used for write sv.forward cmds
|
|
=======================
|
|
*/
|
|
void MSG_Print( sizebuf_t *msg, const char *data )
|
|
{
|
|
size_t length = com.strlen(data) + 1;
|
|
|
|
if( msg->cursize )
|
|
{
|
|
if(msg->data[msg->cursize - 1]) Mem_Copy((byte *)MSG_GetSpace( msg, length ), data, length );
|
|
else Mem_Copy((byte *)MSG_GetSpace( msg, length - 1) - 1, data, length ); // write over trailing 0
|
|
}
|
|
else Mem_Copy((byte *)MSG_GetSpace( msg, length ), data, length );
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
MSG_WriteData
|
|
|
|
used for swap buffers
|
|
=======================
|
|
*/
|
|
void _MSG_WriteData( sizebuf_t *buf, const void *data, size_t length, const char *filename, int fileline )
|
|
{
|
|
Mem_Copy( MSG_GetSpace(buf, length), (void *)data, length );
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
MSG_Clear
|
|
|
|
for clearing overflowed buffer
|
|
=======================
|
|
*/
|
|
void MSG_Clear( sizebuf_t *buf )
|
|
{
|
|
buf->cursize = 0;
|
|
buf->overflowed = false;
|
|
}
|
|
|
|
void MSG_BeginReading( sizebuf_t *msg )
|
|
{
|
|
msg->readcount = 0;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
MSG_WriteBits
|
|
|
|
write # of bytes
|
|
=======================
|
|
*/
|
|
void _MSG_WriteBits( sizebuf_t *msg, int value, int net_type, const char *filename, const int fileline )
|
|
{
|
|
union { long l; float f; } dat;
|
|
byte *buf;
|
|
|
|
if((NWDesc[net_type].min_range + NWDesc[net_type].max_range) != 0 )
|
|
{
|
|
// check range first
|
|
if( value < NWDesc[net_type].min_range || value > NWDesc[net_type].max_range )
|
|
MsgDev( D_WARN, "MSG_Write%s: range error (called at %s:%i)\n", NWDesc[net_type].name, filename, fileline );
|
|
}
|
|
// this isn't an exact overflow check, but close enough
|
|
if( msg->maxsize - msg->cursize < 4 )
|
|
{
|
|
MsgDev( D_ERROR, "MSG_WriteBits: sizebuf overflowed\n" );
|
|
msg->overflowed = true;
|
|
return;
|
|
}
|
|
|
|
switch( net_type )
|
|
{
|
|
case NET_SCALE:
|
|
dat.l = value;
|
|
value = dat.f * 4;
|
|
buf = MSG_GetSpace( msg, 1 );
|
|
buf[0] = value;
|
|
break;
|
|
case NET_COLOR:
|
|
dat.l = value;
|
|
value = dat.f * 255;
|
|
buf = MSG_GetSpace( msg, 1 );
|
|
buf[0] = value;
|
|
break;
|
|
case NET_CHAR:
|
|
case NET_BYTE:
|
|
buf = MSG_GetSpace( msg, 1 );
|
|
buf[0] = value;
|
|
break;
|
|
case NET_SHORT:
|
|
case NET_WORD:
|
|
buf = MSG_GetSpace( msg, 2 );
|
|
buf[0] = value & 0xff;
|
|
buf[1] = value>>8;
|
|
break;
|
|
case NET_LONG:
|
|
case NET_FLOAT:
|
|
buf = MSG_GetSpace( msg, 4 );
|
|
buf[0] = (value>>0 ) & 0xff;
|
|
buf[1] = (value>>8 ) & 0xff;
|
|
buf[2] = (value>>16) & 0xff;
|
|
buf[3] = (value>>24);
|
|
break;
|
|
case NET_ANGLE:
|
|
value = ANGLE2SHORT( value );
|
|
buf = MSG_GetSpace( msg, 2 );
|
|
buf[0] = value & 0xff;
|
|
buf[1] = value>>8;
|
|
break;
|
|
case NET_COORD:
|
|
value *= SV_COORD_FRAC;
|
|
buf = MSG_GetSpace( msg, 4 );
|
|
buf[0] = (value>>0 ) & 0xff;
|
|
buf[1] = (value>>8 ) & 0xff;
|
|
buf[2] = (value>>16) & 0xff;
|
|
buf[3] = (value>>24);
|
|
break;
|
|
default:
|
|
Host_Error( "MSG_WriteBits: bad net.type (called at %s:%i)\n", filename, fileline );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
MSG_ReadBits
|
|
|
|
read # of bytes
|
|
=======================
|
|
*/
|
|
long _MSG_ReadBits( sizebuf_t *msg, int net_type, const char *filename, const int fileline )
|
|
{
|
|
union { long l; float f; } dat;
|
|
long value = 0;
|
|
|
|
switch( net_type )
|
|
{
|
|
case NET_SCALE:
|
|
value = (signed char)(msg->data[msg->readcount]);
|
|
dat.f = value * 0.25f;
|
|
value = dat.l;
|
|
msg->readcount += 1;
|
|
break;
|
|
case NET_COLOR:
|
|
value = (byte)(msg->data[msg->readcount]);
|
|
dat.f = value / 255.0f;
|
|
value = dat.l;
|
|
msg->readcount += 1;
|
|
break;
|
|
case NET_CHAR:
|
|
value = (signed char)msg->data[msg->readcount];
|
|
msg->readcount += 1;
|
|
break;
|
|
case NET_BYTE:
|
|
value = (byte)msg->data[msg->readcount];
|
|
msg->readcount += 1;
|
|
break;
|
|
case NET_WORD:
|
|
case NET_SHORT:
|
|
value = (short)BuffLittleShort(msg->data + msg->readcount);
|
|
msg->readcount += 2;
|
|
break;
|
|
case NET_LONG:
|
|
case NET_FLOAT:
|
|
value = (long)BuffLittleLong(msg->data + msg->readcount);
|
|
msg->readcount += 4;
|
|
break;
|
|
case NET_ANGLE:
|
|
value = (word)BuffLittleShort(msg->data + msg->readcount);
|
|
value = SHORT2ANGLE( value );
|
|
msg->readcount += 2;
|
|
break;
|
|
case NET_COORD:
|
|
value = (long)BuffLittleLong(msg->data + msg->readcount);
|
|
value *= CL_COORD_FRAC;
|
|
msg->readcount += 4;
|
|
break;
|
|
default:
|
|
Host_Error( "MSG_ReadBits: bad net.type (called at %s:%i)\n", filename, fileline );
|
|
break;
|
|
}
|
|
|
|
// end of message
|
|
if( msg->readcount > msg->cursize )
|
|
return -1;
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
MESSAGE IO FUNCTIONS
|
|
Handles byte ordering and avoids alignment errors
|
|
==============================================================================
|
|
*/
|
|
/*
|
|
=======================
|
|
writing functions
|
|
=======================
|
|
*/
|
|
void _MSG_WriteFloat( sizebuf_t *sb, float f, const char *filename, int fileline )
|
|
{
|
|
union { float f; int l; } dat;
|
|
dat.f = f;
|
|
MSG_WriteBits( sb, dat.l, NET_FLOAT );
|
|
}
|
|
|
|
void _MSG_WriteString( sizebuf_t *sb, const char *s, const char *filename, int fileline )
|
|
{
|
|
if( !s ) _MSG_WriteData( sb, "", 1, filename, fileline );
|
|
else
|
|
{
|
|
int l, i;
|
|
char string[MAX_SYSPATH];
|
|
|
|
l = com.strlen( s ) + 1;
|
|
if( l >= MAX_SYSPATH )
|
|
{
|
|
MsgDev( D_ERROR, "MSG_WriteString: exceeds %i symbols (called at %s:%i\n", MAX_SYSPATH, filename, fileline );
|
|
_MSG_WriteData( sb, "", 1, filename, fileline );
|
|
return;
|
|
}
|
|
com.strncpy( string, s, sizeof( string ));
|
|
|
|
// get rid of 0xff chars, because old clients don't like them
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
if(((byte *)string)[i] > 127 )
|
|
string[i] = '.';
|
|
}
|
|
_MSG_WriteData( sb, string, l, filename, fileline );
|
|
}
|
|
}
|
|
|
|
void _MSG_WritePos( sizebuf_t *sb, vec3_t pos, const char *filename, int fileline )
|
|
{
|
|
_MSG_WriteBits( sb, pos[0], NET_FLOAT, filename, fileline );
|
|
_MSG_WriteBits( sb, pos[1], NET_FLOAT, filename, fileline );
|
|
_MSG_WriteBits( sb, pos[2], NET_FLOAT, filename, fileline );
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
reading functions
|
|
=======================
|
|
*/
|
|
float MSG_ReadFloat( sizebuf_t *msg )
|
|
{
|
|
union { float f; int l; } dat;
|
|
dat.l = MSG_ReadBits( msg, NET_FLOAT );
|
|
return dat.f;
|
|
}
|
|
|
|
char *MSG_ReadString( sizebuf_t *msg )
|
|
{
|
|
static char string[MAX_SYSPATH];
|
|
int l = 0, c;
|
|
|
|
do
|
|
{
|
|
// use MSG_ReadByte so -1 is out of bounds
|
|
c = MSG_ReadByte( msg );
|
|
if( c == -1 || c == '\0' )
|
|
break;
|
|
|
|
// translate all fmt spec to avoid crash bugs
|
|
if( c == '%' ) c = '.';
|
|
// don't allow higher ascii values
|
|
if( c > 127 ) c = '.';
|
|
|
|
string[l] = c;
|
|
l++;
|
|
} while( l < sizeof(string) - 1 );
|
|
string[l] = 0; // terminator
|
|
|
|
return string;
|
|
}
|
|
|
|
char *MSG_ReadStringLine( sizebuf_t *msg )
|
|
{
|
|
static char string[MAX_SYSPATH];
|
|
int l = 0, c;
|
|
|
|
do
|
|
{
|
|
// use MSG_ReadByte so -1 is out of bounds
|
|
c = MSG_ReadByte( msg );
|
|
if( c == -1 || c == '\0' || c == '\n' )
|
|
break;
|
|
|
|
// translate all fmt spec to avoid crash bugs
|
|
if( c == '%' ) c = '.';
|
|
|
|
string[l] = c;
|
|
l++;
|
|
} while( l < sizeof(string) - 1 );
|
|
string[l] = 0; // terminator
|
|
|
|
return string;
|
|
}
|
|
|
|
void MSG_ReadData( sizebuf_t *msg, void *data, size_t length )
|
|
{
|
|
int i;
|
|
for( i = 0; i < length; i++ )
|
|
((byte *)data)[i] = MSG_ReadByte( msg );
|
|
}
|
|
|
|
void MSG_ReadPos( sizebuf_t *msg_read, vec3_t pos )
|
|
{
|
|
pos[0] = MSG_ReadBits(msg_read, NET_FLOAT );
|
|
pos[1] = MSG_ReadBits(msg_read, NET_FLOAT );
|
|
pos[2] = MSG_ReadBits(msg_read, NET_FLOAT );
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
delta functions
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
=====================
|
|
MSG_WriteDeltaUsercmd
|
|
=====================
|
|
*/
|
|
void _MSG_WriteDeltaUsercmd( sizebuf_t *msg, usercmd_t *from, usercmd_t *to, const char *filename, const int fileline )
|
|
{
|
|
int num_fields;
|
|
net_field_t *field;
|
|
int *fromF, *toF;
|
|
int i, flags = 0;
|
|
|
|
num_fields = (sizeof(cmd_fields) / sizeof(cmd_fields[0])) - 1;
|
|
if( num_fields > 31 ) return; // this should never happen
|
|
|
|
// compare fields
|
|
for( i = 0, field = cmd_fields; i < num_fields; i++, field++ )
|
|
{
|
|
fromF = (int *)((byte *)from + field->offset );
|
|
toF = (int *)((byte *)to + field->offset );
|
|
if(*fromF != *toF || field->force) flags |= 1<<i;
|
|
}
|
|
if( flags == 0 )
|
|
{
|
|
// nothing at all changed
|
|
MSG_WriteLong( msg, -99 ); // no delta info
|
|
return;
|
|
}
|
|
|
|
MSG_WriteLong( msg, flags ); // send flags who indicates changes
|
|
for( i = 0, field = cmd_fields; i < num_fields; i++, field++ )
|
|
{
|
|
toF = (int *)((byte *)to + field->offset );
|
|
if( flags & 1<<i ) MSG_WriteBits( msg, *toF, field->bits );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
MSG_ReadDeltaUsercmd
|
|
=====================
|
|
*/
|
|
void MSG_ReadDeltaUsercmd( sizebuf_t *msg, usercmd_t *from, usercmd_t *to )
|
|
{
|
|
net_field_t *field;
|
|
int i, flags;
|
|
int *fromF, *toF;
|
|
|
|
*to = *from;
|
|
|
|
if(*(int *)&msg->data[msg->readcount] == -99 )
|
|
{
|
|
MSG_ReadLong( msg );
|
|
return;
|
|
}
|
|
for( i = 0, field = cmd_fields; field->name; i++, field++ )
|
|
{
|
|
// get flags of next packet if LONG out of range
|
|
if((i & 31) == 0) flags = MSG_ReadLong( msg );
|
|
fromF = (int *)((byte *)from + field->offset );
|
|
toF = (int *)((byte *)to + field->offset );
|
|
|
|
if(flags & (1<<i)) *toF = MSG_ReadBits( msg, field->bits );
|
|
else *toF = *fromF; // no change
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
entity_state_t communication
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
==================
|
|
MSG_WriteDeltaEntity
|
|
|
|
Writes part of a packetentities message, including the entity number.
|
|
Can delta from either a baseline or a previous packet_entity
|
|
If to is NULL, a remove entity update will be sent
|
|
If force is not set, then nothing at all will be generated if the entity is
|
|
identical, under the assumption that the in-order delta code will catch it.
|
|
==================
|
|
*/
|
|
void _MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *msg, bool force, bool newentity, const char *filename, int fileline )
|
|
{
|
|
net_field_t *field, *field2;
|
|
int i, j, k, flags;
|
|
int *fromF, *toF;
|
|
|
|
if( to == NULL )
|
|
{
|
|
if( from == NULL ) return;
|
|
// a NULL to is a delta remove message
|
|
MSG_WriteBits( msg, from->number, NET_WORD );
|
|
MSG_WriteBits( msg, -99, NET_LONG );
|
|
return;
|
|
}
|
|
|
|
if( to->number < 0 || to->number >= host.max_edicts )
|
|
Host_Error( "MSG_WriteDeltaEntity: Bad entity number: %i (called at %s:%i)\n", to->number, filename, fileline );
|
|
|
|
MSG_WriteBits( msg, to->number, NET_WORD );
|
|
for( i = j = 0, field = field2 = ent_fields; field->name; i++, j++, field++ )
|
|
{
|
|
fromF = (int *)((byte *)from + field->offset );
|
|
toF = (int *)((byte *)to + field->offset );
|
|
if(*fromF != *toF || (newentity && field->force)) flags |= 1<<j;
|
|
if( j > 31 || !ent_fields[i+1].name) // dump packet
|
|
{
|
|
// NOTE: entity must have changes from one at first of 32 fields
|
|
// otherwise it will ignore updates
|
|
if( flags == 0 && force == 0 && i < 32 )
|
|
{
|
|
msg->cursize -= sizeof(word); // kill header
|
|
return;
|
|
}
|
|
MSG_WriteLong( msg, flags ); // send flags who indicates changes
|
|
for( k = 0; field2->name; k++, field2++ )
|
|
{
|
|
if( k > 31 ) break; // return to main cycle
|
|
toF = (int *)((byte *)to + field2->offset );
|
|
if( flags & 1<<k ) MSG_WriteBits( msg, *toF, field2->bits );
|
|
}
|
|
j = flags = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
MSG_ReadDeltaEntity
|
|
|
|
The entity number has already been read from the message, which
|
|
is how the from state is identified.
|
|
|
|
If the delta removes the entity, entity_state_t->number will be set to -1
|
|
|
|
Can go from either a baseline or a previous packet_entity
|
|
==================
|
|
*/
|
|
void MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number )
|
|
{
|
|
net_field_t *field;
|
|
int i, flags;
|
|
int *fromF, *toF;
|
|
|
|
if( number < 0 || number >= host.max_edicts )
|
|
Host_Error( "MSG_ReadDeltaEntity: bad delta entity number: %i\n", number );
|
|
|
|
*to = *from;
|
|
VectorCopy( from->origin, to->old_origin );
|
|
to->number = number;
|
|
|
|
if(*(int *)&msg->data[msg->readcount] == -99 )
|
|
{
|
|
// check for a remove
|
|
MSG_ReadLong( msg );
|
|
memset( to, 0, sizeof(*to));
|
|
to->number = -1;
|
|
return;
|
|
}
|
|
for( i = 0, field = ent_fields; field->name; i++, field++ )
|
|
{
|
|
// get flags of next packet if LONG out of range
|
|
if((i & 31) == 0) flags = MSG_ReadLong( msg );
|
|
fromF = (int *)((byte *)from + field->offset );
|
|
toF = (int *)((byte *)to + field->offset );
|
|
|
|
if(flags & (1<<i)) *toF = MSG_ReadBits( msg, field->bits );
|
|
else *toF = *fromF; // no change
|
|
}
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
|
|
player state communication
|
|
|
|
============================================================================
|
|
*/
|
|
/*
|
|
=============
|
|
MSG_WriteDeltaPlayerstate
|
|
|
|
=============
|
|
*/
|
|
void MSG_WriteDeltaPlayerstate( entity_state_t *from, entity_state_t *to, sizebuf_t *msg )
|
|
{
|
|
entity_state_t dummy;
|
|
entity_state_t *ops, *ps = to;
|
|
net_field_t *field, *field2;
|
|
int *fromF, *toF;
|
|
int i, j, k;
|
|
uint flags = 0;
|
|
|
|
if( !from )
|
|
{
|
|
memset (&dummy, 0, sizeof(dummy));
|
|
ops = &dummy;
|
|
}
|
|
else ops = from;
|
|
from = to;
|
|
|
|
MSG_WriteByte( msg, svc_playerinfo );
|
|
|
|
for( i = j = 0, field = field2 = ent_fields; field->name; i++, j++, field++ )
|
|
{
|
|
fromF = (int *)((byte *)ops + field->offset );
|
|
toF = (int *)((byte *)ps + field->offset );
|
|
if(*fromF != *toF || field->force) flags |= 1<<j;
|
|
if( j > 31 || !ent_fields[i+1].name) // dump packet
|
|
{
|
|
MSG_WriteLong( msg, flags ); // send flags who indicates changes
|
|
for( k = 0; field2->name; k++, field2++ )
|
|
{
|
|
if( k > 31 ) break; // return to main cycle
|
|
toF = (int *)((byte *)ps + field2->offset );
|
|
if( flags & 1<<k ) MSG_WriteBits( msg, *toF, field2->bits );
|
|
}
|
|
j = flags = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
MSG_ReadDeltaPlayerstate
|
|
===================
|
|
*/
|
|
void MSG_ReadDeltaPlayerstate( sizebuf_t *msg, entity_state_t *from, entity_state_t *to )
|
|
{
|
|
net_field_t *field;
|
|
int *fromF, *toF;
|
|
entity_state_t dummy;
|
|
uint i, flags;
|
|
|
|
// clear to old value before delta parsing
|
|
if( !from )
|
|
{
|
|
from = &dummy;
|
|
memset( &dummy, 0, sizeof( dummy ));
|
|
}
|
|
*to = *from;
|
|
|
|
for( i = 0, field = ent_fields; field->name; i++, field++ )
|
|
{
|
|
// get flags of next packet if LONG out of range
|
|
if((i & 31) == 0) flags = MSG_ReadLong( msg );
|
|
fromF = (int *)((byte *)from + field->offset );
|
|
toF = (int *)((byte *)to + field->offset );
|
|
|
|
if(flags & (1<<i)) *toF = MSG_ReadBits( msg, field->bits );
|
|
else *toF = *fromF; // no change
|
|
}
|
|
} |