core/smn_usermsgs.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Sun Mar 17 21:58:56 2013 +0000 (2013-03-17)
changeset 3837 f86a51ee4eb4
parent 3743 a98686a77974
child 3872 4c93f74cae51
permissions -rw-r--r--
Check for cloned handles when dumping (bug 5245, r=asherkin).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "HandleSys.h"
    33 #include "PluginSys.h"
    34 #include "PlayerManager.h"
    35 #include "smn_usermsgs.h"
    36 #ifdef USE_PROTOBUF_USERMESSAGES
    37 #include "UserMessagePBHelpers.h"
    38 #endif
    39 
    40 HandleType_t g_ProtobufType = NO_HANDLE_TYPE;
    41 HandleType_t g_WrBitBufType = NO_HANDLE_TYPE;
    42 HandleType_t g_RdBitBufType = NO_HANDLE_TYPE;
    43 
    44 Handle_t g_CurMsgHandle;
    45 
    46 #ifndef USE_PROTOBUF_USERMESSAGES
    47 Handle_t g_ReadBufHandle;
    48 bf_read g_ReadBitBuf;
    49 #endif
    50 
    51 int g_MsgPlayers[256];
    52 bool g_IsMsgInExec = false;
    53 
    54 typedef List<MsgListenerWrapper *> MsgWrapperList;
    55 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
    56 
    57 class UsrMessageNatives :
    58 	public SMGlobalClass,
    59 	public IHandleTypeDispatch,
    60 	public IPluginsListener
    61 {
    62 public:
    63 	~UsrMessageNatives();
    64 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
    65 	void OnSourceModAllInitialized();
    66 	void OnSourceModShutdown();
    67 	void OnHandleDestroy(HandleType_t type, void *object);
    68 	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
    69 	void OnPluginUnloaded(IPlugin *plugin);
    70 public:
    71 	MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
    72 	bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
    73 	bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
    74 private:
    75 	CStack<MsgListenerWrapper *> m_FreeListeners;
    76 };
    77 
    78 UsrMessageNatives::~UsrMessageNatives()
    79 {
    80 	CStack<MsgListenerWrapper *>::iterator iter;
    81 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    82 	{
    83 		delete (*iter);
    84 	}
    85 	m_FreeListeners.popall();
    86 }
    87 
    88 void UsrMessageNatives::OnSourceModAllInitialized()
    89 {
    90 	HandleAccess sec;
    91 
    92 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    93 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
    94 
    95 #ifdef USE_PROTOBUF_USERMESSAGES
    96 	g_ProtobufType = g_HandleSys.CreateType("ProtobufUM", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    97 #else
    98 	g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    99 	g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
   100 
   101 	g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
   102 #endif
   103 
   104 	g_PluginSys.AddPluginsListener(this);
   105 }
   106 
   107 void UsrMessageNatives::OnSourceModShutdown()
   108 {
   109 	HandleSecurity sec;
   110 	sec.pIdentity = g_pCoreIdent;
   111 
   112 #ifdef USE_PROTOBUF_USERMESSAGES
   113 	g_HandleSys.RemoveType(g_ProtobufType, g_pCoreIdent);
   114 
   115 	g_ProtobufType = 0;
   116 #else
   117 	g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
   118 
   119 	g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
   120 	g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
   121 
   122 	g_WrBitBufType = 0;
   123 	g_RdBitBufType = 0;
   124 #endif
   125 }
   126 
   127 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
   128 {
   129 #ifdef USE_PROTOBUF_USERMESSAGES
   130 	delete (SMProtobufMessage *)object;
   131 #endif
   132 }
   133 
   134 bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   135 {
   136 #ifdef USE_PROTOBUF_USERMESSAGES
   137 	// Different messages have different sizes, but this works as an approximate
   138 	*pSize = sizeof(protobuf::Message) + sizeof(SMProtobufMessage);
   139 #else
   140 	*pSize = sizeof(bf_read);
   141 #endif
   142 
   143 	return true;
   144 }
   145 
   146 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
   147 {
   148 	MsgWrapperList *pList;
   149 
   150 	if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
   151 	{
   152 		MsgWrapperIter iter;
   153 		MsgListenerWrapper *pListener;
   154 
   155 		for (iter=pList->begin(); iter!=pList->end(); iter++)
   156 		{
   157 			pListener = (*iter);
   158 			if (g_UserMsgs.UnhookUserMessage2(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
   159 			{
   160 				m_FreeListeners.push(pListener);
   161 			}
   162 		}
   163 
   164 		delete pList;
   165 	}
   166 }
   167 
   168 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
   169 {
   170 	MsgWrapperList *pList;
   171 	MsgListenerWrapper *pListener;
   172 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   173 
   174 	if (m_FreeListeners.empty())
   175 	{
   176 		pListener = new MsgListenerWrapper;
   177 	} else {
   178 		pListener = m_FreeListeners.front();
   179 		m_FreeListeners.pop();
   180 	}
   181 
   182 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   183 	{
   184 		pList = new List<MsgListenerWrapper *>;
   185 		pl->SetProperty("MsgListeners", pList);
   186 	}
   187 
   188 	pList->push_back(pListener);
   189 
   190 	return pListener;
   191 }
   192 
   193 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
   194 {
   195 	MsgWrapperList *pList;
   196 	MsgWrapperIter _iter;
   197 	MsgListenerWrapper *pListener;
   198 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   199 
   200 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   201 	{
   202 		return false;
   203 	}
   204 
   205 	for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
   206 	{
   207 		pListener = (*_iter);
   208 		if ((msgid == pListener->GetMessageId()) 
   209 			&& (intercept == pListener->IsInterceptHook()) 
   210 			&& (pHook == pListener->GetHookedFunction()))
   211 		{
   212 			*iter = _iter;
   213 			return true;
   214 		}
   215 	}
   216 
   217 	return false;
   218 }
   219 
   220 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
   221 {
   222 	MsgWrapperList *pList;
   223 	MsgListenerWrapper *pListener;
   224 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   225 
   226 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   227 	{
   228 		return false;
   229 	}
   230 
   231 	pListener = (*iter);
   232 	pList->erase(iter);
   233 	m_FreeListeners.push(pListener);
   234 
   235 	return true;
   236 }
   237 
   238 /***************************************
   239  *                                     *
   240  * USER MESSAGE WRAPPER IMPLEMENTATION *
   241  *                                     *
   242  ***************************************/
   243 
   244 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
   245 {
   246 	if (intercept)
   247 	{
   248 		m_Intercept = hook;
   249 		m_Hook = NULL;
   250 	} else {
   251 		m_Hook = hook;
   252 		m_Intercept = NULL;
   253 	}
   254 
   255 	if (notify)
   256 	{
   257 		m_Notify = notify;
   258 	} else {
   259 		m_Notify = NULL;
   260 	}
   261 
   262 	m_MsgId = msgid;
   263 	m_IsInterceptHook = intercept;
   264 }
   265 
   266 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
   267 {
   268 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
   269 
   270 	for (size_t i=0; i<size; i++)
   271 	{
   272 		pl_array[i] = pFilter->GetRecipientIndex(i);
   273 	}
   274 
   275 	return size;
   276 }
   277 
   278 bool MsgListenerWrapper::IsInterceptHook() const
   279 {
   280 	return m_IsInterceptHook;
   281 }
   282 
   283 int MsgListenerWrapper::GetMessageId() const
   284 {
   285 	return m_MsgId;
   286 }
   287 
   288 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
   289 {
   290 	if (m_Hook)
   291 	{
   292 		return m_Hook;
   293 	} else {
   294 		return m_Intercept;
   295 	}
   296 }
   297 
   298 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
   299 {
   300 	return m_Notify;
   301 }
   302 
   303 #ifdef USE_PROTOBUF_USERMESSAGES
   304 void MsgListenerWrapper::OnUserMessage(int msg_id, protobuf::Message *msg, IRecipientFilter *pFilter)
   305 #else
   306 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   307 #endif
   308 {
   309 	cell_t res;
   310 	Handle_t hndl;
   311 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   312 
   313 #ifdef USE_PROTOBUF_USERMESSAGES
   314 	hndl = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), NULL, g_pCoreIdent, NULL);
   315 #else
   316 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   317 	hndl = g_ReadBufHandle;
   318 #endif
   319 
   320 	m_Hook->PushCell(msg_id);
   321 	m_Hook->PushCell(hndl);
   322 	m_Hook->PushArray(g_MsgPlayers, size);
   323 	m_Hook->PushCell(size);
   324 	m_Hook->PushCell(pFilter->IsReliable());
   325 	m_Hook->PushCell(pFilter->IsInitMessage());
   326 	m_Hook->Execute(&res);
   327 
   328 #ifdef USE_PROTOBUF_USERMESSAGES
   329 	HandleSecurity sec;
   330 	sec.pIdentity = g_pCoreIdent;
   331 	g_HandleSys.FreeHandle(hndl, &sec);
   332 #endif
   333 }
   334 
   335 #ifdef USE_PROTOBUF_USERMESSAGES
   336 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, protobuf::Message *msg, IRecipientFilter *pFilter)
   337 #else
   338 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   339 #endif
   340 {
   341 	Handle_t hndl;
   342 	cell_t res = static_cast<cell_t>(Pl_Continue);
   343 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   344 
   345 #ifdef USE_PROTOBUF_USERMESSAGES
   346 	hndl = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), NULL, g_pCoreIdent, NULL);
   347 #else
   348 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   349 	hndl = g_ReadBufHandle;
   350 #endif
   351 
   352 	m_Intercept->PushCell(msg_id);
   353 	m_Intercept->PushCell(hndl);
   354 	m_Intercept->PushArray(g_MsgPlayers, size);
   355 	m_Intercept->PushCell(size);
   356 	m_Intercept->PushCell(pFilter->IsReliable());
   357 	m_Intercept->PushCell(pFilter->IsInitMessage());
   358 	m_Intercept->Execute(&res);
   359 
   360 #ifdef USE_PROTOBUF_USERMESSAGES
   361 	HandleSecurity sec;
   362 	sec.pIdentity = g_pCoreIdent;
   363 	g_HandleSys.FreeHandle(hndl, &sec);
   364 #endif
   365 
   366 	return static_cast<ResultType>(res);
   367 }
   368 
   369 void MsgListenerWrapper::OnPostUserMessage(int msg_id, bool sent)
   370 {
   371 	if (!m_Notify)
   372 	{
   373 		return;
   374 	}
   375 
   376 	cell_t res;
   377 	m_Notify->PushCell(msg_id);
   378 	m_Notify->PushCell(sent ? 1 : 0);
   379 	m_Notify->Execute(&res);
   380 }
   381 
   382 /***************************************
   383  *                                     *
   384  * USER MESSAGE NATIVE IMPLEMENTATIONS *
   385  *                                     *
   386  ***************************************/
   387 
   388 static UsrMessageNatives s_UsrMessageNatives;
   389 
   390 static cell_t smn_GetUserMessageType(IPluginContext *pCtx, const cell_t *params)
   391 {
   392 	return g_UserMsgs.GetUserMessageType();
   393 }
   394 
   395 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
   396 {
   397 	char *msgname;
   398 	pCtx->LocalToString(params[1], &msgname);
   399 
   400 	return g_UserMsgs.GetMessageIndex(msgname);
   401 }
   402 
   403 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
   404 {
   405 	char *msgname;
   406 
   407 	pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
   408 
   409 	return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
   410 }
   411 
   412 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
   413 {
   414 	char *msgname;
   415 	cell_t *cl_array;
   416 	unsigned int numClients;
   417 	int msgid;
   418 	int client;
   419 	CPlayer *pPlayer = NULL;
   420 
   421 	if (g_IsMsgInExec)
   422 	{
   423 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   424 	}
   425 
   426 	pCtx->LocalToString(params[1], &msgname);
   427 
   428 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   429 	{
   430 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   431 	}
   432 
   433 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   434 
   435 	numClients = params[3];
   436 
   437 	/* Client validation */
   438 	for (unsigned int i = 0; i < numClients; i++)
   439 	{
   440 		client = cl_array[i];
   441 		pPlayer = g_Players.GetPlayerByIndex(client);
   442 
   443 		if (!pPlayer)
   444 		{
   445 			return pCtx->ThrowNativeError("Client index %d is invalid", client);
   446 		} else if (!pPlayer->IsConnected()) {
   447 			return pCtx->ThrowNativeError("Client %d is not connected", client);
   448 		}
   449 	}
   450 
   451 #ifdef USE_PROTOBUF_USERMESSAGES
   452 	protobuf::Message *msg = g_UserMsgs.StartProtobufMessage(msgid, cl_array, numClients, params[4]);
   453 	if (!msg)
   454 	{
   455 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   456 	}
   457 
   458 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), pCtx->GetIdentity(), g_pCoreIdent, NULL);
   459 #else
   460 	bf_write *pBitBuf = g_UserMsgs.StartBitBufMessage(msgid, cl_array, numClients, params[4]);
   461 	if (!pBitBuf)
   462 	{
   463 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   464 	}
   465 
   466 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   467 #endif
   468 
   469 	g_IsMsgInExec = true;
   470 
   471 	return g_CurMsgHandle;
   472 }
   473 
   474 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   475 {
   476 	cell_t *cl_array;
   477 	unsigned int numClients;
   478 	int client;
   479 	CPlayer *pPlayer = NULL;
   480 	int msgid = params[1];
   481 
   482 	if (g_IsMsgInExec)
   483 	{
   484 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   485 	}
   486 
   487 	if (msgid < 0 || msgid >= 255)
   488 	{
   489 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   490 	}
   491 
   492 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   493 
   494 	numClients = params[3];
   495 
   496 	/* Client validation */
   497 	for (unsigned int i = 0; i < numClients; i++)
   498 	{
   499 		client = cl_array[i];
   500 		pPlayer = g_Players.GetPlayerByIndex(client);
   501 
   502 		if (!pPlayer)
   503 		{
   504 			return pCtx->ThrowNativeError("Client index %d is invalid", client);
   505 		} else if (!pPlayer->IsConnected()) {
   506 			return pCtx->ThrowNativeError("Client %d is not connected", client);
   507 		}
   508 	}
   509 
   510 #ifdef USE_PROTOBUF_USERMESSAGES
   511 	protobuf::Message *msg = g_UserMsgs.StartProtobufMessage(msgid, cl_array, numClients, params[4]);
   512 	if (!msg)
   513 	{
   514 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   515 	}
   516 
   517 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), pCtx->GetIdentity(), g_pCoreIdent, NULL);
   518 #else
   519 	bf_write *pBitBuf = g_UserMsgs.StartBitBufMessage(msgid, cl_array, numClients, params[4]);
   520 	if (!pBitBuf)
   521 	{
   522 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   523 	}
   524 
   525 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   526 #endif
   527 	g_IsMsgInExec = true;
   528 
   529 	return g_CurMsgHandle;
   530 }
   531 
   532 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
   533 {
   534 	HandleSecurity sec;
   535 
   536 	if (!g_IsMsgInExec)
   537 	{
   538 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   539 	}
   540 
   541 	g_UserMsgs.EndMessage();
   542 
   543 	sec.pOwner = pCtx->GetIdentity();
   544 	sec.pIdentity = g_pCoreIdent;
   545 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   546 
   547 	g_IsMsgInExec = false;
   548 
   549 	return 1;
   550 }
   551 
   552 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   553 {
   554 	IPluginFunction *pHook, *pNotify;
   555 	MsgListenerWrapper *pListener;
   556 	bool intercept;
   557 	int msgid = params[1];
   558 
   559 	if (msgid < 0 || msgid >= 255)
   560 	{
   561 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   562 	}
   563 
   564 	pHook = pCtx->GetFunctionById(params[2]);
   565 	if (!pHook)
   566 	{
   567 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   568 	}
   569 	pNotify = pCtx->GetFunctionById(params[4]);
   570 	intercept = (params[3]) ? true : false;
   571 
   572 	pListener = s_UsrMessageNatives.CreateListener(pCtx);
   573 	pListener->Initialize(msgid, pHook, pNotify, intercept);
   574 
   575 	g_UserMsgs.HookUserMessage2(msgid, pListener, intercept);
   576 
   577 	return 1;
   578 }
   579 
   580 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
   581 {
   582 	IPluginFunction *pFunc;
   583 	MsgListenerWrapper *pListener;
   584 	MsgWrapperIter iter;
   585 	bool intercept;
   586 	int msgid = params[1];
   587 
   588 	if (msgid < 0 || msgid >= 255)
   589 	{
   590 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   591 	}
   592 
   593 	pFunc = pCtx->GetFunctionById(params[2]);
   594 	if (!pFunc)
   595 	{
   596 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   597 	}
   598 	intercept = (params[3]) ? true : false;
   599 
   600 	if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
   601 	{
   602 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   603 	}
   604 
   605 	pListener = (*iter);
   606 	if (!g_UserMsgs.UnhookUserMessage2(msgid, pListener, intercept))
   607 	{
   608 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   609 	}
   610 
   611 	s_UsrMessageNatives.DeleteListener(pCtx, iter);
   612 
   613 	return 1;
   614 }
   615 
   616 REGISTER_NATIVES(usrmsgnatives)
   617 {
   618 	{"GetUserMessageType",			smn_GetUserMessageType},
   619 	{"GetUserMessageId",			smn_GetUserMessageId},
   620 	{"GetUserMessageName",			smn_GetUserMessageName},
   621 	{"StartMessage",				smn_StartMessage},
   622 	{"StartMessageEx",				smn_StartMessageEx},
   623 	{"EndMessage",					smn_EndMessage},
   624 	{"HookUserMessage",				smn_HookUserMessage},
   625 	{"UnhookUserMessage",			smn_UnhookUserMessage},
   626 	{NULL,							NULL}
   627 };