core/CConCmdManager.cpp
author David Anderson <dvander@alliedmods.net>
Fri Feb 16 04:16:31 2007 +0000 (2007-02-16)
changeset 502 4530ea065a8b
parent 500 868e9b19a872
child 503 c4b8f43bded1
permissions -rw-r--r--
fixed a serious potential bug with swapping plugins to debug mode, leaving stale function pointers without plugin removal
dvander@498
     1
#include "CConCmdManager.h"
dvander@498
     2
#include "sm_srvcmds.h"
dvander@498
     3
dvander@498
     4
CConCmdManager g_ConCmds;
dvander@498
     5
dvander@498
     6
SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
dvander@498
     7
SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
dvander@498
     8
dvander@498
     9
struct PlCmdInfo
dvander@498
    10
{
dvander@498
    11
	ConCmdInfo *pInfo;
dvander@498
    12
	CmdType type;
dvander@498
    13
};
dvander@498
    14
dvander@498
    15
typedef List<PlCmdInfo> CmdList;
dvander@498
    16
dvander@498
    17
char *sm_strdup(const char *str)
dvander@498
    18
{
dvander@498
    19
	char *ptr = new char[strlen(str)+1];
dvander@498
    20
	strcpy(ptr, str);
dvander@498
    21
	return ptr;
dvander@498
    22
}
dvander@498
    23
dvander@498
    24
CConCmdManager::CConCmdManager()
dvander@498
    25
{
dvander@498
    26
	m_pCmds = sm_trie_create();
dvander@498
    27
}
dvander@498
    28
dvander@498
    29
CConCmdManager::~CConCmdManager()
dvander@498
    30
{
dvander@498
    31
	sm_trie_destroy(m_pCmds);
dvander@498
    32
}
dvander@498
    33
dvander@498
    34
void CConCmdManager::OnSourceModAllInitialized()
dvander@498
    35
{
dvander@498
    36
	g_PluginSys.AddPluginsListener(this);
dvander@498
    37
	g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
dvander@498
    38
	SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
dvander@498
    39
}
dvander@498
    40
dvander@498
    41
void CConCmdManager::OnSourceModShutdown()
dvander@498
    42
{
dvander@500
    43
	/* All commands should already be removed by the time we're done */
dvander@498
    44
	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
dvander@498
    45
	g_RootMenu.RemoveRootConsoleCommand("cmds", this);
dvander@498
    46
	g_PluginSys.RemovePluginsListener(this);
dvander@498
    47
}
dvander@498
    48
dvander@498
    49
void CConCmdManager::OnPluginLoaded(IPlugin *plugin)
dvander@498
    50
{
dvander@498
    51
	/* Nothing yet... */
dvander@498
    52
}
dvander@498
    53
dvander@498
    54
void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
dvander@498
    55
{
dvander@498
    56
	CmdList *pList;
dvander@500
    57
	List<ConCmdInfo *> removed;
dvander@498
    58
	if (plugin->GetProperty("CommandList", (void **)&pList, true))
dvander@498
    59
	{
dvander@498
    60
		CmdList::iterator iter;
dvander@498
    61
		for (iter=pList->begin();
dvander@498
    62
			 iter!=pList->end();
dvander@498
    63
			 iter++)
dvander@498
    64
		{
dvander@498
    65
			PlCmdInfo &cmd = (*iter);
dvander@498
    66
			if (cmd.type == Cmd_Server
dvander@498
    67
				|| cmd.type == Cmd_Console)
dvander@498
    68
			{
dvander@498
    69
				ConCmdInfo *pInfo = cmd.pInfo;
dvander@500
    70
				/* See if this is being removed */
dvander@500
    71
				if (removed.find(pInfo) != removed.end())
dvander@500
    72
				{
dvander@500
    73
					continue;
dvander@500
    74
				}
dvander@498
    75
				/* See if there are still hooks */
dvander@498
    76
				if (pInfo->srvhooks
dvander@498
    77
					&& pInfo->srvhooks->GetFunctionCount())
dvander@498
    78
				{
dvander@498
    79
					continue;
dvander@498
    80
				}
dvander@500
    81
				if (pInfo->conhooks
dvander@500
    82
					&& pInfo->conhooks->GetFunctionCount())
dvander@500
    83
				{
dvander@500
    84
					continue;
dvander@500
    85
				}
dvander@498
    86
				/* Remove the command */
dvander@498
    87
				RemoveConCmd(pInfo);
dvander@500
    88
				removed.push_back(pInfo);
dvander@498
    89
			}
dvander@498
    90
		}
dvander@498
    91
		delete pList;
dvander@498
    92
	}
dvander@498
    93
}
dvander@498
    94
