Fixed crash in SDKHooks when throwing bad ent type error on logical ent (r=psychonic).
3 * =============================================================================
5 * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
33 #include "sourcemod.h"
34 #include "sourcemm_api.h"
35 #include "sm_stringutil.h"
37 #include "LibrarySys.h"
39 #include <sourcemod_version.h>
40 #include "logic_bridge.h"
44 SH_DECL_HOOK1_void(IVEngineServer, LogPrint, SH_NOATTRIB, false, const char *);
47 * :TODO: This should be creating the log folder if it doesn't exist
50 ConfigResult Logger::OnSourceModConfigChanged(const char *key,
56 if (strcasecmp(key, "Logging") == 0)
60 if (strcasecmp(value, "on") == 0)
63 } else if (strcasecmp(value, "off") == 0) {
66 UTIL_Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
67 return ConfigResult_Reject;
70 if (source == ConfigSource_Console)
72 state ? EnableLogging() : DisableLogging();
74 m_InitialState = state;
77 return ConfigResult_Accept;
78 } else if (strcasecmp(key, "LogMode") == 0) {
79 if (strcasecmp(value, "daily") == 0)
81 m_Mode = LoggingMode_Daily;
82 } else if (strcasecmp(value, "map") == 0) {
83 m_Mode = LoggingMode_PerMap;
84 } else if (strcasecmp(value, "game") == 0) {
85 m_Mode = LoggingMode_Game;
87 UTIL_Format(error, maxlength, "Invalid value: must be [daily|map|game]");
88 return ConfigResult_Reject;
91 return ConfigResult_Accept;
94 return ConfigResult_Ignore;
97 static void HookLogPrint(const char *message)
99 g_in_game_log_hook = true;
100 bool stopped = logicore.OnLogPrint(message);
101 g_in_game_log_hook = false;
104 RETURN_META(MRES_SUPERCEDE);
107 void Logger::OnSourceModStartup(bool late)
111 SH_ADD_HOOK(IVEngineServer, LogPrint, engine, SH_STATIC(HookLogPrint), false);
114 void Logger::OnSourceModAllShutdown()
117 SH_REMOVE_HOOK(IVEngineServer, LogPrint, engine, SH_STATIC(HookLogPrint), false);
120 void Logger::OnSourceModLevelChange(const char *mapName)
125 void Logger::_NewMapFile()
132 /* Append "Log file closed" to previous log file */
140 tm *curtime = localtime(&t);
144 g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i);
145 FILE *fp = fopen(_filename, "r");
153 m_NrmFileName.assign(_filename);
155 FILE *fp = fopen(m_NrmFileName.c_str(), "w");
159 g_LibSys.GetPlatformError(error, sizeof(error));
160 LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
161 LogFatal("[SM] Platform returned error: \"%s\"", error);
162 LogFatal("[SM] Logging has been disabled.");
167 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
168 fprintf(fp, "L %s: SourceMod log file started (file \"L%02d%02d%03d.log\") (Version \"%s\")\n", date, curtime->tm_mon + 1, curtime->tm_mday, i, SM_VERSION_STRING);
173 void Logger::_CloseFile()
181 if (!m_NrmFileName.empty())
183 fp = fopen(m_NrmFileName.c_str(), "r+");
186 fseek(fp, 0, SEEK_END);
187 LogMessage("Log file closed.");
190 m_NrmFileName.clear();
197 fp = fopen(m_ErrFileName.c_str(), "r+");
200 fseek(fp, 0, SEEK_END);
201 LogError("Error log file session closed.");
204 m_ErrFileName.clear();
207 void Logger::InitLogger(LoggingMode mode)
210 m_Active = m_InitialState;
214 tm *curtime = localtime(&t);
215 m_NrmCurDay = curtime->tm_mday;
216 m_ErrCurDay = curtime->tm_mday;
219 g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
220 m_ErrFileName.assign(_filename);
224 case LoggingMode_PerMap:
228 m_DelayedStart = true;
232 case LoggingMode_Daily:
234 g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
235 m_NrmFileName.assign(_filename);
236 m_DailyPrintHdr = true;
247 void Logger::CloseLogger()
252 void Logger::LogToOpenFile(FILE *fp, const char *msg, ...)
261 LogToOpenFileEx(fp, msg, ap);
265 void Logger::LogToFileOnly(FILE *fp, const char *msg, ...)
274 LogToFileOnlyEx(fp, msg, ap);
278 void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
285 static ConVar *sv_logecho = icvar->FindVar("sv_logecho");
288 UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
293 tm *curtime = localtime(&t);
294 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
296 fprintf(fp, "L %s: %s\n", date, buffer);
298 if (!sv_logecho || sv_logecho->GetBool())
300 g_SMAPI->ConPrintf("L %s: %s\n", date, buffer);
304 void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap)
312 UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
317 tm *curtime = localtime(&t);
318 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
320 fprintf(fp, "L %s: %s\n", date, buffer);
324 void Logger::LogMessage(const char *vafmt, ...)
328 LogMessageEx(vafmt, ap);
332 void Logger::LogMessageEx(const char *vafmt, va_list ap)
339 if (m_Mode == LoggingMode_Game)
341 _PrintToGameLog(vafmt, ap);
347 m_DelayedStart = false;
353 tm *curtime = localtime(&t);
356 if (m_Mode == LoggingMode_PerMap)
358 fp = fopen(m_NrmFileName.c_str(), "a+");
362 fp = fopen(m_NrmFileName.c_str(), "a+");
369 if (m_NrmCurDay != curtime->tm_mday)
372 g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
373 m_NrmFileName.assign(_filename);
374 m_NrmCurDay = curtime->tm_mday;
375 m_DailyPrintHdr = true;
377 fp = fopen(m_NrmFileName.c_str(), "a+");
385 m_DailyPrintHdr = false;
386 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
387 fprintf(fp, "L %s: SourceMod log file session started (file \"L%04d%02d%02d.log\") (Version \"%s\")\n", date, curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday, SM_VERSION_STRING);
389 LogToOpenFileEx(fp, vafmt, ap);
398 g_LibSys.GetPlatformError(error, sizeof(error));
399 LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
400 LogFatal("[SM] Platform returned error: \"%s\"", error);
401 LogFatal("[SM] Logging has been disabled.");
405 void Logger::LogError(const char *vafmt, ...)
409 LogErrorEx(vafmt, ap);
413 void Logger::LogErrorEx(const char *vafmt, va_list ap)
422 tm *curtime = localtime(&t);
424 if (curtime->tm_mday != m_ErrCurDay)
427 g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
428 m_ErrFileName.assign(_filename);
429 m_ErrCurDay = curtime->tm_mday;
430 m_ErrMapStart = false;
433 FILE *fp = fopen(m_ErrFileName.c_str(), "a+");
439 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
440 fprintf(fp, "L %s: SourceMod error session started\n", date);
441 fprintf(fp, "L %s: Info (map \"%s\") (file \"errors_%04d%02d%02d.log\")\n", date, m_CurMapName.c_str(), curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
442 m_ErrMapStart = true;
444 LogToOpenFileEx(fp, vafmt, ap);
450 g_LibSys.GetPlatformError(error, sizeof(error));
451 LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
452 LogFatal("[SM] Platform returned error: \"%s\"", error);
453 LogFatal("[SM] Logging has been disabled.");
459 void Logger::MapChange(const char *mapname)
461 m_CurMapName.assign(mapname);
465 case LoggingMode_Daily:
467 LogMessage("-------- Mapchange to %s --------", mapname);
470 case LoggingMode_PerMap:
484 LogError("Error log file session closed.");
486 m_ErrMapStart = false;
489 void Logger::_PrintToGameLog(const char *fmt, va_list ap)
494 len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
495 len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
500 Engine_LogPrintWrapper(msg);
503 const char *Logger::GetLogFileName(LogType type) const
509 return m_NrmFileName.c_str();
513 return m_ErrFileName.c_str();
522 LoggingMode Logger::GetLoggingMode() const
527 void Logger::EnableLogging()
534 LogMessage("[SM] Logging enabled manually by user.");
537 void Logger::DisableLogging()
543 LogMessage("[SM] Logging disabled manually by user.");
547 void Logger::LogFatal(const char *msg, ...)
555 void Logger::LogFatalEx(const char *msg, va_list ap)
557 /* :TODO: make this print all pretty-like
558 * In fact, the pretty log printing function should be abstracted.
559 * It's already implemented twice which is bad.
562 char path[PLATFORM_MAX_PATH];
564 g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
566 FILE *fp = fopen(path, "at");
570 LogToOpenFileEx(fp, msg, ap);
576 bool g_in_game_log_hook = false;
578 void Engine_LogPrintWrapper(const char *msg)
580 if (g_in_game_log_hook)
582 ENGINE_CALL(LogPrint)(msg);
586 engine->LogPrint(msg);