From e78f6eabd586c96e9c7e27b3c9ff46e7fe389142 Mon Sep 17 00:00:00 2001 From: Saul Rennison Date: Tue, 2 Jul 2013 16:23:42 +0100 Subject: [PATCH 1/2] Added auto-complete to "give" ConCommand --- mp/src/game/server/client.cpp | 166 +++++++++++++++++++++++++--------- mp/src/game/server/util.cpp | 7 +- mp/src/game/server/util.h | 5 + 3 files changed, 134 insertions(+), 44 deletions(-) diff --git a/mp/src/game/server/client.cpp b/mp/src/game/server/client.cpp index 2fd87cfec8..9dc55ac9af 100644 --- a/mp/src/game/server/client.cpp +++ b/mp/src/game/server/client.cpp @@ -787,36 +787,144 @@ CON_COMMAND( say_team, "Display player message to team" ) //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -CON_COMMAND( give, "Give item to player.\n\tArguments: " ) +struct ClassNamePrefix_t { - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); - if ( pPlayer - && (gpGlobals->maxClients == 1 || sv_cheats->GetBool()) - && args.ArgC() >= 2 ) + ClassNamePrefix_t(const char *pszPrefix, bool bKeepPrefix) : m_pszPrefix(pszPrefix), m_bKeepPrefix(bKeepPrefix) + { + m_nLength = strlen(pszPrefix); + } + + const char *m_pszPrefix; + size_t m_nLength; + bool m_bKeepPrefix; +}; + + +// Add class name prefixes to show in the "give" command autocomplete here +static ClassNamePrefix_t s_pEntityPrefixes[] = +{ + ClassNamePrefix_t("ammo_", true), + ClassNamePrefix_t("item_", true), + ClassNamePrefix_t("weapon_", false), +}; + + +static int StringSortFunc(const void *p1, const void *p2) +{ + const char *psz1 = (const char *)p1; + const char *psz2 = (const char *)p2; + + return V_stricmp(psz1, psz2); +} + + +int GiveAutocomplete(const char *partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]) +{ + // Find the first space in our input + const char *firstSpace = V_strstr(partial, " "); + if(!firstSpace) + return 0; + + int commandLength = firstSpace - partial; + + // Extract the command name from the input + char commandName[COMMAND_COMPLETION_ITEM_LENGTH]; + V_StrSlice(partial, 0, commandLength, commandName, sizeof(commandName)); + + // Calculate the length of the command string (minus the command name) + partial += commandLength + 1; + int partialLength = V_strlen(partial); + + // Grab the factory dictionary + if(!EntityFactoryDictionary()) + return 0; + + const EntityFactoryDict_t &factoryDict = EntityFactoryDictionary()->GetFactoryDictionary(); + int numMatches = 0; + + // Iterate through all entity factories + for(int i = factoryDict.First(); i != factoryDict.InvalidIndex() && numMatches < COMMAND_COMPLETION_MAXITEMS; i = factoryDict.Next(i)) { - char item_to_give[ 256 ]; - Q_strncpy( item_to_give, args[1], sizeof( item_to_give ) ); - Q_strlower( item_to_give ); + const char *pszClassName = factoryDict.GetElementName(i); - // Dirty hack to avoid suit playing it's pickup sound - if ( !Q_stricmp( item_to_give, "item_suit" ) ) + // See if this entity classname has a prefix that we show in the + // autocomplete + // TODO: optimise by caching all autocompletable classnames into a hash + // table on first run + int j; + const ClassNamePrefix_t *pPrefix = NULL; + + for(j = 0; j < ARRAYSIZE(s_pEntityPrefixes); ++j) { - pPlayer->EquipSuit( false ); - return; + pPrefix = &s_pEntityPrefixes[j]; + + if(Q_strncmp(pszClassName, pPrefix->m_pszPrefix, pPrefix->m_nLength)) + continue; + + break; } - string_t iszItem = AllocPooledString( item_to_give ); // Make a copy of the classname - pPlayer->GiveNamedItem( STRING(iszItem) ); + // If this entity factory had no prefixes, we could not find the prefix, skip this entity + if(j == ARRAYSIZE(s_pEntityPrefixes)) + continue; + + // Skip past the prefix if it shouldn't be kept + if(!pPrefix->m_bKeepPrefix) + pszClassName += pPrefix->m_nLength; + + // Does this entity match our partial completion? + if(Q_strnicmp(pszClassName, partial, partialLength)) + continue; + + Q_snprintf(commands[numMatches++], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", commandName, pszClassName); } + + // Sort the commands alphabetically + qsort(commands, numMatches, COMMAND_COMPLETION_ITEM_LENGTH, StringSortFunc); + + return numMatches; +} + +CON_COMMAND_F_COMPLETION(give, "Give item to player. Syntax: ", FCVAR_CHEAT, GiveAutocomplete) +{ + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if(!pPlayer) + return; + + if(args.ArgC() != 2) + return; + + char pszClassName[64]; + Q_strncpy(pszClassName, args.Arg(1), sizeof(pszClassName)); + + for(int i = 0; i < ARRAYSIZE(s_pEntityPrefixes) && !CanCreateEntityClass(pszClassName); ++i) + { + // If we keep the prefix in the autocomplete, there's no point + // checking this prefix + if(s_pEntityPrefixes[i].m_bKeepPrefix) + continue; + + Q_snprintf(pszClassName, sizeof(pszClassName), "%s%s", s_pEntityPrefixes[i].m_pszPrefix, args.Arg(1)); + } + + // If this is class name does not have an entity factory, complain to the + // client + if(!CanCreateEntityClass(pszClassName)) + { + ClientPrint(pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs("give: Unknown entity \"%s\"\n", args.Arg(1))); + return; + } + + pPlayer->GiveNamedItem(pszClassName); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -CON_COMMAND( fov, "Change players FOV" ) +CON_COMMAND_F( fov, "Change players FOV", FCVAR_CHEAT ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); - if ( pPlayer && sv_cheats->GetBool() ) + if ( pPlayer ) { if ( args.ArgC() > 1 ) { @@ -999,7 +1107,7 @@ void CC_Player_Use( const CCommand &args ) CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer) { - pPlayer->SelectItem((char *)args[1]); + pPlayer->SelectItem(args[1]); } } static ConCommand use("use", CC_Player_Use, "Use a particular weapon\t\nArguments: "); @@ -1053,9 +1161,6 @@ void EnableNoClip( CBasePlayer *pPlayer ) void CC_Player_NoClip( void ) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1113,9 +1218,6 @@ static ConCommand noclip("noclip", CC_Player_NoClip, "Toggle. Player becomes non //------------------------------------------------------------------------------ void CC_God_f (void) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1146,9 +1248,6 @@ static ConCommand god("god", CC_God_f, "Toggle. Player becomes invulnerable.", F //------------------------------------------------------------------------------ CON_COMMAND_F( setpos, "Move player to specified origin (must have sv_cheats).", FCVAR_CHEAT ) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1180,9 +1279,6 @@ CON_COMMAND_F( setpos, "Move player to specified origin (must have sv_cheats).", //------------------------------------------------------------------------------ void CC_setang_f (const CCommand &args) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1221,9 +1317,6 @@ static float GetHexFloat( const char *pStr ) //------------------------------------------------------------------------------ CON_COMMAND_F( setpos_exact, "Move player to an exact specified origin (must have sv_cheats).", FCVAR_CHEAT ) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1255,9 +1348,6 @@ CON_COMMAND_F( setpos_exact, "Move player to an exact specified origin (must hav CON_COMMAND_F( setang_exact, "Snap player eyes and orientation to specified pitch yaw (must have sv_cheats).", FCVAR_CHEAT ) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1289,9 +1379,6 @@ CON_COMMAND_F( setang_exact, "Snap player eyes and orientation to specified pitc //------------------------------------------------------------------------------ void CC_Notarget_f (void) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; @@ -1313,9 +1400,6 @@ ConCommand notarget("notarget", CC_Notarget_f, "Toggle. Player becomes hidden to //------------------------------------------------------------------------------ void CC_HurtMe_f(const CCommand &args) { - if ( !sv_cheats->GetBool() ) - return; - CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; diff --git a/mp/src/game/server/util.cpp b/mp/src/game/server/util.cpp index 8b2365f763..821f6f9bb8 100644 --- a/mp/src/game/server/util.cpp +++ b/mp/src/game/server/util.cpp @@ -75,13 +75,14 @@ class CEntityFactoryDictionary : public IEntityFactoryDictionary virtual void InstallFactory( IEntityFactory *pFactory, const char *pClassName ); virtual IServerNetworkable *Create( const char *pClassName ); virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ); + virtual IEntityFactory *FindFactory( const char *pClassName ); virtual const char *GetCannonicalName( const char *pClassName ); + virtual const EntityFactoryDict_t &GetFactoryDictionary() { return m_Factories; } + void ReportEntitySizes(); -private: - IEntityFactory *FindFactory( const char *pClassName ); public: - CUtlDict< IEntityFactory *, unsigned short > m_Factories; + EntityFactoryDict_t m_Factories; }; //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/util.h b/mp/src/game/server/util.h index 37d53eff65..91bbbb7a55 100644 --- a/mp/src/game/server/util.h +++ b/mp/src/game/server/util.h @@ -31,6 +31,7 @@ #include "util_shared.h" #include "shareddefs.h" #include "networkvar.h" +#include "utldict.h" struct levellist_t; class IServerNetworkable; @@ -92,6 +93,9 @@ T *_CreateEntity( T *newClass, const char *className ) // This is the glue that hooks .MAP entity class names to our CPP classes +class IEntityFactory; +typedef CUtlDict EntityFactoryDict_t; + abstract_class IEntityFactoryDictionary { public: @@ -100,6 +104,7 @@ abstract_class IEntityFactoryDictionary virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ) = 0; virtual IEntityFactory *FindFactory( const char *pClassName ) = 0; virtual const char *GetCannonicalName( const char *pClassName ) = 0; + virtual const EntityFactoryDict_t &GetFactoryDictionary() = 0; }; IEntityFactoryDictionary *EntityFactoryDictionary(); From 8844deeaad5ba679b7ee32dfdba6c13331d17a75 Mon Sep 17 00:00:00 2001 From: Saul Rennison Date: Tue, 2 Jul 2013 16:43:37 +0100 Subject: [PATCH 2/2] Updated prefixes and re-added item_suit hack --- mp/src/game/server/client.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mp/src/game/server/client.cpp b/mp/src/game/server/client.cpp index 9dc55ac9af..4e6a0e5fe6 100644 --- a/mp/src/game/server/client.cpp +++ b/mp/src/game/server/client.cpp @@ -803,8 +803,7 @@ struct ClassNamePrefix_t // Add class name prefixes to show in the "give" command autocomplete here static ClassNamePrefix_t s_pEntityPrefixes[] = { - ClassNamePrefix_t("ammo_", true), - ClassNamePrefix_t("item_", true), + ClassNamePrefix_t("item_", false), ClassNamePrefix_t("weapon_", false), }; @@ -915,6 +914,13 @@ CON_COMMAND_F_COMPLETION(give, "Give item to player. Syntax: ", FCVAR return; } + // Dirty hack to avoid suit playing its pickup sound + if(FStrEq(pszClassName, "item_suit")) + { + pPlayer->EquipSuit(false); + return; + } + pPlayer->GiveNamedItem(pszClassName); }