mirror of
https://github.com/alliedmodders/amxmodx.git
synced 2024-12-25 06:15:37 +03:00
Cvars: Add bind_pcvar_num/float/string
This commit is contained in:
parent
8ebb7be36d
commit
cb0b9da51f
@ -50,30 +50,61 @@ 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;
|
||||
|
||||
for (size_t i = 0; i < info->hooks.length(); ++i)
|
||||
{
|
||||
CvarHook* hook = info->hooks[i];
|
||||
|
||||
if (hook->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives.
|
||||
{
|
||||
int result = executeForwards(hook->forward->id, reinterpret_cast<cvar_t*>(var), var->string, value);
|
||||
|
||||
if (result >= lastResult)
|
||||
{
|
||||
lastResult = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastResult) // A plugin wants to block the forward.
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int lastResult = 0;
|
||||
int result;
|
||||
|
||||
for (size_t i = 0; i < info->hooks.length(); ++i)
|
||||
if (!info->binds.empty()) // Time to update our available binds.
|
||||
{
|
||||
CvarPlugin* p = info->hooks[i];
|
||||
|
||||
if (p->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives.
|
||||
for (size_t i = 0; i < info->binds.length(); ++i)
|
||||
{
|
||||
result = executeForwards(p->forward->id, reinterpret_cast<cvar_t*>(var), var->string, value);
|
||||
CvarBind* bind = info->binds[i];
|
||||
|
||||
if (result >= lastResult)
|
||||
switch (bind->type)
|
||||
{
|
||||
lastResult = result;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !!!lastResult;
|
||||
// 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];
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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},
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user