core/ChatTriggers.cpp
author David Anderson <dvander@alliedmods.net>
Wed Jun 06 22:31:54 2007 +0000 (2007-06-06)
changeset 896 e30eef2642ba
parent 893 a3bc816549a8
child 897 0c966f18680c
permissions -rw-r--r--
added config options for chat triggers
dvander@893
     1
#include "ChatTriggers.h"
dvander@893
     2
#include "sm_stringutil.h"
dvander@893
     3
#include "TextParsers.h"
dvander@893
     4
#include "ConCmdManager.h"
dvander@893
     5
dvander@893
     6
/* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does */
dvander@893
     7
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
dvander@893
     8
extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
dvander@893
     9
dvander@893
    10
ChatTriggers g_ChatTriggers;
dvander@893
    11
dvander@893
    12
ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false), 
dvander@893
    13
	m_ReplyTo(SM_REPLY_CONSOLE)
dvander@893
    14
{
dvander@893
    15
	m_PubTrigger = sm_strdup("!");
dvander@893
    16
	m_PrivTrigger = sm_strdup("/");
dvander@893
    17
	m_PubTriggerSize = 1;
dvander@893
    18
	m_PrivTriggerSize = 1;
dvander@893
    19
}
dvander@893
    20
dvander@896
    21
dvander@896
    22
ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key, 
dvander@896
    23
													const char *value, 
dvander@896
    24
													ConfigSource source,
dvander@896
    25
													char *error, 
dvander@896
    26
													size_t maxlength)
dvander@896
    27
{
dvander@896
    28
	if (strcmp(key, "PublicChatTrigger") == 0)
dvander@896
    29
	{
dvander@896
    30
		delete [] m_PubTrigger;
dvander@896
    31
		m_PubTrigger = sm_strdup(value);
dvander@896
    32
		m_PubTriggerSize = strlen(m_PubTrigger);
dvander@896
    33
		return ConfigResult_Accept;
dvander@896
    34
	} else if (strcmp(key, "SilentChatTrigger") == 0) {
dvander@896
    35
		delete [] m_PrivTrigger;
dvander@896
    36
		m_PrivTrigger = sm_strdup(value);
dvander@896
    37
		m_PrivTriggerSize = strlen(m_PrivTrigger);
dvander@896
    38
		return ConfigResult_Accept;
dvander@896
    39
	}
dvander@896
    40
dvander@896
    41
	return ConfigResult_Ignore;
dvander@896
    42
}
dvander@896
    43
dvander@893
    44
void ChatTriggers::OnSourceModGameInitialized()
dvander@893
    45
{
dvander@893
    46
	ConCommandBase *pCmd = icvar->GetCommands();
dvander@893
    47
	const char *name;
dvander@893
    48
	while (pCmd)
dvander@893
    49
	{
dvander@893
    50
		if (pCmd->IsCommand())
dvander@893
    51
		{
dvander@893
    52
			name = pCmd->GetName();
dvander@893
    53
			if (strcmp(name, "say") == 0)
dvander@893
    54
			{
dvander@893
    55
				m_pSayCmd = (ConCommand *)pCmd;
dvander@893
    56
				break;
dvander@893
    57
			}
dvander@893
    58
		}
dvander@893
    59
		pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
dvander@893
    60
	}
dvander@893
    61
dvander@893
    62
	if (m_pSayCmd)
dvander@893
    63
	{
dvander@893
    64
		SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
dvander@893
    65
		SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
dvander@893
    66
	}
dvander@893
    67
}
dvander@893
    68
dvander@893
    69
void ChatTriggers::OnSourceModShutdown()
dvander@893
    70
{
dvander@893
    71
	if (m_pSayCmd)
dvander@893
    72
	{
dvander@893
    73
		SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
dvander@893
    74
		SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
dvander@893
    75
	}
dvander@893
    76
}
dvander@893
    77
dvander@893
    78
