Compare commits

..

22 Commits

Author SHA1 Message Date
Alibek Omarov
60c6767337 filesystem: copy absolute path to library in FS_FindLibrary for compatibility
Extend fullPath for longer absolute paths.
2024-04-22 04:02:46 +03:00
Alibek Omarov
75451cc7fa engine: client: print correct message names in legacymode 2024-04-22 04:02:46 +03:00
Alibek Omarov
db10035d9d engine: add GoldSrc protocol definitions 2024-04-22 04:02:46 +03:00
Alibek Omarov
de21d845ec engine: don't forget to put newline at the end of overflowed console message 2024-04-21 20:21:17 +03:00
Alibek Omarov
320f8466e6 engine: hpak: don't print error about missing custom.hpak in quiet mode 2024-04-21 20:05:37 +03:00
Alibek Omarov
f3208e95b2 engine: server: remove misleading message about interface version in case when GetEntityAPI2 has failed
Add message when GetEntityAPI was used instead
2024-04-21 20:05:34 +03:00
Alibek Omarov
ea34bc8652 engine: turn bugcomp into flags that can be enabled/disabled separately 2024-04-21 16:52:15 +03:00
Alibek Omarov
3daf014af8 wscript: always enable -Werror=implicit-function-declaration 2024-04-20 20:48:01 +03:00
Alibek Omarov
ca9d3d262a engine: common: net_encode: directly call MSG_ReadSBitLong for DT_TIMEWINDOW_*, don't do the multiplier check for DT_TIMEWINDOW_BIG 2024-04-20 20:48:01 +03:00
Alibek Omarov
a08f5e439d engine: common: net_encode: fix comparing DT_TIMEWINDOW_* fields
Fixes: 35783bcec2 ("engine: common: net_encode: fix inaccuracy in DT_TIMEWINDOW_* encoding")
2024-04-19 01:51:25 +03:00
Alibek Omarov
8f6771dff4 engine: common: zone: check that Mem_FindPool might return NULL (it can if Sys_Error was called inside of a Sys_Error) 2024-04-19 00:14:26 +03:00
Alibek Omarov
cd47c6b5b3 engine: common: zone: disallow migrating allocations from one pool to another, as this isn't practically used anywhere 2024-04-19 00:14:26 +03:00
Alibek Omarov
1f4f3d7fda engine: common: zone: remove mempool sentinels as pools aren't subject to buffer overflow or double free anymore 2024-04-19 00:14:26 +03:00
Alibek Omarov
e81b5144b3 engine: common: zone: use realloc for managing mem pools
rearrange data in memheader_s to avoid unnecessary paddings
2024-04-19 00:14:26 +03:00
Alibek Omarov
9ec1caed53 engine: common: zone: implement dummy Q_realloc for XASH_CUSTOM_SWAP 2024-04-19 00:14:26 +03:00
Alibek Omarov
ca3b4a9d7f engine: host: oops 2024-04-18 16:48:41 +03:00
Alibek Omarov
87ac217887 engine: common: net_encode: add bitmask operation to delta tests 2024-04-18 16:44:15 +03:00
Alibek Omarov
213650db8f engine: exit with non-zero return code if engine tests are failed 2024-04-18 16:36:18 +03:00
Alibek Omarov
ac46164a6e engine: common: net_encode: implement basic delta tests 2024-04-17 20:27:22 +03:00
Alibek Omarov
6d12d84e94 engine: common: net_encode: apply post_multiplier to integer fields, like GoldSrc does 2024-04-17 06:23:22 +03:00
Alibek Omarov
2a18cde60c engine: common: net_encode: fix applying integer clamp before multiplier
Without this fix, data still might overflow after applying multiplier
and potentially send incorrect data.
2024-04-17 06:16:44 +03:00
Alibek Omarov
35783bcec2 engine: common: net_encode: fix inaccuracy in DT_TIMEWINDOW_* encoding
By avoiding double rounding, we get more accurate time on client when timebase
gets rounded down and target time gets rounded up:

```
>>> round(123.1) - round(124.51)
-2
>>> round(123.1 - 124.51)
-1
```
2024-04-17 06:09:52 +03:00
21 changed files with 543 additions and 275 deletions

View File

@@ -1,17 +1,15 @@
# Bug-compatibility in Xash3D FWGS
Xash3D FWGS has special mode for games that rely on original engine bugs.
In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
Xash3D FWGS has special mode for games that rely on original engine bugs. In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
At this time, we only have implemented GoldSrc bug-compatibility. It can be enabled with `-bugcomp` command line switch.
When `-bugcomp` is specified without argument, it enables everything. This behavior might be changed or removed in future versions.
When `-bugcomp` is specified with argument, it interpreted as flags separated with `+`. This way it's possible to combine multiple levels of bug-compatibility.
## GoldSrc bug-compatibility
### Emulated bugs
* `pfnPEntityOfEntIndex` in GoldSrc returns NULL for last player due to incorrect player index comparison
### Games and mods that require this
* Counter-Strike: Condition Zero - Deleted Scenes
| Flag | Description | Games that require this flag |
| ------- | ----------- | ---------------------------- |
| `peoei` | Reverts `pfnPEntityOfEntIndex` behavior to GoldSrc, where it returns NULL for last player due to incorrect player index comparison | * Counter-Strike: Condition Zero - Deleted Scenes |

View File

@@ -28,11 +28,7 @@ typedef byte rgb_t[3]; // unsigned byte colorpack
typedef vec_t matrix3x4[3][4];
typedef vec_t matrix4x4[4][4];
#if XASH_64BIT
typedef uint32_t poolhandle_t;
#else
typedef void* poolhandle_t;
#endif
#undef true
#undef false

View File

