Cvars: Add bind_pcvar_num/float/string
This commit is contained in:
parent
8ebb7be36d
commit
cb0b9da51f
|
@ -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 lastResult = 0;
|
||||||
int result;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < info->hooks.length(); ++i)
|
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)
|
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)
|
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);
|
CVAR_DIRECTSET(var, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detour is disabled on map change.
|
||||||
|
m_HookDetour->EnableDetour();
|
||||||
|
|
||||||
return info->var;
|
return info->var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +246,9 @@ CvarInfo* CvarManager::FindCvar(const char* name)
|
||||||
cvar_t* var = nullptr;
|
cvar_t* var = nullptr;
|
||||||
CvarInfo* info = nullptr;
|
CvarInfo* info = nullptr;
|
||||||
|
|
||||||
|
// Detour is disabled on map change.
|
||||||
|
m_HookDetour->EnableDetour();
|
||||||
|
|
||||||
// Do we have already cvar in cache?
|
// Do we have already cvar in cache?
|
||||||
if (CacheLookup(name, &info))
|
if (CacheLookup(name, &info))
|
||||||
{
|
{
|
||||||
|
@ -293,7 +330,7 @@ Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const ch
|
||||||
m_HookDetour->EnableDetour();
|
m_HookDetour->EnableDetour();
|
||||||
|
|
||||||
Forward* forward = new Forward(forwardId, *callback);
|
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;
|
return forward;
|
||||||
}
|
}
|
||||||
|
@ -332,11 +369,17 @@ void CvarManager::OnPluginUnloaded()
|
||||||
// Clear only plugin hooks list.
|
// Clear only plugin hooks list.
|
||||||
for (CvarsList::iterator cvar = m_Cvars.begin(); cvar != m_Cvars.end(); cvar++)
|
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)
|
for (size_t i = 0; i < (*cvar)->hooks.length(); ++i)
|
||||||
{
|
{
|
||||||
delete (*cvar)->hooks[i];
|
delete (*cvar)->hooks[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*cvar)->binds.clear();
|
||||||
(*cvar)->hooks.clear();
|
(*cvar)->hooks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,6 +393,11 @@ void CvarManager::OnAmxxShutdown()
|
||||||
// Free everything.
|
// Free everything.
|
||||||
for (CvarsList::iterator cvar = m_Cvars.begin(); cvar != m_Cvars.end(); cvar = m_Cvars.erase(cvar))
|
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)
|
for (size_t i = 0; i < (*cvar)->hooks.length(); ++i)
|
||||||
{
|
{
|
||||||
delete (*cvar)->hooks[i];
|
delete (*cvar)->hooks[i];
|
||||||
|
|
|
@ -45,28 +45,51 @@ struct Forward
|
||||||
ke::AString callback;
|
ke::AString callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CvarPlugin
|
struct CvarHook
|
||||||
{
|
{
|
||||||
CvarPlugin(int id, Forward* fwd) : pluginId(id), forward(fwd) {};
|
CvarHook(int id, Forward* fwd) : pluginId(id), forward(fwd) {};
|
||||||
CvarPlugin(int id) : pluginId(id), forward(new Forward()) {};
|
CvarHook(int id) : pluginId(id), forward(new Forward()) {};
|
||||||
|
|
||||||
int pluginId;
|
int pluginId;
|
||||||
ke::AutoPtr<Forward> forward;
|
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>
|
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_)
|
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_) {};
|
plugin(plugin_), pluginId(pluginId_) {};
|
||||||
|
|
||||||
CvarInfo(const char* name_)
|
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) {};
|
plugin(""), pluginId(-1), amxmodx(false) {};
|
||||||
|
|
||||||
cvar_t* var;
|
cvar_t* var;
|
||||||
|
@ -81,6 +104,7 @@ struct CvarInfo : public ke::InlineListNode<CvarInfo>
|
||||||
ke::AString plugin;
|
ke::AString plugin;
|
||||||
int pluginId;
|
int pluginId;
|
||||||
|
|
||||||
|
CvarsBind binds;
|
||||||
CvarsHook hooks;
|
CvarsHook hooks;
|
||||||
bool amxmodx;
|
bool amxmodx;
|
||||||
|
|
||||||
|
|
|
@ -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 load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug);
|
||||||
int set_amxnatives(AMX* amx, char error[64]);
|
int set_amxnatives(AMX* amx, char error[64]);
|
||||||
int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max);
|
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);
|
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_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);
|
int set_amxstring_utf8_cell(AMX *amx, cell amx_addr, const cell *source, size_t sourcelen, size_t maxlen);
|
||||||
|
|
|
@ -340,6 +340,103 @@ static cell AMX_NATIVE_CALL get_pcvar_bounds(AMX *amx, cell *params)
|
||||||
return hasBound;
|
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)
|
// set_pcvar_flags(pcvar, flags)
|
||||||
static cell AMX_NATIVE_CALL set_pcvar_flags(AMX *amx, cell *params)
|
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},
|
{"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_cvar", get_plugins_cvar},
|
||||||
{"get_plugins_cvarsnum", get_plugins_cvarsnum},
|
{"get_plugins_cvarsnum", get_plugins_cvarsnum},
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,20 @@ cell* get_amxaddr(AMX *amx, cell amx_addr)
|
||||||
return (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + 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)
|
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));
|
register cell* dest = (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));
|
||||||
|
|
|
@ -89,7 +89,7 @@ native cvar_exists(const cvar[]);
|
||||||
native get_cvar_pointer(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:
|
* @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);
|
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.
|
* Returns the number of plugin-registered cvars.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue
Block a user