void ChatTriggers::OnSayCommand_Pre()
dvander@893
    79
{
dvander@893
    80
	int client = g_ConCmds.GetCommandClient();
dvander@893
    81
dvander@893
    82
	/* The server console cannot do this */
dvander@893
    83
	if (client == 0)
dvander@893
    84
	{
dvander@893
    85
		RETURN_META(MRES_IGNORED);
dvander@893
    86
	}
dvander@893
    87
dvander@893
    88
	const char *args = engine->Cmd_Args();
dvander@893
    89
dvander@893
    90
	/* Handle quoted string sets */
dvander@893
    91
	bool is_quoted = false;
dvander@893
    92
	if (args[0] == '"')
dvander@893
    93
	{
dvander@893
    94
		args++;
dvander@893
    95
		is_quoted = true;
dvander@893
    96
	}
dvander@893
    97
dvander@893
    98
	bool is_trigger = false;
dvander@893
    99
	bool is_silent = false;
dvander@893
   100
dvander@893
   101
	/* Check for either trigger */
dvander@893
   102
	if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
dvander@893
   103
	{
dvander@893
   104
		is_trigger = true;
dvander@893
   105
	} else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0) {
dvander@893
   106
		is_trigger = true;
dvander@893
   107
		is_silent = true;
dvander@893
   108
	}
dvander@893
   109
dvander@893
   110
	if (!is_trigger)
dvander@893
   111
	{
dvander@893
   112
		RETURN_META(MRES_IGNORED);
dvander@893
   113
	}
dvander@893
   114
dvander@893
   115
	/* If we're a public command, process later */
dvander@893
   116
	if (!is_silent)
dvander@893
   117
	{
dvander@893
   118
		/* We have to process this in _post_ instead.  Darn. */
dvander@893
   119
		m_bWillProcessInPost = true;
dvander@893
   120
		RETURN_META(MRES_IGNORED);
dvander@893
   121
	}
dvander@893
   122
dvander@893
   123
	/* Otherwise, process now */
dvander@893
   124
	if (ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PrivTriggerSize], is_quoted))
dvander@893
   125
	{
dvander@893
   126
		/* If we succeed, block the original say! */
dvander@893
   127
		RETURN_META(MRES_SUPERCEDE);
dvander@893
   128
	}
dvander@893
   129
dvander@893
   130
	RETURN_META(MRES_IGNORED);
dvander@893
   131
}
dvander@893
   132
dvander@893
   133
void ChatTriggers::OnSayCommand_Post()
dvander@893
   134
{
dvander@893
   135
	if (m_bWillProcessInPost)
dvander@893
   136
	{
dvander@893
   137
		/* Reset this for re-entrancy */
dvander@893
   138
		m_bWillProcessInPost = false;
dvander@893
   139
dvander@893
   140
		/* Get our arguments */
dvander@893
   141
		const char *args = engine->Cmd_Args();
dvander@893
   142
dvander@893
   143
		/* Handle quotations */
dvander@893
   144
		bool is_quoted = false;
dvander@893
   145
		if (args[0] == '"')
dvander@893
   146
		{
dvander@893
   147
			args++;
dvander@893
   148
			is_quoted = true;
dvander@893
   149
		}
dvander@893
   150
dvander@893
   151
		int client = g_ConCmds.GetCommandClient();
dvander@893
   152
dvander@893
   153
		ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PubTriggerSize], is_quoted);
dvander@893
   154
	}
dvander@893
   155
}
dvander@893
   156
dvander@893
   157