@@ -50,7 +50,15 @@ const char *CL_MsgInfo( int cmd )
if( cmd >= 0 && cmd <= svc_lastmsg )
{
// get engine message name
Q_strncpy( sz, svc_strings[cmd], sizeof( sz ));
const char *svc_string = NULL;
if( cls.legacymode )
svc_string = svc_legacy_strings[cmd];
if( !svc_string )
svc_string = svc_strings[cmd];
Q_strncpy( sz, svc_string, sizeof( sz ));
}
else if( cmd > svc_lastmsg && cmd <= ( svc_lastmsg + MAX_USER_MESSAGES ))
{

View File

@@ -273,12 +273,8 @@ typedef struct
typedef enum bugcomp_e
{
// default mode, we assume that user dlls are not relying on engine bugs
BUGCOMP_OFF,
// GoldSrc mode, user dlls are relying on GoldSrc specific bugs
// but fixing them may break regular Xash games
BUGCOMP_GOLDSRC,
// reverts fix for pfnPEntityOfEntIndex for bug compatibility with GoldSrc
BUGCOMP_PENTITYOFENTINDEX_FLAG = BIT( 0 ),
} bugcomp_t;
typedef struct host_parm_s
@@ -356,7 +352,7 @@ typedef struct host_parm_s
int numdecals;
// bug compatibility level, for very "special" games
bugcomp_t bugcomp;
uint32_t bugcomp;
// measure time to first frame
double starttime;

View File

@@ -89,32 +89,32 @@ qboolean FS_LoadProgs( void )
if( !fs_hInstance )
{
Host_Error( "FS_LoadProgs: can't load filesystem library %s: %s\n", name, COM_GetLibraryError() );
Host_Error( "%s: can't load filesystem library %s: %s\n", __func__, name, COM_GetLibraryError() );
return false;
}
if( !( GetFSAPI = (FSAPI)COM_GetProcAddress( fs_hInstance, GET_FS_API )))
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't find GetFSAPI entry point in %s\n", name );
Host_Error( "%s: can't find GetFSAPI entry point in %s\n", __func__, name );
return false;
}
if( !GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ))
if( GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ) != FS_API_VERSION )
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't initialize filesystem API: wrong version\n" );
Host_Error( "%s: can't initialize filesystem API: wrong version\n", __func__ );
return false;
}
if( !( fs_pfnCreateInterface = (pfnCreateInterface_t)COM_GetProcAddress( fs_hInstance, "CreateInterface" )))
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't find CreateInterface entry point in %s\n", name );
Host_Error( "%s: can't find CreateInterface entry point in %s\n", __func__, name );
return false;
}
Con_DPrintf( "FS_LoadProgs: filesystem_stdio successfully loaded\n" );
Con_DPrintf( "%s: filesystem_stdio successfully loaded\n", __func__ );
return true;
}

View File

