extensions/sdktools/vhelpers.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 1954 5d6fd2ddb181
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
     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 "util.h"
    34 #include "vhelpers.h"
    35 
    36 CallHelper s_Teleport;
    37 CallHelper s_GetVelocity;
    38 CallHelper s_EyeAngles;
    39 
    40 class CTraceFilterSimple : public CTraceFilterEntitiesOnly
    41 {
    42 public:
    43 	CTraceFilterSimple(const IHandleEntity *passentity): m_pPassEnt(passentity)
    44 	{
    45 	}
    46 	virtual bool ShouldHitEntity(IHandleEntity *pServerEntity, int contentsMask)
    47 	{
    48 		if (pServerEntity == m_pPassEnt)
    49 		{
    50 			return false;
    51 		}
    52 		return true;
    53 	}
    54 private:
    55 	const IHandleEntity *m_pPassEnt;
    56 };
    57 
    58 bool SetupTeleport()
    59 {
    60 	if (s_Teleport.setup)
    61 	{
    62 		return s_Teleport.supported;
    63 	}
    64 
    65 	/* Setup Teleport */
    66 	int offset;
    67 	if (g_pGameConf->GetOffset("Teleport", &offset))
    68 	{
    69 		PassInfo info[3];
    70 		info[0].flags = info[1].flags = info[2].flags = PASSFLAG_BYVAL;
    71 		info[0].size = info[1].size = info[2].size = sizeof(void *);
    72 		info[0].type = info[1].type = info[2].type = PassType_Basic;
    73 
    74 		s_Teleport.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 3);
    75 
    76 		if (s_Teleport.call != NULL)
    77 		{
    78 			s_Teleport.supported = true;
    79 		}
    80 	}
    81 
    82 	s_Teleport.setup = true;
    83 
    84 	return s_Teleport.supported;
    85 }
    86 
    87 void Teleport(CBaseEntity *pEntity, Vector *origin, QAngle *ang, Vector *velocity)
    88 {
    89 	unsigned char params[sizeof(void *) * 4];
    90 	unsigned char *vptr = params;
    91 	*(CBaseEntity **)vptr = pEntity;
    92 	vptr += sizeof(CBaseEntity *);
    93 	*(Vector **)vptr = origin;
    94 	vptr += sizeof(Vector *);
    95 	*(QAngle **)vptr = ang;
    96 	vptr += sizeof(QAngle *);
    97 	*(Vector **)vptr = velocity;
    98 	
    99 	s_Teleport.call->Execute(params, NULL);
   100 }
   101 
   102 bool IsTeleportSupported()
   103 {
   104 	return SetupTeleport();
   105 }
   106 
   107 bool SetupGetVelocity()
   108 {
   109 	if (s_GetVelocity.setup)
   110 	{
   111 		return s_GetVelocity.supported;
   112 	}
   113 
   114 	int offset;
   115 	if (g_pGameConf->GetOffset("GetVelocity", &offset))
   116 	{
   117 		PassInfo info[2];
   118 		info[0].flags = info[1].flags = PASSFLAG_BYVAL;
   119 		info[0].size = info[1].size = sizeof(void *);
   120 		info[0].type = info[1].type = PassType_Basic;
   121 
   122 		s_GetVelocity.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 2);
   123 
   124 		if (s_GetVelocity.call != NULL)
   125 		{
   126 			s_GetVelocity.supported = true;
   127 		}
   128 	}
   129 
   130 	s_GetVelocity.setup = true;
   131 
   132 	return s_GetVelocity.supported;
   133 }
   134 
   135 void GetVelocity(CBaseEntity *pEntity, Vector *velocity, AngularImpulse *angvelocity)
   136 {
   137 	unsigned char params[sizeof(void *) * 3];
   138 	unsigned char *vptr = params;
   139 	*(CBaseEntity **)vptr = pEntity;
   140 	vptr += sizeof(CBaseEntity *);
   141 	*(Vector **)vptr = velocity;
   142 	vptr += sizeof(Vector *);
   143 	*(AngularImpulse **)vptr = angvelocity;
   144 
   145 	s_GetVelocity.call->Execute(params, NULL);
   146 }
   147 
   148 bool IsGetVelocitySupported()
   149 {
   150 	return SetupGetVelocity();
   151 }
   152 
   153 bool SetupGetEyeAngles()
   154 {
   155 	if (s_EyeAngles.setup)
   156 	{
   157 		return s_EyeAngles.supported;
   158 	}
   159 
   160 	int offset;
   161 	if (g_pGameConf->GetOffset("EyeAngles", &offset))
   162 	{
   163 		PassInfo info[2];
   164 		info[0].flags = info[1].flags = PASSFLAG_BYVAL;
   165 		info[0].size = info[1].size = sizeof(void *);
   166 		info[0].type = info[1].type = PassType_Basic;
   167 
   168 		s_EyeAngles.call = g_pBinTools->CreateVCall(offset, 0, 0, &info[0], &info[1], 1);
   169 
   170 		if (s_EyeAngles.call != NULL)
   171 		{
   172 			s_EyeAngles.supported = true;
   173 		}
   174 	}
   175 
   176 	s_EyeAngles.setup = true;
   177 
   178 	return s_EyeAngles.supported;
   179 }
   180 
   181 bool GetEyeAngles(CBaseEntity *pEntity, QAngle *pAngles)
   182 {
   183 	if (!IsEyeAnglesSupported())
   184 	{
   185 		return false;
   186 	}
   187 
   188 	QAngle *pRetAngle = NULL;
   189 	unsigned char params[sizeof(void *)];
   190 	unsigned char *vptr = params;
   191 
   192 	*(CBaseEntity **)vptr = pEntity;
   193 	vptr += sizeof(CBaseEntity *);
   194 
   195 	s_EyeAngles.call->Execute(params, &pRetAngle);
   196 
   197 	if (pRetAngle == NULL)
   198 	{
   199 		return false;
   200 	}
   201 
   202 	*pAngles = *pRetAngle;
   203 
   204 	return true;
   205 }
   206 
   207 int GetClientAimTarget(edict_t *pEdict, bool only_players)
   208 {
   209 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   210 
   211 	if (pEntity == NULL)
   212 	{
   213 		return -1;
   214 	}
   215 
   216 	Vector eye_position;
   217 	QAngle eye_angles;
   218 
   219 	/* Get the private information we need */
   220 	serverClients->ClientEarPosition(pEdict, &eye_position);
   221 	if (!GetEyeAngles(pEntity, &eye_angles))
   222 	{
   223 		return -2;
   224 	}
   225 
   226 	Vector aim_dir;
   227 	AngleVectors(eye_angles, &aim_dir);
   228 	VectorNormalize(aim_dir);
   229 
   230 	Vector vec_end = eye_position + aim_dir * 8000;
   231 
   232 	Ray_t ray;
   233 	ray.Init(eye_position, vec_end);
   234 
   235 	trace_t tr;
   236 	CTraceFilterSimple simple(pEdict->GetIServerEntity());
   237 
   238 	enginetrace->TraceRay(ray, MASK_SOLID|CONTENTS_DEBRIS|CONTENTS_HITBOX, &simple, &tr);
   239 
   240 	if (tr.fraction == 1.0f || tr.m_pEnt == NULL)
   241 	{
   242 		return -1;
   243 	}
   244 
   245 	edict_t *pTarget = gameents->BaseEntityToEdict(tr.m_pEnt);
   246 	if (pTarget == NULL)
   247 	{
   248 		return -1;
   249 	}
   250 
   251 	int ent_index = engine->IndexOfEdict(pTarget);
   252 
   253 	IGamePlayer *pTargetPlayer = playerhelpers->GetGamePlayer(ent_index);
   254 	if (pTargetPlayer != NULL && !pTargetPlayer->IsInGame())
   255 	{
   256 		return -1;
   257 	}
   258 	else if (only_players && pTargetPlayer == NULL)
   259 	{
   260 		return -1;
   261 	}
   262 
   263 	return ent_index;
   264 }
   265 
   266 bool IsEyeAnglesSupported()
   267 {
   268 	return SetupGetEyeAngles();
   269 }
   270 
   271 bool GetPlayerInfo(int client, player_info_t *info)
   272 {
   273 #if defined ORANGEBOX_BUILD
   274 	return engine->GetPlayerInfo(client, info);
   275 #else
   276 	return (iserver) ? iserver->GetPlayerInfo(client-1, info) : false;
   277 #endif
   278 }
   279 
   280 void ShutdownHelpers()
   281 {
   282 	s_Teleport.Shutdown();
   283 	s_GetVelocity.Shutdown();
   284 }
   285 
   286 const char *GetDTTypeName(int type)
   287 {
   288 	switch (type)
   289 	{
   290 	case DPT_Int:
   291 		{
   292 			return "integer";
   293 		}
   294 	case DPT_Float:
   295 		{
   296 			return "float";
   297 		}
   298 	case DPT_Vector:
   299 		{
   300 			return "vector";
   301 		}
   302 	case DPT_String:
   303 		{
   304 			return "string";
   305 		}
   306 	case DPT_Array:
   307 		{
   308 			return "array";
   309 		}
   310 	case DPT_DataTable:
   311 		{
   312 			return "datatable";
   313 		}
   314 	default:
   315 		{
   316 			return NULL;
   317 		}
   318 	}
   319 
   320 	return NULL;
   321 }
   322 
   323 void UTIL_DrawSendTable_XML(FILE *fp, SendTable *pTable, int space_count)
   324 {
   325 	char spaces[255];
   326 
   327 	for (int i = 0; i < space_count; i++)
   328 	{
   329 		spaces[i] = ' ';
   330 	}
   331 	spaces[space_count] = '\0';
   332 
   333 	const char *type_name;
   334 	SendTable *pOtherTable;
   335 	SendProp *pProp;
   336 
   337 	fprintf(fp, " %s<sendtable name=\"%s\">\n", spaces, pTable->GetName());
   338 	for (int i = 0; i < pTable->GetNumProps(); i++)
   339 	{
   340 		pProp = pTable->GetProp(i);
   341 
   342 		fprintf(fp, "  %s<property name=\"%s\">\n", spaces, pProp->GetName());
   343 
   344 		if ((type_name = GetDTTypeName(pProp->GetType())) != NULL)
   345 		{
   346 			fprintf(fp, "   %s<type>%s</type>\n", spaces, type_name);
   347 		}
   348 		else
   349 		{
   350 			fprintf(fp, "   %s<type>%d</type>\n", spaces, pProp->GetType());
   351 		}
   352 
   353 		fprintf(fp, "   %s<offset>%d</offset>\n", spaces, pProp->GetOffset());
   354 		fprintf(fp, "   %s<bits>%d</bits>\n", spaces, pProp->m_nBits);
   355 
   356 		if ((pOtherTable = pTable->GetProp(i)->GetDataTable()) != NULL)
   357 		{
   358 			UTIL_DrawSendTable_XML(fp, pOtherTable, space_count + 3);
   359 		}
   360 
   361 		fprintf(fp, "  %s</property>\n", spaces);
   362 	}
   363 	fprintf(fp, " %s</sendtable>\n", spaces);
   364 }
   365 
   366 void UTIL_DrawServerClass_XML(FILE *fp, ServerClass *sc)
   367 {
   368 	fprintf(fp, "<serverclass name=\"%s\">\n", sc->GetName());
   369 	UTIL_DrawSendTable_XML(fp, sc->m_pTable, 0);
   370 	fprintf(fp, "</serverclass>\n");
   371 }
   372 
   373 void UTIL_DrawSendTable(FILE *fp, SendTable *pTable, int level)
   374 {
   375 	char spaces[255];
   376 	for (int i=0; i<level; i++)
   377 		spaces[i] = ' ';
   378 	spaces[level] = '\0';
   379 
   380 	const char *name, *type;
   381 	SendProp *pProp;
   382 
   383 	fprintf(fp, "%sSub-Class Table (%d Deep): %s\n", spaces, level, pTable->GetName());
   384 
   385 	for (int i=0; i<pTable->GetNumProps(); i++)
   386 	{
   387 		pProp = pTable->GetProp(i);
   388 		name = pProp->GetName();
   389 		if (pProp->GetDataTable())
   390 		{
   391 			UTIL_DrawSendTable(fp, pProp->GetDataTable(), level + 1);
   392 		}
   393 		else
   394 		{
   395 			type = GetDTTypeName(pProp->GetType());
   396 
   397 			if (type != NULL)
   398 			{
   399 				fprintf(fp,
   400 					"%s-Member: %s (offset %d) (type %s) (bits %d)\n", 
   401 					spaces, 
   402 					pProp->GetName(),
   403 					pProp->GetOffset(),
   404 					type,
   405 					pProp->m_nBits);
   406 			}
   407 			else
   408 			{
   409 				fprintf(fp,
   410 					"%s-Member: %s (offset %d) (type %d) (bits %d)\n", 
   411 					spaces, 
   412 					pProp->GetName(),
   413 					pProp->GetOffset(),
   414 					pProp->GetType(),
   415 					pProp->m_nBits);
   416 			}
   417 		}
   418 	}
   419 }
   420 
   421 CON_COMMAND(sm_dump_netprops_xml, "Dumps the networkable property table as an XML file")
   422 {
   423 #if !defined ORANGEBOX_BUILD
   424 	CCommand args;
   425 #endif
   426 
   427 	if (args.ArgC() < 2)
   428 	{
   429 		META_CONPRINT("Usage: sm_dump_netprops_xml <file>\n");
   430 		return;
   431 	}
   432 
   433 	const char *file = args.Arg(1);
   434 	if (!file || file[0] == '\0')
   435 	{
   436 		META_CONPRINT("Usage: sm_dump_netprops_xml <file>\n");
   437 		return;
   438 	}
   439 
   440 	char path[PLATFORM_MAX_PATH];
   441 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   442 
   443 	FILE *fp = NULL;
   444 	if ((fp = fopen(path, "wt")) == NULL)
   445 	{
   446 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   447 		return;
   448 	}
   449 
   450 	fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
   451 	fprintf(fp, "<!-- Dump of all network properties for \"%s\" follows -->\n\n", g_pSM->GetGameFolderName());
   452 
   453 	ServerClass *pBase = gamedll->GetAllServerClasses();
   454 	while (pBase != NULL)
   455 	{
   456 		UTIL_DrawServerClass_XML(fp, pBase);
   457 		pBase = pBase->m_pNext;
   458 	}
   459 
   460 	fclose(fp);
   461 }
   462 
   463 CON_COMMAND(sm_dump_netprops, "Dumps the networkable property table as a text file")
   464 {
   465 #if !defined ORANGEBOX_BUILD
   466 	CCommand args;
   467 #endif
   468 
   469 	if (args.ArgC() < 2)
   470 	{
   471 		META_CONPRINT("Usage: sm_dump_netprops <file>\n");
   472 		return;
   473 	}
   474 
   475 	const char *file = args.Arg(1);
   476 	if (!file || file[0] == '\0')
   477 	{
   478 		META_CONPRINT("Usage: sm_dump_netprops <file>\n");
   479 		return;
   480 	}
   481 
   482 	char path[PLATFORM_MAX_PATH];
   483 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   484 
   485 	FILE *fp = NULL;
   486 	if ((fp = fopen(path, "wt")) == NULL)
   487 	{
   488 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   489 		return;
   490 	}
   491 
   492 	fprintf(fp, "// Dump of all network properties for \"%s\" follows\n//\n\n", g_pSM->GetGameFolderName());
   493 
   494 	ServerClass *pBase = gamedll->GetAllServerClasses();
   495 	while (pBase != NULL)
   496 	{
   497 		fprintf(fp, "%s:\n", pBase->GetName());
   498 		UTIL_DrawSendTable(fp, pBase->m_pTable, 1);
   499 		pBase = pBase->m_pNext;
   500 	}
   501 
   502 	fclose(fp);
   503 }
   504 
   505 #if defined SUBPLATFORM_SECURECRT
   506 void _ignore_invalid_parameter(
   507 						const wchar_t * expression,
   508 						const wchar_t * function, 
   509 						const wchar_t * file, 
   510 						unsigned int line,
   511 						uintptr_t pReserved
   512 						)
   513 {
   514 	/* Wow we don't care, thanks Microsoft. */
   515 }
   516 #endif
   517 
   518 CON_COMMAND(sm_dump_classes, "Dumps the class list as a text file")
   519 {
   520 #if !defined ORANGEBOX_BUILD
   521 	CCommand args;
   522 #endif
   523 
   524 	if (args.ArgC() < 2)
   525 	{
   526 		META_CONPRINT("Usage: sm_dump_classes <file>\n");
   527 		return;
   528 	}
   529 
   530 	const char *file = args.Arg(1);
   531 	if (!file || file[0] == '\0')
   532 	{
   533 		META_CONPRINT("Usage: sm_dump_classes <file>\n");
   534 		return;
   535 	}
   536 
   537 	ICallWrapper *pWrapper = NULL;
   538 
   539 	if (!pWrapper)
   540 	{
   541 		PassInfo retData;
   542 		retData.flags = PASSFLAG_BYVAL;
   543 		retData.size = sizeof(void *);
   544 		retData.type = PassType_Basic;
   545 
   546 		void *addr;
   547 		if (!g_pGameConf->GetMemSig("EntityFactory", &addr) || addr == NULL)
   548 		{
   549 			META_CONPRINT("Failed to locate function\n");
   550 			return;
   551 		}
   552 
   553 		pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retData, NULL, 0);
   554 	}
   555 
   556 
   557 	void *returnData = NULL;
   558 
   559 	pWrapper->Execute(NULL, &returnData);
   560 
   561 	pWrapper->Destroy();
   562 
   563 	if (returnData == NULL)
   564 	{
   565 		return;
   566 	}
   567 
   568 	CEntityFactoryDictionary *dict = ( CEntityFactoryDictionary * )returnData;
   569 
   570 	if ( !dict )
   571 	{
   572 		return;
   573 	}
   574 
   575 	char path[PLATFORM_MAX_PATH];
   576 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   577 
   578 	FILE *fp = NULL;
   579 	if ((fp = fopen(path, "wt")) == NULL)
   580 	{
   581 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   582 		return;
   583 	}
   584 
   585 	char buffer[80];
   586 	buffer[0] = 0;
   587 
   588 #if defined SUBPLATFORM_SECURECRT
   589 	_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
   590 #endif
   591 
   592 	time_t t = g_pSM->GetAdjustedTime();
   593 	size_t written = strftime(buffer, sizeof(buffer), "%d/%m/%Y", localtime(&t));
   594 
   595 #if defined SUBPLATFORM_SECURECRT
   596 	_set_invalid_parameter_handler(handler);
   597 #endif
   598 
   599 	fprintf(fp, "// Dump of all classes for \"%s\" as at %s\n//\n\n", g_pSM->GetGameFolderName(), buffer);
   600 
   601 	for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
   602 	{
   603 		IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i));
   604 		ServerClass *sclass = entity->GetServerClass();
   605 		fprintf(fp,"%s - %s\n",sclass->GetName(), dict->m_Factories.GetElementName(i));
   606 
   607 		typedescription_t *datamap = gamehelpers->FindInDataMap(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags");
   608 		
   609 		int *eflags = (int *)((char *)entity->GetBaseEntity() + datamap->fieldOffset[TD_OFFSET_NORMAL]);
   610 		*eflags |= (1<<0); // EFL_KILLME
   611 	}
   612 
   613 	fclose(fp);
   614 
   615 }
   616 
   617