extensions/cstrike/natives.cpp
author David Anderson <dvander@alliedmods.net>
Sat Aug 10 21:23:02 2013 -0700 (2013-08-10)
changeset 3982 be7835a7c28f
parent 3965 86dedf4462d8
child 4016 ecd3735e908b
permissions -rw-r--r--
Move tracker related opcodes entirely to C++.

The tracker related opcodes:
GENARRAY
GENARRAY_Z
TRACKER_POP_SETHEAP
TRACKER_PUSH_C

All contain some vastly overcomplicated assembly containing logic that could
be implemented much easier in C++. If it were a performance concern, these
opcodes would be entirely in C++, but most of them call out to one or more
routines to do additional work. This patch just moves most of the logic out
to C++ to reduce complexity and fix reported bugs.
faluco@1441
     1
/**
faluco@1441
     2
 * vim: set ts=4 :
faluco@1441
     3
 * =============================================================================
faluco@1441
     4
 * SourceMod Counter-Strike:Source Extension
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
faluco@1441
     6
 * =============================================================================
faluco@1441
     7
 *
faluco@1441
     8
 * This program is free software; you can redistribute it and/or modify it under
faluco@1441
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
faluco@1441
    10
 * Free Software Foundation.
faluco@1441
    11
 * 
faluco@1441
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
faluco@1441
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
faluco@1441
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
faluco@1441
    15
 * details.
faluco@1441
    16
 *
faluco@1441
    17
 * You should have received a copy of the GNU General Public License along with
faluco@1441
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
faluco@1441
    19
 *
faluco@1441
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
faluco@1441
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
faluco@1441
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
faluco@1441
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
faluco@1441
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
faluco@1441
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
faluco@1441
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
faluco@1441
    27
 * or <http://www.sourcemod.net/license.php>.
faluco@1441
    28
 *
faluco@1441
    29
 * Version: $Id$
faluco@1441
    30
 */
faluco@1441
    31
faluco@1441
    32
#include "extension.h"
faluco@1441
    33
#include "RegNatives.h"
rgr_02@3519
    34
#include "forwards.h"
rgr_02@3519
    35
#include "util_cstrike.h"
rgr_02@3313
    36
#include <server_class.h>
faluco@1441
    37
rgr_02@3519
    38
int g_iPriceOffset = -1;
rgr_02@3519
    39
faluco@1441
    40
#define REGISTER_NATIVE_ADDR(name, code) \
faluco@1441
    41
	void *addr; \
rgr_02@3313
    42
	if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \
faluco@1441
    43
	{ \
faluco@1441
    44
		return pContext->ThrowNativeError("Failed to locate function"); \
faluco@1441
    45
	} \
faluco@1441
    46
	code; \
faluco@1441
    47
	g_RegNatives.Register(pWrapper);
faluco@1441
    48
faluco@1441
    49
inline CBaseEntity *GetCBaseEntity(int num, bool isplayer)
faluco@1441
    50
{
rgr_02@3519
    51
	edict_t *pEdict = gamehelpers->EdictOfIndex(num);
faluco@1441
    52
	if (!pEdict || pEdict->IsFree())
faluco@1441
    53
	{
faluco@1441
    54
		return NULL;
faluco@1441
    55
	}
faluco@1441
    56
dvander@1857
    57
	if (num > 0 && num <= playerhelpers->GetMaxClients())
faluco@1441
    58
	{
faluco@1441
    59
		IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(pEdict);
faluco@1441
    60
		if (!pPlayer || !pPlayer->IsConnected())
faluco@1441
    61
		{
faluco@1441
    62
			return NULL;
faluco@1441
    63
		}
dvander@1857
    64
	}
dvander@1857
    65
	else if (isplayer) 
dvander@1857
    66
	{
faluco@1441
    67
		return NULL;
faluco@1441
    68
	}
faluco@1441
    69
faluco@1441
    70
	IServerUnknown *pUnk;
faluco@1441
    71
	if ((pUnk=pEdict->GetUnknown()) == NULL)
faluco@1441
    72
	{
faluco@1441
    73
		return NULL;
faluco@1441
    74
	}
faluco@1441
    75
faluco@1441
    76
	return pUnk->GetBaseEntity();
faluco@1441
    77
}
faluco@1441
    78
rgr_02@3313
    79
bool UTIL_FindDataTable(SendTable *pTable, const char *name, sm_sendprop_info_t *info, unsigned int offset)
rgr_02@3313
    80
{
rgr_02@3313
    81
	const char *pname;
rgr_02@3313
    82
	int props = pTable->GetNumProps();
rgr_02@3313
    83
	SendProp *prop;
rgr_02@3313
    84
	SendTable *table;
rgr_02@3313
    85
rgr_02@3313
    86
	for (int i=0; i<props; i++)
rgr_02@3313
    87
	{
rgr_02@3313
    88
		prop = pTable->GetProp(i);
rgr_02@3313
    89
rgr_02@3313
    90
		if ((table = prop->GetDataTable()) != NULL)
rgr_02@3313
    91
		{
rgr_02@3313
    92
			pname = table->GetName();
rgr_02@3313
    93
			if (pname && strcmp(name, pname) == 0)
rgr_02@3313
    94
			{
rgr_02@3313
    95
				info->prop = prop;
rgr_02@3313
    96
				info->actual_offset = offset + info->prop->GetOffset();
rgr_02@3313
    97
				return true;
rgr_02@3313
    98
			}
rgr_02@3313
    99
rgr_02@3313
   100
			if (UTIL_FindDataTable(table, 
rgr_02@3313
   101
				name,
rgr_02@3313
   102
				info,
rgr_02@3313
   103
				offset + prop->GetOffset())
rgr_02@3313
   104
				)
rgr_02@3313
   105
			{
rgr_02@3313
   106
				return true;
rgr_02@3313
   107
			}
rgr_02@3313
   108
		}
rgr_02@3313
   109
	}
rgr_02@3313
   110
rgr_02@3313
   111
	return false;
rgr_02@3313
   112
}
rgr_02@3313
   113