dvander@498
    95
void CommandCallback()
dvander@498
    96
{
dvander@498
    97
	g_ConCmds.InternalDispatch();
dvander@498
    98
}
dvander@498
    99
dvander@498
   100
void CConCmdManager::SetCommandClient(int client)
dvander@498
   101
{
dvander@498
   102
	m_CmdClient = client + 1;
dvander@498
   103
}
dvander@498
   104
dvander@500
   105
ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type)
dvander@500
   106
{
dvander@500
   107
	const char *cmd = engine->Cmd_Argv(0);
dvander@500
   108
	int args = engine->Cmd_Argc() - 1;
dvander@500
   109
dvander@500
   110
	ConCmdInfo *pInfo;
dvander@500
   111
	if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
dvander@500
   112
	{
dvander@500
   113
		cell_t result = type;
dvander@500
   114
		if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
dvander@500
   115
		{
dvander@500
   116
			pInfo->conhooks->PushCell(client);
dvander@500
   117
			pInfo->conhooks->PushCell(args);
dvander@500
   118
			pInfo->conhooks->Execute(&result);
dvander@500
   119
		}
dvander@500
   120
		if (result >= Pl_Stop)
dvander@500
   121
		{
dvander@500
   122
			return Pl_Stop;
dvander@500
   123
		}
dvander@500
   124
		type = (ResultType)result;
dvander@500
   125
	}
dvander@500
   126
dvander@500
   127
	return type;
dvander@500
   128
}
dvander@500
   129
dvander@498
   130
void CConCmdManager::InternalDispatch()
dvander@498
   131
{
dvander@500
   132
	/**
dvander@500
   133
	 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
dvander@500
   134
	 * OR this dispatch.  They will NEVER go through both.
dvander@500
   135
	 * -- 
dvander@500
   136
	 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
dvander@500
   137
	 */
dvander@498
   138
dvander@498
   139
	const char *cmd = engine->Cmd_Argv(0);
dvander@498
   140
	ConCmdInfo *pInfo;
dvander@498
   141
	if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
dvander@498
   142
	{
dvander@498
   143
		return;
dvander@498
   144
	}
dvander@498
   145
dvander@498
   146
	cell_t result = Pl_Continue;
dvander@500
   147
	int args = engine->Cmd_Argc() - 1;
dvander@500
   148
dvander@500
   149
	if (m_CmdClient == 0)
dvander@498
   150
	{
dvander@500
   151
		if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount())
dvander@500
   152
		{
dvander@500
   153
			pInfo->srvhooks->PushCell(args);
dvander@500
   154
			pInfo->srvhooks->Execute(&result);
dvander@500
   155
		}
dvander@500
   156
	
dvander@500
   157
		/* Check if there's an early stop */
dvander@500
   158
		if (result >= Pl_Stop)
dvander@500
   159
		{
dvander@500
   160
			if (!pInfo->sourceMod)
dvander@500
   161
			{
dvander@500
   162
				RETURN_META(MRES_SUPERCEDE);
dvander@500
   163
			}
dvander@500
   164
			return;
dvander@500
   165
		}
dvander@500
   166
	}
dvander@500
   167
dvander@500
   168
	/* Execute console command hooks */
dvander@500
   169
	if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
dvander@500
   170
	{
dvander@500
   171
		pInfo->conhooks->PushCell(m_CmdClient);
dvander@500
   172
		pInfo->conhooks->PushCell(args);
dvander@500
   173
		pInfo->conhooks->Execute(&result);
dvander@498
   174
	}
dvander@498
   175
dvander@498
   176
	if (result >= Pl_Handled)
dvander@498
   177
	{
dvander@498
   178
		if (!pInfo->sourceMod)
dvander@498
   179
		{
dvander@498
   180
			RETURN_META(MRES_SUPERCEDE);
dvander@498
   181
		}
dvander@498
   182
		return;
dvander@498
   183
	}
dvander@498
   184
}
dvander@498
   185
dvander@500
   186
void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction, 
dvander@500
   187
									   const char *name, 
dvander@500
   188
									   const char *description, 
dvander@500
   189
									   int flags)
dvander@500
   190
{
dvander@500
   191
	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
dvander@500
   192
dvander@500
   193
	if (!pInfo->conhooks)
dvander@500
   194
	{
dvander@500
   195
		pInfo->conhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 2, NULL, Param_Cell, Param_Cell);
dvander@500
   196
	}
dvander@500
   197
dvander@500
   198
	pInfo->conhooks->AddFunction(pFunction);
dvander@500
   199
