diff --git a/README.md b/README.md index da7ae14..8a07e58 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,14 @@ # MusicName -This plugin prints the names of music played in chat based on a config file. - -> [!NOTE] -> The [`clientpref-ver`](https://github.com/notkoen/sm-plugin-MusicName/tree/clientpref-ver) branch contains the code for ClientPrefs support that saves player settings for automatically displaying song names. +This plugin prints the names of music played on maps to chat based on a config file. ## Config Formatting The plugin reads configs in the format below under `/sourcemod/configs/musicname/.cfg`. For sample configs, you can refer to my other [Music-Names](https://github.com/notkoen/music-names) repository. > [!CAUTION] -> File extensions must be **included** *(such as `.mp3`)* for music to be detected and displayed +> File extensions must be **included** *(such as `.mp3`)* for music to be detected > [!CAUTION] > File names must be **lowercase** diff --git a/addons/sourcemod/scripting/MusicName.sp b/addons/sourcemod/scripting/MusicName.sp index 7dcb918..b318a43 100644 --- a/addons/sourcemod/scripting/MusicName.sp +++ b/addons/sourcemod/scripting/MusicName.sp @@ -1,41 +1,42 @@ #pragma newdecls required #pragma semicolon 1 +#include #include -#include -#include #include -#include -public Plugin myinfo = { +public Plugin myinfo = +{ name = "Music Names", author = "koen, .Rushaway", - description = "Displays the name of the current song in chat", - version = "1.2-nc", - url = "https://github.com/notkoen" + description = "Display the name of music in chat", + version = "1.3", }; char g_sCurrentSong[256]; -char g_sCurrentMap[PLATFORM_MAX_PATH]; bool g_bConfigLoaded = false; bool g_bDisplay[MAXPLAYERS+1] = {true, ...}; +bool g_bLate = false; + +Cookie g_cDisplayStyle; StringMap g_songNames; StringMap g_fLastPlayedTime; -float g_fCooldownTime = 30.0; -ConVar g_cvCooldownTime; +ConVar g_hCVar_CooldownTime; +ConVar g_hCVar_Message; -public void OnPluginStart() { - RegConsoleCmd("sm_np", Command_NowPlaying, "Display the name of the current song"); - RegConsoleCmd("sm_nowplaying", Command_NowPlaying, "Display the name of the current song"); - RegConsoleCmd("sm_songname", Command_NowPlaying, "Display the name of the current song"); +public void OnPluginStart() +{ + RegConsoleCmd("sm_np", Command_NowPlaying, "Display the name of the current song"); + RegConsoleCmd("sm_nowplaying", Command_NowPlaying, "Display the name of the current song"); + RegConsoleCmd("sm_songname", Command_NowPlaying, "Display the name of the current song"); - RegConsoleCmd("sm_togglenp", Command_ToggleNP, "Toggle music name display in chat"); - RegConsoleCmd("sm_togglenowplaying", Command_ToggleNP, "Toggle music name display in chat"); + RegConsoleCmd("sm_togglenp", Command_ToggleNP, "Toggle music name display in chat"); + RegConsoleCmd("sm_togglenowplaying", Command_ToggleNP, "Toggle music name display in chat"); - RegConsoleCmd("sm_mn_dump", Command_DumpMusicnames, "Print all song names to console"); + RegConsoleCmd("sm_mn_dump", Command_DumpMusic, "Print all song names to console"); RegAdminCmd("sm_mn_reload", Command_ReloadMusicnames, ADMFLAG_CONFIG, "Reloads music name config"); @@ -45,66 +46,137 @@ public void OnPluginStart() { LoadTranslations("MusicName.phrases"); - g_cvCooldownTime = CreateConVar("sm_musicname_cooldown", "5.0", "Cooldown in seconds before the same song can be announced again", _, true, 1.0); - g_cvCooldownTime.AddChangeHook(OnCooldownChanged); - g_fCooldownTime = g_cvCooldownTime.FloatValue; - + g_hCVar_CooldownTime = CreateConVar("sm_musicname_cooldown", "5.0", "Cooldown in seconds before the same song can be announced again", _, true, 1.0); + g_hCVar_Message = CreateConVar("sm_musicname_message", "1", "Whether to display config message at round start", _, true, 0.0, true, 1.0); AutoExecConfig(true); g_songNames = new StringMap(); g_fLastPlayedTime = new StringMap(); - GetCurrentMap(g_sCurrentMap, PLATFORM_MAX_PATH); LoadConfig(); + + g_cDisplayStyle = new Cookie("display_musicnames", "Display music names cookie", CookieAccess_Private); + SetCookieMenuItem(CookiesMenu, 0, "Music Names"); + + if (!g_bLate) + return; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || IsFakeClient(client)) + continue; + + if (!AreClientCookiesCached(client)) + { + g_bDisplay[client] = true; + continue; + } + + OnClientCookiesCached(client); + } } -public void OnCooldownChanged(ConVar convar, const char[] oldValue, const char[] newValue) { - g_fCooldownTime = g_cvCooldownTime.FloatValue; +//---------------------------------------------------------------------------------------------------- +// Purpose: Late load check +//---------------------------------------------------------------------------------------------------- +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_bLate = late; + return APLRes_Success; } -public void OnPluginEnd() { +//---------------------------------------------------------------------------------------------------- +// Purpose: SourceMod event hooks +//---------------------------------------------------------------------------------------------------- +public void OnPluginEnd() +{ delete g_songNames; delete g_fLastPlayedTime; RemoveAmbientSoundHook(Hook_AmbientSound); } -public void OnMapStart() { - GetCurrentMap(g_sCurrentMap, sizeof(g_sCurrentMap)); +public void OnMapStart() +{ LoadConfig(); - g_fLastPlayedTime.Clear(); + + if (g_songNames != null) + delete g_songNames; + + if (g_fLastPlayedTime != null) + delete g_fLastPlayedTime; + + g_songNames = new StringMap(); + g_fLastPlayedTime = new StringMap(); } -public void OnMapEnd() { +public void OnMapEnd() +{ delete g_songNames; delete g_fLastPlayedTime; - g_songNames = new StringMap(); - g_fLastPlayedTime = new StringMap(); } -public void OnClientDisconnected(int client) { +public void OnClientDisconnected(int client) +{ g_bDisplay[client] = true; } -public void OnRoundStart(Handle event, const char[] name, bool dontBroadcast) { +//---------------------------------------------------------------------------------------------------- +// Purpose: Cookie functions +//---------------------------------------------------------------------------------------------------- +public void OnClientCookiesCached(int client) +{ + char buffer[2]; + g_cDisplayStyle.Get(client, buffer, sizeof(buffer)); + + if (buffer[0] == '\0') + g_cDisplayStyle.Set(client, "1"); + else + g_bDisplay[client] = view_as(StringToInt(buffer)); +} + +public void CookiesMenu(int client, CookieMenuAction actions, any info, char[] buffer, int maxlen) +{ + if (actions == CookieMenuAction_DisplayOption) + FormatEx(buffer, maxlen, "Display Music Names: %s", g_bDisplay[client] ? "On" : "Off"); + + if (actions == CookieMenuAction_SelectOption) { + ToggleFeature(client); + ShowCookieMenu(client); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Round start event hook +//---------------------------------------------------------------------------------------------------- +public void OnRoundStart(Handle event, const char[] name, bool dontBroadcast) +{ g_sCurrentSong = ""; - g_fLastPlayedTime.Clear(); + delete g_fLastPlayedTime; + g_fLastPlayedTime = new StringMap(); // Print map supported message - if (g_bConfigLoaded) + if (g_bConfigLoaded && g_hCVar_Message.BoolValue) CreateTimer(5.0, Timer_OnRoundStartPost, _, TIMER_FLAG_NO_MAPCHANGE); } -public Action Timer_OnRoundStartPost(Handle timer) { +public Action Timer_OnRoundStartPost(Handle timer) +{ CPrintToChatAll("%t %t", "Chat Prefix", "Map Supported"); return Plugin_Stop; } -public Action Command_NowPlaying(int client, int args) { - if (!g_bConfigLoaded) { +//---------------------------------------------------------------------------------------------------- +// Purpose: Now playing command callback +//---------------------------------------------------------------------------------------------------- +public Action Command_NowPlaying(int client, int args) +{ + if (!g_bConfigLoaded) + { CPrintToChat(client, "%t %t", "Chat Prefix", "No Config"); return Plugin_Handled; } - if (g_sCurrentSong[0] == '\0') { + if (g_sCurrentSong[0] == '\0') + { CPrintToChat(client, "%t %t", "Chat Prefix", "No Name"); return Plugin_Handled; } @@ -113,19 +185,22 @@ public Action Command_NowPlaying(int client, int args) { return Plugin_Handled; } -public Action Command_ToggleNP(int client, int args) { - g_bDisplay[client] = !g_bDisplay[client]; - - if (g_bDisplay[client]) - CPrintToChat(client, "%t %t", "Chat Prefix", "Display Status", "Enabled"); - else - CPrintToChat(client, "%t %t", "Chat Prefix", "Display Status", "Disabled"); - +//---------------------------------------------------------------------------------------------------- +// Purpose: Toggle auto display music command callback +//---------------------------------------------------------------------------------------------------- +public Action Command_ToggleNP(int client, int args) +{ + ToggleFeature(client); return Plugin_Handled; } -public Action Command_DumpMusicnames(int client, int args) { - if (!g_bConfigLoaded) { +//---------------------------------------------------------------------------------------------------- +// Purpose: Dump music name command callback +//---------------------------------------------------------------------------------------------------- +public Action Command_DumpMusic(int client, int args) +{ + if (!g_bConfigLoaded) + { CPrintToChat(client, "%t %t", "Chat Prefix", "No Config"); return Plugin_Handled; } @@ -136,7 +211,9 @@ public Action Command_DumpMusicnames(int client, int args) { StringMapSnapshot snap = g_songNames.Snapshot(); int len = snap.Length; char szBuffer[256], szSongName[256]; - for (int i = 0; i < len; i++) { + + for (int i = 0; i < len; i++) + { snap.GetKey(i, szBuffer, sizeof(szBuffer)); g_songNames.GetString(szBuffer, szSongName, sizeof(szSongName)); PrintToConsole(client, "%i. %s", i + 1, szSongName); @@ -148,32 +225,106 @@ public Action Command_DumpMusicnames(int client, int args) { return Plugin_Handled; } -public Action Command_ReloadMusicnames(int client, int args) { +//---------------------------------------------------------------------------------------------------- +// Purpose: Reload config command callback +//---------------------------------------------------------------------------------------------------- +public Action Command_ReloadMusicnames(int client, int args) +{ LoadConfig(); - CPrintToChat(client, "%t %t", "Chat Prefix", "Reload Config"); + + if (g_bConfigLoaded) + CPrintToChat(client, "%t %t", "Chat Prefix", "Reload Config"); + else + CPrintToChat(client, "%t %t", "Chat Prefix", "Reload Failed"); + return Plugin_Handled; } -public void LoadConfig() { +//---------------------------------------------------------------------------------------------------- +// Purpose: Sound hook callback +//---------------------------------------------------------------------------------------------------- +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_bConfigLoaded) + return Plugin_Continue; + + // Mappers might use "volume 0" to stop music, which triggers sound hook + // So check if this is the input + if (volume == 0.0) + return Plugin_Continue; + + char sFileName[PLATFORM_MAX_PATH]; + GetFileFromPath(sample, sFileName, sizeof(sFileName)); + StringToLowerCase(sFileName); + + char sBuffer[PLATFORM_MAX_PATH]; + if (g_songNames.GetString(sFileName, sBuffer, sizeof(sBuffer))) + { + // Mappers might also use "volume" input to fade music out, which also triggers sound hook + // So check if detected song is same as current song + if (strcmp(sBuffer, g_sCurrentSong, false) == 0) + return Plugin_Continue; + + float currentTime = GetGameTime(); + float lastPlayed; + + // Check if the song was played recently + if (g_fLastPlayedTime.GetValue(sFileName, lastPlayed) && (currentTime - lastPlayed) < g_hCVar_CooldownTime.FloatValue) + return Plugin_Continue; + + // Update the timestamp + g_fLastPlayedTime.SetValue(sFileName, currentTime); + g_sCurrentSong = sBuffer; + + // Announce the song to players + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || IsFakeClient(client) || !g_bDisplay[client]) + continue; + + if (g_sCurrentSong[0] != '\0') + CPrintToChat(client, "%t %t", "Chat Prefix", "Now Playing", g_sCurrentSong); + } + } + + return Plugin_Continue; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Loads song config +//---------------------------------------------------------------------------------------------------- +stock void LoadConfig() +{ g_bConfigLoaded = false; - g_songNames.Clear(); - char g_sConfig[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, g_sConfig, sizeof(g_sConfig), "configs/musicname/%s.cfg", g_sCurrentMap); + delete g_fLastPlayedTime; + delete g_songNames; + g_fLastPlayedTime = new StringMap(); + g_songNames = new StringMap(); + + char g_sCurrentMap[PLATFORM_MAX_PATH]; + GetCurrentMap(g_sCurrentMap, sizeof(g_sCurrentMap)); + StringToLowerCase(g_sCurrentMap); + + char g_sConfigPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, g_sConfigPath, sizeof(g_sConfigPath), "configs/musicname/%s.cfg", g_sCurrentMap); KeyValues kv = new KeyValues("music"); - if (!kv.ImportFromFile(g_sConfig)) { + if (!kv.ImportFromFile(g_sConfigPath)) + { delete kv; return; } - if (!kv.GotoFirstSubKey(false)) { + if (!kv.GotoFirstSubKey(false)) + { delete kv; LogError("[MusicNames] Invalid config formatting for %s", g_sCurrentMap); return; } - do { + do + { char sKey[128]; char sValue[128]; @@ -189,7 +340,11 @@ public void LoadConfig() { return; } -void GetFileFromPath(const char[] path, char[] buffer, int maxlen) { +//---------------------------------------------------------------------------------------------------- +// Purpose: Get file name from file path +//---------------------------------------------------------------------------------------------------- +stock void GetFileFromPath(const char[] path, char[] buffer, int maxlen) +{ char normalizedPath[PLATFORM_MAX_PATH]; strcopy(normalizedPath, sizeof(normalizedPath), path); ReplaceString(normalizedPath, sizeof(normalizedPath), "\\", "/"); @@ -201,50 +356,30 @@ void GetFileFromPath(const char[] path, char[] buffer, int maxlen) { strcopy(buffer, maxlen, normalizedPath); } -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_bConfigLoaded) - return Plugin_Continue; - - // Mappers might use "volume 0" to stop music from playing - // This triggers ambient sound hook - // So let's check if this is the input first - if (volume == 0.0) - return Plugin_Continue; - - char sFileName[PLATFORM_MAX_PATH]; - GetFileFromPath(sample, sFileName, sizeof(sFileName)); - StringToLowerCase(sFileName); - - char sBuffer[PLATFORM_MAX_PATH]; - if (g_songNames.GetString(sFileName, sBuffer, sizeof(sBuffer))) { - // Mappers might also use "volume" input to fade music out - // This will trigger the sound hook as well - // So let's check if detected song is same as current song - if (strcmp(sBuffer, g_sCurrentSong, false) == 0) - return Plugin_Continue; - - float currentTime = GetGameTime(); - float lastPlayed; - - // Check if the song was played recently - if (g_fLastPlayedTime.GetValue(sFileName, lastPlayed) && (currentTime - lastPlayed) < g_fCooldownTime) - return Plugin_Continue; - - // Update the timestamp - g_fLastPlayedTime.SetValue(sFileName, currentTime); - g_sCurrentSong = sBuffer; - - // Announce the song to players - for (int client = 1; client <= MaxClients; client++) { - if (!IsClientInGame(client) || IsFakeClient(client)) - continue; - - if (!g_bDisplay[client]) - continue; - - CPrintToChat(client, "%t %t", "Chat Prefix", "Now Playing", g_sCurrentSong); - } +//---------------------------------------------------------------------------------------------------- +// Purpose: Convert string to lowercase +//---------------------------------------------------------------------------------------------------- +stock void StringToLowerCase(char[] input) +{ + int i = 0, x; + while ((x = input[i]) != '\0') + { + if ('A' <= x <= 'Z') + input[i] += ('a' - 'A'); + i++; } +} - return Plugin_Continue; +//---------------------------------------------------------------------------------------------------- +// Purpose: Toggle feature +//---------------------------------------------------------------------------------------------------- +stock void ToggleFeature(int client) +{ + g_bDisplay[client] = !g_bDisplay[client]; + g_cDisplayStyle.Set(client, g_bDisplay[client] ? "1" : "0"); + + if (g_bDisplay[client]) + CPrintToChat(client, "%t %t", "Chat Prefix", "Display Status", "Enabled"); + else + CPrintToChat(client, "%t %t", "Chat Prefix", "Display Status", "Disabled"); } \ No newline at end of file diff --git a/addons/sourcemod/translations/MusicName.phrases.txt b/addons/sourcemod/translations/MusicName.phrases.txt index 681b4b1..e70ecef 100644 --- a/addons/sourcemod/translations/MusicName.phrases.txt +++ b/addons/sourcemod/translations/MusicName.phrases.txt @@ -58,6 +58,14 @@ "fr" "Configuration des noms de musique rechargée." } + "Reload Failed" + { + "en" "{fullred}Failed {white}to reload music names config." + "zho" "重新載入歌名配置{fullred}出了問題{white}。" + "chi" "重新载入歌名配置{fullred}出了问题{white}。" + "fr" "{fullred}Échec {white}du rechargement de la configuration des noms de musique." + } + "Map Supported" { "en" "The current map has a music name config. Use \"{green}!np{white}\" to see the name of the current playing song." diff --git a/sourceknight.yaml b/sourceknight.yaml index a1fb22d..1d2b60f 100644 --- a/sourceknight.yaml +++ b/sourceknight.yaml @@ -17,13 +17,6 @@ project: - source: /addons dest: /addons - - name: utilshelper - type: git - repo: https://github.com/srcdslab/sm-plugin-UtilsHelper - unpack: - - source: /addons/sourcemod/scripting/include - dest: /addons/sourcemod/scripting/include - root: / output: /addons/sourcemod/plugins targets: