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