bool ChatTriggers::ProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
dvander@893
   158
{
dvander@893
   159
	/* Eat up whitespace */
dvander@893
   160
	while (*args != '\0' && IsWhitespace(args))
dvander@893
   161
	{
dvander@893
   162
		args++;
dvander@893
   163
	}
dvander@893
   164
dvander@893
   165
	/* Check if we're still valid */
dvander@893
   166
	if (*args == '\0')
dvander@893
   167
	{
dvander@893
   168
		return false;
dvander@893
   169
	}
dvander@893
   170
dvander@893
   171
	/* Extract a command.  This is kind of sloppy. */
dvander@893
   172
	char cmd_buf[64];
dvander@893
   173
	size_t cmd_len = 0;
dvander@893
   174
	const char *inptr = args;
dvander@893
   175
	while (*inptr != '\0' 
dvander@893
   176
			&& !IsWhitespace(inptr) 
dvander@893
   177
			&& cmd_len < sizeof(cmd_buf) - 1)
dvander@893
   178
	{
dvander@893
   179
		cmd_buf[cmd_len++] = *inptr++;
dvander@893
   180
	}
dvander@893
   181
	cmd_buf[cmd_len] = '\0';
dvander@893
   182
dvander@893
   183
	/* See if we have this registered */
dvander@893
   184
	bool prepended = false;
dvander@893
   185
	if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
dvander@893
   186
	{
dvander@893
   187
		/* Check if we had an "sm_" prefix */
dvander@893
   188
		if (strncmp(cmd_buf, "sm_", 3) == 0)
dvander@893
   189
		{
dvander@893
   190
			return false;
dvander@893
   191
		}
dvander@893
   192
dvander@893
   193
		/* Now, prepend.  Don't worry about the buffers.  This will 
dvander@893
   194
		 * work because the sizes are limited from earlier.
dvander@893
   195
		 */
dvander@893
   196
		char new_buf[80];
dvander@893
   197
		strcpy(new_buf, "sm_");
dvander@893
   198
		strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
dvander@893
   199
dvander@893
   200
		/* Recheck */
dvander@893
   201
		if (!g_ConCmds.LookForSourceModCommand(new_buf))
dvander@893
   202
		{
dvander@893
   203
			return false;
dvander@893
   204
		}
dvander@893
   205
		
dvander@893
   206
		prepended = true;
dvander@893
   207
	}
dvander@893
   208
dvander@893
   209
	/* See if we need to do extra string manipulation */
dvander@893
   210
	if (is_quoted || prepended)
dvander@893
   211
	{
dvander@893
   212
		static char buffer[300];
dvander@893
   213
		size_t len;
dvander@893
   214
dvander@893
   215
		/* Check if we need to prepend sm_ */
dvander@893
   216
		if (prepended)
dvander@893
   217
		{
dvander@893
   218
			len = UTIL_Format(buffer, sizeof(buffer), "sm_%s", args);
dvander@893
   219
		} else {
dvander@893
   220
			len = strncopy(buffer, args, sizeof(buffer));
dvander@893
   221
		}
dvander@893
   222
		
dvander@893
   223
		/* Check if we need to strip a quote */
dvander@893
   224
		if (is_quoted)
dvander@893
   225
		{
dvander@893
   226
			if (buffer[len-1] == '"')
dvander@893
   227
			{
dvander@893
   228
				buffer[--len] = '\0';
dvander@893
   229
			}
dvander@893
   230
		}
dvander@893
   231
dvander@893
   232
		args = buffer;
dvander@893
   233
	}
dvander@893
   234
dvander@893
   235
	/* Finally, execute! */
dvander@893
   236
	unsigned int old = SetReplyTo(SM_REPLY_CHAT);
dvander@893
   237
	serverpluginhelpers->ClientCommand(pEdict, args);
dvander@893
   238
	SetReplyTo(old);
dvander@893
   239
dvander@893
   240
	return true;
dvander@893
   241
}
dvander@893
   242
dvander@893
   243
unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
dvander@893
   244
{
dvander@893
   245
	unsigned int old = m_ReplyTo;
dvander@893
   246
dvander@893
   247
	m_ReplyTo = reply;
dvander@893
   248
dvander@893
   249
	return old;
dvander@893
   250
}
dvander@893
   251
dvander@893
   252
unsigned int ChatTriggers::GetReplyTo()
dvander@893
   253
{
dvander@893
   254
	return m_ReplyTo;
dvander@893
   255
}