added sm_forceinputplayer to ForceInputs added ZE_FFVII_Mako_Reactor_V6_B08 savelevel config fixed runtime error in voiceannounce_ex and ExtraCommands
		
			
				
	
	
		
			254 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			SourcePawn
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			SourcePawn
		
	
	
	
	
	
#pragma semicolon 1
 | 
						|
 | 
						|
#include <sourcemod>
 | 
						|
#include <sdktools>
 | 
						|
#include <dhooks>
 | 
						|
#include <voiceannounce_ex>
 | 
						|
 | 
						|
#define PLUGIN_VERSION "2.0.0"
 | 
						|
 | 
						|
new Handle:g_hProcessVoice = INVALID_HANDLE;
 | 
						|
new Handle:g_hOnClientTalking = INVALID_HANDLE;
 | 
						|
new Handle:g_hOnClientTalkingEnd = INVALID_HANDLE;
 | 
						|
new bool:g_bLateLoad = false;
 | 
						|
 | 
						|
new g_iHookID[MAXPLAYERS+1] = { -1, ... };
 | 
						|
new Handle:g_hClientMicTimers[MAXPLAYERS + 1] = {INVALID_HANDLE, ...};
 | 
						|
 | 
						|
new bool:g_bIsCSGO;
 | 
						|
new 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 = "https://forums.alliedmods.net"
 | 
						|
}
 | 
						|
 | 
						|
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
 | 
						|
{
 | 
						|
	if(GetEngineVersion() == Engine_CSGO)
 | 
						|
		g_bIsCSGO = true;
 | 
						|
	else
 | 
						|
		g_bIsCSGO = false;
 | 
						|
 | 
						|
	CreateNative("IsClientSpeaking", Native_IsClientTalking);
 | 
						|
 | 
						|
	RegPluginLibrary("voiceannounce_ex");
 | 
						|
 | 
						|
	g_bLateLoad = late;
 | 
						|
	return APLRes_Success;
 | 
						|
}
 | 
						|
 | 
						|
