core/CConCmdManager.cpp
author David Anderson <dvander@alliedmods.net>
Mon Feb 26 09:21:08 2007 +0000 (2007-02-26)
changeset 539 fa7e8a232a18
parent 525 d36c45088e36
child 546 3b5fe88ff68d
permissions -rw-r--r--
fixed stupid export
ds@504
     1
/**
ds@504
     2
* ===============================================================
ds@504
     3
* SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
ds@504
     4
* ===============================================================
ds@504
     5
*
ds@504
     6
* This file is not open source and may not be copied without explicit
ds@504
     7
* written permission of AlliedModders LLC.  This file may not be redistributed 
ds@504
     8
* in whole or significant part.
ds@504
     9
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
ds@504
    10
*
ds@504
    11
* Version: $Id$
ds@504
    12
*/
ds@504
    13
dvander@498
    14
#include "CConCmdManager.h"
dvander@498
    15
#include "sm_srvcmds.h"
dvander@513
    16
#include "AdminCache.h"
ds@504
    17
#include "sm_stringutil.h"
dvander@514
    18
#include "CPlayerManager.h"
dvander@514
    19
#include "CTranslator.h"
dvander@498
    20
dvander@498
    21
CConCmdManager g_ConCmds;
dvander@498
    22
dvander@498
    23
SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
dvander@498
    24
SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
dvander@498
    25
dvander@498
    26
struct PlCmdInfo
dvander@498
    27
{
dvander@498
    28
	ConCmdInfo *pInfo;
dvander@513
    29
	CmdHook *pHook;
dvander@498
    30
	CmdType type;
dvander@498
    31
};
dvander@513
    32
typedef List<PlCmdInfo> CmdList;
dvander@513
    33
void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info);
dvander@498
    34
dvander@513
    35
CConCmdManager::CConCmdManager() : m_Strings(1024)
dvander@498
    36
{
dvander@498
    37
	m_pCmds = sm_trie_create();
dvander@513
    38
	m_pCmdGrps = sm_trie_create();
dvander@498
    39
}
dvander@498
    40
dvander@498
    41
CConCmdManager::~CConCmdManager()
dvander@498
    42
{
dvander@498
    43
	sm_trie_destroy(m_pCmds);
dvander@513
    44
	sm_trie_destroy(m_pCmdGrps);
dvander@498
    45
}
dvander@498
    46
dvander@498
    47
void CConCmdManager::OnSourceModAllInitialized()
dvander@498
    48
{
dvander@498
    49
	g_PluginSys.AddPluginsListener(this);
dvander@498
    50
	g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
dvander@498
    51
	SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
dvander@498
    52
}
dvander@498
    53
dvander@498
    54
void CConCmdManager::OnSourceModShutdown()
dvander@498
    55
{
dvander@500
    56
	/* All commands should already be removed by the time we're done */
dvander@498
    57
	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
dvander@498
    58
	g_RootMenu.RemoveRootConsoleCommand("cmds", this);
dvander@498
    59
	g_PluginSys.RemovePluginsListener(this);
dvander@498
    60
}
dvander@498
    61
dvander@513
    62
void CConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
dvander@498
    63
{
dvander@513
    64
	List<CmdHook *>::iterator iter = cmdlist.begin();
dvander@513
    65
	CmdHook *pHook;
dvander@513
    66
dvander@513
    67
	while (iter != cmdlist.end())
dvander@513
    68
	{
dvander@513
    69
		pHook = (*iter);
dvander@513
    70
		if (pHook->pf->GetParentContext() == pContext)
dvander@513
    71
		{
dvander@513
    72
			delete pHook->pAdmin;
dvander@513
    73
			delete pHook;
dvander@513
    74
			iter = cmdlist.erase(iter);
dvander@513
    75
		} else {
dvander@513
    76
			iter++;
dvander@513
    77
		}
dvander@513
    78
	}
dvander@498
    79
}
dvander@498
    80
dvander@498
    81
