diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 5f719bf2..0dba952d 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -37,6 +37,7 @@ GNU General Public License for more details. #define DT_TIMEWINDOW_BIG BIT( 6 ) // and re-encoded on the client relative to the client's clock #define DT_STRING BIT( 7 ) // A null terminated string, sent as 8 byte chars #define DT_SIGNED BIT( 8 ) // sign modificator +#define DT_SIGNED_GS BIT( 31 ) // GoldSrc-specific sign modificator #define NUM_FIELDS( x ) ((sizeof( x ) / sizeof( x[0] )) - 1) @@ -47,6 +48,7 @@ GNU General Public License for more details. #define PHYS_DEF( x ) #x, offsetof( movevars_t, x ), sizeof( ((movevars_t *)0)->x ) #define CLDT_DEF( x ) #x, offsetof( clientdata_t, x ), sizeof( ((clientdata_t *)0)->x ) #define WPDT_DEF( x ) #x, offsetof( weapon_data_t, x ), sizeof( ((weapon_data_t *)0)->x ) +#define DESC_DEF( x ) #x, offsetof( goldsrc_delta_t, x ), sizeof( ((goldsrc_delta_t *)0)->x ) static qboolean delta_init = false; @@ -315,6 +317,18 @@ static const delta_field_t ent_fields[] = { NULL }, }; +static const delta_field_t meta_fields[] = +{ +{ DESC_DEF( fieldType ), }, +{ DESC_DEF( fieldName ), }, +{ DESC_DEF( fieldOffset ), }, +{ DESC_DEF( fieldSize ), }, +{ DESC_DEF( significant_bits ), }, +{ DESC_DEF( premultiply ), }, +{ DESC_DEF( postmultiply ), }, +{ NULL }, +}; + #if XASH_ENGINE_TESTS typedef struct delta_test_struct_t { @@ -352,22 +366,6 @@ static const delta_field_t test_fields[] = }; #endif -enum -{ - DT_EVENT_T = 0, - DT_MOVEVARS_T, - DT_USERCMD_T, - DT_CLIENTDATA_T, - DT_WEAPONDATA_T, - DT_ENTITY_STATE_T, - DT_ENTITY_STATE_PLAYER_T, - DT_CUSTOM_ENTITY_STATE_T, -#if XASH_ENGINE_TESTS - DT_DELTA_TEST_STRUCT_T, -#endif - DT_STRUCT_COUNT -}; - static delta_info_t dt_info[] = { [DT_EVENT_T] = { "event_t", ev_fields, NUM_FIELDS( ev_fields ) }, @@ -375,9 +373,10 @@ static delta_info_t dt_info[] = [DT_USERCMD_T] = { "usercmd_t", cmd_fields, NUM_FIELDS( cmd_fields ) }, [DT_CLIENTDATA_T] = { "clientdata_t", cd_fields, NUM_FIELDS( cd_fields ) }, [DT_WEAPONDATA_T] = { "weapon_data_t", wd_fields, NUM_FIELDS( wd_fields ) }, -[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) }, +[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) }, [DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) }, [DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) }, +[DT_GOLDSRC_DELTA_T] = { "goldsrc_delta_t", meta_fields, NUM_FIELDS( meta_fields ) }, #if XASH_ENGINE_TESTS [DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) }, #endif @@ -493,7 +492,7 @@ static qboolean Delta_AddField( delta_info_t *dt, const char *pName, int flags, int i; // check for coexisting field - for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ ) + for( i = 0, pField = dt->pFields; i < dt->numFields && pField; i++, pField++ ) { if( !Q_strcmp( pField->name, pName )) { @@ -621,6 +620,64 @@ void Delta_ParseTableField( sizebuf_t *msg ) Delta_AddField( dt, pName, flags, bits, mul, post_mul ); } +void Delta_InitMeta( void ) +{ + delta_info_t *dt = Delta_FindStructByIndex( DT_GOLDSRC_DELTA_T ); + + if( dt->bInitialized ) + return; + + Delta_AddField( dt, "fieldType", DT_INTEGER, 32, 1.0f, 1.0f ); + Delta_AddField( dt, "fieldName", DT_STRING, 1, 1.0f, 1.0f ); + Delta_AddField( dt, "fieldOffset", DT_INTEGER, 16, 1.0f, 1.0f ); + Delta_AddField( dt, "fieldSize", DT_INTEGER, 8, 1.0f, 1.0f ); + Delta_AddField( dt, "significant_bits", DT_INTEGER, 8, 1.0f, 1.0f ); + Delta_AddField( dt, "premultiply", DT_FLOAT, 32, 4000.0f, 1.0f ); + Delta_AddField( dt, "postmultiply", DT_FLOAT, 32, 4000.0f, 1.0f ); + dt->numFields = dt->maxFields; + dt->bInitialized = true; +} + +void Delta_ParseTableField_GS( sizebuf_t *msg ) +{ + const char *s = MSG_ReadString( msg ); + delta_info_t *dt = Delta_FindStruct( s ); + goldsrc_delta_t null = { 0 }; + int i, num_fields; + + + // delta encoders it's already initialized on this machine (local game) + if( delta_init ) + { + Delta_Shutdown(); + Delta_InitMeta(); + } + + if( !dt ) + Host_Error( "%s: not initialized", __func__ ); + + num_fields = MSG_ReadShort( msg ); + if( num_fields > dt->maxFields ) + Host_Error( "%s: numFields > maxFields", __func__ ); + + MSG_StartBitWriting( msg ); + + for( i = 0; i < num_fields; i++ ) + { + goldsrc_delta_t to; + + Delta_ReadGSFields( msg, DT_GOLDSRC_DELTA_T, &null, &to, 0.0f ); + + // patch our DT_SIGNED flag + if( FBitSet( to.fieldType, DT_SIGNED_GS )) + SetBits( to.fieldType, DT_SIGNED ); + + Delta_AddField( dt, to.fieldName, to.fieldType, to.significant_bits, to.premultiply, to.postmultiply ); + } + + MSG_EndBitWriting( msg ); +} + static qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delta_t *pField, qboolean bPost ) { string token; @@ -1166,7 +1223,7 @@ write fields by offsets assume from and to is valid ===================== */ -static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, const void *from, const void *to, double timebase ) +static void Delta_WriteField_( sizebuf_t *msg, delta_t *pField, const void *from, const void *to, double timebase ) { int signbit = FBitSet( pField->flags, DT_SIGNED ) ? 1 : 0; float flValue, flAngle; @@ -1174,14 +1231,6 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, const void *f int dt; const char *pStr; - if( Delta_CompareField( pField, from, to )) - { - MSG_WriteOneBit( msg, 0 ); // unchanged - return false; - } - - MSG_WriteOneBit( msg, 1 ); // changed - if( pField->flags & DT_BYTE ) { if( signbit ) @@ -1255,6 +1304,21 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, const void *f pStr = (char *)((byte *)to + pField->offset ); MSG_WriteString( msg, pStr ); } +} + +static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, const void *from, const void *to, double timebase ) +{ + if( Delta_CompareField( pField, from, to )) + { + MSG_WriteOneBit( msg, 0 ); // unchanged + return false; + } + + MSG_WriteOneBit( msg, 1 ); // changed + + Delta_WriteField_( msg, pField, from, to, timebase ); + + return true; return true; } @@ -1313,7 +1377,7 @@ read fields by offsets assume 'from' and 'to' is valid ===================== */ -static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, const void *from, void *to, double timebase ) +static void Delta_ReadField_( sizebuf_t *msg, delta_t *pField, void *to, double timebase ) { qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false; float flValue, flAngle, flTime; @@ -1321,12 +1385,6 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, const void *fr const char *pStr; char *pOut; - if( !MSG_ReadOneBit( msg ) ) - { - Delta_CopyField( pField, from, to, timebase ); - return false; - } - Assert( pField->multiplier != 0.0f ); if( pField->flags & DT_BYTE ) @@ -1410,9 +1468,80 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, const void *fr pOut = (char *)((byte *)to + pField->offset ); Q_strncpy( pOut, pStr, pField->size ); } +} + +static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, const void *from, void *to, double timebase ) +{ + if( !MSG_ReadOneBit( msg )) + { + Delta_CopyField( pField, from, to, timebase ); + return false; + } + + Delta_ReadField_( msg, pField, to, timebase ); return true; } +void Delta_ReadGSFields( sizebuf_t *msg, int index, const void *from, void *to, double timebase ) +{ + delta_info_t *dt = Delta_FindStructByIndex( index ); + uint8_t bits[8] = { 0 }; + delta_t *pField; + byte c; + int i; + + c = MSG_ReadUBitLong( msg, 3 ); + + for( i = 0; i < c; i++ ) + bits[i] = MSG_ReadByte( msg ); + + for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ ) + { + int b = i >> 3; + int n = 1 << ( i & 7 ); + + if( FBitSet( bits[b], n )) + Delta_ReadField_( msg, pField, to, timebase ); + else Delta_CopyField( pField, from, to, timebase ); + } +} + +void Delta_WriteGSFields( sizebuf_t *msg, int index, const void *from, const void *to, double timebase ) +{ + delta_info_t *dt = Delta_FindStructByIndex( index ); + delta_t *pField; + uint8_t bits[8] = { 0 }; + uint c = 0; + int i; + + Delta_CustomEncode( dt, from, to ); + + for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ ) + { + if( !Delta_CompareField( pField, from, to )) + { + int b = i >> 3; + int n = 1 << ( i & 7 ); + + SetBits( bits[b], n ); + c = b + 1; + } + } + + MSG_WriteUBitLong( msg, c, 3 ); + for( i = 0; i < c; i++ ) + MSG_WriteByte( msg, bits[i] ); + + for( i = 0, pField = dt->pFields; i < dt->numFields; i++, pField++ ) + { + int b = i >> 3; + int n = 1 << ( i & 7 ); + + if( FBitSet( bits[b], n )) + Delta_WriteField_( msg, pField, from, to, timebase ); + } +} + /* ============================================================================= @@ -1882,7 +2011,10 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, const entity_state_t *from, entity int baseline_offset = 0; if( number < 0 || number >= clgame.maxEntities ) - Host_Error( "%s: bad delta entity number: %i\n", __func__, number ); + { + Con_Printf( S_ERROR "%s: bad delta entity number: %i\n", __func__, number ); + return false; + } fRemoveType = MSG_ReadUBitLong( msg, 2 ); @@ -1904,7 +2036,8 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, const entity_state_t *from, entity return false; } - Host_Error( "%s: unknown update type %i\n", __func__, fRemoveType ); + Con_Printf( S_ERROR "%s: unknown update type %i\n", __func__, fRemoveType ); + return false; } if( !cls.legacymode ) @@ -1952,7 +2085,11 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, const entity_state_t *from, entity dt = Delta_FindStructByIndex( DT_ENTITY_STATE_T ); } - Assert( dt && dt->bInitialized ); + if( !dt || !dt->bInitialized ) + { + Con_Printf( S_ERROR "%s: broken delta\n", __func__ ); + return true; + } pField = dt->pFields; Assert( pField != NULL ); diff --git a/engine/common/net_encode.h b/engine/common/net_encode.h index 81c7a8b5..48332929 100644 --- a/engine/common/net_encode.h +++ b/engine/common/net_encode.h @@ -33,6 +33,23 @@ enum DELTA_STATIC, }; +enum +{ + DT_EVENT_T = 0, + DT_MOVEVARS_T, + DT_USERCMD_T, + DT_CLIENTDATA_T, + DT_WEAPONDATA_T, + DT_ENTITY_STATE_T, + DT_ENTITY_STATE_PLAYER_T, + DT_CUSTOM_ENTITY_STATE_T, + DT_GOLDSRC_DELTA_T, +#if XASH_ENGINE_TESTS + DT_DELTA_TEST_STRUCT_T, +#endif + DT_STRUCT_COUNT +}; + // struct info (filled by engine) typedef struct { @@ -54,6 +71,17 @@ struct delta_s qboolean bInactive; // unsetted by user request }; +typedef struct goldsrc_delta_s +{ + int fieldType; + char fieldName[32]; + int fieldOffset; + short fieldSize; + int significant_bits; + float premultiply; + float postmultiply; +} goldsrc_delta_t; + typedef void (*pfnDeltaEncode)( struct delta_s *pFields, const byte *from, const byte *to ); typedef struct @@ -76,6 +104,7 @@ typedef struct // void Delta_Init( void ); void Delta_InitClient( void ); +void Delta_InitMeta( void ); void Delta_Shutdown( void ); void Delta_AddEncoder( char *name, pfnDeltaEncode encodeFunc ); int Delta_FindField( delta_t *pFields, const char *fieldname ); @@ -87,6 +116,7 @@ void Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber ); // send table over network void Delta_WriteDescriptionToClient( sizebuf_t *msg ); void Delta_ParseTableField( sizebuf_t *msg ); +void Delta_ParseTableField_GS( sizebuf_t *msg ); // encode routines @@ -109,5 +139,7 @@ void MSG_ReadWeaponData( sizebuf_t *msg, const struct weapon_data_s *from, struc void MSG_WriteDeltaEntity( const struct entity_state_s *from, const struct entity_state_s *to, sizebuf_t *msg, qboolean force, int type, double timebase, int ofs ); qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, const struct entity_state_s *from, struct entity_state_s *to, int num, int type, double timebase ); int Delta_TestBaseline( const struct entity_state_s *from, const struct entity_state_s *to, qboolean player, double timebase ); +void Delta_ReadGSFields( sizebuf_t *msg, int index, const void *from, void *to, double timebase ); +void Delta_WriteGSFields( sizebuf_t *msg, int index, const void *from, const void *to, double timebase ); #endif//NET_ENCODE_H