amxmodx/amxmodx/cvars.cpp
2015-10-05 19:12:30 +02:00

730 lines
18 KiB
C++

// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
#include "CvarManager.h"
#include "amxmodx.h"
#include "nongpl_matches.h"
char CVarTempBuffer[64];
const char *invis_cvar_list[5] ={ "amxmodx_version", "amxmodx_modules", "amx_debug", "amx_mldebug", "amx_client_languages" };
// create_cvar(const name[], const default_value[], flags = 0, const description[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0)
static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
const char* value = get_amxstring(amx, params[2], 1, length);
const char* helpText = get_amxstring(amx, params[4], 2, length);
int flags = params[3];
CPluginMngr::CPlugin *plugin = g_plugins.findPluginFast(amx);
if (CheckBadConList(name, 0))
{
plugin->AddToFailCounter(1);
}
CvarInfo* info = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags, helpText);
if (info)
{
bool hasMin = params[5] != 0;
bool hasMax = params[7] != 0;
float minVal = amx_ctof(params[6]);
float maxVal = amx_ctof(params[8]);
if (hasMin && hasMax)
{
if (minVal > maxVal)
{
LogError(amx, AMX_ERR_NATIVE, "The minimum value can not be above the maximum value");
return 0;
}
else if (maxVal < minVal)
{
LogError(amx, AMX_ERR_NATIVE, "The maximum value can not be below the minimum value");
return 0;
}
}
g_CvarManager.SetCvarMin(info, hasMin, minVal, plugin->getId());
g_CvarManager.SetCvarMax(info, hasMax, maxVal, plugin->getId());
return reinterpret_cast<cell>(info->var);
}
return 0;
}
// register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0)
static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
const char* value = get_amxstring(amx, params[2], 1, length);
int flags = params[3];
float fvalue = amx_ctof(params[4]);
CPluginMngr::CPlugin *plugin = g_plugins.findPluginFast(amx);
if (CheckBadConList(name, 0))
{
plugin->AddToFailCounter(1);
}
CvarInfo* info = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags);
if (info)
{
return reinterpret_cast<cell>(info->var);
}
return 0;
}
// cvar_exists(const cvar[])
static cell AMX_NATIVE_CALL cvar_exists(AMX *amx, cell *params)
{
int ilen;
return (g_CvarManager.FindCvar(get_amxstring(amx, params[1], 0, ilen)) ? 1 : 0);
}
// get_cvar_pointer(const cvar[])
static cell AMX_NATIVE_CALL get_cvar_pointer(AMX *amx, cell *params)
{
int len;
const char *name = get_amxstring(amx, params[1], 0, len);
CvarInfo* info = g_CvarManager.FindCvar(name);
return reinterpret_cast<cell>(info ? info->var : 0);
}
// hook_cvar_change(cvarHandle, const callback[])
static cell AMX_NATIVE_CALL hook_cvar_change(AMX *amx, cell *params)
{
cvar_t* var = reinterpret_cast<cvar_t*>(params[1]);
if (!var)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid cvar handle: %p", var);
return 0;
}
const char* callback;
AutoForward* forward = g_CvarManager.HookCvarChange(var, amx, params[2], &callback);
if (!forward)
{
LogError(amx, AMX_ERR_NATIVE, "Function \"%s\" is not present", callback);
return 0;
}
return reinterpret_cast<cell>(forward);
}
// enable_cvar_hook(cvarhook:handle);
static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params)
{
AutoForward* forward = reinterpret_cast<AutoForward*>(params[1]);
if (!forward)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward);
return 0;
}
forward->state = AutoForward::FSTATE_OK;
return 1;
}
// disable_cvar_hook(cvarhook:handle);
static cell AMX_NATIVE_CALL disable_cvar_hook(AMX *amx, cell *params)
{
AutoForward* forward = reinterpret_cast<AutoForward*>(params[1]);
if (!forward)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward);
return 0;
}
forward->state = AutoForward::FSTATE_STOP;
return 1;
}
// get_cvar_flags(const cvar[])
static cell AMX_NATIVE_CALL get_cvar_flags(AMX *amx, cell *params)
{
int ilen;
char* sCvar = get_amxstring(amx, params[1], 0, ilen);
CvarInfo* info = g_CvarManager.FindCvar(sCvar);
return info ? info->var->flags : 0;
}
// get_cvar_float(const cvarname[])
static cell AMX_NATIVE_CALL get_cvar_float(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
CvarInfo* info = g_CvarManager.FindCvar(name);
return info ? amx_ftoc(info->var->value) : 0;
}
// get_cvar_num(const cvarname[])
static cell AMX_NATIVE_CALL get_cvar_num(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
CvarInfo* info = g_CvarManager.FindCvar(name);
return info ? (int)info->var->value : 0;
}
// get_cvar_string(const cvarname[], output[], iLen)
static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
CvarInfo* info = g_CvarManager.FindCvar(name);
const char *value = info ? info->var->string : "";
length = info ? strlen(value) : 0;
return set_amxstring_utf8(amx, params[2], value, length, params[3]);
}
// set_cvar_flags(const cvar[], flags)
static cell AMX_NATIVE_CALL set_cvar_flags(AMX *amx, cell *params)
{
int ilen;
const char* sCvar = get_amxstring(amx, params[1], 0, ilen);
if (!strcmp(sCvar, "amx_version") || !strcmp(sCvar, "amxmodx_version") || !strcmp(sCvar, "fun_version") || !strcmp(sCvar, "sv_cheats"))
return 0;
CvarInfo* info = g_CvarManager.FindCvar(sCvar);
if (info)
{
info->var->flags |= (int)(params[2]);
return 1;
}
return 0;
}
// set_cvar_float(const cvar[], Float:value)
static cell AMX_NATIVE_CALL set_cvar_float(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
CvarInfo* info = g_CvarManager.FindCvar(name);
if (info)
{
ke::SafeSprintf(CVarTempBuffer, sizeof(CVarTempBuffer), "%f", amx_ctof(params[2]));
CVAR_DIRECTSET(info->var, &CVarTempBuffer[0]);
}
return 1;
}
// set_cvar_num(const cvarname[], value)
static cell AMX_NATIVE_CALL set_cvar_num(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
int value = params[2];
CvarInfo* info = g_CvarManager.FindCvar(name);
if (info)
{
ke::SafeSprintf(CVarTempBuffer, sizeof(CVarTempBuffer), "%d", value);
CVAR_DIRECTSET(info->var, &CVarTempBuffer[0]);
}
return 1;
}
// set_cvar_string(const cvar[], const value[])
static cell AMX_NATIVE_CALL set_cvar_string(AMX *amx, cell *params)
{
int length;
const char* name = get_amxstring(amx, params[1], 0, length);
CvarInfo* info = g_CvarManager.FindCvar(name);
if (info)
{
CVAR_DIRECTSET(info->var, get_amxstring(amx, params[2], 1, length));
}
return 1;
}
// get_pcvar_flags(pcvar)
static cell AMX_NATIVE_CALL get_pcvar_flags(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return ptr->flags;
}
// Float:get_pcvar_float(pcvar)
static cell AMX_NATIVE_CALL get_pcvar_float(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return amx_ftoc(ptr->value);
}
// get_pcvar_num(pcvar)
static cell AMX_NATIVE_CALL get_pcvar_num(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return (int)ptr->value;
}
// bool:get_pcvar_bool(pcvar)
static cell AMX_NATIVE_CALL get_pcvar_bool(AMX *amx, cell *params)
{
return !!get_pcvar_num(amx, params);
}
// get_pcvar_string(pcvar, string[], maxlen)
static cell AMX_NATIVE_CALL get_pcvar_string(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
return set_amxstring_utf8(amx, params[2], ptr->string ? ptr->string : "", ptr->string ? strlen(ptr->string) : 0, params[3]);
}
// get_pcvar_bounds(pcvar, CvarBounds:type, &Float:value)
static cell AMX_NATIVE_CALL get_pcvar_bounds(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;
}
bool hasBound = false;
float bound;
switch (params[2])
{
case CvarBound_Lower:
hasBound = info->bound.hasMin;
bound = info->bound.minVal;
break;
case CvarBound_Upper:
hasBound = info->bound.hasMax;
bound = info->bound.maxVal;
break;
default:
LogError(amx, AMX_ERR_NATIVE, "Invalid CvarBounds value: %d", params[2]);
return 0;
}
*get_amxaddr(amx, params[3]) = amx_ftoc(bound);
return hasBound;
}
// 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 g_CvarManager.BindCvar(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 g_CvarManager.BindCvar(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 g_CvarManager.BindCvar(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)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
ptr->flags = static_cast<int>(params[2]);
return 1;
}
// set_pcvar_float(pcvar, Float:num)
static cell AMX_NATIVE_CALL set_pcvar_float(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
ke::SafeSprintf(CVarTempBuffer, sizeof(CVarTempBuffer), "%f", amx_ctof(params[2]));
CVAR_DIRECTSET(ptr, &CVarTempBuffer[0]);
return 1;
}
// set_pcvar_num(pcvar, num)
static cell AMX_NATIVE_CALL set_pcvar_num(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
ke::SafeSprintf(CVarTempBuffer, sizeof(CVarTempBuffer), "%d", params[2]);
CVAR_DIRECTSET(ptr, &CVarTempBuffer[0]);
return 1;
}
// set_pcvar_string(pcvar, const string[])
static cell AMX_NATIVE_CALL set_pcvar_string(AMX *amx, cell *params)
{
cvar_t *ptr = reinterpret_cast<cvar_t *>(params[1]);
if (!ptr)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer");
return 0;
}
int len;
CVAR_DIRECTSET(ptr, get_amxstring(amx, params[2], 0, len));
return 1;
}
// set_pcvar_bounds(pcvar, CvarBounds:type, bool:set, Float:value = 0.0)
static cell AMX_NATIVE_CALL set_pcvar_bounds(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;
}
bool set = params[3] != 0;
int pluginId = g_plugins.findPluginFast(amx)->getId();
float value = amx_ctof(params[4]);
switch (params[2])
{
case CvarBound_Lower:
{
if (set && info->bound.hasMax && value > info->bound.maxVal)
{
LogError(amx, AMX_ERR_NATIVE, "The minimum value can not be above the maximum value");
return 0;
}
g_CvarManager.SetCvarMin(info, set, value, pluginId);
break;
}
case CvarBound_Upper:
{
if (set && info->bound.hasMin && value < info->bound.minVal)
{
LogError(amx, AMX_ERR_NATIVE, "The maximum value can not be below the minimum value");
return 0;
}
g_CvarManager.SetCvarMax(info, set, value, pluginId);
break;
}
default:
{
LogError(amx, AMX_ERR_NATIVE, "Invalid CvarBounds value: %d", params[2]);
return 0;
}
}
return 1;
}
// remove_cvar_flags(const cvar[], flags=-1)
static cell AMX_NATIVE_CALL remove_cvar_flags(AMX *amx, cell *params)
{
int ilen;
char* sCvar = get_amxstring(amx, params[1], 0, ilen);
if (!strcmp(sCvar, "amx_version") || !strcmp(sCvar, "amxmodx_version") || !strcmp(sCvar, "fun_version") || !strcmp(sCvar, "sv_cheats"))
return 0;
CvarInfo* info = g_CvarManager.FindCvar(sCvar);
if (info)
{
info->var->flags &= ~((int)(params[2]));
return 1;
}
return 0;
}
// get_plugins_cvar(id, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=0, description[]="", desc_len=0)
static cell AMX_NATIVE_CALL get_plugins_cvar(AMX *amx, cell *params)
{
CvarInfo* info = g_CvarManager.FindCvar(params[1]);
if (info)
{
set_amxstring(amx, params[2], info->name.chars(), params[3]);
*get_amxaddr(amx, params[4]) = info->var->flags;
*get_amxaddr(amx, params[5]) = info->pluginId;
*get_amxaddr(amx, params[6]) = reinterpret_cast<cell>(info->var);
if (*params / sizeof(cell) >= 7)
{
set_amxstring(amx, params[7], info->description.chars(), params[8]);
}
return 1;
}
return 0;
}
// get_plugins_cvarsnum()
static cell AMX_NATIVE_CALL get_plugins_cvarsnum(AMX *amx, cell *params)
{
return g_CvarManager.GetRegCvarsCount();
}
#if defined AMD64
static bool g_warned_ccqv = false;
#endif
// query_client_cvar(id, const cvar[], const resultfunc[])
static cell AMX_NATIVE_CALL query_client_cvar(AMX *amx, cell *params)
{
int numParams = params[0] / sizeof(cell);
if (numParams != 3 && numParams != 5)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid number of parameters passed!");
return 0;
}
#if defined AMD64
if (!g_warned_ccqv)
{
LogError(amx, AMX_ERR_NATIVE, "[AMXX] Client CVAR Querying is not available on AMD64 (one time warn)");
g_warned_ccqv = true;
}
return 0;
#endif
if (!g_NewDLL_Available)
{
LogError(amx, AMX_ERR_NATIVE, "Client CVAR querying is not enabled - check MM version!");
return 0;
}
int id = params[1];
if (id < 1 || id > gpGlobals->maxClients)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid player id %d", id);
return 0;
}
CPlayer *pPlayer = GET_PLAYER_POINTER_I(id);
if (!pPlayer->initialized || pPlayer->IsBot())
{
LogError(amx, AMX_ERR_NATIVE, "Player %d is either not connected or a bot", id);
return 0;
}
int dummy;
const char *cvarname = get_amxstring(amx, params[2], 0, dummy);
const char *resultfuncname = get_amxstring(amx, params[3], 1, dummy);
// public clientcvarquery_result(id, const cvar[], const result[], [const param[]])
int iFunc;
if (numParams == 5 && params[4] != 0)
iFunc = registerSPForwardByName(amx, resultfuncname, FP_CELL, FP_STRING, FP_STRING, FP_ARRAY, FP_DONE);
else
iFunc = registerSPForwardByName(amx, resultfuncname, FP_CELL, FP_STRING, FP_STRING, FP_DONE);
if (iFunc == -1)
{
LogError(amx, AMX_ERR_NATIVE, "Function \"%s\" is not present", resultfuncname);
return 0;
}
ClientCvarQuery_Info *queryObject = new ClientCvarQuery_Info;
queryObject->resultFwd = iFunc;
queryObject->requestId = MAKE_REQUESTID(PLID);
if (numParams == 5 && params[4] != 0)
{
queryObject->paramLen = params[4] + 1;
queryObject->params = new cell[queryObject->paramLen];
if (!queryObject->params)
{
delete queryObject;
unregisterSPForward(iFunc);
LogError(amx, AMX_ERR_MEMORY, "Hmm. Out of memory?");
return 0;
}
memcpy(reinterpret_cast<void*>(queryObject->params), reinterpret_cast<const void *>(get_amxaddr(amx, params[5])), queryObject->paramLen * sizeof(cell));
queryObject->params[queryObject->paramLen - 1] = 0;
}
else {
queryObject->params = NULL;
queryObject->paramLen = 0;
}
pPlayer->queries.push_back(queryObject);
QUERY_CLIENT_CVAR_VALUE2(pPlayer->pEdict, cvarname, queryObject->requestId);
return 1;
}
AMX_NATIVE_INFO g_CvarNatives[] =
{
{"create_cvar", create_cvar},
{"register_cvar", register_cvar},
{"cvar_exists", cvar_exists},
{"get_cvar_pointer", get_cvar_pointer},
{"hook_cvar_change", hook_cvar_change},
{"enable_cvar_hook", enable_cvar_hook},
{"disable_cvar_hook", disable_cvar_hook},
{"get_cvar_flags", get_cvar_flags},
{"get_cvar_float", get_cvar_float},
{"get_cvar_num", get_cvar_num},
{"get_cvar_string", get_cvar_string},
{"set_cvar_flags", set_cvar_flags},
{"set_cvar_float", set_cvar_float},
{"set_cvar_num", set_cvar_num},
{"set_cvar_string", set_cvar_string},
{"get_pcvar_flags", get_pcvar_flags},
{"get_pcvar_float", get_pcvar_float},
{"get_pcvar_num", get_pcvar_num},
{"get_pcvar_bool", get_pcvar_bool},
{"get_pcvar_string", get_pcvar_string},
{"get_pcvar_bounds", get_pcvar_bounds},
{"set_pcvar_flags", set_pcvar_flags},
{"set_pcvar_float", set_pcvar_float},
{"set_pcvar_num", set_pcvar_num},
{"set_pcvar_bool", set_pcvar_num},
{"set_pcvar_string", set_pcvar_string},
{"set_pcvar_bounds", set_pcvar_bounds},
{"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},
{"query_client_cvar", query_client_cvar},
{NULL, NULL}
};