diff --git a/Documentation/bug-compatibility.md b/Documentation/bug-compatibility.md index 27ac9849..790fb04b 100644 --- a/Documentation/bug-compatibility.md +++ b/Documentation/bug-compatibility.md @@ -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 | diff --git a/engine/common/common.h b/engine/common/common.h index 773b2716..31211ab7 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -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; diff --git a/engine/common/host.c b/engine/common/host.c index 362ca09e..a2da3a67 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -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 ", "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 )) @@ -947,6 +956,53 @@ static void Host_RunTests( int stage ) } #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 @@ -1051,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 @@ -1184,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" ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 05849c25..d02412d7 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -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 ));