Cvars: Add bind_pcvar_num/float/string

This commit is contained in:
Arkshine 2015-01-24 21:28:43 +01:00
parent 8ebb7be36d
commit cb0b9da51f
6 changed files with 244 additions and 22 deletions

View File

@ -50,21 +50,17 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value)
}
}
if (info->hooks.empty()) // No hooked cvars, nothing to call.
if (!info->hooks.empty())
{
return true;
}
int lastResult = 0;
int result;
for (size_t i = 0; i < info->hooks.length(); ++i)
{
CvarPlugin* p = info->hooks[i];
CvarHook* hook = info->hooks[i];
if (p->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives.
if (hook->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives.
{
result = executeForwards(p->forward->id, reinterpret_cast<cvar_t*>(var), var->string, value);
int result = executeForwards(hook->forward->id, reinterpret_cast<cvar_t*>(var), var->string, value);
if (result >= lastResult)
{
@ -73,7 +69,42 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value)
}
}
return !!!lastResult;
if (lastResult) // A plugin wants to block the forward.
{
return false;
}
}
if (!info->binds.empty()) // Time to update our available binds.
{
for (size_t i = 0; i < info->binds.length(); ++i)
{
CvarBind* bind = info->binds[i];
switch (bind->type)
{
case CvarBind::CvarType_Int:
{
*bind->varAddress = atoi(value);
break;
}
case CvarBind::CvarType_Float:
{
float fvalue = atof(value);
*bind->varAddress = amx_ftoc(fvalue);
break;
}
case CvarBind::CvarType_String:
{
set_amxstring_simple(bind->varAddress, value, bind->varLength);
break;
}
}
}
}
// Nothing to block.
return true;
}
DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value)
@ -204,6 +235,9 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char*
CVAR_DIRECTSET(var, value);
}
// Detour is disabled on map change.
m_HookDetour->EnableDetour();
return info->var;
}
@ -212,6 +246,9 @@ CvarInfo* CvarManager::FindCvar(const char* name)
cvar_t* var = nullptr;
CvarInfo* info = nullptr;
// Detour is disabled on map change.
m_HookDetour->EnableDetour();
// Do we have already cvar in cache?
if (CacheLookup(name, &info))
{
@ -293,7 +330,7 @@ Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const ch
m_HookDetour->EnableDetour();
Forward* forward = new Forward(forwardId, *callback);
info->hooks.append(new CvarPlugin(g_plugins.findPlugin(amx)->getId(), forward));
info->hooks.append(new CvarHook(g_plugins.findPlugin(amx)->getId(), forward));
return forward;
}
@ -332,11 +369,17 @@ void CvarManager::OnPluginUnloaded()
// Clear only plugin hooks list.
for (CvarsList::iterator cvar = m_Cvars.begin(); cvar != m_Cvars.end(); cvar++)
{
for (size_t i = 0; i < (*cvar)->binds.length(); ++i)
{
delete (*cvar)->binds[i];
}
for (size_t i = 0; i < (*cvar)->hooks.length(); ++i)
{
delete (*cvar)->hooks[i];
}
(*cvar)->binds.clear();
(*cvar)->hooks.clear();
}
@ -350,6 +393,11 @@ void CvarManager::OnAmxxShutdown()
// Free everything.
for (CvarsList::iterator cvar = m_Cvars.begin(); cvar != m_Cvars.end(); cvar = m_Cvars.erase(cvar))
{
for (size_t i = 0; i < (*cvar)->binds.length(); ++i)
{
delete (*cvar)->binds[i];
}
for (size_t i = 0; i < (*cvar)->hooks.length(); ++i)
{
delete (*cvar)->hooks[i];

View File

@ -45,28 +45,51 @@ struct Forward
ke::AString callback;
};
struct CvarPlugin
struct CvarHook
{
CvarPlugin(int id, Forward* fwd) : pluginId(id), forward(fwd) {};
CvarPlugin(int id) : pluginId(id), forward(new Forward()) {};
CvarHook(int id, Forward* fwd) : pluginId(id), forward(fwd) {};
CvarHook(int id) : pluginId(id), forward(new Forward()) {};
int pluginId;
ke::AutoPtr<Forward> forward;
};
typedef ke::Vector<CvarPlugin*> CvarsHook;
struct CvarBind
{
enum CvarType
{
CvarType_Int,
CvarType_Float,
CvarType_String,
};
CvarBind(int id_, CvarType type_, cell* varAddress_, size_t varLength_)
:
pluginId(id_), type(type_), varAddress(varAddress_), varLength(varLength_) {};
int pluginId;
CvarType type;
cell* varAddress;
size_t varLength;
};
typedef ke::Vector<CvarHook*> CvarsHook;
typedef ke::Vector<CvarBind*> CvarsBind;
struct CvarInfo : public ke::InlineListNode<CvarInfo>
{
CvarInfo(const char* name_, const char* helpText, bool hasMin_, float min_, bool hasMax_, float max_,
CvarInfo(const char* name_, const char* helpText,
bool hasMin_, float min_, bool hasMax_, float max_,
const char* plugin_, int pluginId_)
:
name(name_), description(helpText), hasMin(hasMin_), minVal(min_), hasMax(hasMax_), maxVal(max_),
name(name_), description(helpText),
hasMin(hasMin_), minVal(min_), hasMax(hasMax_), maxVal(max_),
plugin(plugin_), pluginId(pluginId_) {};
CvarInfo(const char* name_)
:
name(name_), defaultval(""), description(""), hasMin(false), minVal(0), hasMax(false), maxVal(0),
name(name_), defaultval(""), description(""),
hasMin(false), minVal(0), hasMax(false), maxVal(0),
plugin(""), pluginId(-1), amxmodx(false) {};
cvar_t* var;
@ -81,6 +104,7 @@ struct CvarInfo : public ke::InlineListNode<CvarInfo>
ke::AString plugin;
int pluginId;
CvarsBind binds;
CvarsHook hooks;
bool amxmodx;

View File

@ -282,6 +282,7 @@ int amxstring_len(cell* cstr);
int load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug);
int set_amxnatives(AMX* amx, char error[64]);
int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max);
int set_amxstring_simple(cell *dest, const char *source, int max);
template <typename T> int set_amxstring_utf8(AMX *amx, cell amx_addr, const T *source, size_t sourcelen, size_t maxlen);
int set_amxstring_utf8_char(AMX *amx, cell amx_addr, const char *source, size_t sourcelen, size_t maxlen);
int set_amxstring_utf8_cell(AMX *amx, cell amx_addr, const cell *source, size_t sourcelen, size_t maxlen);

View File

@ -340,6 +340,103 @@ static cell AMX_NATIVE_CALL get_pcvar_bounds(AMX *amx, cell *params)
return hasBound;
}
bool bind_pcvar(CvarInfo* info, CvarBind::CvarType type, AMX* amx, cell varofs, size_t varlen = 0)
{
if (varofs > amx->hlw) // If variable address is not inside global area, we can't bind it.
{
LogError(amx, AMX_ERR_NATIVE, "A global variable must be provided");
return false;
}
int pluginId = g_plugins.findPluginFast(amx)->getId();
cell* address = get_amxaddr(amx, varofs);
// To avoid unexpected behavior, probably better to error such situations.
for (size_t i = 0; i < info->binds.length(); ++i)
{
CvarBind* bind = info->binds[i];
if (bind->pluginId == pluginId)
{
if (bind->varAddress == address)
{
LogError(amx, AMX_ERR_NATIVE, "A same variable can't be binded with several cvars");
}
else
{
LogError(amx, AMX_ERR_NATIVE, "A cvar can't be binded with several variables");
}
return false;
}
}
CvarBind* bind = new CvarBind(pluginId, type, get_amxaddr(amx, varofs), varlen);
info->binds.append(bind);
// Update right away variable with current cvar value.
switch (type)
{
case CvarBind::CvarType_Int:
*bind->varAddress = atoi(info->var->string);
break;
case CvarBind::CvarType_Float:
*bind->varAddress = amx_ftoc(info->var->value);
break;
case CvarBind::CvarType_String:
set_amxstring_simple(bind->varAddress, info->var->string, bind->varLength);
break;
}
return true;
}
// bind_pcvar_float(pcvar, &Float:var)
static cell AMX_NATIVE_CALL bind_pcvar_float(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
CvarInfo* info = nullptr;
if (!ptr || !(info = g_CvarManager.FindCvar(ptr->name)))
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return bind_pcvar(info, CvarBind::CvarType_Float, amx, params[2]);
}
// bind_pcvar_num(pcvar, &any:var)
static cell AMX_NATIVE_CALL bind_pcvar_num(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
CvarInfo* info = nullptr;
if (!ptr || !(info = g_CvarManager.FindCvar(ptr->name)))
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return bind_pcvar(info, CvarBind::CvarType_Int, amx, params[2]);
}
// bind_pcvar_string(pcvar, any:var[], varlen)
static cell AMX_NATIVE_CALL bind_pcvar_string(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
CvarInfo* info = nullptr;
if (!ptr || !(info = g_CvarManager.FindCvar(ptr->name)))
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return bind_pcvar(info, CvarBind::CvarType_String, amx, params[2], params[3]);
}
// set_pcvar_flags(pcvar, flags)
static cell AMX_NATIVE_CALL set_pcvar_flags(AMX *amx, cell *params)
{
@ -613,6 +710,10 @@ AMX_NATIVE_INFO g_CvarNatives[] =
{"remove_cvar_flags", remove_cvar_flags},
{"bind_pcvar_float", bind_pcvar_float},
{"bind_pcvar_num", bind_pcvar_num},
{"bind_pcvar_string", bind_pcvar_string},
{"get_plugins_cvar", get_plugins_cvar},
{"get_plugins_cvarsnum", get_plugins_cvarsnum},

View File

@ -65,6 +65,20 @@ cell* get_amxaddr(AMX *amx, cell amx_addr)
return (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));
}
int set_amxstring_simple(cell *dest, const char *source, int max)
{
cell* start = dest;
while (max-- && *source)
{
*dest++ = (unsigned char)*source++;
}
*dest = 0;
return dest - start;
}
int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max)
{
register cell* dest = (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));