rgr_02@3313
   114
//Taken From Sourcemod Core
rgr_02@3313
   115
unsigned int strncopy(char *dest, const char *src, size_t count)
rgr_02@3313
   116
{
rgr_02@3313
   117
	if (!count)
rgr_02@3313
   118
	{
rgr_02@3313
   119
		return 0;
rgr_02@3313
   120
	}
rgr_02@3313
   121
rgr_02@3313
   122
	char *start = dest;
rgr_02@3313
   123
	while ((*src) && (--count))
rgr_02@3313
   124
	{
rgr_02@3313
   125
		*dest++ = *src++;
rgr_02@3313
   126
	}
rgr_02@3313
   127
	*dest = '\0';
rgr_02@3313
   128
rgr_02@3313
   129
	return (dest - start);
rgr_02@3313
   130
}
rgr_02@3313
   131
faluco@1441
   132
static cell_t CS_RespawnPlayer(IPluginContext *pContext, const cell_t *params)
faluco@1441
   133
{
faluco@1441
   134
	static ICallWrapper *pWrapper = NULL;
faluco@1441
   135
	if (!pWrapper)
faluco@1441
   136
	{
faluco@1441
   137
		REGISTER_NATIVE_ADDR("RoundRespawn", 
faluco@1441
   138
			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, NULL, 0));
faluco@1441
   139
	}
faluco@1441
   140
faluco@1441
   141
	CBaseEntity *pEntity;
faluco@1441
   142
	if (!(pEntity=GetCBaseEntity(params[1], true)))
faluco@1441
   143
	{
faluco@1441
   144
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
faluco@1441
   145
	}
faluco@1441
   146
	pWrapper->Execute(&pEntity, NULL);
faluco@1441
   147
faluco@1441
   148
	return 1;
faluco@1441
   149
}
faluco@1441
   150
faluco@1441
   151
static cell_t CS_SwitchTeam(IPluginContext *pContext, const cell_t *params)
faluco@1441
   152
{
faluco@1441
   153
	static ICallWrapper *pWrapper = NULL;
faluco@1441
   154
	if (!pWrapper)
faluco@1441
   155
	{
faluco@1441
   156
		REGISTER_NATIVE_ADDR("SwitchTeam", 
faluco@1441
   157
			PassInfo pass[1]; \
faluco@1441
   158
			pass[0].flags = PASSFLAG_BYVAL; \
faluco@1441
   159
			pass[0].size = sizeof(int); \
faluco@1441
   160
			pass[0].type = PassType_Basic; \
faluco@1441
   161
			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1))
faluco@1441
   162
	}
faluco@1441
   163
faluco@1441
   164
	CBaseEntity *pEntity;
faluco@1441
   165
	if (!(pEntity=GetCBaseEntity(params[1], true)))
faluco@1441
   166
	{
faluco@1441
   167
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
faluco@1441
   168
	}
faluco@1441
   169
faluco@1441
   170
	unsigned char vstk[sizeof(CBaseEntity *) + sizeof(int)];
faluco@1441
   171
	unsigned char *vptr = vstk;
faluco@1441
   172
faluco@1441
   173
	*(CBaseEntity **)vptr = pEntity;
faluco@1441
   174
	vptr += sizeof(CBaseEntity *);
faluco@1441
   175
	*(int *)vptr = params[2];
faluco@1441
   176
	pWrapper->Execute(vstk, NULL);
faluco@1441
   177
faluco@1441
   178
	return 1;
faluco@1441
   179
}
faluco@1441
   180
rgr_02@3313
   181
static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
rgr_02@3313
   182
{
rgr_02@3313
   183
	static ICallWrapper *pWrapper = NULL;
rgr_02@3313
   184
	if (!pWrapper)
rgr_02@3313
   185
	{
rgr_02@3313
   186
		REGISTER_NATIVE_ADDR("CSWeaponDrop",
rgr_02@3313
   187
			PassInfo pass[3]; \
rgr_02@3313
   188
			pass[0].flags = PASSFLAG_BYVAL; \
rgr_02@3313
   189
			pass[0].type  = PassType_Basic; \
rgr_02@3313
   190
			pass[0].size  = sizeof(CBaseEntity *); \
rgr_02@3313
   191
			pass[1].flags = PASSFLAG_BYVAL; \
rgr_02@3313
   192
			pass[1].type  = PassType_Basic; \
rgr_02@3313
   193
			pass[1].size  = sizeof(bool); \
rgr_02@3313
   194
			pass[2].flags = PASSFLAG_BYVAL; \
rgr_02@3313
   195
			pass[2].type  = PassType_Basic; \
rgr_02@3313
   196
			pass[2].size  = sizeof(bool); \
rgr_02@3313
   197
			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 3))
rgr_02@3313
   198
	}
rgr_02@3313
   199
rgr_02@3313
   200
	CBaseEntity *pEntity;
rgr_02@3313
   201
	if (!(pEntity = GetCBaseEntity(params[1], true)))
rgr_02@3313
   202
	{
rgr_02@3313
   203
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
rgr_02@3313
   204
	}
rgr_02@3313
   205
rgr_02@3313
   206
	CBaseEntity *pWeapon;
rgr_02@3313
   207
	if (!(pWeapon = GetCBaseEntity(params[2], false)))
rgr_02@3313
   208
	{
rgr_02@3313
   209
		return pContext->ThrowNativeError("Weapon index %d is not valid", params[2]);
rgr_02@3313
   210
	}
rgr_02@3313
   211
rgr_02@3313
   212
	//Psychonic is awesome for this
kyle@3634
   213
	sm_sendprop_info_t spi;
rgr_02@3313
   214
	IServerUnknown *pUnk = (IServerUnknown *)pWeapon;
