Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.
(This day shall henceforth be known as the Eighty Column Massacre of '07)
3 * =============================================================================
5 * Copyright (C) 2004-2007 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 "systems/LibrarySys.h"
36 #include "vm/sp_vm_engine.h"
37 #include <sh_string.h>
38 #include "PluginSys.h"
40 #include "CoreConfig.h"
42 #include "ExtensionSys.h"
43 #include "AdminCache.h"
44 #include "sm_stringutil.h"
45 #include "PlayerManager.h"
46 #include "Translator.h"
47 #include "ForwardSys.h"
49 #include "MenuStyle_Valve.h"
50 #include "MenuStyle_Radio.h"
53 SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
54 SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
55 SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool);
56 SH_DECL_HOOK1_void(IVEngineServer, ServerCommand, SH_NOATTRIB, false, const char *);
58 SourcePawnEngine g_SourcePawn;
59 SourceModBase g_SourceMod;
61 ILibrary *g_pJIT = NULL;
62 SourceHook::String g_BaseDir;
63 ISourcePawnEngine *g_pSourcePawn = &g_SourcePawn;
64 IVirtualMachine *g_pVM;
65 IdentityToken_t *g_pCoreIdent = NULL;
66 float g_LastTime = 0.0f;
67 float g_LastMenuTime = 0.0f;
68 float g_LastAuthCheck = 0.0f;
69 IForward *g_pOnGameFrame = NULL;
70 IForward *g_pOnMapEnd = NULL;
71 bool g_Loaded = false;
72 int g_StillFrames = 0;
73 float g_StillTime = 0.0f;
75 typedef int (*GIVEENGINEPOINTER)(ISourcePawnEngine *);
76 typedef int (*GIVEENGINEPOINTER2)(ISourcePawnEngine *, unsigned int api_version);
77 typedef unsigned int (*GETEXPORTCOUNT)();
78 typedef IVirtualMachine *(*GETEXPORT)(unsigned int);
79 typedef void (*NOTIFYSHUTDOWN)();
83 NOTIFYSHUTDOWN notify = (NOTIFYSHUTDOWN)g_pJIT->GetSymbolAddress("NotifyShutdown");
89 g_pJIT->CloseLibrary();
92 SourceModBase::SourceModBase()
94 m_IsMapLoading = false;
95 m_ExecPluginReload = false;
96 m_ExecOnMapEnd = false;
97 m_GotBasePath = false;
100 ConfigResult SourceModBase::OnSourceModConfigChanged(const char *key,
106 if (strcasecmp(key, "BasePath") == 0)
108 if (source == ConfigSource_Console)
110 UTIL_Format(error, maxlength, "Cannot be set at runtime");
111 return ConfigResult_Reject;
116 g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/%s", g_BaseDir.c_str(), value);
117 g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), value);
119 m_GotBasePath = true;
122 return ConfigResult_Accept;
125 return ConfigResult_Ignore;
128 bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late)
130 const char *gamepath = g_SMAPI->GetBaseDir();
132 /* Store full path to game */
133 g_BaseDir.assign(gamepath);
135 /* Store name of game directory by itself */
136 size_t len = strlen(gamepath);
137 for (size_t i = len - 1; i >= 0; i--)
139 if (gamepath[i] == PLATFORM_SEP_CHAR)
141 strncopy(m_ModDir, &gamepath[++i], sizeof(m_ModDir));
146 /* Initialize CoreConfig so we can get SourceMod base path properly - this basically parses core.cfg */
147 g_CoreConfig.Initialize();
149 /* This shouldn't happen, but can't hurt to be safe */
150 if (!g_LibSys.PathExists(m_SMBaseDir) || !m_GotBasePath)
152 g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/addons/sourcemod", g_BaseDir.c_str());
153 g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), "addons/sourcemod");
154 m_GotBasePath = true;
157 /* Attempt to load the JIT! */
158 char file[PLATFORM_MAX_PATH];
160 g_SMAPI->PathFormat(file, sizeof(file), "%s/bin/sourcepawn.jit.x86.%s",
165 g_pJIT = g_LibSys.OpenLibrary(file, myerror, sizeof(myerror));
168 if (error && maxlength)
170 snprintf(error, maxlength, "%s (failed to load bin/sourcepawn.jit.x86.%s)",
179 GIVEENGINEPOINTER2 jit_init2 = (GIVEENGINEPOINTER2)g_pJIT->GetSymbolAddress("GiveEnginePointer2");
182 GIVEENGINEPOINTER jit_init = (GIVEENGINEPOINTER)g_pJIT->GetSymbolAddress("GiveEnginePointer");
186 if (error && maxlength)
188 snprintf(error, maxlength, "Failed to find GiveEnginePointer in JIT!");
193 if ((err=jit_init(g_pSourcePawn)) != 0)
196 if (error && maxlength)
198 snprintf(error, maxlength, "GiveEnginePointer returned %d in the JIT", err);
203 /* On version bumps, we should check for older versions as well, if the new version fails.
204 * We can then check the exports to see if any VM versions will be sufficient.
206 if ((err=jit_init2(g_pSourcePawn, SOURCEPAWN_ENGINE_API_VERSION)) != SP_ERROR_NONE)
209 if (error && maxlength)
211 snprintf(error, maxlength, "JIT incompatible with SourceMod version");
217 GETEXPORTCOUNT jit_getnum = (GETEXPORTCOUNT)g_pJIT->GetSymbolAddress("GetExportCount");
218 GETEXPORT jit_get = (GETEXPORT)g_pJIT->GetSymbolAddress("GetExport");
222 if (error && maxlength)
224 snprintf(error, maxlength, "JIT is missing a necessary export!");
229 unsigned int num = jit_getnum();
233 if (error && maxlength)
235 snprintf(error, maxlength, "JIT did not export any virtual machines!");
240 unsigned int api_version;
241 for (unsigned int i=0; i<num; i++)
243 if ((g_pVM=jit_get(i)) == NULL)
245 if (error && maxlength)
247 snprintf(error, maxlength, "JIT did not export any virtual machines!");
251 /* Refuse any API that we might not be able to deal with.
252 * Also refuse anything < 3 because we need fake natives.
254 api_version = g_pVM->GetAPIVersion();
255 if (api_version < 3 || api_version > SOURCEPAWN_VM_API_VERSION)
257 if (error && maxlength)
259 snprintf(error, maxlength, "JIT is not a compatible version");
273 /* Hook this now so we can detect startup without calling StartSourceMod() */
274 SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
276 /* Only load if we're not late */
279 StartSourceMod(false);
285 void SourceModBase::StartSourceMod(bool late)
287 SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false);
288 SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false);
290 enginePatch = SH_GET_CALLCLASS(engine);
291 gamedllPatch = SH_GET_CALLCLASS(gamedll);
294 SMGlobalClass *pBase = SMGlobalClass::head;
297 pBase->OnSourceModStartup(false);
298 pBase = pBase->m_pGlobalClassNext;
301 /* Make the global core identity */
302 g_pCoreIdent = g_ShareSys.CreateCoreIdentity();
305 pBase = SMGlobalClass::head;
308 pBase->OnSourceModAllInitialized();
309 pBase = pBase->m_pGlobalClassNext;
313 g_ShareSys.AddInterface(NULL, this);
319 static bool g_LevelEndBarrier = false;
320 bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
322 /* If we're not loaded... */
325 /* Do all global initialization now */
326 StartSourceMod(true);
329 m_IsMapLoading = true;
330 m_ExecPluginReload = true;
331 m_ExecOnMapEnd = true;
333 g_LastMenuTime = 0.0f;
334 g_LastAuthCheck = 0.0f;
335 g_SimTicks.ticking = true;
336 g_SimTicks.tickcount = 0;
341 SMGlobalClass *pBase = SMGlobalClass::head;
344 pBase->OnSourceModLevelChange(pMapName);
345 pBase = pBase->m_pGlobalClassNext;
348 DoGlobalPluginLoads();
350 m_IsMapLoading = false;
353 pBase = SMGlobalClass::head;
356 pBase->OnSourceModPluginsLoaded();
357 pBase = pBase->m_pGlobalClassNext;
362 g_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
367 g_pOnMapEnd = g_Forwards.CreateForward("OnMapEnd", ET_Ignore, 0, NULL);
370 g_LevelEndBarrier = true;
372 RETURN_META_VALUE(MRES_IGNORED, true);
375 void StartTickSimulation()
377 g_SimTicks.ticking = false;
378 g_SimTicks.tickcount = 0;
379 g_SimTicks.ticktime = gpGlobals->curtime;
382 void StopTickSimulation()
384 g_SimTicks.ticking = true;
385 g_Timers.MapChange(false);
387 g_LastTime = gpGlobals->curtime;
392 g_SimTicks.tickcount++;
393 g_SimTicks.ticktime = g_StillTime + (g_SimTicks.tickcount * gpGlobals->interval_per_tick);
396 void SourceModBase::GameFrame(bool simulating)
401 * Note: This is all hardcoded rather than delegated to save
402 * precious CPU cycles.
404 float curtime = gpGlobals->curtime;
405 int framecount = gpGlobals->framecount;
407 /* Verify that we're still ticking */
408 if (g_SimTicks.ticking)
410 if (g_StillFrames == 0)
412 g_StillFrames = framecount;
413 g_StillTime = curtime;
415 /* Try to detect when we've stopped ticking.
416 * We do this once 10 frames pass and there have been no ticks.
418 if (g_StillTime == curtime)
420 if (framecount - g_StillFrames >= 5)
422 StartTickSimulation();
426 /* We're definitely ticking we get here,
427 * but update everything as a precaution */
428 g_StillFrames = framecount;
429 g_StillTime = curtime;
433 /* We need to make sure we should still be simulating. */
434 if (g_StillTime != curtime)
436 /* Wow, we're ticking again! It's time to revert. */
437 StopTickSimulation();
440 /* Nope, not ticking. Simulate! */
442 curtime = g_SimTicks.ticktime;
445 if (curtime - g_LastTime >= 0.1f)
448 && (gpGlobals->curtime - g_LastAuthCheck > 0.7f))
450 g_LastAuthCheck = gpGlobals->curtime;
451 g_Players.RunAuthChecks();
455 g_LastTime = curtime;
458 if (g_SimTicks.ticking && curtime - g_LastMenuTime >= 1.0f)
460 g_ValveMenuStyle.ProcessWatchList();
461 g_RadioMenuStyle.ProcessWatchList();
462 g_LastMenuTime = curtime;
465 if (g_pOnGameFrame && g_pOnGameFrame->GetFunctionCount())
467 g_pOnGameFrame->Execute(NULL);
471 void SourceModBase::LevelShutdown()
473 if (g_LevelEndBarrier)
475 SMGlobalClass *next = SMGlobalClass::head;
478 next->OnSourceModLevelEnd();
479 next = next->m_pGlobalClassNext;
481 g_LevelEndBarrier = false;
484 if (g_pOnMapEnd && m_ExecOnMapEnd)
486 g_pOnMapEnd->Execute(NULL);
487 m_ExecOnMapEnd = false;
490 g_OnMapStarted = false;
492 if (m_ExecPluginReload)
494 g_PluginSys.ReloadOrUnloadPlugins();
495 m_ExecPluginReload = false;
499 bool SourceModBase::IsMapLoading() const
501 return m_IsMapLoading;
504 void SourceModBase::DoGlobalPluginLoads()
506 char config_path[PLATFORM_MAX_PATH];
507 char plugins_path[PLATFORM_MAX_PATH];
509 BuildPath(Path_SM, config_path,
511 "configs/plugin_settings.cfg");
512 BuildPath(Path_SM, plugins_path,
513 sizeof(plugins_path),
516 /* Load any auto extensions */
517 g_Extensions.TryAutoload();
519 /* Run the first pass */
520 g_PluginSys.LoadAll_FirstPass(config_path, plugins_path);
522 /* Mark any extensions as loaded */
523 g_Extensions.MarkAllLoaded();
525 /* No modules yet, it's safe to call this from here */
526 g_PluginSys.LoadAll_SecondPass();
528 /* Re-mark any extensions as loaded */
529 g_Extensions.MarkAllLoaded();
531 /* Call OnAllPluginsLoaded */
532 g_PluginSys.AllPluginsLoaded();
535 size_t SourceModBase::BuildPath(PathType type, char *buffer, size_t maxlength, char *format, ...)
537 char _buffer[PLATFORM_MAX_PATH];
540 va_start(ap, format);
541 vsnprintf(_buffer, PLATFORM_MAX_PATH, format, ap);
544 const char *base = NULL;
545 if (type == Path_Game)
547 base = GetGamePath();
548 } else if (type == Path_SM) {
549 base = GetSourceModPath();
550 } else if (type == Path_SM_Rel) {
556 return g_LibSys.PathFormat(buffer, maxlength, "%s/%s", base, _buffer);
558 return g_LibSys.PathFormat(buffer, maxlength, "%s", _buffer);
562 void SourceModBase::CloseSourceMod()
564 /* Force a level end */
568 g_PluginSys.Shutdown();
570 /* Unload extensions */
571 g_Extensions.Shutdown();
573 SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
579 g_Forwards.ReleaseForward(g_pOnGameFrame);
584 g_Forwards.ReleaseForward(g_pOnMapEnd);
588 SMGlobalClass *pBase = SMGlobalClass::head;
591 pBase->OnSourceModShutdown();
592 pBase = pBase->m_pGlobalClassNext;
595 /* Delete all data packs */
596 CStack<CDataPack *>::iterator iter;
598 for (iter=m_freepacks.begin(); iter!=m_freepacks.end(); iter++)
603 m_freepacks.popall();
606 pBase = SMGlobalClass::head;
609 pBase->OnSourceModAllShutdown();
610 pBase = pBase->m_pGlobalClassNext;
615 SH_RELEASE_CALLCLASS(enginePatch);
621 SH_RELEASE_CALLCLASS(gamedllPatch);
625 SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false);
626 SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false);
633 void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
635 IExtensionInterface *pAPI = pExt->GetAPI();
636 const char *tag = pAPI->GetExtensionTag();
640 va_start(ap, format);
641 vsnprintf(buffer, sizeof(buffer), format, ap);
646 g_Logger.LogMessage("[%s] %s", tag, buffer);
648 g_Logger.LogMessage("%s", buffer);
652 void SourceModBase::LogError(IExtension *pExt, const char *format, ...)
654 IExtensionInterface *pAPI = pExt->GetAPI();
655 const char *tag = pAPI->GetExtensionTag();
659 va_start(ap, format);
660 vsnprintf(buffer, sizeof(buffer), format, ap);
665 g_Logger.LogError("[%s] %s", tag, buffer);
667 g_Logger.LogError("%s", buffer);
671 size_t SourceModBase::FormatString(char *buffer, size_t maxlength, IPluginContext *pContext, const cell_t *params, unsigned int param)
675 pContext->LocalToString(params[param], &fmt);
677 int lparam = ++param;
679 return atcprintf(buffer, maxlength, fmt, pContext, params, &lparam);
682 const char *SourceModBase::GetSourceModPath() const
687 const char *SourceModBase::GetGamePath() const
689 return g_BaseDir.c_str();
692 void SourceModBase::SetGlobalTarget(unsigned int index)
697 void SourceModBase::SetAuthChecking(bool set)
699 m_CheckingAuth = set;
702 unsigned int SourceModBase::GetGlobalTarget() const
707 IDataPack *SourceModBase::CreateDataPack()
710 if (m_freepacks.empty())
712 pack = new CDataPack;
714 pack = m_freepacks.front();
721 void SourceModBase::FreeDataPack(IDataPack *pack)
723 m_freepacks.push(static_cast<CDataPack *>(pack));
726 Handle_t SourceModBase::GetDataPackHandleType(bool readonly)
732 const char *SourceModBase::GetGameFolderName() const
737 ISourcePawnEngine *SourceModBase::GetScriptingEngine()
739 return g_pSourcePawn;
742 IVirtualMachine *SourceModBase::GetScriptingVM()
747 void SourceModBase::AllPluginsLoaded()
749 SMGlobalClass *base = SMGlobalClass::head;
752 base->OnSourceModGameInitialized();
753 base = base->m_pGlobalClassNext;
757 SMGlobalClass *SMGlobalClass::head = NULL;
759 SMGlobalClass::SMGlobalClass()
761 m_pGlobalClassNext = SMGlobalClass::head;
762 SMGlobalClass::head = this;