mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-15 09:30:01 +00:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
1982 lines
49 KiB
C
1982 lines
49 KiB
C
/*
|
|
net_encode.c - encode network messages
|
|
Copyright (C) 2010 Uncle Mike
|
|
|
|
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 3 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.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "netchan.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "net_encode.h"
|
|
#include "event_api.h"
|
|
#include "usercmd.h"
|
|
#include "pm_movevars.h"
|
|
#include "entity_state.h"
|
|
#include "weaponinfo.h"
|
|
#include "event_args.h"
|
|
#include "protocol.h"
|
|
#include "client.h"
|
|
|
|
#define DELTA_PATH "delta.lst"
|
|
|
|
static qboolean delta_init = false;
|
|
|
|
// list of all the struct names
|
|
static const delta_field_t cmd_fields[] =
|
|
{
|
|
{ UCMD_DEF( lerp_msec ) },
|
|
{ UCMD_DEF( msec ) },
|
|
{ UCMD_DEF( viewangles[0] ) },
|
|
{ UCMD_DEF( viewangles[1] ) },
|
|
{ UCMD_DEF( viewangles[2] ) },
|
|
{ UCMD_DEF( forwardmove ) },
|
|
{ UCMD_DEF( sidemove ) },
|
|
{ UCMD_DEF( upmove ) },
|
|
{ UCMD_DEF( lightlevel ) },
|
|
{ UCMD_DEF( buttons ) },
|
|
{ UCMD_DEF( impulse ) },
|
|
{ UCMD_DEF( weaponselect ) },
|
|
{ UCMD_DEF( impact_index ) },
|
|
{ UCMD_DEF( impact_position[0] ) },
|
|
{ UCMD_DEF( impact_position[1] ) },
|
|
{ UCMD_DEF( impact_position[2] ) },
|
|
{ NULL },
|
|
};
|
|
|
|
static const delta_field_t pm_fields[] =
|
|
{
|
|
{ PHYS_DEF( gravity ) },
|
|
{ PHYS_DEF( stopspeed ) },
|
|
{ PHYS_DEF( maxspeed ) },
|
|
{ PHYS_DEF( spectatormaxspeed ) },
|
|
{ PHYS_DEF( accelerate ) },
|
|
{ PHYS_DEF( airaccelerate ) },
|
|
{ PHYS_DEF( wateraccelerate ) },
|
|
{ PHYS_DEF( friction ) },
|
|
{ PHYS_DEF( edgefriction ) },
|
|
{ PHYS_DEF( waterfriction ) },
|
|
{ PHYS_DEF( bounce ) },
|
|
{ PHYS_DEF( stepsize ) },
|
|
{ PHYS_DEF( maxvelocity ) },
|
|
{ PHYS_DEF( zmax ) },
|
|
{ PHYS_DEF( waveHeight ) },
|
|
{ PHYS_DEF( footsteps ) },
|
|
{ PHYS_DEF( skyName ) },
|
|
{ PHYS_DEF( rollangle ) },
|
|
{ PHYS_DEF( rollspeed ) },
|
|
{ PHYS_DEF( skycolor_r ) },
|
|
{ PHYS_DEF( skycolor_g ) },
|
|
{ PHYS_DEF( skycolor_b ) },
|
|
{ PHYS_DEF( skyvec_x ) },
|
|
{ PHYS_DEF( skyvec_y ) },
|
|
{ PHYS_DEF( skyvec_z ) },
|
|
{ PHYS_DEF( fog_settings ) },
|
|
{ PHYS_DEF( wateralpha ) },
|
|
{ PHYS_DEF( skydir_x ) },
|
|
{ PHYS_DEF( skydir_y ) },
|
|
{ PHYS_DEF( skydir_z ) },
|
|
{ PHYS_DEF( skyangle ) },
|
|
{ NULL },
|
|
};
|
|
|
|
static const delta_field_t ev_fields[] =
|
|
{
|
|
{ EVNT_DEF( flags ) },
|
|
{ EVNT_DEF( entindex ) },
|
|
{ EVNT_DEF( origin[0] ) },
|
|
{ EVNT_DEF( origin[1] ) },
|
|
{ EVNT_DEF( origin[2] ) },
|
|
{ EVNT_DEF( angles[0] ) },
|
|
{ EVNT_DEF( angles[1] ) },
|
|
{ EVNT_DEF( angles[2] ) },
|
|
{ EVNT_DEF( velocity[0] ) },
|
|
{ EVNT_DEF( velocity[1] ) },
|
|
{ EVNT_DEF( velocity[2] ) },
|
|
{ EVNT_DEF( ducking ) },
|
|
{ EVNT_DEF( fparam1 ) },
|
|
{ EVNT_DEF( fparam2 ) },
|
|
{ EVNT_DEF( iparam1 ) },
|
|
{ EVNT_DEF( iparam2 ) },
|
|
{ EVNT_DEF( bparam1 ) },
|
|
{ EVNT_DEF( bparam2 ) },
|
|
{ NULL },
|
|
};
|
|
|
|
static const delta_field_t wd_fields[] =
|
|
{
|
|
{ WPDT_DEF( m_iId ) },
|
|
{ WPDT_DEF( m_iClip ) },
|
|
{ WPDT_DEF( m_flNextPrimaryAttack ) },
|
|
{ WPDT_DEF( m_flNextSecondaryAttack ) },
|
|
{ WPDT_DEF( m_flTimeWeaponIdle ) },
|
|
{ WPDT_DEF( m_fInReload ) },
|
|
{ WPDT_DEF( m_fInSpecialReload ) },
|
|
{ WPDT_DEF( m_flNextReload ) },
|
|
{ WPDT_DEF( m_flPumpTime ) },
|
|
{ WPDT_DEF( m_fReloadTime ) },
|
|
{ WPDT_DEF( m_fAimedDamage ) },
|
|
{ WPDT_DEF( m_fNextAimBonus ) },
|
|
{ WPDT_DEF( m_fInZoom ) },
|
|
{ WPDT_DEF( m_iWeaponState ) },
|
|
{ WPDT_DEF( iuser1 ) },
|
|
{ WPDT_DEF( iuser2 ) },
|
|
{ WPDT_DEF( iuser3 ) },
|
|
{ WPDT_DEF( iuser4 ) },
|
|
{ WPDT_DEF( fuser1 ) },
|
|
{ WPDT_DEF( fuser2 ) },
|
|
{ WPDT_DEF( fuser3 ) },
|
|
{ WPDT_DEF( fuser4 ) },
|
|
{ NULL },
|
|
};
|
|
|
|
static const delta_field_t cd_fields[] =
|
|
{
|
|
{ CLDT_DEF( origin[0] ) },
|
|
{ CLDT_DEF( origin[1] ) },
|
|
{ CLDT_DEF( origin[2] ) },
|
|
{ CLDT_DEF( velocity[0] ) },
|
|
{ CLDT_DEF( velocity[1] ) },
|
|
{ CLDT_DEF( velocity[2] ) },
|
|
{ CLDT_DEF( viewmodel ) },
|
|
{ CLDT_DEF( punchangle[0] ) },
|
|
{ CLDT_DEF( punchangle[1] ) },
|
|
{ CLDT_DEF( punchangle[2] ) },
|
|
{ CLDT_DEF( flags ) },
|
|
{ CLDT_DEF( waterlevel ) },
|
|
{ CLDT_DEF( watertype ) },
|
|
{ CLDT_DEF( view_ofs[0] ) },
|
|
{ CLDT_DEF( view_ofs[1] ) },
|
|
{ CLDT_DEF( view_ofs[2] ) },
|
|
{ CLDT_DEF( health ) },
|
|
{ CLDT_DEF( bInDuck ) },
|
|
{ CLDT_DEF( weapons ) },
|
|
{ CLDT_DEF( flTimeStepSound ) },
|
|
{ CLDT_DEF( flDuckTime ) },
|
|
{ CLDT_DEF( flSwimTime ) },
|
|
{ CLDT_DEF( waterjumptime ) },
|
|
{ CLDT_DEF( maxspeed ) },
|
|
{ CLDT_DEF( fov ) },
|
|
{ CLDT_DEF( weaponanim ) },
|
|
{ CLDT_DEF( m_iId ) },
|
|
{ CLDT_DEF( ammo_shells ) },
|
|
{ CLDT_DEF( ammo_nails ) },
|
|
{ CLDT_DEF( ammo_cells ) },
|
|
{ CLDT_DEF( ammo_rockets ) },
|
|
{ CLDT_DEF( m_flNextAttack ) },
|
|
{ CLDT_DEF( tfstate ) },
|
|
{ CLDT_DEF( pushmsec ) },
|
|
{ CLDT_DEF( deadflag ) },
|
|
{ CLDT_DEF( physinfo ) },
|
|
{ CLDT_DEF( iuser1 ) },
|
|
{ CLDT_DEF( iuser2 ) },
|
|
{ CLDT_DEF( iuser3 ) },
|
|
{ CLDT_DEF( iuser4 ) },
|
|
{ CLDT_DEF( fuser1 ) },
|
|
{ CLDT_DEF( fuser2 ) },
|
|
{ CLDT_DEF( fuser3 ) },
|
|
{ CLDT_DEF( fuser4 ) },
|
|
{ CLDT_DEF( vuser1[0] ) },
|
|
{ CLDT_DEF( vuser1[1] ) },
|
|
{ CLDT_DEF( vuser1[2] ) },
|
|
{ CLDT_DEF( vuser2[0] ) },
|
|
{ CLDT_DEF( vuser2[1] ) },
|
|
{ CLDT_DEF( vuser2[2] ) },
|
|
{ CLDT_DEF( vuser3[0] ) },
|
|
{ CLDT_DEF( vuser3[1] ) },
|
|
{ CLDT_DEF( vuser3[2] ) },
|
|
{ CLDT_DEF( vuser4[0] ) },
|
|
{ CLDT_DEF( vuser4[1] ) },
|
|
{ CLDT_DEF( vuser4[2] ) },
|
|
{ NULL },
|
|
};
|
|
|
|
static const delta_field_t ent_fields[] =
|
|
{
|
|
{ ENTS_DEF( entityType ) },
|
|
{ ENTS_DEF( origin[0] ) },
|
|
{ ENTS_DEF( origin[1] ) },
|
|
{ ENTS_DEF( origin[2] ) },
|
|
{ ENTS_DEF( angles[0] ) },
|
|
{ ENTS_DEF( angles[1] ) },
|
|
{ ENTS_DEF( angles[2] ) },
|
|
{ ENTS_DEF( modelindex ) },
|
|
{ ENTS_DEF( sequence ) },
|
|
{ ENTS_DEF( frame ) },
|
|
{ ENTS_DEF( colormap ) },
|
|
{ ENTS_DEF( skin ) },
|
|
{ ENTS_DEF( solid ) },
|
|
{ ENTS_DEF( effects ) },
|
|
{ ENTS_DEF( scale ) },
|
|
{ ENTS_DEF( eflags ) },
|
|
{ ENTS_DEF( rendermode ) },
|
|
{ ENTS_DEF( renderamt ) },
|
|
{ ENTS_DEF( rendercolor.r ) },
|
|
{ ENTS_DEF( rendercolor.g ) },
|
|
{ ENTS_DEF( rendercolor.b ) },
|
|
{ ENTS_DEF( renderfx ) },
|
|
{ ENTS_DEF( movetype ) },
|
|
{ ENTS_DEF( animtime ) },
|
|
{ ENTS_DEF( framerate ) },
|
|
{ ENTS_DEF( body ) },
|
|
{ ENTS_DEF( controller[0] ) },
|
|
{ ENTS_DEF( controller[1] ) },
|
|
{ ENTS_DEF( controller[2] ) },
|
|
{ ENTS_DEF( controller[3] ) },
|
|
{ ENTS_DEF( blending[0] ) },
|
|
{ ENTS_DEF( blending[1] ) },
|
|
{ ENTS_DEF( blending[2] ) },
|
|
{ ENTS_DEF( blending[3] ) },
|
|
{ ENTS_DEF( velocity[0] ) },
|
|
{ ENTS_DEF( velocity[1] ) },
|
|
{ ENTS_DEF( velocity[2] ) },
|
|
{ ENTS_DEF( mins[0] ) },
|
|
{ ENTS_DEF( mins[1] ) },
|
|
{ ENTS_DEF( mins[2] ) },
|
|
{ ENTS_DEF( maxs[0] ) },
|
|
{ ENTS_DEF( maxs[1] ) },
|
|
{ ENTS_DEF( maxs[2] ) },
|
|
{ ENTS_DEF( aiment ) },
|
|
{ ENTS_DEF( owner ) },
|
|
{ ENTS_DEF( friction ) },
|
|
{ ENTS_DEF( gravity ) },
|
|
{ ENTS_DEF( team ) },
|
|
{ ENTS_DEF( playerclass ) },
|
|
{ ENTS_DEF( health ) },
|
|
{ ENTS_DEF( spectator ) },
|
|
{ ENTS_DEF( weaponmodel ) },
|
|
{ ENTS_DEF( gaitsequence ) },
|
|
{ ENTS_DEF( basevelocity[0] ) },
|
|
{ ENTS_DEF( basevelocity[1] ) },
|
|
{ ENTS_DEF( basevelocity[2] ) },
|
|
{ ENTS_DEF( usehull ) },
|
|
{ ENTS_DEF( oldbuttons ) }, // probably never transmitted
|
|
{ ENTS_DEF( onground ) },
|
|
{ ENTS_DEF( iStepLeft ) },
|
|
{ ENTS_DEF( flFallVelocity ) },
|
|
{ ENTS_DEF( fov ) },
|
|
{ ENTS_DEF( weaponanim ) },
|
|
{ ENTS_DEF( startpos[0] ) },
|
|
{ ENTS_DEF( startpos[1] ) },
|
|
{ ENTS_DEF( startpos[2] ) },
|
|
{ ENTS_DEF( endpos[0] ) },
|
|
{ ENTS_DEF( endpos[1] ) },
|
|
{ ENTS_DEF( endpos[2] ) },
|
|
{ ENTS_DEF( impacttime ) },
|
|
{ ENTS_DEF( starttime ) },
|
|
{ ENTS_DEF( iuser1 ) },
|
|
{ ENTS_DEF( iuser2 ) },
|
|
{ ENTS_DEF( iuser3 ) },
|
|
{ ENTS_DEF( iuser4 ) },
|
|
{ ENTS_DEF( fuser1 ) },
|
|
{ ENTS_DEF( fuser2 ) },
|
|
{ ENTS_DEF( fuser3 ) },
|
|
{ ENTS_DEF( fuser4 ) },
|
|
{ ENTS_DEF( vuser1[0] ) },
|
|
{ ENTS_DEF( vuser1[1] ) },
|
|
{ ENTS_DEF( vuser1[2] ) },
|
|
{ ENTS_DEF( vuser2[0] ) },
|
|
{ ENTS_DEF( vuser2[1] ) },
|
|
{ ENTS_DEF( vuser2[2] ) },
|
|
{ ENTS_DEF( vuser3[0] ) },
|
|
{ ENTS_DEF( vuser3[1] ) },
|
|
{ ENTS_DEF( vuser3[2] ) },
|
|
{ ENTS_DEF( vuser4[0] ) },
|
|
{ ENTS_DEF( vuser4[1] ) },
|
|
{ ENTS_DEF( vuser4[2] ) },
|
|
{ NULL },
|
|
};
|
|
|
|
static delta_info_t dt_info[] =
|
|
{
|
|
{ "event_t", ev_fields, NUM_FIELDS( ev_fields ) },
|
|
{ "movevars_t", pm_fields, NUM_FIELDS( pm_fields ) },
|
|
{ "usercmd_t", cmd_fields, NUM_FIELDS( cmd_fields ) },
|
|
{ "clientdata_t", cd_fields, NUM_FIELDS( cd_fields ) },
|
|
{ "weapon_data_t", wd_fields, NUM_FIELDS( wd_fields ) },
|
|
{ "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
|
{ "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
|
{ "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
|
{ NULL },
|
|
};
|
|
|
|
delta_info_t *Delta_FindStruct( const char *name )
|
|
{
|
|
int i;
|
|
|
|
if( !COM_CheckString( name ))
|
|
return NULL;
|
|
|
|
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
|
{
|
|
if( !Q_stricmp( dt_info[i].pName, name ))
|
|
return &dt_info[i];
|
|
}
|
|
|
|
Con_DPrintf( S_WARN "Struct %s not found in delta_info\n", name );
|
|
|
|
// found nothing
|
|
return NULL;
|
|
}
|
|
|
|
int Delta_NumTables( void )
|
|
{
|
|
return NUM_FIELDS( dt_info );
|
|
}
|
|
|
|
delta_info_t *Delta_FindStructByIndex( int index )
|
|
{
|
|
if( index < 0 || index >= NUM_FIELDS( dt_info ))
|
|
return NULL;
|
|
|
|
return &dt_info[index];
|
|
}
|
|
|
|
delta_info_t *Delta_FindStructByEncoder( const char *encoderName )
|
|
{
|
|
int i;
|
|
|
|
if( !COM_CheckString( encoderName ) )
|
|
return NULL;
|
|
|
|
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
|
{
|
|
if( !Q_stricmp( dt_info[i].funcName, encoderName ))
|
|
return &dt_info[i];
|
|
}
|
|
// found nothing
|
|
return NULL;
|
|
}
|
|
|
|
delta_info_t *Delta_FindStructByDelta( const delta_t *pFields )
|
|
{
|
|
int i;
|
|
|
|
if( !pFields ) return NULL;
|
|
|
|
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
|
{
|
|
if( dt_info[i].pFields == pFields )
|
|
return &dt_info[i];
|
|
}
|
|
// found nothing
|
|
return NULL;
|
|
}
|
|
|
|
void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *to )
|
|
{
|
|
int i;
|
|
|
|
Assert( dt != NULL );
|
|
|
|
// set all fields is active by default
|
|
for( i = 0; i < dt->numFields; i++ )
|
|
dt->pFields[i].bInactive = false;
|
|
|
|
if( dt->userCallback )
|
|
{
|
|
dt->userCallback( dt->pFields, from, to );
|
|
}
|
|
}
|
|
|
|
delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName )
|
|
{
|
|
if( !fieldName || !*fieldName )
|
|
return NULL;
|
|
|
|
for( ; pInfo->name; pInfo++ )
|
|
{
|
|
if( !Q_strcmp( pInfo->name, fieldName ))
|
|
return (delta_field_t *)pInfo;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fieldName )
|
|
{
|
|
int i;
|
|
|
|
if( !fieldName || !*fieldName )
|
|
return -1;
|
|
|
|
for( i = 0; pInfo->name; i++, pInfo++ )
|
|
{
|
|
if( !Q_strcmp( pInfo->name, fieldName ))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
qboolean Delta_AddField( const char *pStructName, const char *pName, int flags, int bits, float mul, float post_mul )
|
|
{
|
|
delta_info_t *dt;
|
|
delta_field_t *pFieldInfo;
|
|
delta_t *pField;
|
|
int i;
|
|
|
|
// get the delta struct
|
|
dt = Delta_FindStruct( pStructName );
|
|
Assert( dt != NULL );
|
|
|
|
// check for coexisting field
|
|
for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( !Q_strcmp( pField->name, pName ))
|
|
{
|
|
// update existed field
|
|
pField->flags = flags;
|
|
pField->bits = bits;
|
|
pField->multiplier = mul;
|
|
pField->post_multiplier = post_mul;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// find field description
|
|
pFieldInfo = Delta_FindFieldInfo( dt->pInfo, pName );
|
|
if( !pFieldInfo )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_Add: couldn't find description for %s->%s\n", pStructName, pName );
|
|
return false;
|
|
}
|
|
|
|
if( dt->numFields + 1 > dt->maxFields )
|
|
{
|
|
Con_DPrintf( S_WARN "Delta_Add: can't add %s->%s encoder list is full\n", pStructName, pName );
|
|
return false; // too many fields specified (duplicated ?)
|
|
}
|
|
|
|
// allocate a new one
|
|
dt->pFields = Z_Realloc( dt->pFields, (dt->numFields + 1) * sizeof( delta_t ));
|
|
for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ );
|
|
|
|
// copy info to new field
|
|
pField->name = pFieldInfo->name;
|
|
pField->offset = pFieldInfo->offset;
|
|
pField->size = pFieldInfo->size;
|
|
pField->flags = flags;
|
|
pField->bits = bits;
|
|
pField->multiplier = mul;
|
|
pField->post_multiplier = post_mul;
|
|
dt->numFields++;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Delta_WriteTableField( sizebuf_t *msg, int tableIndex, const delta_t *pField )
|
|
{
|
|
int nameIndex;
|
|
delta_info_t *dt;
|
|
|
|
Assert( pField != NULL );
|
|
|
|
if( !COM_CheckString( pField->name ))
|
|
return; // not initialized ?
|
|
|
|
dt = Delta_FindStructByIndex( tableIndex );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name );
|
|
Assert( nameIndex >= 0 && nameIndex < dt->maxFields );
|
|
|
|
MSG_BeginServerCmd( msg, svc_deltatable );
|
|
MSG_WriteUBitLong( msg, tableIndex, 4 ); // assume we support 16 network tables
|
|
MSG_WriteUBitLong( msg, nameIndex, 8 ); // 255 fields by struct should be enough
|
|
MSG_WriteUBitLong( msg, pField->flags, 10 ); // flags are indicated various input types
|
|
MSG_WriteUBitLong( msg, pField->bits - 1, 5 ); // max received value is 32 (32 bit)
|
|
|
|
// multipliers is null-compressed
|
|
if( pField->multiplier != 1.0f )
|
|
{
|
|
MSG_WriteOneBit( msg, 1 );
|
|
MSG_WriteFloat( msg, pField->multiplier );
|
|
}
|
|
else MSG_WriteOneBit( msg, 0 );
|
|
|
|
if( pField->post_multiplier != 1.0f )
|
|
{
|
|
MSG_WriteOneBit( msg, 1 );
|
|
MSG_WriteFloat( msg, pField->post_multiplier );
|
|
}
|
|
else MSG_WriteOneBit( msg, 0 );
|
|
}
|
|
|
|
void Delta_ParseTableField( sizebuf_t *msg )
|
|
{
|
|
int tableIndex, nameIndex;
|
|
float mul = 1.0f, post_mul = 1.0f;
|
|
int flags, bits;
|
|
const char *pName;
|
|
qboolean ignore = false;
|
|
delta_info_t *dt;
|
|
|
|
tableIndex = MSG_ReadUBitLong( msg, 4 );
|
|
dt = Delta_FindStructByIndex( tableIndex );
|
|
if( !dt )
|
|
Host_Error( "Delta_ParseTableField: not initialized" );
|
|
|
|
nameIndex = MSG_ReadUBitLong( msg, 8 ); // read field name index
|
|
if( ( nameIndex >= 0 && nameIndex < dt->maxFields ) )
|
|
{
|
|
pName = dt->pInfo[nameIndex].name;
|
|
}
|
|
else
|
|
{
|
|
ignore = true;
|
|
Con_Reportf( "Delta_ParseTableField: wrong nameIndex %d for table %s, ignoring\n", nameIndex, dt->pName );
|
|
}
|
|
|
|
flags = MSG_ReadUBitLong( msg, 10 );
|
|
bits = MSG_ReadUBitLong( msg, 5 ) + 1;
|
|
|
|
// read the multipliers
|
|
if( MSG_ReadOneBit( msg ))
|
|
mul = MSG_ReadFloat( msg );
|
|
|
|
if( MSG_ReadOneBit( msg ))
|
|
post_mul = MSG_ReadFloat( msg );
|
|
|
|
if( ignore )
|
|
return;
|
|
|
|
// delta encoders it's already initialized on this machine (local game)
|
|
if( delta_init )
|
|
Delta_Shutdown();
|
|
|
|
// add field to table
|
|
Delta_AddField( dt->pName, pName, flags, bits, mul, post_mul );
|
|
}
|
|
|
|
qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delta_t *pField, qboolean bPost )
|
|
{
|
|
string token;
|
|
delta_field_t *pFieldInfo;
|
|
char *oldpos;
|
|
|
|
*delta_script = COM_ParseFile( *delta_script, token );
|
|
if( Q_strcmp( token, "(" ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ParseField: expected '(', found '%s' instead\n", token );
|
|
return false;
|
|
}
|
|
|
|
// read the variable name
|
|
if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ParseField: missing field name\n" );
|
|
return false;
|
|
}
|
|
|
|
pFieldInfo = Delta_FindFieldInfo( pInfo, token );
|
|
if( !pFieldInfo )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ParseField: unable to find field %s\n", token );
|
|
return false;
|
|
}
|
|
|
|
*delta_script = COM_ParseFile( *delta_script, token );
|
|
if( Q_strcmp( token, "," ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ParseField: expected ',', found '%s' instead\n", token );
|
|
return false;
|
|
}
|
|
|
|
// copy base info to new field
|
|
pField->name = pFieldInfo->name;
|
|
pField->offset = pFieldInfo->offset;
|
|
pField->size = pFieldInfo->size;
|
|
pField->flags = 0;
|
|
|
|
// read delta-flags
|
|
while(( *delta_script = COM_ParseFile( *delta_script, token )) != NULL )
|
|
{
|
|
if( !Q_strcmp( token, "," ))
|
|
break; // end of flags argument
|
|
|
|
if( !Q_strcmp( token, "|" ))
|
|
continue;
|
|
|
|
if( !Q_strcmp( token, "DT_BYTE" ))
|
|
pField->flags |= DT_BYTE;
|
|
else if( !Q_strcmp( token, "DT_SHORT" ))
|
|
pField->flags |= DT_SHORT;
|
|
else if( !Q_strcmp( token, "DT_FLOAT" ))
|
|
pField->flags |= DT_FLOAT;
|
|
else if( !Q_strcmp( token, "DT_INTEGER" ))
|
|
pField->flags |= DT_INTEGER;
|
|
else if( !Q_strcmp( token, "DT_ANGLE" ))
|
|
pField->flags |= DT_ANGLE;
|
|
else if( !Q_strcmp( token, "DT_TIMEWINDOW_8" ))
|
|
pField->flags |= DT_TIMEWINDOW_8;
|
|
else if( !Q_strcmp( token, "DT_TIMEWINDOW_BIG" ))
|
|
pField->flags |= DT_TIMEWINDOW_BIG;
|
|
else if( !Q_strcmp( token, "DT_STRING" ))
|
|
pField->flags |= DT_STRING;
|
|
else if( !Q_strcmp( token, "DT_SIGNED" ))
|
|
pField->flags |= DT_SIGNED;
|
|
}
|
|
|
|
if( Q_strcmp( token, "," ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ParseField: expected ',', found '%s' instead\n", token );
|
|
return false;
|
|
}
|
|
|
|
// read delta-bits
|
|
if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ReadField: %s field bits argument is missing\n", pField->name );
|
|
return false;
|
|
}
|
|
|
|
pField->bits = Q_atoi( token );
|
|
|
|
*delta_script = COM_ParseFile( *delta_script, token );
|
|
if( Q_strcmp( token, "," ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ReadField: expected ',', found '%s' instead\n", token );
|
|
return false;
|
|
}
|
|
|
|
// read delta-multiplier
|
|
if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ReadField: %s missing 'multiplier' argument\n", pField->name );
|
|
return false;
|
|
}
|
|
|
|
pField->multiplier = Q_atof( token );
|
|
|
|
if( bPost )
|
|
{
|
|
*delta_script = COM_ParseFile( *delta_script, token );
|
|
if( Q_strcmp( token, "," ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ReadField: expected ',', found '%s' instead\n", token );
|
|
return false;
|
|
}
|
|
|
|
// read delta-postmultiplier
|
|
if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ReadField: %s missing 'post_multiply' argument\n", pField->name );
|
|
return false;
|
|
}
|
|
|
|
pField->post_multiplier = Q_atof( token );
|
|
}
|
|
else
|
|
{
|
|
// to avoid division by zero
|
|
pField->post_multiplier = 1.0f;
|
|
}
|
|
|
|
// closing brace...
|
|
*delta_script = COM_ParseFile( *delta_script, token );
|
|
if( Q_strcmp( token, ")" ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_ParseField: expected ')', found '%s' instead\n", token );
|
|
return false;
|
|
}
|
|
|
|
// ... and trying to parse optional ',' post-symbol
|
|
oldpos = *delta_script;
|
|
*delta_script = COM_ParseFile( *delta_script, token );
|
|
if( token[0] != ',' ) *delta_script = oldpos; // not a ','
|
|
|
|
return true;
|
|
}
|
|
|
|
void Delta_ParseTable( char **delta_script, delta_info_t *dt, const char *encodeDll, const char *encodeFunc )
|
|
{
|
|
string token;
|
|
delta_t *pField;
|
|
const delta_field_t *pInfo;
|
|
|
|
// allocate the delta-structures
|
|
if( !dt->pFields ) dt->pFields = (delta_t *)Z_Calloc( dt->maxFields * sizeof( delta_t ));
|
|
|
|
pField = dt->pFields;
|
|
pInfo = dt->pInfo;
|
|
dt->numFields = 0;
|
|
|
|
// assume we have handled '{'
|
|
while(( *delta_script = COM_ParseFile( *delta_script, token )) != NULL )
|
|
{
|
|
Assert( dt->numFields <= dt->maxFields );
|
|
|
|
if( !Q_strcmp( token, "DEFINE_DELTA" ))
|
|
{
|
|
if( Delta_ParseField( delta_script, pInfo, &pField[dt->numFields], false ))
|
|
dt->numFields++;
|
|
}
|
|
else if( !Q_strcmp( token, "DEFINE_DELTA_POST" ))
|
|
{
|
|
if( Delta_ParseField( delta_script, pInfo, &pField[dt->numFields], true ))
|
|
dt->numFields++;
|
|
}
|
|
else if( token[0] == '}' )
|
|
{
|
|
// end of the section
|
|
break;
|
|
}
|
|
}
|
|
|
|
// copy function name
|
|
Q_strncpy( dt->funcName, encodeFunc, sizeof( dt->funcName ));
|
|
|
|
if( !Q_stricmp( encodeDll, "none" ))
|
|
dt->customEncode = CUSTOM_NONE;
|
|
else if( !Q_stricmp( encodeDll, "gamedll" ))
|
|
dt->customEncode = CUSTOM_SERVER_ENCODE;
|
|
else if( !Q_stricmp( encodeDll, "clientdll" ))
|
|
dt->customEncode = CUSTOM_CLIENT_ENCODE;
|
|
|
|
// adjust to fit memory size
|
|
if( dt->numFields < dt->maxFields )
|
|
{
|
|
dt->pFields = Z_Realloc( dt->pFields, dt->numFields * sizeof( delta_t ));
|
|
}
|
|
|
|
dt->bInitialized = true; // table is ok
|
|
}
|
|
|
|
void Delta_InitFields( void )
|
|
{
|
|
byte *afile;
|
|
char *pfile;
|
|
string encodeDll, encodeFunc, token;
|
|
delta_info_t *dt;
|
|
|
|
afile = FS_LoadFile( DELTA_PATH, NULL, false );
|
|
if( !afile ) Sys_Error( "DELTA_Load: couldn't load file %s\n", DELTA_PATH );
|
|
|
|
pfile = (char *)afile;
|
|
|
|
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
|
|
{
|
|
dt = Delta_FindStruct( token );
|
|
|
|
if( dt == NULL )
|
|
{
|
|
Sys_Error( "%s: unknown struct %s\n", DELTA_PATH, token );
|
|
}
|
|
|
|
pfile = COM_ParseFile( pfile, encodeDll );
|
|
|
|
if( !Q_stricmp( encodeDll, "none" ))
|
|
Q_strcpy( encodeFunc, "null" );
|
|
else pfile = COM_ParseFile( pfile, encodeFunc );
|
|
|
|
// jump to '{'
|
|
pfile = COM_ParseFile( pfile, token );
|
|
|
|
if( token[0] != '{' )
|
|
{
|
|
Sys_Error( "%s: missing '{' in section %s\n", DELTA_PATH, dt->pName );
|
|
}
|
|
|
|
Delta_ParseTable( &pfile, dt, encodeDll, encodeFunc );
|
|
}
|
|
|
|
Mem_Free( afile );
|
|
}
|
|
|
|
void Delta_Init( void )
|
|
{
|
|
delta_info_t *dt;
|
|
|
|
// shutdown it first
|
|
if( delta_init ) Delta_Shutdown ();
|
|
|
|
Delta_InitFields (); // initialize fields
|
|
delta_init = true;
|
|
|
|
dt = Delta_FindStruct( "movevars_t" );
|
|
|
|
Assert( dt != NULL );
|
|
if( dt->bInitialized ) return; // "movevars_t" already specified by user
|
|
|
|
// create movevars_t delta internal
|
|
Delta_AddField( "movevars_t", "gravity", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "stopspeed", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "maxspeed", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "spectatormaxspeed", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "accelerate", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "airaccelerate", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "wateraccelerate", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "friction", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "edgefriction", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "waterfriction", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "bounce", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "stepsize", DT_FLOAT|DT_SIGNED, 16, 16.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "maxvelocity", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
|
|
if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD ))
|
|
Delta_AddField( "movevars_t", "zmax", DT_FLOAT|DT_SIGNED, 18, 1.0f, 1.0f );
|
|
else Delta_AddField( "movevars_t", "zmax", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f );
|
|
|
|
Delta_AddField( "movevars_t", "waveHeight", DT_FLOAT|DT_SIGNED, 16, 16.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "skyName", DT_STRING, 1, 1.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "footsteps", DT_INTEGER, 1, 1.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "rollangle", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "rollspeed", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "skycolor_r", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f ); // 0 - 264
|
|
Delta_AddField( "movevars_t", "skycolor_g", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "skycolor_b", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "skyvec_x", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); // 0 - 1
|
|
Delta_AddField( "movevars_t", "skyvec_y", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "wateralpha", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
|
|
Delta_AddField( "movevars_t", "fog_settings", DT_INTEGER, 32, 1.0f, 1.0f );
|
|
dt->numFields = NUM_FIELDS( pm_fields ) - 4;
|
|
|
|
// now done
|
|
dt->bInitialized = true;
|
|
}
|
|
|
|
void Delta_InitClient( void )
|
|
{
|
|
int i, numActive = 0;
|
|
|
|
// already initalized
|
|
if( delta_init ) return;
|
|
|
|
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
|
{
|
|
if( dt_info[i].numFields > 0 )
|
|
{
|
|
dt_info[i].bInitialized = true;
|
|
numActive++;
|
|
}
|
|
}
|
|
|
|
if( numActive ) delta_init = true;
|
|
}
|
|
|
|
void Delta_Shutdown( void )
|
|
{
|
|
int i;
|
|
|
|
if( !delta_init ) return;
|
|
|
|
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
|
{
|
|
dt_info[i].numFields = 0;
|
|
dt_info[i].customEncode = CUSTOM_NONE;
|
|
dt_info[i].userCallback = NULL;
|
|
dt_info[i].funcName[0] = '\0';
|
|
|
|
if( dt_info[i].pFields )
|
|
{
|
|
Z_Free( dt_info[i].pFields );
|
|
dt_info[i].pFields = NULL;
|
|
}
|
|
|
|
dt_info[i].bInitialized = false;
|
|
}
|
|
|
|
delta_init = false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Delta_ClampIntegerField
|
|
|
|
prevent data to out of range
|
|
=====================
|
|
*/
|
|
int Delta_ClampIntegerField( delta_t *pField, int iValue, qboolean bSigned, int numbits )
|
|
{
|
|
#ifdef _DEBUG
|
|
if( numbits < 32 && abs( iValue ) >= (uint)BIT( numbits ))
|
|
Con_Reportf( "%s %d overflow %d\n", pField->name, abs( iValue ), (uint)BIT( numbits ));
|
|
#endif
|
|
if( numbits < 32 )
|
|
{
|
|
int signbits = bSigned ? (numbits - 1) : numbits;
|
|
int maxnum = BIT( signbits ) - 1;
|
|
int minnum = bSigned ? -maxnum : 0;
|
|
iValue = bound( minnum, iValue, maxnum );
|
|
}
|
|
|
|
return iValue; // clamped;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Delta_CompareField
|
|
|
|
compare fields by offsets
|
|
assume from and to is valid
|
|
=====================
|
|
*/
|
|
qboolean Delta_CompareField( delta_t *pField, void *from, void *to, float timebase )
|
|
{
|
|
qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false;
|
|
float val_a, val_b;
|
|
int fromF, toF;
|
|
|
|
Assert( pField != NULL );
|
|
Assert( from != NULL );
|
|
Assert( to != NULL );
|
|
|
|
if( pField->bInactive )
|
|
return true;
|
|
|
|
fromF = toF = 0;
|
|
|
|
if( pField->flags & DT_BYTE )
|
|
{
|
|
if( pField->flags & DT_SIGNED )
|
|
{
|
|
fromF = *(signed char *)((byte *)from + pField->offset );
|
|
toF = *(signed char *)((byte *)to + pField->offset );
|
|
}
|
|
else
|
|
{
|
|
fromF = *(byte *)((byte *)from + pField->offset );
|
|
toF = *(byte *)((byte *)to + pField->offset );
|
|
}
|
|
|
|
fromF = Delta_ClampIntegerField( pField, fromF, bSigned, pField->bits );
|
|
toF = Delta_ClampIntegerField( pField, toF, bSigned, pField->bits );
|
|
if( pField->multiplier != 1.0f ) fromF *= pField->multiplier;
|
|
if( pField->multiplier != 1.0f ) toF *= pField->multiplier;
|
|
}
|
|
else if( pField->flags & DT_SHORT )
|
|
{
|
|
if( pField->flags & DT_SIGNED )
|
|
{
|
|
fromF = *(short *)((byte *)from + pField->offset );
|
|
toF = *(short *)((byte *)to + pField->offset );
|
|
}
|
|
else
|
|
{
|
|
fromF = *(word *)((byte *)from + pField->offset );
|
|
toF = *(word *)((byte *)to + pField->offset );
|
|
}
|
|
|
|
fromF = Delta_ClampIntegerField( pField, fromF, bSigned, pField->bits );
|
|
toF = Delta_ClampIntegerField( pField, toF, bSigned, pField->bits );
|
|
if( pField->multiplier != 1.0f ) fromF *= pField->multiplier;
|
|
if( pField->multiplier != 1.0f ) toF *= pField->multiplier;
|
|
}
|
|
else if( pField->flags & DT_INTEGER )
|
|
{
|
|
#if defined __GNUC__ && __GNUC_MAJOR < 9 && !defined __clang__
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wduplicated-branches"
|
|
#endif
|
|
if( pField->flags & DT_SIGNED )
|
|
{
|
|
fromF = *(int *)((byte *)from + pField->offset );
|
|
toF = *(int *)((byte *)to + pField->offset );
|
|
}
|
|
else
|
|
{
|
|
fromF = *(uint *)((byte *)from + pField->offset );
|
|
toF = *(uint *)((byte *)to + pField->offset );
|
|
}
|
|
#if defined __GNUC__ && __GNUC_MAJOR < 9 && !defined __clang__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
fromF = Delta_ClampIntegerField( pField, fromF, bSigned, pField->bits );
|
|
toF = Delta_ClampIntegerField( pField, toF, bSigned, pField->bits );
|
|
if( pField->multiplier != 1.0f ) fromF *= pField->multiplier;
|
|
if( pField->multiplier != 1.0f ) toF *= pField->multiplier;
|
|
}
|
|
else if( pField->flags & ( DT_ANGLE|DT_FLOAT ))
|
|
{
|
|
// don't convert floats to integers
|
|
fromF = *((int *)((byte *)from + pField->offset ));
|
|
toF = *((int *)((byte *)to + pField->offset ));
|
|
}
|
|
else if( pField->flags & DT_TIMEWINDOW_8 )
|
|
{
|
|
val_a = Q_rint((*(float *)((byte *)from + pField->offset )) * 100.0f );
|
|
val_b = Q_rint((*(float *)((byte *)to + pField->offset )) * 100.0f );
|
|
val_a -= Q_rint(timebase * 100.0f);
|
|
val_b -= Q_rint(timebase * 100.0f);
|
|
fromF = *((int *)&val_a);
|
|
toF = *((int *)&val_b);
|
|
}
|
|
else if( pField->flags & DT_TIMEWINDOW_BIG )
|
|
{
|
|
val_a = (*(float *)((byte *)from + pField->offset ));
|
|
val_b = (*(float *)((byte *)to + pField->offset ));
|
|
|
|
if( pField->multiplier != 1.0f )
|
|
{
|
|
val_a *= pField->multiplier;
|
|
val_b *= pField->multiplier;
|
|
val_a = (timebase * pField->multiplier) - val_a;
|
|
val_b = (timebase * pField->multiplier) - val_b;
|
|
}
|
|
else
|
|
{
|
|
val_a = timebase - val_a;
|
|
val_b = timebase - val_b;
|
|
}
|
|
|
|
fromF = *((int *)&val_a);
|
|
toF = *((int *)&val_b);
|
|
}
|
|
else if( pField->flags & DT_STRING )
|
|
{
|
|
// compare strings
|
|
char *s1 = (char *)((byte *)from + pField->offset );
|
|
char *s2 = (char *)((byte *)to + pField->offset );
|
|
|
|
// 0 is equal, otherwise not equal
|
|
toF = Q_strcmp( s1, s2 );
|
|
}
|
|
|
|
return ( fromF == toF ) ? true : false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Delta_TestBaseline
|
|
|
|
compare baselines to find optimal
|
|
=====================
|
|
*/
|
|
int Delta_TestBaseline( entity_state_t *from, entity_state_t *to, qboolean player, float timebase )
|
|
{
|
|
delta_info_t *dt = NULL;
|
|
delta_t *pField;
|
|
int i, countBits;
|
|
int numChanges = 0;
|
|
|
|
countBits = MAX_ENTITY_BITS + 2;
|
|
|
|
if( to == NULL )
|
|
{
|
|
if( from == NULL ) return 0;
|
|
return countBits;
|
|
}
|
|
|
|
if( FBitSet( to->entityType, ENTITY_BEAM ))
|
|
dt = Delta_FindStruct( "custom_entity_state_t" );
|
|
else if( player )
|
|
dt = Delta_FindStruct( "entity_state_player_t" );
|
|
else dt = Delta_FindStruct( "entity_state_t" );
|
|
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
countBits++; // entityType flag
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
// flag about field change (sets always)
|
|
countBits++;
|
|
|
|
if( !Delta_CompareField( pField, from, to, timebase ))
|
|
{
|
|
// strings are handled difference
|
|
if( FBitSet( pField->flags, DT_STRING ))
|
|
countBits += Q_strlen((char *)((byte *)to + pField->offset )) * 8;
|
|
else countBits += pField->bits;
|
|
}
|
|
}
|
|
|
|
// g-cont. compare bitcount directly no reason to call BitByte here
|
|
return countBits;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Delta_WriteField
|
|
|
|
write fields by offsets
|
|
assume from and to is valid
|
|
=====================
|
|
*/
|
|
qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase )
|
|
{
|
|
qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false;
|
|
float flValue, flAngle, flTime;
|
|
uint iValue;
|
|
const char *pStr;
|
|
|
|
if( Delta_CompareField( pField, from, to, timebase ))
|
|
{
|
|
MSG_WriteOneBit( msg, 0 ); // unchanged
|
|
return false;
|
|
}
|
|
|
|
MSG_WriteOneBit( msg, 1 ); // changed
|
|
|
|
if( pField->flags & DT_BYTE )
|
|
{
|
|
iValue = *(byte *)((byte *)to + pField->offset );
|
|
iValue = Delta_ClampIntegerField( pField, iValue, bSigned, pField->bits );
|
|
if( pField->multiplier != 1.0f ) iValue *= pField->multiplier;
|
|
MSG_WriteBitLong( msg, iValue, pField->bits, bSigned );
|
|
}
|
|
else if( pField->flags & DT_SHORT )
|
|
{
|
|
iValue = *(word *)((byte *)to + pField->offset );
|
|
iValue = Delta_ClampIntegerField( pField, iValue, bSigned, pField->bits );
|
|
if( pField->multiplier != 1.0f ) iValue *= pField->multiplier;
|
|
MSG_WriteBitLong( msg, iValue, pField->bits, bSigned );
|
|
}
|
|
else if( pField->flags & DT_INTEGER )
|
|
{
|
|
iValue = *(uint *)((byte *)to + pField->offset );
|
|
iValue = Delta_ClampIntegerField( pField, iValue, bSigned, pField->bits );
|
|
if( pField->multiplier != 1.0f ) iValue *= pField->multiplier;
|
|
MSG_WriteBitLong( msg, iValue, pField->bits, bSigned );
|
|
}
|
|
else if( pField->flags & DT_FLOAT )
|
|
{
|
|
flValue = *(float *)((byte *)to + pField->offset );
|
|
iValue = (int)(flValue * pField->multiplier);
|
|
iValue = Delta_ClampIntegerField( pField, iValue, bSigned, pField->bits );
|
|
MSG_WriteBitLong( msg, iValue, pField->bits, bSigned );
|
|
}
|
|
else if( pField->flags & DT_ANGLE )
|
|
{
|
|
flAngle = *(float *)((byte *)to + pField->offset );
|
|
|
|
// NOTE: never applies multipliers to angle because
|
|
// result may be wrong on client-side
|
|
MSG_WriteBitAngle( msg, flAngle, pField->bits );
|
|
}
|
|
else if( pField->flags & DT_TIMEWINDOW_8 )
|
|
{
|
|
flValue = *(float *)((byte *)to + pField->offset );
|
|
flTime = Q_rint( timebase * 100.0f ) - Q_rint( flValue * 100.0f );
|
|
iValue = (uint)abs( flTime );
|
|
iValue = Delta_ClampIntegerField( pField, iValue, bSigned, pField->bits );
|
|
MSG_WriteBitLong( msg, iValue, pField->bits, bSigned );
|
|
}
|
|
else if( pField->flags & DT_TIMEWINDOW_BIG )
|
|
{
|
|
flValue = *(float *)((byte *)to + pField->offset );
|
|
flTime = Q_rint( timebase * pField->multiplier ) - Q_rint( flValue * pField->multiplier );
|
|
iValue = (uint)abs( flTime );
|
|
iValue = Delta_ClampIntegerField( pField, iValue, bSigned, pField->bits );
|
|
MSG_WriteBitLong( msg, iValue, pField->bits, bSigned );
|
|
}
|
|
else if( pField->flags & DT_STRING )
|
|
{
|
|
pStr = (char *)((byte *)to + pField->offset );
|
|
MSG_WriteString( msg, pStr );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Delta_ReadField
|
|
|
|
read fields by offsets
|
|
assume 'from' and 'to' is valid
|
|
=====================
|
|
*/
|
|
qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase )
|
|
{
|
|
qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false;
|
|
float flValue, flAngle, flTime;
|
|
qboolean bChanged;
|
|
uint iValue;
|
|
const char *pStr;
|
|
char *pOut;
|
|
|
|
bChanged = MSG_ReadOneBit( msg );
|
|
|
|
Assert( pField->multiplier != 0.0f );
|
|
|
|
if( pField->flags & DT_BYTE )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
|
if( pField->multiplier != 1.0f ) iValue /= pField->multiplier;
|
|
}
|
|
else
|
|
{
|
|
iValue = *(byte *)((byte *)from + pField->offset );
|
|
}
|
|
*(byte *)((byte *)to + pField->offset ) = iValue;
|
|
}
|
|
else if( pField->flags & DT_SHORT )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
|
if( pField->multiplier != 1.0f ) iValue /= pField->multiplier;
|
|
}
|
|
else
|
|
{
|
|
iValue = *(word *)((byte *)from + pField->offset );
|
|
}
|
|
*(word *)((byte *)to + pField->offset ) = iValue;
|
|
}
|
|
else if( pField->flags & DT_INTEGER )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
|
if( pField->multiplier != 1.0f ) iValue /= pField->multiplier;
|
|
}
|
|
else
|
|
{
|
|
iValue = *(uint *)((byte *)from + pField->offset );
|
|
}
|
|
*(uint *)((byte *)to + pField->offset ) = iValue;
|
|
}
|
|
else if( pField->flags & DT_FLOAT )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
|
flValue = (int)iValue * ( 1.0f / pField->multiplier );
|
|
flValue = flValue * pField->post_multiplier;
|
|
}
|
|
else
|
|
{
|
|
flValue = *(float *)((byte *)from + pField->offset );
|
|
}
|
|
*(float *)((byte *)to + pField->offset ) = flValue;
|
|
}
|
|
else if( pField->flags & DT_ANGLE )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
flAngle = MSG_ReadBitAngle( msg, pField->bits );
|
|
}
|
|
else
|
|
{
|
|
flAngle = *(float *)((byte *)from + pField->offset );
|
|
}
|
|
*(float *)((byte *)to + pField->offset ) = flAngle;
|
|
}
|
|
else if( pField->flags & DT_TIMEWINDOW_8 )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
|
flValue = (float)((int)(iValue * 0.01f ));
|
|
flTime = timebase + flValue;
|
|
}
|
|
else
|
|
{
|
|
flTime = *(float *)((byte *)from + pField->offset );
|
|
}
|
|
*(float *)((byte *)to + pField->offset ) = flTime;
|
|
}
|
|
else if( pField->flags & DT_TIMEWINDOW_BIG )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
|
flValue = (float)((int)iValue) * ( 1.0f / pField->multiplier );
|
|
flTime = timebase + flValue;
|
|
}
|
|
else
|
|
{
|
|
flTime = *(float *)((byte *)from + pField->offset );
|
|
}
|
|
*(float *)((byte *)to + pField->offset ) = flTime;
|
|
}
|
|
else if( pField->flags & DT_STRING )
|
|
{
|
|
if( bChanged )
|
|
{
|
|
pStr = MSG_ReadString( msg );
|
|
}
|
|
else
|
|
{
|
|
pStr = (char *)((byte *)from + pField->offset );
|
|
}
|
|
|
|
pOut = (char *)((byte *)to + pField->offset );
|
|
Q_strncpy( pOut, pStr, pField->size );
|
|
}
|
|
return bChanged;
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
usercmd_t communication
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
=====================
|
|
MSG_WriteDeltaUsercmd
|
|
=====================
|
|
*/
|
|
void MSG_WriteDeltaUsercmd( sizebuf_t *msg, usercmd_t *from, usercmd_t *to )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "usercmd_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_WriteField( msg, pField, from, to, 0.0f );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
MSG_ReadDeltaUsercmd
|
|
=====================
|
|
*/
|
|
void MSG_ReadDeltaUsercmd( sizebuf_t *msg, usercmd_t *from, usercmd_t *to )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "usercmd_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
*to = *from;
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_ReadField( msg, pField, from, to, 0.0f );
|
|
}
|
|
|
|
COM_NormalizeAngles( to->viewangles );
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
|
|
event_args_t communication
|
|
|
|
============================================================================
|
|
*/
|
|
/*
|
|
=====================
|
|
MSG_WriteDeltaEvent
|
|
=====================
|
|
*/
|
|
void MSG_WriteDeltaEvent( sizebuf_t *msg, event_args_t *from, event_args_t *to )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "event_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_WriteField( msg, pField, from, to, 0.0f );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
MSG_ReadDeltaEvent
|
|
=====================
|
|
*/
|
|
void MSG_ReadDeltaEvent( sizebuf_t *msg, event_args_t *from, event_args_t *to )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "event_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
*to = *from;
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_ReadField( msg, pField, from, to, 0.0f );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
movevars_t communication
|
|
|
|
=============================================================================
|
|
*/
|
|
qboolean MSG_WriteDeltaMovevars( sizebuf_t *msg, movevars_t *from, movevars_t *to )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i, startBit;
|
|
int numChanges = 0;
|
|
|
|
dt = Delta_FindStruct( "movevars_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
startBit = msg->iCurBit;
|
|
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
|
|
MSG_BeginServerCmd( msg, svc_deltamovevars );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( Delta_WriteField( msg, pField, from, to, 0.0f ))
|
|
numChanges++;
|
|
}
|
|
|
|
// if we have no changes - kill the message
|
|
if( !numChanges )
|
|
{
|
|
MSG_SeekToBit( msg, startBit, SEEK_SET );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MSG_ReadDeltaMovevars( sizebuf_t *msg, movevars_t *from, movevars_t *to )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "movevars_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
*to = *from;
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_ReadField( msg, pField, from, to, 0.0f );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
clientdata_t communication
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
==================
|
|
MSG_WriteClientData
|
|
|
|
Writes current client data only for local client
|
|
Other clients can grab the client state from entity_state_t
|
|
==================
|
|
*/
|
|
void MSG_WriteClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, float timebase )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i, startBit;
|
|
int numChanges = 0;
|
|
|
|
dt = Delta_FindStruct( "clientdata_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
startBit = msg->iCurBit;
|
|
|
|
MSG_WriteOneBit( msg, 1 ); // have clientdata
|
|
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( Delta_WriteField( msg, pField, from, to, timebase ))
|
|
numChanges++;
|
|
}
|
|
|
|
if( numChanges ) return; // we have updates
|
|
|
|
MSG_SeekToBit( msg, startBit, SEEK_SET );
|
|
MSG_WriteOneBit( msg, 0 ); // no changes
|
|
}
|
|
|
|
/*
|
|
==================
|
|
MSG_ReadClientData
|
|
|
|
Read the clientdata
|
|
==================
|
|
*/
|
|
void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, float timebase )
|
|
{
|
|
#if !XASH_DEDICATED
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "clientdata_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
*to = *from;
|
|
|
|
if( !cls.legacymode && !MSG_ReadOneBit( msg ))
|
|
return; // we have no changes
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_ReadField( msg, pField, from, to, timebase );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
weapon_data_t communication
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
==================
|
|
MSG_WriteWeaponData
|
|
|
|
Writes current client data only for local client
|
|
Other clients can grab the client state from entity_state_t
|
|
==================
|
|
*/
|
|
void MSG_WriteWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to, float timebase, int index )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i, startBit;
|
|
int numChanges = 0;
|
|
|
|
dt = Delta_FindStruct( "weapon_data_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
|
|
startBit = msg->iCurBit;
|
|
|
|
MSG_WriteOneBit( msg, 1 );
|
|
MSG_WriteUBitLong( msg, index, MAX_WEAPON_BITS );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( Delta_WriteField( msg, pField, from, to, timebase ))
|
|
numChanges++;
|
|
}
|
|
|
|
// if we have no changes - kill the message
|
|
if( !numChanges ) MSG_SeekToBit( msg, startBit, SEEK_SET );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
MSG_ReadWeaponData
|
|
|
|
Read the clientdata
|
|
==================
|
|
*/
|
|
void MSG_ReadWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to, float timebase )
|
|
{
|
|
delta_t *pField;
|
|
delta_info_t *dt;
|
|
int i;
|
|
|
|
dt = Delta_FindStruct( "weapon_data_t" );
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
*to = *from;
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_ReadField( msg, pField, from, to, timebase );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
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, qboolean force, int delta_type, float timebase, int baseline )
|
|
{
|
|
delta_info_t *dt = NULL;
|
|
delta_t *pField;
|
|
int i, startBit;
|
|
int numChanges = 0;
|
|
|
|
if( to == NULL )
|
|
{
|
|
int fRemoveType;
|
|
|
|
if( from == NULL ) return;
|
|
|
|
// a NULL to is a delta remove message
|
|
MSG_WriteUBitLong( msg, from->number, MAX_ENTITY_BITS );
|
|
|
|
// fRemoveType:
|
|
// 0 - keep alive, has delta-update
|
|
// 1 - remove from delta message (but keep states)
|
|
// 2 - completely remove from server
|
|
if( force ) fRemoveType = 2;
|
|
else fRemoveType = 1;
|
|
|
|
MSG_WriteUBitLong( msg, fRemoveType, 2 );
|
|
return;
|
|
}
|
|
|
|
startBit = msg->iCurBit;
|
|
|
|
if( to->number < 0 || to->number >= GI->max_edicts )
|
|
Host_Error( "MSG_WriteDeltaEntity: Bad entity number: %i\n", to->number );
|
|
|
|
MSG_WriteUBitLong( msg, to->number, MAX_ENTITY_BITS );
|
|
MSG_WriteUBitLong( msg, 0, 2 ); // alive
|
|
|
|
if( baseline != 0 )
|
|
{
|
|
MSG_WriteOneBit( msg, 1 );
|
|
MSG_WriteSBitLong( msg, baseline, 7 );
|
|
}
|
|
else MSG_WriteOneBit( msg, 0 );
|
|
|
|
if( force || ( to->entityType != from->entityType ))
|
|
{
|
|
MSG_WriteOneBit( msg, 1 );
|
|
MSG_WriteUBitLong( msg, to->entityType, 2 );
|
|
numChanges++;
|
|
}
|
|
else MSG_WriteOneBit( msg, 0 );
|
|
|
|
if( FBitSet( to->entityType, ENTITY_BEAM ))
|
|
{
|
|
dt = Delta_FindStruct( "custom_entity_state_t" );
|
|
}
|
|
else if( delta_type == DELTA_PLAYER )
|
|
{
|
|
dt = Delta_FindStruct( "entity_state_player_t" );
|
|
}
|
|
else
|
|
{
|
|
dt = Delta_FindStruct( "entity_state_t" );
|
|
}
|
|
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
if( delta_type == DELTA_STATIC )
|
|
{
|
|
// static entities won't to be custom encoded
|
|
for( i = 0; i < dt->numFields; i++ )
|
|
dt->pFields[i].bInactive = false;
|
|
}
|
|
else
|
|
{
|
|
// activate fields and call custom encode func
|
|
Delta_CustomEncode( dt, from, to );
|
|
}
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( Delta_WriteField( msg, pField, from, to, timebase ))
|
|
numChanges++;
|
|
}
|
|
|
|
// if we have no changes - kill the message
|
|
if( !numChanges && !force ) MSG_SeekToBit( msg, startBit, SEEK_SET );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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 MAX_EDICTS
|
|
Can go from either a baseline or a previous packet_entity
|
|
==================
|
|
*/
|
|
qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, int delta_type, float timebase )
|
|
{
|
|
#if !XASH_DEDICATED
|
|
delta_info_t *dt = NULL;
|
|
delta_t *pField;
|
|
int i, fRemoveType;
|
|
int baseline_offset = 0;
|
|
|
|
if( number < 0 || number >= clgame.maxEntities )
|
|
Host_Error( "MSG_ReadDeltaEntity: bad delta entity number: %i\n", number );
|
|
|
|
fRemoveType = MSG_ReadUBitLong( msg, 2 );
|
|
|
|
if( fRemoveType )
|
|
{
|
|
// check for a remove
|
|
memset( to, 0, sizeof( *to ));
|
|
|
|
if( fRemoveType & 1 )
|
|
{
|
|
// removed from delta-message
|
|
return false;
|
|
}
|
|
|
|
if( fRemoveType & 2 )
|
|
{
|
|
// entity was removed from server
|
|
to->number = -1;
|
|
return false;
|
|
}
|
|
|
|
Host_Error( "MSG_ReadDeltaEntity: unknown update type %i\n", fRemoveType );
|
|
}
|
|
|
|
if( !cls.legacymode )
|
|
{
|
|
if( MSG_ReadOneBit( msg ))
|
|
baseline_offset = MSG_ReadSBitLong( msg, 7 );
|
|
|
|
if( baseline_offset != 0 )
|
|
{
|
|
if( delta_type == DELTA_STATIC )
|
|
{
|
|
int backup = Q_max( 0, clgame.numStatics - abs( baseline_offset ));
|
|
from = &clgame.static_entities[backup].baseline;
|
|
}
|
|
else if( baseline_offset > 0 )
|
|
{
|
|
int backup = cls.next_client_entities - baseline_offset;
|
|
from = &cls.packet_entities[backup % cls.num_client_entities];
|
|
}
|
|
else
|
|
{
|
|
baseline_offset = abs( baseline_offset );
|
|
if( baseline_offset < cl.instanced_baseline_count )
|
|
from = &cl.instanced_baseline[baseline_offset];
|
|
}
|
|
}
|
|
}
|
|
// g-cont. probably is redundant
|
|
*to = *from;
|
|
|
|
if( MSG_ReadOneBit( msg ))
|
|
to->entityType = MSG_ReadUBitLong( msg, 2 );
|
|
to->number = number;
|
|
|
|
if( cls.legacymode ? ( to->entityType == ENTITY_BEAM ) : FBitSet( to->entityType, ENTITY_BEAM ))
|
|
{
|
|
dt = Delta_FindStruct( "custom_entity_state_t" );
|
|
}
|
|
else if( delta_type == DELTA_PLAYER )
|
|
{
|
|
dt = Delta_FindStruct( "entity_state_player_t" );
|
|
}
|
|
else
|
|
{
|
|
dt = Delta_FindStruct( "entity_state_t" );
|
|
}
|
|
|
|
Assert( dt && dt->bInitialized );
|
|
|
|
pField = dt->pFields;
|
|
Assert( pField != NULL );
|
|
|
|
// process fields
|
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
|
{
|
|
Delta_ReadField( msg, pField, from, to, timebase );
|
|
}
|
|
#endif // XASH_DEDICATED
|
|
// message parsed
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
game.dll interface
|
|
|
|
=============================================================================
|
|
*/
|
|
void GAME_EXPORT Delta_AddEncoder( char *name, pfnDeltaEncode encodeFunc )
|
|
{
|
|
delta_info_t *dt;
|
|
|
|
dt = Delta_FindStructByEncoder( name );
|
|
|
|
if( !dt || !dt->bInitialized )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_AddEncoder: couldn't find delta with specified custom encode %s\n", name );
|
|
return;
|
|
}
|
|
|
|
if( dt->customEncode == CUSTOM_NONE )
|
|
{
|
|
Con_DPrintf( S_ERROR "Delta_AddEncoder: %s not supposed for custom encoding\n", dt->pName );
|
|
return;
|
|
}
|
|
|
|
// register new encode func
|
|
dt->userCallback = encodeFunc;
|
|
}
|
|
|
|
int GAME_EXPORT Delta_FindField( delta_t *pFields, const char *fieldname )
|
|
{
|
|
delta_info_t *dt;
|
|
delta_t *pField;
|
|
int i;
|
|
|
|
dt = Delta_FindStructByDelta( pFields );
|
|
if( dt == NULL || !fieldname || !fieldname[0] )
|
|
return -1;
|
|
|
|
for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( !Q_strcmp( pField->name, fieldname ))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void GAME_EXPORT Delta_SetField( delta_t *pFields, const char *fieldname )
|
|
{
|
|
delta_info_t *dt;
|
|
delta_t *pField;
|
|
int i;
|
|
|
|
dt = Delta_FindStructByDelta( pFields );
|
|
if( dt == NULL || !fieldname || !fieldname[0] )
|
|
return;
|
|
|
|
for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( !Q_strcmp( pField->name, fieldname ))
|
|
{
|
|
pField->bInactive = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GAME_EXPORT Delta_UnsetField( delta_t *pFields, const char *fieldname )
|
|
{
|
|
delta_info_t *dt;
|
|
delta_t *pField;
|
|
int i;
|
|
|
|
dt = Delta_FindStructByDelta( pFields );
|
|
if( dt == NULL || !fieldname || !fieldname[0] )
|
|
return;
|
|
|
|
for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ )
|
|
{
|
|
if( !Q_strcmp( pField->name, fieldname ))
|
|
{
|
|
pField->bInactive = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GAME_EXPORT Delta_SetFieldByIndex( delta_t *pFields, int fieldNumber )
|
|
{
|
|
delta_info_t *dt;
|
|
|
|
dt = Delta_FindStructByDelta( pFields );
|
|
if( dt == NULL || fieldNumber < 0 || fieldNumber >= dt->numFields )
|
|
return;
|
|
|
|
dt->pFields[fieldNumber].bInactive = false;
|
|
}
|
|
|
|
void GAME_EXPORT Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber )
|
|
{
|
|
delta_info_t *dt;
|
|
|
|
dt = Delta_FindStructByDelta( pFields );
|
|
if( dt == NULL || fieldNumber < 0 || fieldNumber >= dt->numFields )
|
|
return;
|
|
|
|
dt->pFields[fieldNumber].bInactive = true;
|
|
}
|