/**
* vim: set ts=4 :
* =============================================================================
* SourceMod SDKTools Extension
* Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or .
*
* Version: $Id: outputnatives.cpp 1521 2007-10-01 21:20:30Z faluco $
*/
#include "extension.h"
#include "output.h"
// HookSingleEntityOutput(ent, const String:output[], function, bool:once);
cell_t HookSingleEntityOutput(IPluginContext *pContext, const cell_t *params)
{
if (!g_OutputManager.IsEnabled())
{
return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
}
edict_t *pEdict = engine->PEntityOfEntIndex(params[1]);
if (!pEdict)
{
return pContext->ThrowNativeError("Invalid Entity index %i", params[1]);
}
const char *classname = pEdict->GetClassName();
char *outputname;
pContext->LocalToString(params[2], &outputname);
OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, true);
//Check for an existing identical hook
SourceHook::List::iterator _iter;
omg_hooks *hook;
IPluginFunction *pFunction;
pFunction = pContext->GetFunctionById(params[3]);
for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
{
hook = (omg_hooks *)*_iter;
if (hook->pf == pFunction && hook->entity_filter == pEdict->m_NetworkSerialNumber)
{
return 0;
}
}
hook = g_OutputManager.NewHook();
hook->entity_filter = pEdict->m_NetworkSerialNumber;
hook->entity_index = engine->IndexOfEdict(pEdict);
hook->only_once= !!params[4];
hook->pf = pFunction;
hook->m_parent = pOutputName;
hook->in_use = false;
hook->delete_me = false;
pOutputName->hooks.push_back(hook);
g_OutputManager.OnHookAdded();
IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
SourceHook::List *pList = NULL;
if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
{
pList = new SourceHook::List;
pPlugin->SetProperty("OutputHookList", pList);
}
pList->push_back(hook);
return 1;
}
// HookEntityOutput(const String:classname[], const String:output[], function);
cell_t HookEntityOutput(IPluginContext *pContext, const cell_t *params)
{
if (!g_OutputManager.IsEnabled())
{
return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
}
//Find or create the base structures for this classname and the output
char *classname;
pContext->LocalToString(params[1], &classname);
char *outputname;
pContext->LocalToString(params[2], &outputname);
OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, true);
//Check for an existing identical hook
SourceHook::List::iterator _iter;
omg_hooks *hook;
IPluginFunction *pFunction;
pFunction = pContext->GetFunctionById(params[3]);
for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
{
hook = (omg_hooks *)*_iter;
if (hook->pf == pFunction && hook->entity_filter == -1)
{
//already hooked to this function...
//throw an error or just let them get away with stupidity?
// seems like poor coding if they dont know if something is hooked or not
return 0;
}
}
hook = g_OutputManager.NewHook();
hook->entity_filter = -1;
hook->pf = pFunction;
hook->m_parent = pOutputName;
hook->in_use = false;
hook->delete_me = false;
pOutputName->hooks.push_back(hook);
g_OutputManager.OnHookAdded();
IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
SourceHook::List *pList = NULL;
if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
{
pList = new SourceHook::List;
pPlugin->SetProperty("OutputHookList", pList);
}
pList->push_back(hook);
return 1;
}
// UnHookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback);
cell_t UnHookEntityOutput(IPluginContext *pContext, const cell_t *params)
{
if (!g_OutputManager.IsEnabled())
{
return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
}
char *classname;
pContext->LocalToString(params[1], &classname);
char *outputname;
pContext->LocalToString(params[2], &outputname);
OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, false);
if (!pOutputName)
{
return 0;
}
//Check for an existing identical hook
SourceHook::List::iterator _iter;
omg_hooks *hook;
IPluginFunction *pFunction;
pFunction = pContext->GetFunctionById(params[3]);
for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
{
hook = (omg_hooks *)*_iter;
if (hook->pf == pFunction && hook->entity_filter == -1)
{
// remove this hook.
if (hook->in_use)
{
hook->delete_me = true;
return 1;
}
pOutputName->hooks.erase(_iter);
g_OutputManager.CleanUpHook(hook);
return 1;
}
}
return 0;
}
// UnHookSingleEntityOutput(entity, const String:output[], EntityOutput:callback);
cell_t UnHookSingleEntityOutput(IPluginContext *pContext, const cell_t *params)
{
if (!g_OutputManager.IsEnabled())
{
return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
}
// Find the classname of the entity and lookup the classname and output structures
edict_t *pEdict = engine->PEntityOfEntIndex(params[1]);
if (!pEdict)
{
return pContext->ThrowNativeError("Invalid Entity index %i", params[1]);
}
const char *classname = pEdict->GetClassName();
char *outputname;
pContext->LocalToString(params[2], &outputname);
OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, false);
if (!pOutputName)
{
return 0;
}
//Check for an existing identical hook
SourceHook::List::iterator _iter;
omg_hooks *hook;
IPluginFunction *pFunction;
pFunction = pContext->GetFunctionById(params[3]);
for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
{
hook = (omg_hooks *)*_iter;
if (hook->pf == pFunction && hook->entity_index == engine->IndexOfEdict(pEdict))
{
// remove this hook.
if (hook->in_use)
{
hook->delete_me = true;
return 1;
}
pOutputName->hooks.erase(_iter);
g_OutputManager.CleanUpHook(hook);
return 1;
}
}
return 0;
}
#if 0
static cell_t GetVariantType(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
return pInfo->crab.FieldType();
}
static cell_t GetVariantInt(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
if (pInfo->crab.FieldType() != FIELD_INTEGER)
{
return pContext->ThrowNativeError("Variant is not an integer");
}
return pInfo->crab.Int();
}
static cell_t GetVariantBool(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
if (pInfo->crab.FieldType() != FIELD_BOOLEAN)
{
return pContext->ThrowNativeError("Variant is not a boolean");
}
return pInfo->crab.Bool();
}
static cell_t GetVariantEntity(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
if (pInfo->crab.FieldType() != FIELD_EHANDLE)
{
return pContext->ThrowNativeError("Variant is not an entity");
}
edict_t *pEdict = g_OutputManager.BaseHandleToEdict((CBaseHandle)pInfo->crab.Entity());
return engine->IndexOfEdict(pEdict);
}
static cell_t GetVariantFloat(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
if (pInfo->crab.FieldType() != FIELD_FLOAT)
{
return pContext->ThrowNativeError("Variant is not a float");
}
return sp_ftoc(pInfo->crab.Float());
}
static cell_t GetVariantVector(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
if (pInfo->crab.FieldType() != FIELD_VECTOR)
{
return pContext->ThrowNativeError("Variant is not a vector");
}
cell_t *r;
pContext->LocalToPhysAddr(params[2], &r);
Vector temp;
pInfo->crab.Vector3D(temp);
r[0] = sp_ftoc(temp[0]);
r[1] = sp_ftoc(temp[1]);
r[2] = sp_ftoc(temp[2]);
return 0;
}
static cell_t GetVariantColour(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
if (pInfo->crab.FieldType() != FIELD_COLOR32)
{
return pContext->ThrowNativeError("Variant is not a colour");
}
cell_t *r;
pContext->LocalToPhysAddr(params[2], &r);
color32 temp = pInfo->crab.Color32();
r[0] = temp.r;
r[1] = temp.g;
r[2] = temp.b;
r[4] = temp.a;
return 0;
}
static cell_t GetVariantString(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast(params[1]);
HandleError err;
varhandle_t *pInfo;
HandleSecurity sec;
sec.pOwner = NULL;
sec.pIdentity = myself->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
}
char *dest;
const char *src = pInfo->crab.String();
size_t len;
pContext->LocalToString(params[2], &dest);
/* Perform bounds checking */
len = strlen(src);
if (len >= (unsigned)params[3])
{
len = params[3] - 1;
} else {
len = params[3];
}
/* Copy */
memmove(dest, src, len);
dest[len] = '\0';
return 0;
}
#endif
sp_nativeinfo_t g_EntOutputNatives[] =
{
{"HookEntityOutput", HookEntityOutput},
{"UnHookEntityOutput", UnHookEntityOutput},
{"HookSingleEntityOutput", HookSingleEntityOutput},
{"UnHookSingleEntityOutput", UnHookSingleEntityOutput},
#if 0 //Removed because we don't need them
{"GetVariantType", GetVariantType},
{"GetVariantInt", GetVariantInt},
{"GetVariantFloat", GetVariantFloat},
{"GetVariantBool", GetVariantBool},
{"GetVariantString", GetVariantString},
{"GetVariantEntity", GetVariantEntity},
{"GetVariantVector", GetVariantVector},
{"GetVariantColour", GetVariantColour},
#endif
{NULL, NULL},
};
#if 0
//Include file stuff that wasn't needed:
enum FieldType
{
FieldType_Float = 1,
FieldType_String = 2,
FieldType_Vector = 3,
FieldType_Int = 5,
FieldType_Bool = 6,
FieldType_Colour = 9,
FieldType_Entity = 13
}
/**
* Gets the current type of a variant handle
*
* @param variant A variant handle.
* @return Current type of the variant.
*/
native FieldType:GetVariantType(Handle:variant);
/**
* Retreives a Float from a variant handle.
*
* @param variant A variant handle.
* @return Float value.
* @error Variant handle is not type FieldType_Float
*/
native Float:GetVariantFloat(Handle:variant);
/**
* Retreives an Integer from a variant handle.
*
* @param variant A variant handle.
* @return Int value.
* @error Variant handle is not type FieldType_Int
*/
native GetVariantInt(Handle:variant);
/**
* Retreives a bool from a variant handle.
*
* @param variant A variant handle.
* @return bool value.
* @error Variant handle is not type FieldType_Bool
*/
native bool:GetVariantBool(Handle:variant);
/**
* Retreives an entity from a variant handle.
*
* @param variant A variant handle.
* @return Entity Index.
* @error Variant handle is not type FieldType_Entity
*/
native GetVariantEntity(Handle:variant);
/**
* Retreives a Vector from a variant handle.
*
* @param variant A variant handle.
* @param vec buffer to store the Vector.
* @noreturn
* @error Variant handle is not type FieldType_Vector
*/
native GetVariantVector(Handle:variant, Float:vec[3]);
/**
* Retreives a String from a variant handle.
*
* @note This native does not require the variant to be FieldType_String,
* It can convert any type into a string representation.
*
* @param variant A variant handle.
* @param buffer buffer to store the string in
* @param maxlen Maximum length of buffer.
* @noreturn
*/
native GetVariantString(Handle:variant, String:buffer[], maxlen);
/**
* Retreives a Colour from a variant handle.
*
* @note Colour is in format r,g,b,a
*
* @param variant A variant handle.
* @param colour buffer array to store the colour in.
* @noreturn
* @error Variant handle is not type FieldType_Colour
*/
native GetVariantColour(Handle:variant, colour[4]);
#endif