From 7346b0c08101aab1d85cc81d6aa7b8553900ba93 Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Thu, 14 Feb 2013 19:28:12 -0500 Subject: [PATCH] Added support for "fuzzy" map names in L4D and later (bug 5599, r=asherkin). --- core/HalfLife2.cpp | 41 +++++++++++++++++++++++++++++++++++++ core/HalfLife2.h | 1 + core/NextMap.cpp | 5 +++-- core/logic/smn_maplists.cpp | 3 ++- core/smn_halflife.cpp | 2 +- public/IGameHelpers.h | 10 +++++++++ 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/core/HalfLife2.cpp b/core/HalfLife2.cpp index 8e7deccf..a9feccff 100644 --- a/core/HalfLife2.cpp +++ b/core/HalfLife2.cpp @@ -1131,3 +1131,44 @@ const char *CHalfLife2::GetEntityClassname(CBaseEntity *pEntity) return *(const char **)(((unsigned char *)pEntity) + offset); } + +#if SOURCE_ENGINE >= SE_LEFT4DEAD +static bool ResolveFuzzyMapName(const char *fuzzyName, char *outFullname, int size) +{ + static ConCommand *pHelperCmd = g_pCVar->FindCommand("changelevel"); + if (!pHelperCmd || !pHelperCmd->CanAutoComplete()) + return false; + + static size_t helperCmdLen = strlen(pHelperCmd->GetName()); + + CUtlVector results; + pHelperCmd->AutoCompleteSuggest(fuzzyName, results); + if (results.Count() == 0) + return false; + + // Results come back as you'd see in autocomplete. (ie. "changelevel fullmapnamehere"), + // so skip ahead to start of map path/name + + // Like the engine, we're only going to deal with the first match. + + strncopy(outFullname, &results[0][helperCmdLen + 1], size); + + return true; +} +#endif + +bool CHalfLife2::IsMapValid(const char *map) +{ + bool ret = engine->IsMapValid(map); +#if SOURCE_ENGINE >= SE_LEFT4DEAD + if (!ret) + { + static char szFuzzyName[PLATFORM_MAX_PATH]; + if (ResolveFuzzyMapName(map, szFuzzyName, sizeof(szFuzzyName))) + { + ret = engine->IsMapValid(szFuzzyName); + } + } +#endif + return ret; +} diff --git a/core/HalfLife2.h b/core/HalfLife2.h index 46f2654f..047affd8 100644 --- a/core/HalfLife2.h +++ b/core/HalfLife2.h @@ -148,6 +148,7 @@ public: //IGameHelpers ICommandLine *GetValveCommandLine(); const char *GetEntityClassname(edict_t *pEdict); const char *GetEntityClassname(CBaseEntity *pEntity); + bool IsMapValid(const char *map); public: void AddToFakeCliCmdQueue(int client, int userid, const char *cmd); void ProcessFakeCliCmdQueue(); diff --git a/core/NextMap.cpp b/core/NextMap.cpp index 605a1577..5bfc3f12 100644 --- a/core/NextMap.cpp +++ b/core/NextMap.cpp @@ -31,6 +31,7 @@ #include "NextMap.h" #include "Logger.h" +#include "HalfLife2.h" #include "sourcemm_api.h" #include "sm_stringutil.h" #include "sourcehook.h" @@ -109,7 +110,7 @@ const char *NextMapManager::GetNextMap() bool NextMapManager::SetNextMap(const char *map) { - if (!engine->IsMapValid(map)) + if (!g_HL2.IsMapValid(map)) { return false; } @@ -133,7 +134,7 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const const char *newmap = sm_nextmap.GetString(); - if (newmap[0] == 0 || !engine->IsMapValid(newmap)) + if (newmap[0] == 0 || !g_HL2.IsMapValid(newmap)) { RETURN_META(MRES_IGNORED); } diff --git a/core/logic/smn_maplists.cpp b/core/logic/smn_maplists.cpp index a35c6e8c..92795248 100644 --- a/core/logic/smn_maplists.cpp +++ b/core/logic/smn_maplists.cpp @@ -33,6 +33,7 @@ #include #include "common_logic.h" #include "CellArray.h" +#include #include #include #include @@ -524,7 +525,7 @@ private: { continue; } - if (!engine->IsMapValid(ptr)) + if (!gamehelpers->IsMapValid(ptr)) { continue; } diff --git a/core/smn_halflife.cpp b/core/smn_halflife.cpp index 40dd1587..a6884f94 100644 --- a/core/smn_halflife.cpp +++ b/core/smn_halflife.cpp @@ -63,7 +63,7 @@ static cell_t IsMapValid(IPluginContext *pContext, const cell_t *params) char *map; pContext->LocalToString(params[1], &map); - return engine->IsMapValid(map); + return g_HL2.IsMapValid(map); } static cell_t IsDedicatedServer(IPluginContext *pContext, const cell_t *params) diff --git a/public/IGameHelpers.h b/public/IGameHelpers.h index 4e165d7e..a04de922 100644 --- a/public/IGameHelpers.h +++ b/public/IGameHelpers.h @@ -308,6 +308,16 @@ namespace SourceMod * @return Pointer to the string, or NULL if bad pointer. */ virtual const char *GetEntityClassname(CBaseEntity *pEntity) =0; + + /** + * @brief Returns whether or not a map name is valid to use with the + * engine's Changelevel functionality. It need not be an exact filename on + * some engines. For a check on the exact name, use IVEngineServer::IsMapValid. + * + * @param map Map name. + * @return True if valid, otherwise false. + */ + virtual bool IsMapValid(const char *map) =0; }; }