Add missing files

This commit is contained in:
Nicholas Hastings 2014-08-13 14:26:18 -07:00
parent 17d5af0e2f
commit 486910a9a1
3 changed files with 1441 additions and 0 deletions

551
core/logic/Logger.cpp Normal file
View File

@ -0,0 +1,551 @@
/**
* vim: set ts=4 sw=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include <time.h>
#include <cstdarg>
#include "Logger.h"
#include <sourcemod_version.h>
#include <ISourceMod.h>
Logger g_Logger;
/**
* :TODO: This should be creating the log folder if it doesn't exist
*/
ConfigResult Logger::OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source,
char *error,
size_t maxlength)
{
if (strcasecmp(key, "Logging") == 0)
{
bool state;
if (strcasecmp(value, "on") == 0)
{
state = true;
} else if (strcasecmp(value, "off") == 0) {
state = false;
} else {
smcore.Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
return ConfigResult_Reject;
}
if (source == ConfigSource_Console)
{
state ? EnableLogging() : DisableLogging();
} else {
m_InitialState = state;
}
return ConfigResult_Accept;
} else if (strcasecmp(key, "LogMode") == 0) {
if (strcasecmp(value, "daily") == 0)
{
m_Mode = LoggingMode_Daily;
} else if (strcasecmp(value, "map") == 0) {
m_Mode = LoggingMode_PerMap;
} else if (strcasecmp(value, "game") == 0) {
m_Mode = LoggingMode_Game;
} else {
smcore.Format(error, maxlength, "Invalid value: must be [daily|map|game]");
return ConfigResult_Reject;
}
return ConfigResult_Accept;
}
return ConfigResult_Ignore;
}
void Logger::OnSourceModStartup(bool late)
{
InitLogger(m_Mode);
}
void Logger::OnSourceModAllShutdown()
{
CloseLogger();
}
void Logger::OnSourceModLevelChange(const char *mapName)
{
MapChange(mapName);
}
void Logger::_NewMapFile()
{
if (!m_Active)
{
return;
}
/* Append "Log file closed" to previous log file */
_CloseFile();
char _filename[256];
int i = 0;
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
while (true)
{
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i);
FILE *fp = fopen(_filename, "r");
if (!fp)
{
break;
}
fclose(fp);
i++;
}
m_NrmFileName.assign(_filename);
FILE *fp = fopen(m_NrmFileName.c_str(), "w");
if (!fp)
{
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
LogFatal("[SM] Logging has been disabled.");
m_Active = false;
return;
} else {
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
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, SOURCEMOD_VERSION);
fclose(fp);
}
}
void Logger::_CloseFile()
{
if (!m_Active)
{
return;
}
FILE *fp = NULL;
if (!m_NrmFileName.empty())
{
fp = fopen(m_NrmFileName.c_str(), "r+");
if (fp)
{
fseek(fp, 0, SEEK_END);
LogMessage("Log file closed.");
fclose(fp);
}
m_NrmFileName.clear();
}
if (!m_ErrMapStart)
{
return;
}
fp = fopen(m_ErrFileName.c_str(), "r+");
if (fp)
{
fseek(fp, 0, SEEK_END);
LogError("Error log file session closed.");
fclose(fp);
}
m_ErrFileName.clear();
}
void Logger::InitLogger(LoggingMode mode)
{
m_Mode = mode;
m_Active = m_InitialState;
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
m_NrmCurDay = curtime->tm_mday;
m_ErrCurDay = curtime->tm_mday;
char _filename[256];
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_ErrFileName.assign(_filename);
switch (m_Mode)
{
case LoggingMode_PerMap:
{
if (!m_Active)
{
m_DelayedStart = true;
}
break;
}
case LoggingMode_Daily:
{
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_NrmFileName.assign(_filename);
m_DailyPrintHdr = true;
break;
}
default:
{
/* do nothing... */
break;
}
}
}
void Logger::CloseLogger()
{
_CloseFile();
}
void Logger::LogToOpenFile(FILE *fp, const char *msg, ...)
{
if (!m_Active)
{
return;
}
va_list ap;
va_start(ap, msg);
LogToOpenFileEx(fp, msg, ap);
va_end(ap);
}
void Logger::LogToFileOnly(FILE *fp, const char *msg, ...)
{
if (!m_Active)
{
return;
}
va_list ap;
va_start(ap, msg);
LogToFileOnlyEx(fp, msg, ap);
va_end(ap);
}
void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
{
if (!m_Active)
{
return;
}
static ConVar *sv_logecho = smcore.FindConVar("sv_logecho");
char buffer[3072];
smcore.FormatArgs(buffer, sizeof(buffer), msg, ap);
char date[32];
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: %s\n", date, buffer);
if (!sv_logecho || smcore.GetCvarBool(sv_logecho))
{
static char conBuffer[4096];
smcore.Format(conBuffer, sizeof(conBuffer), "L %s: %s\n", date, buffer);
smcore.ConPrint(conBuffer);
}
}
void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap)
{
if (!m_Active)
{
return;
}
char buffer[3072];
smcore.FormatArgs(buffer, sizeof(buffer), msg, ap);
char date[32];
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: %s\n", date, buffer);
fflush(fp);
}
void Logger::LogMessage(const char *vafmt, ...)
{
va_list ap;
va_start(ap, vafmt);
LogMessageEx(vafmt, ap);
va_end(ap);
}
void Logger::LogMessageEx(const char *vafmt, va_list ap)
{
if (!m_Active)
{
return;
}
if (m_Mode == LoggingMode_Game)
{
_PrintToGameLog(vafmt, ap);
return;
}
if (m_DelayedStart)
{
m_DelayedStart = false;
_NewMapFile();
}
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
FILE *fp = NULL;
if (m_Mode == LoggingMode_PerMap)
{
fp = fopen(m_NrmFileName.c_str(), "a+");
if (!fp)
{
_NewMapFile();
fp = fopen(m_NrmFileName.c_str(), "a+");
if (!fp)
{
goto print_error;
}
}
} else {
if (m_NrmCurDay != curtime->tm_mday)
{
char _filename[256];
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_NrmFileName.assign(_filename);
m_NrmCurDay = curtime->tm_mday;
m_DailyPrintHdr = true;
}
fp = fopen(m_NrmFileName.c_str(), "a+");
}
if (fp)
{
if (m_DailyPrintHdr)
{
char date[32];
m_DailyPrintHdr = false;
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
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, SOURCEMOD_VERSION);
}
LogToOpenFileEx(fp, vafmt, ap);
fclose(fp);
} else {
goto print_error;
}
return;
print_error:
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
LogFatal("[SM] Logging has been disabled.");
m_Active = false;
}
void Logger::LogError(const char *vafmt, ...)
{
va_list ap;
va_start(ap, vafmt);
LogErrorEx(vafmt, ap);
va_end(ap);
}
void Logger::LogErrorEx(const char *vafmt, va_list ap)
{
if (!m_Active)
{
return;
}
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
if (curtime->tm_mday != m_ErrCurDay)
{
char _filename[256];
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_ErrFileName.assign(_filename);
m_ErrCurDay = curtime->tm_mday;
m_ErrMapStart = false;
}
FILE *fp = fopen(m_ErrFileName.c_str(), "a+");
if (fp)
{
if (!m_ErrMapStart)
{
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: SourceMod error session started\n", date);
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);
m_ErrMapStart = true;
}
LogToOpenFileEx(fp, vafmt, ap);
fclose(fp);
}
else
{
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
LogFatal("[SM] Logging has been disabled.");
m_Active = false;
return;
}
}
void Logger::MapChange(const char *mapname)
{
m_CurMapName.assign(mapname);
switch (m_Mode)
{
case LoggingMode_Daily:
{
LogMessage("-------- Mapchange to %s --------", mapname);
break;
}
case LoggingMode_PerMap:
{
_NewMapFile();
break;
}
default:
{
/* Do nothing... */
break;
}
}
if (m_ErrMapStart)
{
LogError("Error log file session closed.");
}
m_ErrMapStart = false;
}
void Logger::_PrintToGameLog(const char *fmt, va_list ap)
{
char msg[3072];
size_t len;
len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
msg[len++] = '\n';
msg[len] = '\0';
Engine_LogPrintWrapper(msg);
}
const char *Logger::GetLogFileName(LogType type) const
{
switch (type)
{
case LogType_Normal:
{
return m_NrmFileName.c_str();
}
case LogType_Error:
{
return m_ErrFileName.c_str();
}
default:
{
return "";
}
}
}
LoggingMode Logger::GetLoggingMode() const
{
return m_Mode;
}
void Logger::EnableLogging()
{
if (m_Active)
{
return;
}
m_Active = true;
LogMessage("[SM] Logging enabled manually by user.");
}
void Logger::DisableLogging()
{
if (!m_Active)
{
return;
}
LogMessage("[SM] Logging disabled manually by user.");
m_Active = false;
}
void Logger::LogFatal(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
LogFatalEx(msg, ap);
va_end(ap);
}
void Logger::LogFatalEx(const char *msg, va_list ap)
{
/* :TODO: make this print all pretty-like
* In fact, the pretty log printing function should be abstracted.
* It's already implemented twice which is bad.
*/
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
FILE *fp = fopen(path, "at");
if (fp)
{
m_Active = true;
LogToOpenFileEx(fp, msg, ap);
m_Active = false;
fclose(fp);
}
}