void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
dvander@498
    82
{
dvander@498
    83
	CmdList *pList;
dvander@500
    84
	List<ConCmdInfo *> removed;
dvander@498
    85
	if (plugin->GetProperty("CommandList", (void **)&pList, true))
dvander@498
    86
	{
dvander@513
    87
		IPluginContext *pContext = plugin->GetBaseContext();
dvander@498
    88
		CmdList::iterator iter;
dvander@513
    89
		/* First pass!
dvander@513
    90
	 	 * Only bother if there's an actual command list on this plugin... 
dvander@513
    91
	 	 */
dvander@498
    92
		for (iter=pList->begin();
dvander@513
    93
			iter!=pList->end();
dvander@513
    94
			iter++)
dvander@498
    95
		{
dvander@498
    96
			PlCmdInfo &cmd = (*iter);
dvander@513
    97
			ConCmdInfo *pInfo = cmd.pInfo;
dvander@513
    98
dvander@513
    99
			/* Has this chain already been fully cleaned/removed? */
dvander@513
   100
			if (removed.find(pInfo) != removed.end())
dvander@498
   101
			{
dvander@513
   102
				continue;
dvander@498
   103
			}
dvander@513
   104
dvander@513
   105
			/* Remove any hooks from us on this command */
dvander@513
   106
			RemoveConCmds(pInfo->conhooks, pContext);
dvander@513
   107
			RemoveConCmds(pInfo->srvhooks, pContext);
dvander@513
   108
dvander@513
   109
			/* See if there are still hooks */
dvander@513
   110
			if (pInfo->srvhooks.size())
dvander@513
   111
			{
dvander@513
   112
				continue;
dvander@513
   113
			}
dvander@513
   114
			if (pInfo->conhooks.size())
dvander@513
   115
			{
dvander@513
   116
				continue;
dvander@513
   117
			}
dvander@513
   118
dvander@513
   119
			/* Remove the command, it should be safe now */
dvander@513
   120
			RemoveConCmd(pInfo);
dvander@513
   121
			removed.push_back(pInfo);
dvander@498
   122
		}
dvander@498
   123
		delete pList;
dvander@498
   124
	}
dvander@498
   125
}
dvander@498
   126
dvander@498
   127
void CommandCallback()
dvander@498
   128
{
dvander@498
   129
	g_ConCmds.InternalDispatch();
dvander@498
   130
}
dvander@498
   131
dvander@498
   132
void CConCmdManager::SetCommandClient(int client)
dvander@498
   133
{
dvander@498
   134
	m_CmdClient = client + 1;
dvander@498
   135
}
dvander@498
   136
dvander@500
   137
ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type)
dvander@500
   138
{
dvander@500
   139
	const char *cmd = engine->Cmd_Argv(0);
dvander@500
   140
	int args = engine->Cmd_Argc() - 1;
dvander@500
   141
dvander@500
   142
	ConCmdInfo *pInfo;
dvander@500
   143
	if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
dvander@500
   144
	{
dvander@500
   145
		cell_t result = type;
dvander@513
   146
		cell_t tempres = result;
dvander@513
   147
		List<CmdHook *>::iterator iter;
dvander@513
   148
		CmdHook *pHook;
dvander@513
   149
		for (iter=pInfo->conhooks.begin();
dvander@513
   150
			 iter!=pInfo->conhooks.end();
dvander@513
   151
			 iter++)
dvander@500
   152
		{
dvander@513
   153
			pHook = (*iter);
dvander@525
   154
			if (!pHook->pf->IsRunnable())
dvander@525
   155
			{
dvander@525
   156
				continue;
dvander@525
   157
			}
dvander@514
   158
			if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin))
dvander@513
   159
			{
dvander@514
   160
				if (result < Pl_Handled)
dvander@514
   161
				{
dvander@514
   162
					result = Pl_Handled;
dvander@514
   163
				}
dvander@514
   164
				continue;
dvander@513
   165
			}
dvander@513
   166
			pHook->pf->PushCell(client);
dvander@513
   167
			pHook->pf->PushCell(args);
dvander@513
   168
			if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
dvander@513
   169
			{
dvander@513
   170
				if (tempres > result)
dvander@513
   171
				{
dvander@513
   172
					result = tempres;
dvander@513
   173
				}
dvander@513
   174
				if (result == Pl_Stop)
dvander@513
   175
				{
dvander@513
   176
					break;
dvander@513
   177
				}
dvander@513
   178
			}
dvander@500
   179
		}
dvander@500
   180
		type = (ResultType)result;
dvander@500
   181
	}
dvander@500
   182
dvander@500
   183
	return type;
