diff --git a/Documentation/bug-compatibility.md b/Documentation/bug-compatibility.md index 790fb04b..7feddce1 100644 --- a/Documentation/bug-compatibility.md +++ b/Documentation/bug-compatibility.md @@ -13,3 +13,4 @@ When `-bugcomp` is specified with argument, it interpreted as flags separated wi | 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 | +| `gsmrf` | Rewrites message at the moment when Game DLL attempts to write an internal engine message, usually specific to GoldSrc protocol. Right now only supports `svc_spawnstaticsound`, more messages added by request. | * MetaMod/AMXModX based mods | diff --git a/engine/common/common.h b/engine/common/common.h index 31211ab7..8cb701f0 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -275,6 +275,10 @@ typedef enum bugcomp_e { // reverts fix for pfnPEntityOfEntIndex for bug compatibility with GoldSrc BUGCOMP_PENTITYOFENTINDEX_FLAG = BIT( 0 ), + + // rewrites mod's attempts to write GoldSrc-specific messages into Xash protocol + // (new wrappers are added by request) + BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG = BIT( 1 ), } bugcomp_t; typedef struct host_parm_s diff --git a/engine/common/host.c b/engine/common/host.c index a2da3a67..78540c36 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -75,6 +75,7 @@ typedef struct feature_message_s static feature_message_t bugcomp_features[] = { { BUGCOMP_PENTITYOFENTINDEX_FLAG, "pfnPEntityOfEntIndex bugfix revert", "peoei" }, +{ BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG, "GoldSrc Message Rewrite Facility", "gsmrf" }, }; static feature_message_t engine_features[] = diff --git a/engine/server/server.h b/engine/server/server.h index 69d3eda5..b828c14d 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -329,6 +329,8 @@ typedef struct int msg_realsize; // left in bytes int msg_index; // for debug messages int msg_dest; // msg destination ( MSG_ONE, MSG_ALL etc ) + int msg_rewrite_index; + int msg_rewrite_pos; qboolean msg_started; // to avoid recursive included messages edict_t *msg_ent; // user message member entity vec3_t msg_org; // user message member origin diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 7d1421f1..c256b379 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -2501,6 +2501,62 @@ int GAME_EXPORT pfnDecalIndex( const char *m ) return -1; } +static int SV_CanRewriteMessage( int msg_num ) +{ + // feature is disabled + if( !FBitSet( host.bugcomp, BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG )) + return 0; + + switch( msg_num ) + { + case svc_goldsrc_spawnstaticsound: + return svc_sound; + } + + return 0; +} + +static qboolean SV_RewriteMessage( void ) +{ + vec3_t origin; + const char *sample = NULL; + float vol, attn; + int ent, pitch, flags, idx; + int cmd; + + MSG_SeekToBit( &sv.multicast, svgame.msg_rewrite_pos, SEEK_SET ); + + cmd = MSG_ReadCmd( &sv.multicast, NS_SERVER ); + + switch( cmd ) + { + case svc_goldsrc_spawnstaticsound: + MSG_ReadVec3Coord( &sv.multicast, origin ); + idx = MSG_ReadShort( &sv.multicast ); + vol = MSG_ReadByte( &sv.multicast ); + attn = MSG_ReadByte( &sv.multicast ) / 64.0f; + ent = MSG_ReadShort( &sv.multicast ); + pitch = MSG_ReadByte( &sv.multicast ); + flags = MSG_ReadByte( &sv.multicast ); + + if( FBitSet( flags, SND_SENTENCE )) + sample = va( "!%i", idx ); + else if( idx >= 0 && idx < MAX_SOUNDS ) + sample = sv.sound_precache[idx]; + + if( !COM_CheckString( sample )) + { + Con_Printf( S_ERROR "%s: unrecognized sample in svc_spawnstaticsound, index %d, flags 0x%x\n", __func__, idx, flags ); + return false; + } + + MSG_SeekToBit( &sv.multicast, svgame.msg_rewrite_pos, SEEK_SET ); + return SV_BuildSoundMsg( &sv.multicast, EDICT_NUM( ent ), CHAN_STATIC, sample, vol, attn, flags, pitch, origin ); + } + + return false; +} + /* ============= pfnMessageBegin @@ -2518,10 +2574,24 @@ static void GAME_EXPORT pfnMessageBegin( int msg_dest, int msg_num, const float // check range msg_num = bound( svc_bad, msg_num, 255 ); + svgame.msg_rewrite_index = 0; + svgame.msg_rewrite_pos = 0; + if( msg_num <= svc_lastmsg ) { - svgame.msg_index = -msg_num; // this is a system message - svgame.msg_name = svc_strings[msg_num]; + // check if we should rewrite this message into something else... + if( SV_CanRewriteMessage( msg_num )) + { + svgame.msg_index = -SV_CanRewriteMessage( msg_num ); + svgame.msg_name = svc_goldsrc_strings[msg_num] ? svc_goldsrc_strings[msg_num] : svc_strings[msg_num]; + svgame.msg_rewrite_index = msg_num; + svgame.msg_rewrite_pos = MSG_TellBit( &sv.multicast ); + } + else + { + svgame.msg_index = -msg_num; // this is a system message + svgame.msg_name = svc_strings[msg_num]; + } if( msg_num == svc_temp_entity ) iSize = -1; // temp entity have variable size @@ -2596,6 +2666,25 @@ static void GAME_EXPORT pfnMessageEnd( void ) return; } + if( svgame.msg_rewrite_index != 0 ) + { + if( SV_RewriteMessage( )) + { + if( MSG_CheckOverflow( &sv.multicast )) + { + Con_Printf( S_ERROR "MessageEnd: %s has overflow multicast buffer (post-rewrite)\n", name ); + MSG_Clear( &sv.multicast ); + return; + } + } + else + { + Con_Printf( S_ERROR "MessageEnd: failed to rewrite message %s\n", name ); + MSG_Clear( &sv.multicast ); + return; + } + } + // check for system message if( svgame.msg_index < 0 ) { @@ -2662,9 +2751,7 @@ static void GAME_EXPORT pfnMessageEnd( void ) // update some messages in case their was format was changed and we want to keep backward compatibility if( svgame.msg_index < 0 ) { - int svc_msg = abs( svgame.msg_index ); - - if(( svc_msg == svc_finale || svc_msg == svc_cutscene ) && svgame.msg_realsize == 0 ) + if(( svgame.msg_index == -svc_finale || svgame.msg_index == -svc_cutscene ) && svgame.msg_realsize == 0 ) MSG_WriteChar( &sv.multicast, 0 ); // write null string }