113
core/logic/Logger.h Normal file
View File

@ -0,0 +1,113 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_CLOGGER_H_
#define _INCLUDE_SOURCEMOD_CLOGGER_H_
#include <stdio.h>
#include <sh_string.h>
#include "common_logic.h"
using namespace SourceHook;
enum LogType
{
LogType_Normal,
LogType_Error
};
enum LoggingMode
{
LoggingMode_Daily,
LoggingMode_PerMap,
LoggingMode_Game
};
class Logger : public SMGlobalClass, public ILogger
{
public:
Logger() : m_Mode(LoggingMode_Daily), m_ErrMapStart(false),
m_Active(false), m_DelayedStart(false), m_DailyPrintHdr(false),
m_InitialState(true)
{
}
public: //SMGlobalClass
ConfigResult OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source,
char *error,
size_t maxlength);
void OnSourceModStartup(bool late);
void OnSourceModAllShutdown();
void OnSourceModLevelChange(const char *mapName);
public:
void InitLogger(LoggingMode mode);
void CloseLogger();
void EnableLogging();
void DisableLogging();
void LogMessage(const char *msg, ...);
void LogMessageEx(const char *msg, va_list ap);
void LogError(const char *msg, ...);
void LogErrorEx(const char *msg, va_list ap);
void LogFatal(const char *msg, ...);
void LogFatalEx(const char *msg, va_list ap);
void LogToOpenFile(FILE *fp, const char *msg, ...);
void LogToOpenFileEx(FILE *fp, const char *msg, va_list ap);
/* This version does not print to console, and is thus thread-safe */
void LogToFileOnly(FILE *fp, const char *msg, ...);
void LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap);
void MapChange(const char *mapname);
const char *GetLogFileName(LogType type) const;
LoggingMode GetLoggingMode() const;
private:
void _CloseFile();
void _NewMapFile();
void _PrintToGameLog(const char *fmt, va_list ap);
private:
String m_NrmFileName;
String m_ErrFileName;
String m_CurMapName;
LoggingMode m_Mode;
int m_NrmCurDay;
int m_ErrCurDay;
bool m_ErrMapStart;
bool m_Active;
bool m_DelayedStart;
bool m_DailyPrintHdr;
bool m_InitialState;
};
void Engine_LogPrintWrapper(const char *msg);
extern Logger g_Logger;
#endif // _INCLUDE_SOURCEMOD_CLOGGER_H_