dvander@500
   200
	/* Add to the plugin */
dvander@500
   201
	CmdList *pList;
dvander@500
   202
	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
dvander@500
   203
	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@500
   204
	{
dvander@500
   205
		pList = new CmdList();
dvander@500
   206
		pPlugin->SetProperty("CommandList", pList);
dvander@500
   207
	}
dvander@500
   208
	PlCmdInfo info;
dvander@500
   209
	info.pInfo = pInfo;
dvander@500
   210
	info.type = Cmd_Console;
dvander@500
   211
	pList->push_back(info);
dvander@500
   212
}
dvander@500
   213
dvander@498
   214
void CConCmdManager::AddServerCommand(IPluginFunction *pFunction, 
dvander@498
   215
									  const char *name, 
dvander@498
   216
									  const char *description, 
dvander@498
   217
									  int flags)
dvander@498
   218
dvander@498
   219
{
dvander@498
   220
	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
dvander@498
   221
dvander@498
   222
	if (!pInfo->srvhooks)
dvander@498
   223
	{
dvander@498
   224
		pInfo->srvhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_Cell);
dvander@498
   225
	}
dvander@498
   226
dvander@498
   227
	pInfo->srvhooks->AddFunction(pFunction);
dvander@498
   228
dvander@498
   229
	/* Add to the plugin */
dvander@498
   230
	CmdList *pList;
dvander@498
   231
	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
dvander@498
   232
	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@498
   233
	{
dvander@498
   234
		pList = new CmdList();
dvander@498
   235
		pPlugin->SetProperty("CommandList", pList);
dvander@498
   236
	}
dvander@498
   237
	PlCmdInfo info;
dvander@498
   238
	info.pInfo = pInfo;
dvander@500
   239
	info.type = Cmd_Server;
dvander@498
   240
	pList->push_back(info);
dvander@498
   241
}
dvander@498
   242
dvander@498
   243
void CConCmdManager::AddToCmdList(ConCmdInfo *info)
dvander@498
   244
{
dvander@498
   245
	List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
dvander@498
   246
	ConCmdInfo *pInfo;
dvander@498
   247
	bool inserted = false;
dvander@498
   248
	const char *orig;
dvander@498
   249
dvander@498
   250
	if (info->pCmd)
dvander@498
   251
	{
dvander@498
   252
		orig = info->pCmd->GetName();
dvander@498
   253
	}
dvander@498
   254
dvander@498
   255
	/* Insert this into the help list, SORTED alphabetically. */
dvander@498
   256
	while (iter != m_CmdList.end())
dvander@498
   257
	{
dvander@498
   258
		const char *cmd = NULL;
dvander@498
   259
		pInfo = (*iter);
dvander@498
   260
		if (pInfo->pCmd)
dvander@498
   261
		{
dvander@498
   262
			cmd = pInfo->pCmd->GetName();
dvander@498
   263
		}
dvander@498
   264
		if (strcmp(orig, cmd) < 0)
dvander@498
   265
		{
dvander@498
   266
			m_CmdList.insert(iter, info);
dvander@498
   267
			inserted = true;
dvander@498
   268
			break;
dvander@498
   269
		}
dvander@498
   270
		iter++;
dvander@498
   271
	}
dvander@498
   272
dvander@498
   273
	if (!inserted)
dvander@498
   274
	{
dvander@498
   275
		m_CmdList.push_back(info);
dvander@498
   276
	}
dvander@498
   277
}
dvander@498
   278
dvander@498
   279
void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
dvander@498
   280
{
dvander@498
   281
	/* Remove console-specific information */
dvander@498
   282
	if (info->pCmd)
dvander@498
   283
	{
dvander@498
   284
		/* Remove from the trie */
dvander@498
   285
		sm_trie_delete(m_pCmds, info->pCmd->GetName());
dvander@498
   286
dvander@498
   287
		if (info->sourceMod)
dvander@498
   288
		{
dvander@498
   289
			/* Unlink from SourceMM */
dvander@498
   290
			g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
dvander@498
   291
			/* Delete the command's memory */
dvander@498
   292
			char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
dvander@498
   293
			char *new_name = const_cast<char *>(info->pCmd->GetName());
dvander@498
   294
			delete [] new_help;
dvander@498
   295
			delete [] new_name;
dvander@498
   296
			delete info->pCmd;
dvander@498
   297
		} else {
dvander@498
   298
			/* Remove the external hook */
dvander@498
   299
			SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
dvander@498
   300
		}
dvander@498
   301
	}
dvander@498
   302
	
dvander@498
   303
	/* Remove from list */
dvander@498
   304
	m_CmdList.remove(info);
dvander@498
   305
	
dvander@498
   306
	/* Free forwards */
dvander@498
   307
	if (info->srvhooks)
dvander@498
   308
	{
dvander@498
   309
		g_Forwards.ReleaseForward(info->srvhooks);
dvander@498
   310
	}
dvander@498
   311
}
dvander@498
   312