dvander@500
   184
}
dvander@500
   185
dvander@498
   186
void CConCmdManager::InternalDispatch()
dvander@498
   187
{
dvander@500
   188
	/**
dvander@500
   189
	 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
dvander@500
   190
	 * OR this dispatch.  They will NEVER go through both.
dvander@500
   191
	 * -- 
dvander@500
   192
	 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
dvander@500
   193
	 */
dvander@498
   194
dvander@498
   195
	const char *cmd = engine->Cmd_Argv(0);
dvander@498
   196
	ConCmdInfo *pInfo;
dvander@498
   197
	if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
dvander@498
   198
	{
dvander@498
   199
		return;
dvander@498
   200
	}
dvander@498
   201
dvander@498
   202
	cell_t result = Pl_Continue;
dvander@500
   203
	int args = engine->Cmd_Argc() - 1;
dvander@500
   204
dvander@513
   205
	List<CmdHook *>::iterator iter;
dvander@513
   206
	CmdHook *pHook;
dvander@513
   207
dvander@513
   208
	/* Execute server-only commands if viable */
dvander@513
   209
	if (m_CmdClient == 0 && pInfo->srvhooks.size())
dvander@498
   210
	{
dvander@513
   211
		cell_t tempres = result;
dvander@513
   212
		for (iter=pInfo->srvhooks.begin();
dvander@513
   213
			 iter!=pInfo->srvhooks.end();
dvander@513
   214
			 iter++)
dvander@500
   215
		{
dvander@513
   216
			pHook = (*iter);
dvander@525
   217
			if (!pHook->pf->IsRunnable())
dvander@525
   218
			{
dvander@525
   219
				continue;
dvander@525
   220
			}
dvander@513
   221
			pHook->pf->PushCell(args);
dvander@513
   222
			if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
dvander@513
   223
			{
dvander@513
   224
				if (tempres > result)
dvander@513
   225
				{
dvander@513
   226
					result = tempres;
dvander@513
   227
				}
dvander@513
   228
				if (result == Pl_Stop)
dvander@513
   229
				{
dvander@513
   230
					break;
dvander@513
   231
				}
dvander@513
   232
			}
dvander@500
   233
		}
dvander@500
   234
	
dvander@500
   235
		/* Check if there's an early stop */
dvander@500
   236
		if (result >= Pl_Stop)
dvander@500
   237
		{
dvander@500
   238
			if (!pInfo->sourceMod)
dvander@500
   239
			{
dvander@500
   240
				RETURN_META(MRES_SUPERCEDE);
dvander@500
   241
			}
dvander@500
   242
			return;
dvander@500
   243
		}
dvander@500
   244
	}
dvander@500
   245
dvander@513
   246
	/* Execute console commands */
dvander@513
   247
	if (pInfo->conhooks.size())
dvander@500
   248
	{
dvander@513
   249
		cell_t tempres = result;
dvander@513
   250
		for (iter=pInfo->conhooks.begin();
dvander@513
   251
			iter!=pInfo->conhooks.end();
dvander@513
   252
			iter++)
dvander@513
   253
		{
dvander@513
   254
			pHook = (*iter);
dvander@525
   255
			if (!pHook->pf->IsRunnable())
dvander@525
   256
			{
dvander@525
   257
				continue;
dvander@525
   258
			}
dvander@513
   259
			if (m_CmdClient 
dvander@513
   260
				&& pHook->pAdmin
dvander@514
   261
				&& !CheckAccess(m_CmdClient, cmd, pHook->pAdmin))
dvander@513
   262
			{
dvander@514
   263
				if (result < Pl_Handled)
dvander@514
   264
				{
dvander@514
   265
					result = Pl_Handled;
dvander@514
   266
				}
dvander@514
   267
				continue;
dvander@513
   268
			}
dvander@513
   269
			pHook->pf->PushCell(m_CmdClient);
dvander@513
   270
			pHook->pf->PushCell(args);
dvander@513
   271
			if (pHook->pf->Execute(&tempres) != SP_ERROR_NONE)
dvander@513
   272
			{
dvander@513
   273
				if (tempres > result)
dvander@513
   274
				{
dvander@513
   275
					result = tempres;
dvander@513
   276
				}
dvander@513
   277
				if (result == Pl_Stop)
dvander@513
   278
				{
dvander@513
   279
					break;
dvander@513
   280
				}
dvander@513
   281
			}
dvander@513
   282
		}
dvander@498
   283
	}
dvander@498
   284
dvander@498
   285
	if (result >= Pl_Handled)
dvander@498
   286
	{
dvander@498
   287
		if (!pInfo->sourceMod)
dvander@498
   288
		{
dvander@498
   289
			RETURN_META(MRES_SUPERCEDE);
dvander@498
   290
		}
dvander@498
   291
		return;
dvander@498
   292
	}
dvander@498
   293
}
dvander@498
   294
