From 10a95cfdce5ea52f8ef991785053eb1c4da1d9c7 Mon Sep 17 00:00:00 2001 From: Ross Bemrose Date: Mon, 14 Sep 2015 11:21:26 -0400 Subject: [PATCH] Add new function: GetMapDisplayName. This function will resolve the name of a map using FindMap, then (if applicable), will turn a workshop map name into a nicely formatted name. Currently only TF2 and CS:GO Map Workshops are supported. More can be added at a later date. This function returns false if a map was not found, but true in any other instance even if FindMap could not resolve the map name. This patch also updates the following core plugins to use this GetMapDisplayName: BaseTriggers BaseVotes MapChooser NextMap Nominations RandomCycle RockTheVote --- core/HalfLife2.cpp | 32 +++++++++++++ core/HalfLife2.h | 1 + core/smn_halflife.cpp | 9 ++++ plugins/basetriggers.sp | 4 +- plugins/basevotes.sp | 7 ++- plugins/basevotes/votemap.sp | 10 ++-- plugins/include/halflife.inc | 18 +++++++ plugins/mapchooser.sp | 48 ++++++++++++++----- plugins/nextmap.sp | 3 +- plugins/nominations.sp | 73 ++++++++++++++++++++--------- plugins/randomcycle.sp | 13 +++-- plugins/rockthevote.sp | 2 + plugins/testsuite/mapdisplayname.sp | 25 ++++++++++ 13 files changed, 195 insertions(+), 50 deletions(-) create mode 100644 plugins/testsuite/mapdisplayname.sp diff --git a/core/HalfLife2.cpp b/core/HalfLife2.cpp index 39a51bd2..15b7924a 100644 --- a/core/HalfLife2.cpp +++ b/core/HalfLife2.cpp @@ -1273,6 +1273,38 @@ SMFindMapResult CHalfLife2::FindMap(const char *pMapName, char *pFoundMap, size_ #endif } +bool CHalfLife2::GetMapDisplayName(const char *pMapName, char *pDisplayname, size_t nMapNameMax) +{ + SMFindMapResult result = FindMap(pMapName, pDisplayname, nMapNameMax); + + if (result == SMFindMapResult::NotFound) + { + return false; + } + +#if SOURCE_ENGINE == SE_CSGO + char *lastSlashPos; + // In CSGO, workshop maps show up as workshop/123456789/mapname + if (strncmp(pDisplayname, "workshop/", 9) == 0 && (lastSlashPos = strrchr(pDisplayname, '/')) != NULL) + { + ke::SafeSprintf(pDisplayname, nMapNameMax, "%s", &lastSlashPos[1]); + return true; + } +#elif SOURCE_ENGINE == SE_TF2 + char *ugcPos; + // In TF2, workshop maps show up as workshop/mapname.ugc123456789 + if (strncmp(pDisplayname, "workshop/", 9) == 0 && (ugcPos = strstr(pDisplayname, ".ugc")) != NULL) + { + // Overwrite the . with a nul and SafeSprintf will handle the rest + ugcPos[0] = '\0'; + ke::SafeSprintf(pDisplayname, nMapNameMax, "%s", &pDisplayname[9]); + return true; + } +#endif + + return true; +} + bool CHalfLife2::IsMapValid(const char *map) { if (!map || !map[0]) diff --git a/core/HalfLife2.h b/core/HalfLife2.h index 96432723..83908d1a 100644 --- a/core/HalfLife2.h +++ b/core/HalfLife2.h @@ -187,6 +187,7 @@ public: //IGameHelpers bool IsMapValid(const char *map); SMFindMapResult FindMap(char *pMapName, size_t nMapNameMax); SMFindMapResult FindMap(const char *pMapName, char *pFoundMap = NULL, size_t nMapNameMax = 0); + bool GetMapDisplayName(const char *pMapName, char *pDisplayname, size_t nMapNameMax); #if SOURCE_ENGINE >= SE_ORANGEBOX string_t AllocPooledString(const char *pszValue); #endif diff --git a/core/smn_halflife.cpp b/core/smn_halflife.cpp index 4a4abc5e..156a8ce7 100644 --- a/core/smn_halflife.cpp +++ b/core/smn_halflife.cpp @@ -82,6 +82,14 @@ static cell_t FindMap(IPluginContext *pContext, const cell_t *params) return static_cast(g_HL2.FindMap(pMapname, pDestMap, params[3])); } +static cell_t GetMapDisplayName(IPluginContext *pContext, const cell_t *params) +{ + char *pMapname, *pDisplayname; + pContext->LocalToString(params[1], &pMapname); + pContext->LocalToString(params[2], &pDisplayname); + return static_cast(g_HL2.GetMapDisplayName(pMapname, pDisplayname, params[3])); +} + static cell_t IsDedicatedServer(IPluginContext *pContext, const cell_t *params) { return engine->IsDedicatedServer(); @@ -642,6 +650,7 @@ REGISTER_NATIVES(halflifeNatives) {"IsDedicatedServer", IsDedicatedServer}, {"IsMapValid", IsMapValid}, {"FindMap", FindMap}, + {"GetMapDisplayName", GetMapDisplayName}, {"SetFakeClientConVar", SetFakeClientConVar}, {"SetRandomSeed", SetRandomSeed}, {"PrecacheModel", PrecacheModel}, diff --git a/plugins/basetriggers.sp b/plugins/basetriggers.sp index 0c8eb595..0366e4c1 100644 --- a/plugins/basetriggers.sp +++ b/plugins/basetriggers.sp @@ -211,6 +211,7 @@ public Action:Command_Nextmap(client, args) } else { + GetMapDisplayName(map, map, sizeof(map)); ReplyToCommand(client, "[SM] %t", "Next Map", map); } @@ -291,7 +292,8 @@ public OnClientSayCommand_Post(client, const String:command[], const String:sArg { char map[PLATFORM_MAX_PATH]; GetNextMap(map, sizeof(map)); - + GetMapDisplayName(map, map, sizeof(map)); + if (g_Cvar_TriggerShow.IntValue) { if (mapchooser && EndOfMapVoteEnabled() && !HasEndOfMapVoteFinished()) diff --git a/plugins/basevotes.sp b/plugins/basevotes.sp index ac04b6c8..54d58467 100644 --- a/plugins/basevotes.sp +++ b/plugins/basevotes.sp @@ -116,7 +116,7 @@ public OnPluginStart() OnAdminMenuReady(topmenu); } - g_SelectedMaps = CreateArray(33); + g_SelectedMaps = CreateArray(ByteCountToCells(PLATFORM_MAX_PATH)); g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem|MenuAction_Display); g_MapList.SetTitle("%T", "Please select a map", LANG_SERVER); @@ -311,8 +311,11 @@ public Handler_VoteCallback(Menu menu, MenuAction action, param1, param2) case (voteType:map): { + // single-vote items don't use the display item + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(item, displayName, sizeof(displayName)); LogAction(-1, -1, "Changing map to %s due to vote.", item); - PrintToChatAll("[SM] %t", "Changing map", item); + PrintToChatAll("[SM] %t", "Changing map", displayName); new Handle:dp; CreateDataTimer(5.0, Timer_ChangeMap, dp); WritePackString(dp, item); diff --git a/plugins/basevotes/votemap.sp b/plugins/basevotes/votemap.sp index b1b643c2..9791dfc2 100644 --- a/plugins/basevotes/votemap.sp +++ b/plugins/basevotes/votemap.sp @@ -48,7 +48,7 @@ DisplayVoteMapMenu(client, mapCount, String:maps[5][]) if (mapCount == 1) { - strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), maps[0]); + GetMapDisplayName(maps[0], g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); g_hVoteMenu.SetTitle("Change Map To"); g_hVoteMenu.AddItem(maps[0], "Yes"); @@ -61,7 +61,9 @@ DisplayVoteMapMenu(client, mapCount, String:maps[5][]) g_hVoteMenu.SetTitle("Map Vote"); for (new i = 0; i < mapCount; i++) { - g_hVoteMenu.AddItem(maps[i], maps[i]); + decl String:displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(maps[i], displayName, sizeof(displayName)); + g_hVoteMenu.AddItem(maps[i], displayName); } } @@ -288,8 +290,10 @@ int LoadMapList(Menu menu) for (new i = 0; i < map_count; i++) { + decl String:displayName[PLATFORM_MAX_PATH]; GetArrayString(g_map_array, i, map_name, sizeof(map_name)); - menu.AddItem(map_name, map_name); + GetMapDisplayName(map_name, displayName, sizeof(displayName)); + menu.AddItem(map_name, displayName); } return map_count; diff --git a/plugins/include/halflife.inc b/plugins/include/halflife.inc index 422a115d..ac64f7fd 100644 --- a/plugins/include/halflife.inc +++ b/plugins/include/halflife.inc @@ -167,6 +167,24 @@ native bool:IsMapValid(const String:map[]); */ native FindMapResult FindMap(const char[] map, char[] foundmap, int maxlen); +/** + * Get the display name of a workshop map. + * + * Note: You do not need to call FindMap first. This native will call FindMap internally. + * + * @param map Map name (usually same as map path relative to maps/ dir, + * excluding .bsp extension). + * @param displayName Map's display name, i.e. cp_mymapname or de_mymapname. + * If FindMap returns FindMap_PossiblyAvailable or FindMap_NotFound, + * the map cannot be resolved and this native will return false, + * but displayName will be a copy of map. + * @param maxlen Maximum length to write to displayName var. + * @return true if FindMap returns FindMap_Found, FindMap_FuzzyMatch, or + * FindMap_NonCanonical. + * false if FindMap returns FindMap_PossiblyAvailable or FindMap_NotFound. + */ +native bool GetMapDisplayName(const char[] map, char[] displayName, int maxlen); + /** * Returns whether the server is dedicated. * diff --git a/plugins/mapchooser.sp b/plugins/mapchooser.sp index 223fb024..5b966bca 100644 --- a/plugins/mapchooser.sp +++ b/plugins/mapchooser.sp @@ -74,6 +74,8 @@ ConVar g_Cvar_RunOffPercent; Handle g_VoteTimer = null; Handle g_RetryTimer = null; +// g_MapList stores unresolved names so we can resolve them after every map change in the workshop updates. +// g_OldMapList and g_NextMapList are resolved. g_NominateList depends on the nominations implementation. /* Data Handles */ ArrayList g_MapList; ArrayList g_NominateList; @@ -288,15 +290,18 @@ public Action Command_SetNextmap(int client, int args) } char map[PLATFORM_MAX_PATH]; + char displayName[PLATFORM_MAX_PATH]; GetCmdArg(1, map, sizeof(map)); - if (!IsMapValid(map)) + if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) { ReplyToCommand(client, "[SM] %t", "Map was not found", map); return Plugin_Handled; } - - ShowActivity(client, "%t", "Changed Next Map", map); + + GetMapDisplayName(displayName, displayName, sizeof(displayName)); + + ShowActivity(client, "%t", "Changed Next Map", displayName); LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); SetNextMap(map); @@ -321,7 +326,7 @@ void SetupTimeleftTimer() int startTime = g_Cvar_StartTime.IntValue * 60; if (time - startTime < 0 && g_Cvar_EndOfMapVote.BoolValue && !g_MapVoteCompleted && !g_HasVoteStarted) { - InitiateVote(MapChange_MapEnd, null); + InitiateVote(MapChange_MapEnd, null); } else { @@ -585,11 +590,12 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ int nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; - for (int i=0; i Nominate_Replaced) { /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ - ReplyToCommand(client, "%t", "Map Already In Vote", mapname); + ReplyToCommand(client, "%t", "Map Already In Vote", displayName); return Plugin_Handled; } - g_mapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + g_mapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - ReplyToCommand(client, "%t", "Map Inserted", mapname); + ReplyToCommand(client, "%t", "Map Inserted", displayName); LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); return Plugin_Handled; @@ -187,10 +200,20 @@ public Action Command_Nominate(int client, int args) char mapname[PLATFORM_MAX_PATH]; GetCmdArg(1, mapname, sizeof(mapname)); + if (FindMap(mapname, mapname, sizeof(mapname)) == FindMap_NotFound) + { + // We couldn't resolve the map entry to a filename, so... + ReplyToCommand(client, "%t", "Map was not found", mapname); + return Plugin_Handled; + } + + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(mapname, displayName, sizeof(displayName)); + int status; if (!g_mapTrie.GetValue(mapname, status)) { - ReplyToCommand(client, "%t", "Map was not found", mapname); + ReplyToCommand(client, "%t", "Map was not found", displayName); return Plugin_Handled; } @@ -220,7 +243,7 @@ public Action Command_Nominate(int client, int args) { if (result == Nominate_AlreadyInVote) { - ReplyToCommand(client, "%t", "Map Already In Vote", mapname); + ReplyToCommand(client, "%t", "Map Already In Vote", displayName); } else { @@ -236,7 +259,7 @@ public Action Command_Nominate(int client, int args) char name[MAX_NAME_LENGTH]; GetClientName(client, name, sizeof(name)); - PrintToChatAll("[SM] %t", "Map Nominated", name, mapname); + PrintToChatAll("[SM] %t", "Map Nominated", name, displayName); return Plugin_Continue; } @@ -273,13 +296,17 @@ void BuildMapMenu() GetCurrentMap(currentMap, sizeof(currentMap)); } - for (int i = 0; i < g_MapList.Length; i++) { int status = MAPSTATUS_ENABLED; g_MapList.GetString(i, map, sizeof(map)); + FindMap(map, map, sizeof(map)); + + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(map, displayName, sizeof(displayName)); + if (g_Cvar_ExcludeCurrent.BoolValue) { if (StrEqual(map, currentMap)) @@ -297,7 +324,7 @@ void BuildMapMenu() } } - g_MapMenu.AddItem(map, map); + g_MapMenu.AddItem(map, displayName); g_mapTrie.SetValue(map, status); } @@ -312,8 +339,8 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p { case MenuAction_Select: { - char map[PLATFORM_MAX_PATH], name[MAX_NAME_LENGTH]; - menu.GetItem(param2, map, sizeof(map)); + char map[PLATFORM_MAX_PATH], name[MAX_NAME_LENGTH], displayName[PLATFORM_MAX_PATH]; + menu.GetItem(param2, map, sizeof(map), _, displayName, sizeof(displayName)); GetClientName(param1, name, sizeof(name)); @@ -335,11 +362,11 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p if (result == Nominate_Replaced) { - PrintToChatAll("[SM] %t", "Map Nomination Changed", name, map); + PrintToChatAll("[SM] %t", "Map Nomination Changed", name, displayName); return 0; } - PrintToChatAll("[SM] %t", "Map Nominated", name, map); + PrintToChatAll("[SM] %t", "Map Nominated", name, displayName); } case MenuAction_DrawItem: @@ -366,8 +393,8 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p case MenuAction_DisplayItem: { - char map[PLATFORM_MAX_PATH]; - menu.GetItem(param2, map, sizeof(map)); + char map[PLATFORM_MAX_PATH], displayName[PLATFORM_MAX_PATH]; + menu.GetItem(param2, map, sizeof(map), _, displayName, sizeof(displayName)); int status; @@ -383,19 +410,19 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p { if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) { - Format(display, sizeof(display), "%s (%T)", map, "Current Map", param1); + Format(display, sizeof(display), "%s (%T)", displayName, "Current Map", param1); return RedrawMenuItem(display); } if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) { - Format(display, sizeof(display), "%s (%T)", map, "Recently Played", param1); + Format(display, sizeof(display), "%s (%T)", displayName, "Recently Played", param1); return RedrawMenuItem(display); } if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) { - Format(display, sizeof(display), "%s (%T)", map, "Nominated", param1); + Format(display, sizeof(display), "%s (%T)", displayName, "Nominated", param1); return RedrawMenuItem(display); } } diff --git a/plugins/randomcycle.sp b/plugins/randomcycle.sp index a2e3ee14..1be9ddee 100644 --- a/plugins/randomcycle.sp +++ b/plugins/randomcycle.sp @@ -82,6 +82,7 @@ public void OnConfigsExecuted() public Action Timer_RandomizeNextmap(Handle timer) { char map[PLATFORM_MAX_PATH]; + char resolvedMap[PLATFORM_MAX_PATH]; bool oldMaps = false; if (g_Cvar_ExcludeMaps.IntValue && g_MapList.Length > g_Cvar_ExcludeMaps.IntValue) @@ -89,16 +90,14 @@ public Action Timer_RandomizeNextmap(Handle timer) oldMaps = true; } - int b = GetRandomInt(0, g_MapList.Length - 1); - g_MapList.GetString(b, map, sizeof(map)); - - while (oldMaps && g_OldMapList.FindString(map) != -1) + do { - b = GetRandomInt(0, g_MapList.Length - 1); + int b = GetRandomInt(0, g_MapList.Length - 1); g_MapList.GetString(b, map, sizeof(map)); - } + FindMap(map, resolvedMap, sizeof(resolvedMap)); + } while (oldMaps && g_OldMapList.FindString(resolvedMap) != -1); - g_OldMapList.PushString(map); + g_OldMapList.PushString(resolvedMap); SetNextMap(map); if (g_OldMapList.Length > g_Cvar_ExcludeMaps.IntValue) diff --git a/plugins/rockthevote.sp b/plugins/rockthevote.sp index 9474e386..89b9d104 100644 --- a/plugins/rockthevote.sp +++ b/plugins/rockthevote.sp @@ -243,6 +243,8 @@ void StartRTV() char map[PLATFORM_MAX_PATH]; if (GetNextMap(map, sizeof(map))) { + GetMapDisplayName(map, map, sizeof(map)); + PrintToChatAll("[SM] %t", "Changing Maps", map); CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); g_InChange = true; diff --git a/plugins/testsuite/mapdisplayname.sp b/plugins/testsuite/mapdisplayname.sp new file mode 100644 index 00000000..dd00d2a5 --- /dev/null +++ b/plugins/testsuite/mapdisplayname.sp @@ -0,0 +1,25 @@ +#include + +public void OnPluginStart() +{ + RegServerCmd("test_mapdisplayname", test_mapdisplayname); +} + +public Action test_mapdisplayname( int argc ) +{ + char mapName[PLATFORM_MAX_PATH]; + GetCmdArg(1, mapName, sizeof(mapName)); + + char displayName[PLATFORM_MAX_PATH]; + + if (GetMapDisplayName(mapName, displayName, sizeof(displayName))) + { + PrintToServer("GetMapDisplayName says \"%s\" for \"%s\"", displayName, mapName); + } + else + { + PrintToServer("GetMapDisplayName says \"%s\" was not found or not resolved", mapName); + } + + return Plugin_Handled; +} \ No newline at end of file