@@ -65,6 +65,32 @@ static CVAR_DEFINE( host_sleeptime, "sleeptime", "1", FCVAR_ARCHIVE|FCVAR_FILTER
static CVAR_DEFINE_AUTO( host_sleeptime_debug, "0", 0, "print sleeps between frames" );
CVAR_DEFINE( con_gamemaps, "con_mapfilter", "1", FCVAR_ARCHIVE, "when true show only maps in game folder" );
typedef struct feature_message_s
{
uint32_t mask;
const char *msg;
const char *arg;
} feature_message_t;
static feature_message_t bugcomp_features[] =
{
{ BUGCOMP_PENTITYOFENTINDEX_FLAG, "pfnPEntityOfEntIndex bugfix revert", "peoei" },
};
static feature_message_t engine_features[] =
{
{ ENGINE_WRITE_LARGE_COORD, "Big World Support" },
{ ENGINE_QUAKE_COMPATIBLE, "Quake Compatibility" },
{ ENGINE_LOAD_DELUXEDATA, "Deluxemap Support" },
{ ENGINE_PHYSICS_PUSHER_EXT, "Improved MOVETYPE_PUSH" },
{ ENGINE_LARGE_LIGHTMAPS, "Large Lightmaps" },
{ ENGINE_COMPENSATE_QUAKE_BUG, "Stupid Quake Bug Compensation" },
{ ENGINE_IMPROVED_LINETRACE, "Improved Trace Line" },
{ ENGINE_COMPUTE_STUDIO_LERP, "Studio MOVETYPE_STEP Lerping" },
{ ENGINE_LINEAR_GAMMA_SPACE, "Linear Gamma Space" },
{ ENGINE_STEP_POSHISTORY_LERP, "MOVETYPE_STEP Position History Based Lerping" },
};
static void Sys_PrintUsage( void )
{
string version_str;
@@ -92,7 +118,7 @@ static void Sys_PrintUsage( void )
O("-minidumps ", "enable writing minidumps when game is crashed")
#endif
O("-rodir <path> ", "set read-only base directory")
O("-bugcomp ", "enable precise bug compatibility")
O("-bugcomp [opts] ", "enable precise bug compatibility")
O(" ", "will break games that don't require it")
O(" ", "refer to engine documentation for more info")
O("-disablehelp ", "disable this message")
@@ -193,31 +219,14 @@ void Host_ShutdownServer( void )
Host_PrintEngineFeatures
================
*/
static void Host_PrintEngineFeatures( int features )
static void Host_PrintFeatures( uint32_t flags, const char *s, feature_message_t *features, size_t size )
{
struct
{
uint32_t mask;
const char *msg;
} features_str[] =
{
{ ENGINE_WRITE_LARGE_COORD, "Big World Support" },
{ ENGINE_QUAKE_COMPATIBLE, "Quake Compatibility" },
{ ENGINE_LOAD_DELUXEDATA, "Deluxemap Support" },
{ ENGINE_PHYSICS_PUSHER_EXT, "Improved MOVETYPE_PUSH" },
{ ENGINE_LARGE_LIGHTMAPS, "Large Lightmaps" },
{ ENGINE_COMPENSATE_QUAKE_BUG, "Stupid Quake Bug Compensation" },
{ ENGINE_IMPROVED_LINETRACE, "Improved Trace Line" },
{ ENGINE_COMPUTE_STUDIO_LERP, "Studio MOVETYPE_STEP Lerping" },
{ ENGINE_LINEAR_GAMMA_SPACE, "Linear Gamma Space" },
{ ENGINE_STEP_POSHISTORY_LERP, "MOVETYPE_STEP Position History Based Lerping" },
};
int i;
size_t i;
for( i = 0; i < ARRAYSIZE( features_str ); i++ )
for( i = 0; i < size; i++ )
{
if( FBitSet( features, features_str[i].mask ))
Con_Reportf( "^3EXT:^7 %s is enabled\n", features_str[i].msg );
if( FBitSet( flags, features[i].mask ))
Con_Printf( "^3%s:^7 %s is enabled\n", s, features[i].msg );
}
}
@@ -245,7 +254,7 @@ void Host_ValidateEngineFeatures( uint32_t features )
SetBits( features, ENGINE_STEP_POSHISTORY_LERP );
// print requested first
Host_PrintEngineFeatures( features );
Host_PrintFeatures( features, "EXT", engine_features, ARRAYSIZE( engine_features ));
// now warn about incompatible bits
if( FBitSet( features, ENGINE_STEP_POSHISTORY_LERP|ENGINE_COMPUTE_STUDIO_LERP ) == ( ENGINE_STEP_POSHISTORY_LERP|ENGINE_COMPUTE_STUDIO_LERP ))
@@ -941,11 +950,59 @@ static void Host_RunTests( int stage )
TEST_LIST_1_CLIENT;
#endif
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
Sys_Quit();
}
}
#endif
static uint32_t Host_CheckBugcomp( void )
{
uint32_t flags = 0;
string args, arg;
char *prev, *next;
size_t i;
if( !Sys_CheckParm( "-bugcomp" ))
return 0;
if( Sys_GetParmFromCmdLine( "-bugcomp", args ) && args[0] != '-' )
{
for( prev = args, next = Q_strchrnul( prev, '+' ); ; prev = next + 1, next = Q_strchrnul( prev, '+' ))
{
Q_strncpy( arg, prev, next - prev + 1 );
for( i = 0; i < ARRAYSIZE( bugcomp_features ); i++ )
{
if( !Q_stricmp( bugcomp_features[i].arg, arg ))
{
SetBits( flags, bugcomp_features[i].mask );
break;
}
}
if( i == ARRAYSIZE( bugcomp_features ))
{
Con_Printf( S_ERROR "Unknown bugcomp flag %s\n", arg );
Con_Printf( "Valid flags are:\n" );
for( i = 0; i < ARRAYSIZE( bugcomp_features ); i++ )
Con_Printf( "\t%s: %s\n", bugcomp_features[i].arg, bugcomp_features[i].msg );
}
if( !*next )
break;
}
}
else
{
// no argument specified -bugcomp just enables everything
flags = -1;
}
Host_PrintFeatures( flags, "BUGCOMP", bugcomp_features, ARRAYSIZE( bugcomp_features ));
return flags;
}
/*
=================
Host_InitCommon
@@ -1050,13 +1107,6 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
// member console allowing
host.allow_console_init = host.allow_console;
if( Sys_CheckParm( "-bugcomp" ))
{
// add argument check here when we add other levels
// of bugcompatibility
host.bugcomp = BUGCOMP_GOLDSRC;
}
// timeBeginPeriod( 1 ); // a1ba: Do we need this?
// NOTE: this message couldn't be passed into game console but it doesn't matter
@@ -1183,16 +1233,11 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
Sys_InitLog();
// print bugcompatibility level here, after log was initialized
if( host.bugcomp == BUGCOMP_GOLDSRC )
{
Con_Printf( "^3BUGCOMP^7: GoldSrc bug-compatibility enabled\n" );
}
// print current developer level to simplify processing users feedback
if( developer > 0 ) {
if( developer > 0 )
Con_Printf( "Developer level: ^3%i\n", developer );
}
host.bugcomp = Host_CheckBugcomp();
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );

View File

@@ -400,7 +400,8 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet, qboolean de
f = FS_Open( pakname, "rb", true );
if( !f )
{
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
if( !quiet )
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
return true;
}

View File

@@ -24,7 +24,8 @@ typedef struct dll_user_s
qboolean custom_loader; // a bit who indicated loader type
qboolean encrypted; // dll is crypted (some client.dll in HL, CS etc)
char dllName[32]; // for debug messages
string fullPath, shortPath; // actual dll paths
char fullPath[2048];
string shortPath; // actual dll paths
// ordinals stuff, valid only on Win32
word *ordinals;

View File

@@ -89,6 +89,36 @@ const char *svc_strings[svc_lastmsg+1] =
"svc_exec",
};
const char *svc_legacy_strings[svc_lastmsg+1] =
{
[svc_legacy_changing] = "svc_legacy_changing",
[svc_legacy_ambientsound] = "svc_legacy_ambientsound",
[svc_legacy_soundindex] = "svc_legacy_soundindex",
[svc_legacy_ambientsound] = "svc_legacy_ambientsound",
[svc_legacy_modelindex] = "svc_legacy_modelindex",
[svc_legacy_eventindex] = "svc_legacy_eventindex",
[svc_legacy_chokecount] = "svc_legacy_chokecount",
};
const char *svc_goldsrc_strings[svc_lastmsg+1] =
{
[svc_goldsrc_version] = "svc_goldsrc_version",
[svc_goldsrc_serverinfo] = "svc_goldsrc_serverinfo",
[svc_goldsrc_deltadescription] = "svc_goldsrc_deltadescription",
[svc_goldsrc_stopsound] = "svc_goldsrc_stopsound",
[svc_goldsrc_damage] = "svc_goldsrc_damage",
[svc_goldsrc_killedmonster] = "svc_goldsrc_killedmonster",
[svc_goldsrc_foundsecret] = "svc_goldsrc_foundsecret",
[svc_goldsrc_spawnstaticsound] = "svc_goldsrc_spawnstaticsound",
[svc_goldsrc_decalname] = "svc_goldsrc_decalname",
[svc_goldsrc_newusermsg] = "svc_goldsrc_newusermsg",
[svc_goldsrc_newmovevars] = "svc_goldsrc_newmovevars",
[svc_goldsrc_sendextrainfo] = "svc_goldsrc_sendextrainfo",
[svc_goldsrc_timescale] = "svc_goldsrc_timescale",
[svc_goldsrc_sendcvarvalue] = "svc_goldsrc_sendcvarvalue",
[svc_goldsrc_sendcvarvalue2] = "svc_goldsrc_sendcvarvalue2",
};
void MSG_InitMasks( void )
{
uint startbit, endbit;

View File

@@ -315,6 +315,43 @@ static const delta_field_t ent_fields[] =
{ NULL },
};
#if XASH_ENGINE_TESTS
typedef struct delta_test_struct_t
{
char dt_string[128]; // always signed
float dt_timewindow_big; // always signed
float dt_timewindow_8; // always signed
float dt_angle; // always_signed
float dt_float_signed;
float dt_float_unsigned;
int32_t dt_integer_signed;
uint32_t dt_integer_unsigned;
int16_t dt_short_signed;
uint16_t dt_short_unsigned;
int8_t dt_byte_signed;
uint8_t dt_byte_unsigned;
} delta_test_struct_t;
#define TEST_DEF( x ) #x, offsetof( delta_test_struct_t, x ), sizeof( ((delta_test_struct_t *)0)->x )
static const delta_field_t test_fields[] =
{
{ TEST_DEF( dt_string ) },
{ TEST_DEF( dt_timewindow_big )},
{ TEST_DEF( dt_timewindow_8 )},
{ TEST_DEF( dt_angle ) },
{ TEST_DEF( dt_float_signed ) },
{ TEST_DEF( dt_float_unsigned ) },
{ TEST_DEF( dt_integer_signed ) },
{ TEST_DEF( dt_integer_unsigned ) },
{ TEST_DEF( dt_short_signed ) },
{ TEST_DEF( dt_short_unsigned ) },
{ TEST_DEF( dt_byte_signed ) },
{ TEST_DEF( dt_byte_unsigned ) },
{ NULL },
};
#endif
enum
{
DT_EVENT_T = 0,
@@ -325,6 +362,10 @@ enum
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[] =
@@ -337,7 +378,10 @@ static delta_info_t dt_info[] =
[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 ) },
{ NULL },
#if XASH_ENGINE_TESTS
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) },
#endif
[DT_STRUCT_COUNT] = { NULL },
};
static delta_info_t *Delta_FindStruct( const char *name )
@@ -411,9 +455,7 @@ static void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *
dt->pFields[i].bInactive = false;
if( dt->userCallback )
{
dt->userCallback( dt->pFields, from, to );
}
}
static delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName )
@@ -974,14 +1016,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
toF = *(uint8_t *)((int8_t *)to + pField->offset );
}
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
else if( pField->flags & DT_SHORT )
{
@@ -996,14 +1038,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
toF = *(uint16_t *)((int8_t *)to + pField->offset );
}
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
else if( pField->flags & DT_INTEGER )
{
@@ -1017,14 +1059,15 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
fromF = *(uint32_t *)((int8_t *)from + pField->offset );
toF = *(uint32_t *)((int8_t *)to + pField->offset );
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
}
else if( pField->flags & ( DT_ANGLE|DT_FLOAT ))
{
@@ -1034,33 +1077,17 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
}
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.0);
val_b -= Q_rint(timebase * 100.0);
fromF = FloatAsInt( val_a );
toF = FloatAsInt( val_b );
val_a = *(float *)((byte *)from + pField->offset );
val_b = *(float *)((byte *)to + pField->offset );
fromF = Q_rint( val_a * 100.0 );
toF = Q_rint( val_b * 100.0 );
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
val_a = (*(float *)((byte *)from + pField->offset ));
val_b = (*(float *)((byte *)to + pField->offset ));
if( !Q_equal( 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 = FloatAsInt( val_a );
toF = FloatAsInt( val_b );
val_a = *(float *)((byte *)from + pField->offset );
val_b = *(float *)((byte *)to + pField->offset );
fromF = Q_rint( val_a * pField->multiplier );
toF = Q_rint( val_b * pField->multiplier );
}
else if( pField->flags & DT_STRING )
{
@@ -1144,6 +1171,7 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
int signbit = FBitSet( pField->flags, DT_SIGNED ) ? 1 : 0;
float flValue, flAngle;
uint iValue;
int dt;
const char *pStr;
if( Delta_CompareField( pField, from, to, timebase ))
@@ -1160,11 +1188,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int8_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint8_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_SHORT )
@@ -1173,11 +1201,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int16_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint16_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_INTEGER )
@@ -1186,11 +1214,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int32_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint32_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_FLOAT )
@@ -1210,19 +1238,17 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
signbit = 1; // timewindow is always signed
flValue = *(float *)((byte *)to + pField->offset );
iValue = (int)Q_rint( timebase * 100.0 ) - (int)Q_rint( flValue * 100.0 );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
dt = Q_rint(( timebase - flValue ) * 100.0 );
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
MSG_WriteSBitLong( msg, dt, pField->bits );
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
signbit = 1; // timewindow is always signed
flValue = *(float *)((byte *)to + pField->offset );
iValue = (int)Q_rint( timebase * pField->multiplier ) - (int)Q_rint( flValue * pField->multiplier );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
dt = Q_rint(( timebase - flValue ) * pField->multiplier );
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
MSG_WriteSBitLong( msg, dt, pField->bits );
}
else if( pField->flags & DT_STRING )
{
@@ -1306,9 +1332,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
if( pField->flags & DT_BYTE )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int8_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1317,9 +1346,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else if( pField->flags & DT_SHORT )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int16_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1328,9 +1360,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else if( pField->flags & DT_INTEGER )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int32_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1344,11 +1379,11 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else
flValue = iValue;
if( !Q_equal( pField->multiplier, 1.0 ) )
flValue = flValue / pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0 ))
flValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ) )
flValue = flValue * pField->post_multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
flValue *= pField->post_multiplier;
*(float *)((byte *)to + pField->offset ) = flValue;
}
@@ -1359,22 +1394,14 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
bSigned = true; // timewindow is always signed
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
flTime = (timebase * 100.0 - (int)iValue) / 100.0;
iValue = MSG_ReadSBitLong( msg, pField->bits );
flTime = ( timebase * 100.0 - (int)iValue ) / 100.0;
*(float *)((byte *)to + pField->offset ) = flTime;
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
bSigned = true; // timewindow is always signed
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
else
flTime = timebase - (int)iValue;
iValue = MSG_ReadSBitLong( msg, pField->bits );
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
*(float *)((byte *)to + pField->offset ) = flTime;
}
else if( pField->flags & DT_STRING )
@@ -2069,3 +2096,103 @@ void GAME_EXPORT Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber )
dt->pFields[fieldNumber].bInactive = true;
}
#if XASH_ENGINE_TESTS
#include "tests.h"
void Test_RunDelta( void )
{
delta_info_t *dt = &dt_info[DT_DELTA_TEST_STRUCT_T];
delta_test_struct_t from, to = { 0 };
delta_test_struct_t null = { 0 };
sizebuf_t msg;
int i;
char buffer[4096] = { 0 };
const double timebase = 123.123;
// a1ba: netbuffer bitmasks are initialized in netchan for some reason
// initialize it ourselves just in case
MSG_InitMasks(); // initialize bit-masks
Delta_AddField( dt, "dt_string", DT_STRING, 1, 1.0f, 1.0f );
Delta_AddField( dt, "dt_timewindow_big", DT_TIMEWINDOW_BIG, 24, 1000.f, 1.0f );
Delta_AddField( dt, "dt_timewindow_8", DT_TIMEWINDOW_8, 8, 1.0f, 1.0f );
Delta_AddField( dt, "dt_angle", DT_ANGLE, 16, 1.0f, 1.0f );
Delta_AddField( dt, "dt_float_signed", DT_FLOAT | DT_SIGNED, 22, 100.0f, 1.0f );
Delta_AddField( dt, "dt_float_unsigned", DT_FLOAT, 24, 10000.0f, 0.1f );
Delta_AddField( dt, "dt_integer_signed", DT_INTEGER | DT_SIGNED, 24, 1.0f, 1.0f );
Delta_AddField( dt, "dt_integer_unsigned", DT_INTEGER, 24, 1.0f, 1.0f );
Delta_AddField( dt, "dt_short_signed", DT_SHORT | DT_SIGNED, 16, 1.0f, 1.0f );
Delta_AddField( dt, "dt_short_unsigned", DT_SHORT, 15, 0.125f, 1.0f );
Delta_AddField( dt, "dt_byte_signed", DT_BYTE | DT_SIGNED, 6, 1.0f, 1.0f );
Delta_AddField( dt, "dt_byte_unsigned", DT_BYTE, 8, 1.0f, 1.0f );
Q_strncpy( from.dt_string, "test data check it's the same", sizeof( from.dt_string ));
from.dt_timewindow_big = timebase + 2.3456;
from.dt_timewindow_8 = timebase + 0.0234;
from.dt_angle = 160.245f;
from.dt_float_signed = -15.123f;
from.dt_float_unsigned = 1235.321f;
from.dt_integer_signed = -412784;
from.dt_integer_unsigned = 123453;
from.dt_short_signed = -12343;
from.dt_short_unsigned = 32131;
from.dt_byte_signed = 16;
from.dt_byte_unsigned = 218;
MSG_Init( &msg, "test message", buffer, sizeof( buffer ));
for( i = 0; i < dt->numFields; i++ )
Delta_WriteField( &msg, &dt->pFields[i], &null, &from, timebase );
MSG_SeekToBit( &msg, 0, SEEK_SET );
for( i = 0; i < dt->numFields; i++ )
Delta_ReadField( &msg, &dt->pFields[i], &null, &to, timebase );
Con_Printf( "struct as encoded to delta:\n" );
TASSERT_STR( from.dt_string, to.dt_string );
// the epsilon value is derived from multiplier value
TASSERT( Q_equal_e( from.dt_timewindow_big, to.dt_timewindow_big, 0.001f ));
// dt_timewindow_8 type has multiplier locked at 100.0f
TASSERT( Q_equal_e( from.dt_timewindow_8, to.dt_timewindow_8, 0.01f ));
TASSERT( Q_equal_e( from.dt_angle, to.dt_angle, 0.1f ));
TASSERT( Q_equal_e( from.dt_float_signed, to.dt_float_signed, 0.01f ));
// dt_float_unsigned has post-multiplier that doesn't affect network data
// and therefore should be reverted back when comparing
TASSERT( Q_equal_e( from.dt_float_unsigned, to.dt_float_unsigned * 10.f , 0.01f ));
TASSERT_EQi( from.dt_integer_signed, to.dt_integer_signed );
TASSERT_EQi( from.dt_integer_unsigned, to.dt_integer_unsigned );
TASSERT_EQi( from.dt_short_signed, to.dt_short_signed );
TASSERT(( from.dt_short_unsigned & ( 0xffff << 3 )) == to.dt_short_unsigned );
TASSERT_EQi( from.dt_byte_signed, to.dt_byte_signed );
TASSERT_EQi( from.dt_byte_unsigned, to.dt_byte_unsigned );
Con_Printf( "from.dt_timewindow_big = %f\n", from.dt_timewindow_big );
Con_Printf( "to.dt_timewindow_big = %f\n", to.dt_timewindow_big );
Con_Printf( "from.dt_timewindow_8 = %f\n", from.dt_timewindow_8 );
Con_Printf( "to.dt_timewindow_8 = %f\n", to.dt_timewindow_8 );
Con_Printf( "from.dt_angle = %f\n", from.dt_angle );
Con_Printf( "to.dt_angle = %f\n", to.dt_angle );
Con_Printf( "from.dt_float_signed = %f\n", from.dt_float_signed );
Con_Printf( "to.dt_float_signed = %f\n", to.dt_float_signed );
Con_Printf( "from.dt_float_unsigned = %f\n", from.dt_float_unsigned );
Con_Printf( "to.dt_float_unsigned = %f\n", to.dt_float_unsigned );
Con_Printf( "from.dt_integer_signed = %i\n", from.dt_integer_signed );
Con_Printf( "to.dt_integer_signed = %i\n", to.dt_integer_signed );
Con_Printf( "from.dt_integer_unsigned = %i\n", from.dt_integer_unsigned );
Con_Printf( "to.dt_integer_unsigned = %i\n", to.dt_integer_unsigned );
Con_Printf( "from.dt_short_signed = %i\n", from.dt_short_signed );
Con_Printf( "to.dt_short_signed = %i\n", to.dt_short_signed );
Con_Printf( "from.dt_short_unsigned = %i\n", from.dt_short_unsigned );
Con_Printf( "to.dt_short_unsigned = %i\n", to.dt_short_unsigned );
Con_Printf( "from.dt_byte_signed = %i\n", from.dt_byte_signed );
Con_Printf( "to.dt_byte_signed = %i\n", to.dt_byte_signed );
Con_Printf( "from.dt_byte_unsigned = %i\n", from.dt_byte_unsigned );
Con_Printf( "to.dt_byte_unsigned = %i\n", to.dt_byte_unsigned );
}
#endif // XASH_ENGINE_TESTS

View File

@@ -278,6 +278,8 @@ GNU General Public License for more details.
#define SU_WEAPON (1<<14)
extern const char *svc_strings[svc_lastmsg+1];
extern const char *svc_legacy_strings[svc_lastmsg+1];
extern const char *svc_goldsrc_strings[svc_lastmsg+1];
extern const char *clc_strings[clc_lastmsg+1];
// FWGS extensions
@@ -316,4 +318,40 @@ extern const char *clc_strings[clc_lastmsg+1];
// Master Server protocol
#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" // TODO: implement IP filter
// GoldSrc protocol definitions
#define PROTOCOL_GOLDSRC_VERSION_REAL 48
#define PROTOCOL_GOLDSRC_VERSION (PROTOCOL_GOLDSRC_VERSION_REAL | (BIT( 7 ))) // should be 48, only to differentiate it from PROTOCOL_LEGACY_VERSION
#define svc_goldsrc_version svc_changing
#define svc_goldsrc_serverinfo svc_serverdata
#define svc_goldsrc_deltadescription svc_deltatable
#define svc_goldsrc_stopsound svc_resource
#define svc_goldsrc_damage svc_restoresound
#define svc_goldsrc_killedmonster 27
#define svc_goldsrc_foundsecret 28
#define svc_goldsrc_spawnstaticsound 29
#define svc_goldsrc_decalname svc_bspdecal
#define svc_goldsrc_newusermsg svc_usermessage
#define svc_goldsrc_newmovevars svc_deltamovevars
#define svc_goldsrc_sendextrainfo 54
#define svc_goldsrc_timescale 55
#define svc_goldsrc_sendcvarvalue svc_querycvarvalue
#define svc_goldsrc_sendcvarvalue2 svc_querycvarvalue2
#define clc_goldsrc_hltv clc_requestcvarvalue // 9
#define clc_goldsrc_requestcvarvalue clc_requestcvarvalue2 // 10
#define clc_goldsrc_requestcvarvalue2 11
#define clc_goldsrc_lastmsg 12
#define S2C_REJECT_BADPASSWORD '8'
#define S2C_REJECT '9'
#define S2C_CHALLENGE "A00000000"
#define S2C_CONNECTION "B"
#define MAX_GOLDSRC_RESOURCE_BITS 12
#define MAX_GOLDSRC_ENTITY_BITS 11
// #define MAX_GOLDSRC_EDICTS BIT( MAX_ENTITY_BITS )
#define MAX_GOLDSRC_EDICTS ( BIT( MAX_ENTITY_BITS ) + ( MAX_CLIENTS * 15 ))
#define LAST_GOLDSRC_EDICT ( BIT( MAX_ENTITY_BITS ) - 1 )
#endif//NET_PROTOCOL_H

View File

@@ -341,15 +341,18 @@ void GAME_EXPORT Con_Printf( const char *szFmt, ... )
{
static char buffer[MAX_PRINT_MSG];
va_list args;
qboolean add_newline;
if( !host.allow_console )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
va_end( args );
Sys_Print( buffer );
if( add_newline )
Sys_Print( "\n" );
}
/*
@@ -362,18 +365,21 @@ void GAME_EXPORT Con_DPrintf( const char *szFmt, ... )
{
static char buffer[MAX_PRINT_MSG];
va_list args;
qboolean add_newline;
if( host_developer.value < DEV_NORMAL )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
va_end( args );
if( buffer[0] == '0' && buffer[1] == '\n' && buffer[2] == '\0' )
return; // hlrally spam
Sys_Print( buffer );
if( add_newline )
Sys_Print( "\n" );
}
/*
@@ -386,15 +392,18 @@ void Con_Reportf( const char *szFmt, ... )
{
static char buffer[MAX_PRINT_MSG];
va_list args;
qboolean add_newline;
if( host_developer.value < DEV_EXTENDED )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
va_end( args );
Sys_Print( buffer );
if( add_newline )
Sys_Print( "\n" );
}

View File

@@ -53,7 +53,7 @@ GNU General Public License for more details.
#include "library.h"
#include "whereami.h"
static int error_on_exit = 0; // arg for exit();
int error_on_exit = 0; // arg for exit();
/*
================

View File

@@ -41,7 +41,7 @@ NOTE: never change this structure because all dll descriptions in xash code
writes into struct by offsets not names
========================================================================
*/
extern int error_on_exit;
void Sys_Sleep( int msec );
double Sys_DoubleTime( void );
char *Sys_GetClipboardData( void );