dvander@514
   295
bool CConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin)
dvander@513
   296
{
dvander@514
   297
	FlagBits cmdflags = pAdmin->eflags;
dvander@514
   298
	if (cmdflags == 0)
dvander@514
   299
	{
dvander@514
   300
		return true;
dvander@514
   301
	}
dvander@514
   302
dvander@514
   303
	CPlayer *player = g_Players.GetPlayerByIndex(client);
dvander@514
   304
	if (!player)
dvander@514
   305
	{
dvander@514
   306
		return false;
dvander@514
   307
	}
dvander@514
   308
dvander@514
   309
	AdminId adm = player->GetAdminId();
dvander@514
   310
	if (adm != INVALID_ADMIN_ID)
dvander@514
   311
	{
dvander@514
   312
		FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective);
dvander@514
   313
dvander@514
   314
		/* root knows all, WHOA */
dvander@514
   315
		if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT)
dvander@514
   316
		{
dvander@514
   317
			return true;
dvander@514
   318
		}
dvander@514
   319
dvander@514
   320
		/* See if our other flags match */
dvander@520
   321
		if ((bits & cmdflags) == cmdflags)
dvander@514
   322
		{
dvander@514
   323
			return true;
dvander@514
   324
		}
dvander@514
   325
dvander@514
   326
		/* Check for overrides */
dvander@514
   327
		unsigned int groups = g_Admins.GetAdminGroupCount(adm);
dvander@514
   328
		GroupId gid;
dvander@514
   329
		OverrideRule rule;
dvander@514
   330
		bool override = false;
dvander@514
   331
		for (unsigned int i=0; i<groups; i++)
dvander@514
   332
		{
dvander@514
   333
			gid = g_Admins.GetAdminGroup(adm, i, NULL);
dvander@514
   334
			/* First get group-level override */
dvander@514
   335
			override = g_Admins.GetGroupCommandOverride(gid, cmd, Override_CommandGroup, &rule);
dvander@514
   336
			/* Now get the specific command override */
dvander@514
   337
			if (g_Admins.GetGroupCommandOverride(gid, cmd, Override_Command, &rule))
dvander@514
   338
			{
dvander@514
   339
				override = true;
dvander@514
   340
			}
dvander@514
   341
			if (override)
dvander@514
   342
			{
dvander@514
   343
				if (rule == Command_Allow)
dvander@514
   344
				{
dvander@514
   345
					return true;
dvander@514
   346
				} else if (rule == Command_Deny) {
dvander@514
   347
					break;
dvander@514
   348
				}
dvander@514
   349
			}
dvander@514
   350
		}
dvander@514
   351
	}
dvander@514
   352
dvander@520
   353
	if (player->IsFakeClient()
dvander@520
   354
		|| player->GetEdict() == NULL)
dvander@520
   355
	{
dvander@520
   356
		return false;
dvander@520
   357
	}
dvander@520
   358
	
dvander@514
   359
	/* If we got here, the command failed... */
dvander@520
   360
	char buffer[128];
dvander@520
   361
	if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL)
dvander@520
   362
		!= Trans_Okay)
dvander@520
   363
	{
dvander@520
   364
		snprintf(buffer, sizeof(buffer), "You do not have access to this command");
dvander@520
   365
	}
dvander@520
   366
dvander@520
   367
	char fullbuffer[192];
dvander@520
   368
	snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer);
dvander@520
   369
dvander@520
   370
	engine->ClientPrintf(player->GetEdict(), fullbuffer);
dvander@520
   371
	
dvander@514
   372
	return false;
dvander@513
   373
}
dvander@513
   374
dvander@500
   375
