#pragma semicolon 1 #include #include #include #include #include #include #pragma newdecls required #define MAX_MAPMUSIC_ENTITIES 2048 #define PLUGIN_VERSION "2.0.4" int MAX_ENTITIES = 0; bool g_bLateLoad = false; bool g_bStopWeaponSounds[MAXPLAYERS+1] = { false, ... }; bool g_bStopMapMusic[MAXPLAYERS+1] = { false, ... }; bool g_bStopWeaponSoundsHooked = false; bool g_bStopMapMusicHooked = false; int g_iMapMusicEntities[MAX_MAPMUSIC_ENTITIES]; int g_iNumSounds = 0; static char g_sKVPATH[PLATFORM_MAX_PATH]; KeyValues g_hWepSounds; Handle g_hCookieStopSound = null; Handle g_hCookieStopMapMusic = null; public Plugin myinfo = { name = "Toggle Game Sounds", author = "GoD-Tony, edit by Obus + BotoX, Oleg Tsvetkov", description = "Allows clients to stop hearing weapon sounds and map music", version = PLUGIN_VERSION, url = "http://www.sourcemod.net/" }; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { if (GetEngineVersion() != Engine_CSGO && GetEngineVersion() != Engine_CSS) { strcopy(error, err_max, "This plugin supports only CS:GO and CS:S!"); return APLRes_Failure; } g_bLateLoad = late; return APLRes_Success; } public void OnPluginStart() { // Load translations LoadTranslations("plugin.stopsound.phrases"); LoadTranslations("common.phrases"); // For On/Off buttons in Cookies Menu // Detect game and hook appropriate tempent. AddTempEntHook("Shotgun Shot", CSS_Hook_ShotgunShot); // Ambient sounds AddAmbientSoundHook(Hook_AmbientSound); // Map music will be caught here HookEvent("round_end", Event_RoundEnd); HookEvent("player_spawn", Event_PlayerSpawn); CreateConVar("sm_stopsound_version", PLUGIN_VERSION, "Toggle Weapon Sounds", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_REPLICATED); RegConsoleCmd("sm_stopsound", Command_StopSound, "Toggle hearing weapon sounds"); RegConsoleCmd("sm_stopmusic", Command_StopMusic, "Toggle hearing map music"); RegConsoleCmd("sm_music", Command_StopMusic, "Toggle hearing map music"); // Create KeyValues g_hWepSounds = new KeyValues("WeaponSounds"); BuildPath(Path_SM, g_sKVPATH, sizeof(g_sKVPATH), "data/playerprefs.WepSounds.txt"); g_hWepSounds.ImportFromFile(g_sKVPATH); // Cookies g_hCookieStopSound = RegClientCookie("weaponsound_blocked", "Are weapon sounds enabled", CookieAccess_Protected); g_hCookieStopMapMusic = RegClientCookie("mapmusic_blocked", "Are map music enabled", CookieAccess_Protected); SetCookieMenuItem(CookieMenuHandler_StopSounds, 0, "Stop sounds"); // Suppress reload sound effects UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); // Game-specific setup if (GetEngineVersion() == Engine_CSGO) { MAX_ENTITIES = 4096; // Weapon sounds will be caught here. AddNormalSoundHook(Hook_NormalSound_CSGO); if (ReloadEffect != INVALID_MESSAGE_ID) { HookUserMessage(ReloadEffect, Hook_ReloadEffect_CSGO, true); } } else { // CS:S MAX_ENTITIES = 2048; // Weapon sounds will be caught here. AddNormalSoundHook(Hook_NormalSound_CSS); if (ReloadEffect != INVALID_MESSAGE_ID) { HookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); } } // Late load if (g_bLateLoad) { int entity = -1; while ((entity = FindEntityByClassname(entity, "ambient_generic*")) != -1) { OnEntitySpawned(entity); } for (int client = 1; client <= MaxClients; client++) { if (IsClientInGame(client) && AreClientCookiesCached(client)) { OnClientCookiesCached(client); } } g_bLateLoad = false; } } public void OnPluginEnd() { for (int client = 1; client <= MaxClients; client++) { if (IsClientInGame(client)) { OnClientDisconnect_Post(client); } } // Remove tempent hook RemoveTempEntHook("Shotgun Shot", CSS_Hook_ShotgunShot); // Remove ambient sound hook RemoveAmbientSoundHook(Hook_AmbientSound); // Find ReloadEffect UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); // Remove game-specific staff if (GetEngineVersion() == Engine_CSGO) { RemoveNormalSoundHook(Hook_NormalSound_CSGO); if (ReloadEffect != INVALID_MESSAGE_ID) { UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSGO, true); } } else { RemoveNormalSoundHook(Hook_NormalSound_CSS); if (ReloadEffect != INVALID_MESSAGE_ID) { UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); } } // Delete KeyValues delete g_hWepSounds; } public void OnMapStart() { g_iNumSounds = 0; } public void OnEntityCreated(int entity, const char[] classname) { if (!StrEqual(classname, "ambient_generic", false)) { return; } SDKHook(entity, SDKHook_Spawn, OnEntitySpawned); } public void OnEntitySpawned(int entity) { if (entity < 0 || entity > MAX_ENTITIES || !IsValidEntity(entity)) { return; } if (g_iNumSounds >= MAX_MAPMUSIC_ENTITIES) { // Something went wrong... return; } char sSoundPath[PLATFORM_MAX_PATH]; GetEntPropString(entity, Prop_Data, "m_iszSound", sSoundPath, sizeof(sSoundPath)); int iLen = strlen(sSoundPath); if (iLen > 4 && (StrEqual(sSoundPath[iLen - 4], ".mp3", false) || StrEqual(sSoundPath[iLen - 4], ".wav", false))) { g_iMapMusicEntities[g_iNumSounds++] = EntIndexToEntRef(entity); } } public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { g_iNumSounds = 0; } public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); if (!IsClientInGame(client) || GetClientTeam(client) <= CS_TEAM_SPECTATOR) { return; } if (g_bStopWeaponSounds[client]) { CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); } if (g_bStopMapMusic[client]) { CPrintToChat(client, "%t %t", "Chat Prefix", "Map music disabled"); } } public Action Timer_DelayedStopForEntity(Handle timer, any data) { DataPack datapack = view_as(data); datapack.Reset(); char sSample[PLATFORM_MAX_PATH]; datapack.ReadString(sSample, sizeof(sSample)); int entity = datapack.ReadCell(); StopSoundFromEntity(sSample, entity); return Plugin_Stop; } public Action Command_StopSound(int client, int args) { if (client == 0) { ReplyToCommand(client, "[SM] Cannot use command from server console."); return Plugin_Handled; } g_bStopWeaponSounds[client] = !g_bStopWeaponSounds[client]; CheckWeaponSoundsHooks(); if (g_bStopWeaponSounds[client]) { SetClientCookie(client, g_hCookieStopSound, "1"); CReplyToCommand(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); } else { SetClientCookie(client, g_hCookieStopSound, "0"); CReplyToCommand(client, "%t %t", "Chat Prefix", "Weapon sounds enabled"); } return Plugin_Handled; } public Action Command_StopMusic(int client, int args) { if (client == 0) { ReplyToCommand(client, "[SM] Cannot use command from server console."); return Plugin_Handled; } g_bStopMapMusic[client] = !g_bStopMapMusic[client]; CheckMapMusicHooks(); if (g_bStopMapMusic[client]) { SetClientCookie(client, g_hCookieStopMapMusic, "1"); CReplyToCommand(client, "%t %t", "Chat Prefix", "Map music disabled"); StopMapMusic(); } else { SetClientCookie(client, g_hCookieStopMapMusic, "0"); CReplyToCommand(client, "%t %t", "Chat Prefix", "Map music enabled"); } return Plugin_Handled; } public void OnClientCookiesCached(int client) { char sBuffer[2]; int iValue = 0; // Weapon Sounds cookie GetClientCookie(client, g_hCookieStopSound, sBuffer, sizeof(sBuffer)); iValue = StringToInt(sBuffer); if (iValue == 1) { g_bStopWeaponSounds[client] = true; g_bStopWeaponSoundsHooked = true; } else { g_bStopWeaponSounds[client] = BackwardCapabilityCheck(client); if (iValue != 0) { SetClientCookie(client, g_hCookieStopSound, "0"); } } // Map Music cookie GetClientCookie(client, g_hCookieStopMapMusic, sBuffer, sizeof(sBuffer)); iValue = StringToInt(sBuffer); if (iValue == 1) { g_bStopMapMusic[client] = true; g_bStopMapMusicHooked = true; } else { g_bStopMapMusic[client] = false; if (iValue != 0) { SetClientCookie(client, g_hCookieStopMapMusic, "0"); } } } // Because we have some players, whose settings are saved to KV file. We want to save this data. bool BackwardCapabilityCheck(int client) { char sSteamId[32]; GetClientAuthId(client, AuthId_Steam2, sSteamId, sizeof(sSteamId)); g_hWepSounds.Rewind(); if (!g_hWepSounds.JumpToKey(sSteamId, false)) { return false; } int disabled = g_hWepSounds.GetNum("disabled", 0); g_hWepSounds.DeleteThis(); g_hWepSounds.Rewind(); return (disabled == 1); } public void OnClientDisconnect_Post(int client) { g_bStopWeaponSounds[client] = false; g_bStopMapMusic[client] = false; CheckWeaponSoundsHooks(); CheckMapMusicHooks(); } void CheckWeaponSoundsHooks() { bool bShouldHook = false; for (int i = 1; i <= MaxClients; i++) { if (g_bStopWeaponSounds[i]) { bShouldHook = true; break; } } // Fake (un)hook because toggling actual hooks will cause server instability. g_bStopWeaponSoundsHooked = bShouldHook; } void CheckMapMusicHooks() { bool bShouldHook = false; for (int i = 1; i <= MaxClients; i++) { if (g_bStopMapMusic[i]) { bShouldHook = true; break; } } // Fake (un)hook because toggling actual hooks will cause server instability. g_bStopMapMusicHooked = bShouldHook; } void StopMapMusic() { char sSound[PLATFORM_MAX_PATH]; int entity = INVALID_ENT_REFERENCE; for (int i = 0; i < g_iNumSounds; i++) { entity = EntRefToEntIndex(g_iMapMusicEntities[i]); if (entity != INVALID_ENT_REFERENCE) { GetEntPropString(entity, Prop_Data, "m_iszSound", sSound, sizeof(sSound)); StopSoundFromEntity(sSound, entity); } } } void StopSoundFromEntity(const char[] sSample, int entity) { for (int i = 1; i <= MaxClients; i++) { if (g_bStopMapMusic[i] && IsClientInGame(i)) { Client_StopSound(i, entity, SNDCHAN_STATIC, sSample); } } } bool IsEntityInMapMusicEntities(int entity) { for (int i = 0; i < g_iNumSounds; i++) { if (entity == EntRefToEntIndex(g_iMapMusicEntities[i])) { return true; } } return false; } // I guess this is from SMLib void Client_StopSound(int client, int entity, int channel, const char[] name) { EmitSoundToClient(client, name, entity, channel, SNDLEVEL_NONE, SND_STOP, 0.0, SNDPITCH_NORMAL, _, _, _, true); } public void CookieMenuHandler_StopSounds(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) { if (action == CookieMenuAction_DisplayOption) { Format(buffer, maxlen, "%T", "Cookie Menu Stop Sounds", client); } else if (action == CookieMenuAction_SelectOption) { ShowStopSoundsSettingsMenu(client); } } void ShowStopSoundsSettingsMenu(int client) { Menu menu = new Menu(MenuHandler_StopSoundsSettings); menu.SetTitle("%T", "Cookie Menu Stop Sounds Title", client); char sBuffer[128]; Format(sBuffer, sizeof(sBuffer), "%T%T", "Weapon Sounds", client, g_bStopWeaponSounds[client] ? "Disabled" : "Enabled", client); menu.AddItem("0", sBuffer); Format(sBuffer, sizeof(sBuffer), "%T%T", "Map Sounds", client, g_bStopMapMusic[client] ? "Disabled" : "Enabled", client); menu.AddItem("1", sBuffer); menu.ExitBackButton = true; menu.Display(client, MENU_TIME_FOREVER); } public int MenuHandler_StopSoundsSettings(Menu menu, MenuAction action, int client, int selection) { if (action == MenuAction_Cancel) { ShowCookieMenu(client); } else if (action == MenuAction_Select) { if (selection == 0) { g_bStopWeaponSounds[client] = !g_bStopWeaponSounds[client]; CheckWeaponSoundsHooks(); if (g_bStopWeaponSounds[client]) { SetClientCookie(client, g_hCookieStopSound, "1"); CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); } else { SetClientCookie(client, g_hCookieStopSound, "0"); CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds enabled"); } } else if (selection == 1) { g_bStopMapMusic[client] = !g_bStopMapMusic[client]; CheckMapMusicHooks(); if (g_bStopMapMusic[client]) { SetClientCookie(client, g_hCookieStopMapMusic, "1"); CPrintToChat(client, "%t %t", "Chat Prefix", "Map music disabled"); StopMapMusic(); } else { SetClientCookie(client, g_hCookieStopMapMusic, "0"); CPrintToChat(client, "%t %t", "Chat Prefix", "Map music enabled"); } } ShowStopSoundsSettingsMenu(client); } else if (action == MenuAction_End) { delete menu; } } public Action Hook_NormalSound_CSS(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) { // Ignore non-weapon sounds. if (!g_bStopWeaponSoundsHooked || channel != SNDCHAN_WEAPON) { return Plugin_Continue; } for (int i = 0; i < numClients; i++) { int client = clients[i]; if (g_bStopWeaponSounds[client]) { // Remove the client from the array. for (int j = i; j < numClients - 1; j++) { clients[j] = clients[j + 1]; } numClients--; i--; } } return (numClients > 0) ? Plugin_Changed : Plugin_Stop; } public Action Hook_NormalSound_CSGO(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) { // Ignore non-weapon sounds. if (!g_bStopWeaponSoundsHooked || (channel != SNDCHAN_WEAPON && !(channel == SNDCHAN_AUTO && strncmp(sample, "physics/flesh", 13) == 0) && !(channel == SNDCHAN_STATIC && StrContains(sample, "player/headshot", true) != -1))) { return Plugin_Continue; } for (int i = 0; i < numClients; i++) { int client = clients[i]; if (g_bStopWeaponSounds[client] || !IsClientConnected(client)) { // Remove the client from the array. for (int j = i; j < numClients - 1; j++) { clients[j] = clients[j + 1]; } numClients--; i--; } } return (numClients > 0) ? Plugin_Changed : Plugin_Stop; } public Action CSS_Hook_ShotgunShot(const char[] te_name, const int[] Players, int numClients, float delay) { if (!g_bStopWeaponSoundsHooked) { return Plugin_Continue; } // Check which clients need to be excluded. int[] newClients = new int[numClients]; int newTotal = 0; for (int i = 0; i < numClients; i++) { if (!g_bStopWeaponSounds[Players[i]]) { newClients[newTotal++] = Players[i]; } } // No clients were excluded. if (newTotal == numClients) { return Plugin_Continue; } else if (newTotal == 0) { // All clients were excluded and there is no need to broadcast. 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("m_iWeaponID", TE_ReadNum("m_iWeaponID")); 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, newTotal, delay); return Plugin_Stop; } public Action Hook_ReloadEffect_CSS(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init) { if (!g_bStopWeaponSoundsHooked) { return Plugin_Continue; } int client = msg.ReadShort(); // Check which clients need to be excluded. int[] newClients = new int[playersNum]; int newTotal = 0; for (int i = 0; i < playersNum; i++) { int client_ = players[i]; if (IsClientInGame(client_) && !g_bStopWeaponSounds[client_]) { newClients[newTotal++] = client_; } } if (newTotal == playersNum) { // No clients were excluded. return Plugin_Continue; } else if (newTotal == 0) { // All clients were excluded and there is no need to broadcast. return Plugin_Handled; } DataPack pack = new DataPack(); pack.WriteCell(client); pack.WriteCell(newTotal); for (int i = 0; i < newTotal; i++) { pack.WriteCell(newClients[i]); } RequestFrame(OnReloadEffect, pack); return Plugin_Handled; } public Action Hook_ReloadEffect_CSGO(UserMsg msg_id, Protobuf msg, const int[] players, int playersNum, bool reliable, bool init) { if (!g_bStopWeaponSoundsHooked) { return Plugin_Continue; } int client = PbReadInt(msg, "entidx"); // Check which clients need to be excluded. int[] newClients = new int[playersNum]; int newTotal = 0; for (int i = 0; i < playersNum; i++) { int client_ = players[i]; if (IsClientInGame(client_) && !g_bStopWeaponSounds[client_]) { newClients[newTotal++] = client_; } } if (newTotal == playersNum) { // No clients were excluded. return Plugin_Continue; } else if (newTotal == 0) { // All clients were excluded and there is no need to broadcast. return Plugin_Handled; } DataPack pack = new DataPack(); pack.WriteCell(client); pack.WriteCell(newTotal); for (int i = 0; i < newTotal; i++) { pack.WriteCell(newClients[i]); } RequestFrame(OnReloadEffect, pack); return Plugin_Handled; } public void OnReloadEffect(DataPack pack) { pack.Reset(); int client = pack.ReadCell(); int newTotal = pack.ReadCell(); int[] players = new int[newTotal]; int playersNum = 0; for (int i = 0; i < newTotal; i++) { int client_ = pack.ReadCell(); if (IsClientInGame(client_)) { players[playersNum++] = client_; } } CloseHandle(pack); Handle ReloadEffect = StartMessage("ReloadEffect", players, playersNum, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { PbSetInt(ReloadEffect, "entidx", client); } else { BfWriteShort(ReloadEffect, client); } EndMessage(); } public Action Hook_AmbientSound(char sample[PLATFORM_MAX_PATH], int &entity, float &volume, int &level, int &pitch, float pos[3], int &flags, float &delay) { if (g_bStopMapMusicHooked && IsEntityInMapMusicEntities(entity)) { DataPack datapack; CreateDataTimer(0.0, Timer_DelayedStopForEntity, datapack, TIMER_FLAG_NO_MAPCHANGE); datapack.WriteString(sample); datapack.WriteCell(entity); } return Plugin_Continue; }