3 * =============================================================================
4 * SourceMod Counter-Strike:Source Extension
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
32 #include "extension.h"
33 #include "RegNatives.h"
35 #include "util_cstrike.h"
36 #include <server_class.h>
38 int g_iPriceOffset = -1;
40 #define REGISTER_NATIVE_ADDR(name, code) \
42 if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \
44 return pContext->ThrowNativeError("Failed to locate function"); \
47 g_RegNatives.Register(pWrapper);
49 inline CBaseEntity *GetCBaseEntity(int num, bool isplayer)
51 edict_t *pEdict = gamehelpers->EdictOfIndex(num);
52 if (!pEdict || pEdict->IsFree())
57 if (num > 0 && num <= playerhelpers->GetMaxClients())
59 IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(pEdict);
60 if (!pPlayer || !pPlayer->IsConnected())
71 if ((pUnk=pEdict->GetUnknown()) == NULL)
76 return pUnk->GetBaseEntity();
79 bool UTIL_FindDataTable(SendTable *pTable, const char *name, sm_sendprop_info_t *info, unsigned int offset)
82 int props = pTable->GetNumProps();
86 for (int i=0; i<props; i++)
88 prop = pTable->GetProp(i);
90 if ((table = prop->GetDataTable()) != NULL)
92 pname = table->GetName();
93 if (pname && strcmp(name, pname) == 0)
96 info->actual_offset = offset + info->prop->GetOffset();
100 if (UTIL_FindDataTable(table,
103 offset + prop->GetOffset())
114 //Taken From Sourcemod Core
115 unsigned int strncopy(char *dest, const char *src, size_t count)
123 while ((*src) && (--count))
129 return (dest - start);
132 static cell_t CS_RespawnPlayer(IPluginContext *pContext, const cell_t *params)
134 static ICallWrapper *pWrapper = NULL;
137 REGISTER_NATIVE_ADDR("RoundRespawn",
138 pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, NULL, 0));
141 CBaseEntity *pEntity;
142 if (!(pEntity=GetCBaseEntity(params[1], true)))
144 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
146 pWrapper->Execute(&pEntity, NULL);
151 static cell_t CS_SwitchTeam(IPluginContext *pContext, const cell_t *params)
153 static ICallWrapper *pWrapper = NULL;
156 REGISTER_NATIVE_ADDR("SwitchTeam",
158 pass[0].flags = PASSFLAG_BYVAL; \
159 pass[0].size = sizeof(int); \
160 pass[0].type = PassType_Basic; \
161 pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1))
164 CBaseEntity *pEntity;
165 if (!(pEntity=GetCBaseEntity(params[1], true)))
167 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
170 unsigned char vstk[sizeof(CBaseEntity *) + sizeof(int)];
171 unsigned char *vptr = vstk;
173 *(CBaseEntity **)vptr = pEntity;
174 vptr += sizeof(CBaseEntity *);
175 *(int *)vptr = params[2];
176 pWrapper->Execute(vstk, NULL);
181 static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
183 static ICallWrapper *pWrapper = NULL;
186 REGISTER_NATIVE_ADDR("CSWeaponDrop",
188 pass[0].flags = PASSFLAG_BYVAL; \
189 pass[0].type = PassType_Basic; \
190 pass[0].size = sizeof(CBaseEntity *); \
191 pass[1].flags = PASSFLAG_BYVAL; \
192 pass[1].type = PassType_Basic; \
193 pass[1].size = sizeof(bool); \
194 pass[2].flags = PASSFLAG_BYVAL; \
195 pass[2].type = PassType_Basic; \
196 pass[2].size = sizeof(bool); \
197 pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 3))
200 CBaseEntity *pEntity;
201 if (!(pEntity = GetCBaseEntity(params[1], true)))
203 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
206 CBaseEntity *pWeapon;
207 if (!(pWeapon = GetCBaseEntity(params[2], false)))
209 return pContext->ThrowNativeError("Weapon index %d is not valid", params[2]);
212 //Psychonic is awesome for this
213 sm_sendprop_info_t spi;
214 IServerUnknown *pUnk = (IServerUnknown *)pWeapon;
215 IServerNetworkable *pNet = pUnk->GetNetworkable();
217 if (!UTIL_FindDataTable(pNet->GetServerClass()->m_pTable, "DT_WeaponCSBase", &spi, 0))
218 return pContext->ThrowNativeError("Entity index %d is not a weapon", params[2]);
220 if (!gamehelpers->FindSendPropInfo("CBaseCombatWeapon", "m_hOwnerEntity", &spi))
221 return pContext->ThrowNativeError("Invalid entity index %d for weapon", params[2]);
223 CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pWeapon + spi.actual_offset);
224 if (params[1] != hndl.GetEntryIndex())
225 return pContext->ThrowNativeError("Weapon %d is not owned by client %d", params[2], params[1]);
227 if (params[4] == 1 && g_pCSWeaponDropDetoured)
228 g_pIgnoreCSWeaponDropDetour = true;
230 unsigned char vstk[sizeof(CBaseEntity *) * 2 + sizeof(bool) * 2];
231 unsigned char *vptr = vstk;
233 // <psychonic> first one is always false. second is true to toss, false to just drop
234 *(CBaseEntity **)vptr = pEntity;
235 vptr += sizeof(CBaseEntity *);
236 *(CBaseEntity **)vptr = pWeapon;
237 vptr += sizeof(CBaseEntity *);
238 *(bool *)vptr = false;
239 vptr += sizeof(bool);
240 *(bool *)vptr = (params[3]) ? true : false;
242 pWrapper->Execute(vstk, NULL);
247 static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params)
249 if (g_pSDKTools == NULL)
251 smutils->LogError(myself, "SDKTools interface not found. TerminateRound native disabled.");
253 else if (g_pSDKTools->GetInterfaceVersion() < 2)
255 //<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
256 return pContext->ThrowNativeError("SDKTools interface is outdated. TerminateRound native disabled.");
259 static ICallWrapper *pWrapper = NULL;
263 REGISTER_NATIVE_ADDR("TerminateRound",
265 pass[0].flags = PASSFLAG_BYVAL; \
266 pass[0].type = PassType_Basic; \
267 pass[0].size = sizeof(float); \
268 pass[1].flags = PASSFLAG_BYVAL; \
269 pass[1].type = PassType_Basic; \
270 pass[1].size = sizeof(int); \
271 pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
274 void *gamerules = g_pSDKTools->GetGameRules();
275 if (gamerules == NULL)
277 return pContext->ThrowNativeError("GameRules not available. TerminateRound native disabled.");
280 if (params[3] == 1 && g_pTerminateRoundDetoured)
281 g_pIgnoreTerminateDetour = true;
283 unsigned char vstk[sizeof(void *) + sizeof(float)+ sizeof(int)];
284 unsigned char *vptr = vstk;
286 *(void **)vptr = gamerules;
287 vptr += sizeof(void *);
288 *(float *)vptr = sp_ctof(params[1]);
289 vptr += sizeof(float);
290 *(int*)vptr = params[2];
292 pWrapper->Execute(vstk, NULL);
297 static cell_t CS_GetTranslatedWeaponAlias(IPluginContext *pContext, const cell_t *params)
302 pContext->LocalToString(params[2], &dest);
303 pContext->LocalToString(params[1], &weapon);
305 const char *ret = GetTranslatedWeaponAlias(weapon);
306 strncopy(dest, ret, params[3]);
311 static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
313 //Hard code return values for weapons that dont call GetWeaponPrice and always use default value.
314 int id = GetRealWeaponID(params[2]);
315 #if SOURCE_ENGINE == SE_CSGO
316 if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_KNIFE_GG)
318 if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_SHIELD)
321 if (id == WEAPON_KEVLAR)
323 if (id == WEAPON_ASSAULTSUIT)
325 if (id == WEAPON_NIGHTVISION)
327 #if SOURCE_ENGINE == SE_CSGO
328 if (id == WEAPON_DEFUSER)
334 if (g_iPriceOffset == -1)
336 if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset))
339 return pContext->ThrowNativeError("Failed to get WeaponPrice offset");
343 CBaseEntity *pEntity;
344 if (!(pEntity = GetCBaseEntity(params[1], true)))
346 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
349 void *info = GetWeaponInfo(id);
351 return pContext->ThrowNativeError("Failed to get weaponinfo");
353 int price = *(int *)((intptr_t)info + g_iPriceOffset);
355 if (params[3] || weaponNameOffset == -1)
358 const char *weapon_name = (const char *)((intptr_t)info + weaponNameOffset);
360 #if SOURCE_ENGINE == SE_CSGO
361 return CallPriceForwardCSGO(params[1], weapon_name, price);
363 return CallPriceForward(params[1], weapon_name, price);
367 static cell_t CS_GetClientClanTag(IPluginContext *pContext, const cell_t *params)
372 if (!g_pGameConf->GetMemSig("SetClanTag", &addr) || !addr)
374 return pContext->ThrowNativeError("Failed to locate function");
378 CBaseEntity *pEntity;
379 if (!(pEntity = GetCBaseEntity(params[1], true)))
381 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
384 static int tagOffsetOffset = -1;
385 static int tagOffset;
387 if (tagOffsetOffset == -1)
389 if (!g_pGameConf->GetOffset("ClanTagOffset", &tagOffsetOffset))
391 tagOffsetOffset = -1;
392 return pContext->ThrowNativeError("Unable to find ClanTagOffset gamedata");
395 tagOffset = *(int *)((intptr_t)addr + tagOffsetOffset);
400 const char *src = (char *)((intptr_t)pEntity + tagOffset);
401 pContext->StringToLocalUTF8(params[2], params[3], src, &len);
406 static cell_t CS_SetClientClanTag(IPluginContext *pContext, const cell_t *params)
408 static ICallWrapper *pWrapper = NULL;
412 REGISTER_NATIVE_ADDR("SetClanTag",
414 pass[0].flags = PASSFLAG_BYVAL; \
415 pass[0].type = PassType_Basic; \
416 pass[0].size = sizeof(CBaseEntity *); \
417 pass[1].flags = PASSFLAG_BYVAL; \
418 pass[1].type = PassType_Basic; \
419 pass[1].size = sizeof(char *); \
420 pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
423 CBaseEntity *pEntity;
424 if (!(pEntity = GetCBaseEntity(params[1], true)))
426 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
430 pContext->LocalToString(params[2], &szNewTag);
432 unsigned char vstk[sizeof(CBaseEntity *) + sizeof(char *)];
433 unsigned char *vptr = vstk;
435 *(CBaseEntity **)vptr = pEntity;
436 vptr += sizeof(CBaseEntity *);
437 *(char **)vptr = szNewTag;
439 pWrapper->Execute(vstk, NULL);
444 static cell_t CS_AliasToWeaponID(IPluginContext *pContext, const cell_t *params)
448 pContext->LocalToString(params[1], &weapon);
450 return GetFakeWeaponID(AliasToWeaponID(weapon));
453 static cell_t CS_SetTeamScore(IPluginContext *pContext, const cell_t *params)
455 if (g_pSDKTools == NULL)
457 smutils->LogError(myself, "SDKTools interface not found. CS_SetTeamScore native disabled.");
459 else if (g_pSDKTools->GetInterfaceVersion() < 2)
461 //<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
462 return pContext->ThrowNativeError("SDKTools interface is outdated. CS_SetTeamScore native disabled.");
468 if (!g_pGameConf->GetMemSig("CheckWinLimit", &addr) || !addr)
470 return pContext->ThrowNativeError("Failed to locate CheckWinLimit function");
474 static int ctTeamOffsetOffset = -1;
475 static int ctTeamOffset;
477 static int tTeamOffsetOffset = -1;
478 static int tTeamOffset;
480 if (ctTeamOffsetOffset == -1)
482 if (!g_pGameConf->GetOffset("CTTeamScoreOffset", &ctTeamOffsetOffset))
484 ctTeamOffsetOffset = -1;
485 return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
488 ctTeamOffset = *(int *)((intptr_t)addr + ctTeamOffsetOffset);
491 if (tTeamOffsetOffset == -1)
493 if (!g_pGameConf->GetOffset("TTeamScoreOffset", &tTeamOffsetOffset))
495 tTeamOffsetOffset = -1;
496 return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
499 tTeamOffset = *(int *)((intptr_t)addr + tTeamOffsetOffset);
502 void *gamerules = g_pSDKTools->GetGameRules();
503 if (gamerules == NULL)
505 return pContext->ThrowNativeError("GameRules not available. CS_SetTeamScore native disabled.");
510 *(int16_t *)((intptr_t)gamerules+ctTeamOffset) = params[2];
513 else if (params[1] == 2)
515 *(int16_t *)((intptr_t)gamerules+tTeamOffset) = params[2];
518 return pContext->ThrowNativeError("Invalid team index passed (%i).", params[1]);
521 static cell_t CS_GetTeamScore(IPluginContext *pContext, const cell_t *params)
523 if (g_pSDKTools == NULL)
525 smutils->LogError(myself, "SDKTools interface not found. CS_GetTeamScore native disabled.");
527 else if (g_pSDKTools->GetInterfaceVersion() < 2)
529 //<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
530 return pContext->ThrowNativeError("SDKTools interface is outdated. CS_GetTeamScore native disabled.");
536 if (!g_pGameConf->GetMemSig("CheckWinLimit", &addr) || !addr)
538 return pContext->ThrowNativeError("Failed to locate CheckWinLimit function");
542 static int ctTeamOffsetOffset = -1;
543 static int ctTeamOffset;
545 static int tTeamOffsetOffset = -1;
546 static int tTeamOffset;
548 if (ctTeamOffsetOffset == -1)
550 if (!g_pGameConf->GetOffset("CTTeamScoreOffset", &ctTeamOffsetOffset))
552 ctTeamOffsetOffset = -1;
553 return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
556 ctTeamOffset = *(int *)((intptr_t)addr + ctTeamOffsetOffset);
559 if (tTeamOffsetOffset == -1)
561 if (!g_pGameConf->GetOffset("TTeamScoreOffset", &tTeamOffsetOffset))
563 tTeamOffsetOffset = -1;
564 return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
567 tTeamOffset = *(int *)((intptr_t)addr + tTeamOffsetOffset);
570 void *gamerules = g_pSDKTools->GetGameRules();
571 if (gamerules == NULL)
573 return pContext->ThrowNativeError("GameRules not available. CS_GetTeamScore native disabled.");
578 return *(int16_t *)((intptr_t)gamerules+ctTeamOffset);
580 else if (params[1] == 2)
582 return *(int16_t *)((intptr_t)gamerules+tTeamOffset);
584 return pContext->ThrowNativeError("Invalid team index passed (%i).", params[1]);
587 static cell_t CS_SetMVPCount(IPluginContext *pContext, const cell_t *params)
592 if (!g_pGameConf->GetMemSig("IncrementNumMVPs", &addr) || !addr)
594 return pContext->ThrowNativeError("Failed to locate IncrementNumMVPs function");
598 CBaseEntity *pEntity;
599 if (!(pEntity = GetCBaseEntity(params[1], true)))
601 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
604 static int mvpOffsetOffset = -1;
605 static int mvpOffset;
607 if (mvpOffsetOffset == -1)
609 if (!g_pGameConf->GetOffset("MVPCountOffset", &mvpOffsetOffset))
611 mvpOffsetOffset = -1;
612 return pContext->ThrowNativeError("Unable to find MVPCountOffset gamedata");
615 mvpOffset = *(int *)((intptr_t)addr + mvpOffsetOffset);
618 *(int *)((intptr_t)pEntity + mvpOffset) = params[2];
623 static cell_t CS_GetMVPCount(IPluginContext *pContext, const cell_t *params)
628 if (!g_pGameConf->GetMemSig("IncrementNumMVPs", &addr) || !addr)
630 return pContext->ThrowNativeError("Failed to locate IncrementNumMVPs function");
634 CBaseEntity *pEntity;
635 if (!(pEntity = GetCBaseEntity(params[1], true)))
637 return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
640 static int mvpOffsetOffset = -1;
641 static int mvpOffset;
643 if (mvpOffsetOffset == -1)
645 if (!g_pGameConf->GetOffset("MVPCountOffset", &mvpOffsetOffset))
647 mvpOffsetOffset = -1;
648 return pContext->ThrowNativeError("Unable to find MVPCountOffset gamedata");
651 mvpOffset = *(int *)((intptr_t)addr + mvpOffsetOffset);
654 return *(int *)((intptr_t)pEntity + mvpOffset);
657 sp_nativeinfo_t g_CSNatives[] =
659 {"CS_RespawnPlayer", CS_RespawnPlayer},
660 {"CS_SwitchTeam", CS_SwitchTeam},
661 {"CS_DropWeapon", CS_DropWeapon},
662 {"CS_TerminateRound", CS_TerminateRound},
663 {"CS_GetTranslatedWeaponAlias", CS_GetTranslatedWeaponAlias},
664 {"CS_GetWeaponPrice", CS_GetWeaponPrice},
665 {"CS_GetClientClanTag", CS_GetClientClanTag},
666 {"CS_SetClientClanTag", CS_SetClientClanTag},
667 {"CS_AliasToWeaponID", CS_AliasToWeaponID},
668 {"CS_GetTeamScore", CS_GetTeamScore},
669 {"CS_SetTeamScore", CS_SetTeamScore},
670 {"CS_GetMVPCount", CS_GetMVPCount},
671 {"CS_SetMVPCount", CS_SetMVPCount},