void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction, 
dvander@500
   376
									   const char *name, 
dvander@500
   377
									   const char *description, 
dvander@500
   378
									   int flags)
dvander@500
   379
{
dvander@500
   380
	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
dvander@513
   381
	CmdHook *pHook = new CmdHook();
dvander@500
   382
dvander@513
   383
	pHook->pf = pFunction;
dvander@513
   384
	if (description && description[0])
dvander@500
   385
	{
dvander@513
   386
		pHook->helptext.assign(description);
dvander@500
   387
	}
dvander@513
   388
	pInfo->conhooks.push_back(pHook);
dvander@500
   389
dvander@500
   390
	/* Add to the plugin */
dvander@500
   391
	CmdList *pList;
dvander@500
   392
	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
dvander@500
   393
	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@500
   394
	{
dvander@500
   395
		pList = new CmdList();
dvander@500
   396
		pPlugin->SetProperty("CommandList", pList);
dvander@500
   397
	}
dvander@500
   398
	PlCmdInfo info;
dvander@500
   399
	info.pInfo = pInfo;
dvander@500
   400
	info.type = Cmd_Console;
dvander@513
   401
	info.pHook = pHook;
dvander@513
   402
	AddToPlCmdList(pList, info);
dvander@513
   403
}
dvander@513
   404
dvander@513
   405
bool CConCmdManager::AddAdminCommand(IPluginFunction *pFunction, 
dvander@513
   406
									 const char *name, 
dvander@513
   407
									 const char *group,
dvander@513
   408
									 int adminflags,
dvander@513
   409
									 const char *description, 
dvander@513
   410
									 int flags)
dvander@513
   411
{
dvander@513
   412
	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
dvander@513
   413
dvander@513
   414
	CmdHook *pHook = new CmdHook();
dvander@513
   415
	AdminCmdInfo *pAdmin = new AdminCmdInfo();
dvander@513
   416
dvander@513
   417
	pHook->pf = pFunction;
dvander@513
   418
	if (description && description[0])
dvander@513
   419
	{
dvander@513
   420
		pHook->helptext.assign(description);
dvander@513
   421
	}
dvander@513
   422
	pHook->pAdmin = pAdmin;
dvander@513
   423
dvander@513
   424
	void *object;
dvander@513
   425
	int grpid;
dvander@513
   426
	if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
dvander@513
   427
	{
dvander@513
   428
		grpid = m_Strings.AddString(group);
dvander@513
   429
		sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
dvander@513
   430
	} else {
dvander@513
   431
		grpid = (int)object;
dvander@513
   432
	}
dvander@513
   433
dvander@513
   434
	pAdmin->cmdGrpId = grpid;
dvander@520
   435
	pAdmin->flags = adminflags;
dvander@513
   436
dvander@513
   437
	/* First get the command group override, if any */
dvander@513
   438
	bool override = g_Admins.GetCommandOverride(group, 
dvander@513
   439
		Override_CommandGroup,
dvander@513
   440
		&(pAdmin->eflags));
dvander@513
   441
dvander@513
   442
	/* Next get the command override, if any */
dvander@513
   443
	if (g_Admins.GetCommandOverride(name,
dvander@513
   444
		Override_Command,
dvander@513
   445
		&(pAdmin->eflags)))
dvander@513
   446
	{
dvander@513
   447
		override = true;
dvander@513
   448
	}
dvander@513
   449
dvander@513
   450
	/* Assign normal flags if there were no overrides */
dvander@513
   451
	if (!override)
dvander@513
   452
	{
dvander@513
   453
		pAdmin->eflags = pAdmin->flags;
dvander@513
   454
	}
dvander@513
   455
dvander@513
   456
	/* Finally, add the hook */
dvander@513
   457
	pInfo->conhooks.push_back(pHook);
dvander@513
   458
dvander@513
   459
	/* Now add to the plugin */
dvander@513
   460
	CmdList *pList;
dvander@513
   461
	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
dvander@513
   462
	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@513
   463
	{
dvander@513
   464
		pList = new CmdList();
dvander@513
   465
		pPlugin->SetProperty("CommandList", pList);
dvander@513
   466
	}
dvander@513
   467
	PlCmdInfo info;
dvander@513
   468
	info.pInfo = pInfo;
dvander@513
   469
	info.type = Cmd_Admin;
dvander@513
   470
	info.pHook = pHook;
dvander@513
   471
	AddToPlCmdList(pList, info);
dvander@513
   472
dvander@513
   473
	return true;
dvander@500
   474
}
dvander@500
   475