rgr_02@3313
   215
	IServerNetworkable *pNet = pUnk->GetNetworkable();
rgr_02@3313
   216
kyle@3634
   217
	if (!UTIL_FindDataTable(pNet->GetServerClass()->m_pTable, "DT_WeaponCSBase", &spi, 0))
rgr_02@3313
   218
		return pContext->ThrowNativeError("Entity index %d is not a weapon", params[2]);
rgr_02@3313
   219
kyle@3634
   220
	if (!gamehelpers->FindSendPropInfo("CBaseCombatWeapon", "m_hOwnerEntity", &spi))
rgr_02@3313
   221
		return pContext->ThrowNativeError("Invalid entity index %d for weapon", params[2]);
rgr_02@3313
   222
kyle@3634
   223
	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pWeapon + spi.actual_offset);
skamonkey@3710
   224
	if (params[1] != hndl.GetEntryIndex() || hndl != ((IServerEntity *)pEntity)->GetRefEHandle())
rgr_02@3313
   225
		return pContext->ThrowNativeError("Weapon %d is not owned by client %d", params[2], params[1]);
rgr_02@3313
   226
rgr_02@3313
   227
	if (params[4] == 1 && g_pCSWeaponDropDetoured)
rgr_02@3313
   228
		g_pIgnoreCSWeaponDropDetour = true;
rgr_02@3313
   229
rgr_02@3313
   230
	unsigned char vstk[sizeof(CBaseEntity *) * 2 + sizeof(bool) * 2];
rgr_02@3313
   231
	unsigned char *vptr = vstk;
rgr_02@3313
   232
rgr_02@3313
   233
	// <psychonic> first one is always false. second is true to toss, false to just drop
rgr_02@3313
   234
	*(CBaseEntity **)vptr = pEntity;
rgr_02@3313
   235
	vptr += sizeof(CBaseEntity *);
rgr_02@3313
   236
	*(CBaseEntity **)vptr = pWeapon;
rgr_02@3313
   237
	vptr += sizeof(CBaseEntity *);
rgr_02@3313
   238
	*(bool *)vptr = false;
rgr_02@3313
   239
	vptr += sizeof(bool);
rgr_02@3313
   240
	*(bool *)vptr = (params[3]) ? true : false;
rgr_02@3313
   241
rgr_02@3313
   242
 	pWrapper->Execute(vstk, NULL);
rgr_02@3313
   243
rgr_02@3313
   244
	return 1;
rgr_02@3313
   245
}
rgr_02@3313
   246
rgr_02@3313
   247
static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params)
rgr_02@3313
   248
{
rgr_02@3313
   249
	if (g_pSDKTools == NULL)
rgr_02@3313
   250
	{
rgr_02@3313
   251
		smutils->LogError(myself, "SDKTools interface not found. TerminateRound native disabled.");
rgr_02@3313
   252
	}
rgr_02@3313
   253
	else if (g_pSDKTools->GetInterfaceVersion() < 2)
rgr_02@3313
   254
	{
rgr_02@3313
   255
		//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
rgr_02@3313
   256
		return pContext->ThrowNativeError("SDKTools interface is outdated. TerminateRound native disabled.");
rgr_02@3313
   257
	}
rgr_02@3313
   258
rgr_02@3313
   259
	static ICallWrapper *pWrapper = NULL;
rgr_02@3313
   260
rgr_02@3313
   261
	if (!pWrapper)
rgr_02@3313
   262
	{
rgr_02@3313
   263
		REGISTER_NATIVE_ADDR("TerminateRound",
rgr_02@3313
   264
			PassInfo pass[2]; \
rgr_02@3313
   265
			pass[0].flags = PASSFLAG_BYVAL; \
rgr_02@3313
   266
			pass[0].type = PassType_Basic; \
rgr_02@3313
   267
			pass[0].size = sizeof(float); \
rgr_02@3313
   268
			pass[1].flags = PASSFLAG_BYVAL; \
rgr_02@3313
   269
			pass[1].type = PassType_Basic; \
rgr_02@3313
   270
			pass[1].size = sizeof(int); \
rgr_02@3313
   271
			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
rgr_02@3313
   272
	}
rgr_02@3313
   273
rgr_02@3313
   274
	void *gamerules = g_pSDKTools->GetGameRules();
rgr_02@3313
   275
	if (gamerules == NULL)
rgr_02@3313
   276
	{
rgr_02@3313
   277
		return pContext->ThrowNativeError("GameRules not available. TerminateRound native disabled.");
rgr_02@3313
   278
	}
rgr_02@3313
   279
rgr_02@3313
   280
	if (params[3] == 1 && g_pTerminateRoundDetoured)
rgr_02@3313
   281
		g_pIgnoreTerminateDetour = true;
rgr_02@3313
   282
rgr_02@3313
   283
	unsigned char vstk[sizeof(void *) + sizeof(float)+ sizeof(int)];
rgr_02@3313
   284
	unsigned char *vptr = vstk;
rgr_02@3313
   285
rgr_02@3313
   286
	*(void **)vptr = gamerules;
rgr_02@3313
   287
	vptr += sizeof(void *);
rgr_02@3313
   288
	*(float *)vptr = sp_ctof(params[1]);
rgr_02@3313
   289
	vptr += sizeof(float);
rgr_02@3313
   290
	*(int*)vptr = params[2];
rgr_02@3313
   291
rgr_02@3313
   292
	pWrapper->Execute(vstk, NULL);
rgr_02@3313
   293
rgr_02@3313
   294
	return 1;
rgr_02@3313
   295
}
rgr_02@3313
   296
kyle@3702
   297
static cell_t CS_WeaponIDToAlias(IPluginContext *pContext, const cell_t *params)
kyle@3702
   298
{
drifter01620@3965
   299
	if (!IsValidWeaponID(params[1]))
drifter01620@3965
   300
		return pContext->ThrowNativeError("Invalid WeaponID passed for this game");
drifter01620@3965
   301
kyle@3702
   302
	char *dest;
kyle@3702
   303
kyle@3702
   304
	pContext->LocalToString(params[2], &dest);
kyle@3702
   305
kyle@3702
   306
	const char *ret = WeaponIDToAlias(params[1]);
kyle@3702
   307
	
kyle@3702
   308
	if (ret == NULL)
kyle@3702
   309
	{
kyle@3702
   310
		return 0;
kyle@3702
   311
	}
kyle@3702
   312
	
kyle@3702
   313
	return strncopy(dest, ret, params[3]);
kyle@3702
   314
}
kyle@3702
   315
rgr_02@3313
   316
static cell_t CS_GetTranslatedWeaponAlias(IPluginContext *pContext, const cell_t *params)
rgr_02@3313
   317
{
rgr_02@3519
   318
	char *weapon;
rgr_02@3519
   319
	char *dest;
rgr_02@3313
   320
rgr_02@3313
   321
	pContext->LocalToString(params[2], &dest);
rgr_02@3313
   322
	pContext->LocalToString(params[1], &weapon);
rgr_02@3313
   323
rgr_02@3519
   324
	const char *ret = GetTranslatedWeaponAlias(weapon);
rgr_02@3313
   325
	strncopy(dest, ret, params[3]);
rgr_02@3313
   326
	
rgr_02@3313
   327
	return 1;
rgr_02@3313
   328
}
skamonkey@3405
   329
rgr_02@3324
   330
static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
rgr_02@3324
   331
{
drifter01620@3965
   332
	if (!IsValidWeaponID(params[2]))
drifter01620@3965
   333
		return pContext->ThrowNativeError("Invalid WeaponID passed for this game");
drifter01620@3964
   334
drifter01620@3964
   335
	int id = GetRealWeaponID(params[2]);
drifter01620@3964
   336
rgr_02@3359
   337
	//Hard code return values for weapons that dont call GetWeaponPrice and always use default value.
rgr_02@3519
   338
#if SOURCE_ENGINE == SE_CSGO
rgr_02@3519
   339
	if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_KNIFE_GG)
drifter01620@3964
   340
		return 0;
rgr_02@3519
   341
#else
rgr_02@3519
   342
 	if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_SHIELD)
