core/UserMessages.cpp
author David Anderson <dvander@alliedmods.net>
Thu Jun 21 23:06:37 2007 +0000 (2007-06-21)
changeset 997 2fc3152c3118
parent 766 c9195659604c
child 1132 e8d4e82e9541
permissions -rw-r--r--
bumped menu api again - added feature to get menu end reason
dvander@592
     1
/**
ds@653
     2
 * vim: set ts=4 :
ds@653
     3
 * ===============================================================
ds@653
     4
 * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
ds@653
     5
 * ===============================================================
ds@653
     6
 *
ds@653
     7
 * This file is not open source and may not be copied without explicit
ds@653
     8
 * written permission of AlliedModders LLC.  This file may not be redistributed 
ds@653
     9
 * in whole or significant part.
ds@653
    10
 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
ds@653
    11
 *
ds@653
    12
 * Version: $Id$
ds@653
    13
 */
dvander@592
    14
dvander@592
    15
#include "UserMessages.h"
ds@765
    16
#include "sm_stringutil.h"
dvander@592
    17
dvander@616
    18
UserMessages g_UserMsgs;
dvander@592
    19
dvander@592
    20
SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
dvander@592
    21
SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
dvander@592
    22
dvander@616
    23
UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
dvander@592
    24
{
dvander@592
    25
	m_Names = sm_trie_create();
dvander@592
    26
	m_HookCount = 0;
dvander@592
    27
	m_InExec = false;
dvander@592
    28
	m_InHook = false;
dvander@592
    29
	m_CurFlags = 0;
dvander@592
    30
	m_CurId = INVALID_MESSAGE_ID;
dvander@592
    31
}
dvander@592
    32
dvander@616
    33
UserMessages::~UserMessages()
dvander@592
    34
{
dvander@592
    35
	sm_trie_destroy(m_Names);
faluco@641
    36
faluco@641
    37
	CStack<ListenerInfo *>::iterator iter;
faluco@641
    38
	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
faluco@641
    39
	{
faluco@641
    40
		delete (*iter);
faluco@641
    41
	}
faluco@641
    42
	m_FreeListeners.popall();
dvander@592
    43
}
dvander@592
    44
ds@765
    45
void UserMessages::OnSourceModStartup(bool late)
ds@765
    46
{
ds@765
    47
	/* -1 means SourceMM was unable to get the user message list */
ds@765
    48
	m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
ds@765
    49
}
ds@765
    50
dvander@616
    51
void UserMessages::OnSourceModAllInitialized()
dvander@592
    52
{
dvander@592
    53
	g_ShareSys.AddInterface(NULL, this);
dvander@592
    54
}
dvander@592
    55
dvander@616
    56
void UserMessages::OnSourceModAllShutdown()
dvander@592
    57
{
dvander@592
    58
	if (m_HookCount)
dvander@592
    59
	{
dvander@616
    60
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
dvander@616
    61
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
dvander@616
    62
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
dvander@616
    63
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
dvander@592
    64
	}
dvander@592
    65
	m_HookCount = 0;
dvander@592
    66
}
dvander@592
    67
dvander@616
    68
int UserMessages::GetMessageIndex(const char *msg)
dvander@592
    69
{
dvander@592
    70
	int msgid;
ds@698
    71
dvander@592
    72
	if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
dvander@592
    73
	{
ds@765
    74
		if (m_FallbackSearch)
ds@765
    75
		{
ds@765
    76
			char msgbuf[64];
ds@765
    77
			int size;
ds@765
    78
			msgid = 0;
ds@765
    79
ds@765
    80
			while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
ds@765
    81
			{
ds@765
    82
				if (strcmp(msgbuf, msg) == 0)
ds@765
    83
				{
ds@765
    84
					sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
ds@765
    85
					return msgid;
ds@765
    86
				}
ds@765
    87
				msgid++;
ds@765
    88
			}
ds@765
    89
		}
ds@765
    90
ds@698
    91
		msgid = g_SMAPI->FindUserMessage(msg);
dvander@592
    92
ds@698
    93
		if (msgid != INVALID_MESSAGE_ID)
dvander@592
    94
		{
ds@698
    95
			sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
dvander@592
    96
		}
dvander@592
    97
	}
dvander@592
    98
dvander@592
    99
	return msgid;
dvander@592
   100
}
dvander@592
   101
ds@765
   102
bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
ds@765
   103
{
ds@765
   104
	if (m_FallbackSearch)
ds@765
   105
	{
ds@765
   106
		int size;
ds@765
   107
		return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
ds@765
   108
	}
ds@766
   109
ds@765
   110
	const char *msg = g_SMAPI->GetUserMessage(msgid);
ds@765
   111
ds@765
   112
	if (msg)
ds@765
   113
	{
ds@765
   114
		strncopy(buffer, msg, maxlength);
ds@765
   115
		return true;
ds@765
   116
	}
ds@765
   117
ds@765
   118
	return false;
ds@765
   119
}
ds@765
   120