dvander@498
   476
void CConCmdManager::AddServerCommand(IPluginFunction *pFunction, 
dvander@498
   477
									  const char *name, 
dvander@498
   478
									  const char *description, 
dvander@498
   479
									  int flags)
dvander@498
   480
dvander@498
   481
{
dvander@498
   482
	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
dvander@513
   483
	CmdHook *pHook = new CmdHook();
dvander@498
   484
dvander@513
   485
	pHook->pf = pFunction;
dvander@513
   486
	if (description && description[0])
dvander@498
   487
	{
dvander@513
   488
		pHook->helptext.assign(description);
dvander@498
   489
	}
dvander@498
   490
dvander@513
   491
	pInfo->srvhooks.push_back(pHook);
dvander@498
   492
dvander@498
   493
	/* Add to the plugin */
dvander@498
   494
	CmdList *pList;
dvander@498
   495
	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
dvander@498
   496
	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@498
   497
	{
dvander@498
   498
		pList = new CmdList();
dvander@498
   499
		pPlugin->SetProperty("CommandList", pList);
dvander@498
   500
	}
dvander@498
   501
	PlCmdInfo info;
dvander@498
   502
	info.pInfo = pInfo;
dvander@500
   503
	info.type = Cmd_Server;
dvander@513
   504
	info.pHook = pHook;
dvander@513
   505
	AddToPlCmdList(pList, info);
dvander@513
   506
}
dvander@513
   507
dvander@513
   508
void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
dvander@513
   509
{
dvander@513
   510
	CmdList::iterator iter = pList->begin();
dvander@513
   511
	bool inserted = false;
dvander@513
   512
	const char *orig = NULL;
dvander@513
   513
dvander@513
   514
	orig = info.pInfo->pCmd->GetName();
dvander@513
   515
dvander@513
   516
	/* Insert this into the help list, SORTED alphabetically. */
dvander@513
   517
	while (iter != pList->end())
dvander@513
   518
	{
dvander@513
   519
		PlCmdInfo &obj = (*iter);
dvander@513
   520
		const char *cmd = obj.pInfo->pCmd->GetName();
dvander@513
   521
		if (strcmp(orig, cmd) < 0)
dvander@513
   522
		{
dvander@513
   523
			pList->insert(iter, info);
dvander@513
   524
			inserted = true;
dvander@513
   525
			break;
dvander@513
   526
		}
dvander@513
   527
		iter++;
dvander@513
   528
	}
dvander@513
   529
dvander@513
   530
	if (!inserted)
dvander@513
   531
	{
dvander@513
   532
		pList->push_back(info);
dvander@513
   533
	}
dvander@498
   534
}
dvander@498
   535
dvander@498
   536
void CConCmdManager::AddToCmdList(ConCmdInfo *info)
dvander@498
   537
{
dvander@498
   538
	List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
dvander@498
   539
	ConCmdInfo *pInfo;
dvander@498
   540
	bool inserted = false;
dvander@503
   541
	const char *orig = NULL;
dvander@498
   542
dvander@503
   543
	orig = info->pCmd->GetName();
dvander@498
   544
dvander@498
   545
	/* Insert this into the help list, SORTED alphabetically. */
dvander@498
   546
	while (iter != m_CmdList.end())
dvander@498
   547
	{
dvander@498
   548
		const char *cmd = NULL;
dvander@498
   549
		pInfo = (*iter);
dvander@503
   550
		cmd = pInfo->pCmd->GetName();
dvander@498
   551
		if (strcmp(orig, cmd) < 0)
dvander@498
   552
		{
dvander@498
   553
			m_CmdList.insert(iter, info);
dvander@498
   554
			inserted = true;
dvander@498
   555
			break;
dvander@498
   556
		}
dvander@498
   557
		iter++;
dvander@498
   558
	}
dvander@498
   559
dvander@498
   560
	if (!inserted)
dvander@498
   561
	{
dvander@498
   562
		m_CmdList.push_back(info);
dvander@498
   563
	}
dvander@498
   564
}
dvander@498
   565