rgr_02@3359
   343
		return 0;
drifter01620@3964
   344
	else if (id == WEAPON_KEVLAR)
rgr_02@3359
   345
		return 650;
drifter01620@3964
   346
	else if (id == WEAPON_ASSAULTSUIT)
rgr_02@3359
   347
		return 1000;
drifter01620@3964
   348
#endif
drifter01620@3964
   349
	else if (id == WEAPON_NIGHTVISION)
rgr_02@3359
   350
		return 1250;
rgr_02@3519
   351
#if SOURCE_ENGINE == SE_CSGO
drifter01620@3964
   352
	else if (id == WEAPON_DEFUSER)
drifter01620@3964
   353
		return 400;
rgr_02@3519
   354
#endif
rgr_02@3359
   355
rgr_02@3519
   356
	if (g_iPriceOffset == -1)
rgr_02@3324
   357
	{
rgr_02@3519
   358
		if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset))
rgr_02@3324
   359
		{
rgr_02@3519
   360
			g_iPriceOffset = -1;
rgr_02@3324
   361
			return pContext->ThrowNativeError("Failed to get WeaponPrice offset");
rgr_02@3324
   362
		}
rgr_02@3324
   363
	}
rgr_02@3324
   364
rgr_02@3324
   365
	CBaseEntity *pEntity;
rgr_02@3324
   366
	if (!(pEntity = GetCBaseEntity(params[1], true)))
rgr_02@3324
   367
	{
rgr_02@3324
   368
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
rgr_02@3324
   369
	}
rgr_02@3324
   370
rgr_02@3560
   371
	void *info = GetWeaponInfo(id);
rgr_02@3324
   372
	if (!info)
rgr_02@3324
   373
		return pContext->ThrowNativeError("Failed to get weaponinfo");
rgr_02@3324
   374
rgr_02@3519
   375
	int price = *(int *)((intptr_t)info + g_iPriceOffset);
rgr_02@3324
   376
rgr_02@3324
   377
	if (params[3] || weaponNameOffset == -1)
rgr_02@3324
   378
		return price;
rgr_02@3324
   379
rgr_02@3324
   380
	const char *weapon_name = (const char *)((intptr_t)info + weaponNameOffset);
rgr_02@3324
   381
rgr_02@3519
   382
#if SOURCE_ENGINE == SE_CSGO
rgr_02@3519
   383
	return CallPriceForwardCSGO(params[1], weapon_name, price);
rgr_02@3519
   384
#else
rgr_02@3324
   385
	return CallPriceForward(params[1], weapon_name, price);
rgr_02@3519
   386
#endif
rgr_02@3324
   387
}
rgr_02@3313
   388
skamonkey@3405
   389
