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