core/smn_usermsgs.cpp
author David Anderson <dvander@alliedmods.net>
Sat Mar 10 21:12:40 2007 +0000 (2007-03-10)
changeset 590 cb5add383a86
parent 553 a8efe0286237
child 592 c2a2c38f1331
permissions -rw-r--r--
reorganized this a bit
     1 /**
     2 * vim: set ts=4 :
     3 * ===============================================================
     4 * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5 * ===============================================================
     6 *
     7 * This file is not open source and may not be copied without explicit
     8 * written permission of AlliedModders LLC.  This file may not be redistributed 
     9 * in whole or significant part.
    10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11 *
    12 * Version: $Id$
    13 */
    14 
    15 #include "HandleSys.h"
    16 #include "PluginSys.h"
    17 #include "CUserMessages.h"
    18 #include "smn_usermsgs.h"
    19 
    20 HandleType_t g_WrBitBufType;
    21 Handle_t g_CurMsgHandle;
    22 int g_MsgPlayers[256];
    23 bool g_IsMsgInExec = false;
    24 
    25 class UsrMessageNatives :
    26 	public SMGlobalClass,
    27 	public IHandleTypeDispatch,
    28 	public IPluginsListener
    29 {
    30 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
    31 	void OnSourceModAllInitialized();
    32 	void OnSourceModShutdown();
    33 	void OnHandleDestroy(HandleType_t type, void *object);
    34 	void OnPluginUnloaded(IPlugin *plugin);
    35 public:
    36 	MsgListenerWrapper *GetNewListener(IPluginContext *pCtx);
    37 	MsgListenerWrapper *FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
    38 	bool RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept);
    39 private:
    40 	CStack<MsgListenerWrapper *> m_FreeListeners;
    41 };
    42 
    43 void UsrMessageNatives::OnSourceModAllInitialized()
    44 {
    45 	g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    46 	g_PluginSys.AddPluginsListener(this);
    47 }
    48 
    49 void UsrMessageNatives::OnSourceModShutdown()
    50 {
    51 	g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
    52 	g_PluginSys.RemovePluginsListener(this);
    53 	g_WrBitBufType = 0;
    54 }
    55 
    56 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
    57 {
    58 }
    59 
    60 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
    61 {
    62 	List<MsgListenerWrapper *> *wrapper_list;
    63 
    64 	if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list), true))
    65 	{
    66 		List<MsgListenerWrapper *>::iterator iter;
    67 		MsgListenerWrapper *listener;
    68 
    69 		for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++)
    70 		{
    71 			listener = (*iter);
    72 			if (g_UserMsgs.UnhookUserMessage(listener->GetMessageId(), listener, listener->IsInterceptHook()))
    73 			{
    74 				m_FreeListeners.push(listener);
    75 			}
    76 		}
    77 
    78 		delete wrapper_list;
    79 	}
    80 }
    81 
    82 MsgListenerWrapper *UsrMessageNatives::GetNewListener(IPluginContext *pCtx)
    83 {
    84 	MsgListenerWrapper *listener_wrapper;
    85 
    86 	if (m_FreeListeners.empty())
    87 	{
    88 		listener_wrapper = new MsgListenerWrapper;
    89 	} else {
    90 		listener_wrapper = m_FreeListeners.front();
    91 		m_FreeListeners.pop();
    92 	}
    93 
    94 	List<MsgListenerWrapper *> *wrapper_list;
    95 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
    96 
    97 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
    98 	{
    99 		wrapper_list = new List<MsgListenerWrapper *>;
   100 		pl->SetProperty("MsgListeners", wrapper_list);
   101 	}
   102 
   103 	wrapper_list->push_back(listener_wrapper);
   104 
   105 	return listener_wrapper;
   106 }
   107 
   108 MsgListenerWrapper *UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
   109 {
   110 	MsgListenerWrapper *listener;
   111 	List<MsgListenerWrapper *> *wrapper_list;
   112 	List<MsgListenerWrapper *>::iterator iter;
   113 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   114 	bool found = false;
   115 
   116 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
   117 	{
   118 		return NULL;
   119 	}
   120 
   121 	for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++)
   122 	{
   123 		listener = (*iter);
   124 		if ((msgid == listener->GetMessageId()) 
   125 			&& (intercept == listener->IsInterceptHook()) 
   126 			&& (pHook == listener->GetHookedFunction()))
   127 		{
   128 			found = true;
   129 			break;
   130 		}
   131 	}
   132 
   133 	return (found) ? listener : NULL;
   134 }
   135 
   136 bool UsrMessageNatives::RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept)
   137 {
   138 	List<MsgListenerWrapper *> *wrapper_list;
   139 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   140 
   141 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
   142 	{
   143 		return false;
   144 	}
   145 
   146 	wrapper_list->remove(listener);
   147 	m_FreeListeners.push(listener);
   148 
   149 	return true;
   150 }
   151 
   152 /***************************************
   153  *                                     *
   154  * USER MESSAGE WRAPPER IMPLEMENTATION *
   155  *                                     *
   156  ***************************************/
   157 
   158 size_t MsgListenerWrapper::PreparePlArray(int *pl_array, IRecipientFilter *pFilter)
   159 {
   160 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
   161 
   162 	for (size_t i=0; i<size; i++)
   163 	{
   164 		pl_array[i] = pFilter->GetRecipientIndex(i);
   165 	}
   166 
   167 	return size;
   168 }
   169 
   170 bool MsgListenerWrapper::IsInterceptHook() const
   171 {
   172 	return m_IsInterceptHook;
   173 }
   174 
   175 int MsgListenerWrapper::GetMessageId() const
   176 {
   177 	return m_MsgId;
   178 }
   179 
   180 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
   181 {
   182 	if (m_Hook)
   183 	{
   184 		return m_Hook;
   185 	} else {
   186 		return m_Intercept;
   187 	}
   188 }
   189 
   190 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
   191 {
   192 	return m_Notify;
   193 }
   194 
   195 void MsgListenerWrapper::InitListener(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
   196 {
   197 	if (intercept)
   198 	{
   199 		m_Intercept = hook;
   200 		m_Hook = NULL;
   201 	} else {
   202 		m_Hook = hook;
   203 		m_Intercept = NULL;
   204 	}
   205 
   206 	if (notify)
   207 	{
   208 		m_Notify = notify;
   209 	} else {
   210 		m_Notify = NULL;
   211 	}
   212 
   213 	m_MsgId = msgid;
   214 	m_IsInterceptHook = intercept;
   215 }
   216 
   217 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   218 {
   219 	cell_t res;
   220 	size_t size = PreparePlArray(g_MsgPlayers, pFilter);
   221 
   222 	m_Hook->PushCell(msg_id);
   223 	m_Hook->PushCell(0); //:TODO: push handle!
   224 	m_Hook->PushArray(g_MsgPlayers, size);
   225 	m_Hook->PushCell(size);
   226 	m_Hook->PushCell(pFilter->IsReliable());
   227 	m_Hook->PushCell(pFilter->IsInitMessage());
   228 	m_Hook->Execute(&res);
   229 }
   230 
   231 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   232 {
   233 	cell_t res = static_cast<cell_t>(Pl_Continue);
   234 	size_t size = PreparePlArray(g_MsgPlayers, pFilter);
   235 
   236 	m_Intercept->PushCell(msg_id);
   237 	m_Intercept->PushCell(0); //:TODO: push handle!
   238 	m_Intercept->PushArray(g_MsgPlayers, size);
   239 	m_Intercept->PushCell(size);
   240 	m_Intercept->PushCell(pFilter->IsReliable());
   241 	m_Intercept->PushCell(pFilter->IsInitMessage());
   242 	m_Intercept->Execute(&res);
   243 
   244 	return static_cast<ResultType>(res);
   245 }
   246 
   247 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
   248 {
   249 	cell_t res;
   250 
   251 	if (!m_Notify)
   252 	{
   253 		return;
   254 	}
   255 
   256 	m_Notify->PushCell(msg_id);
   257 	m_Notify->Execute(&res);
   258 }
   259 
   260 /***************************************
   261  *                                     *
   262  * USER MESSAGE NATIVE IMPLEMENTATIONS *
   263  *                                     *
   264  ***************************************/
   265 
   266 static UsrMessageNatives s_UsrMessageNatives;
   267 
   268 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
   269 {
   270 	char *msgname;
   271 	int err;
   272 	if ((err=pCtx->LocalToString(params[1], &msgname)) != SP_ERROR_NONE)
   273 	{
   274 		pCtx->ThrowNativeErrorEx(err, NULL);
   275 		return 0;
   276 	}
   277 
   278 	return g_UserMsgs.GetMessageIndex(msgname);
   279 }
   280 
   281 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
   282 {
   283 	char *msgname;
   284 
   285 	pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
   286 
   287 	if (!g_UserMsgs.GetMessageName(params[1], msgname, params[3]))
   288 	{
   289 		msgname = "";
   290 		return 0;
   291 	}
   292 
   293 	return 1;
   294 }
   295 
   296 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
   297 {
   298 	char *msgname;
   299 	cell_t *cl_array;
   300 	int msgid, err;
   301 	bf_write *pBitBuf;
   302 
   303 	if (g_IsMsgInExec)
   304 	{
   305 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   306 	}
   307 
   308 	if ((err=pCtx->LocalToString(params[1], &msgname)) != SP_ERROR_NONE)
   309 	{
   310 		pCtx->ThrowNativeErrorEx(err, NULL);
   311 		return 0;
   312 	}
   313 
   314 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   315 	{
   316 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   317 	}
   318 
   319 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   320 
   321 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   322 
   323 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   324 	g_IsMsgInExec = true;
   325 
   326 	return g_CurMsgHandle;
   327 }
   328 
   329 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   330 {
   331 	cell_t *cl_array;
   332 	bf_write *pBitBuf;
   333 	int msgid = params[1];
   334 
   335 	if (g_IsMsgInExec)
   336 	{
   337 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   338 	}
   339 
   340 	if (msgid < 0 || msgid >= 255)
   341 	{
   342 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   343 	}
   344 
   345 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   346 
   347 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   348 
   349 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   350 	g_IsMsgInExec = true;
   351 
   352 	return g_CurMsgHandle;
   353 }
   354 
   355 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
   356 {
   357 	HandleSecurity sec;
   358 
   359 	if (!g_IsMsgInExec)
   360 	{
   361 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   362 	}
   363 
   364 	g_UserMsgs.EndMessage();
   365 
   366 	sec.pOwner = pCtx->GetIdentity();
   367 	sec.pIdentity = g_pCoreIdent;
   368 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   369 
   370 	g_IsMsgInExec = false;
   371 
   372 	return 1;
   373 }
   374 
   375 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   376 {
   377 	IPluginFunction *pHook, *pNotify;
   378 	MsgListenerWrapper *listener;
   379 	bool intercept;
   380 	int msgid = params[1];
   381 
   382 	if (msgid < 0 || msgid >= 255)
   383 	{
   384 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   385 	}
   386 
   387 	pHook = pCtx->GetFunctionById(params[2]);
   388 	if (!pHook)
   389 	{
   390 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   391 	}
   392 	pNotify = pCtx->GetFunctionById(params[4]);
   393 
   394 	intercept = (params[3]) ? true : false;
   395 	listener = s_UsrMessageNatives.GetNewListener(pCtx);
   396 	listener->InitListener(msgid, pHook, pNotify, intercept);
   397 
   398 	g_UserMsgs.HookUserMessage(msgid, listener, intercept);
   399 
   400 	return 1;
   401 }
   402 
   403 static cell_t smn_UnHookUserMessage(IPluginContext *pCtx, const cell_t *params)
   404 {
   405 	IPluginFunction *pFunc;
   406 	MsgListenerWrapper *listener;
   407 	bool intercept;
   408 	int msgid = params[1];
   409 
   410 	if (msgid < 0 || msgid >= 255)
   411 	{
   412 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   413 	}
   414 
   415 	pFunc = pCtx->GetFunctionById(params[2]);
   416 	if (!pFunc)
   417 	{
   418 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   419 	}
   420 
   421 	intercept = (params[3]) ? true : false;
   422 	listener = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
   423 	if (!listener)
   424 	{
   425 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   426 	}
   427 
   428 	if (!g_UserMsgs.UnhookUserMessage(msgid, listener, intercept))
   429 	{
   430 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   431 	}
   432 
   433 	s_UsrMessageNatives.RemoveListener(pCtx, listener, intercept);
   434 
   435 	return 1;
   436 }
   437 
   438 REGISTER_NATIVES(usrmsgnatives)
   439 {
   440 	{"GetUserMessageId",			smn_GetUserMessageId},
   441 	{"GetUserMessageName",			smn_GetUserMessageName},
   442 	{"StartMessage",				smn_StartMessage},
   443 	{"StartMessageEx",				smn_StartMessageEx},
   444 	{"EndMessage",					smn_EndMessage},
   445 	{"HookUserMessage",				smn_HookUserMessage},
   446 	{"UnHookUserMessage",			smn_UnHookUserMessage},
   447 	{NULL,							NULL}
   448 };