static cell_t CS_GetClientClanTag(IPluginContext *pContext, const cell_t *params)
skamonkey@3405
   390
{
skamonkey@3405
   391
	static void *addr;
skamonkey@3405
   392
	if (!addr)
skamonkey@3405
   393
	{
skamonkey@3405
   394
		if (!g_pGameConf->GetMemSig("SetClanTag", &addr) || !addr)
skamonkey@3405
   395
		{
skamonkey@3405
   396
			return pContext->ThrowNativeError("Failed to locate function");
skamonkey@3405
   397
		}
skamonkey@3405
   398
	}
skamonkey@3405
   399
skamonkey@3405
   400
	CBaseEntity *pEntity;
skamonkey@3405
   401
	if (!(pEntity = GetCBaseEntity(params[1], true)))
skamonkey@3405
   402
	{
skamonkey@3405
   403
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
skamonkey@3405
   404
	}
skamonkey@3405
   405
skamonkey@3405
   406
	static int tagOffsetOffset = -1;
skamonkey@3405
   407
	static int tagOffset;
skamonkey@3405
   408
skamonkey@3405
   409
	if (tagOffsetOffset == -1)
skamonkey@3405
   410
	{
skamonkey@3405
   411
		if (!g_pGameConf->GetOffset("ClanTagOffset", &tagOffsetOffset))
skamonkey@3405
   412
		{
skamonkey@3405
   413
			tagOffsetOffset = -1;
skamonkey@3405
   414
			return pContext->ThrowNativeError("Unable to find ClanTagOffset gamedata");
skamonkey@3405
   415
		}
skamonkey@3405
   416
skamonkey@3405
   417
		tagOffset = *(int *)((intptr_t)addr + tagOffsetOffset);
skamonkey@3405
   418
	}
skamonkey@3405
   419
skamonkey@3405
   420
	size_t len;
skamonkey@3405
   421
skamonkey@3405
   422
	const char *src = (char *)((intptr_t)pEntity + tagOffset);
skamonkey@3405
   423
	pContext->StringToLocalUTF8(params[2], params[3], src, &len);
skamonkey@3405
   424
skamonkey@3405
   425
	return len;
skamonkey@3405
   426
}
skamonkey@3405
   427
skamonkey@3405
   428
