/* VoiceAnnounceEx * * Copyright (C) 2017-2019 Francisco 'Franc1sco' GarcĂ­a * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see http://www.gnu.org/licenses/. */ #include #include #include #include #pragma semicolon 1 #pragma newdecls required #define PLUGIN_VERSION "2.2.1" Handle g_hProcessVoice; Handle g_hOnClientTalking; Handle g_hOnClientTalkingEnd; bool g_bLateLoad; int g_iHookID[MAXPLAYERS+1] = { -1, ... }; Handle g_hClientMicTimers[MAXPLAYERS + 1]; bool g_bCsgo; Handle g_hCSGOVoice; public Plugin myinfo = { name = "VoiceAnnounceEx", author = "Franc1sco franug, Mini and GoD-Tony", description = "Feature for developers to check/control client mic usage.", version = PLUGIN_VERSION, url = "http://steamcommunity.com/id/franug" } public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { g_bCsgo = (GetEngineVersion() == Engine_CSGO || GetEngineVersion() == Engine_Left4Dead || GetEngineVersion() == Engine_Left4Dead2); CreateNative("IsClientSpeaking", Native_IsClientTalking); RegPluginLibrary("voiceannounce_ex"); g_hOnClientTalking = CreateGlobalForward("OnClientSpeakingEx", ET_Ignore, Param_Cell); g_hOnClientTalkingEnd = CreateGlobalForward("OnClientSpeakingEnd", ET_Ignore, Param_Cell); g_bLateLoad = late; return APLRes_Success; } public int Native_IsClientTalking(Handle plugin, int numParams) { int iClient = GetNativeCell(1); if (iClient > MaxClients || iClient <= 0) return ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); else if (!IsClientInGame(iClient)) return ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); else if (IsFakeClient(iClient)) return ThrowNativeError(SP_ERROR_NATIVE, "Cannot do mic checks on fake clients."); return g_hClientMicTimers[iClient] != INVALID_HANDLE; } public void OnPluginStart() { CreateConVar("voiceannounce_ex_version", PLUGIN_VERSION, "VoiceAnnounceEx version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); int offset; if (g_bCsgo) { offset = GameConfGetOffset(GetConfig(), "OnVoiceTransmit"); if (offset == -1) SetFailState("Failed to get offset"); g_hCSGOVoice = DHookCreate(offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity, CSGOVoicePost); } else { offset = GameConfGetOffset(GetConfig(), "CGameClient::ProcessVoiceData"); g_hProcessVoice = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Address, Hook_ProcessVoiceData); DHookAddParam(g_hProcessVoice, HookParamType_ObjectPtr); } if (g_bLateLoad) { for (int i = 1; i <= MaxClients; i++) { if (!IsClientInGame(i) || IsFakeClient(i)) continue; OnClientPutInServer(i); } } } public void OnClientPutInServer(int iClient) { if (IsFakeClient(iClient)) return; if (g_bCsgo) DHookEntity(g_hCSGOVoice, true, iClient); else g_iHookID[iClient] = DHookRaw(g_hProcessVoice, true, GetIMsgHandler(iClient)); g_hClientMicTimers[iClient] = INVALID_HANDLE; } public void OnClientDisconnect(int iClient) { if (IsFakeClient(iClient)) return; if (g_bCsgo) { if (g_iHookID[iClient] != -1) { DHookRemoveHookID(g_iHookID[iClient]); g_iHookID[iClient] = -1; } } g_hClientMicTimers[iClient] = INVALID_HANDLE; } public MRESReturn Hook_ProcessVoiceData(Address pThis) { Address pIClient = pThis - view_as
(4); int iClient = view_as(GetPlayerSlot(pIClient)) + 1; if (!IsClientConnected(iClient)) return MRES_Ignored; if (g_hClientMicTimers[iClient] != INVALID_HANDLE) delete g_hClientMicTimers[iClient]; g_hClientMicTimers[iClient] = CreateTimer(0.3, Timer_ClientMicUsage, GetClientUserId(iClient)); Call_StartForward(g_hOnClientTalking); Call_PushCell(iClient); Call_Finish(); return MRES_Ignored; } public MRESReturn CSGOVoicePost(int iClient, Handle hReturn, Handle hParams) { if (g_hClientMicTimers[iClient] != INVALID_HANDLE) delete g_hClientMicTimers[iClient]; g_hClientMicTimers[iClient] = CreateTimer(0.3, Timer_ClientMicUsage, GetClientUserId(iClient)); Call_StartForward(g_hOnClientTalking); Call_PushCell(iClient); Call_Finish(); return MRES_Ignored; } public Action Timer_ClientMicUsage(Handle hTimer, int iUserid) { int iClient = GetClientOfUserId(iUserid); if (!iClient) return; if (g_hClientMicTimers[iClient] != hTimer) return; g_hClientMicTimers[iClient] = INVALID_HANDLE; Call_StartForward(g_hOnClientTalkingEnd); Call_PushCell(iClient); Call_Finish(); } /* * Internal Functions * Credits go to GoD-Tony */ stock Handle GetConfig() { static Handle hGameConf = INVALID_HANDLE; if (hGameConf == INVALID_HANDLE) hGameConf = LoadGameConfigFile("voiceannounce_ex.games"); return hGameConf; } stock Address GetBaseServer() { static Address pBaseServer = Address_Null; if (pBaseServer == Address_Null) pBaseServer = GameConfGetAddress(GetConfig(), "CBaseServer"); return pBaseServer; } stock Address GetIClient(int slot) { static Handle hGetClient = INVALID_HANDLE; if (hGetClient == INVALID_HANDLE) { StartPrepSDKCall(SDKCall_Raw); PrepSDKCall_SetFromConf(GetConfig(), SDKConf_Virtual, "CBaseServer::GetClient"); PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); hGetClient = EndPrepSDKCall(); } return view_as
(SDKCall(hGetClient, GetBaseServer(), slot)); } stock any GetPlayerSlot(Address pIClient) { static Handle hPlayerSlot = INVALID_HANDLE; if (hPlayerSlot == INVALID_HANDLE) { StartPrepSDKCall(SDKCall_Raw); PrepSDKCall_SetFromConf(GetConfig(), SDKConf_Virtual, "CBaseClient::GetPlayerSlot"); PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); hPlayerSlot = EndPrepSDKCall(); } return SDKCall(hPlayerSlot, pIClient); } stock Address GetIMsgHandler(int iClient) { return GetIClient(iClient - 1) + view_as
(4); }