dvander@616
   121
bf_write *UserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
dvander@592
   122
{
dvander@592
   123
	bf_write *buffer;
faluco@593
   124
faluco@593
   125
	if (m_InExec || m_InHook)
dvander@592
   126
	{
dvander@592
   127
		return NULL;
dvander@592
   128
	}
dvander@592
   129
	if (msg_id < 0 || msg_id >= 255)
dvander@592
   130
	{
dvander@592
   131
		return NULL;
dvander@592
   132
	}
dvander@592
   133
faluco@593
   134
	m_CellRecFilter.Initialize(players, playersNum);
dvander@592
   135
dvander@592
   136
	m_CurFlags = flags;
dvander@592
   137
	if (m_CurFlags & USERMSG_INITMSG)
dvander@592
   138
	{
faluco@593
   139
		m_CellRecFilter.SetToInit(true);
dvander@592
   140
	}
dvander@592
   141
	if (m_CurFlags & USERMSG_RELIABLE)
dvander@592
   142
	{
faluco@593
   143
		m_CellRecFilter.SetToReliable(true);
dvander@592
   144
	}
dvander@592
   145
dvander@592
   146
	m_InExec = true;
dvander@592
   147
dvander@592
   148
	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
dvander@592
   149
	{
dvander@592
   150
		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
dvander@592
   151
	} else {
dvander@592
   152
		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
dvander@592
   153
	}
dvander@592
   154
dvander@592
   155
	return buffer;
dvander@592
   156
}
dvander@592
   157
dvander@616
   158
bool UserMessages::EndMessage()
dvander@592
   159
{
dvander@592
   160
	if (!m_InExec)
dvander@592
   161
	{
dvander@592
   162
		return false;
dvander@592
   163
	}
dvander@592
   164
dvander@592
   165
	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
dvander@592
   166
	{
dvander@592
   167
		engine->MessageEnd();
dvander@592
   168
	} else {
dvander@592
   169
		ENGINE_CALL(MessageEnd)();
dvander@592
   170
	}
dvander@592
   171
dvander@592
   172
	m_InExec = false;
dvander@592
   173
	m_CurFlags = 0;
faluco@593
   174
	m_CellRecFilter.Reset();
dvander@592
   175
dvander@592
   176
	return true;
dvander@592
   177
}
dvander@592
   178
dvander@616
   179
bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
dvander@592
   180
{
dvander@592
   181
	if (msg_id < 0 || msg_id >= 255)
dvander@592
   182
	{
dvander@592
   183
		return false;
dvander@592
   184
	}
dvander@592
   185
faluco@593
   186
	ListenerInfo *pInfo;
faluco@593
   187
	if (m_FreeListeners.empty())
faluco@593
   188
	{
faluco@593
   189
		pInfo = new ListenerInfo;
faluco@593
   190
	} else {
faluco@593
   191
		pInfo = m_FreeListeners.front();
faluco@593
   192
		m_FreeListeners.pop();
faluco@593
   193
	}
faluco@593
   194
faluco@593
   195
	pInfo->Callback = pListener;
faluco@593
   196
	pInfo->IsHooked = false;
faluco@593
   197
	pInfo->KillMe = false;
faluco@593
   198
dvander@592
   199
	if (!m_HookCount++)
dvander@592
   200
	{
dvander@616
   201
		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
dvander@616
   202
		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
dvander@616
   203
		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
dvander@616
   204
		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
dvander@592
   205
	}
dvander@592
   206
dvander@592
   207
	if (intercept)
dvander@592
   208
	{
faluco@593
   209
		m_msgIntercepts[msg_id].push_back(pInfo);
dvander@592
   210
	} else {
faluco@593
   211
		m_msgHooks[msg_id].push_back(pInfo);
dvander@592
   212
	}
dvander@592
   213
dvander@592
   214
	return true;
dvander@592
   215
}
dvander@592
   216
dvander@616
   217
bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
faluco@593
   218
{	
faluco@593
   219
	MsgList *pList;
dvander@592
   220
	MsgIter iter;
faluco@593
   221
	ListenerInfo *pInfo;
dvander@592
   222
	bool deleted = false;
dvander@592
   223
dvander@592
   224
	if (msg_id < 0 || msg_id >= 255)
dvander@592
   225
	{
dvander@592
   226
		return false;
dvander@592
   227
	}
dvander@592
   228
faluco@593
   229
	pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
faluco@593
   230
	for (iter=pList->begin(); iter!=pList->end(); iter++)
dvander@592
   231
	{
faluco@593
   232
		pInfo = (*iter);
faluco@593
   233
		if (pInfo->Callback == pListener)
dvander@592
   234
		{
faluco@593
   235
			if (pInfo->IsHooked)
faluco@593
   236
			{
faluco@593
   237
				pInfo->KillMe = true;
faluco@593
   238
				return true;
faluco@593
   239
			}
faluco@593
   240
			pList->erase(iter);
dvander@592
   241
			deleted = true;
dvander@592
   242
			break;
dvander@592
   243
		}
dvander@592
   244
	}
dvander@592
   245
faluco@593
   246
	if (deleted)
faluco@593
   247
	{
faluco@593
   248
		_DecRefCounter();
faluco@593
   249
	}
faluco@593
   250
faluco@593
   251
	return deleted;
faluco@593
   252
}
faluco@593
   253
dvander@616
   254
void UserMessages::_DecRefCounter()
faluco@593
   255
{
faluco@593
   256
	if (--m_HookCount == 0)
dvander@592
   257
	{
dvander@616
   258
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
dvander@616
   259
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
dvander@616
   260
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
dvander@616
   261
		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
dvander@592
   262
	}
dvander@592
   263
}
dvander@592
   264
dvander@616
   265
bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
dvander@592
   266
{
dvander@592
   267
	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
dvander@592
   268
	bool is_hook_empty = m_msgHooks[msg_type].empty();
dvander@592
   269
faluco@593
   270
	if ((is_intercept_empty && is_hook_empty)
faluco@593
   271
		|| (m_InExec && !(m_CurFlags & USERMSG_PASSTHRU_ALL)))
dvander@592
   272
	{
dvander@592
   273
		m_InHook = false;
dvander@592
   274
		RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592
   275
	}
dvander@592
   276
dvander@592
   277
	m_CurId = msg_type;
dvander@592
   278
	m_CurRecFilter = filter;
dvander@592
   279
	m_InHook = true;
faluco@597
   280
	m_BlockEndPost = false;
dvander@592
   281
dvander@592
   282
	if (!is_intercept_empty)
dvander@592
   283
	{
faluco@593
   284
		m_InterceptBuffer.Reset();
dvander@592
   285
		RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
dvander@592
   286
	}
dvander@592
   287
dvander@592
   288
	RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592
   289
}
dvander@592
   290
dvander@616
   291
bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
dvander@592
   292
{
dvander@592
   293
	if (!m_InHook)
dvander@592
   294
	{
dvander@592
   295
		RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592
   296
	}
dvander@592
   297
dvander@592
   298
	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
dvander@592
   299
dvander@592
   300
	RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592
   301
}
dvander@592
   302
dvander@616
   303
void UserMessages::OnMessageEnd_Post()
dvander@592
   304
{
faluco@597
   305
	if (!m_InHook || m_BlockEndPost)
dvander@592
   306
	{
dvander@592
   307
		RETURN_META(MRES_IGNORED);
dvander@592
   308
	}
dvander@592
   309
faluco@593
   310
	MsgList *pList;
dvander@592
   311
	MsgIter iter;
faluco@593
   312
	ListenerInfo *pInfo;
faluco@593
   313
faluco@593
   314
	pList = &m_msgIntercepts[m_CurId];
faluco@593
   315
	for (iter=pList->begin(); iter!=pList->end(); )
faluco@593
   316
	{
faluco@593
   317
		pInfo = (*iter);
faluco@593
   318
		pInfo->IsHooked = true;
faluco@593
   319
		pInfo->Callback->OnUserMessageSent(m_CurId);
dvander@592
   320
faluco@593
   321
		if (pInfo->KillMe)
faluco@593
   322
		{
faluco@593
   323
			iter = pList->erase(iter);
faluco@593
   324
			m_FreeListeners.push(pInfo);
faluco@593
   325
			_DecRefCounter();
faluco@593
   326
			continue;
faluco@593
   327
		}
faluco@593
   328
faluco@593
   329
		pInfo->IsHooked = false;
faluco@593
   330
		iter++;
dvander@592
   331
	}
dvander@592
   332
faluco@593
   333
	pList = &m_msgHooks[m_CurId];
faluco@594
   334
	for (iter=pList->begin(); iter!=pList->end(); )
dvander@592
   335
	{
faluco@593
   336
		pInfo = (*iter);
faluco@594
   337
		pInfo->IsHooked = true;
faluco@593
   338
		pInfo->Callback->OnUserMessageSent(m_CurId);
faluco@593
   339
faluco@593
   340
		if (pInfo->KillMe)
faluco@593
   341
		{
faluco@593
   342
			iter = pList->erase(iter);
faluco@593
   343
			m_FreeListeners.push(pInfo);
faluco@593
   344
			_DecRefCounter();
faluco@593
   345
			continue;
faluco@593
   346
		}
faluco@593
   347
faluco@593
   348
		pInfo->IsHooked = false;
faluco@593
   349
		iter++;
dvander@592
   350
	}
faluco@593
   351
faluco@593
   352
	m_InHook = false;
dvander@592
   353
}
dvander@592
   354