static cell_t CS_SetClientClanTag(IPluginContext *pContext, const cell_t *params)
skamonkey@3405
   429
{
skamonkey@3405
   430
	static ICallWrapper *pWrapper = NULL;
skamonkey@3405
   431
skamonkey@3405
   432
	if (!pWrapper)
skamonkey@3405
   433
	{
skamonkey@3405
   434
		REGISTER_NATIVE_ADDR("SetClanTag",
skamonkey@3405
   435
			PassInfo pass[2]; \
skamonkey@3405
   436
			pass[0].flags = PASSFLAG_BYVAL; \
skamonkey@3405
   437
			pass[0].type  = PassType_Basic; \
skamonkey@3405
   438
			pass[0].size  = sizeof(CBaseEntity *); \
skamonkey@3405
   439
			pass[1].flags = PASSFLAG_BYVAL; \
skamonkey@3405
   440
			pass[1].type  = PassType_Basic; \
skamonkey@3405
   441
			pass[1].size  = sizeof(char *); \
skamonkey@3405
   442
			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
skamonkey@3405
   443
	}
skamonkey@3405
   444
skamonkey@3405
   445
	CBaseEntity *pEntity;
skamonkey@3405
   446
	if (!(pEntity = GetCBaseEntity(params[1], true)))
skamonkey@3405
   447
	{
skamonkey@3405
   448
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
skamonkey@3405
   449
	}
skamonkey@3405
   450
skamonkey@3405
   451
	char *szNewTag;
skamonkey@3405
   452
	pContext->LocalToString(params[2], &szNewTag);
skamonkey@3405
   453
skamonkey@3405
   454
	unsigned char vstk[sizeof(CBaseEntity *) + sizeof(char *)];
skamonkey@3405
   455
	unsigned char *vptr = vstk;
skamonkey@3405
   456
skamonkey@3405
   457
	*(CBaseEntity **)vptr = pEntity;
skamonkey@3405
   458
	vptr += sizeof(CBaseEntity *);
skamonkey@3405
   459
	*(char **)vptr = szNewTag;
skamonkey@3405
   460
skamonkey@3405
   461
	pWrapper->Execute(vstk, NULL);
skamonkey@3405
   462
skamonkey@3405
   463
	return 1;
rgr_02@3519
   464
}
rgr_02@3519
   465
rgr_02@3519
   466
static cell_t CS_AliasToWeaponID(IPluginContext *pContext, const cell_t *params)
rgr_02@3519
   467
{
rgr_02@3519
   468
	char *weapon;
rgr_02@3519
   469
rgr_02@3519
   470
	pContext->LocalToString(params[1], &weapon);
rgr_02@3519
   471
drifter01620@3965
   472
	int id = GetFakeWeaponID(AliasToWeaponID(weapon));
drifter01620@3965
   473
drifter01620@3965
   474
	if (!IsValidWeaponID(id))
drifter01620@3965
   475
		return SMCSWeapon_NONE;
drifter01620@3965
   476
drifter01620@3965
   477
	return id;
skamonkey@3405
   478
}
skamonkey@3405
   479
rgr_02@3530
   480
static cell_t CS_SetTeamScore(IPluginContext *pContext, const cell_t *params)
rgr_02@3530
   481
{
rgr_02@3530
   482
	if (g_pSDKTools == NULL)
rgr_02@3530
   483
	{
rgr_02@3530
   484
		smutils->LogError(myself, "SDKTools interface not found. CS_SetTeamScore native disabled.");
rgr_02@3530
   485
	}
rgr_02@3530
   486
	else if (g_pSDKTools->GetInterfaceVersion() < 2)
rgr_02@3530
   487
	{
rgr_02@3530
   488
		//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
rgr_02@3530
   489
		return pContext->ThrowNativeError("SDKTools interface is outdated. CS_SetTeamScore native disabled.");
rgr_02@3530
   490
	}
rgr_02@3530
   491
rgr_02@3530
   492
	static void *addr;
rgr_02@3530
   493
	if (!addr)
rgr_02@3530
   494
	{
rgr_02@3530
   495
		if (!g_pGameConf->GetMemSig("CheckWinLimit", &addr) || !addr)
rgr_02@3530
   496
		{
rgr_02@3530
   497
			return pContext->ThrowNativeError("Failed to locate CheckWinLimit function");
rgr_02@3530
   498
		}
rgr_02@3530
   499
	}
rgr_02@3530
   500
rgr_02@3530
   501
	static int ctTeamOffsetOffset = -1;
rgr_02@3530
   502
	static int ctTeamOffset;
rgr_02@3530
   503
rgr_02@3530
   504
	static int tTeamOffsetOffset = -1;
rgr_02@3530
   505
	static int tTeamOffset;
rgr_02@3530
   506
rgr_02@3530
   507
	if (ctTeamOffsetOffset == -1)
rgr_02@3530
   508
	{
rgr_02@3530
   509
		if (!g_pGameConf->GetOffset("CTTeamScoreOffset", &ctTeamOffsetOffset))
rgr_02@3530
   510
		{
rgr_02@3530
   511
			ctTeamOffsetOffset = -1;
rgr_02@3530
   512
			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
rgr_02@3530
   513
		}
rgr_02@3530
   514
rgr_02@3530
   515
		ctTeamOffset = *(int *)((intptr_t)addr + ctTeamOffsetOffset);
rgr_02@3530
   516
	}
rgr_02@3530
   517
rgr_02@3530
   518
	if (tTeamOffsetOffset == -1)
rgr_02@3530
   519
	{
rgr_02@3530
   520
		if (!g_pGameConf->GetOffset("TTeamScoreOffset", &tTeamOffsetOffset))
rgr_02@3530
   521
		{
rgr_02@3530
   522
			tTeamOffsetOffset = -1;
rgr_02@3530
   523
			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
rgr_02@3530
   524
		}
rgr_02@3530
   525
rgr_02@3530
   526
		tTeamOffset = *(int *)((intptr_t)addr + tTeamOffsetOffset);
rgr_02@3530
   527
	}
rgr_02@3530
   528
rgr_02@3530
   529
	void *gamerules = g_pSDKTools->GetGameRules();
rgr_02@3530
   530
	if (gamerules == NULL)
rgr_02@3530
   531
	{
rgr_02@3530
   532
		return pContext->ThrowNativeError("GameRules not available. CS_SetTeamScore native disabled.");
rgr_02@3530
   533
	}
rgr_02@3530
   534
rgr_02@3530
   535
	if (params[1] == 3)
rgr_02@3530
   536
	{
rgr_02@3530
   537
		*(int16_t *)((intptr_t)gamerules+ctTeamOffset) = params[2];
rgr_02@3530
   538
		return 1;
rgr_02@3530
   539
	}
rgr_02@3530
   540
	else if (params[1] == 2)
rgr_02@3530
   541
	{
rgr_02@3530
   542
		*(int16_t *)((intptr_t)gamerules+tTeamOffset) = params[2];
rgr_02@3530
   543
		return 1;
rgr_02@3530
   544
	}
rgr_02@3530
   545
	return pContext->ThrowNativeError("Invalid team index passed (%i).", params[1]);
rgr_02@3530
   546
}
rgr_02@3530
   547
rgr_02@3530
   548
static cell_t CS_GetTeamScore(IPluginContext *pContext, const cell_t *params)
rgr_02@3530
   549
{
rgr_02@3530
   550
	if (g_pSDKTools == NULL)
rgr_02@3530
   551
	{
rgr_02@3530
   552
		smutils->LogError(myself, "SDKTools interface not found. CS_GetTeamScore native disabled.");
rgr_02@3530
   553
	}
rgr_02@3530
   554
	else if (g_pSDKTools->GetInterfaceVersion() < 2)
rgr_02@3530
   555
	{
rgr_02@3530
   556
		//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
rgr_02@3530
   557
		return pContext->ThrowNativeError("SDKTools interface is outdated. CS_GetTeamScore native disabled.");
rgr_02@3530
   558
	}
rgr_02@3530
   559
rgr_02@3530
   560
	static void *addr;
rgr_02@3530
   561
	if (!addr)
rgr_02@3530
   562
	{
rgr_02@3530
   563
		if (!g_pGameConf->GetMemSig("CheckWinLimit", &addr) || !addr)
rgr_02@3530
   564
		{
rgr_02@3530
   565
			return pContext->ThrowNativeError("Failed to locate CheckWinLimit function");
rgr_02@3530
   566
		}
rgr_02@3530
   567
	}
rgr_02@3530
   568
rgr_02@3530
   569
	static int ctTeamOffsetOffset = -1;
rgr_02@3530
   570
	static int ctTeamOffset;
rgr_02@3530
   571
rgr_02@3530
   572
	static int tTeamOffsetOffset = -1;
rgr_02@3530
   573
	static int tTeamOffset;
rgr_02@3530
   574
rgr_02@3530
   575
	if (ctTeamOffsetOffset == -1)
rgr_02@3530
   576
	{
rgr_02@3530
   577
		if (!g_pGameConf->GetOffset("CTTeamScoreOffset", &ctTeamOffsetOffset))
rgr_02@3530
   578
		{
rgr_02@3530
   579
			ctTeamOffsetOffset = -1;
rgr_02@3530
   580
			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
rgr_02@3530
   581
		}
rgr_02@3530
   582
rgr_02@3530
   583
		ctTeamOffset = *(int *)((intptr_t)addr + ctTeamOffsetOffset);
rgr_02@3530
   584
	}
rgr_02@3530
   585
rgr_02@3530
   586
	if (tTeamOffsetOffset == -1)
rgr_02@3530
   587
	{
rgr_02@3530
   588
		if (!g_pGameConf->GetOffset("TTeamScoreOffset", &tTeamOffsetOffset))
rgr_02@3530
   589
		{
rgr_02@3530
   590
			tTeamOffsetOffset = -1;
rgr_02@3530
   591
			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
rgr_02@3530
   592
		}
rgr_02@3530
   593
rgr_02@3530
   594
		tTeamOffset = *(int *)((intptr_t)addr + tTeamOffsetOffset);
rgr_02@3530
   595
	}
rgr_02@3530
   596
rgr_02@3530
   597
	void *gamerules = g_pSDKTools->GetGameRules();
rgr_02@3530
   598
	if (gamerules == NULL)
rgr_02@3530
   599
	{
rgr_02@3530
   600
		return pContext->ThrowNativeError("GameRules not available. CS_GetTeamScore native disabled.");
rgr_02@3530
   601
	}
rgr_02@3530
   602
rgr_02@3530
   603
	if (params[1] == 3)
rgr_02@3530
   604
	{
rgr_02@3530
   605
		return *(int16_t *)((intptr_t)gamerules+ctTeamOffset);
rgr_02@3530
   606
	}
rgr_02@3530
   607
	else if (params[1] == 2)
rgr_02@3530
   608
	{
rgr_02@3530
   609
		return *(int16_t *)((intptr_t)gamerules+tTeamOffset);
rgr_02@3530
   610
	}
rgr_02@3530
   611
	return pContext->ThrowNativeError("Invalid team index passed (%i).", params[1]);
rgr_02@3530
   612
}
rgr_02@3530
   613
drifter01620@3965
   614
static cell_t CS_IsValidWeaponID(IPluginContext *pContext, const cell_t *params)
drifter01620@3965
   615
{
drifter01620@3965
   616
	return IsValidWeaponID(params[1]) ? 1 : 0;
drifter01620@3965
   617
}
drifter01620@3965
   618
skamonkey@3706
   619
template <typename T>
skamonkey@3706
   620
static inline T *GetPlayerVarAddressOrError(const char *pszGamedataName, IPluginContext *pContext, CBaseEntity *pPlayerEntity)
rgr_02@3530
   621
{
skamonkey@3706
   622
	char szBaseName[128];
skamonkey@3706
   623
	g_pSM->Format(szBaseName, sizeof(szBaseName), "%sBase", pszGamedataName);
skamonkey@3706
   624
skamonkey@3706
   625
	const char *pszBaseVar = g_pGameConf->GetKeyValue(szBaseName);
skamonkey@3706
   626
	if (pszBaseVar == NULL)
skamonkey@3706
   627
	{
skamonkey@3706
   628
		pContext->ThrowNativeError("Failed to locate %s key in gamedata", szBaseName);
skamonkey@3706
   629
		return NULL;
skamonkey@3706
   630
	}
skamonkey@3706
   631
skamonkey@3706
   632
	int interimOffset = 0;
skamonkey@3706
   633
	sm_sendprop_info_t info;
skamonkey@3706
   634
	if (gamehelpers->FindSendPropInfo("CCSPlayer", pszBaseVar, &info))
rgr_02@3530
   635
	{
skamonkey@3706
   636
		interimOffset = info.actual_offset;
skamonkey@3706
   637
	}
skamonkey@3706
   638
	else
skamonkey@3706
   639
	{
skamonkey@3706
   640
		datamap_t *pMap = gamehelpers->GetDataMap(pPlayerEntity);
skamonkey@3706
   641
		typedescription_t *td = gamehelpers->FindInDataMap(pMap, pszBaseVar);
skamonkey@3706
   642
		if (td)
rgr_02@3530
   643
		{
skamonkey@3706
   644
#if SOURCE_ENGINE >= SE_LEFT4DEAD
skamonkey@3706
   645
			interimOffset = td->fieldOffset;
skamonkey@3706
   646
#else
skamonkey@3706
   647
			interimOffset = td->fieldOffset[TD_OFFSET_NORMAL];
skamonkey@3706
   648
#endif
rgr_02@3530
   649
		}
rgr_02@3530
   650
	}
rgr_02@3530
   651
skamonkey@3706
   652
	if (interimOffset == 0)
skamonkey@3706
   653
	{
skamonkey@3706
   654
		pContext->ThrowNativeError("Failed to find property \"%s\" on player.", pszBaseVar);
skamonkey@3706
   655
		return NULL;
skamonkey@3706
   656
	}
skamonkey@3706
   657
		
skamonkey@3706
   658
skamonkey@3706
   659
	int tempOffset;
skamonkey@3706
   660
	if (!g_pGameConf->GetOffset(pszGamedataName, &tempOffset))
skamonkey@3706
   661
	{
skamonkey@3706
   662
		pContext->ThrowNativeError("Failed to locate %s offset in gamedata", pszGamedataName);
skamonkey@3706
   663
		return NULL;
skamonkey@3706
   664
	}
skamonkey@3706
   665
skamonkey@3706
   666
	return (T *)((intptr_t)pPlayerEntity + interimOffset + tempOffset);
skamonkey@3706
   667
}
skamonkey@3706
   668
skamonkey@3706
   669
template <typename T>
skamonkey@3706
   670
static inline cell_t GetPlayerVar(IPluginContext *pContext, const cell_t *params, const char *varName)
skamonkey@3706
   671
{
skamonkey@3706
   672
	CBaseEntity *pPlayer = GetCBaseEntity(params[1], true);
skamonkey@3706
   673
	if (!pPlayer)
rgr_02@3530
   674
	{
rgr_02@3530
   675
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
rgr_02@3530
   676
	}
rgr_02@3530
   677
skamonkey@3706
   678
	T *pVar = GetPlayerVarAddressOrError<T>(varName, pContext, pPlayer);
skamonkey@3706
   679
	if (pVar)
skamonkey@3706
   680
	{	
skamonkey@3706
   681
		return (cell_t)*pVar;
rgr_02@3530
   682
	}
rgr_02@3530
   683
skamonkey@3706
   684
	return 0;
skamonkey@3706
   685
}
skamonkey@3706
   686
skamonkey@3706
   687
template <typename T>
skamonkey@3706
   688
static inline cell_t SetPlayerVar(IPluginContext *pContext, const cell_t *params, const char *varName)
skamonkey@3706
   689
{
skamonkey@3706
   690
	CBaseEntity *pPlayer = GetCBaseEntity(params[1], true);
skamonkey@3706
   691
	if (!pPlayer)
skamonkey@3706
   692
	{
skamonkey@3706
   693
		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
skamonkey@3706
   694
	}
rgr_02@3530
   695
skamonkey@3706
   696
	T *pVar = GetPlayerVarAddressOrError<T>(varName, pContext, pPlayer);
skamonkey@3706
   697
	if (pVar)
skamonkey@3706
   698
	{
skamonkey@3706
   699
		*pVar = (T)params[2];
skamonkey@3706
   700
	}
skamonkey@3706
   701
skamonkey@3706
   702
	return 0;
skamonkey@3706
   703
}
skamonkey@3706
   704
skamonkey@3706
   705
static cell_t CS_SetMVPCount(IPluginContext *pContext, const cell_t *params)
skamonkey@3706
   706
{
skamonkey@3706
   707
	return SetPlayerVar<int>(pContext, params, "MVPs");
rgr_02@3530
   708
}
rgr_02@3530
   709
rgr_02@3530
   710
static cell_t CS_GetMVPCount(IPluginContext *pContext, const cell_t *params)
rgr_02@3530
   711
{
skamonkey@3706
   712
	return GetPlayerVar<int>(pContext, params, "MVPs");
skamonkey@3706
   713
}
rgr_02@3530
   714
skamonkey@3706
   715
static cell_t CS_SetClientContributionScore(IPluginContext *pContext, const cell_t *params)
skamonkey@3706
   716
{
skamonkey@3706
   717
#if SOURCE_ENGINE == SE_CSGO
skamonkey@3706
   718
	return SetPlayerVar<int>(pContext, params, "CScore");
skamonkey@3706
   719
#else
skamonkey@3706
   720
	return pContext->ThrowNativeError("SetClientContributionScore is not supported on this game");
skamonkey@3706
   721
#endif
skamonkey@3706
   722
}
rgr_02@3530
   723
skamonkey@3706
   724
static cell_t CS_GetClientContributionScore(IPluginContext *pContext, const cell_t *params)
skamonkey@3706
   725
{
skamonkey@3706
   726
#if SOURCE_ENGINE == SE_CSGO
skamonkey@3706
   727
	return GetPlayerVar<int>(pContext, params, "CScore");
skamonkey@3706
   728
#else
skamonkey@3706
   729
	return pContext->ThrowNativeError("GetClientContributionScore is not supported on this game");
skamonkey@3706
   730
#endif
skamonkey@3706
   731
}
rgr_02@3530
   732
skamonkey@3706
   733
static cell_t CS_SetClientAssists(IPluginContext *pContext, const cell_t *params)
skamonkey@3706
   734
{
skamonkey@3706
   735
#if SOURCE_ENGINE == SE_CSGO
skamonkey@3706
   736
	return SetPlayerVar<int>(pContext, params, "Assists");
skamonkey@3706
   737
#else
skamonkey@3706
   738
	return pContext->ThrowNativeError("SetClientAssists is not supported on this game");
skamonkey@3706
   739
#endif
skamonkey@3706
   740
}
rgr_02@3530
   741
skamonkey@3706
   742
static cell_t CS_GetClientAssists(IPluginContext *pContext, const cell_t *params)
skamonkey@3706
   743
{
skamonkey@3706
   744
#if SOURCE_ENGINE == SE_CSGO
skamonkey@3706
   745
	return GetPlayerVar<int>(pContext, params, "Assists");
skamonkey@3706
   746
#else
skamonkey@3706
   747
	return pContext->ThrowNativeError("GetClientAssists is not supported on this game");
skamonkey@3706
   748
#endif
rgr_02@3530
   749
}
rgr_02@3530
   750
faluco@1441
   751
sp_nativeinfo_t g_CSNatives[] = 
faluco@1441
   752
{
faluco@1441
   753
	{"CS_RespawnPlayer",			CS_RespawnPlayer}, 
rgr_02@3313
   754
	{"CS_SwitchTeam",				CS_SwitchTeam},
rgr_02@3313
   755
	{"CS_DropWeapon",				CS_DropWeapon},
rgr_02@3313
   756
	{"CS_TerminateRound",			CS_TerminateRound},
rgr_02@3313
   757
	{"CS_GetTranslatedWeaponAlias",	CS_GetTranslatedWeaponAlias},
rgr_02@3324
   758
	{"CS_GetWeaponPrice",			CS_GetWeaponPrice},
skamonkey@3405
   759
	{"CS_GetClientClanTag",			CS_GetClientClanTag},
skamonkey@3405
   760
	{"CS_SetClientClanTag",			CS_SetClientClanTag},
rgr_02@3519
   761
	{"CS_AliasToWeaponID",			CS_AliasToWeaponID},
rgr_02@3530
   762
	{"CS_GetTeamScore",				CS_GetTeamScore},
rgr_02@3530
   763
	{"CS_SetTeamScore",				CS_SetTeamScore},
rgr_02@3530
   764
	{"CS_GetMVPCount",				CS_GetMVPCount},
rgr_02@3530
   765
	{"CS_SetMVPCount",				CS_SetMVPCount},
kyle@3702
   766
	{"CS_WeaponIDToAlias",			CS_WeaponIDToAlias},
skamonkey@3706
   767
	{"CS_GetClientContributionScore",	CS_GetClientContributionScore},
skamonkey@3706
   768
	{"CS_SetClientContributionScore",	CS_SetClientContributionScore},
skamonkey@3706
   769
	{"CS_GetClientAssists",			CS_GetClientAssists},
skamonkey@3706
   770
	{"CS_SetClientAssists",			CS_SetClientAssists},
drifter01620@3965
   771
	{"CS_IsValidWeaponID",			CS_IsValidWeaponID},
faluco@1441
   772
	{NULL,							NULL}
faluco@1441
   773
};
dvander@1692
   774