777
core/logic/smn_core.cpp Normal file
View File

@ -0,0 +1,777 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include "common_logic.h"
#include "Logger.h"
#include <ISourceMod.h>
#include <ITranslator.h>
#include <sourcehook.h>
#include <sh_memory.h>
#if defined PLATFORM_WINDOWS
#include <windows.h>
#elif defined PLATFORM_POSIX
#include <limits.h>
#include <unistd.h>
#include <sys/times.h>
#endif
HandleType_t g_PlIter;
IForward *g_OnLogAction = NULL;
static ConVar *sm_datetime_format = NULL;
class CoreNativeHelpers :
public SMGlobalClass,
public IHandleTypeDispatch
{
public:
void OnSourceModAllInitialized()
{
HandleAccess hacc;
handlesys->InitAccessDefaults(NULL, &hacc);
hacc.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
g_PlIter = handlesys->CreateType("PluginIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_OnLogAction = forwardsys->CreateForward("OnLogAction",
ET_Hook,
5,
NULL,
Param_Cell,
Param_Cell,
Param_Cell,
Param_Cell,
Param_String);
sm_datetime_format = smcore.FindConVar("sm_datetime_format");
}
void OnHandleDestroy(HandleType_t type, void *object)
{
IPluginIterator *iter = (IPluginIterator *)object;
iter->Release();
}
void OnSourceModShutdown()
{
forwardsys->ReleaseForward(g_OnLogAction);
handlesys->RemoveType(g_PlIter, g_pCoreIdent);
}
} g_CoreNativeHelpers;
void LogAction(Handle_t hndl, int type, int client, int target, const char *message)
{
if (g_OnLogAction->GetFunctionCount())
{
cell_t result = 0;
g_OnLogAction->PushCell(hndl);
g_OnLogAction->PushCell(type);
g_OnLogAction->PushCell(client);
g_OnLogAction->PushCell(target);
g_OnLogAction->PushString(message);
g_OnLogAction->Execute(&result);
if (result >= (ResultType)Pl_Handled)
{
return;
}
}
const char *logtag = "SM";
if (type == 2)
{
HandleError err;
IPlugin *pPlugin = scripts->FindPluginByHandle(hndl, &err);
if (pPlugin)
{
logtag = pPlugin->GetFilename();
}
}
g_Logger.LogMessage("[%s] %s", logtag, message);
}
static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
{
char buffer[512];
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
if (pContext->GetLastNativeError() == SP_ERROR_NONE)
{
pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
}
return 0;
}
static cell_t GetTime(IPluginContext *pContext, const cell_t *params)
{
time_t t = g_pSM->GetAdjustedTime();
cell_t *addr;
pContext->LocalToPhysAddr(params[1], &addr);
*(time_t *)addr = t;
return static_cast<cell_t>(t);
}
#if defined SUBPLATFORM_SECURECRT
void _ignore_invalid_parameter(
const wchar_t * expression,
const wchar_t * function,
const wchar_t * file,
unsigned int line,
uintptr_t pReserved
)
{
/* Wow we don't care, thanks Microsoft. */
}
#endif
static cell_t FormatTime(IPluginContext *pContext, const cell_t *params)
{
char *format, *buffer;
pContext->LocalToString(params[1], &buffer);
pContext->LocalToStringNULL(params[3], &format);
if (format == NULL)
{
format = const_cast<char *>(smcore.GetCvarString(sm_datetime_format));
}
#if defined SUBPLATFORM_SECURECRT
_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
#endif
time_t t = (params[4] == -1) ? g_pSM->GetAdjustedTime() : (time_t)params[4];
size_t written = strftime(buffer, params[2], format, localtime(&t));
#if defined SUBPLATFORM_SECURECRT
_set_invalid_parameter_handler(handler);
#endif
if (params[2] && format[0] != '\0' && !written)
{
pContext->ThrowNativeError("Invalid time format or buffer too small");
return 0;
}
return 1;
}
static cell_t GetPluginIterator(IPluginContext *pContext, const cell_t *params)
{
IPluginIterator *iter = scripts->GetPluginIterator();
Handle_t hndl = handlesys->CreateHandle(g_PlIter, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE)
{
iter->Release();
}
return hndl;
}
static cell_t MorePlugins(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IPluginIterator *pIter;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
return pIter->MorePlugins() ? 1 : 0;
}
static cell_t ReadPlugin(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IPluginIterator *pIter;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
IPlugin *pPlugin = pIter->GetPlugin();
if (!pPlugin)
{
return BAD_HANDLE;
}
pIter->NextPlugin();
return pPlugin->GetMyHandle();
}
IPlugin *GetPluginFromHandle(IPluginContext *pContext, Handle_t hndl)
{
if (hndl == BAD_HANDLE)
{
return scripts->FindPluginByContext(pContext->GetContext());
} else {
HandleError err;
IPlugin *pPlugin = scripts->FindPluginByHandle(hndl, &err);
if (!pPlugin)
{
pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
return pPlugin;
}
}
static cell_t GetPluginStatus(IPluginContext *pContext, const cell_t *params)
{
IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
if (!pPlugin)
{
return 0;
}
return pPlugin->GetStatus();
}
static cell_t GetPluginFilename(IPluginContext *pContext, const cell_t *params)
{
IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
if (!pPlugin)
{
return 0;
}
pContext->StringToLocalUTF8(params[2], params[3], pPlugin->GetFilename(), NULL);
return 1;
}
static cell_t IsPluginDebugging(IPluginContext *pContext, const cell_t *params)
{
IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
if (!pPlugin)
{
return 0;
}
return pPlugin->IsDebugging() ? 1 : 0;
}
/* Local to plugins only */
enum PluginInfo
{
PlInfo_Name, /**< Plugin name */
PlInfo_Author, /**< Plugin author */
PlInfo_Description, /**< Plugin description */
PlInfo_Version, /**< Plugin verison */
PlInfo_URL, /**< Plugin URL */
};
static cell_t GetPluginInfo(IPluginContext *pContext, const cell_t *params)
{
IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
if (!pPlugin)
{
return 0;
}
const sm_plugininfo_t *info = pPlugin->GetPublicInfo();
if (!info)
{
return 0;
}
const char *str = NULL;
switch ((PluginInfo)params[2])
{
case PlInfo_Name:
{
str = info->name;
break;
}
case PlInfo_Author:
{
str = info->author;
break;
}
case PlInfo_Description:
{
str = info->description;
break;
}
case PlInfo_Version:
{
str = info->version;
break;
}
case PlInfo_URL:
{
str = info->url;
break;
}
}
if (!str || str[0] == '\0')
{
return 0;
}
pContext->StringToLocalUTF8(params[3], params[4], str, NULL);
return 1;
}
static cell_t SetFailState(IPluginContext *pContext, const cell_t *params)
{
char *str;
SMPlugin *pPlugin;
pContext->LocalToString(params[1], &str);
pPlugin = scripts->FindPluginByContext(pContext->GetContext());
if (params[0] == 1)
{
pPlugin->SetErrorState(Plugin_Error, "%s", str);
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", str);
}
else
{
char buffer[2048];
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
{
pPlugin->SetErrorState(Plugin_Error, "%s", str);
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "Formatting error (%s)", str);
}
else
{
pPlugin->SetErrorState(Plugin_Error, "%s", buffer);
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
}
}
return 0;
}
static cell_t GetSysTickCount(IPluginContext *pContext, const cell_t *params)
{
#if defined PLATFORM_WINDOWS
return (cell_t)GetTickCount();
#elif defined PLATFORM_POSIX
tms tm;
clock_t ticks = times(&tm);
long ticks_per_sec = sysconf(_SC_CLK_TCK);
double fticks = (double)ticks / (double)ticks_per_sec;
fticks *= 1000.0f;
if (fticks > INT_MAX)
{
double r = (int)(fticks / INT_MAX) * (double)INT_MAX;
fticks -= r;
}
return (cell_t)fticks;
#endif
}
static cell_t AutoExecConfig(IPluginContext *pContext, const cell_t *params)
{
SMPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext());
char *cfg, *folder;
pContext->LocalToString(params[2], &cfg);
pContext->LocalToString(params[3], &folder);
if (cfg[0] == '\0')
{
static char temp_str[255];
static char temp_file[PLATFORM_MAX_PATH];
char *ptr;
libsys->GetFileFromPath(temp_str, sizeof(temp_str), plugin->GetFilename());
if ((ptr = strstr(temp_str, ".smx")) != NULL)
{
*ptr = '\0';
}
/* We have the raw filename! */
g_pSM->Format(temp_file, sizeof(temp_file), "plugin.%s", temp_str);
cfg = temp_file;
}
plugin->AddConfig(params[1] ? true : false, cfg, folder);
return 1;
}
static cell_t MarkNativeAsOptional(IPluginContext *pContext, const cell_t *params)
{
char *name;
uint32_t idx;
sp_native_t *native;
pContext->LocalToString(params[1], &name);
if (pContext->FindNativeByName(name, &idx) != SP_ERROR_NONE)
{
/* Oops! This HAS to silently fail! */
return 0;
}
pContext->GetNativeByIndex(idx, &native);
native->flags |= SP_NTVFLAG_OPTIONAL;
return 1;
}
static cell_t RegPluginLibrary(IPluginContext *pContext, const cell_t *params)
{
char *name;
SMPlugin *pl = scripts->FindPluginByContext(pContext->GetContext());
pContext->LocalToString(params[1], &name);
pl->AddLibrary(name);
return 1;
}
static cell_t LibraryExists(IPluginContext *pContext, const cell_t *params)
{
char *str;
pContext->LocalToString(params[1], &str);
if (strcmp(str, "__CanTestFeatures__") == 0)
{
return 1;
}
if (scripts->LibraryExists(str))
{
return 1;
}
if (extsys->LibraryExists(str))
{
return 1;
}
return 0;
}
static cell_t sm_LogAction(IPluginContext *pContext, const cell_t *params)
{
char buffer[2048];
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
{
return 0;
}
IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
LogAction(pPlugin->GetMyHandle(), 2, params[1], params[2], buffer);
return 1;
}
static cell_t LogToFile(IPluginContext *pContext, const cell_t *params)
{
char *file;
pContext->LocalToString(params[1], &file);
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
FILE *fp = fopen(path, "at");
if (!fp)
{
return pContext->ThrowNativeError("Could not open file \"%s\"", path);
}
char buffer[2048];
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
{
fclose(fp);
return 0;
}
IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
g_Logger.LogToOpenFile(fp, "[%s] %s", pPlugin->GetFilename(), buffer);
fclose(fp);
return 1;
}
static cell_t LogToFileEx(IPluginContext *pContext, const cell_t *params)
{
char *file;
pContext->LocalToString(params[1], &file);
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
FILE *fp = fopen(path, "at");
if (!fp)
{
return pContext->ThrowNativeError("Could not open file \"%s\"", path);
}
char buffer[2048];
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
{
fclose(fp);
return 0;
}
g_Logger.LogToOpenFile(fp, "%s", buffer);
fclose(fp);
return 1;
}
static cell_t GetExtensionFileStatus(IPluginContext *pContext, const cell_t *params)
{
char *str;
pContext->LocalToString(params[1], &str);
IExtension *pExtension = extsys->FindExtensionByFile(str);
if (!pExtension)
{
return -2;
}
if (!pExtension->IsLoaded())
{
return -1;
}
char *error;
pContext->LocalToString(params[2], &error);
if (!pExtension->IsRunning(error, params[3]))
{
return 0;
}
return 1;
}
static cell_t FindPluginByNumber(IPluginContext *pContext, const cell_t *params)
{
IPlugin *pPlugin = scripts->FindPluginByOrder(params[1]);
if (pPlugin == NULL)
{
return BAD_HANDLE;
}
return pPlugin->GetMyHandle();
}
static cell_t VerifyCoreVersion(IPluginContext *pContext, const cell_t *params)
{
return 4;
}
static cell_t GetFeatureStatus(IPluginContext *pContext, const cell_t *params)
{
FeatureType type = (FeatureType)params[1];
char *name;
pContext->LocalToString(params[2], &name);
return sharesys->TestFeature(pContext->GetRuntime(), type, name);
}
static cell_t RequireFeature(IPluginContext *pContext, const cell_t *params)
{
FeatureType type = (FeatureType)params[1];
char *name;
pContext->LocalToString(params[2], &name);
if (sharesys->TestFeature(pContext->GetRuntime(), type, name) != FeatureStatus_Available)
{
char buffer[255];
char *msg = buffer;
char default_message[255];
SMPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
if (pContext->GetLastNativeError() != SP_ERROR_NONE || buffer[0] == '\0')
{
g_pSM->Format(default_message, sizeof(default_message), "Feature \"%s\" not available", name);
msg = default_message;
}
pPlugin->SetErrorState(Plugin_Error, "%s", msg);
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", msg);
}
return 1;
}
enum NumberType
{
NumberType_Int8,
NumberType_Int16,
NumberType_Int32
};
//memory addresses below 0x10000 are automatically considered invalid for dereferencing
#define VALID_MINIMUM_MEMORY_ADDRESS 0x10000
static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params)
{
void *addr = reinterpret_cast<void*>(params[1]);
if (addr == NULL)
{
return pContext->ThrowNativeError("Address cannot be null");
}
else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
{
return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
}
NumberType size = static_cast<NumberType>(params[2]);
switch(size)
{
case NumberType_Int8:
return *reinterpret_cast<uint8_t*>(addr);
case NumberType_Int16:
return *reinterpret_cast<uint16_t*>(addr);
case NumberType_Int32:
return *reinterpret_cast<uint32_t*>(addr);
default:
return pContext->ThrowNativeError("Invalid number types %d", size);
}
}
static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params)
{
void *addr = reinterpret_cast<void*>(params[1]);
if (addr == NULL)
{
return pContext->ThrowNativeError("Address cannot be null");
}
else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
{
return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
}
cell_t data = params[2];
NumberType size = static_cast<NumberType>(params[3]);
switch(size)
{
case NumberType_Int8:
SourceHook::SetMemAccess(addr, sizeof(uint8_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
*reinterpret_cast<uint8_t*>(addr) = data;
break;
case NumberType_Int16:
SourceHook::SetMemAccess(addr, sizeof(uint16_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
*reinterpret_cast<uint16_t*>(addr) = data;
break;
case NumberType_Int32:
SourceHook::SetMemAccess(addr, sizeof(uint32_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
*reinterpret_cast<uint32_t*>(addr) = data;
break;
default:
return pContext->ThrowNativeError("Invalid number types %d", size);
}
return 0;
}
REGISTER_NATIVES(coreNatives)
{
{"ThrowError", ThrowError},
{"GetTime", GetTime},
{"FormatTime", FormatTime},
{"GetPluginIterator", GetPluginIterator},
{"MorePlugins", MorePlugins},
{"ReadPlugin", ReadPlugin},
{"GetPluginStatus", GetPluginStatus},
{"GetPluginFilename", GetPluginFilename},
{"IsPluginDebugging", IsPluginDebugging},
{"GetPluginInfo", GetPluginInfo},
{"SetFailState", SetFailState},
{"GetSysTickCount", GetSysTickCount},
{"AutoExecConfig", AutoExecConfig},
{"MarkNativeAsOptional", MarkNativeAsOptional},
{"RegPluginLibrary", RegPluginLibrary},
{"LibraryExists", LibraryExists},
{"LogAction", sm_LogAction},
{"LogToFile", LogToFile},
{"LogToFileEx", LogToFileEx},
{"GetExtensionFileStatus", GetExtensionFileStatus},
{"FindPluginByNumber", FindPluginByNumber},
{"VerifyCoreVersion", VerifyCoreVersion},
{"GetFeatureStatus", GetFeatureStatus},
{"RequireFeature", RequireFeature},
{"LoadFromAddress", LoadFromAddress},
{"StoreToAddress", StoreToAddress},
{NULL, NULL},
};