diff --git a/scripts/cmd5.py b/scripts/cmd5.py index 67fc8450e..67516f608 100644 --- a/scripts/cmd5.py +++ b/scripts/cmd5.py @@ -29,5 +29,7 @@ for filename in sys.argv[1:]: f += cstrip([l.strip() for l in open(filename, "rb")]) hash = hashlib.md5(f).hexdigest().lower()[16:] -#TODO 0.7: improve nethash creation +#TODO 0.8: improve nethash creation +if hash == "0a027ded5791b521": + hash = "802f1be60a05665f" print('#define GAME_NETVERSION_HASH "%s"' % hash) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 041b5da53..97b585fb0 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -509,6 +509,7 @@ void CClient::Connect(const char *pAddress) } m_RconAuthed = 0; + m_UseTempRconCommands = 0; if(m_ServerAddress.port == 0) m_ServerAddress.port = Port; m_NetClient.Connect(&m_ServerAddress); @@ -1175,6 +1176,18 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(Unpacker.Error() == 0) m_pConsole->DeregisterTemp(pName); } + else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_MAPLIST_ENTRY_ADD) + { + const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC); + if(Unpacker.Error() == 0) + m_pConsole->RegisterTempMap(pName); + } + else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_MAPLIST_ENTRY_REM) + { + const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC); + if(Unpacker.Error() == 0) + m_pConsole->DeregisterTempMap(pName); + } else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_RCON_AUTH_ON) { m_RconAuthed = 1; @@ -1186,6 +1199,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(m_UseTempRconCommands) m_pConsole->DeregisterTempAll(); m_UseTempRconCommands = 0; + m_pConsole->DeregisterTempMapAll(); } else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_RCON_LINE) { diff --git a/src/engine/console.h b/src/engine/console.h index 21a6ff4c4..013b9b62e 100644 --- a/src/engine/console.h +++ b/src/engine/console.h @@ -24,6 +24,8 @@ public: TEMPCMD_HELP_LENGTH=96, TEMPCMD_PARAMS_LENGTH=16, + TEMPMAP_NAME_LENGTH = 32, + MAX_PRINT_CB=4, }; @@ -67,12 +69,16 @@ public: virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const = 0; virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp) = 0; virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) = 0; + virtual void PossibleMaps(const char *pStr, FPossibleCallback pfnCallback, void *pUser) = 0; virtual void ParseArguments(int NumArgs, const char **ppArguments) = 0; virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0; virtual void RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp) = 0; virtual void DeregisterTemp(const char *pName) = 0; virtual void DeregisterTempAll() = 0; + virtual void RegisterTempMap(const char *pName) = 0; + virtual void DeregisterTempMap(const char *pName) = 0; + virtual void DeregisterTempMapAll() = 0; virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) = 0; virtual void StoreCommands(bool Store) = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index d86a8300c..2e5894bdf 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -280,6 +280,10 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) m_pCurrentMapData = 0; m_CurrentMapSize = 0; + m_NumMapEntries = 0; + m_pFirstMapEntry = 0; + m_pLastMapEntry = 0; + m_MapReload = 0; m_RconClientID = IServer::RCON_CID_SERV; @@ -664,6 +668,7 @@ int CServer::NewClientCallback(int ClientID, void *pUser) pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; + pThis->m_aClients[ClientID].m_pMapListEntryToSend = 0; pThis->m_aClients[ClientID].m_NoRconNote = false; pThis->m_aClients[ClientID].m_Quitting = false; pThis->m_aClients[ClientID].Reset(); @@ -694,6 +699,7 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; + pThis->m_aClients[ClientID].m_pMapListEntryToSend = 0; pThis->m_aClients[ClientID].m_NoRconNote = false; pThis->m_aClients[ClientID].m_Quitting = false; pThis->m_aClients[ClientID].m_Snapshots.PurgeAll(); @@ -774,6 +780,36 @@ void CServer::UpdateClientRconCommands() } } +void CServer::SendMapListEntryAdd(const CMapListEntry *pMapListEntry, int ClientID) +{ + CMsgPacker Msg(NETMSG_MAPLIST_ENTRY_ADD, true); + Msg.AddString(pMapListEntry->m_aName, 256); + SendMsg(&Msg, MSGFLAG_VITAL, ClientID); +} + +void CServer::SendMapListEntryRem(const CMapListEntry *pMapListEntry, int ClientID) +{ + CMsgPacker Msg(NETMSG_MAPLIST_ENTRY_REM, true); + Msg.AddString(pMapListEntry->m_aName, 256); + SendMsg(&Msg, MSGFLAG_VITAL, ClientID); +} + + +void CServer::UpdateClientMapListEntries() +{ + for(int ClientID = Tick() % MAX_RCONCMD_RATIO; ClientID < MaxClients(); ClientID += MAX_RCONCMD_RATIO) + { + if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY && m_aClients[ClientID].m_Authed) + { + for(int i = 0; i < MAX_MAPLISTENTRY_SEND && m_aClients[ClientID].m_pMapListEntryToSend; ++i) + { + SendMapListEntryAdd(m_aClients[ClientID].m_pMapListEntryToSend, ClientID); + m_aClients[ClientID].m_pMapListEntryToSend = m_aClients[ClientID].m_pMapListEntryToSend->m_pNext; + } + } + } +} + void CServer::ProcessClientPacket(CNetChunk *pPacket) { int ClientID = pPacket->m_ClientID; @@ -979,6 +1015,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) m_aClients[ClientID].m_Authed = AUTHED_ADMIN; m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER); + m_aClients[ClientID].m_pMapListEntryToSend = m_pFirstMapEntry; SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted."); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID); @@ -992,6 +1029,9 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) m_aClients[ClientID].m_Authed = AUTHED_MOD; m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER); SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted."); + const IConsole::CCommandInfo *pInfo = Console()->GetCommandInfo("sv_map", CFGFLAG_SERVER, false); + if(pInfo && pInfo->GetAccessLevel() == IConsole::ACCESS_LEVEL_MOD) + m_aClients[ClientID].m_pMapListEntryToSend = m_pFirstMapEntry; char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); @@ -1232,6 +1272,13 @@ int CServer::Run() // m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this); + // list maps + m_pMapListHeap = new CHeap(); + CSubdirCallbackUserdata Userdata; + Userdata.m_pServer = this; + str_copy(Userdata.m_aName, "", sizeof(Userdata.m_aName)); + m_pStorage->ListDirectory(IStorage::TYPE_ALL, "maps/", MapListEntryCallback, &Userdata); + // load map if(!LoadMap(g_Config.m_SvMap)) { @@ -1361,6 +1408,7 @@ int CServer::Run() DoSnapshot(); UpdateClientRconCommands(); + UpdateClientMapListEntries(); } // master server stuff @@ -1414,6 +1462,52 @@ int CServer::Run() return 0; } +int CServer::MapListEntryCallback(const char *pFilename, int IsDir, int DirType, void *pUser) +{ + CSubdirCallbackUserdata *pUserdata = (CSubdirCallbackUserdata *)pUser; + CServer *pThis = pUserdata->m_pServer; + + if(pFilename[0] == '.') // hidden files + return 0; + + char aFilename[512]; + if(pUserdata->m_aName[0]) + str_format(aFilename, sizeof(aFilename), "%s/%s", pUserdata->m_aName, pFilename); + else + str_format(aFilename, sizeof(aFilename), "%s", pFilename); + + if(IsDir) + { + CSubdirCallbackUserdata Userdata; + Userdata.m_pServer = pThis; + str_copy(Userdata.m_aName, aFilename, sizeof(Userdata.m_aName)); + char FindPath[512]; + str_format(FindPath, sizeof(FindPath), "maps/%s/", aFilename); + pThis->m_pStorage->ListDirectory(IStorage::TYPE_ALL, FindPath, MapListEntryCallback, &Userdata); + return 0; + } + + const char *pSuffix = str_endswith(aFilename, ".map"); + if(!pSuffix) // not ending with .map + { + return 0; + } + + CMapListEntry *pEntry = (CMapListEntry *)pThis->m_pMapListHeap->Allocate(sizeof(CMapListEntry)); + pThis->m_NumMapEntries++; + pEntry->m_pNext = 0; + pEntry->m_pPrev = pThis->m_pLastMapEntry; + if(pEntry->m_pPrev) + pEntry->m_pPrev->m_pNext = pEntry; + pThis->m_pLastMapEntry = pEntry; + if(!pThis->m_pFirstMapEntry) + pThis->m_pFirstMapEntry = pEntry; + + str_truncate(pEntry->m_aName, sizeof(pEntry->m_aName), aFilename, pSuffix-aFilename); + + return 0; +} + void CServer::ConKick(IConsole::IResult *pResult, void *pUser) { if(pResult->NumArguments() > 1) @@ -1446,7 +1540,7 @@ void CServer::ConStatus(IConsole::IResult *pResult, void *pUser) } else str_format(aBuf, sizeof(aBuf), "id=%d addr=%s connecting", i, aAddrStr); - pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } } } @@ -1518,6 +1612,7 @@ void CServer::ConLogout(IConsole::IResult *pResult, void *pUser) pServer->m_aClients[pServer->m_RconClientID].m_Authed = AUTHED_NO; pServer->m_aClients[pServer->m_RconClientID].m_AuthTries = 0; pServer->m_aClients[pServer->m_RconClientID].m_pRconCmdToSend = 0; + pServer->m_aClients[pServer->m_RconClientID].m_pMapListEntryToSend = 0; pServer->SendRconLine(pServer->m_RconClientID, "Logout successful."); char aBuf[32]; str_format(aBuf, sizeof(aBuf), "ClientID=%d logged out", pServer->m_RconClientID); diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 2112092f1..4fd5fe25f 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -4,7 +4,7 @@ #define ENGINE_SERVER_SERVER_H #include - +#include class CSnapIDPool { @@ -76,9 +76,12 @@ public: AUTHED_ADMIN, MAX_RCONCMD_SEND=16, + MAX_MAPLISTENTRY_SEND = 32, MAX_RCONCMD_RATIO=8, }; + class CMapListEntry; + class CClient { public: @@ -128,6 +131,7 @@ public: bool m_NoRconNote; bool m_Quitting; const IConsole::CCommandInfo *m_pRconCmdToSend; + const CMapListEntry *m_pMapListEntryToSend; void Reset(); }; @@ -163,6 +167,25 @@ public: int m_CurrentMapSize; int m_MapChunksPerRequest; + //maplist + struct CMapListEntry + { + CMapListEntry *m_pPrev; + CMapListEntry *m_pNext; + char m_aName[IConsole::TEMPMAP_NAME_LENGTH]; + }; + + struct CSubdirCallbackUserdata + { + CServer *m_pServer; + char m_aName[IConsole::TEMPMAP_NAME_LENGTH]; + }; + + CHeap *m_pMapListHeap; + CMapListEntry *m_pLastMapEntry; + CMapListEntry *m_pFirstMapEntry; + int m_NumMapEntries; + int m_RconPasswordSet; int m_GeneratedRconPassword; @@ -214,6 +237,9 @@ public: void SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID); void SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID); void UpdateClientRconCommands(); + void SendMapListEntryAdd(const CMapListEntry *pMapListEntry, int ClientID); + void SendMapListEntryRem(const CMapListEntry *pMapListEntry, int ClientID); + void UpdateClientMapListEntries(); void ProcessClientPacket(CNetChunk *pPacket); @@ -228,6 +254,8 @@ public: void InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer, IConsole *pConsole); int Run(); + static int MapListEntryCallback(const char *pFilename, int IsDir, int DirType, void *pUser); + static void ConKick(IConsole::IResult *pResult, void *pUser); static void ConStatus(IConsole::IResult *pResult, void *pUser); static void ConShutdown(IConsole::IResult *pResult, void *pUser); diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index daf966540..18d752323 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -356,6 +356,15 @@ void CConsole::PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPoss } } +void CConsole::PossibleMaps(const char *pStr, FPossibleCallback pfnCallback, void *pUser) +{ + for(CMapListEntryTemp *pMapEntry = m_pFirstMapEntry; pMapEntry; pMapEntry = pMapEntry->m_pNext) + { + if(str_find_nocase(pMapEntry->m_aName, pStr)) + pfnCallback(pMapEntry->m_aName, pUser); + } +} + CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask) { for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) @@ -653,6 +662,10 @@ CConsole::CConsole(int FlagMask) m_StoreCommands = true; m_paStrokeStr[0] = "0"; m_paStrokeStr[1] = "1"; + m_pTempMapListHeap = 0; + m_NumMapListEntries = 0; + m_pFirstMapEntry = 0; + m_pLastMapEntry = 0; m_ExecutionQueue.Reset(); m_pFirstCommand = 0; m_pFirstExec = 0; @@ -864,6 +877,71 @@ void CConsole::DeregisterTempAll() m_pRecycleList = 0; } +void CConsole::RegisterTempMap(const char *pName) +{ + if(!m_pTempMapListHeap) + m_pTempMapListHeap = new CHeap(); + CMapListEntryTemp *pEntry = (CMapListEntryTemp *)m_pTempMapListHeap->Allocate(sizeof(CMapListEntryTemp)); + pEntry->m_pNext = 0; + pEntry->m_pPrev = m_pLastMapEntry; + if(pEntry->m_pPrev) + pEntry->m_pPrev->m_pNext = pEntry; + m_pLastMapEntry = pEntry; + if(!m_pFirstMapEntry) + m_pFirstMapEntry = pEntry; + str_copy(pEntry->m_aName, pName, TEMPMAP_NAME_LENGTH); + m_NumMapListEntries++; +} + +void CConsole::DeregisterTempMap(const char *pName) +{ + CMapListEntryTemp *pEntry = m_pFirstMapEntry; + + while(pEntry) + { + if(str_comp_nocase(pName, pEntry->m_aName) == 0) + break; + pEntry = pEntry->m_pNext; + } + + m_NumMapListEntries--; + CHeap *pNewTempMapListHeap = new CHeap(); + CMapListEntryTemp *pNewFirstEntry = 0; + CMapListEntryTemp *pNewLastEntry = 0; + int NewMapEntryNum = m_NumMapListEntries; + + for(CMapListEntryTemp *pSrc = m_pFirstMapEntry; pSrc; pSrc = pSrc->m_pNext) + { + if(pSrc == pEntry) + continue; + + CMapListEntryTemp *pDst = (CMapListEntryTemp *)pNewTempMapListHeap->Allocate(sizeof(CMapListEntryTemp)); + pDst->m_pNext = 0; + pDst->m_pPrev = m_pLastMapEntry; + if(pDst->m_pPrev) + pDst->m_pPrev->m_pNext = pDst; + m_pLastMapEntry = pDst; + if(!m_pFirstMapEntry) + m_pFirstMapEntry = pDst; + + str_copy(pDst->m_aName, pSrc->m_aName, TEMPMAP_NAME_LENGTH); + } + + delete m_pTempMapListHeap; + m_pTempMapListHeap = pNewTempMapListHeap; + m_pFirstMapEntry = pNewFirstEntry; + m_pLastMapEntry = pNewLastEntry; + m_NumMapListEntries = NewMapEntryNum; +} + +void CConsole::DeregisterTempMapAll() +{ + m_pTempMapListHeap->Reset(); + m_pFirstMapEntry = 0; + m_pLastMapEntry = 0; + m_NumMapListEntries = 0; +} + void CConsole::Con_Chain(IResult *pResult, void *pUserData) { CChain *pInfo = (CChain *)pUserData; diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h index 3b99489a6..114826177 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -57,7 +57,7 @@ class CConsole : public IConsole static void ConToggle(IResult *pResult, void *pUser); static void ConToggleStroke(IResult *pResult, void *pUser); static void ConModCommandAccess(IResult *pResult, void *pUser); - static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser); + static void ConModCommandStatus(IResult *pResult, void *pUser); void ExecuteFileRecurse(const char *pFilename); void ExecuteLineStroked(int Stroke, const char *pStr); @@ -154,6 +154,17 @@ class CConsole : public IConsole void AddCommandSorted(CCommand *pCommand); CCommand *FindCommand(const char *pName, int FlagMask); + struct CMapListEntryTemp { + CMapListEntryTemp *m_pPrev; + CMapListEntryTemp *m_pNext; + char m_aName[TEMPMAP_NAME_LENGTH]; + }; + + CHeap *m_pTempMapListHeap; + int m_NumMapListEntries; + CMapListEntryTemp *m_pFirstMapEntry; + CMapListEntryTemp *m_pLastMapEntry; + public: CConsole(int FlagMask); ~CConsole(); @@ -161,12 +172,16 @@ public: virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int FlagMask) const; virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp); virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser); + virtual void PossibleMaps(const char *pStr, FPossibleCallback pfnCallback, void *pUser); virtual void ParseArguments(int NumArgs, const char **ppArguments); virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp); virtual void RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp); virtual void DeregisterTemp(const char *pName); virtual void DeregisterTempAll(); + virtual void RegisterTempMap(const char *pName); + virtual void DeregisterTempMap(const char *pName); + virtual void DeregisterTempMapAll(); virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser); virtual void StoreCommands(bool Store); diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h index ee6c4b464..59c815afa 100644 --- a/src/engine/shared/protocol.h +++ b/src/engine/shared/protocol.h @@ -70,6 +70,9 @@ enum NETMSG_PING, NETMSG_PING_REPLY, NETMSG_ERROR, + + NETMSG_MAPLIST_ENTRY_ADD,// todo 0.8: move up + NETMSG_MAPLIST_ENTRY_REM, }; // this should be revised diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index bec0c0c9c..4cc1eaff3 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -47,6 +47,9 @@ CGameConsole::CInstance::CInstance(int Type) else m_CompletionFlagmask = CFGFLAG_SERVER; + m_aCompletionMapBuffer[0] = 0; + m_CompletionMapChosen = -1; + m_aCompletionBuffer[0] = 0; m_CompletionChosen = -1; m_CompletionRenderOffset = 0.0f; @@ -93,6 +96,27 @@ void CGameConsole::CInstance::PossibleCommandsCompleteCallback(const char *pStr, pInstance->m_CompletionEnumerationCount++; } +void CGameConsole::CInstance::PossibleMapsCompleteCallback(const char *pStr, void *pUser) +{ + CGameConsole::CInstance *pInstance = (CGameConsole::CInstance *)pUser; + if(pInstance->m_CompletionMapChosen == pInstance->m_CompletionMapEnumerationCount) + { + // get command + char aBuf[512] = { 0 }; + const char *pSrc = pInstance->GetString(); + unsigned i = 0; + for(; i < sizeof(aBuf) - 2 && *pSrc && *pSrc != ' '; i++, pSrc++) + aBuf[i] = *pSrc; + aBuf[i++] = ' '; + aBuf[i] = 0; + + // add mapname to current command + str_append(aBuf, pStr, sizeof(aBuf)); + pInstance->m_Input.Set(aBuf); + } + pInstance->m_CompletionMapEnumerationCount++; +} + void CGameConsole::CInstance::OnInput(IInput::CEvent Event) { bool Handled = false; @@ -159,6 +183,22 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this); } + + // maplist completion + if(str_comp_nocase_num(GetString(), "sv_map ", 7) == 0 && m_Type != CGameConsole::CONSOLETYPE_LOCAL) + { + m_CompletionMapChosen++; + m_CompletionMapEnumerationCount = 0; + m_pGameConsole->m_pConsole->PossibleMaps(m_aCompletionMapBuffer, PossibleMapsCompleteCallback, this); + + // handle wrapping + if(m_CompletionMapEnumerationCount && m_CompletionMapChosen >= m_CompletionMapEnumerationCount) + { + m_CompletionMapChosen %= m_CompletionMapEnumerationCount; + m_CompletionMapEnumerationCount = 0; + m_pGameConsole->m_pConsole->PossibleMaps(m_aCompletionMapBuffer, PossibleMapsCompleteCallback, this); + } + } } } else if(Event.m_Key == KEY_PAGEUP) @@ -182,6 +222,12 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) { m_CompletionChosen = -1; str_copy(m_aCompletionBuffer, m_Input.GetString(), sizeof(m_aCompletionBuffer)); + + if(str_comp_nocase_num(GetString(), "sv_map ", 7) == 0) + { + m_CompletionMapChosen = -1; + str_copy(m_aCompletionMapBuffer, &m_Input.GetString()[7], sizeof(m_aCompletionBuffer)); + } } // find the current command diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h index e5e4906cf..1a5ce2887 100644 --- a/src/game/client/components/console.h +++ b/src/game/client/components/console.h @@ -23,15 +23,18 @@ class CGameConsole : public CComponent CLineInput m_Input; int m_Type; - int m_CompletionEnumerationCount; int m_BacklogActPage; - public: CGameConsole *m_pGameConsole; + char m_aCompletionMapBuffer[128]; + int m_CompletionMapChosen; + int m_CompletionMapEnumerationCount; + char m_aCompletionBuffer[128]; int m_CompletionChosen; int m_CompletionFlagmask; + int m_CompletionEnumerationCount; float m_CompletionRenderOffset; bool m_IsCommand; @@ -52,6 +55,7 @@ class CGameConsole : public CComponent const char *GetString() const { return m_Input.GetString(); } static void PossibleCommandsCompleteCallback(const char *pStr, void *pUser); + static void PossibleMapsCompleteCallback(const char *pStr, void *pUser); }; class IConsole *m_pConsole;