View File

@ -89,7 +89,7 @@ native cvar_exists(const cvar[]);
native get_cvar_pointer(const cvar[]);
/**
* Creates a hook for when a console variable's value is changed.
* Creates a hook for when a cvar's value is changed.
*
* @note Callback will be called in the following manner:
*
@ -394,6 +394,40 @@ native bool:get_pcvar_bounds(pcvar, CvarBounds:type, &Float:value);
*/
native set_pcvar_bounds(pcvar, CvarBounds:type, bool:set, Float:value = 0.0);
/**
* Binds a cvar to a global integer variable.
* This means that variable will be automagically updated on cvar's value change.
*
* @param pcvar Pointer to cvar
* @param var Global variable to update to
*
* @error Invalid cvar pointer, invalid provided variable or cvar/variable already binded.
*/
native bind_pcvar_num(pcvar, &any:var);
/**
* Binds a cvar to a global float variable.
* This means that variable will be automagically updated on cvar's value change.
*
* @param pcvar Pointer to cvar
* @param var Global variable to update to
*
* @error Invalid cvar pointer, invalid provided variable or cvar/variable already binded.
*/
native bind_pcvar_float(pcvar, &Float:var);
/**
* Binds a cvar to a global string variable.
* This means that variable will be automagically updated on cvar's value change.
*
* @param pcvar Pointer to cvar
* @param var Global variable to update to
* @param varlen Maximum length of string buffer
*
* @error Invalid cvar pointer, invalid provided variable or cvar/variable already binded.
*/
native bind_pcvar_string(pcvar, any:var[], varlen);
/**
* Returns the number of plugin-registered cvars.
*