dvander@370: /** dvander@370: * =============================================================== dvander@370: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. dvander@370: * =============================================================== dvander@370: * dvander@370: * This file is not open source and may not be copied without explicit dvander@370: * written permission of AlliedModders LLC. This file may not be redistributed dvander@370: * in whole or significant part. dvander@370: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php dvander@370: * dvander@370: * Version: $Id$ dvander@370: */ dvander@370: dvander@160: #include dvander@160: #include "sourcemod.h" dvander@160: #include "sourcemm_api.h" dvander@160: #include "systems/LibrarySys.h" dvander@160: #include "vm/sp_vm_engine.h" dvander@160: #include dvander@176: #include "PluginSys.h" dvander@252: #include "ShareSys.h" faluco@281: #include "CLogger.h" dvander@315: #include "ExtensionSys.h" dvander@340: #include "AdminCache.h" dvander@347: #include "sm_stringutil.h" dvander@448: #include "CPlayerManager.h" dvander@520: #include "CTranslator.h" dvander@531: #include "ForwardSys.h" dvander@160: dvander@209: SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); faluco@333: SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false); dvander@448: SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool); dvander@209: dvander@160: SourcePawnEngine g_SourcePawn; dvander@160: SourceModBase g_SourceMod; dvander@160: dvander@160: ILibrary *g_pJIT = NULL; dvander@160: SourceHook::String g_BaseDir; dvander@160: ISourcePawnEngine *g_pSourcePawn = &g_SourcePawn; dvander@160: IVirtualMachine *g_pVM; dvander@252: IdentityToken_t *g_pCoreIdent = NULL; dvander@448: float g_LastTime = 0.0f; dvander@448: float g_LastAuthCheck = 0.0f; dvander@531: IForward *g_pOnGameFrame = NULL; dvander@160: dvander@160: typedef int (*GIVEENGINEPOINTER)(ISourcePawnEngine *); dvander@160: typedef unsigned int (*GETEXPORTCOUNT)(); dvander@160: typedef IVirtualMachine *(*GETEXPORT)(unsigned int); dvander@160: typedef void (*NOTIFYSHUTDOWN)(); dvander@160: dvander@160: void ShutdownJIT() dvander@160: { dvander@160: NOTIFYSHUTDOWN notify = (NOTIFYSHUTDOWN)g_pJIT->GetSymbolAddress("NotifyShutdown"); dvander@160: if (notify) dvander@160: { dvander@160: notify(); dvander@160: } dvander@160: dvander@160: g_pJIT->CloseLibrary(); dvander@160: } dvander@160: dvander@209: SourceModBase::SourceModBase() dvander@209: { dvander@209: m_IsMapLoading = false; faluco@333: m_ExecPluginReload = false; dvander@209: } dvander@209: dvander@160: bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late) dvander@160: { dvander@160: g_BaseDir.assign(g_SMAPI->GetBaseDir()); dvander@209: g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/addons/sourcemod", g_BaseDir.c_str()); dvander@345: g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), "addons/sourcemod"); dvander@160: dvander@160: /* Attempt to load the JIT! */ dvander@160: char file[PLATFORM_MAX_PATH]; dvander@160: char myerror[255]; dvander@209: g_SMAPI->PathFormat(file, sizeof(file), "%s/bin/sourcepawn.jit.x86.%s", dvander@317: GetSourceModPath(), dvander@160: PLATFORM_LIB_EXT dvander@160: ); dvander@160: dvander@160: g_pJIT = g_LibSys.OpenLibrary(file, myerror, sizeof(myerror)); dvander@160: if (!g_pJIT) dvander@160: { dvander@160: if (error && err_max) dvander@160: { dvander@160: snprintf(error, err_max, "%s (failed to load bin/sourcepawn.jit.x86.%s)", dvander@160: myerror, dvander@160: PLATFORM_LIB_EXT); dvander@160: } dvander@160: return false; dvander@160: } dvander@160: dvander@160: int err; dvander@160: GIVEENGINEPOINTER jit_init = (GIVEENGINEPOINTER)g_pJIT->GetSymbolAddress("GiveEnginePointer"); dvander@160: if (!jit_init) dvander@160: { dvander@160: ShutdownJIT(); dvander@160: if (error && err_max) dvander@160: { dvander@160: snprintf(error, err_max, "Failed to find GiveEnginePointer in JIT!"); dvander@160: } dvander@160: return false; dvander@160: } dvander@160: dvander@160: if ((err=jit_init(g_pSourcePawn)) != 0) dvander@160: { dvander@160: ShutdownJIT(); dvander@160: if (error && err_max) dvander@160: { dvander@160: snprintf(error, err_max, "GiveEnginePointer returned %d in the JIT", err); dvander@160: } dvander@160: return false; dvander@160: } dvander@160: dvander@160: GETEXPORTCOUNT jit_getnum = (GETEXPORTCOUNT)g_pJIT->GetSymbolAddress("GetExportCount"); dvander@160: GETEXPORT jit_get = (GETEXPORT)g_pJIT->GetSymbolAddress("GetExport"); dvander@160: if (!jit_get) dvander@160: { dvander@160: ShutdownJIT(); dvander@160: if (error && err_max) dvander@160: { dvander@160: snprintf(error, err_max, "JIT is missing a necessary export!"); dvander@160: } dvander@160: return false; dvander@160: } dvander@160: dvander@160: unsigned int num = jit_getnum(); dvander@160: if (!num || ((g_pVM=jit_get(0)) == NULL)) dvander@160: { dvander@160: ShutdownJIT(); dvander@160: if (error && err_max) dvander@160: { dvander@160: snprintf(error, err_max, "JIT did not export any virtual machines!"); dvander@160: } dvander@160: return false; dvander@160: } dvander@160: dvander@176: unsigned int api = g_pVM->GetAPIVersion(); dvander@176: if (api != SOURCEPAWN_VM_API_VERSION) dvander@176: { dvander@176: ShutdownJIT(); dvander@176: if (error && err_max) dvander@176: { dvander@176: snprintf(error, err_max, "JIT is not a compatible version"); dvander@176: } dvander@176: return false; dvander@176: } dvander@176: dvander@209: StartSourceMod(late); dvander@176: dvander@160: return true; dvander@160: } dvander@209: dvander@209: void SourceModBase::StartSourceMod(bool late) dvander@209: { dvander@209: /* First initialize the global hooks we need */ dvander@209: SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); faluco@333: SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false); dvander@448: SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false); dvander@209: dvander@496: enginePatch = SH_GET_CALLCLASS(engine); dvander@532: gamedllPatch = SH_GET_CALLCLASS(gamedll); dvander@496: dvander@214: /* Notify! */ dvander@214: SMGlobalClass *pBase = SMGlobalClass::head; dvander@214: while (pBase) dvander@214: { dvander@315: pBase->OnSourceModStartup(false); dvander@214: pBase = pBase->m_pGlobalClassNext; dvander@214: } dvander@214: dvander@252: /* Make the global core identity */ dvander@252: g_pCoreIdent = g_ShareSys.CreateCoreIdentity(); dvander@252: dvander@214: /* Notify! */ dvander@214: pBase = SMGlobalClass::head; dvander@214: while (pBase) dvander@214: { dvander@214: pBase->OnSourceModAllInitialized(); dvander@214: pBase = pBase->m_pGlobalClassNext; dvander@214: } dvander@317: dvander@317: /* Add us now... */ dvander@317: g_ShareSys.AddInterface(NULL, this); dvander@209: } dvander@209: dvander@209: bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) dvander@209: { dvander@209: m_IsMapLoading = true; faluco@333: m_ExecPluginReload = true; dvander@448: g_LastTime = 0.0f; dvander@448: g_LastAuthCheck = 0.0f; dvander@209: faluco@281: g_Logger.MapChange(pMapName); faluco@281: dvander@520: /* Refresh language stuff */ dvander@520: char path[PLATFORM_MAX_PATH]; dvander@520: BuildPath(Path_SM, path, sizeof(path), "configs/languages.cfg"); dvander@520: g_Translator.RebuildLanguageDatabase(path); dvander@520: dvander@209: DoGlobalPluginLoads(); dvander@209: dvander@209: m_IsMapLoading = false; dvander@209: dvander@438: g_Admins.DumpAdminCache(AdminCache_Overrides, true); dvander@438: g_Admins.DumpAdminCache(AdminCache_Groups, true); dvander@340: dvander@531: if (!g_pOnGameFrame) dvander@531: { dvander@531: g_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL); dvander@531: } dvander@531: dvander@209: RETURN_META_VALUE(MRES_IGNORED, true); dvander@209: } dvander@209: dvander@448: void SourceModBase::GameFrame(bool simulating) dvander@448: { dvander@448: /** dvander@448: * Note: This is all hardcoded rather than delegated to save dvander@448: * precious CPU cycles. dvander@448: */ dvander@448: float curtime = gpGlobals->curtime; dvander@448: if (curtime - g_LastTime > 0.1f) dvander@448: { dvander@448: if (m_CheckingAuth dvander@448: && (gpGlobals->curtime - g_LastAuthCheck > 0.7f)) dvander@448: { dvander@448: g_LastAuthCheck = gpGlobals->curtime; dvander@448: g_Players.RunAuthChecks(); dvander@448: } dvander@448: g_LastTime = curtime; dvander@448: } dvander@531: dvander@531: if (g_pOnGameFrame && g_pOnGameFrame->GetFunctionCount()) dvander@531: { dvander@531: g_pOnGameFrame->Execute(NULL); dvander@531: } dvander@448: } dvander@448: faluco@333: void SourceModBase::LevelShutdown() faluco@333: { faluco@333: if (m_ExecPluginReload) faluco@333: { faluco@333: g_PluginSys.ReloadOrUnloadPlugins(); faluco@333: m_ExecPluginReload = false; faluco@333: } faluco@333: } faluco@333: faluco@484: bool SourceModBase::IsMapLoading() const dvander@315: { dvander@315: return m_IsMapLoading; dvander@315: } dvander@315: dvander@209: void SourceModBase::DoGlobalPluginLoads() dvander@209: { dvander@209: char config_path[PLATFORM_MAX_PATH]; dvander@209: char plugins_path[PLATFORM_MAX_PATH]; dvander@209: dvander@317: BuildPath(Path_SM, config_path, dvander@209: sizeof(config_path), dvander@317: "configs/plugin_settings.cfg"); dvander@317: BuildPath(Path_SM, plugins_path, dvander@209: sizeof(plugins_path), dvander@317: "plugins"); dvander@209: dvander@397: /* Load any auto extensions */ dvander@397: g_Extensions.TryAutoload(); dvander@397: dvander@315: /* Run the first pass */ dvander@211: g_PluginSys.LoadAll_FirstPass(config_path, plugins_path); dvander@213: dvander@315: /* Mark any extensions as loaded */ dvander@315: g_Extensions.MarkAllLoaded(); dvander@315: dvander@213: /* No modules yet, it's safe to call this from here */ dvander@213: g_PluginSys.LoadAll_SecondPass(); dvander@315: dvander@315: /* Re-mark any extensions as loaded */ dvander@315: g_Extensions.MarkAllLoaded(); dvander@211: } dvander@211: dvander@317: size_t SourceModBase::BuildPath(PathType type, char *buffer, size_t maxlength, char *format, ...) dvander@317: { dvander@317: char _buffer[PLATFORM_MAX_PATH+1]; dvander@317: va_list ap; dvander@317: dvander@317: va_start(ap, format); dvander@317: vsnprintf(_buffer, PLATFORM_MAX_PATH, format, ap); dvander@317: va_end(ap); dvander@317: dvander@317: const char *base = NULL; dvander@317: if (type == Path_Game) dvander@317: { dvander@317: base = GetModPath(); dvander@317: } else if (type == Path_SM) { dvander@317: base = GetSourceModPath(); dvander@345: } else if (type == Path_SM_Rel) { dvander@345: base = m_SMRelDir; dvander@317: } dvander@317: dvander@317: if (base) dvander@317: { dvander@317: return g_LibSys.PathFormat(buffer, maxlength, "%s/%s", base, _buffer); dvander@317: } else { dvander@317: return g_LibSys.PathFormat(buffer, maxlength, "%s", _buffer); dvander@317: } dvander@317: } dvander@317: faluco@289: void SourceModBase::CloseSourceMod() faluco@289: { dvander@531: if (g_pOnGameFrame) dvander@531: { dvander@531: g_Forwards.ReleaseForward(g_pOnGameFrame); dvander@531: } dvander@531: faluco@289: /* Notify! */ faluco@289: SMGlobalClass *pBase = SMGlobalClass::head; faluco@289: while (pBase) faluco@289: { faluco@289: pBase->OnSourceModShutdown(); faluco@289: pBase = pBase->m_pGlobalClassNext; faluco@289: } faluco@289: faluco@484: /* Delete all data packs */ faluco@484: CStack::iterator iter; faluco@484: CDataPack *pd; faluco@484: for (iter=m_freepacks.begin(); iter!=m_freepacks.end(); iter++) faluco@484: { faluco@484: pd = (*iter); faluco@484: delete pd; faluco@484: } faluco@484: m_freepacks.popall(); faluco@484: faluco@289: /* Notify! */ faluco@289: pBase = SMGlobalClass::head; faluco@289: while (pBase) faluco@289: { faluco@289: pBase->OnSourceModAllShutdown(); faluco@289: pBase = pBase->m_pGlobalClassNext; faluco@289: } faluco@289: dvander@496: if (enginePatch) dvander@496: { dvander@496: SH_RELEASE_CALLCLASS(enginePatch); dvander@496: enginePatch = NULL; dvander@496: } dvander@496: dvander@532: if (gamedllPatch) dvander@532: { dvander@532: SH_RELEASE_CALLCLASS(gamedllPatch); dvander@532: gamedllPatch = NULL; dvander@532: } dvander@532: faluco@333: SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); faluco@333: SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false); dvander@448: SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false); faluco@333: faluco@289: /* Rest In Peace */ faluco@289: ShutdownJIT(); faluco@289: } faluco@289: dvander@317: void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...) dvander@317: { dvander@317: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@317: const char *tag = pAPI->GetExtensionTag(); dvander@317: char buffer[2048]; dvander@317: va_list ap; dvander@317: dvander@317: va_start(ap, format); dvander@317: vsnprintf(buffer, sizeof(buffer), format, ap); dvander@317: va_end(ap); dvander@317: dvander@317: if (tag) dvander@317: { dvander@317: g_Logger.LogMessage("[%s] %s", tag, buffer); dvander@317: } else { dvander@317: g_Logger.LogMessage("%s", buffer); dvander@317: } dvander@317: } dvander@317: dvander@317: void SourceModBase::LogError(IExtension *pExt, const char *format, ...) dvander@317: { dvander@317: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@317: const char *tag = pAPI->GetExtensionTag(); dvander@317: char buffer[2048]; dvander@317: va_list ap; dvander@317: dvander@317: va_start(ap, format); dvander@317: vsnprintf(buffer, sizeof(buffer), format, ap); dvander@317: va_end(ap); dvander@317: dvander@317: if (tag) dvander@317: { dvander@317: g_Logger.LogError("[%s] %s", tag, buffer); dvander@317: } else { dvander@317: g_Logger.LogError("%s", buffer); dvander@317: } dvander@317: } dvander@317: dvander@347: size_t SourceModBase::FormatString(char *buffer, size_t maxlength, IPluginContext *pContext, const cell_t *params, unsigned int param) dvander@347: { dvander@347: char *fmt; dvander@347: dvander@347: pContext->LocalToString(params[param], &fmt); dvander@347: dvander@347: int lparam = ++param; dvander@347: dvander@347: return atcprintf(buffer, maxlength, fmt, pContext, params, &lparam); dvander@347: } dvander@347: faluco@484: const char *SourceModBase::GetSourceModPath() const dvander@209: { dvander@209: return m_SMBaseDir; dvander@209: } dvander@211: faluco@484: const char *SourceModBase::GetModPath() const dvander@255: { dvander@255: return g_BaseDir.c_str(); dvander@255: } dvander@255: faluco@422: void SourceModBase::SetGlobalTarget(unsigned int index) faluco@422: { faluco@422: m_target = index; faluco@422: } faluco@422: dvander@448: void SourceModBase::SetAuthChecking(bool set) dvander@448: { dvander@448: m_CheckingAuth = set; dvander@448: } dvander@448: faluco@422: unsigned int SourceModBase::GetGlobalTarget() const faluco@422: { faluco@422: return m_target; faluco@422: } faluco@422: faluco@484: IDataPack *SourceModBase::CreateDataPack() faluco@484: { faluco@484: CDataPack *pack; faluco@484: if (m_freepacks.empty()) faluco@484: { faluco@484: pack = new CDataPack; faluco@484: } else { faluco@484: pack = m_freepacks.front(); faluco@484: m_freepacks.pop(); faluco@484: pack->Initialize(); faluco@484: } faluco@484: return pack; faluco@484: } faluco@484: faluco@484: void SourceModBase::FreeDataPack(IDataPack *pack) faluco@484: { faluco@484: m_freepacks.push(static_cast(pack)); faluco@484: } faluco@484: faluco@484: Handle_t SourceModBase::GetDataPackHandleType(bool readonly) faluco@484: { faluco@484: //:TODO: faluco@484: return 0; faluco@484: } faluco@484: dvander@211: SMGlobalClass *SMGlobalClass::head = NULL; dvander@211: dvander@211: SMGlobalClass::SMGlobalClass() dvander@211: { dvander@211: m_pGlobalClassNext = SMGlobalClass::head; dvander@211: SMGlobalClass::head = this; dvander@211: }