dvander@616
   355
void UserMessages::OnMessageEnd_Pre()
dvander@592
   356
{
dvander@592
   357
	if (!m_InHook)
dvander@592
   358
	{
dvander@592
   359
		RETURN_META(MRES_IGNORED);
dvander@592
   360
	}
dvander@592
   361
faluco@593
   362
	MsgList *pList;
dvander@592
   363
	MsgIter iter;
faluco@593
   364
	ListenerInfo *pInfo;
faluco@593
   365
dvander@592
   366
	ResultType res;
dvander@592
   367
	bool intercepted = false;
dvander@592
   368
	bool handled = false;
dvander@592
   369
faluco@593
   370
	pList = &m_msgIntercepts[m_CurId];
faluco@593
   371
	for (iter=pList->begin(); iter!=pList->end(); )
dvander@592
   372
	{
faluco@593
   373
		pInfo = (*iter);
faluco@593
   374
		pInfo->IsHooked = true;
faluco@593
   375
		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
faluco@593
   376
faluco@597
   377
		intercepted = true;
faluco@597
   378
faluco@593
   379
		switch (res)
dvander@592
   380
		{
faluco@593
   381
		case Pl_Stop:
faluco@593
   382
			{
faluco@593
   383
				if (pInfo->KillMe)
faluco@593
   384
				{
faluco@593
   385
					pList->erase(iter);
faluco@593
   386
					m_FreeListeners.push(pInfo);
faluco@593
   387
					_DecRefCounter();
faluco@593
   388
					goto supercede;
faluco@593
   389
				}
faluco@593
   390
				pInfo->IsHooked = false;
faluco@593
   391
				goto supercede;
faluco@593
   392
			}
faluco@593
   393
		case Pl_Handled:
faluco@593
   394
			{
faluco@593
   395
				handled = true;
faluco@593
   396
				if (pInfo->KillMe)
faluco@593
   397
				{
faluco@593
   398
					iter = pList->erase(iter);
faluco@593
   399
					m_FreeListeners.push(pInfo);
faluco@593
   400
					_DecRefCounter();
faluco@593
   401
					continue;
faluco@593
   402
				}
faluco@593
   403
				break;
faluco@593
   404
			}
faluco@593
   405
		default:
faluco@593
   406
			{
faluco@593
   407
				if (pInfo->KillMe)
faluco@593
   408
				{
faluco@593
   409
					iter = pList->erase(iter);
faluco@593
   410
					m_FreeListeners.push(pInfo);
faluco@593
   411
					_DecRefCounter();
faluco@593
   412
					continue;
faluco@593
   413
				}
faluco@597
   414
				break;
faluco@593
   415
			}
dvander@592
   416
		}
faluco@593
   417
		pInfo->IsHooked = false;
faluco@593
   418
		iter++;
dvander@592
   419
	}
dvander@592
   420
dvander@592
   421
	if (handled)
dvander@592
   422
	{
dvander@592
   423
		goto supercede;
dvander@592
   424
	}
dvander@592
   425
dvander@592
   426
	if (intercepted)
dvander@592
   427
	{
faluco@593
   428
		bf_write *engine_bfw;
faluco@593
   429
dvander@592
   430
		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
dvander@592
   431
		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
dvander@592
   432
		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
dvander@592
   433
		ENGINE_CALL(MessageEnd)();
dvander@592
   434
	}
dvander@592
   435
faluco@593
   436
	pList = &m_msgHooks[m_CurId];
faluco@593
   437
	for (iter=pList->begin(); iter!=pList->end(); )
dvander@592
   438
	{
faluco@593
   439
		pInfo = (*iter);
faluco@593
   440
		pInfo->IsHooked = true;
faluco@593
   441
		pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
faluco@593
   442
faluco@593
   443
		if (pInfo->KillMe)
faluco@593
   444
		{
faluco@593
   445
			iter = pList->erase(iter);
faluco@593
   446
			m_FreeListeners.push(pInfo);
faluco@593
   447
			_DecRefCounter();
faluco@593
   448
			continue;
faluco@593
   449
		}
faluco@593
   450
faluco@594
   451
		pInfo->IsHooked = false;
faluco@593
   452
		iter++;
dvander@592
   453
	}
dvander@592
   454
faluco@597
   455
	RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
dvander@592
   456
supercede:
faluco@593
   457
	m_InHook = false;
faluco@597
   458
	m_BlockEndPost = true;
dvander@592
   459
	RETURN_META(MRES_SUPERCEDE);
dvander@592
   460
}