From c75d607a00e578c53daff7f1deee8b453092fa81 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Sun, 13 Jul 2008 05:13:37 +0000 Subject: [PATCH] - Added ForceChangeLevel and Map History to nextmap api - Changed base plugins to use new api - Added sm_maphistory command to nextmap.sp --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%402413 --- core/NextMap.cpp | 135 ++++++++++++++++++++++++++++++++++++ core/NextMap.h | 41 +++++++++++ core/smn_nextmap.cpp | 54 ++++++++++++++- plugins/basecommands/map.sp | 2 +- plugins/basetriggers.sp | 6 +- plugins/basevotes.sp | 2 +- plugins/include/nextmap.inc | 32 ++++++++- plugins/mapchooser.sp | 2 +- plugins/nextmap.sp | 64 +++++++++++++++++ plugins/rockthevote.sp | 2 +- 10 files changed, 329 insertions(+), 11 deletions(-) diff --git a/core/NextMap.cpp b/core/NextMap.cpp index 3c2c0b1e..b2fffe48 100644 --- a/core/NextMap.cpp +++ b/core/NextMap.cpp @@ -33,13 +33,26 @@ #include "Logger.h" #include "sourcemm_api.h" #include "sm_stringutil.h" +#include "sourcehook.h" +#include "sm_srvcmds.h" NextMapManager g_NextMap; SH_DECL_HOOK2_void(IVEngineServer, ChangeLevel, SH_NOATTRIB, 0, const char *, const char *); +#if defined ORANGEBOX_BUILD +SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); +#else +extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0); +extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0); +#endif + +ConCommand *changeLevelCmd = NULL; + ConVar sm_nextmap("sm_nextmap", "", FCVAR_NOTIFY); +bool g_forcedChange = false; + void NextMapManager::OnSourceModAllInitialized_Post() { #if defined ORANGEBOX_BUILD @@ -47,6 +60,30 @@ void NextMapManager::OnSourceModAllInitialized_Post() #else SH_ADD_HOOK_MEMFUNC(IVEngineServer, ChangeLevel, engine, this, &NextMapManager::HookChangeLevel, false); #endif + + ConCommandBase *pBase = icvar->GetCommands(); + ConCommand *pCmd = NULL; + while (pBase) + { + if (strcmp(pBase->GetName(), "changelevel") == 0) + { + /* Don't want to return convar with same name */ + if (!pBase->IsCommand()) + { + break; + } + + pCmd = (ConCommand *)pBase; + break; + } + pBase = const_cast(pBase->GetNext()); + } + + if (pCmd != NULL) + { + SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CmdChangeLevelCallback, false); + changeLevelCmd = pCmd; + } } void NextMapManager::OnSourceModShutdown() @@ -56,6 +93,20 @@ void NextMapManager::OnSourceModShutdown() #else SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, ChangeLevel, engine, this, &NextMapManager::HookChangeLevel, false); #endif + + if (changeLevelCmd != NULL) + { + SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, changeLevelCmd, CmdChangeLevelCallback, false); + } + + SourceHook::List::iterator iter; + iter = m_mapHistory.begin(); + + while (iter != m_mapHistory.end()) + { + delete (MapChangeData *)*iter; + iter = m_mapHistory.erase(iter); + } } const char *NextMapManager::GetNextMap() @@ -77,6 +128,12 @@ bool NextMapManager::SetNextMap(const char *map) void NextMapManager::HookChangeLevel(const char *map, const char *unknown) { + if (g_forcedChange) + { + g_Logger.LogMessage("[SM] Changed map to \"%s\"", map); + RETURN_META(MRES_IGNORED); + } + const char *newmap = sm_nextmap.GetString(); if (newmap[0] == 0 || !engine->IsMapValid(newmap)) @@ -86,5 +143,83 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown) g_Logger.LogMessage("[SM] Changed map to \"%s\"", newmap); + UTIL_Format(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), newmap); + UTIL_Format(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), "Normal level change"); + RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::ChangeLevel, (newmap, unknown)); } + +void NextMapManager::OnSourceModLevelChange( const char *mapName ) +{ + /* Skip the first 'mapchange' when the server starts up */ + if (m_tempChangeInfo.startTime != 0) + { + if (strcmp(mapName, m_tempChangeInfo.m_mapName) == 0) + { + /* The map change was as we expected */ + m_mapHistory.push_back(new MapChangeData(lastMap, m_tempChangeInfo.m_changeReason, m_tempChangeInfo.startTime)); + } + else + { + /* Something intercepted the mapchange */ + char newReason[255]; + UTIL_Format(newReason, sizeof(newReason), "%s (Map overridden)", m_tempChangeInfo.m_changeReason); + m_mapHistory.push_back(new MapChangeData(lastMap, newReason, m_tempChangeInfo.startTime)); + } + + /* TODO: Should this be customizable? */ + if (m_mapHistory.size() > 20) + { + SourceHook::List::iterator iter; + iter = m_mapHistory.begin(); + + delete (MapChangeData *)*iter; + + m_mapHistory.erase(iter); + } + } + + m_tempChangeInfo.m_mapName[0] ='\0'; + m_tempChangeInfo.m_changeReason[0] = '\0'; + m_tempChangeInfo.startTime = time(NULL); + UTIL_Format(lastMap, sizeof(lastMap), mapName); +} + +void NextMapManager::ForceChangeLevel( const char *mapName, const char* changeReason ) +{ + /* Store the mapname and reason */ + UTIL_Format(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), mapName); + UTIL_Format(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), changeReason); + + /* Change level and skip our hook */ + g_forcedChange = true; + engine->ChangeLevel(mapName, NULL); + g_forcedChange = false; +} + +NextMapManager::NextMapManager() +{ + m_tempChangeInfo = MapChangeData(); + m_mapHistory = SourceHook::List(); +} + +#if defined ORANGEBOX_BUILD +void CmdChangeLevelCallback(const CCommand &command) +{ +#else +void CmdChangeLevelCallback() +{ + CCommand command; +#endif + + if (command.ArgC() < 2) + { + return; + } + + if (g_NextMap.m_tempChangeInfo.m_mapName[0] == '\0') + { + UTIL_Format(g_NextMap.m_tempChangeInfo.m_mapName, sizeof(g_NextMap.m_tempChangeInfo.m_mapName), command.Arg(1)); + UTIL_Format(g_NextMap.m_tempChangeInfo.m_changeReason, sizeof(g_NextMap.m_tempChangeInfo.m_changeReason), "changelevel Command"); + } +} \ No newline at end of file diff --git a/core/NextMap.h b/core/NextMap.h index a7ae75ba..8f6377ce 100644 --- a/core/NextMap.h +++ b/core/NextMap.h @@ -34,17 +34,58 @@ #include "sm_globals.h" #include "eiface.h" +#include "sh_list.h" +#include "sm_stringutil.h" + +struct MapChangeData +{ + MapChangeData(const char *mapName, const char *changeReason, time_t time) + { + UTIL_Format(m_mapName, sizeof(m_mapName), mapName); + UTIL_Format(m_changeReason, sizeof(m_changeReason), changeReason); + startTime = time; + } + + MapChangeData() + { + m_mapName[0] = '\0'; + m_changeReason[0] = '\0'; + startTime = 0; + } + + char m_mapName[32]; + char m_changeReason[100]; + time_t startTime; +}; class NextMapManager : public SMGlobalClass { public: + NextMapManager(); + +#if defined ORANGEBOX_BUILD + friend void CmdChangeLevelCallback(const CCommand &command); +#else + friend void CmdChangeLevelCallback(); +#endif + void OnSourceModAllInitialized_Post(); void OnSourceModShutdown(); + void OnSourceModLevelChange(const char *mapName); const char *GetNextMap(); bool SetNextMap(const char *map); + void ForceChangeLevel(const char *mapName, const char* changeReason); + void HookChangeLevel(const char *map, const char *unknown); + +public: + SourceHook::List m_mapHistory; + +private: + MapChangeData m_tempChangeInfo; + char lastMap[32]; }; extern NextMapManager g_NextMap; diff --git a/core/smn_nextmap.cpp b/core/smn_nextmap.cpp index 0140a573..ef6f4ab2 100644 --- a/core/smn_nextmap.cpp +++ b/core/smn_nextmap.cpp @@ -54,10 +54,58 @@ static cell_t sm_SetNextMap(IPluginContext *pCtx, const cell_t *params) return g_NextMap.SetNextMap(map); } +static cell_t sm_ForceChangeLevel(IPluginContext *pCtx, const cell_t *params) +{ + char *map; + char *changeReason; + + pCtx->LocalToString(params[1], &map); + pCtx->LocalToString(params[2], &changeReason); + + g_NextMap.ForceChangeLevel(map, changeReason); + + return 0; +} + +static cell_t sm_GetMapHistorySize(IPluginContext *pCtx, const cell_t *params) +{ + return g_NextMap.m_mapHistory.size(); +} + +static cell_t sm_GetMapHistory(IPluginContext *pCtx, const cell_t *params) +{ + if (params[1] < 0 || params[1] >= (int)g_NextMap.m_mapHistory.size()) + { + return pCtx->ThrowNativeError("Invalid Map History Index"); + } + + SourceHook::List::iterator iter; + iter = g_NextMap.m_mapHistory.end(); + iter--; + + for (int i=0; iStringToLocal(params[2], params[3], data->m_mapName); + pCtx->StringToLocal(params[4], params[5], data->m_changeReason); + + cell_t *startTime; + pCtx->LocalToPhysAddr(params[6], &startTime); + *startTime = data->startTime; + + return 0; +} REGISTER_NATIVES(nextmapnatives) { - {"GetNextMap", sm_GetNextMap}, - {"SetNextMap", sm_SetNextMap}, - {NULL, NULL}, + {"GetNextMap", sm_GetNextMap}, + {"SetNextMap", sm_SetNextMap}, + {"ForceChangeLevel", sm_ForceChangeLevel}, + {"GetMapHistorySize", sm_GetMapHistorySize}, + {"GetMapHistory", sm_GetMapHistory}, + {NULL, NULL}, }; diff --git a/plugins/basecommands/map.sp b/plugins/basecommands/map.sp index 1e003c78..9415d0d8 100644 --- a/plugins/basecommands/map.sp +++ b/plugins/basecommands/map.sp @@ -109,7 +109,7 @@ public Action:Timer_ChangeMap(Handle:timer, Handle:dp) ResetPack(dp); ReadPackString(dp, map, sizeof(map)); - ServerCommand("changelevel \"%s\"", map); + ForceChangeLevel(map, "sm_map Command"); return Plugin_Stop; } diff --git a/plugins/basetriggers.sp b/plugins/basetriggers.sp index c01eeb32..4d731fbb 100644 --- a/plugins/basetriggers.sp +++ b/plugins/basetriggers.sp @@ -54,9 +54,9 @@ new Handle:g_Cvar_WinLimit = INVALID_HANDLE; new Handle:g_Cvar_FragLimit = INVALID_HANDLE; new Handle:g_Cvar_MaxRounds = INVALID_HANDLE; -#define TIMELEFT_ALL_ALWAYS 0 -#define TIMELEFT_ALL_MAYBE 1 -#define TIMELEFT_ONE 2 +#define TIMELEFT_ALL_ALWAYS 0 /* Print to all players */ +#define TIMELEFT_ALL_MAYBE 1 /* Print to all players if sm_trigger_show allows */ +#define TIMELEFT_ONE 2 /* Print to a single player */ public OnPluginStart() { diff --git a/plugins/basevotes.sp b/plugins/basevotes.sp index cc2e3462..42157a90 100644 --- a/plugins/basevotes.sp +++ b/plugins/basevotes.sp @@ -424,7 +424,7 @@ public Action:Timer_ChangeMap(Handle:timer, Handle:dp) ResetPack(dp); ReadPackString(dp, mapname, sizeof(mapname)); - ServerCommand("changelevel \"%s\"", mapname); + ForceChangeLevel(mapname, "sm_votemap Result"); return Plugin_Stop; } diff --git a/plugins/include/nextmap.inc b/plugins/include/nextmap.inc index e811ae52..ec085824 100644 --- a/plugins/include/nextmap.inc +++ b/plugins/include/nextmap.inc @@ -51,4 +51,34 @@ native bool:SetNextMap(const String:map[]); * @param maxlen Maximum length of the map buffer. * @return True if a Map was found and copied, false if no nextmap is set (map will be unchanged). */ -native bool:GetNextMap(String:map[], maxlen); \ No newline at end of file +native bool:GetNextMap(String:map[], maxlen); + +/** + * Changes the current map and records the reason for the change with maphistory + * + * @param map Map to change to. + * @param reason Reason for change. + * @noreturn + */ +native ForceChangeLevel(const String:map[], const String:reason[]); + +/** + * Gets the current number of maps in the map history + * + * @return Number of maps. + */ +native GetMapHistorySize(); + +/** + * Retrieves a map from the map history list. + * + * @param item Item number. Must be 0 or greater and less than GetMapHistorySize(). + * @param map Buffer to store the map name. + * @param mapLen Length of map buffer. + * @param reason Buffer to store the change reason. + * @param reasonLen Length of the reason buffer. + * @param startTime Time the map started. + * @noreturn + * @error Invalid item number. + */ +native GetMapHistory(item, String:map[], mapLen, String:reason[], reasonLen, &startTime); \ No newline at end of file diff --git a/plugins/mapchooser.sp b/plugins/mapchooser.sp index 395a93a6..65beec70 100644 --- a/plugins/mapchooser.sp +++ b/plugins/mapchooser.sp @@ -771,7 +771,7 @@ public Action:Timer_ChangeMap(Handle:hTimer, Handle:dp) ReadPackString(dp, map, sizeof(map)); } - ServerCommand("changelevel \"%s\"", map); + ForceChangeLevel(map, "Map Vote"); return Plugin_Stop; } diff --git a/plugins/nextmap.sp b/plugins/nextmap.sp index f4c55d31..6f76a184 100644 --- a/plugins/nextmap.sp +++ b/plugins/nextmap.sp @@ -51,6 +51,8 @@ public Plugin:myinfo = new g_MapPos = -1; new Handle:g_MapList = INVALID_HANDLE; new g_MapListSerial = -1; + +new g_CurrentMapStartTime; public OnPluginStart() { @@ -65,6 +67,7 @@ public OnPluginStart() RegConsoleCmd("nextmap", Command_Nextmap); RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + RegAdminCmd("sm_maphistory", Command_MapHistory, ADMFLAG_CHANGEMAP, "Shows the most recent maps played"); RegConsoleCmd("listmaps", Command_List); // Set to the current map so OnMapStart() will know what to do @@ -72,6 +75,11 @@ public OnPluginStart() GetCurrentMap(currentMap, 64); SetNextMap(currentMap); } + +public OnMapStart() +{ + g_CurrentMapStartTime = GetTime(); +} public OnConfigsExecuted() { @@ -228,3 +236,59 @@ FindAndSetNextMap() GetArrayString(g_MapList, g_MapPos, mapName, sizeof(mapName)); SetNextMap(mapName); } + +public Action:Command_MapHistory(client, args) +{ + new mapCount = GetMapHistorySize(); + + decl String:mapName[32]; + decl String:changeReason[100]; + decl String:timeString[100]; + decl String:playedTime[100]; + new startTime; + + new lastMapStartTime = g_CurrentMapStartTime; + + PrintToConsole(client, "Map History:\n"); + PrintToConsole(client, "Map : Started : Played Time : Reason for ending"); + + GetCurrentMap(mapName, sizeof(mapName)); + PrintToConsole(client, "%02i. %s (Current Map)", 0, mapName); + + for (new i=0; i 0) + { + Format(buffer, maxlen, "%id %ih %im", days, hours, (seconds >= 30) ? minutes+1 : minutes); + } + else if (hours > 0) + { + Format(buffer, maxlen, "%ih %im", hours, (seconds >= 30) ? minutes+1 : minutes); + } + else if (minutes > 0) + { + Format(buffer, maxlen, "%im", (seconds >= 30) ? minutes+1 : minutes); + } + else + { + Format(buffer, maxlen, "%is", seconds); + } +} \ No newline at end of file diff --git a/plugins/rockthevote.sp b/plugins/rockthevote.sp index d05364d9..9ca69668 100644 --- a/plugins/rockthevote.sp +++ b/plugins/rockthevote.sp @@ -298,7 +298,7 @@ public Action:Timer_ChangeMap(Handle:hTimer) new String:map[65]; if (GetNextMap(map, sizeof(map))) { - ServerCommand("changelevel \"%s\"", map); + ForceChangeLevel(map, "RTV after mapvote"); } return Plugin_Stop;