#include #include #include #pragma newdecls required #pragma semicolon 1 public Plugin myinfo = { name = "Sound Manager", author = "Haze", description = "", version = "1.0.5", url = "" } #define Mute_Soundscapes (1 << 0) #define Mute_AmbientSounds (1 << 1) #define Mute_GunSounds (1 << 2) #define Mute_TriggerSounds (1 << 3) #define Mute_NormalSounds (1 << 4) #define Debug (1 << 5) // Engine EngineVersion gEV_Type = Engine_Unknown; // Player settings int gI_Settings[MAXPLAYERS+1]; bool gB_AlreadyMuted[MAXPLAYERS+1]; // Debug int gI_LastSoundscape[MAXPLAYERS+1]; // Cookie Handle gH_SettingsCookie = null; // DHooks Address gP_SoundscapeSystem = Address_Null; Handle gH_AcceptInput = null; Handle gH_GetPlayerSlot = null; Handle gH_GetStringID = null; int gI_AmbientOffset = -1; int gI_m_soundscapesOffset = -1; int gI_VolumeOffset = -1; int gI_CurrentSoundscapeOffset = -1; int gI_CGameClientThing = -1; // Other int gI_SilentSoundScape = -1; char gS_WeaponID[32]; bool gB_ShouldHookShotgunShot = false; ArrayList gA_LoopingAmbients = null; bool gB_EntitiesFound = false; // Late Load bool gB_LateLoad = false; //-----------------------FORWARDS------------------------- public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { gB_LateLoad = late; } public void OnPluginStart() { gEV_Type = GetEngineVersion(); if(gEV_Type == Engine_CSS) { FormatEx(gS_WeaponID, sizeof(gS_WeaponID), "m_iWeaponID"); } else if(gEV_Type == Engine_CSGO) { FormatEx(gS_WeaponID, sizeof(gS_WeaponID), "m_weapon"); } else { SetFailState("This plugin is only supported for CSS and CSGO."); } // Commands RegConsoleCmd("sm_snd", Command_Sounds); RegConsoleCmd("sm_sound", Command_Sounds); RegConsoleCmd("sm_sounds", Command_Sounds); RegConsoleCmd("sm_music", Command_Sounds); RegConsoleCmd("sm_stopmusic", Command_Sounds); RegConsoleCmd("sm_stopsounds", Command_Sounds); // Cookie gH_SettingsCookie = RegClientCookie("sound_settings", "Sound Manager Settings", CookieAccess_Protected); gA_LoopingAmbients = new ArrayList(ByteCountToCells(4)); // Hook round_start HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); // Dhooks LoadDHooks(); // Sound Hook AddTempEntHook("Shotgun Shot", Hook_ShotgunShot); // Late Load if(gB_LateLoad) { int entity = -1; while((entity = FindEntityByClassname(entity, "point_clientcommand")) != -1) { DHookEntity(gH_AcceptInput, false, entity); } Event_RoundStart(null, "", false); for(int i = 1; i <= MaxClients; i++) { if(!IsValidClient(i)) { continue; } if(AreClientCookiesCached(i)) { OnClientCookiesCached(i); } } } } public void OnEntityCreated(int entity, const char[] classname) { if(StrEqual(classname, "point_clientcommand")) { DHookEntity(gH_AcceptInput, false, entity); } } public void OnClientDisconnect_Post(int client) { gB_AlreadyMuted[client] = false; gI_Settings[client] = 0; CheckShotgunShotHook(); } public void OnClientCookiesCached(int client) { gI_LastSoundscape[client] = -1; char sCookie[16]; GetClientCookie(client, gH_SettingsCookie, sCookie, 16); if(strlen(sCookie) == 0) { gI_Settings[client] = 0; } else { gI_Settings[client] = StringToInt(sCookie); } if((gI_Settings[client] & Mute_GunSounds) && gB_ShouldHookShotgunShot == false) { gB_ShouldHookShotgunShot = true; } if(gI_Settings[client] & Mute_Soundscapes) { ClientCommand(client, "cl_soundscape_flush"); // maybe auth can take a long time and a soundscape slips through... } } public Action OnPlayerRunCmd(int client) { if(!IsValidClient(client)) { return Plugin_Continue; } if(gI_Settings[client] & Mute_AmbientSounds == 0) { return Plugin_Continue; } if(gB_AlreadyMuted[client]) { return Plugin_Continue; } if(!gB_EntitiesFound) { return Plugin_Continue; } for(int i = 0; i < gA_LoopingAmbients.Length; i++) { int entity = EntRefToEntIndex(gA_LoopingAmbients.Get(i)); if(entity != INVALID_ENT_REFERENCE) { char sSound[PLATFORM_MAX_PATH]; GetEntPropString(entity, Prop_Data, "m_iszSound", sSound, PLATFORM_MAX_PATH); int channel = SNDCHAN_STATIC; int level = SNDLEVEL_NONE; float volume = 0.0; int pitch = SNDPITCH_NORMAL; char sSample[PLATFORM_MAX_PATH]; if (GetGameSoundParams(sSound, channel, level, volume, pitch, sSample, sizeof(sSample), entity)) { sSound = sSample; } EmitSoundToClient(client, sSound, entity, SNDCHAN_STATIC, SNDLEVEL_NONE, SND_STOP, 0.0, 0, _, _, _, true); if(gI_Settings[client] & Debug) { PrintToChat(client, "[Debug] Ambient Muted (%s)", sSound); } } } gB_AlreadyMuted[client] = true; return Plugin_Continue; } //------------------------------------------------------------- void LoadDHooks() { Handle hGameData = LoadGameConfigFile("SoundManager.games"); if(!hGameData) { SetFailState("Failed to load SoundManager gamedata."); } StartPrepSDKCall_GetStringID(hGameData); HookSoundscapes(hGameData); HookAcceptInput(); HookSendSound(hGameData); if ((gI_AmbientOffset = GameConfGetOffset(hGameData, "SoundInfo_t::bIsAmbient")) == -1) { SetFailState("Could not get SoundInfo_t::bIsAmbient offset"); } if ((gI_m_soundscapesOffset = GameConfGetOffset(hGameData, "CSoundscapeSystem::m_soundscapes")) == -1) { SetFailState("Could not get CSoundscapeSystem::m_soundscapes offset"); } if ((gI_VolumeOffset = GameConfGetOffset(hGameData, "SoundInfo_t::fVolume")) == -1) { SetFailState("Could not get SoundInfo_t::fVolume offset"); } if ((gI_CurrentSoundscapeOffset = GameConfGetOffset(hGameData, "ss_update_t::pCurrentSoundscape")) == -1) { SetFailState("Could not get ss_update_t::pCurrentSoundscape"); } if ((gI_CGameClientThing = GameConfGetOffset(hGameData, "CGameClient::thing")) == -1) { SetFailState("Could not get CGameClient::thing"); } delete hGameData; } //-------------------------SOUNDSCAPES------------------------- void StartPrepSDKCall_GetStringID(Handle hGameData) { gP_SoundscapeSystem = GameConfGetAddress(hGameData, "CSoundscapeSystem"); if(gP_SoundscapeSystem == Address_Null) { SetFailState("Could not get address of CSoundscapeSystem pointer."); } StartPrepSDKCall(SDKCall_Raw); PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CStringRegistry::GetStringID"); PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); gH_GetStringID = EndPrepSDKCall(); if (gH_GetStringID == null) { SetFailState("Could not initialize call to CStringRegistry::GetStringID."); } } int GetSoundscapeIndex(const char[] sString) { return SDKCall(gH_GetStringID, gP_SoundscapeSystem + view_as
(gI_m_soundscapesOffset), sString); } public void OnMapStart() { static const char sSilentSoundScapes[][] = { "port.LightHum2", "Nothing" }; gI_SilentSoundScape = -1; if (gH_GetStringID) { for(int i = 0; i < sizeof(sSilentSoundScapes); i++) { gI_SilentSoundScape = GetSoundscapeIndex(sSilentSoundScapes[i]); if(gI_SilentSoundScape != -1) { //PrintToServer("Found: %s (%d)", sSilentSoundScapes[i], gI_SilentSoundScape); break; } } } } void HookSoundscapes(Handle hGameData) { Handle hFunction = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity); DHookSetFromConf(hFunction, hGameData, SDKConf_Signature, "CEnvSoundscape::UpdateForPlayer"); DHookAddParam(hFunction, HookParamType_ObjectPtr); if(!DHookEnableDetour(hFunction, false, DHook_UpdateForPlayer)) { SetFailState("Couldn't enable CEnvSoundscape::UpdateForPlayer detour."); } } /* struct ss_update_t { CBasePlayer *pPlayer; Offset: 0 | Size: 4 CEnvSoundscape *pCurrentSoundscape; Offset: 4 | Size: 4 Vector playerPosition; Offset: 8 | Size: 12 float currentDistance; Offset: 20 | Size: 4 int traceCount; Offset: 24 | Size: 4 bool bInRange; Offset: 28 | Size: 4 }; */ //void CEnvSoundscape::UpdateForPlayer( ss_update_t &update ) public MRESReturn DHook_UpdateForPlayer(int pThis, Handle hParams) { #if 0 if(gI_SilentSoundScape == -1) { return MRES_Ignored; } #endif int client = DHookGetParamObjectPtrVar(hParams, 1, 0, ObjectValueType_CBaseEntityPtr); MRESReturn ret = MRES_Ignored; if(gI_Settings[client] & Mute_Soundscapes) { SetEntProp(client, Prop_Data, "soundscapeIndex", gI_SilentSoundScape); if((gI_Settings[client] & Debug) && gI_LastSoundscape[client] != gI_SilentSoundScape && GetEntProp(client, Prop_Data, "soundscapeIndex") == gI_SilentSoundScape) { PrintToChat(client, "[Debug] Soundscape Blocked"); } ret = MRES_Supercede; } else { DHookSetParamObjectPtrVar(hParams, 1, gI_CurrentSoundscapeOffset, ObjectValueType_CBaseEntityPtr, 0); } gI_LastSoundscape[client] = GetEntProp(client, Prop_Data, "soundscapeIndex"); return ret; } //--------------------------------------------------------------- //------------------------TRIGGER OUTPUTS------------------------ void HookAcceptInput() { Handle hGameData = LoadGameConfigFile("sdktools.games"); int offset = GameConfGetOffset(hGameData, "AcceptInput"); delete hGameData; if(offset == -1) { SetFailState("Failed to load \"AcceptInput\", invalid offset."); } gH_AcceptInput = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, DHook_AcceptInput); DHookAddParam(gH_AcceptInput, HookParamType_CharPtr); DHookAddParam(gH_AcceptInput, HookParamType_CBaseEntity); DHookAddParam(gH_AcceptInput, HookParamType_CBaseEntity); DHookAddParam(gH_AcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); DHookAddParam(gH_AcceptInput, HookParamType_Int); } // virtual bool AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ); public MRESReturn DHook_AcceptInput(int pThis, Handle hReturn, Handle hParams) { if(DHookIsNullParam(hParams, 2)) { return MRES_Ignored; } int client = DHookGetParam(hParams, 2); if(gI_Settings[client] & Mute_TriggerSounds == 0) { return MRES_Ignored; } char sParameter[128]; DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, sParameter, 128); char sExplode[1][128]; ExplodeString(sParameter, " ", sExplode, sizeof(sExplode), sizeof(sExplode[])); if(strcmp(sExplode[0], "play") == 0 || strcmp(sExplode[0], "playgamesound") == 0 || strcmp(sExplode[0], "play_hrtf") == 0 || strcmp(sExplode[0], "snd_playsounds") == 0) { if(gI_Settings[client] & Debug) { PrintToChat(client, "[Debug] Output Blocked (%s)", sParameter); } DHookSetReturn(hReturn, false); return MRES_Supercede; } return MRES_Ignored; } //----------------------------------------------------- //----------------AMBIENT/NORMAL SOUNDS---------------- void HookSendSound(Handle hGameData) { Handle hFunction = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_Address); DHookSetFromConf(hFunction, hGameData, SDKConf_Signature, "CGameClient::SendSound"); DHookAddParam(hFunction, HookParamType_ObjectPtr); DHookAddParam(hFunction, HookParamType_Bool); if(!DHookEnableDetour(hFunction, false, DHook_SendSound)) { SetFailState("Couldn't enable CGameClient::SendSound detour."); } StartPrepSDKCall(SDKCall_Raw); PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "CBaseClient::GetPlayerSlot"); PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); gH_GetPlayerSlot = EndPrepSDKCall(); if(gH_GetPlayerSlot == null) { SetFailState("Could not initialize call to CBaseClient::GetPlayerSlot."); } } //CSS: /*struct SoundInfo_t { int nSequenceNumber; Offset: 0 | Size: 4 int nEntityIndex; Offset: 4 | Size: 4 int nChannel; Offset: 8 | Size: 4 const char *pszName; Offset: 12 | Size: 4 Vector vOrigin; Offset: 16 | Size: 12 Vector vDirection; Offset: 28 | Size: 12 float fVolume; Offset: 40 | Size: 4 soundlevel_t Soundlevel; Offset: 44 | Size: 4 bool bLooping; Offset: 48 | Size: 1 int nPitch; Offset: 52 | Size: 4 int nSpecialDSP; Offset: 56 | Size: 4 Vector vListenerOrigin; Offset: 60 | Size: 12 int nFlags; Offset: 72 | Size: 4 int nSoundNum; Offset: 76 | Size: 4 float fDelay; Offset: 80 | Size: 4 bool bIsSentence; Offset: 84 | Size: 1 bool bIsAmbient; Offset: 85 | Size: 1 int nSpeakerEntity; Offset: 88 | Size: 4 };*/ //CSGO: /*struct SoundInfo_t { Vector vOrigin; Offset: 0 | Size: 12 Vector vDirection Offset: 12 | Size: 12 Vector vListenerOrigin; Offset: 24 | Size: 12 const char *pszName; Offset: 36 | Size: 4 float fVolume; Offset: 40 | Size: 4 float fDelay; Offset: 44 | Size: 4 float fTickTime; Offset: 48 | Size: 4 int nSequenceNumber; Offset: 52 | Size: 4 int nEntityIndex; Offset: 56 | Size: 4 int nChannel; Offset: 60 | Size: 4 int nPitch; Offset: 64 | Size: 4 int nFlags; Offset: 68 | Size: 4 unsigned int nSoundNum; Offset: 72 | Size: 4 int nSpeakerEntity; Offset: 76 | Size: 4 int nRandomSeed; Offset: 80 | Size: 4 soundlevel_t Soundlevel; Offset: 84 | Size: 4 bool bIsSentence; Offset: 88 | Size: 1 bool bIsAmbient; Offset: 89 | Size: 1 bool bLooping; Offset: 90 | Size: 1 };*/ //void CGameClient::SendSound( SoundInfo_t &sound, bool isReliable ) public MRESReturn DHook_SendSound(Address pThis, Handle hParams) { if(DHookGetParamObjectPtrVar(hParams, 1, gI_VolumeOffset, ObjectValueType_Float) == 0.0) { return MRES_Ignored; } Address pIClient = pThis + view_as
(gI_CGameClientThing); int client = view_as(SDKCall(gH_GetPlayerSlot, pIClient)) + 1; if(!IsValidClient(client)) { return MRES_Ignored; } bool bIsAmbient = DHookGetParamObjectPtrVar(hParams, 1, gI_AmbientOffset, ObjectValueType_Bool); MRESReturn ret = MRES_Ignored; if(bIsAmbient) { if(gI_Settings[client] & Mute_AmbientSounds) { if(gI_Settings[client] & Debug) { PrintToChat(client, "[Debug] Ambient Blocked"); } ret = MRES_Supercede; } } else { if(gI_Settings[client] & Mute_NormalSounds) { if(gI_Settings[client] & Debug) { PrintToChat(client, "[Debug] Sound Blocked"); } ret = MRES_Supercede; } } return ret; } //----------------------------------------------------- //------------------------MENU------------------------- public Action Command_Sounds(int client, int args) { if(!IsValidClient(client)) { return Plugin_Handled; } Menu menu = new Menu(MenuHandler_Sounds); menu.SetTitle("Sound Manager\n "); char sDisplay[64]; char sInfo[16]; if(gEV_Type == Engine_CSGO) { FormatEx(sDisplay, 64, "Stop Active Sounds\n "); menu.AddItem("stopactive", sDisplay); } FormatEx(sDisplay, 64, "Soundscapes: [%s]", gI_Settings[client] & Mute_Soundscapes ? "Muted" : "On"); IntToString(Mute_Soundscapes, sInfo, 16); menu.AddItem(sInfo, sDisplay); FormatEx(sDisplay, 64, "Ambient Sounds: [%s]", gI_Settings[client] & Mute_AmbientSounds ? "Muted" : "On"); IntToString(Mute_AmbientSounds, sInfo, 16); menu.AddItem(sInfo, sDisplay); FormatEx(sDisplay, 64, "Normal Sounds: [%s]", gI_Settings[client] & Mute_NormalSounds ? "Muted" : "On"); IntToString(Mute_NormalSounds, sInfo, 16); menu.AddItem(sInfo, sDisplay); FormatEx(sDisplay, 64, "Trigger Sounds: [%s]\n ", gI_Settings[client] & Mute_TriggerSounds ? "Muted" : "On"); IntToString(Mute_TriggerSounds, sInfo, 16); menu.AddItem(sInfo, sDisplay); FormatEx(sDisplay, 64, "Gun Sounds: [%s]\n ", gI_Settings[client] & Mute_GunSounds ? "Muted" : "On"); IntToString(Mute_GunSounds, sInfo, 16); menu.AddItem(sInfo, sDisplay); if(CheckCommandAccess(client, "soundmanager_debug", ADMFLAG_RCON)) { FormatEx(sDisplay, 64, "Debug Prints: [%s]", gI_Settings[client] & Debug ? "Yes" : "No"); IntToString(Debug, sInfo, 16); menu.AddItem(sInfo, sDisplay); } menu.Display(client, MENU_TIME_FOREVER); return Plugin_Handled; } public int MenuHandler_Sounds(Menu menu, MenuAction action, int param1, int param2) { if(action == MenuAction_Select) { char sInfo[16]; menu.GetItem(param2, sInfo, 16); if(gEV_Type == Engine_CSGO) { if(StrEqual(sInfo, "stopactive")) { ClientCommand(param1, "playgamesound Music.StopAllExceptMusic"); ClientCommand(param1, "playgamesound Music.StopAllMusic"); Command_Sounds(param1, 0); return 0; } } int iOption = StringToInt(sInfo); gI_Settings[param1] ^= iOption; if(iOption == Mute_GunSounds) { CheckShotgunShotHook(); } else if(iOption == Mute_Soundscapes) { if(gI_Settings[param1] & Mute_Soundscapes) { ClientCommand(param1, "cl_soundscape_flush"); } } char sCookie[16]; IntToString(gI_Settings[param1], sCookie, 16); SetClientCookie(param1, gH_SettingsCookie, sCookie); Command_Sounds(param1, 0); } else if(action == MenuAction_End) { delete menu; } return 0; } //---------------------------------------------------- //-----------------------EVENTS----------------------- public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { gB_EntitiesFound = false; gA_LoopingAmbients.Clear(); int entity = INVALID_ENT_REFERENCE; while((entity = FindEntityByClassname(entity, "ambient_generic")) != INVALID_ENT_REFERENCE) { if(GetEntProp(entity, Prop_Data, "m_fLooping") == 1) { gA_LoopingAmbients.Push(EntIndexToEntRef(entity)); } } gB_EntitiesFound = true; } //---------------------------------------------------- // Credits to GoD-Tony for everything related to stopping gun sounds public Action Hook_ShotgunShot(const char[] te_name, const int[] Players, int numClients, float delay) { if(!gB_ShouldHookShotgunShot) { return Plugin_Continue; } // Check which clients need to be excluded. int newClients[MAXPLAYERS+1]; int count = 0; for(int i = 0; i < numClients; i++) { int iClient = Players[i]; // player not muting gun sounds if(gI_Settings[iClient] & Mute_GunSounds == 0) { newClients[count] = iClient; count++; } } // No clients were excluded. if(count == numClients) { return Plugin_Continue; } // All clients were excluded and there is no need to broadcast. else if(count == 0) { return Plugin_Stop; } // Re-broadcast to clients that still need it. float vTemp[3]; TE_Start("Shotgun Shot"); TE_ReadVector("m_vecOrigin", vTemp); TE_WriteVector("m_vecOrigin", vTemp); TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); TE_WriteNum(gS_WeaponID, TE_ReadNum(gS_WeaponID)); TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); TE_Send(newClients, count, delay); return Plugin_Stop; } void CheckShotgunShotHook() { bool bShouldHook = false; for(int i = 1; i <= MaxClients; i++) { if(!IsClientInGame(i)) { continue; } if(gI_Settings[i] & Mute_GunSounds) { bShouldHook = true; break; } } // Fake (un)hook because toggling actual hooks will cause server instability. gB_ShouldHookShotgunShot = bShouldHook; } bool IsValidClient(int client) { return (client >= 1 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsClientSourceTV(client)); }