Page 1 of 1

[CODE] [ORPHEU] How to hook when player use +voicerecord

Posted: Wed Sep 30, 2026 2:50 pm
by Archive-Poster
I'll try to show you guys how to detect when some player use +voicerecord command.

This code will run using Orpheu and, at this time, will run only on linux because I don't have the windows signature but I can't test on windows too.
Download the file attached and install signatures in configs/orpheu/functions (only linux engine 4883)

First of all, lets talk about how messages from client will be processed by server.

Each time a client sends information to servers, it uses CL messages.
They are handled server side using SV_ExecuteClientMessage function (engine library).

This function will check which command was sent by client and then run the associated function (listed sv_clcfuncs structure)

These are the available messages with their associated engine functions:

Code: Select all

clc_bad            // no function associated
clc_nop            // no function associated
clc_move            SV_ParseMove
clc_stringcmd        SV_ParseStringCommand
clc_delta            SV_ParseDelta
clc_resourcelist        SV_ParseResourceList
clc_tmove            // no function associated
clc_fileconsistency    SV_ParseConsistencyResponse
clc_voicedata        SV_ParseVoiceData
clc_hltv            SV_IgnoreHLTV
clc_cvarvalue        SV_ParseCvarValue
clc_cvarvalue2        SV_ParseCvarValue2 
This function has only one argument, a pointer to client_t structure.
All I know about that structure for HL1 engine is it's 20200 bytes long (for linux at least)

So, for +voicerecord command we are going to work with SV_ParseVoiceData

When the client holds +voicerecord bind, SV_ParseVoiceData repeatedly is called until it stops using it.

Here's the code to make a plugin that register 2 forwards:

client_voicerecord_on
Called from Voice_SetClientListening (from fakemeta) when sender is using +voicerecord and receiver is the lowest id connected to server.
client_voicerecord_off
Called from Voice_SetClientListening (from fakemeta) after client_voicerecord_on and sender is the same as previus forward and receiver is the highest id connected to server.

Code: Select all

#include <amxmodx>
#include <fakemeta>
#include <hamsandwich>
#include <orpheu>
#include <orpheu_memory>

#define GetPlayerByClientStruct(%0)     ( ( %0 - g_client_t_address ) / 20200 + 1 )

new g_top_client
new g_client_t_address
new g_player_voice_status[ 32 ]

new g_fwdVoiceRecordOn
new g_fwdVoiceRecordOff

public plugin_init()
{
    register_forward( FM_Voice_SetClientListening, "fwd_Voice_SetClientListening" )
    OrpheuRegisterHook( OrpheuGetFunction( "SV_ParseVoiceData" ), "SV_ParseVoiceData" )
    RegisterHam( Ham_Spawn, "player", "hamPlayerSpawn" )

    g_fwdVoiceRecordOn = CreateMultiForward( "client_voicerecord_on", ET_IGNORE, FP_CELL )
    g_fwdVoiceRecordOff = CreateMultiForward( "client_voicerecord_off", ET_IGNORE, FP_CELL )

    new svs = OrpheuMemoryGet( "svs_ptr" )

    g_client_t_address = OrpheuMemoryGetAtAddress( svs + 4, "engineInt" )
    arrayset( g_player_voice_status, 0, 32 )
    g_top_client = 0
}

public SV_ParseVoiceData( info )
{
    // Flag user as it's using voicerecord
    new player = GetPlayerByClientStruct( info )
    g_player_voice_status[ player - 1 ] = 1

}

public hamPlayerSpawn( id )
{
    set_top_client()
    g_player_voice_status[ id - 1 ] = 0
}

public fwd_Voice_SetClientListening( receiver, sender, bool:listen )
{
    new result
    // Detect +voicerecord
    if( receiver == 1 && g_player_voice_status[ sender - 1 ] )
    {
        ExecuteForward( g_fwdVoiceRecordOn, result, sender )
    }

    // Detect -voicerecord
    if( receiver == g_top_client && g_player_voice_status[ sender - 1 ] )
    {
        ExecuteForward( g_fwdVoiceRecordOff, result, sender )
        g_player_voice_status[ sender - 1 ] = 0
    }

    return FMRES_IGNORED
}

public client_disconnect( id )
{
    set_top_client()
    g_player_voice_status[ id - 1 ] = 0
}

stock set_top_client()
{
    new players[ 32 ], num

    get_players( players, num )

    for( num--; num >= 0; num-- )
    {
        if( players[ num ] > g_top_client )
          g_top_client = players[ num ]
    }
} 
Each time SV_ParseVoiceData is called, we set a flag for the associated player.
The problem is how to know which player is using +voicerecord.
Well, we receive a pointer to the current player client_t structure.

At offset 4 of svs structure we have the pointer to the first player client_t structure and we know it's 20200 bytes long for each client, so we can do some math and get which player is using the command.

Code: Select all

svs[4] = contents at svs_ptr + 4 bytes (should be readed as integer)
param1 = first argument of SV_ParseVoiceData (memory address)
20200 = size of client_t structure

id of player = ( svs[4] - param1 ) / 20200 + 1 
And here's a simple plugin that uses those new forwards:

Code: Select all

#include <amxmodx>
#include <fakemeta>

forward client_voicerecord_on( id )
forward client_voicerecord_off( id )

public client_voicerecord_on( id )
{
        client_print(id, print_center, "you're talking!!")
}

public client_voicerecord_off( id )
{
//
}