dvander@514
   566
void CConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits)
dvander@514
   567
{
dvander@514
   568
	ConCmdInfo *pInfo;
dvander@514
   569
dvander@514
   570
	if (type == Override_Command)
dvander@514
   571
	{
dvander@514
   572
		if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
dvander@514
   573
		{
dvander@514
   574
			return;
dvander@514
   575
		}
dvander@514
   576
dvander@514
   577
		List<CmdHook *>::iterator iter;
dvander@514
   578
		CmdHook *pHook;
dvander@514
   579
dvander@514
   580
		for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++)
dvander@514
   581
		{
dvander@514
   582
			pHook = (*iter);
dvander@514
   583
			if (pHook->pAdmin)
dvander@514
   584
			{
dvander@514
   585
				if (bits)
dvander@514
   586
				{
dvander@514
   587
					pHook->pAdmin->eflags = bits;
dvander@514
   588
				} else {
dvander@514
   589
					pHook->pAdmin->eflags = pHook->pAdmin->flags;
dvander@514
   590
				}
dvander@514
   591
			}
dvander@514
   592
		}
dvander@514
   593
	} else if (type == Override_CommandGroup) {
dvander@514
   594
		void *object;
dvander@514
   595
		if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object))
dvander@514
   596
		{
dvander@514
   597
			return;
dvander@514
   598
		}
dvander@514
   599
		int grpid = (int)object;
dvander@514
   600
dvander@514
   601
		/* This is bad :( loop through all commands */
dvander@514
   602
		List<ConCmdInfo *>::iterator iter;
dvander@514
   603
		CmdHook *pHook;
dvander@514
   604
		for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++)
dvander@514
   605
		{
dvander@514
   606
			pInfo = (*iter);
dvander@514
   607
			for (List<CmdHook *>::iterator citer=pInfo->conhooks.begin();
dvander@514
   608
				 citer!=pInfo->conhooks.end();
dvander@514
   609
				 citer++)
dvander@514
   610
			{
dvander@514
   611
				pHook = (*citer);
dvander@514
   612
				if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid)
dvander@514
   613
				{
dvander@514
   614
					if (bits)
dvander@514
   615
					{
dvander@514
   616
						pHook->pAdmin->eflags = bits;
dvander@514
   617
					} else {
dvander@514
   618
						pHook->pAdmin->eflags = pHook->pAdmin->flags;
dvander@514
   619
					}
dvander@514
   620
				}
dvander@514
   621
			}
dvander@514
   622
		}
dvander@514
   623
	}
dvander@514
   624
}
dvander@514
   625
dvander@498
   626
void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
dvander@498
   627
{
dvander@503
   628
	/* Remove console-specific information
dvander@503
   629
	 * This should always be true as of right now
dvander@503
   630
	 */
dvander@498
   631
	if (info->pCmd)
dvander@498
   632
	{
dvander@498
   633
		/* Remove from the trie */
dvander@498
   634
		sm_trie_delete(m_pCmds, info->pCmd->GetName());
dvander@498
   635
dvander@498
   636
		if (info->sourceMod)
dvander@498
   637
		{
dvander@498
   638
			/* Unlink from SourceMM */
dvander@498
   639
			g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
dvander@498
   640
			/* Delete the command's memory */
dvander@498
   641
			char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
dvander@498
   642
			char *new_name = const_cast<char *>(info->pCmd->GetName());
dvander@498
   643
			delete [] new_help;
dvander@498
   644
			delete [] new_name;
dvander@498
   645
			delete info->pCmd;
dvander@498
   646
		} else {
dvander@498
   647
			/* Remove the external hook */
dvander@498
   648
			SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
dvander@498
   649
		}
dvander@498
   650
	}
dvander@498
   651
	
dvander@498
   652
	/* Remove from list */
dvander@498
   653
	m_CmdList.remove(info);
dvander@513
   654
dvander@513
   655
	delete info;
dvander@498
   656
}
dvander@498
   657
