extensions/sdktools/voice.cpp
author Fyren <fyrenmoo@gmail.com>
Sat Jun 05 23:15:29 2010 -0700 (2010-06-05)
changeset 3031 e45ed10cb5ba
parent 2797 1b63e6a22664
child 3210 61d9463f0765
permissions -rw-r--r--
Fix sdktools voice memory corruption (bug 4415, r=dvander)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     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 <extension.h>
    33 #include <hooks.h>
    34 
    35 #define SPEAK_NORMAL		0
    36 #define SPEAK_MUTED			1
    37 #define SPEAK_ALL			2
    38 #define SPEAK_LISTENALL		4
    39 #define SPEAK_TEAM			8
    40 #define SPEAK_LISTENTEAM	16
    41 #define LISTEN_DEFAULT		0
    42 #define LISTEN_NO			1
    43 #define LISTEN_YES			2
    44 
    45 /* this enum needs to be in the order as the LISTEN_ defines above to keep
    46    the old broken SetClientListening functionality untouched */
    47 enum ListenOverride
    48 {
    49 	Listen_Default,		/**< Leave it up to the game */
    50 	Listen_No,			/**< Can't hear */
    51 	Listen_Yes,			/**< Can hear */
    52 };
    53 
    54 size_t g_VoiceFlags[65];
    55 size_t g_VoiceHookCount = 0;
    56 int g_ClientOverrides[65];
    57 ListenOverride g_VoiceMap[65][65];
    58 bool g_ClientMutes[65][65];
    59 
    60 SH_DECL_HOOK3(IVoiceServer, SetClientListening, SH_NOATTRIB, 0, bool, int, int, bool);
    61 
    62 #if SOURCE_ENGINE >= SE_ORANGEBOX
    63 SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &);
    64 #else
    65 SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
    66 #endif
    67 
    68 bool DecHookCount(int amount = 1);
    69 bool DecHookCount(int amount)
    70 {
    71 	g_VoiceHookCount -= amount;
    72 	assert(g_VoiceHookCount);
    73 	if (g_VoiceHookCount == 0)
    74 	{
    75 		SH_REMOVE_HOOK_MEMFUNC(IVoiceServer, SetClientListening, voiceserver, &g_SdkTools, &SDKTools::OnSetClientListening, false);
    76 		return true;
    77 	}
    78 
    79 	return false;
    80 }
    81 
    82 void IncHookCount()
    83 {
    84 	if (!g_VoiceHookCount++)
    85 	{
    86 		SH_ADD_HOOK_MEMFUNC(IVoiceServer, SetClientListening, voiceserver, &g_SdkTools, &SDKTools::OnSetClientListening, false);
    87 	}
    88 }
    89 
    90 void SDKTools::VoiceInit()
    91 {
    92 	memset(g_VoiceMap, 0, sizeof(g_VoiceMap));
    93 	memset(g_ClientOverrides, 0, sizeof(g_ClientOverrides));
    94 	memset(g_ClientMutes, 0, sizeof(g_ClientMutes));
    95 
    96 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &SDKTools::OnClientCommand, true);
    97 }
    98 
    99 #if SOURCE_ENGINE >= SE_ORANGEBOX
   100 void SDKTools::OnClientCommand(edict_t *pEntity, const CCommand &args)
   101 {
   102 #else
   103 void SDKTools::OnClientCommand(edict_t *pEntity)
   104 {
   105 	CCommand args;
   106 #endif
   107 	int client = IndexOfEdict(pEntity);
   108 
   109 	if ((args.ArgC() > 1) && (stricmp(args.Arg(0), "vban") == 0))
   110 	{
   111 		for (int i = 1; (i < args.ArgC()) && (i < 3); i++)
   112 		{
   113 			unsigned long mask = 0;
   114 			sscanf(args.Arg(i), "%p", (void**)&mask);
   115 			
   116 			for (int j = 0; j < 32; j++)
   117 			{
   118 				g_ClientMutes[client][1 + j + 32 * (i - 1)] = !!(mask & 1 << j);
   119 			}
   120 		}
   121 	}
   122 
   123 	RETURN_META(MRES_IGNORED);
   124 }
   125 
   126 
   127 bool SDKTools::OnSetClientListening(int iReceiver, int iSender, bool bListen)
   128 {
   129 	if (g_ClientMutes[iReceiver][iSender])
   130 	{
   131 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   132 	}
   133 
   134 	if (g_VoiceMap[iReceiver][iSender] == Listen_No)
   135 	{
   136 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   137 	}
   138 	else if (g_VoiceMap[iReceiver][iSender] == Listen_Yes)
   139 	{
   140 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   141 	}
   142 
   143 	if (g_VoiceFlags[iSender] & SPEAK_MUTED)
   144 	{
   145 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   146 	}
   147 
   148 	if ((g_VoiceFlags[iSender] & SPEAK_ALL) || (g_VoiceFlags[iReceiver] & SPEAK_LISTENALL))
   149 	{
   150 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   151 	}
   152 
   153 	if ((g_VoiceFlags[iSender] & SPEAK_TEAM) || (g_VoiceFlags[iReceiver] & SPEAK_LISTENTEAM))
   154 	{
   155 		IGamePlayer *pReceiver = playerhelpers->GetGamePlayer(iReceiver);
   156 		IGamePlayer *pSender = playerhelpers->GetGamePlayer(iSender);
   157 
   158 		if (pReceiver && pSender && pReceiver->IsInGame() && pSender->IsInGame())
   159 		{
   160 			IPlayerInfo *pRInfo = pReceiver->GetPlayerInfo();
   161 			IPlayerInfo *pSInfo = pSender->GetPlayerInfo();
   162 
   163 			if (pRInfo && pSInfo && pRInfo->GetTeamIndex() == pSInfo->GetTeamIndex())
   164 			{
   165 				RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   166 			}
   167 		}
   168 	}
   169 
   170 	RETURN_META_VALUE(MRES_IGNORED, bListen);
   171 }
   172 
   173 void SDKTools::OnClientDisconnecting(int client)
   174 {
   175 	g_Hooks.OnClientDisconnecting(client);
   176 
   177 	int max_clients = playerhelpers->GetMaxClients();
   178 
   179 	if (g_VoiceHookCount == 0)
   180 	{
   181 		return;
   182 	}
   183 
   184 	/* This can probably be optimized more, but I doubt it's that much 
   185 	 * of an actual bottleneck.
   186 	 */
   187 
   188 	/* Reset other clients who receive from or have muted this client */
   189 	for (int i = 1; i <= max_clients; i++)
   190 	{
   191 		if (i == client)
   192 		{
   193 			continue;
   194 		}
   195 
   196 		g_ClientMutes[i][client] = false;
   197 		
   198 		if (g_VoiceMap[i][client] != Listen_Default)
   199 		{
   200 			g_VoiceMap[i][client] = Listen_Default;
   201 			if (DecHookCount())
   202 			{
   203 				break;
   204 			}
   205 		}
   206 	}
   207 
   208 	/* Reset this client's mutes */
   209 	memset(&g_ClientMutes[client], 0, sizeof(bool) * 65);
   210 
   211 	/* Reset other clients who send to this client */
   212 	if (g_ClientOverrides[client] > 0)
   213 	{
   214 		DecHookCount(g_ClientOverrides[client]);
   215 		g_ClientOverrides[client] = 0;
   216 		memset(&g_VoiceMap[client], false, sizeof(ListenOverride) * 65);
   217 	}
   218 
   219 	if (g_VoiceFlags[client])
   220 	{
   221 		g_VoiceFlags[client] = 0;
   222 		DecHookCount();
   223 	}
   224 }
   225 
   226 static cell_t SetClientListeningFlags(IPluginContext *pContext, const cell_t *params)
   227 {
   228 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   229 	if (player == NULL)
   230 	{
   231 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   232 	}
   233 	else if (!player->IsConnected())
   234 	{
   235 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   236 	}
   237 
   238 	if (!params[2] && g_VoiceFlags[params[1]])
   239 	{
   240 		DecHookCount();
   241 	}
   242 	else if (!g_VoiceFlags[params[1]] && params[2])
   243 	{
   244 		IncHookCount();
   245 	}
   246 
   247 	g_VoiceFlags[params[1]] = params[2];
   248 
   249 	return 1;
   250 }
   251 
   252 static cell_t GetClientListeningFlags(IPluginContext *pContext, const cell_t *params)
   253 {
   254 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   255 	if (player == NULL)
   256 	{
   257 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   258 	}
   259 	else if (!player->IsConnected())
   260 	{
   261 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   262 	}
   263 
   264 	return g_VoiceFlags[params[1]];
   265 }
   266 
   267 static cell_t SetClientListening(IPluginContext *pContext, const cell_t *params)
   268 {
   269 	int r, s;
   270 	IGamePlayer *player;
   271 	
   272 	player = playerhelpers->GetGamePlayer(params[1]);
   273 	if (player == NULL)
   274 	{
   275 		return pContext->ThrowNativeError("Receiver client index %d is invalid", params[1]);
   276 	}
   277 	else if (!player->IsConnected())
   278 	{
   279 		return pContext->ThrowNativeError("Receiver client %d is not connected", params[1]);
   280 	}
   281 
   282 	player = playerhelpers->GetGamePlayer(params[2]);
   283 	if (player == NULL)
   284 	{
   285 		return pContext->ThrowNativeError("Sender client index %d is invalid", params[2]);
   286 	}
   287 	else if (!player->IsConnected())
   288 	{
   289 		return pContext->ThrowNativeError("Sender client %d is not connected", params[2]);
   290 	}
   291 
   292 	r = params[1];
   293 	s = params[2];
   294 	
   295 	if (g_VoiceMap[r][s] == Listen_Default && params[3] != Listen_Default)
   296 	{
   297 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   298 		g_ClientOverrides[r]++;
   299 		IncHookCount();
   300 	}
   301 	else if (g_VoiceMap[r][s] != Listen_Default && params[3] == Listen_Default)
   302 	{
   303 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   304 		g_ClientOverrides[r]--;
   305 		DecHookCount();
   306 	}
   307 	else
   308 	{
   309 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   310 	}
   311 
   312 	return 1;
   313 }
   314 
   315 static cell_t GetClientListening(IPluginContext *pContext, const cell_t *params)
   316 {
   317 	IGamePlayer *player;
   318 
   319 	player = playerhelpers->GetGamePlayer(params[1]);
   320 	if (player == NULL)
   321 	{
   322 		return pContext->ThrowNativeError("Receiver client index %d is invalid", params[1]);
   323 	}
   324 	else if (!player->IsConnected())
   325 	{
   326 		return pContext->ThrowNativeError("Receiver client %d is not connected", params[1]);
   327 	}
   328 
   329 	player = playerhelpers->GetGamePlayer(params[2]);
   330 	if (player == NULL)
   331 	{
   332 		return pContext->ThrowNativeError("Sender client index %d is invalid", params[2]);
   333 	}
   334 	else if (!player->IsConnected())
   335 	{
   336 		return pContext->ThrowNativeError("Sender client %d is not connected", params[2]);
   337 	}
   338 
   339 	return g_VoiceMap[params[1]][params[2]];
   340 }
   341 
   342 static cell_t IsClientMuted(IPluginContext *pContext, const cell_t *params)
   343 {
   344 	IGamePlayer *player;
   345 
   346 	player = playerhelpers->GetGamePlayer(params[1]);
   347 	if (player == NULL)
   348 	{
   349 		return pContext->ThrowNativeError("Muter client index %d is invalid", params[1]);
   350 	}
   351 	else if (!player->IsConnected())
   352 	{
   353 		return pContext->ThrowNativeError("Muter client %d is not connected", params[1]);
   354 	}
   355 
   356 	player = playerhelpers->GetGamePlayer(params[2]);
   357 	if (player == NULL)
   358 	{
   359 		return pContext->ThrowNativeError("Mutee client index %d is invalid", params[2]);
   360 	}
   361 	else if (!player->IsConnected())
   362 	{
   363 		return pContext->ThrowNativeError("Mutee client %d is not connected", params[2]);
   364 	}
   365 
   366 	return g_ClientMutes[params[1]][params[2]];
   367 }
   368 
   369 sp_nativeinfo_t g_VoiceNatives[] =
   370 {
   371 	{"SetClientListeningFlags",		SetClientListeningFlags},
   372 	{"GetClientListeningFlags",		GetClientListeningFlags},
   373 	{"SetClientListening",			SetClientListening},
   374 	{"GetClientListening",			GetClientListening},
   375 	{"SetListenOverride",			SetClientListening},
   376 	{"GetListenOverride",			GetClientListening},
   377 	{"IsClientMuted",				IsClientMuted},
   378 	{NULL,							NULL},
   379 };