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