dvander@498
   658
ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
dvander@498
   659
{
dvander@498
   660
	ConCmdInfo *pInfo;
dvander@498
   661
	if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
dvander@498
   662
	{
dvander@498
   663
		pInfo = new ConCmdInfo();
dvander@498
   664
		/* Find the commandopan */
dvander@498
   665
		ConCommandBase *pBase = icvar->GetCommands();
dvander@498
   666
		ConCommand *pCmd = NULL;
dvander@498
   667
		while (pBase)
dvander@498
   668
		{
dvander@498
   669
			if (pBase->IsCommand()
dvander@498
   670
				&& (strcmp(pBase->GetName(), name) == 0))
dvander@498
   671
			{
dvander@498
   672
				pCmd = (ConCommand *)pBase;
dvander@498
   673
				break;
dvander@498
   674
			}
dvander@498
   675
			pBase = const_cast<ConCommandBase *>(pBase->GetNext());
dvander@498
   676
		}
dvander@498
   677
dvander@498
   678
		if (!pCmd)
dvander@498
   679
		{
dvander@498
   680
			/* Note that we have to duplicate because the source might not be 
dvander@498
   681
			 * a static string, and these expect static memory.
dvander@498
   682
			 */
dvander@498
   683
			if (!description)
dvander@498
   684
			{
dvander@498
   685
				description = "";
dvander@498
   686
			}
dvander@498
   687
			char *new_name = sm_strdup(name);
dvander@498
   688
			char *new_help = sm_strdup(description);
dvander@498
   689
			pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
dvander@498
   690
			pInfo->sourceMod = true;
dvander@498
   691
		} else {
dvander@498
   692
			SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
dvander@498
   693
		}
dvander@498
   694
dvander@498
   695
		pInfo->pCmd = pCmd;
dvander@498
   696
dvander@498
   697
		sm_trie_insert(m_pCmds, name, pInfo);
dvander@498
   698
		AddToCmdList(pInfo);
dvander@498
   699
	}
dvander@498
   700
dvander@498
   701
	return pInfo;
dvander@498
   702
}
dvander@498
   703
dvander@498
   704
void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
dvander@498
   705
{
dvander@499
   706
	if (argcount >= 3)
dvander@499
   707
	{
dvander@499
   708
		const char *text = engine->Cmd_Argv(2);
dvander@499
   709
		int id = atoi(text);
dvander@499
   710
		CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
dvander@499
   711
dvander@499
   712
		if (!pPlugin)
dvander@499
   713
		{
dvander@499
   714
			g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
dvander@499
   715
			return;
dvander@499
   716
		}
dvander@499
   717
dvander@499
   718
		CmdList *pList;
dvander@499
   719
		if (!pPlugin->GetProperty("CommandList", (void **)&pList))
dvander@499
   720
		{
dvander@499
   721
			g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
dvander@499
   722
			return;
dvander@499
   723
		}
dvander@499
   724
		if (!pList->size())
dvander@499
   725
		{
dvander@499
   726
			g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
dvander@499
   727
			return;
dvander@499
   728
		}
dvander@499
   729
dvander@499
   730
		CmdList::iterator iter;
dvander@499
   731
		const char *type = NULL;
dvander@499
   732
		const char *name;
dvander@499
   733
		const char *help;
dvander@499
   734
		g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
dvander@499
   735
		for (iter=pList->begin();
dvander@499
   736
			 iter!=pList->end();
dvander@499
   737
			 iter++)
dvander@499
   738
		{
dvander@499
   739
			PlCmdInfo &cmd = (*iter);
dvander@499
   740
			if (cmd.type == Cmd_Server)
dvander@499
   741
			{
dvander@499
   742
				type = "server";
dvander@499
   743
			} else if (cmd.type == Cmd_Console) {
dvander@499
   744
				type = "console";
dvander@513
   745
			} else if (cmd.type == Cmd_Admin) {
dvander@513
   746
				type = "admin";
dvander@499
   747
			}
dvander@499
   748
			name = cmd.pInfo->pCmd->GetName();
dvander@513
   749
			if (cmd.pHook->helptext.size())
dvander@513
   750
			{
dvander@513
   751
				help = cmd.pHook->helptext.c_str();
dvander@513
   752
			} else {
dvander@513
   753
				help = cmd.pInfo->pCmd->GetHelpText();
dvander@513
   754
			}
dvander@500
   755
			g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);		
dvander@499
   756
		}
dvander@499
   757
dvander@499
   758
		return;
dvander@499
   759
	}
dvander@499
   760
dvander@499
   761
	g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
dvander@498
   762
}