Merge pull request #247 from AnthonyIacono/normalshook-upgrade

Improvements for NormalSHook functionality (r=psychonic).
This commit is contained in:
Nicholas Hastings 2015-02-18 14:43:33 -05:00
commit 589482736f
2 changed files with 284 additions and 3 deletions

View File

@ -283,6 +283,57 @@ void SoundHooks::OnEmitAmbientSound(int entindex, const Vector &pos, const char
}
}
#if SOURCE_ENGINE >= SE_PORTAL2
// This should probably be moved to the gamedata
#define SOUND_ENTRY_HASH_SEED 0x444F5441
uint32 GenerateSoundEntryHash(char const *pSoundEntry)
{
// First we need to convert the sound entry to lowercase before we calculate the hash
int nSoundEntryLength = strlen(pSoundEntry);
char *pSoundEntryLowerCase = (char *)stackalloc(nSoundEntryLength + 1);
for (int nIndex = 0; nIndex < nSoundEntryLength; nIndex++)
pSoundEntryLowerCase[nIndex] = tolower(pSoundEntry[nIndex]);
// Second we need to calculate the hash using the algorithm reconstructed from CS:GO
const uint32 nMagicNumber = 0x5bd1e995;
uint32 nSoundHash = SOUND_ENTRY_HASH_SEED ^ nSoundEntryLength;
unsigned char *pData = (unsigned char *)pSoundEntryLowerCase;
while (nSoundEntryLength >= 4)
{
uint32 nLittleDWord = LittleDWord(*(uint32 *)pData);
nLittleDWord *= nMagicNumber;
nLittleDWord ^= nLittleDWord >> 24;
nLittleDWord *= nMagicNumber;
nSoundHash *= nMagicNumber;
nSoundHash ^= nLittleDWord;
pData += 4;
nSoundEntryLength -= 4;
}
switch (nSoundEntryLength)
{
case 3: nSoundHash ^= pData[2] << 16;
case 2: nSoundHash ^= pData[1] << 8;
case 1: nSoundHash ^= pData[0];
nSoundHash *= nMagicNumber;
};
nSoundHash ^= nSoundHash >> 13;
nSoundHash *= nMagicNumber;
nSoundHash ^= nSoundHash >> 15;
return nSoundHash;
}
#endif
#if SOURCE_ENGINE >= SE_PORTAL2
int SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChannel, const char *pSoundEntry, unsigned int nSoundEntryHash, const char *pSample,
float flVolume, soundlevel_t iSoundlevel, int nSeed, int iFlags, int iPitch, const Vector *pOrigin,
@ -306,6 +357,15 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann
char buffer[PLATFORM_MAX_PATH];
strcpy(buffer, pSample);
char soundEntry[PLATFORM_MAX_PATH] = "";
#if SOURCE_ENGINE >= SE_PORTAL2
Q_strncpy(soundEntry, pSoundEntry, sizeof(soundEntry));
#endif
#if SOURCE_ENGINE < SE_PORTAL2
int nSeed = 0;
#endif
for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
{
int players[SM_MAXPLAYERS], size;
@ -321,6 +381,8 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann
pFunc->PushCellByRef(reinterpret_cast<cell_t *>(&iSoundlevel));
pFunc->PushCellByRef(&iPitch);
pFunc->PushCellByRef(&iFlags);
pFunc->PushStringEx(soundEntry, sizeof(soundEntry), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
pFunc->PushCellByRef(&nSeed);
g_InSoundHook = true;
pFunc->Execute(&res);
g_InSoundHook = false;
@ -360,6 +422,16 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann
#endif
}
#if SOURCE_ENGINE >= SE_PORTAL2
if (strcmp(pSoundEntry, soundEntry) != 0 || strcmp(pSample, buffer) != 0)
{
if (strcmp(soundEntry, buffer) == 0)
nSoundEntryHash = -1;
else if (strcmp(soundEntry, "") != 0)
nSoundEntryHash = GenerateSoundEntryHash(soundEntry);
}
#endif
CellRecipientFilter crf;
crf.Initialize(players, size);
#if SOURCE_ENGINE >= SE_PORTAL2
@ -368,7 +440,7 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann
-1,
static_cast<int (IEngineSound::*)(IRecipientFilter &, int, int, const char*, unsigned int, const char*, float, soundlevel_t,
int, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>(&IEngineSound::EmitSound),
(crf, iEntIndex, iChannel, buffer, -1, buffer, flVolume, iSoundlevel, nSeed, iFlags, iPitch, pOrigin,
(crf, iEntIndex, iChannel, soundEntry, nSoundEntryHash, buffer, flVolume, iSoundlevel, nSeed, iFlags, iPitch, pOrigin,
pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity)
);
#elif SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_TF2
@ -421,6 +493,15 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan
char buffer[PLATFORM_MAX_PATH];
strcpy(buffer, pSample);
char soundEntry[PLATFORM_MAX_PATH] = "";
#if SOURCE_ENGINE >= SE_PORTAL2
Q_strncpy(soundEntry, pSoundEntry, sizeof(soundEntry));
#endif
#if SOURCE_ENGINE < SE_PORTAL2
int nSeed = 0;
#endif
for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
{
int players[SM_MAXPLAYERS], size;
@ -436,6 +517,8 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan
pFunc->PushCellByRef(&sndlevel);
pFunc->PushCellByRef(&iPitch);
pFunc->PushCellByRef(&iFlags);
pFunc->PushStringEx(soundEntry, sizeof(soundEntry), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
pFunc->PushCellByRef(&nSeed);
g_InSoundHook = true;
pFunc->Execute(&res);
g_InSoundHook = false;
@ -475,6 +558,16 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan
#endif
}
#if SOURCE_ENGINE >= SE_PORTAL2
if (strcmp(pSoundEntry, soundEntry) != 0 || strcmp(pSample, buffer) != 0)
{
if (strcmp(soundEntry, buffer) == 0)
nSoundEntryHash = -1;
else if (strcmp(soundEntry, "") != 0)
nSoundEntryHash = GenerateSoundEntryHash(soundEntry);
}
#endif
CellRecipientFilter crf;
crf.Initialize(players, size);
#if SOURCE_ENGINE >= SE_PORTAL2
@ -483,7 +576,7 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan
-1,
static_cast<int (IEngineSound::*)(IRecipientFilter &, int, int, const char *, unsigned int, const char *, float, float,
int, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>(&IEngineSound::EmitSound),
(crf, iEntIndex, iChannel, buffer, -1, buffer, flVolume, SNDLVL_TO_ATTN(static_cast<soundlevel_t>(sndlevel)),
(crf, iEntIndex, iChannel, soundEntry, nSoundEntryHash, buffer, flVolume, SNDLVL_TO_ATTN(static_cast<soundlevel_t>(sndlevel)),
nSeed, iFlags, iPitch, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity)
);
#elif SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_TF2
@ -1037,6 +1130,147 @@ static cell_t EmitSound(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t EmitSoundEntry(IPluginContext *pContext, const cell_t *params)
{
#if SOURCE_ENGINE < SE_PORTAL2
return pContext->ThrowNativeError("EmitSoundEntry is not available in this game.");
#else
cell_t *addr, *cl_array;
CellRecipientFilter crf;
unsigned int numClients;
int client;
IGamePlayer *pPlayer = NULL;
pContext->LocalToPhysAddr(params[1], &cl_array);
numClients = params[2];
/* Client validation */
for (unsigned int i = 0; i < numClients; i++)
{
client = cl_array[i];
pPlayer = playerhelpers->GetGamePlayer(client);
if (!pPlayer)
{
return pContext->ThrowNativeError("Client index %d is invalid", client);
}
else if (!pPlayer->IsInGame()) {
return pContext->ThrowNativeError("Client %d is not connected", client);
}
}
crf.Initialize(cl_array, numClients);
char *soundEntry;
pContext->LocalToString(params[3], &soundEntry);
char *sample;
pContext->LocalToString(params[4], &sample);
// By default, we want the hash to equal maxint
unsigned int soundEntryHash = -1;
// We only generate a hash if the sample is not the same as the sound entry and the sound entry is not empty.
if (strcmp(soundEntry, sample) != 0 && strcmp(soundEntry, "") != 0)
soundEntryHash = GenerateSoundEntryHash(soundEntry);
int entity = SoundReferenceToIndex(params[5]);
int channel = params[6];
int level = params[7];
int seed = params[8];
int flags = params[9];
float vol = sp_ctof(params[10]);
int pitch = params[11];
int speakerentity = params[12];
Vector *pOrigin = NULL, origin;
Vector *pDir = NULL, dir;
pContext->LocalToPhysAddr(params[13], &addr);
if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
{
pOrigin = &origin;
origin.x = sp_ctof(addr[0]);
origin.y = sp_ctof(addr[1]);
origin.z = sp_ctof(addr[2]);
}
pContext->LocalToPhysAddr(params[14], &addr);
if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
{
pDir = &dir;
dir.x = sp_ctof(addr[0]);
dir.y = sp_ctof(addr[1]);
dir.z = sp_ctof(addr[2]);
}
bool updatePos = params[15] ? true : false;
float soundtime = sp_ctof(params[16]);
CUtlVector<Vector> *pOrigVec = NULL;
CUtlVector<Vector> origvec;
if (params[0] > 16)
{
pOrigVec = &origvec;
for (cell_t i = 17; i <= params[0]; i++)
{
Vector vec;
pContext->LocalToPhysAddr(params[i], &addr);
vec.x = sp_ctof(addr[0]);
vec.y = sp_ctof(addr[1]);
vec.z = sp_ctof(addr[2]);
origvec.AddToTail(vec);
}
}
/* If we're going to a "local player" and this is a dedicated server,
* intelligently redirect each sound.
*/
if (entity == -2 && engine->IsDedicatedServer())
{
for (unsigned int i = 0; i < numClients; i++)
{
cell_t player[1];
player[0] = cl_array[i];
crf.Reset();
crf.Initialize(player, 1);
if (g_InSoundHook)
{
SH_CALL(enginesoundPatch,
static_cast<int (IEngineSound::*)(IRecipientFilter &, int, int, const char*, unsigned int, const char*, float,
soundlevel_t, int, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>
(&IEngineSound::EmitSound))(crf, player[0], channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, seed,
flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity);
}
else
{
engsound->EmitSound(crf, player[0], channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, seed,
flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity);
}
}
}
else {
if (g_InSoundHook)
{
SH_CALL(enginesoundPatch,
static_cast<int (IEngineSound::*)(IRecipientFilter &, int, int, const char*, unsigned int, const char*, float,
soundlevel_t, int, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>
(&IEngineSound::EmitSound))(crf, entity, channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level,
seed, flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity);
}
else
{
engsound->EmitSound(crf, entity, channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, seed,
flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity);
}
}
return 1;
#endif
}
static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params)
{
cell_t *addr;
@ -1253,6 +1487,7 @@ sp_nativeinfo_t g_SoundNatives[] =
{"EmitAmbientSound", EmitAmbientSound},
{"EmitSentence", EmitSentence},
{"EmitSound", EmitSound},
{"EmitSoundEntry", EmitSoundEntry},
{"FadeClientVolume", FadeClientVolume},
{"GetSoundDuration", GetSoundDuration},
{"PrefetchSound", PrefetchSound},

View File

@ -221,6 +221,48 @@ native EmitSound(const clients[],
Float:soundtime = 0.0,
any:...);
/**
* Emits a sound or game sound to a list of clients using the latest version of the engine sound interface.
* This native is only available in engines that are greater than or equal to Portal 2.
*
* @param clients Array of client indexes.
* @param numClients Number of clients in the array.
* @param soundEntry Sound entry name.
* @param sample Sound file name relative to the "sounds" folder.
* @param entity Entity to emit from.
* @param channel Channel to emit with.
* @param level Sound level.
* @param seed Sound seed.
* @param flags Sound flags.
* @param volume Sound volume.
* @param pitch Sound pitch.
* @param speakerentity Unknown.
* @param origin Sound origin.
* @param dir Sound direction.
* @param updatePos Unknown (updates positions?)
* @param soundtime Alternate time to play sound for.
* @param ... Optional list of Float[3] arrays to specify additional origins.
* @noreturn
* @error Invalid client index.
*/
native EmitSoundEntry(const clients[],
numClients,
const String:soundEntry[],
const String:sample[],
entity = SOUND_FROM_PLAYER,
channel = SNDCHAN_AUTO,
level = SNDLEVEL_NORMAL,
seed = 0,
flags = SND_NOFLAGS,
Float:volume = SNDVOL_NORMAL,
pitch = SNDPITCH_NORMAL,
speakerentity = -1,
const Float:origin[3] = NULL_VECTOR,
const Float:dir[3] = NULL_VECTOR,
bool:updatePos = true,
Float:soundtime = 0.0,
any:...);
/**
* Emits a sentence to a list of clients.
*
@ -307,6 +349,8 @@ typedef AmbientSHook = function Action (
* @param level Sound level.
* @param pitch Sound pitch.
* @param flags Sound flags.
* @param soundEntry Game sound entry name. (Used in engines newer than Portal 2)
* @param seed Sound seed. (Used in engines newer than Portal 2)
* @return Plugin_Continue to allow the sound to be played, Plugin_Stop to block it,
* Plugin_Changed when any parameter has been modified.
*/
@ -319,7 +363,9 @@ typedef NormalSHook = function Action (
float &volume,
int &level,
int &pitch,
int &flags
int &flags,
char soundEntry[PLATFORM_MAX_PATH],
int &seed
);
/**