engine: implement small Message Rewrite Facitility that allows to run mods that directly write internal GoldSrc messages

This commit is contained in:
Alibek Omarov 2024-04-21 20:09:58 +03:00
parent 7ccb0b5c02
commit dd410a2de5
5 changed files with 100 additions and 5 deletions

View File

@ -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 |

View File

@ -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

View File

@ -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[] =

View File

@ -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

View File

@ -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
}