View File

@@ -39,13 +39,15 @@ void Test_RunCon( void );
void Test_RunVOX( void );
void Test_RunIPFilter( void );
void Test_RunGamma( void );
void Test_RunDelta( void );
#define TEST_LIST_0 \
Test_RunLibCommon(); \
Test_RunCommon(); \
Test_RunCmd(); \
Test_RunCvar(); \
Test_RunIPFilter();
Test_RunIPFilter(); \
Test_RunDelta();
#define TEST_LIST_0_CLIENT \
Test_RunCon(); \

View File

@@ -25,18 +25,39 @@ GNU General Public License for more details.
#include "platform/swap/swap.h"
#define Q_malloc SWAP_Malloc
#define Q_free SWAP_Free
static void *Q_realloc( void *mem, size_t size )
{
void *newmem;
if( mem && size == 0 )
{
Q_free( mem );
return NULL;
}
newmem = Q_malloc( size );
if( mem && newmem )
{
memcpy( newmem, mem, size );
Q_free( mem );
}
return newmem;
}
#else
#define Q_malloc malloc
#define Q_free free
#define Q_realloc realloc
#endif
typedef struct memheader_s
{
struct memheader_s *next; // next and previous memheaders in chain belonging to pool
struct memheader_s *prev;
struct mempool_s *pool; // pool this memheader belongs to
size_t size; // size of the memory after the header (excluding header and sentinel2)
const char *filename; // file name and line where Mem_Alloc was called
size_t size; // size of the memory after the header (excluding header and sentinel2)
poolhandle_t poolptr; // pool this memheader belongs to
int fileline;
#if !XASH_64BIT
uint32_t pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32
@@ -48,50 +69,34 @@ typedef struct memheader_s
typedef struct mempool_s
{
uint32_t sentinel1; // should always be MEMHEADER_SENTINEL1
struct memheader_s *chain; // chain of individual memory allocations
size_t totalsize; // total memory allocated in this pool (inside memheaders)
size_t realsize; // total memory allocated in this pool (actual malloc total)
size_t lastchecksize; // updated each time the pool is displayed by memlist
struct mempool_s *next; // linked into global mempool list
const char *filename; // file name and line where Mem_AllocPool was called
int fileline;
#if XASH_64BIT
poolhandle_t idx;
#endif
char name[64]; // name of the pool
uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1
} mempool_t;
static mempool_t *poolchain = NULL; // critical stuff
static size_t poolcount = 0;
#if XASH_64BIT
// a1ba: due to mempool being passed with the model through reused 32-bit field
// which makes engine incompatible with 64-bit pointers I changed mempool type
// from pointer to 32-bit handle, thankfully mempool structure is private
// But! Mempools are handled through linked list so we can't index them safely
static poolhandle_t lastidx = 0;
static mempool_t *Mem_FindPool( poolhandle_t poolptr )
{
mempool_t *pool;
for( pool = poolchain; pool; pool = pool->next )
{
if( pool->idx == poolptr )
return pool;
}
if( likely( poolptr > 0 && poolptr <= poolcount ))
return &poolchain[poolptr - 1];
Sys_Error( "%s: not allocated or double freed pool %d", __FUNCTION__, poolptr );
return NULL;
}
#else
static mempool_t *Mem_FindPool( poolhandle_t poolptr )
static poolhandle_t Mem_PoolIndex( mempool_t *mempool )
{
return (mempool_t *)poolptr;
return (poolhandle_t)(mempool - poolchain) + 1;
}
#endif
static inline void Mem_PoolAdd( mempool_t *pool, size_t size )
{
@@ -111,7 +116,7 @@ static inline void Mem_PoolLinkAlloc( mempool_t *pool, memheader_t *mem )
if( mem->next ) mem->next->prev = mem;
pool->chain = mem;
mem->prev = NULL;
mem->pool = pool;
mem->poolptr = Mem_PoolIndex( pool );
}
static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
@@ -119,7 +124,7 @@ static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
if( mem->next ) mem->next->prev = mem->prev;
if( mem->prev ) mem->prev->next = mem->next;
else pool->chain = mem->next;
mem->pool = NULL;
mem->poolptr = 0;
}
static inline void Mem_InitAlloc( memheader_t *mem, size_t size, const char *filename, int fileline )
@@ -165,23 +170,6 @@ static qboolean Mem_CheckAllocHeader( const char *func, const memheader_t *mem,
return true;
}
static qboolean Mem_CheckPool( const char *func, const mempool_t *pool, const char *filename, int fileline )
{
if( pool->sentinel1 != MEMHEADER_SENTINEL1 )
{
Sys_Error( "%s: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)\n", func, pool->filename, pool->fileline, filename, fileline );
return false;
}
if( pool->sentinel2 != MEMHEADER_SENTINEL1 )
{
Sys_Error( "%s: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)\n", func, pool->filename, pool->fileline, filename, fileline );
return false;
}
return true;
}
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
{
memheader_t *mem;
@@ -190,12 +178,16 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char
if( size <= 0 )
return NULL;
if( !poolptr )
if( unlikely( !poolptr ))
{
Sys_Error( "%s: pool == NULL (alloc at %s:%i)\n", __func__, filename, fileline );
return NULL;
}
pool = Mem_FindPool( poolptr );
if( !pool )
return NULL;
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( byte ));
if( mem == NULL )
{
@@ -205,7 +197,6 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char
Mem_InitAlloc( mem, size, filename, fileline );
pool = Mem_FindPool( poolptr );
Mem_PoolAdd( pool, size );
Mem_PoolLinkAlloc( pool, mem );
@@ -222,7 +213,9 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline
if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline ))
return;
pool = mem->pool;
pool = Mem_FindPool( mem->poolptr );
if( !pool )
return;
// unlink memheader from doubly linked list
if(( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem ))
@@ -258,7 +251,7 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
if( size <= 0 )
return data; // no need to reallocate
if( !poolptr )
if( unlikely( !poolptr ))
{
Sys_Error( "Mem_Realloc: pool == NULL (alloc at %s:%i)\n", filename, fileline );
return NULL;
@@ -272,25 +265,21 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
if( !Mem_CheckAllocHeader( "Mem_Realloc", mem, filename, fileline ))
return NULL;
if( unlikely( mem->poolptr != poolptr ))
{
Sys_Error( "Mem_Realloc: pool migration is not allowed (alloc at %s:%i, realloc at %s:%i)\n",
Mem_CheckFilename( mem->filename ), mem->fileline, filename, fileline );
return NULL;
}
oldsize = mem->size;
if( size == oldsize )
return data;
#if XASH_CUSTOM_SWAP
{
char *nb = _Mem_Alloc( poolptr, size, clear, filename, fileline );
size_t newsize = mem->size < size ? mem->size : size; // upper data can be trucnated!
memcpy( nb, data, newsize );
_Mem_Free( data, filename, fileline ); // free unused old block
return nb;
}
#else // XASH_CUSTOM_SWAP
pool = Mem_FindPool( poolptr );
oldmem = (uintptr_t)mem;
mem = realloc( mem, sizeof( memheader_t ) + size + sizeof( byte ));
mem = Q_realloc( mem, sizeof( memheader_t ) + size + sizeof( byte ));
if( mem == NULL )
{
@@ -312,14 +301,7 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
}
else Mem_PoolSubtract( pool, oldsize - size );
// if allocation was migrated from one pool to another
// (this is possible with original Mem_Realloc func)
if( unlikely( mem->pool != pool ))
{
Mem_PoolUnlinkAlloc( mem->pool, mem );
Mem_PoolLinkAlloc( pool, mem );
}
else if( oldmem != (uintptr_t)mem ) // just relink pointers
if( oldmem != (uintptr_t)mem ) // just relink pointers
{
if( mem->next ) mem->next->prev = mem;
if( mem->prev ) mem->prev->next = mem;
@@ -327,69 +309,81 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
}
return (void *)((byte *)mem + sizeof( memheader_t ));
#endif // XASH_CUSTOM_SWAP
}
static poolhandle_t Mem_InitPool( mempool_t *pool, const char *name, const char *filename, int fileline )
{
memset( pool, 0, sizeof( *pool ));
// fill header
pool->filename = filename;
pool->fileline = fileline;
pool->realsize = sizeof( mempool_t );
Q_strncpy( pool->name, name, sizeof( pool->name ));
return Mem_PoolIndex( pool );
}
poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline )
{
mempool_t *pool;
size_t i;
pool = (mempool_t *)Q_malloc( sizeof( mempool_t ));
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( pool->filename == NULL )
return Mem_InitPool( pool, name, filename, fileline );
}
pool = (mempool_t *)Q_realloc( poolchain, sizeof( *poolchain ) * ( poolcount + 1 ));
if( pool == NULL )
{
Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline );
return 0;
}
memset( pool, 0, sizeof( mempool_t ));
// fill header
pool->sentinel1 = MEMHEADER_SENTINEL1;
pool->sentinel2 = MEMHEADER_SENTINEL1;
pool->filename = filename;
pool->fileline = fileline;
pool->chain = NULL;
pool->totalsize = 0;
pool->realsize = sizeof( mempool_t );
Q_strncpy( pool->name, name, sizeof( pool->name ));
pool->next = poolchain;
poolchain = pool;
#if XASH_64BIT
pool->idx = ++lastidx;
return pool->idx;
#else
return (poolhandle_t)pool;
#endif
pool = &poolchain[poolcount++];
return Mem_InitPool( pool, name, filename, fileline );
}
void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline )
{
mempool_t *pool;
mempool_t **chainaddress;
if( *poolptr && ( pool = Mem_FindPool( *poolptr )))
{
// unlink pool from chain
for( chainaddress = &poolchain; *chainaddress && *chainaddress != pool; chainaddress = &((*chainaddress)->next));
if( *chainaddress != pool ) Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
*chainaddress = pool->next;
if( !pool->filename )
{
Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
*poolptr = 0;
return;
}
// free memory owned by the pool
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
while( pool->chain )
Mem_FreeBlock( pool->chain, filename, fileline );
// free the pool itself
memset( pool, 0xBF, sizeof( mempool_t ));
Q_free( pool );
pool->chain = NULL;
pool->filename = NULL; // mark as reusable
*poolptr = 0;
}
}
void _Mem_EmptyPool( poolhandle_t poolptr, const char *filename, int fileline )
{
mempool_t *pool = Mem_FindPool( poolptr );
if( !poolptr ) Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
mempool_t *pool;
if( unlikely( !poolptr ))
{
Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
return;
}
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
pool = Mem_FindPool( poolptr );
if( !pool )
return;
// free memory owned by the pool
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
@@ -412,7 +406,8 @@ static qboolean Mem_CheckAlloc( mempool_t *pool, void *data )
else
{
// search all pools
for( pool = poolchain; pool; pool = pool->next )
size_t i;
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( Mem_CheckAlloc( pool, data ))
return true;
@@ -440,23 +435,24 @@ void _Mem_Check( const char *filename, int fileline )
{
memheader_t *mem;
mempool_t *pool;
size_t i;
for( pool = poolchain; pool; pool = pool->next )
Mem_CheckPool( "Mem_CheckSentinels", pool, filename, fileline );
for( pool = poolchain; pool; pool = pool->next )
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
for( mem = pool->chain; mem; mem = mem->next )
Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline );
}
void Mem_PrintStats( void )
{
size_t count = 0, size = 0, realsize = 0;
size_t count = 0, size = 0, realsize = 0, i;
mempool_t *pool;
Mem_Check();
for( pool = poolchain; pool; pool = pool->next )
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( !pool->filename )
continue;
count++;
size += pool->totalsize;
realsize += pool->realsize;
@@ -470,15 +466,19 @@ void Mem_PrintList( size_t minallocationsize )
{
mempool_t *pool;
memheader_t *mem;
size_t i;
Mem_Check();
Con_Printf( "memory pool list:\n" );
Con_Printf( "\t^3size\t\t\t\tname\n");
for( pool = poolchain; pool; pool = pool->next )
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
long changed_size = (long)pool->totalsize - (long)pool->lastchecksize;
if( !pool->filename )
continue;
// poolnames can contain color symbols, make sure what color is reset
if( pool->lastchecksize != 0 && changed_size != 0 )
{

View File

@@ -3344,12 +3344,10 @@ pfnPEntityOfEntIndex
=============
*/
static edict_t *pfnPEntityOfEntIndex( int iEntIndex )
static edict_t *pfnPEntityOfEntIndexBroken( int iEntIndex )
{
// have to be bug-compatible with GoldSrc in this function
if( host.bugcomp == BUGCOMP_GOLDSRC )
return SV_PEntityOfEntIndex( iEntIndex, false );
return SV_PEntityOfEntIndex( iEntIndex, true );
return SV_PEntityOfEntIndex( iEntIndex, false );
}
/*
@@ -4684,7 +4682,7 @@ static enginefuncs_t gEngfuncs =
pfnPEntityOfEntOffset,
pfnEntOffsetOfPEntity,
pfnIndexOfEdict,
pfnPEntityOfEntIndex,
pfnPEntityOfEntIndexAllEntities,
pfnFindEntityByVars,
pfnGetModelPtr,
pfnRegUserMsg,
@@ -5155,6 +5153,11 @@ qboolean SV_LoadProgs( const char *name )
// make sure what physic functions is cleared
memset( &svgame.physFuncs, 0, sizeof( svgame.physFuncs ));
// revert fix for pfnPEntityOfEntIndex to be compatible with GoldSrc
// games that rely on this bug
if( FBitSet( host.bugcomp, BUGCOMP_PENTITYOFENTINDEX_FLAG ))
gEngfuncs.pfnPEntityOfEntIndex = pfnPEntityOfEntIndexBroken;
// make local copy of engfuncs to prevent overwrite it with bots.dll
memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs ));
@@ -5167,7 +5170,7 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: failed to get address of GetEntityAPI proc\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
@@ -5178,7 +5181,7 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: failed to get address of GiveFnptrsToDll proc\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
@@ -5203,7 +5206,8 @@ qboolean SV_LoadProgs( const char *name )
{
if( !GetEntityAPI2( &svgame.dllFuncs, &version ))
{
Con_Printf( S_WARN "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
if( INTERFACE_VERSION != version )
Con_Printf( S_WARN "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
// fallback to old API
if( !GetEntityAPI( &svgame.dllFuncs, version ))
@@ -5211,9 +5215,10 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
else Con_Reportf( "SV_LoadProgs: ^2initailized legacy EntityAPI ^7ver. %i\n", version );
}
else Con_Reportf( "SV_LoadProgs: ^2initailized extended EntityAPI ^7ver. %i\n", version );
}
@@ -5222,9 +5227,10 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
else Con_Reportf( "SV_LoadProgs: ^2initailized legacy EntityAPI ^7ver. %i\n", version );
SV_InitOperatorCommands();
Mod_InitStudioAPI();

View File

@@ -1363,8 +1363,13 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll
if( index >= 0 && !dllInfo->encrypted && search )
{
Q_snprintf( dllInfo->fullPath, sizeof( dllInfo->fullPath ),
"%s%s", search->filename, dllInfo->shortPath );
// gamedll might resolve it's own path using dladdr()
// combine it with engine returned path to gamedir
// it might lead to double gamedir like this
// - valve/valve/dlls/hl.so
// instead of expected
// - valve/dlls/hl.so
Q_snprintf( dllInfo->fullPath, sizeof( dllInfo->fullPath ), "%s/%s%s", fs_rootdir, search->filename, dllInfo->shortPath );
dllInfo->custom_loader = false; // we can loading from disk and use normal debugging
}
else

View File

@@ -31,7 +31,7 @@ extern "C"
{
#endif // __cplusplus
#define FS_API_VERSION 2 // not stable yet!
#define FS_API_VERSION 3 // not stable yet!
#define FS_API_CREATEINTERFACE_TAG "XashFileSystem002" // follow FS_API_VERSION!!!
#define FILESYSTEM_INTERFACE_VERSION "VFileSystem009" // never change this!
@@ -121,8 +121,8 @@ typedef enum
typedef struct fs_dllinfo_t
{
string fullPath;
string shortPath;
char fullPath[2048]; // absolute disk path
string shortPath; // vfs path
qboolean encrypted;
qboolean custom_loader;
} fs_dllinfo_t;

View File

@@ -73,7 +73,8 @@ GNU General Public License for more details.
#define Q_min( a, b ) (((a) < (b)) ? (a) : (b))
#define Q_max( a, b ) (((a) > (b)) ? (a) : (b))
#define Q_equal( a, b ) (((a) > ((b) - EQUAL_EPSILON)) && ((a) < ((b) + EQUAL_EPSILON)))
#define Q_equal_e( a, b, e ) (((a) > ((b) - (e))) && ((a) < ((b) + (e))))
#define Q_equal( a, b ) Q_equal_e( a, b, EQUAL_EPSILON )
#define Q_recip( a ) ((float)(1.0f / (float)(a)))
#define Q_floor( a ) ((float)(int)(a))
#define Q_ceil( a ) ((float)(int)((a) + 1))

View File

@@ -279,7 +279,7 @@ def configure(conf):
conf.env.append_unique('CXXFLAGS', cxxflags)
conf.env.append_unique('LINKFLAGS', linkflags)
if conf.env.COMPILER_CC != 'msvc' and not conf.options.DISABLE_WERROR:
if conf.env.COMPILER_CC != 'msvc':
opt_flags = [
# '-Wall', '-Wextra', '-Wpedantic',
'-fdiagnostics-color=always',
@@ -342,6 +342,11 @@ def configure(conf):
opt_cxxflags = [] # TODO:
if conf.options.DISABLE_WERROR:
opt_flags = []
opt_cflags = ['-Werror=implicit-function-declaration']
opt_cxxflags = []
conf.env.CFLAGS_werror = conf.filter_cflags(opt_flags + opt_cflags, cflags)
conf.env.CXXFLAGS_werror = conf.filter_cxxflags(opt_flags + opt_cxxflags, cxxflags)