public Native_IsClientTalking(Handle:plugin, numParams)
 | 
						|
{
 | 
						|
	new client = GetNativeCell(1);
 | 
						|
 | 
						|
	if (client > MaxClients || client <= 0)
 | 
						|
	{
 | 
						|
		ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid.");
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!IsClientInGame(client))
 | 
						|
	{
 | 
						|
		ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game.");
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (IsFakeClient(client))
 | 
						|
	{
 | 
						|
		ThrowNativeError(SP_ERROR_NATIVE, "Cannot do mic checks on fake clients.");
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	return (g_hClientMicTimers[client] == INVALID_HANDLE) ? false : true;
 | 
						|
}
 | 
						|
 | 
						|
public OnPluginStart()
 | 
						|
{
 | 
						|
	//CreateConVar("voiceannounce_ex_version", PLUGIN_VERSION, "plugin", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
 | 
						|
	new Offset;
 | 
						|
	if(g_bIsCSGO)
 | 
						|
	{
 | 
						|
		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);
 | 
						|
	}
 | 
						|
 | 
						|
	g_hOnClientTalking = CreateGlobalForward("OnClientSpeakingEx", ET_Single, Param_Cell);
 | 
						|
	g_hOnClientTalkingEnd = CreateGlobalForward("OnClientSpeakingEnd", ET_Ignore, Param_Cell);
 | 
						|
 | 
						|
	if(g_bLateLoad)
 | 
						|
	{
 | 
						|
		for(new i = 1; i <= MaxClients; i++)
 | 
						|
		{
 | 
						|
			if(IsClientInGame(i) && !IsFakeClient(i))
 | 
						|
				OnClientPutInServer(i);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
public OnClientPutInServer(client)
 | 
						|
{
 | 
						|
	if (!IsFakeClient(client))
 | 
						|
	{
 | 
						|
		if(g_bIsCSGO)
 | 
						|
			DHookEntity(g_hCSGOVoice, true, client);
 | 
						|
		else
 | 
						|
			g_iHookID[client] = DHookRaw(g_hProcessVoice, true, GetIMsgHandler(client));
 | 
						|
 | 
						|
		if(g_hClientMicTimers[client] != INVALID_HANDLE)
 | 
						|
			KillTimer(g_hClientMicTimers[client]);
 | 
						|
 | 
						|
		g_hClientMicTimers[client] = INVALID_HANDLE;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
public OnClientDisconnect(client)
 | 
						|
{
 | 
						|
	if(g_bIsCSGO)
 | 
						|
	{
 | 
						|
		if(g_iHookID[client] != -1)
 | 
						|
		{
 | 
						|
			DHookRemoveHookID(g_iHookID[client]);
 | 
						|
			g_iHookID[client] = -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if(g_hClientMicTimers[client] != INVALID_HANDLE)
 | 
						|
		KillTimer(g_hClientMicTimers[client]);
 | 
						|
 | 
						|
	g_hClientMicTimers[client] = INVALID_HANDLE;
 | 
						|
}
 | 
						|
 | 
						|
public MRESReturn:Hook_ProcessVoiceData(Address:pThis, Handle:hParams)
 | 
						|
{
 | 
						|
	new Address:pIClient = pThis - Address:4;
 | 
						|
	new client = GetPlayerSlot(pIClient) + 1;
 | 
						|
 | 
						|
	if(g_hClientMicTimers[client] != INVALID_HANDLE)
 | 
						|
	{
 | 
						|
		KillTimer(g_hClientMicTimers[client]);
 | 
						|
		g_hClientMicTimers[client] = CreateTimer(0.3, Timer_ClientMicUsage, GetClientUserId(client));
 | 
						|
	}
 | 
						|
 | 
						|
	if(g_hClientMicTimers[client] == INVALID_HANDLE && IsClientInGame(client))
 | 
						|
		g_hClientMicTimers[client] = CreateTimer(0.3, Timer_ClientMicUsage, GetClientUserId(client));
 | 
						|
 | 
						|
	new bool:returnBool = true;
 | 
						|
	Call_StartForward(g_hOnClientTalking);
 | 
						|
	Call_PushCell(client);
 | 
						|
	Call_Finish(_:returnBool);
 | 
						|
 | 
						|
	if(!returnBool)
 | 
						|
		return MRES_Supercede;
 | 
						|
 | 
						|
	return MRES_Ignored;
 | 
						|
}
 | 
						|
 | 
						|
public MRESReturn:CSGOVoicePost(client, Handle:hReturn)
 | 
						|
{
 | 
						|
	if(g_hClientMicTimers[client] != INVALID_HANDLE)
 | 
						|
	{
 | 
						|
		KillTimer(g_hClientMicTimers[client]);
 | 
						|
		g_hClientMicTimers[client] = CreateTimer(0.3, Timer_ClientMicUsage, GetClientUserId(client));
 | 
						|
	}
 | 
						|
 | 
						|
	if(g_hClientMicTimers[client] == INVALID_HANDLE && IsClientInGame(client))
 | 
						|
		g_hClientMicTimers[client] = CreateTimer(0.3, Timer_ClientMicUsage, GetClientUserId(client));
 | 
						|
 | 
						|
 | 
						|
	new bool:returnBool = true;
 | 
						|
	Call_StartForward(g_hOnClientTalking);
 | 
						|
	Call_PushCell(client);
 | 
						|
	Call_Finish(_:returnBool);
 | 
						|
 | 
						|
	if(!returnBool)
 | 
						|
		return MRES_Supercede;
 | 
						|
 | 
						|
	return MRES_Ignored;
 | 
						|
}
 | 
						|
 | 
						|
public Action:Timer_ClientMicUsage(Handle:timer, any:userid)
 | 
						|
{
 | 
						|
	new client = GetClientOfUserId(userid);
 | 
						|
	g_hClientMicTimers[client] = INVALID_HANDLE;
 | 
						|
 | 
						|
	Call_StartForward(g_hOnClientTalkingEnd);
 | 
						|
	Call_PushCell(client);
 | 
						|
	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(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 Address:SDKCall(hGetClient, GetBaseServer(), slot);
 | 
						|
}
 | 
						|
 | 
						|
stock 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(client)
 | 
						|
{
 | 
						|
	return GetIClient(client - 1) + Address:4;
 | 
						|
}
 |