Compare commits

..
Author SHA1 Message Date
Nicholas Hastings 76e8e7e50c Update versioning for 1.6.1 release. 2014-08-17 09:14:04 -04:00
Ryan Stecker 40d049e1f6 Update changelog for 1.6.1. 2014-08-17 09:05:29 -04:00
Ryan Stecker 48012ccf08 Mark TF2_RemoveWearable as optional. 2014-08-16 16:30:39 -04:00
Ryan Stecker 80ceb149b0 Fix typo related to invalid regex handle errors. 2014-08-14 14:15:18 -04:00
Nicholas Hastings 1ef60a13d0 Fix automatic language detection on CS:GO (bug 6163). 2014-08-11 05:28:29 -07:00
WildCard65 e8e40d126e Actually remove extra wearables.
Old logic was to try to get extra wearables on a non-existent entity.
2014-08-08 16:04:34 +01:00
Ryan Stecker c7033827f5 Implement StringToKeyValues. 2014-08-03 09:49:16 -04:00
Ryan Stecker ff27214615 Remove the extra wearables of weapons in TF2_RemoveWeaponSlot. 2014-08-03 09:06:22 -04:00
Ross Bemrose 7b45e963d5 Make sure the edict isn't free.
return GetParametersForSoundEx's return value instead of always true.
2014-07-31 07:53:19 -07:00
Ross Bemrose 96965ef7cd Fixed overwriting the same copy/pasted line twice instead of once per new native. 2014-07-31 07:53:12 -07:00
Ross Bemrose 8f7ce184fb Commit new version of bug 5942: New functions for SDKTools to get information from game_sound files. 2014-07-31 07:52:58 -07:00
Ryan Stecker 4bed0a2893 Fix PerformGravity's logging format. 2014-07-31 12:46:27 +01:00
Kyle Sanderson 6ab2e23d9d Rename SDKHooks function and variable to be friendlier. 2014-07-31 12:46:23 +01:00
Ryan Stecker dd27de8d85 Add more TF2_RemoveWearable documentation. 2014-07-31 12:46:22 +01:00
Asher Baker 8c79a6df33 Improved error reporting in basebans custom reasons loading. 2014-07-31 12:46:13 +01:00
Ryan Stecker e8111f3c0a Implement TF2_RemoveWearable native. 2014-07-31 12:46:09 +01:00
Kyle Sanderson a038181193 Pre-allocate 8 elements to skip trivial reallocations near the beginning of the vectors life. 2014-07-31 12:46:08 +01:00
Kyle Sanderson 52fe916311 Store only the callback as per VoiDeD, re-eval if HookList changes. 2014-07-31 12:46:05 +01:00
Kyle Sanderson d01d7b1879 HackFix: Mimic forwardsys style of pawn calling. 2014-07-31 12:46:04 +01:00
Nicholas Hastings d8406a3efc Update Fistful of Frags gamedata. 2014-07-30 06:36:00 -04:00
Peace-Maker d74204aded Fix regression. SDKHook_ReloadPost never firing
The switch to vtable hooks typo'd the ReloadPost hooks.
2014-07-16 18:05:27 -04:00
David Anderson 77be859667 Remove references to the DisableJIT option. r=psychonic 2014-07-15 09:42:15 -07:00
David Anderson 71193c67b2 Revert "Merge pull request #94 from alliedmodders/debug-jit"
This reverts commit cc1493aa66, reversing
changes made to 5776909b13.
2014-07-15 09:35:16 -07:00
David Anderson cc1493aa66 Merge pull request #94 from alliedmodders/debug-jit
Add instrumentation to JIT for invalid instruction errors.
2014-07-14 09:49:18 -07:00
David Anderson 43946e6b6f Add instrumentation to JIT invalid instruction errors. 2014-07-14 09:19:44 -07:00
Ruben Gonzalez 5776909b13 Update TerminateRound code and gamedata for update. 2014-07-11 09:20:05 -04:00
Nicholas Hastings 048c227fcc Bumping version. 2014-07-03 15:58:02 -04:00
23 changed files with 636 additions and 160 deletions
+21
View File
@@ -1,5 +1,26 @@
SourceMod Changelog
SourceMod 1.6.1 [2014-08-17]
URL: http://wiki.alliedmods.net/SourceMod_1.6.1_Release_Notes
User Changes:
- Updated game support for CS:GO and Fistful of Frags.
- Added support for Romanian language.
- Re-added languages.cfg, missing from 1.6.0, causing all clients to use server language.
- Fixed a regression in version 1.6.0 that caused certain SDKHooks usage patterns to cause a server crash (PR 107).
- Improved error reporting in basebans plugin when banreasons.txt is missing (PR 115).
- Fixed incorrect logged gravity value for sm_gravity command (Ryan "VoiDeD" Stecker) (PR 116).
- Fixed a regression in version 1.6.0 causing SDKHook_ReloadPost hooks to not fire in plugins (Peace-Maker) (PR 96).
- Added workaround for game bug in TF2_RemoveWeaponSlot to avoid wearable entities left on map (Ryan "VoiDeD" Stecker) (bug 6206, PR 121).
- Removed DisableJIT option from core.cfg. (It was never meant for normal use.)
Developer Changes:
- Added new TF2_RemoveWearable native (Ryan "VoiDeD" Stecker, WildCard65) (PR 114).
- Added new functions for interacting with and playing scripted game sounds (Ross "Powerlord" Bemrose) (bug 5942, PR 65).
- Added new StringToKeyValues natives (Ryan "VoiDeD" Stecker) (PR 74).
----------------------------------------------------------
SourceMod 1.6.0 [2014-07-03]
URL: http://wiki.alliedmods.net/SourceMod_1.6.0_Release_Notes
-8
View File
@@ -132,13 +132,5 @@
* passed. You can disable this feature by setting the value to "0".
*/
"SlowScriptTimeout" "8"
/**
* Disable the SourcePawn just-in-time compiler. This is intended for C++ developers using
* debugging tools and finding it difficult to inspect JIT stack frames. The interpreter
* is not as well-tested as the JIT and will make script execution roughly 10X slower, so
* it is not intended for general purpose use.
*/
"DisableJIT" "no"
}
+12 -2
View File
@@ -648,8 +648,11 @@ QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char
return InvalidQueryCvarCookie;
}
ConVarQuery query = {cookie, pCallback, (cell_t)hndl, IndexOfEdict(pPlayer)};
m_ConVarQueries.push_back(query);
if (pCallback != NULL)
{
ConVarQuery query = { cookie, pCallback, (cell_t) hndl, IndexOfEdict(pPlayer) };
m_ConVarQueries.push_back(query);
}
#endif
return cookie;
@@ -753,6 +756,13 @@ void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, CEntityIn
void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
#endif // SE_DOTA
{
#if SOURCE_ENGINE == SE_CSGO
if (g_Players.HandleConVarQuery(cookie, pPlayer, result, cvarName, cvarValue))
{
return;
}
#endif
IPluginFunction *pCallback = NULL;
cell_t value = 0;
List<ConVarQuery>::iterator iter;
+35
View File
@@ -502,6 +502,9 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
/* Get the client's language */
if (m_QueryLang)
{
#if SOURCE_ENGINE == SE_CSGO
pPlayer->m_LangId = translator->GetServerLanguage();
#else
const char *name;
if (!pPlayer->IsFakeClient() && (name=engine->GetClientConVarValue(client, "cl_language")))
{
@@ -510,6 +513,7 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
} else {
pPlayer->m_LangId = translator->GetServerLanguage();
}
#endif
}
List<IClientListener *>::iterator iter;
@@ -727,6 +731,13 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
}
pPlayer->Authorize_Post();
}
#if SOURCE_ENGINE == SE_CSGO
else
{
// Not a bot
pPlayer->m_LanguageCookie = g_ConVarManager.QueryClientConVar(pEntity, "cl_language", NULL, 0);
}
#endif
if (playerinfo)
{
@@ -1862,6 +1873,24 @@ void CmdMaxplayersCallback()
g_Players.MaxPlayersChanged();
}
#if SOURCE_ENGINE == SE_CSGO
bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
{
for (int i = 1; i <= m_maxClients; i++)
{
if (m_Players[i].m_LanguageCookie == cookie)
{
unsigned int langid;
m_Players[i].m_LangId = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
return true;
}
}
return false;
}
#endif
/*******************
*** PLAYER CODE ***
@@ -1886,6 +1915,9 @@ CPlayer::CPlayer()
m_bIsReplay = false;
m_Serial.value = -1;
m_SteamAccountID = 0;
#if SOURCE_ENGINE == SE_CSGO
m_LanguageCookie = InvalidQueryCvarCookie;
#endif
}
void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
@@ -1966,6 +1998,9 @@ void CPlayer::Disconnect()
m_bIsReplay = false;
m_Serial.value = -1;
m_SteamAccountID = 0;
#if SOURCE_ENGINE == SE_CSGO
m_LanguageCookie = InvalidQueryCvarCookie;
#endif
}
void CPlayer::SetName(const char *name)
+6
View File
@@ -131,6 +131,9 @@ private:
bool m_bIsReplay;
serial_t m_Serial;
unsigned int m_SteamAccountID;
#if SOURCE_ENGINE == SE_CSGO
QueryCvarCookie_t m_LanguageCookie;
#endif
};
class PlayerManager :
@@ -211,6 +214,9 @@ public:
unsigned int GetReplyTo();
unsigned int SetReplyTo(unsigned int reply);
void MaxPlayersChanged(int newvalue = -1);
#if SOURCE_ENGINE == SE_CSGO
bool HandleConVarQuery(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
#endif
private:
#if SOURCE_ENGINE == SE_DOTA
void OnServerActivate();
+28 -1
View File
@@ -797,6 +797,32 @@ static cell_t smn_FileToKeyValues(IPluginContext *pCtx, const cell_t *params)
return g_HL2.KVLoadFromFile(kv, basefilesystem, path);
}
static cell_t smn_StringToKeyValues(IPluginContext *pCtx, const cell_t *params)
{
Handle_t hndl = static_cast<Handle_t>(params[1]);
HandleError herr;
HandleSecurity sec;
KeyValueStack *pStk;
KeyValues *kv;
sec.pOwner = NULL;
sec.pIdentity = g_pCoreIdent;
if ((herr=handlesys->ReadHandle(hndl, g_KeyValueType, &sec, (void **)&pStk))
!= HandleError_None)
{
return pCtx->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr);
}
char *buffer;
char *resourceName;
pCtx->LocalToString(params[2], &buffer);
pCtx->LocalToString(params[3], &resourceName);
kv = pStk->pCurRoot.front();
return kv->LoadFromBuffer(resourceName, buffer);
}
static cell_t smn_KvSetEscapeSequences(IPluginContext *pCtx, const cell_t *params)
{
Handle_t hndl = static_cast<Handle_t>(params[1]);
@@ -1101,6 +1127,7 @@ REGISTER_NATIVES(keyvaluenatives)
{"KvGetDataType", smn_KvGetDataType},
{"KeyValuesToFile", smn_KeyValuesToFile},
{"FileToKeyValues", smn_FileToKeyValues},
{"StringToKeyValues", smn_StringToKeyValues},
{"KvSetEscapeSequences", smn_KvSetEscapeSequences},
{"KvDeleteThis", smn_KvDeleteThis},
{"KvDeleteKey", smn_KvDeleteKey},
@@ -1113,4 +1140,4 @@ REGISTER_NATIVES(keyvaluenatives)
{"KvGetVector", smn_KvGetVector},
{"KvSetVector", smn_KvSetVector},
{NULL, NULL}
};
};
-2
View File
@@ -372,12 +372,10 @@ static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params)
float delay = sp_ctof(params[1]);
int reason = params[2];
signed int unknown = 1;//We might want to find what this is?
__asm
{
push reason
movss xmm1, delay
mov edi, unknown
mov ecx, gamerules
call addr
}
+2 -2
View File
@@ -106,7 +106,7 @@ static cell_t MatchRegex(IPluginContext *pCtx, const cell_t *params)
if ((err=g_pHandleSys->ReadHandle(hndl, g_RegexHandle, &sec, (void **)&x)) != HandleError_None)
{
return pCtx->ThrowNativeError("Invalid file handle %x (error %d)", hndl, err);
return pCtx->ThrowNativeError("Invalid regex handle %x (error %d)", hndl, err);
}
if (!x)
@@ -159,7 +159,7 @@ static cell_t GetRegexSubString(IPluginContext *pCtx, const cell_t *params)
if ((err=g_pHandleSys->ReadHandle(hndl, g_RegexHandle, &sec, (void **)&x)) != HandleError_None)
{
return pCtx->ThrowNativeError("Invalid file handle %x (error %d)", hndl, err);
return pCtx->ThrowNativeError("Invalid regex handle %x (error %d)", hndl, err);
}
if (!x)
+69 -105
View File
@@ -438,6 +438,21 @@ FeatureStatus SDKHooks::GetFeatureStatus(FeatureType type, const char *name)
/**
* Functions
*/
static void PopulateCallbackList(const ke::Vector<HookList> &source, ke::Vector<IPluginFunction *> &destination, int entity)
{
destination.ensure(8); /* Skip trivial allocations as AMTL uses length<<1. */
for (size_t iter = 0; iter < source.length(); ++iter)
{
if (source[iter].entity != entity)
{
continue;
}
destination.append(source[iter].callback);
}
}
cell_t SDKHooks::Call(int entity, SDKHookType type, int other)
{
return Call(gamehelpers->ReferenceToEntity(entity), type, gamehelpers->ReferenceToEntity(other));
@@ -461,17 +476,14 @@ cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, CBaseEntity *pOther)
continue;
}
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
int entity = gamehelpers->EntityToBCompatRef(pEnt);
int other = gamehelpers->EntityToBCompatRef(pOther);
for (entry = 0; entry < pawnlist.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(other);
@@ -914,15 +926,11 @@ void SDKHooks::Hook_FireBulletsPost(const FireBulletsInfo_t &info)
const char *weapon = pInfo->GetWeaponName();
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(info.m_iShots);
callback->PushString(weapon?weapon:"");
@@ -956,15 +964,11 @@ int SDKHooks::Hook_GetMaxHealth()
cell_t res = Pl_Continue;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCellByRef(&new_max);
callback->Execute(&res);
@@ -1013,15 +1017,11 @@ int SDKHooks::Hook_OnTakeDamage(CTakeDamageInfoHack &info)
cell_t res, ret = Pl_Continue;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCellByRef(&attacker);
callback->PushCellByRef(&inflictor);
@@ -1095,15 +1095,11 @@ int SDKHooks::Hook_OnTakeDamagePost(CTakeDamageInfoHack &info)
int entity = gamehelpers->EntityToBCompatRef(pEntity);
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(info.GetAttacker());
callback->PushCell(info.GetInflictor());
@@ -1168,15 +1164,11 @@ bool SDKHooks::Hook_Reload()
int entity = gamehelpers->EntityToBCompatRef(pEntity);
cell_t res = Pl_Continue;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->Execute(&res);
}
@@ -1195,7 +1187,7 @@ bool SDKHooks::Hook_ReloadPost()
CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
CVTableHook vhook(pEntity);
ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_Reload];
ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_ReloadPost];
for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
{
if (vhook != vtablehooklist[entry]->vtablehook)
@@ -1206,15 +1198,11 @@ bool SDKHooks::Hook_ReloadPost()
int entity = gamehelpers->EntityToBCompatRef(pEntity);
cell_t origreturn = META_RESULT_ORIG_RET(bool) ? 1 : 0;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(origreturn);
callback->Execute(NULL);
@@ -1253,15 +1241,11 @@ bool SDKHooks::Hook_ShouldCollide(int collisionGroup, int contentsMask)
cell_t origRet = ((META_RESULT_STATUS >= MRES_OVERRIDE)?(META_RESULT_OVERRIDE_RET(bool)):(META_RESULT_ORIG_RET(bool))) ? 1 : 0;
cell_t res = 0;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(collisionGroup);
callback->PushCell(contentsMask);
@@ -1301,15 +1285,11 @@ void SDKHooks::Hook_Spawn()
int entity = gamehelpers->EntityToBCompatRef(pEntity);
cell_t res = Pl_Continue;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->Execute(&res);
}
@@ -1399,15 +1379,11 @@ void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir,
int ammotype = info.GetAmmoType();
cell_t res, ret = Pl_Continue;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCellByRef(&attacker);
callback->PushCellByRef(&inflictor);
@@ -1477,15 +1453,11 @@ void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vec
int entity = gamehelpers->EntityToBCompatRef(pEntity);
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(info.GetAttacker());
callback->PushCell(info.GetInflictor());
@@ -1521,15 +1493,11 @@ void SDKHooks::Hook_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE
int caller = gamehelpers->EntityToBCompatRef(pCaller);
cell_t ret = Pl_Continue;
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(activator);
callback->PushCell(caller);
@@ -1564,15 +1532,11 @@ void SDKHooks::Hook_UsePost(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_T
int activator = gamehelpers->EntityToBCompatRef(pActivator);
int caller = gamehelpers->EntityToBCompatRef(pCaller);
ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
for (entry = 0; entry < pawnlist.length(); ++entry)
ke::Vector<IPluginFunction *> callbackList;
PopulateCallbackList(vtablehooklist[entry]->hooks, callbackList, entity);
for (entry = 0; entry < callbackList.length(); ++entry)
{
if (entity != pawnlist[entry].entity)
{
continue;
}
IPluginFunction *callback = pawnlist[entry].callback;
IPluginFunction *callback = callbackList[entry];
callback->PushCell(entity);
callback->PushCell(activator);
callback->PushCell(caller);
+2
View File
@@ -66,6 +66,7 @@ IPlayerInfoManager *playerinfomngr = NULL;
ICvar *icvar = NULL;
IServer *iserver = NULL;
CGlobalVars *gpGlobals;
ISoundEmitterSystemBase *soundemitterbase = NULL;
#if SOURCE_ENGINE >= SE_ORANGEBOX
IServerTools *servertools = NULL;
@@ -258,6 +259,7 @@ bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool
#if SOURCE_ENGINE >= SE_ORANGEBOX
GET_V_IFACE_ANY(GetServerFactory, servertools, IServerTools, VSERVERTOOLS_INTERFACE_VERSION);
#endif
GET_V_IFACE_ANY(GetEngineFactory, soundemitterbase, ISoundEmitterSystemBase, SOUNDEMITTERSYSTEM_INTERFACE_VERSION);
gpGlobals = ismm->GetCGlobals();
enginePatch = SH_GET_CALLCLASS(engine);
+2
View File
@@ -50,6 +50,7 @@
#include <convar.h>
#include <iserver.h>
#include <cdll_int.h>
#include "SoundEmitterSystem/isoundemittersystembase.h"
#if SOURCE_ENGINE >= SE_ORANGEBOX
#include <itoolentity.h>
@@ -124,6 +125,7 @@ extern CGlobalVars *gpGlobals;
#if SOURCE_ENGINE >= SE_ORANGEBOX
extern IServerTools *servertools;
#endif
extern ISoundEmitterSystemBase *soundemitterbase;
/* Interfaces from SourceMod */
extern IBinTools *g_pBinTools;
extern IGameConfig *g_pGameConf;
+115
View File
@@ -512,6 +512,74 @@ RETURN_META_NEWPARAMS(
#endif
}
bool GetSoundParams(CSoundParameters *soundParams, const char *soundname, cell_t entindex)
{
if ( !soundname[0] )
return false;
#if SOURCE_ENGINE >= SE_PORTAL2
HSOUNDSCRIPTHASH index = (HSOUNDSCRIPTHASH)soundemitterbase->GetSoundIndex(soundname);
#else
HSOUNDSCRIPTHANDLE index = (HSOUNDSCRIPTHANDLE)soundemitterbase->GetSoundIndex(soundname);
#endif
if (!soundemitterbase->IsValidIndex(index))
return false;
gender_t gender = GENDER_NONE;
// I don't know if gender applies to any mutliplayer games, but just in case...
// Of course, if it's SOUND_FROM_PLAYER, we have no idea which gender it is
int ent = SoundReferenceToIndex(entindex);
if (ent > 0)
{
edict_t *edict = gamehelpers->EdictOfIndex(ent);
if (edict != NULL && !edict->IsFree())
{
IServerEntity *serverEnt = edict->GetIServerEntity();
if (serverEnt != NULL)
{
const char *actormodel = STRING(serverEnt->GetModelName());
gender = soundemitterbase->GetActorGender(actormodel);
}
}
}
return soundemitterbase->GetParametersForSoundEx(soundname, index, *soundParams, gender);
}
bool InternalPrecacheScriptSound(const char *soundname)
{
int soundIndex = soundemitterbase->GetSoundIndex(soundname);
if (!soundemitterbase->IsValidIndex(soundIndex))
{
return false;
}
CSoundParametersInternal *internal = soundemitterbase->InternalGetParametersForSound(soundIndex);
if (!internal)
return false;
int waveCount = internal->NumSoundNames();
if (!waveCount)
{
return false;
}
for (int wave = 0; wave < waveCount; wave++)
{
const char* waveName = soundemitterbase->GetWaveName(internal->GetSoundNames()[wave].symbol);
// return true even if we precache no new wavs
if (!engsound->IsSoundPrecached(waveName))
{
engsound->PrecacheSound(waveName);
}
}
return true;
}
/************************
* *
* Sound Related Natives *
@@ -1135,6 +1203,51 @@ static cell_t smn_GetDistGainFromSoundLevel(IPluginContext *pContext, const cell
return sp_ftoc(engsound->GetDistGainFromSoundLevel((soundlevel_t)decibel, distance));
}
// native bool:GetGameSoundParams(const String:gameSound[], &channel, &soundLevel, &Float:volume, &pitch, String:sample[], maxlength, entity=SOUND_FROM_WORLD)
static cell_t smn_GetGameSoundParams(IPluginContext *pContext, const cell_t *params)
{
char *soundname;
pContext->LocalToString(params[1], &soundname);
CSoundParameters soundParams;
if (!GetSoundParams(&soundParams, soundname, params[8]))
return false;
cell_t *channel;
cell_t *fakeVolume;
cell_t *pitch;
cell_t *soundLevel;
pContext->LocalToPhysAddr(params[2], &channel);
pContext->LocalToPhysAddr(params[3], &soundLevel);
pContext->LocalToPhysAddr(params[4], &fakeVolume);
pContext->LocalToPhysAddr(params[5], &pitch);
*channel = soundParams.channel;
*pitch = soundParams.pitch;
*soundLevel = (cell_t)soundParams.soundlevel;
*fakeVolume = sp_ftoc(soundParams.volume);
pContext->StringToLocal(params[6], params[7], soundParams.soundname);
// Precache the sound we're returning
if (!engsound->IsSoundPrecached(soundParams.soundname))
{
InternalPrecacheScriptSound(soundname);
}
return true;
}
// native bool:PrecacheScriptSound(const String:soundname[])
static cell_t smn_PrecacheScriptSound(IPluginContext *pContext, const cell_t *params)
{
char *soundname;
pContext->LocalToString(params[1], &soundname);
return InternalPrecacheScriptSound(soundname);
}
sp_nativeinfo_t g_SoundNatives[] =
{
{"EmitAmbientSound", EmitAmbientSound},
@@ -1149,5 +1262,7 @@ sp_nativeinfo_t g_SoundNatives[] =
{"RemoveAmbientSoundHook", smn_RemoveAmbientSoundHook},
{"RemoveNormalSoundHook", smn_RemoveNormalSoundHook},
{"GetDistGainFromSoundLevel", smn_GetDistGainFromSoundLevel},
{"GetGameSoundParams", smn_GetGameSoundParams},
{"PrecacheScriptSound", smn_PrecacheScriptSound},
{NULL, NULL},
};
+50
View File
@@ -599,6 +599,55 @@ cell_t TF2_IsHolidayActive(IPluginContext *pContext, const cell_t *params)
return (retValue) ? 1 : 0;
}
cell_t TF2_RemoveWearable(IPluginContext *pContext, const cell_t *params)
{
static ICallWrapper *pWrapper = NULL;
//CBasePlayer::RemoveWearable(CEconWearable *)
if (!pWrapper)
{
int offset;
if (!g_pGameConf->GetOffset("RemoveWearable", &offset))
{
return pContext->ThrowNativeError("Failed to locate function");
}
PassInfo pass[1];
pass[0].flags = PASSFLAG_BYVAL;
pass[0].size = sizeof(CBaseEntity *);
pass[0].type = PassType_Basic;
pWrapper = g_pBinTools->CreateVCall(offset, 0, 0, NULL, pass, 1);
g_RegNatives.Register(pWrapper);
}
CBaseEntity *pEntity;
if (!(pEntity = UTIL_GetCBaseEntity(params[1], true)))
{
return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
}
CBaseEntity *pWearable;
if (!(pWearable = UTIL_GetCBaseEntity(params[2], false)))
{
return pContext->ThrowNativeError("Wearable index %d is not valid", params[2]);
}
unsigned char vstk[sizeof(void *) + sizeof(CBaseEntity *)];
unsigned char *vptr = vstk;
*(void **)vptr = (void *)pEntity;
vptr += sizeof(void *);
*(CBaseEntity **)vptr = pWearable;
pWrapper->Execute(vstk, NULL);
return 1;
}
sp_nativeinfo_t g_TFNatives[] =
{
{"TF2_IgnitePlayer", TF2_Burn},
@@ -616,5 +665,6 @@ sp_nativeinfo_t g_TFNatives[] =
{"TF2_MakeBleed", TF2_MakeBleed},
{"TF2_IsPlayerInDuel", TF2_IsPlayerInDuel},
{"TF2_IsHolidayActive", TF2_IsHolidayActive},
{"TF2_RemoveWearable", TF2_RemoveWearable},
{NULL, NULL}
};
+3 -3
View File
@@ -167,9 +167,9 @@
}
"CommitSuicide"
{
"windows" "441"
"linux" "441"
"mac" "441"
"windows" "442"
"linux" "442"
"mac" "442"
}
"GetVelocity"
{
+1 -1
View File
@@ -96,7 +96,7 @@
"TerminateRound"
{
"library" "server"
"windows" "\x55\x8B\xEC\x83\x2A\x2A\x83\x2A\x2A\x53\x56\x8B\xF1\xF3\x0F\x2A\x2A\x2A\x2A\x57\x33\xFF\x89\x2A\x2A\x2A\xC7\x2A\x2A\x2A\x00\x00\x00\x00"
"windows" "\x55\x8B\xEC\x83\x2A\x2A\x83\x2A\x2A\x53\x56\x57\x8B\xF9\xF3\x0F\x2A\x2A\x2A\x2A\x33\xDB\xC7\x2A\x2A\x2A\x00\x00\x00\x00\x89"
"linux" "@_ZN12CCSGameRules14TerminateRoundEfi"
"mac" "@_ZN12CCSGameRules14TerminateRoundEfi"
}
+7
View File
@@ -137,6 +137,13 @@
"linux" "139"
"mac" "139"
}
"RemoveWearable"
{
"windows" "426"
"linux" "427"
"mac" "427"
}
}
}
}
+17 -34
View File
@@ -58,22 +58,12 @@ new String:g_BanReasonsPath[PLATFORM_MAX_PATH];
#include "basebans/ban.sp"
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
BuildPath(Path_SM, g_BanReasonsPath, sizeof(g_BanReasonsPath), "configs/banreasons.txt");
if(LoadBanReasons())
{
return APLRes_Success;
}
else
{
Format(error, err_max, "Couldn't load file %s: See log for details.", g_BanReasonsPath);
return APLRes_Failure;
}
}
public OnPluginStart()
{
BuildPath(Path_SM, g_BanReasonsPath, sizeof(g_BanReasonsPath), "configs/banreasons.txt");
LoadBanReasons();
LoadTranslations("common.phrases");
LoadTranslations("basebans.phrases");
LoadTranslations("core.phrases");
@@ -106,14 +96,7 @@ public OnPluginEnd()
public OnMapStart()
{
//(Re-)Load BanReasons
if(g_hKvBanReasons != INVALID_HANDLE)
{
CloseHandle(g_hKvBanReasons);
}
if(!LoadBanReasons())
{
SetFailState("Error trying to load or parse %s: See logfile for details", g_BanReasonsPath);
}
LoadBanReasons();
}
public OnClientDisconnect(client)
@@ -121,32 +104,32 @@ public OnClientDisconnect(client)
g_IsWaitingForChatReason[client] = false;
}
public bool:LoadBanReasons()
LoadBanReasons()
{
if (g_hKvBanReasons != INVALID_HANDLE)
{
CloseHandle(g_hKvBanReasons);
}
g_hKvBanReasons = CreateKeyValues("banreasons");
if(FileToKeyValues(g_hKvBanReasons, g_BanReasonsPath))
{
decl String:sectionName[255];
if(!KvGetSectionName(g_hKvBanReasons, sectionName, sizeof(sectionName)))
{
LogMessage("Error in %s: File corrupt or in the wrong format", g_BanReasonsPath);
return false;
return SetFailState("Error in %s: File corrupt or in the wrong format", g_BanReasonsPath);
}
if(strcmp(sectionName, "banreasons") != 0)
{
LogMessage("Error in %s: Couldn't find 'banreasons'", g_BanReasonsPath);
return false;
return SetFailState("Error in %s: Couldn't find 'banreasons'", g_BanReasonsPath);
}
//Reset kvHandle
KvRewind(g_hKvBanReasons);
return true;
}
else
{
LogMessage("Error in %s: File not found, corrupt or in the wrong format", g_BanReasonsPath);
return false;
} else {
return SetFailState("Error in %s: File not found, corrupt or in the wrong format", g_BanReasonsPath);
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ new g_GravityTarget[MAXPLAYERS+1];
PerformGravity(client, target, Float:amount)
{
SetEntityGravity(target, amount);
LogAction(client, target, "\"%L\" set gravity on \"%L\" to %d.", client, target, amount);
LogAction(client, target, "\"%L\" set gravity on \"%L\" to %f.", client, target, amount);
}
public AdminMenu_Gravity(Handle:topmenu,
+12
View File
@@ -360,6 +360,18 @@ native bool:KeyValuesToFile(Handle:kv, const String:file[]);
*/
native bool:FileToKeyValues(Handle:kv, const String:file[]);
/**
* Converts a given string to a KeyValues tree. The string is read into
* the current postion of the tree.
*
* @param kv KeyValues Handle.
* @param buffer String buffer to load into the KeyValues.
* @param resourceName The resource name of the KeyValues, used for error tracking purposes.
* @return True on success, false otherwise.
* @error Invalid Handle.
*/
native bool:StringToKeyValues(Handle:kv, const String:buffer[], const String:resourceName[]="StringToKeyValues");
/**
* Sets whether or not the KeyValues parser will read escape sequences.
* For example, \n would be read as a literal newline. This defaults
+226
View File
@@ -448,3 +448,229 @@ stock ATTN_TO_SNDLEVEL(Float:attn)
}
return 0;
}
/**
* Retrieves the parameters for a game sound.
*
* Game sounds are found in a game's scripts/game_sound.txt or other files
* referenced from it
*
* Note that if a game sound has a rndwave section, one of them will be returned
* at random.
*
* @param gameSound Name of game sound.
* @param channel Channel to emit with.
* @param level Sound level.
* @param volume Sound volume.
* @param pitch Sound pitch.
* @param sample Sound file name relative to the "sounds" folder.
* @param maxlength Maximum length of sample string buffer.
* @param entity Entity the sound is being emitted from.
* @return True if the sound was successfully retrieved, false if it
* was not found
*/
native bool:GetGameSoundParams(const String:gameSound[],
&channel,
&soundLevel,
&Float:volume,
&pitch,
String:sample[],
maxlength,
entity=SOUND_FROM_PLAYER);
/**
* Emits a game sound to a list of clients.
*
* Game sounds are found in a game's scripts/game_sound.txt or other files
* referenced from it
*
* Note that if a game sound has a rndwave section, one of them will be returned
* at random.
*
* @param clients Array of client indexes.
* @param numClients Number of clients in the array.
* @param gameSound Name of game sound.
* @param entity Entity to emit from.
* @param flags Sound flags.
* @param speakerentity Unknown.
* @param origin Sound origin.
* @param dir Sound direction.
* @param updatePos Unknown (updates positions?)
* @param soundtime Alternate time to play sound for.
* @return True if the sound was played successfully, false if it failed
* @error Invalid client index.
*/
stock bool:EmitGameSound(const clients[],
numClients,
const String:gameSound[],
entity = SOUND_FROM_PLAYER,
flags = SND_NOFLAGS,
speakerentity = -1,
const Float:origin[3] = NULL_VECTOR,
const Float:dir[3] = NULL_VECTOR,
bool:updatePos = true,
Float:soundtime = 0.0)
{
new channel;
new level;
new Float:volume;
new pitch;
new String:sample[PLATFORM_MAX_PATH];
if (GetGameSoundParams(gameSound, channel, level, volume, pitch, sample, sizeof(sample), entity))
{
EmitSound(clients, numClients, sample, entity, channel, level, flags, volume, pitch, speakerentity, origin, dir, updatePos, soundtime);
return true;
}
else
{
return false;
}
}
/**
* Emits an ambient game sound.
*
* Game sounds are found in a game's scripts/game_sound.txt or other files
* referenced from it
*
* Note that if a game sound has a rndwave section, one of them will be returned
* at random.
*
* @param gameSound Name of game sound.
* @param pos Origin of sound.
* @param entity Entity index to associate sound with.
* @param flags Sound flags.
* @param delay Play delay.
* @noreturn
*/
stock bool:EmitAmbientGameSound(const String:gameSound[],
const Float:pos[3],
entity = SOUND_FROM_WORLD,
flags = SND_NOFLAGS,
Float:delay = 0.0)
{
new channel; // This is never actually used for Ambients, but it's a mandatory field to GetGameSoundParams
new level;
new Float:volume;
new pitch;
new String:sample[PLATFORM_MAX_PATH];
if (GetGameSoundParams(gameSound, channel, level, volume, pitch, sample, sizeof(sample), entity))
{
EmitAmbientSound(sample, pos, entity, level, flags, volume, pitch, delay);
return true;
}
else
{
return false;
}
}
/**
* Wrapper to emit a game sound to one client.
*
* Game sounds are found in a game's scripts/game_sound.txt or other files
* referenced from it
*
* Note that if a game sound has a rndwave section, one of them will be returned
* at random.
*
* @param client Client index.
* @param gameSound Name of game sound.
* @param entity Entity to emit from.
* @param flags Sound flags.
* @param speakerentity Unknown.
* @param origin Sound origin.
* @param dir Sound direction.
* @param updatePos Unknown (updates positions?)
* @param soundtime Alternate time to play sound for.
* @noreturn
* @error Invalid client index.
*/
stock bool:EmitGameSoundToClient(client,
const String:gameSound[],
entity = SOUND_FROM_PLAYER,
flags = SND_NOFLAGS,
speakerentity = -1,
const Float:origin[3] = NULL_VECTOR,
const Float:dir[3] = NULL_VECTOR,
bool:updatePos = true,
Float:soundtime = 0.0)
{
new clients[1];
clients[0] = client;
/* Save some work for SDKTools and remove SOUND_FROM_PLAYER references */
entity = (entity == SOUND_FROM_PLAYER) ? client : entity;
return EmitGameSound(clients, 1, gameSound, entity, flags,
speakerentity, origin, dir, updatePos, soundtime);
}
/**
* Wrapper to emit game sound to all clients.
*
* Game sounds are found in a game's scripts/game_sound.txt or other files
* referenced from it
*
* Note that if a game sound has a rndwave section, one of them will be returned
* at random.
*
* @param gameSound Name of game sound.
* @param entity Entity to emit from.
* @param flags Sound flags.
* @param speakerentity Unknown.
* @param origin Sound origin.
* @param dir Sound direction.
* @param updatePos Unknown (updates positions?)
* @param soundtime Alternate time to play sound for.
* @noreturn
* @error Invalid client index.
*/
stock bool:EmitGameSoundToAll(const String:gameSound[],
entity = SOUND_FROM_PLAYER,
flags = SND_NOFLAGS,
speakerentity = -1,
const Float:origin[3] = NULL_VECTOR,
const Float:dir[3] = NULL_VECTOR,
bool:updatePos = true,
Float:soundtime = 0.0)
{
new clients[MaxClients];
new total = 0;
for (new i=1; i<=MaxClients; i++)
{
if (IsClientInGame(i))
{
clients[total++] = i;
}
}
if (!total)
{
return false;
}
return EmitGameSound(clients, total, gameSound, entity, flags,
speakerentity, origin, dir, updatePos, soundtime);
}
/**
* Precache a game sound.
*
* Most games will precache all game sounds on map start, but this is not guaranteed...
* Team Fortress 2 is known to not pre-cache MvM game mode sounds on non-MvM maps.
*
* Due to the above, this native should be called before any calls to GetGameSoundParams,
* EmitGameSound*, or EmitAmbientGameSound.
*
* It should be safe to pass already precached game sounds to this function.
*
* Note: It precaches all files for a game sound.
*
* @param soundname Game sound to precache
*
* @return True if the game sound was found, false if sound did not exist
* or had no files
*/
native bool:PrecacheScriptSound(const String:soundname[]);
+12
View File
@@ -359,6 +359,17 @@ native bool:TF2_IsHolidayActive(TFHoliday:holiday);
*/
native bool:TF2_IsPlayerInDuel(client);
/**
* Removes an econ wearable (hat, misc, etc) from a player.
* This also deletes the wearable entity.
*
* @param client Client index.
* @param wearable Index of the wearable entity.
* @noreturn
* @error Invalid client index, client not in game, invalid wearable entity, or no mod support.
*/
native TF2_RemoveWearable(client, wearable);
/**
* Called after a condition is added to a player
*
@@ -436,5 +447,6 @@ public __ext_tf2_SetNTVOptional()
MarkNativeAsOptional("TF2_GetClass");
MarkNativeAsOptional("TF2_IsPlayerInDuel");
MarkNativeAsOptional("TF2_IsHolidayActive");
MarkNativeAsOptional("TF2_RemoveWearable");
}
#endif
+14
View File
@@ -435,6 +435,20 @@ stock TF2_RemoveWeaponSlot(client, slot)
new weaponIndex;
while ((weaponIndex = GetPlayerWeaponSlot(client, slot)) != -1)
{
// bug #6206
// papering over a valve bug where a weapon's extra wearables aren't properly removed from the weapon's owner
new extraWearable = GetEntPropEnt(weaponIndex, Prop_Send, "m_hExtraWearable");
if (extraWearable != -1)
{
TF2_RemoveWearable(client, extraWearable);
}
extraWearable = GetEntPropEnt(weaponIndex, Prop_Send, "m_hExtraWearableViewModel");
if (extraWearable != -1)
{
TF2_RemoveWearable(client, extraWearable);
}
RemovePlayerItem(client, weaponIndex);
AcceptEntityInput(weaponIndex, "Kill");
}
+1 -1
View File
@@ -1 +1 @@
1.6.0
1.6.1