More consistency of some sort...
Oh god, what has possessed me to do this?
3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
15 #include "ConVarManager.h"
16 #include "PluginSys.h"
17 #include "ForwardSys.h"
18 #include "HandleSys.h"
19 #include "sm_srvcmds.h"
20 #include "sm_stringutil.h"
21 #include <sh_vector.h>
23 ConVarManager g_ConVarManager;
25 const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
26 typedef List<const ConVar *> ConVarList;
28 ConVarManager::ConVarManager() : m_ConVarType(0)
30 /* Create a convar lookup trie */
31 m_ConVarCache = sm_trie_create();
34 ConVarManager::~ConVarManager()
36 List<ConVarInfo *>::iterator iter;
38 /* Destroy our convar lookup trie */
39 sm_trie_destroy(m_ConVarCache);
41 /* Destroy all the ConVarInfo structures */
42 for (iter = m_ConVars.begin(); iter != m_ConVars.end(); iter++)
50 void ConVarManager::OnSourceModAllInitialized()
54 /* Set up access rights for the 'ConVar' handle type */
55 sec.access[HandleAccess_Read] = 0;
56 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
57 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
59 /* Create the 'ConVar' handle type */
60 m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
62 /* Add the 'convars' option to the 'sm' console command */
63 g_RootMenu.AddRootConsoleCommand("convars", "View convars created by a plugin", this);
66 void ConVarManager::OnSourceModShutdown()
68 IChangeableForward *fwd;
69 List<ConVarInfo *>::iterator i;
71 /* Iterate list of ConVarInfo structures */
72 for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
74 fwd = (*i)->changeForward;
76 /* Free any convar-change forwards that still exist */
79 g_Forwards.ReleaseForward(fwd);
83 /* Remove the 'convars' option from the 'sm' console command */
84 g_RootMenu.RemoveRootConsoleCommand("convars", this);
86 /* Remove the 'ConVar' handle type */
87 g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
90 void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
92 ConVarList *pConVarList;
94 /* If plugin has a convar list, free its memory */
95 if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
101 void ConVarManager::OnHandleDestroy(HandleType_t type, void *object)
104 ConVar *pConVar = static_cast<ConVar *>(object);
106 /* Find convar in lookup trie */
107 sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), reinterpret_cast<void **>(&info));
109 /* If convar was created by SourceMod plugin... */
112 /* Delete string allocations */
113 delete [] pConVar->GetName();
114 delete [] pConVar->GetDefault();
115 delete [] pConVar->GetHelpText();
117 /* Then unlink it from SourceMM */
118 g_SMAPI->UnregisterConCmdBase(g_PLAPI, pConVar);
122 void ConVarManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
126 /* Get plugin index that was passed */
127 int id = atoi(g_RootMenu.GetArgument(2));
129 /* Get plugin object */
130 CPlugin *plugin = g_PluginSys.GetPluginByOrder(id);
134 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
138 /* Get plugin name */
139 const sm_plugininfo_t *plinfo = plugin->GetPublicInfo();
140 const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : plugin->GetFilename();
142 ConVarList *pConVarList;
143 ConVarList::iterator iter;
145 /* If no convar list... */
146 if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
148 g_RootMenu.ConsolePrint("[SM] No convars found for: %s", plname);
152 g_RootMenu.ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
153 g_RootMenu.ConsolePrint(" %-32.31s %s", "[Name]", "[Value]");
155 /* Iterate convar list and display each one */
156 for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
158 const ConVar *pConVar = (*iter);
159 g_RootMenu.ConsolePrint(" %-32.31s %s", pConVar->GetName(), pConVar->GetString());
165 /* Display usage of subcommand */
166 g_RootMenu.ConsolePrint("[SM] Usage: sm convars <plugin #>");
169 Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *description, int flags, bool hasMin, float min, bool hasMax, float max)
171 ConVar *pConVar = NULL;
172 ConVarInfo *pInfo = NULL;
175 /* Find out if the convar exists already */
176 pConVar = icvar->FindVar(name);
178 /* If the convar already exists... */
181 /* First find out if we already have a handle to it */
182 if (sm_trie_retrieve(m_ConVarCache, name, (void **)&pInfo))
184 return pInfo->handle;
186 /* If we don't, then create a new handle from the convar */
187 hndl = g_HandleSys.CreateHandle(m_ConVarType, pConVar, NULL, g_pCoreIdent, NULL);
189 /* Create and initialize ConVarInfo structure */
190 pInfo = new ConVarInfo();
191 pInfo->handle = hndl;
192 pInfo->sourceMod = false;
193 pInfo->changeForward = NULL;
194 pInfo->origCallback = pConVar->GetCallback();
196 /* Insert struct into caches */
197 m_ConVars.push_back(pInfo);
198 sm_trie_insert(m_ConVarCache, name, pInfo);
204 // To prevent creating a convar that has the same name as a console command... ugh
205 ConCommandBase *pBase = icvar->GetCommands();
209 if (pBase->IsCommand() && strcmp(pBase->GetName(), name) == 0)
214 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
217 /* Since an existing convar (or concmd with the same name) was not found , now we can finally create it */
218 pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(description), hasMin, min, hasMax, max);
220 /* Add convar to plugin's list */
221 AddConVarToPluginList(pContext, pConVar);
223 /* Create a handle from the new convar */
224 hndl = g_HandleSys.CreateHandle(m_ConVarType, pConVar, NULL, g_pCoreIdent, NULL);
226 /* Create and initialize ConVarInfo structure */
227 pInfo = new ConVarInfo();
228 pInfo->handle = hndl;
229 pInfo->sourceMod = true;
230 pInfo->changeForward = NULL;
231 pInfo->origCallback = NULL;
233 /* Insert struct into caches */
234 m_ConVars.push_back(pInfo);
235 sm_trie_insert(m_ConVarCache, name, pInfo);
240 Handle_t ConVarManager::FindConVar(const char *name)
242 ConVar *pConVar = NULL;
246 /* Search for convar */
247 pConVar = icvar->FindVar(name);
249 /* If it doesn't exist, then return an invalid handle */
255 /* At this point, the convar exists. So, find out if we already have a handle */
256 if (sm_trie_retrieve(m_ConVarCache, name, (void **)&pInfo))
258 return pInfo->handle;
261 /* If we don't have a handle, then create a new one */
262 hndl = g_HandleSys.CreateHandle(m_ConVarType, pConVar, NULL, g_pCoreIdent, NULL);
264 /* Create and initilize ConVarInfo structure */
265 pInfo = new ConVarInfo();
266 pInfo->handle = hndl;
267 pInfo->sourceMod = false;
268 pInfo->changeForward = NULL;
269 pInfo->origCallback = pConVar->GetCallback();
271 /* Insert struct into our caches */
272 m_ConVars.push_back(pInfo);
273 sm_trie_insert(m_ConVarCache, name, pInfo);
278 void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
281 IChangeableForward *pForward;
283 /* Find the convar in the lookup trie */
284 if (sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), (void **)&pInfo))
286 /* Get the forward */
287 pForward = pInfo->changeForward;
289 /* If forward does not exist, create it */
292 pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
293 pInfo->changeForward = pForward;
295 /* Install our own callback */
296 pConVar->InstallChangeCallback(OnConVarChanged);
299 /* Add function to forward's list */
300 pForward->AddFunction(pFunction);
304 void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
307 IChangeableForward *pForward;
308 IPluginContext *pContext = pFunction->GetParentContext();
310 /* Find the convar in the lookup trie */
311 if (sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), (void **)&pInfo))
313 /* Get the forward */
314 pForward = pInfo->changeForward;
316 /* If the forward doesn't exist, we can't unhook anything */
319 pContext->ThrowNativeError("Convar \"%s\" has no active hook", pConVar->GetName());
323 /* Remove the function from the forward's list */
324 if (!pForward->RemoveFunction(pFunction))
326 pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", pConVar->GetName());
330 /* If the forward now has 0 functions in it... */
331 if (pForward->GetFunctionCount() == 0)
333 /* Free this forward */
334 g_Forwards.ReleaseForward(pForward);
335 pInfo->changeForward = NULL;
337 /* Put back the original convar callback */
338 pConVar->InstallChangeCallback(pInfo->origCallback);
343 void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
345 ConVarList *pConVarList;
346 ConVarList::iterator iter;
347 bool inserted = false;
348 const char *orig = pConVar->GetName();
350 IPlugin *plugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
352 /* Check plugin for an existing convar list */
353 if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
355 pConVarList = new ConVarList();
356 plugin->SetProperty("ConVarList", pConVarList);
359 /* Insert convar into list which is sorted alphabetically */
360 for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
362 if (strcmp(orig, (*iter)->GetName()) < 0)
364 pConVarList->insert(iter, pConVar);
372 pConVarList->push_back(pConVar);
376 void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
378 /* If the values are the same, exit early in order to not trigger callbacks */
379 if (strcmp(pConVar->GetString(), oldValue) == 0)
384 Trie *pCache = g_ConVarManager.GetConVarCache();
387 /* Find the convar in the lookup trie */
388 sm_trie_retrieve(pCache, pConVar->GetName(), (void **)&pInfo);
390 FnChangeCallback origCallback = pInfo->origCallback;
391 IChangeableForward *pForward = pInfo->changeForward;
393 /* If there was a change callback installed previously, call it */
396 origCallback(pConVar, oldValue);
399 /* Now call forwards in plugins that have hooked this */
400 pForward->PushCell(pInfo->handle);
401 pForward->PushString(oldValue);
402 pForward->PushString(pConVar->GetString());
403 pForward->Execute(NULL);
406 static int s_YamagramState = 0;
408 void _YamagramPrinterTwoPointOhOh(int yamagram)
413 g_RootMenu.ConsolePrint("Answer the following questions correctly and Gaben may not eat you after all.");
414 g_RootMenu.ConsolePrint("You will be given one hint in the form of my patented yamagrams.");
415 g_RootMenu.ConsolePrint("Type sm_nana to see the last question.");
416 g_RootMenu.ConsolePrint("Type sm_nana <answer> to attempt an answer of the question.");
417 g_RootMenu.ConsolePrint("-------------------------------");
418 _YamagramPrinterTwoPointOhOh(1);
421 g_RootMenu.ConsolePrint("Question Ichi (1)");
422 g_RootMenu.ConsolePrint("One can turn into a cow by doing what action?");
423 g_RootMenu.ConsolePrint("Hint: AGE SANS GRIT");
426 g_RootMenu.ConsolePrint("Question Ni (2)");
427 g_RootMenu.ConsolePrint("What kind of hat should you wear when using the Internet?");
428 g_RootMenu.ConsolePrint("Hint: BRR MOOSE");
431 g_RootMenu.ConsolePrint("Question San (3)");
432 g_RootMenu.ConsolePrint("Who is the lead developer of SourceMod?");
433 g_RootMenu.ConsolePrint("Hint: VEAL BANDANA DID RIP SOON");
436 g_RootMenu.ConsolePrint("Question Yon (4)");
437 g_RootMenu.ConsolePrint("A terrible translation of 'SVN Revision' to Japanese romaji might be ...");
438 g_RootMenu.ConsolePrint("Hint: I TAKE IN AN AIR OK");
441 g_RootMenu.ConsolePrint("Question Go (5)");
442 g_RootMenu.ConsolePrint("What is a fundamental concept in the game of Go?");
443 g_RootMenu.ConsolePrint("Hint: AD LADEN THIEF");
446 g_RootMenu.ConsolePrint("Question Roku (6)");
447 g_RootMenu.ConsolePrint("Why am I asking all these strange questions?");
448 g_RootMenu.ConsolePrint("Hint: CHUBBY TITAN EATS EWE WAGE DATA");
451 g_RootMenu.ConsolePrint("Question Nana (7)");
452 g_RootMenu.ConsolePrint("What is my name?");
453 g_RootMenu.ConsolePrint("Hint: AD MODE LAG US");
459 s_YamagramState = yamagram;
462 void _IntExt_CallYamagrams()
464 bool correct = false;
465 const char *arg = engine->Cmd_Args();
467 if (!arg || arg[0] == '\0')
469 _YamagramPrinterTwoPointOhOh(s_YamagramState);
473 switch (s_YamagramState)
476 correct = !strcasecmp(arg, "eating grass");
479 correct = !strcasecmp(arg, "sombrero");
482 correct = !strcasecmp(arg, "david bailopan anderson");
485 correct = !strcasecmp(arg, "kaitei no kairan");
488 correct = !strcasecmp(arg, "life and death");
491 correct = !strcasecmp(arg, "because gabe wanted it that way");
494 g_RootMenu.ConsolePrint("Congratulations, you have answered 6 of my questions.");
495 g_RootMenu.ConsolePrint("However, I have one final question for you. It wouldn't be nana without it.");
496 g_RootMenu.ConsolePrint("-------------------------------");
497 _YamagramPrinterTwoPointOhOh(7);
502 correct = !strcasecmp(arg, "damaged soul");
505 g_RootMenu.ConsolePrint("You don't know how lucky you are to still be alive!");
506 g_RootMenu.ConsolePrint("Congratulations. You have answered all 7 questions correctly.");
507 g_RootMenu.ConsolePrint("The SourceMod Dev Team will be at your door with anti-Gaben grenades");
508 g_RootMenu.ConsolePrint("within seconds. You will also be provided with a rocket launcher,");
509 g_RootMenu.ConsolePrint("just in case Alfred decides to strike you with a blitzkrieg in retaliation.");
519 if (s_YamagramState > 0)
523 g_RootMenu.ConsolePrint("Correct! You are one step closer to avoiding the deadly jaws of Gaben.");
524 g_RootMenu.ConsolePrint("-------------------------------");
527 g_RootMenu.ConsolePrint("Wrong! You better be more careful. Gaben may be at your door at any minute.");
532 _YamagramPrinterTwoPointOhOh(s_YamagramState);
535 void _IntExt_EnableYamagrams()
537 static ConCommand *pCmd = NULL;
540 pCmd = new ConCommand("sm_nana", _IntExt_CallYamagrams, "Try these yamagrams!", FCVAR_GAMEDLL);
541 g_RootMenu.ConsolePrint("[SM] Warning: Gaben has been alerted of your actions. You may be eaten.");
543 g_RootMenu.ConsolePrint("[SM] Gaben has already been alerted of your actions...");
547 void _IntExt_OnHostnameChanged(ConVar *pConVar, char const *oldValue)
549 if (strcmp(oldValue, "Good morning, DS-san.") == 0
550 && strcmp(pConVar->GetString(), "Good night, talking desk lamp.") == 0)
552 _IntExt_EnableYamagrams();