dvander@498
   313
ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
dvander@498
   314
{
dvander@498
   315
	ConCmdInfo *pInfo;
dvander@498
   316
	if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
dvander@498
   317
	{
dvander@498
   318
		pInfo = new ConCmdInfo();
dvander@498
   319
		/* Find the commandopan */
dvander@498
   320
		ConCommandBase *pBase = icvar->GetCommands();
dvander@498
   321
		ConCommand *pCmd = NULL;
dvander@498
   322
		while (pBase)
dvander@498
   323
		{
dvander@498
   324
			if (pBase->IsCommand()
dvander@498
   325
				&& (strcmp(pBase->GetName(), name) == 0))
dvander@498
   326
			{
dvander@498
   327
				pCmd = (ConCommand *)pBase;
dvander@498
   328
				break;
dvander@498
   329
			}
dvander@498
   330
			pBase = const_cast<ConCommandBase *>(pBase->GetNext());
dvander@498
   331
		}
dvander@498
   332
dvander@498
   333
		if (!pCmd)
dvander@498
   334
		{
dvander@498
   335
			/* Note that we have to duplicate because the source might not be 
dvander@498
   336
			 * a static string, and these expect static memory.
dvander@498
   337
			 */
dvander@498
   338
			if (!description)
dvander@498
   339
			{
dvander@498
   340
				description = "";
dvander@498
   341
			}
dvander@498
   342
			char *new_name = sm_strdup(name);
dvander@498
   343
			char *new_help = sm_strdup(description);
dvander@498
   344
			pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
dvander@498
   345
			pInfo->sourceMod = true;
dvander@498
   346
		} else {
dvander@498
   347
			SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
dvander@498
   348
		}
dvander@498
   349
dvander@498
   350
		pInfo->pCmd = pCmd;
dvander@498
   351
dvander@498
   352
		sm_trie_insert(m_pCmds, name, pInfo);
dvander@498
   353
		AddToCmdList(pInfo);
dvander@498
   354
	}
dvander@498
   355
dvander@498
   356
	return pInfo;
dvander@498
   357
}
dvander@498
   358
dvander@498
   359
void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
dvander@498
   360
{
dvander@499
   361
	if (argcount >= 3)
dvander@499
   362
	{
dvander@499
   363
		const char *text = engine->Cmd_Argv(2);
dvander@499
   364
		int id = atoi(text);
dvander@499
   365
		CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
dvander@499
   366
dvander@499
   367
		if (!pPlugin)
dvander@499
   368
		{
dvander@499
   369
			g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
dvander@499
   370
			return;
dvander@499
   371
		}
dvander@499
   372
dvander@499
   373
		CmdList *pList;
dvander@499
   374
		if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@499
   375
		{
dvander@499
   376
			g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
dvander@499
   377
			return;
dvander@499
   378
		}
dvander@499
   379
		if (!pList->size())
dvander@499
   380
		{
dvander@499
   381
			g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
dvander@499
   382
			return;
dvander@499
   383
		}
dvander@499
   384
dvander@499
   385
		CmdList::iterator iter;
dvander@499
   386
		const char *type = NULL;
dvander@499
   387
		const char *name;
dvander@499
   388
		const char *help;
dvander@499
   389
		g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
dvander@499
   390
		for (iter=pList->begin();
dvander@499
   391
			 iter!=pList->end();
dvander@499
   392
			 iter++)
dvander@499
   393
		{
dvander@499
   394
			PlCmdInfo &cmd = (*iter);
dvander@499
   395
			if (cmd.type == Cmd_Server)
dvander@499
   396
			{
dvander@499
   397
				type = "server";
dvander@499
   398
			} else if (cmd.type == Cmd_Console) {
dvander@499
   399
				type = "console";
dvander@499
   400
			} else if (cmd.type == Cmd_Client) {
dvander@499
   401
				type = "client";
dvander@499
   402
			}
dvander@499
   403
			name = cmd.pInfo->pCmd->GetName();
dvander@499
   404
			help = cmd.pInfo->pCmd->GetHelpText();
dvander@500
   405
			g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);		
dvander@499
   406
		}
dvander@499
   407
dvander@499
   408
		return;
dvander@499
   409
	}
dvander@499
   410
dvander@499
   411
	g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
dvander@498
   412
}