From 34d91789c40268c6409ed7c9213894bb3d4aca4c Mon Sep 17 00:00:00 2001 From: Arkshine Date: Fri, 16 Jan 2015 22:27:24 +0100 Subject: [PATCH 01/21] Cvars: Hook Cvar_DirectSet from engine library --- amxmodx/AMBuilder | 3 + amxmodx/cvars.cpp | 70 +++++++++++++++++++++++ amxmodx/cvars.h | 19 ++++++ amxmodx/meta_api.cpp | 3 + amxmodx/msvc12/amxmodx_mm.vcxproj | 15 +++-- amxmodx/msvc12/amxmodx_mm.vcxproj.filters | 30 ++++++++++ public/memtools/CDetour/detours.h | 4 +- 7 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 amxmodx/cvars.cpp create mode 100644 amxmodx/cvars.h diff --git a/amxmodx/AMBuilder b/amxmodx/AMBuilder index 1665ec89..00a8c2b5 100644 --- a/amxmodx/AMBuilder +++ b/amxmodx/AMBuilder @@ -96,6 +96,9 @@ binary.sources = [ 'stackstructs.cpp', 'CTextParsers.cpp', 'textparse.cpp', + 'cvars.cpp', + '../public/memtools/CDetour/detours.cpp', + '../public/memtools/CDetour/asm/asm.c', ] if builder.target_platform == 'windows': diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp new file mode 100644 index 00000000..a6eee812 --- /dev/null +++ b/amxmodx/cvars.cpp @@ -0,0 +1,70 @@ +// 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 "cvars.h" +#include "amxmodx.h" +#include + +CDetour *Cvar_DirectSetDetour; + +DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) +{ + printf("Cvar_DirectSet - %s -> %s\n", var->name, value); + + DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); +} + +void CreateCvarHook(void) +{ + // void PF_Cvar_DirectSet(struct cvar_s *var, const char *value) // = pfnCvar_DirectSet + // { + // Cvar_DirectSet(var, value); // <- We want to hook this. + // } + + byte *baseAddress = (byte *)g_engfuncs.pfnCvar_DirectSet; + uintptr_t *functionAddress = nullptr; + +#if defined(WIN32) + // 55 push ebp + // 8B EC mov ebp, esp + // 8B 45 0C mov eax, [ebp+arg_4] + // 8B 4D 08 mov ecx, [ebp+arg_0] + // 50 push eax + // 51 push ecx + // E8 XX XX XX XX call Cvar_DirectSet + const byte opcodeJump = 0xE8; +#else + // E9 XX XX XX XX jmp Cvar_DirectSet + const byte opcodeJump = 0xE9; +#endif + + const byte opcodeJumpSize = 5; + const byte opcodeJumpByteSize = 1; + + const int maxBytesLimit = 20; + + for (size_t i = 0; i < maxBytesLimit; ++i, ++baseAddress) + { + if (*baseAddress == opcodeJump) + { + functionAddress = (uintptr_t *)(&baseAddress[opcodeJumpSize] + *(uintptr_t *)&baseAddress[opcodeJumpByteSize]); + break; + } + } + + if (functionAddress) + { + CDetour *Cvar_DirectSetDetour = DETOUR_CREATE_STATIC_FIXED(Cvar_DirectSet, (void *)functionAddress); + + if (Cvar_DirectSetDetour) + { + Cvar_DirectSetDetour->EnableDetour(); + } + } +} \ No newline at end of file diff --git a/amxmodx/cvars.h b/amxmodx/cvars.h new file mode 100644 index 00000000..a495f643 --- /dev/null +++ b/amxmodx/cvars.h @@ -0,0 +1,19 @@ +// 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 + +#ifndef CVARS_H +#define CVARS_H + +class CDetour; + +void CreateCvarHook(void); + +extern CDetour *Cvar_DirectSetDetour; + +#endif // CVARS_H diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 2fe486fa..6cfecb41 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -31,6 +31,7 @@ #include "trie_natives.h" #include "CDataPack.h" #include "textparse.h" +#include "cvars.h" plugin_info_t Plugin_info = { @@ -1488,6 +1489,8 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, m GET_HOOK_TABLES(PLID, &g_pEngTable, NULL, NULL); FlagMan.SetFile("cmdaccess.ini"); + + CreateCvarHook(); return (TRUE); } diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj b/amxmodx/msvc12/amxmodx_mm.vcxproj index 80d6961c..ca876873 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj @@ -96,7 +96,7 @@ Disabled - ..\;..\..\public;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) + ..\;..\..\public;..\..\public\memtools;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;_CRT_SECURE_NO_DEPRECATE;HAVE_STDINT_H;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -149,7 +149,7 @@ true Speed true - ..\;..\..\public;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) + ..\;..\..\public;..\..\public\memtools;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;_CRT_SECURE_NO_DEPRECATE;HAVE_STDINT_H;%(PreprocessorDefinitions) false true @@ -200,7 +200,7 @@ Disabled - ..\;..\..\public;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) + ..\;..\..\public;..\..\public\memtools;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;HAVE_STDINT_H;%(PreprocessorDefinitions) EnableFastChecks MultiThreaded @@ -252,7 +252,7 @@ true Speed true - ..\;..\..\public;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) + ..\;..\..\public;..\..\public\memtools;..\..\public\sdk;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;HAVE_STDINT_H;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) false true @@ -292,6 +292,8 @@ + + @@ -317,6 +319,7 @@ + @@ -360,6 +363,9 @@ + + + @@ -383,6 +389,7 @@ + diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters index db6aeada..adabf960 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters @@ -25,6 +25,15 @@ {4022451d-eb5f-4f14-b8d8-2ce23fec6e59} + + {a1f7babf-acb1-4a06-92e9-e8a411e1f02a} + + + {8b35d490-2b01-4997-ba02-5e2cfd9c9017} + + + {64a22cd4-3715-45de-8af2-e54017733be6} + @@ -183,6 +192,15 @@ SDK + + Source Files + + + Memtools\CDetour + + + Memtools\CDetour\asm + @@ -329,6 +347,18 @@ SDK + + Header Files + + + Memtools\CDetour + + + Memtools\CDetour + + + Memtools\CDetour\asm + diff --git a/public/memtools/CDetour/detours.h b/public/memtools/CDetour/detours.h index 89c2b238..b0335191 100644 --- a/public/memtools/CDetour/detours.h +++ b/public/memtools/CDetour/detours.h @@ -32,10 +32,12 @@ #ifndef _INCLUDE_SOURCEMOD_DETOURS_H_ #define _INCLUDE_SOURCEMOD_DETOURS_H_ -#include "amxxmodule.h" //#include //#include #include "detourhelpers.h" +#include + +typedef int32_t cell; /** * CDetours class for SourceMod Extensions by pRED* From faeaf9259e54838d9c6a73d0c5c6e6c5398fbf49 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Fri, 16 Jan 2015 23:14:15 +0100 Subject: [PATCH 02/21] Cvars: Add OnCvarChanged global forward I really tried to use "cvar_changed" as name, but it bothered me so much to look that weird I could not keep it. --- amxmodx/amxmodx.h | 2 ++ amxmodx/cvars.cpp | 11 ++++++++++- amxmodx/meta_api.cpp | 2 ++ plugins/include/amxmodx.inc | 1 + plugins/include/cvars.inc | 23 +++++++++++++++++++++++ support/PackageScript | 1 + 6 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 plugins/include/cvars.inc diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 6b9973ca..654e1f27 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -329,6 +329,8 @@ extern int FF_PluginEnd; extern int FF_InconsistentFile; extern int FF_ClientAuthorized; extern int FF_ChangeLevel; +extern int FF_CvarChanged; + extern bool g_coloredmenus; typedef void (*AUTHORIZEFUNC)(int player, const char *authstring); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index a6eee812..6f7a8c43 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -15,7 +15,16 @@ CDetour *Cvar_DirectSetDetour; DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) { - printf("Cvar_DirectSet - %s -> %s\n", var->name, value); + if (var && value) + { + if (strcmp(var->string, value) != 0) + { + if (executeForwards(FF_CvarChanged, reinterpret_cast(var), var->string, value, var->name) > 0) + { + return; + } + } + } DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); } diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 6cfecb41..7e4ad667 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -143,6 +143,7 @@ int FF_PluginEnd = -1; int FF_InconsistentFile = -1; int FF_ClientAuthorized = -1; int FF_ChangeLevel = -1; +int FF_CvarChanged = -1; bool ColoredMenus(String & ModName) { @@ -503,6 +504,7 @@ int C_Spawn(edict_t *pent) FF_InconsistentFile = registerForward("inconsistent_file", ET_STOP, FP_CELL, FP_STRING, FP_STRINGEX, FP_DONE); FF_ClientAuthorized = registerForward("client_authorized", ET_IGNORE, FP_CELL, FP_DONE); FF_ChangeLevel = registerForward("server_changelevel", ET_STOP, FP_STRING, FP_DONE); + FF_CvarChanged = registerForward("OnCvarChanged", ET_STOP, FP_CELL, FP_STRING, FP_STRING, FP_STRING, FP_DONE); #if defined BINLOG_ENABLED if (!g_BinLog.Open()) diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index e778a381..78107435 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -29,6 +29,7 @@ #include #include #include +#include /** * Called just after server activation. diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc new file mode 100644 index 00000000..21be38f3 --- /dev/null +++ b/plugins/include/cvars.inc @@ -0,0 +1,23 @@ +// 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 + +#if defined _cvars_included + #endinput +#endif +#define _cvars_included + +/** + * Called when a console variable's value is changed. + * + * @param cvarHandle Handle to the cvar that was changed + * @param oldValue String containing the value of the cvar before it was changed + * @param newValue String containing the new value of the cvar + * @param cvarName String containing the name of the cvar + */ +forward OnCvarChanged(cvarHandle, const oldValue[], const newValue[], const cvarName[]); diff --git a/support/PackageScript b/support/PackageScript index 8fa1e99f..868013e6 100644 --- a/support/PackageScript +++ b/support/PackageScript @@ -245,6 +245,7 @@ scripting_files = [ 'include/csstats.inc', 'include/cstrike.inc', 'include/csx.inc', + 'include/cvars.inc', 'include/datapack.inc', 'include/dbi.inc', 'include/dodconst.inc', From 6619176a76a063a19cb65895dd46d782bac7f6c5 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sat, 17 Jan 2015 00:32:08 +0100 Subject: [PATCH 03/21] Cvars: Move CCVar class to cvars.h --- amxmodx/CMisc.h | 26 -------------------------- amxmodx/amxmodx.h | 1 + amxmodx/cvars.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/amxmodx/CMisc.h b/amxmodx/CMisc.h index 851ccc36..276b4714 100755 --- a/amxmodx/CMisc.h +++ b/amxmodx/CMisc.h @@ -13,32 +13,6 @@ #include "CList.h" #include "sh_list.h" -// ***************************************************** -// class CCVar -// ***************************************************** - -class CCVar -{ - cvar_t cvar; - String name; - String plugin; - -public: - CCVar(const char* pname, const char* pplugin, int pflags, float pvalue) : name(pname), plugin(pplugin) - { - cvar.name = (char*)name.c_str(); - cvar.flags = pflags; - cvar.string = ""; - cvar.value = pvalue; - } - - inline cvar_t* getCvar() { return &cvar; } - inline const char* getPluginName() { return plugin.c_str(); } - inline const char* getName() { return name.c_str(); } - inline bool operator == (const char* string) { return (strcmp(name.c_str(), string) == 0); } - int plugin_id; -}; - // ***************************************************** // class CPlayer // ***************************************************** diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 654e1f27..76efc850 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -47,6 +47,7 @@ #include "CLang.h" #include "fakemeta.h" #include "amxxlog.h" +#include "cvars.h" #define AMXXLOG_Log g_log.Log #define AMXXLOG_Error g_log.LogError diff --git a/amxmodx/cvars.h b/amxmodx/cvars.h index a495f643..f267eae6 100644 --- a/amxmodx/cvars.h +++ b/amxmodx/cvars.h @@ -10,8 +10,41 @@ #ifndef CVARS_H #define CVARS_H +#include "cvardef.h" +#include "CString.h" + class CDetour; +class CCVar +{ + cvar_t cvar; + String name; + String plugin; + +public: + CCVar(const char* pname, const char* pplugin, int pflags, float pvalue) : name(pname), plugin(pplugin) + { + cvar.name = (char*)name.c_str(); + cvar.flags = pflags; + cvar.string = ""; + cvar.value = pvalue; + } + + inline cvar_t* getCvar() { + return &cvar; + } + inline const char* getPluginName() { + return plugin.c_str(); + } + inline const char* getName() { + return name.c_str(); + } + inline bool operator == (const char* string) { + return (strcmp(name.c_str(), string) == 0); + } + int plugin_id; +}; + void CreateCvarHook(void); extern CDetour *Cvar_DirectSetDetour; From f973f5beb701d7436baf55d92d660f062a879057 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sat, 17 Jan 2015 00:40:25 +0100 Subject: [PATCH 04/21] Cvars: Convert CString to ke::AString in CCVar class --- amxmodx/cvars.h | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/amxmodx/cvars.h b/amxmodx/cvars.h index f267eae6..9d29bc9d 100644 --- a/amxmodx/cvars.h +++ b/amxmodx/cvars.h @@ -11,37 +11,45 @@ #define CVARS_H #include "cvardef.h" -#include "CString.h" +#include class CDetour; class CCVar { cvar_t cvar; - String name; - String plugin; + ke::AString name; + ke::AString plugin; public: CCVar(const char* pname, const char* pplugin, int pflags, float pvalue) : name(pname), plugin(pplugin) { - cvar.name = (char*)name.c_str(); - cvar.flags = pflags; + cvar.name = name.chars(); + cvar.flags = pflags; cvar.string = ""; - cvar.value = pvalue; + cvar.value = pvalue; } - inline cvar_t* getCvar() { + inline cvar_t* getCvar() + { return &cvar; } - inline const char* getPluginName() { - return plugin.c_str(); + + inline const char* getPluginName() + { + return plugin.chars(); } - inline const char* getName() { - return name.c_str(); + + inline const char* getName() + { + return name.chars(); } - inline bool operator == (const char* string) { - return (strcmp(name.c_str(), string) == 0); + + inline bool operator == (const char* string) + { + return name.compare(string) == 0; } + int plugin_id; }; From cdc7d550eb4c1c9f261ec7c8f1ca0388b6d52535 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Mon, 19 Jan 2015 19:17:41 +0100 Subject: [PATCH 05/21] Cvars: Rearrange a bit some code Changed class CCVar -> struct CvarInfo Added CvarManager class and moved some code there Code of registering a cvar is rewritten Cvars datas are stored in a hashtable for fast lookup For natives compatibility, an inline list is kept (though CList -> ke::InlineList) Replace some CVAR_GET/SEt_ by FindVar --- amxmodx/amxmodx.cpp | 161 ++++++++++++++++--------------- amxmodx/amxmodx.h | 1 - amxmodx/cvars.cpp | 192 +++++++++++++++++++++++++++++++++++-- amxmodx/cvars.h | 73 +++++++------- amxmodx/meta_api.cpp | 6 +- amxmodx/srvcmd.cpp | 27 +----- plugins/multilingual.sma | 4 +- public/sm_namehashset.h | 201 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 511 insertions(+), 154 deletions(-) create mode 100644 public/sm_namehashset.h diff --git a/amxmodx/amxmodx.cpp b/amxmodx/amxmodx.cpp index 10c8ab9c..752a30ff 100755 --- a/amxmodx/amxmodx.cpp +++ b/amxmodx/amxmodx.cpp @@ -1923,11 +1923,15 @@ static cell AMX_NATIVE_CALL get_pcvar_string(AMX *amx, cell *params) static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params) /* 3 param */ { - int ilen; - char* sptemp = get_amxstring(amx, params[1], 0, ilen); + int length; + const char* name = get_amxstring(amx, params[1], 0, length); - const char *value = CVAR_GET_STRING(sptemp); - return set_amxstring_utf8(amx, params[2], value, strlen(value), params[3] + 1); // + EOS + cvar_t* var = g_CvarManager.FindCvar(name); + + const char *value = var ? var->string : ""; + length = var ? strlen(value) : 0; + + return set_amxstring_utf8(amx, params[2], value, length, params[3] + 1); // + EOS } static cell AMX_NATIVE_CALL get_pcvar_float(AMX *amx, cell *params) @@ -1939,18 +1943,17 @@ static cell AMX_NATIVE_CALL get_pcvar_float(AMX *amx, cell *params) return 0; } - REAL val = (REAL)ptr->value; - - return amx_ftoc(val); + return amx_ftoc(ptr->value); } static cell AMX_NATIVE_CALL get_cvar_float(AMX *amx, cell *params) /* 1 param */ { - int ilen; + int length; + const char* name = get_amxstring(amx, params[1], 0, length); - REAL pFloat = CVAR_GET_FLOAT(get_amxstring(amx, params[1], 0, ilen)); - - return amx_ftoc(pFloat); + cvar_t* var = g_CvarManager.FindCvar(name); + + return var ? amx_ftoc(var->value) : 0; } static cell AMX_NATIVE_CALL set_pcvar_float(AMX *amx, cell *params) @@ -1962,16 +1965,25 @@ static cell AMX_NATIVE_CALL set_pcvar_float(AMX *amx, cell *params) return 0; } - UTIL_Format(CVarTempBuffer,sizeof(CVarTempBuffer)-1,"%f",amx_ctof(params[2])); - (*g_engfuncs.pfnCvar_DirectSet)(ptr, &CVarTempBuffer[0]); + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%f", amx_ctof(params[2])); + CVAR_DIRECTSET(ptr, &CVarTempBuffer[0]); + return 1; } static cell AMX_NATIVE_CALL set_cvar_float(AMX *amx, cell *params) /* 2 param */ { - int ilen; - CVAR_SET_FLOAT(get_amxstring(amx, params[1], 0, ilen), amx_ctof(params[2])); - + int length; + const char* name = get_amxstring(amx, params[1], 0, length); + + cvar_t* var = g_CvarManager.FindCvar(name); + + if (var) + { + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%f", amx_ctof(params[2])); + CVAR_DIRECTSET(var, &CVarTempBuffer[0]); + } + return 1; } @@ -1989,8 +2001,13 @@ static cell AMX_NATIVE_CALL get_pcvar_num(AMX *amx, cell *params) static cell AMX_NATIVE_CALL get_cvar_num(AMX *amx, cell *params) /* 1 param */ { - int ilen; - return (int)CVAR_GET_FLOAT(get_amxstring(amx, params[1], 0, ilen)); + int length; + const char* name = get_amxstring(amx, params[1], 0, length); + + cvar_t* var = g_CvarManager.FindCvar(name); + + return var ? (int)var->value : 0; + } static cell AMX_NATIVE_CALL set_pcvar_num(AMX *amx, cell *params) @@ -2003,27 +2020,40 @@ static cell AMX_NATIVE_CALL set_pcvar_num(AMX *amx, cell *params) } UTIL_Format(CVarTempBuffer,sizeof(CVarTempBuffer)-1,"%d",params[2]); - (*g_engfuncs.pfnCvar_DirectSet)(ptr, &CVarTempBuffer[0]); + CVAR_DIRECTSET(ptr, &CVarTempBuffer[0]); return 1; } static cell AMX_NATIVE_CALL set_cvar_num(AMX *amx, cell *params) /* 2 param */ { - int ilen; - CVAR_SET_FLOAT(get_amxstring(amx, params[1], 0, ilen), (float)params[2]); - + int length; + const char* name = get_amxstring(amx, params[1], 0, length); + int value = params[2]; + + cvar_t* var = g_CvarManager.FindCvar(name); + + if (var) + { + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%d", value); + CVAR_DIRECTSET(var, &CVarTempBuffer[0]); + } + return 1; } static cell AMX_NATIVE_CALL set_cvar_string(AMX *amx, cell *params) /* 2 param */ { - int ilen; - char* sptemp = get_amxstring(amx, params[1], 0, ilen); - char* szValue = get_amxstring(amx, params[2], 1, ilen); - - CVAR_SET_STRING(sptemp, szValue); + int length; + const char* name = get_amxstring(amx, params[1], 0, length); + cvar_t* var = g_CvarManager.FindCvar(name); + + if (var) + { + CVAR_DIRECTSET(var, get_amxstring(amx, params[2], 1, length)); + } + return 1; } @@ -2038,7 +2068,7 @@ static cell AMX_NATIVE_CALL set_pcvar_string(AMX *amx, cell *params) /* 2 param int len; - (*g_engfuncs.pfnCvar_DirectSet)(ptr, get_amxstring(amx,params[2],0,len)); + CVAR_DIRECTSET(ptr, get_amxstring(amx,params[2],0,len)); return 1; } @@ -2598,40 +2628,28 @@ static cell AMX_NATIVE_CALL task_exists(AMX *amx, cell *params) /* 1 param */ static cell AMX_NATIVE_CALL cvar_exists(AMX *amx, cell *params) /* 1 param */ { int ilen; - return (CVAR_GET_POINTER(get_amxstring(amx, params[1], 0, ilen)) ? 1 : 0); + return (g_CvarManager.FindCvar(get_amxstring(amx, params[1], 0, ilen)) ? 1 : 0); } static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params) /* 3 param */ { - int i; - char* temp = get_amxstring(amx, params[1], 0, i); + 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(temp, 0)) + if (CheckBadConList(name, 0)) { plugin->AddToFailCounter(1); } - if (!g_cvars.find(temp)) - { - CCVar* cvar = new CCVar(temp, plugin->getName(), params[3], amx_ctof(params[4])); + cvar_t* var = g_CvarManager.CreateCvar(name, value, fvalue, flags, plugin->getName(), plugin->getId()); - cvar->plugin_id = plugin->getId(); - - g_cvars.put(cvar); - - if (CVAR_GET_POINTER(temp) == 0) - { - static cvar_t cvar_reg_helper; - cvar_reg_helper = *(cvar->getCvar()); - CVAR_REGISTER(&cvar_reg_helper); - } - - CVAR_SET_STRING(temp, get_amxstring(amx, params[2], 1, i)); - return reinterpret_cast(CVAR_GET_POINTER(temp)); - } - - return reinterpret_cast(CVAR_GET_POINTER(temp)); + return reinterpret_cast(var); } static cell AMX_NATIVE_CALL get_user_ping(AMX *amx, cell *params) /* 3 param */ @@ -3022,29 +3040,16 @@ static cell AMX_NATIVE_CALL remove_quotes(AMX *amx, cell *params) /* 1 param */ //native get_plugins_cvar(id, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=0); static cell AMX_NATIVE_CALL get_plugins_cvar(AMX *amx, cell *params) { - int id = params[1]; - int iter_id = 0; + CvarInfo* info = g_CvarManager.FindCvar(params[1]); - for (CList::iterator iter=g_cvars.begin(); iter; ++iter) + if (info) { - if (id == iter_id) - { - CCVar *var = &(*iter); - set_amxstring(amx, params[2], var->getName(), params[3]); - cvar_t *ptr = CVAR_GET_POINTER(var->getName()); - if (!ptr) - { - return 0; - } - cell *addr = get_amxaddr(amx, params[4]); - *addr = ptr->flags; - addr = get_amxaddr(amx, params[5]); - *addr = var->plugin_id; - addr = get_amxaddr(amx, params[6]); - *addr = (cell)ptr; - return 1; - } - iter_id++; + 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(info->var); + + return 1; } return 0; @@ -3053,7 +3058,7 @@ static cell AMX_NATIVE_CALL get_plugins_cvar(AMX *amx, cell *params) //native get_plugins_cvarsnum(); static cell AMX_NATIVE_CALL get_plugins_cvarsnum(AMX *amx, cell *params) { - return g_cvars.size(); + return g_CvarManager.GetRegCvarsCount(); } static cell AMX_NATIVE_CALL get_user_aiming(AMX *amx, cell *params) /* 4 param */ @@ -3107,7 +3112,7 @@ static cell AMX_NATIVE_CALL remove_cvar_flags(AMX *amx, cell *params) if (!strcmp(sCvar, "amx_version") || !strcmp(sCvar, "amxmodx_version") || !strcmp(sCvar, "fun_version") || !strcmp(sCvar, "sv_cheats")) return 0; - cvar_t* pCvar = CVAR_GET_POINTER(sCvar); + cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); if (pCvar) { @@ -3135,7 +3140,7 @@ static cell AMX_NATIVE_CALL get_cvar_flags(AMX *amx, cell *params) int ilen; char* sCvar = get_amxstring(amx, params[1], 0, ilen); - cvar_t* pCvar = CVAR_GET_POINTER(sCvar); + cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); return pCvar ? pCvar->flags : 0; } @@ -3162,7 +3167,7 @@ static cell AMX_NATIVE_CALL set_cvar_flags(AMX *amx, cell *params) if (!strcmp(sCvar, "amx_version") || !strcmp(sCvar, "amxmodx_version") || !strcmp(sCvar, "fun_version") || !strcmp(sCvar, "sv_cheats")) return 0; - cvar_t* pCvar = CVAR_GET_POINTER(sCvar); + cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); if (pCvar) { @@ -4448,9 +4453,9 @@ static cell AMX_NATIVE_CALL DestroyForward(AMX *amx, cell *params) static cell AMX_NATIVE_CALL get_cvar_pointer(AMX *amx, cell *params) { int len; - char *temp = get_amxstring(amx, params[1], 0, len); + char *name = get_amxstring(amx, params[1], 0, len); - cvar_t *ptr = CVAR_GET_POINTER(temp); + cvar_t *ptr = g_CvarManager.FindCvar(name); return reinterpret_cast(ptr); } diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 76efc850..3a96630f 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -162,7 +162,6 @@ extern CTaskMngr g_tasksMngr; extern CPlayer g_players[33]; extern CPlayer* mPlayer; extern CmdMngr g_commands; -extern CList g_cvars; extern CList g_forcemodels; extern CList g_forcesounds; extern CList g_forcegeneric; diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 6f7a8c43..1d18dc04 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -11,25 +11,52 @@ #include "amxmodx.h" #include -CDetour *Cvar_DirectSetDetour; +CvarManager g_CvarManager; DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) { - if (var && value) + static bool calledFromCallback = false; + + if (!calledFromCallback && var && value) { if (strcmp(var->string, value) != 0) { + calledFromCallback = true; + if (executeForwards(FF_CvarChanged, reinterpret_cast(var), var->string, value, var->name) > 0) { - return; + //return; } + + calledFromCallback = false; } } DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); } -void CreateCvarHook(void) +CvarManager::CvarManager() : m_AmxmodxCvars(0), m_HookDetour(nullptr) +{ +} + +CvarManager::~CvarManager() +{ + CvarsList::iterator iter = m_Cvars.begin(); + + while (iter != m_Cvars.end()) + { + CvarInfo* info = (*iter); + + iter = m_Cvars.erase(iter); + + delete info; + } + + m_Cache.clear(); + m_HookDetour->Destroy(); +} + +void CvarManager::CreateCvarHook(void) { // void PF_Cvar_DirectSet(struct cvar_s *var, const char *value) // = pfnCvar_DirectSet // { @@ -69,11 +96,160 @@ void CreateCvarHook(void) if (functionAddress) { - CDetour *Cvar_DirectSetDetour = DETOUR_CREATE_STATIC_FIXED(Cvar_DirectSet, (void *)functionAddress); + m_HookDetour = DETOUR_CREATE_STATIC_FIXED(Cvar_DirectSet, (void *)functionAddress); - if (Cvar_DirectSetDetour) + if (m_HookDetour) { - Cvar_DirectSetDetour->EnableDetour(); + m_HookDetour->EnableDetour(); } } -} \ No newline at end of file +} + +cvar_t* CvarManager::CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId) +{ + cvar_t* var = nullptr; + CvarInfo* info = nullptr; + + // Is cvar already cached ? + if (!m_Cache.retrieve(name, &info)) + { + // Not cached - Is cvar already exist? + var = CVAR_GET_POINTER(name); + + // Whether it exists, we need to prepare a new entry. + info = new CvarInfo(); + + // Shared datas. + info->name = name; + info->plugin = plugin; + info->pluginId = plugnId; + + if (var) + { + // Cvar already exists. Just copy. + // "string" will be set after. "value" and "next" are automatically set. + info->var = var; + info->defaultval = var->string; + info->amxmodx = false; + } + else + { + // Registers a new cvar. + static cvar_t cvar_reg_helper; + + // "string" will be set after. "value" and "next" are automatically set. + cvar_reg_helper.name = info->name.chars(); + cvar_reg_helper.string = ""; + cvar_reg_helper.flags = flags; + + // Adds cvar to global list. + CVAR_REGISTER(&cvar_reg_helper); + + // Registering can fail if name is already a registered command. + var = CVAR_GET_POINTER(name); + + // If so, we can't go further. + if (!var) + { + delete info; + return nullptr; + } + + // If ok, we got a valid pointer, we can copy. + info->var = var; + info->defaultval = value; + info->amxmodx = true; + + // Keeps track count of cvars registered by AMXX. + ++m_AmxmodxCvars; + } + + // Add a new entry in the caches. + m_Cvars.append(info); + m_Cache.insert(name, info); + + // Make sure that whether an existing or new cvar is set to the given value. + CVAR_DIRECTSET(var, value); + } + + return info->var; +} + +cvar_t* CvarManager::FindCvar(const char* name) +{ + cvar_t* var = nullptr; + CvarInfo* info = nullptr; + + // Do we have already cvar in cache? + if (m_Cache.retrieve(name, &info)) + { + return info->var; + } + + // Cvar doesn't exist. + if (!(var = CVAR_GET_POINTER(name))) + { + return nullptr; + } + + // Create a new entry. + info = new CvarInfo(); + info->var = var; + info->name = name; + info->plugin = ""; + info->pluginId = -1; + info->amxmodx = false; + + // Add entry in the caches. + m_Cvars.append(info); + m_Cache.insert(name, info); + + return var; +} + +CvarInfo* CvarManager::FindCvar(size_t index) +{ + // Used by get_plugins_cvar native. + // For compatibility, only cvars registered by AMXX are concerned. + + size_t iter_id = 0; + + for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) + { + if (iter->amxmodx && iter_id++ == index) + { + return *(iter); + } + } + + return nullptr; +} + +size_t CvarManager::GetRegCvarsCount() +{ + return m_AmxmodxCvars; +} + +void CvarManager::OnConsoleCommand() +{ + print_srvconsole("Registered cvars:\n"); + print_srvconsole(" %-24.23s %-24.23s %-16.15s\n", "name", "value", "plugin"); + + size_t index = 0; + ke::AString pluginName; + + if (CMD_ARGC() > 2) // Searching for cvars registered to a plugin + { + pluginName = CMD_ARGV(2); + } + + for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) + { + CvarInfo* ci = (*iter); + + if (ci->amxmodx && (!pluginName.length() || strncmp(ci->name.chars(), pluginName.chars(), pluginName.length()) == 0)) + { + print_srvconsole(" [%3d] %-24.23s %-24.23s %-16.15s\n", ++index, ci->name.chars(), ci->var->string, ci->plugin.chars()); + } + } +} diff --git a/amxmodx/cvars.h b/amxmodx/cvars.h index 9d29bc9d..e4b71891 100644 --- a/amxmodx/cvars.h +++ b/amxmodx/cvars.h @@ -11,50 +11,53 @@ #define CVARS_H #include "cvardef.h" -#include +#include +#include class CDetour; -class CCVar +struct CvarInfo : public ke::InlineListNode { - cvar_t cvar; - ke::AString name; - ke::AString plugin; + cvar_t* var; + ke::AString name; + ke::AString defaultval; + ke::AString plugin; + int pluginId; + bool amxmodx; -public: - CCVar(const char* pname, const char* pplugin, int pflags, float pvalue) : name(pname), plugin(pplugin) + static inline bool matches(const char *name, const CvarInfo* info) { - cvar.name = name.chars(); - cvar.flags = pflags; - cvar.string = ""; - cvar.value = pvalue; + return strcmp(name, info->var->name) == 0; } - - inline cvar_t* getCvar() - { - return &cvar; - } - - inline const char* getPluginName() - { - return plugin.chars(); - } - - inline const char* getName() - { - return name.chars(); - } - - inline bool operator == (const char* string) - { - return name.compare(string) == 0; - } - - int plugin_id; }; -void CreateCvarHook(void); +typedef NameHashSet CvarsCache; +typedef ke::InlineList CvarsList; -extern CDetour *Cvar_DirectSetDetour; +class CvarManager +{ + public: + + CvarManager(); + ~CvarManager(); + + public: + + void CreateCvarHook(); + cvar_t* CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId); + cvar_t* FindCvar(const char* name); + CvarInfo* FindCvar(size_t index); + size_t GetRegCvarsCount(); + void OnConsoleCommand(); + + private: + + CvarsCache m_Cache; + CvarsList m_Cvars; + size_t m_AmxmodxCvars; + CDetour* m_HookDetour; +}; + +extern CvarManager g_CvarManager; #endif // CVARS_H diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 7e4ad667..9908738f 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -64,7 +64,6 @@ extern CVector DynamicAdmins; CLog g_log; CForwardMngr g_forwards; CList g_auth; -CList g_cvars; CList g_forcemodels; CList g_forcesounds; CList g_forcegeneric; @@ -122,7 +121,7 @@ cvar_t init_amxmodx_modules = {"amxmodx_modules", "", FCVAR_SPONLY}; cvar_t init_amxmodx_debug = {"amx_debug", "1", FCVAR_SPONLY}; cvar_t init_amxmodx_mldebug = {"amx_mldebug", "", FCVAR_SPONLY}; cvar_t init_amxmodx_language = {"amx_language", "en", FCVAR_SERVER}; -cvar_t init_amxmodx_cl_langs = {"amx_client_languages", "", FCVAR_SERVER}; +cvar_t init_amxmodx_cl_langs = {"amx_client_languages", "1", FCVAR_SERVER}; cvar_t* amxmodx_version = NULL; cvar_t* amxmodx_modules = NULL; cvar_t* amxmodx_language = NULL; @@ -1492,7 +1491,7 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, m FlagMan.SetFile("cmdaccess.ini"); - CreateCvarHook(); + g_CvarManager.CreateCvarHook(); return (TRUE); } @@ -1522,7 +1521,6 @@ C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) g_vault.clear(); g_xvars.clear(); g_plugins.clear(); - g_cvars.clear(); g_langMngr.Clear(); ClearMessages(); diff --git a/amxmodx/srvcmd.cpp b/amxmodx/srvcmd.cpp index 91e264ef..847b7f28 100755 --- a/amxmodx/srvcmd.cpp +++ b/amxmodx/srvcmd.cpp @@ -111,32 +111,7 @@ void amx_command() } else if (!strcmp(cmd, "cvars")) { - print_srvconsole("Registered cvars:\n"); - print_srvconsole(" %-24.23s %-24.23s %-16.15s\n", "name", "value", "plugin"); - - int ammount = 0; - - if (CMD_ARGC() > 2) // Searching for cvars registered to a plugin - { - const char* targetname = CMD_ARGV(2); - size_t len = strlen(targetname); - for (CList::iterator a = g_cvars.begin(); a; ++a) - { - if (strncmp((*a).getPluginName(), targetname, len) == 0) - { - print_srvconsole(" [%3d] %-24.23s %-24.23s %-16.15s\n", ++ammount, (*a).getName(), CVAR_GET_STRING((*a).getName()), (*a).getPluginName()); - } - } - } - else // No search - { - for (CList::iterator a = g_cvars.begin(); a; ++a) - { - print_srvconsole(" [%3d] %-24.23s %-24.23s %-16.15s\n", ++ammount, (*a).getName(), CVAR_GET_STRING((*a).getName()), (*a).getPluginName()); - } - } - - print_srvconsole("%d cvars\n", ammount); + g_CvarManager.OnConsoleCommand(); } else if (!strcmp(cmd, "cmds")) { diff --git a/plugins/multilingual.sma b/plugins/multilingual.sma index 28dbc721..6c41a2fe 100755 --- a/plugins/multilingual.sma +++ b/plugins/multilingual.sma @@ -28,9 +28,9 @@ public plugin_init() register_dictionary("multilingual.txt") register_dictionary("common.txt") register_dictionary("languages.txt") - - g_cvarClientLanguages = register_cvar("amx_client_languages", "1") + g_cvarDisplayClientMessage = register_cvar("amx_language_display_msg", "1") + g_cvarClientLanguages = get_cvar_pointer("amx_client_languages") g_cvarServerLanguage = get_cvar_pointer("amx_language"); register_clcmd("amx_langmenu", "cmdLangMenu", ADMIN_ALL) diff --git a/public/sm_namehashset.h b/public/sm_namehashset.h new file mode 100644 index 00000000..9524987b --- /dev/null +++ b/public/sm_namehashset.h @@ -0,0 +1,201 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 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$ + */ + +#ifndef _include_sourcemod_namehashset_h_ +#define _include_sourcemod_namehashset_h_ + +/** + * @file sm_namehashset.h + * + * @brief Stores a set of uniquely named objects. + */ + +#include +#include +#include +#include + +//namespace SourceMod +//{ + +// The HashPolicy type must have this method: +// static bool matches(const char *key, const T &value); +// +// Depending on what lookup types are used. +// +// If these members are available on T, then the HashPolicy type can be left +// default. It is okay to use |T *|, the functions will still be looked up +// on |T|. +template +class NameHashSet : public ke::SystemAllocatorPolicy +{ + typedef detail::CharsAndLength CharsAndLength; + + // Default policy type: the two types are different. Use them directly. + template + struct Policy + { + typedef KeyType Payload; + + static uint32_t hash(const CharsAndLength &key) + { + return key.hash(); + } + + static bool matches(const CharsAndLength &key, const KeyType &value) + { + return KeyPolicyType::matches(key.chars(), value); + } + }; + + // Specialization: the types are equal, and T is a pointer. Strip the + // pointer off so we can access T:: for match functions. + template + struct Policy + { + typedef KeyType *Payload; + + static uint32_t hash(const detail::CharsAndLength &key) + { + return key.hash(); + } + + static bool matches(const CharsAndLength &key, const KeyType *value) + { + return KeyType::matches(key.chars(), value); + } + }; + + typedef ke::HashTable, ke::SystemAllocatorPolicy> Internal; + +public: + NameHashSet() + { + if (!table_.init()) + this->reportOutOfMemory(); + } + + typedef typename Internal::Result Result; + typedef typename Internal::Insert Insert; + typedef typename Internal::iterator iterator; + + Result find(const char *aKey) + { + return table_.find(aKey); + } + + Insert findForAdd(const char *aKey) + { + return table_.findForAdd(aKey); + } + + void add(Insert &i, const T &value) + { + return table_.add(i, value); + } + + void add(Insert &i, T &&value) + { + return table_.add(i, value); + } + + bool retrieve(const char *aKey, T *value) + { + CharsAndLength key(aKey); + Result r = table_.find(aKey); + if (!r.found()) + return false; + *value = *r; + return true; + } + + bool insert(const char *aKey, const T &value) + { + CharsAndLength key(aKey); + Insert i = table_.findForAdd(key); + if (i.found()) + return false; + return table_.add(i, value); + } + + bool insert(const char *aKey, T &&value) + { + CharsAndLength key(aKey); + Insert i = table_.findForAdd(key); + if (i.found()) + return false; + return table_.add(i, value); + } + + bool contains(const char *aKey) + { + CharsAndLength key(aKey); + Result r = table_.find(aKey); + return r.found(); + } + + bool remove(const char *aKey) + { + CharsAndLength key(aKey); + Result r = table_.find(key); + if (!r.found()) + return false; + table_.remove(r); + return true; + } + + void remove(Result &r) + { + table_.remove(r); + } + + void clear() + { + table_.clear(); + } + + size_t mem_usage() const + { + return table_.estimateMemoryUse(); + } + + iterator iter() + { + return iterator(&table_); + } + +private: + Internal table_; +}; + +//} + +#endif // _include_sourcemod_namehashset_h_ From 0db59636416e707dd3f41e0e605375de9bb7ae31 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Tue, 20 Jan 2015 18:03:34 +0100 Subject: [PATCH 06/21] Cvars: Remove global forward --- amxmodx/amxmodx.h | 1 - amxmodx/cvars.cpp | 15 ++++----------- amxmodx/meta_api.cpp | 2 -- amxmodx/msvc12/amxmodx_mm.vcxproj | 1 + amxmodx/msvc12/amxmodx_mm.vcxproj.filters | 3 +++ plugins/include/cvars.inc | 3 ++- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 3a96630f..8592c9c3 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -329,7 +329,6 @@ extern int FF_PluginEnd; extern int FF_InconsistentFile; extern int FF_ClientAuthorized; extern int FF_ChangeLevel; -extern int FF_CvarChanged; extern bool g_coloredmenus; diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 1d18dc04..3d7ea8c2 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -15,20 +15,13 @@ CvarManager g_CvarManager; DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) { - static bool calledFromCallback = false; - - if (!calledFromCallback && var && value) + // Sanity checks against bogus pointers. + if (var && value) { + // Make sure old and new values are different to not trigger callbacks. if (strcmp(var->string, value) != 0) { - calledFromCallback = true; - - if (executeForwards(FF_CvarChanged, reinterpret_cast(var), var->string, value, var->name) > 0) - { - //return; - } - - calledFromCallback = false; + } } diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 9908738f..fc4bc89d 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -142,7 +142,6 @@ int FF_PluginEnd = -1; int FF_InconsistentFile = -1; int FF_ClientAuthorized = -1; int FF_ChangeLevel = -1; -int FF_CvarChanged = -1; bool ColoredMenus(String & ModName) { @@ -503,7 +502,6 @@ int C_Spawn(edict_t *pent) FF_InconsistentFile = registerForward("inconsistent_file", ET_STOP, FP_CELL, FP_STRING, FP_STRINGEX, FP_DONE); FF_ClientAuthorized = registerForward("client_authorized", ET_IGNORE, FP_CELL, FP_DONE); FF_ChangeLevel = registerForward("server_changelevel", ET_STOP, FP_STRING, FP_DONE); - FF_CvarChanged = registerForward("OnCvarChanged", ET_STOP, FP_CELL, FP_STRING, FP_STRING, FP_STRING, FP_DONE); #if defined BINLOG_ENABLED if (!g_BinLog.Open()) diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj b/amxmodx/msvc12/amxmodx_mm.vcxproj index ca876873..b871d45f 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj @@ -421,6 +421,7 @@ + diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters index adabf960..e0c0e65a 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters @@ -453,6 +453,9 @@ Pawn Includes + + Pawn Includes + diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 21be38f3..3e8dcc62 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -19,5 +19,6 @@ * @param oldValue String containing the value of the cvar before it was changed * @param newValue String containing the new value of the cvar * @param cvarName String containing the name of the cvar + * + * public OnCvarChanged(cvarHandle, const oldValue[], const newValue[], const cvarName[]); */ -forward OnCvarChanged(cvarHandle, const oldValue[], const newValue[], const cvarName[]); From 768fa2c3bc2458910d9f29614901032c5cf16810 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Wed, 21 Jan 2015 20:42:41 +0100 Subject: [PATCH 07/21] Cvars: Add hook_cvar_change, [enable|disable]_cvar_hook natives --- amxmodx/amxmodx.cpp | 56 +++++++++++++++ amxmodx/cvars.cpp | 146 ++++++++++++++++++++++++++++++++------ amxmodx/cvars.h | 45 +++++++++++- amxmodx/meta_api.cpp | 3 + plugins/include/cvars.inc | 42 +++++++++-- 5 files changed, 261 insertions(+), 31 deletions(-) diff --git a/amxmodx/amxmodx.cpp b/amxmodx/amxmodx.cpp index 752a30ff..5956214f 100755 --- a/amxmodx/amxmodx.cpp +++ b/amxmodx/amxmodx.cpp @@ -112,6 +112,36 @@ static cell AMX_NATIVE_CALL emit_sound(AMX *amx, cell *params) /* 7 param */ return 1; } +static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params) /* 7 param */ +{ + Forward* forward = reinterpret_cast(params[1]); + + if (!forward) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward); + return 0; + } + + forward->state = Forward::FSTATE_OK; + + return 1; +} + +static cell AMX_NATIVE_CALL disable_cvar_hook(AMX *amx, cell *params) /* 7 param */ +{ + Forward* forward = reinterpret_cast(params[1]); + + if (!forward) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward); + return 0; + } + + forward->state = Forward::FSTATE_STOP; + + return 1; +} + static cell AMX_NATIVE_CALL server_print(AMX *amx, cell *params) /* 1 param */ { int len; @@ -4831,6 +4861,29 @@ static cell AMX_NATIVE_CALL has_map_ent_class(AMX *amx, cell *params) return len && !FNullEnt(FIND_ENTITY_BY_STRING(NULL, "classname", name)); }; +// hook_cvar_change(cvarHandle, const callback[]) +static cell AMX_NATIVE_CALL hook_cvar_change(AMX *amx, cell *params) +{ + cvar_t* var = reinterpret_cast(params[1]); + + if (!var) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cvar handle: %p", var); + return 0; + } + + const char* callback; + Forward* forward = g_CvarManager.HookCvarChange(var, amx, params[2], &callback); + + if (!forward) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid callback function: %s", callback); + return 0; + } + + return reinterpret_cast(forward); +} + static cell AMX_NATIVE_CALL is_rukia_a_hag(AMX *amx, cell *params) { return 1; @@ -4867,6 +4920,8 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"console_print", console_print}, {"cvar_exists", cvar_exists}, {"emit_sound", emit_sound}, + {"enable_cvar_hook", enable_cvar_hook}, + {"disable_cvar_hook", disable_cvar_hook}, {"engclient_cmd", engclient_cmd}, {"engclient_print", engclient_print}, {"find_player", find_player}, @@ -4940,6 +4995,7 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"get_xvar_id", get_xvar_id}, {"get_xvar_num", get_xvar_num}, {"has_map_ent_class", has_map_ent_class}, + {"hook_cvar_change", hook_cvar_change}, {"int3", int3}, {"is_amd64_server", is_amd64_server}, {"is_dedicated_server", is_dedicated_server}, diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 3d7ea8c2..83b67c06 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -13,19 +13,48 @@ CvarManager g_CvarManager; -DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) +/** + * Returns true to call original function, otherwise false to block it. + */ +bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) { - // Sanity checks against bogus pointers. - if (var && value) + CvarInfo* info = nullptr; + + if (!var || !value // Sanity checks against bogus pointers. + || strcmp(var->string, value) == 0 // Make sure old and new values are different to not trigger callbacks. + || !g_CvarManager.CacheLookup(var->name, &info) // No data in cache, nothing to call. + || info->hooks.empty()) // No hooked cvars, nothing to call. { - // Make sure old and new values are different to not trigger callbacks. - if (strcmp(var->string, value) != 0) + return true; + } + + int lastResult = 0; + int result; + + for (size_t i = 0; i < info->hooks.length(); ++i) + { + CvarPlugin* p = info->hooks[i]; + + if (p->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives. { - + result = executeForwards(p->forward->id, reinterpret_cast(var), var->string, value); + + if (result >= lastResult) + { + lastResult = result; + } } } - DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + return !!!lastResult; +} + +DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) +{ + if (Cvar_DirectSet_Custom(var, value)) + { + DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + } } CvarManager::CvarManager() : m_AmxmodxCvars(0), m_HookDetour(nullptr) @@ -34,19 +63,7 @@ CvarManager::CvarManager() : m_AmxmodxCvars(0), m_HookDetour(nullptr) CvarManager::~CvarManager() { - CvarsList::iterator iter = m_Cvars.begin(); - - while (iter != m_Cvars.end()) - { - CvarInfo* info = (*iter); - - iter = m_Cvars.erase(iter); - - delete info; - } - - m_Cache.clear(); - m_HookDetour->Destroy(); + OnAmxxShutdown(); } void CvarManager::CreateCvarHook(void) @@ -103,8 +120,7 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, float fvalu cvar_t* var = nullptr; CvarInfo* info = nullptr; - // Is cvar already cached ? - if (!m_Cache.retrieve(name, &info)) + if (!CacheLookup(name, &info)) { // Not cached - Is cvar already exist? var = CVAR_GET_POINTER(name); @@ -174,7 +190,7 @@ cvar_t* CvarManager::FindCvar(const char* name) CvarInfo* info = nullptr; // Do we have already cvar in cache? - if (m_Cache.retrieve(name, &info)) + if (CacheLookup(name, &info)) { return info->var; } @@ -218,6 +234,55 @@ CvarInfo* CvarManager::FindCvar(size_t index) return nullptr; } +bool CvarManager::CacheLookup(const char* name, CvarInfo** info) +{ + return m_Cache.retrieve(name, info); +} + +Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback) +{ + CvarInfo* info = nullptr; + + // A cvar is guaranteed to be in cache if pointer is got from + // get_cvar_pointer and register_cvar natives. Though it might be + // provided by another way. If by any chance we run in such + // situation, we create a new entry right now. + + if (!CacheLookup(var->name, &info)) + { + // Create a new entry. + info = new CvarInfo(); + info->var = var; + info->name = var->name; + info->plugin = ""; + info->pluginId = -1; + info->amxmodx = false; + + // Add entry in the caches. + m_Cvars.append(info); + m_Cache.insert(info->name.chars(), info); + } + + int length; + *callback = get_amxstring(amx, param, 0, length); + + int forwardId = registerSPForwardByName(amx, *callback, FP_CELL, FP_STRING, FP_STRING, FP_DONE); + + // Invalid callback, it could be: not a public function, wrongly named, or simply missing. + if (forwardId == -1) + { + return nullptr; + } + + // Detour is disabled on map change. + m_HookDetour->EnableDetour(); + + Forward* forward = new Forward(forwardId, *callback); + info->hooks.append(new CvarPlugin(g_plugins.findPlugin(amx)->getId(), forward)); + + return forward; +} + size_t CvarManager::GetRegCvarsCount() { return m_AmxmodxCvars; @@ -246,3 +311,38 @@ void CvarManager::OnConsoleCommand() } } } + +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)->hooks.length(); ++i) + { + delete (*cvar)->hooks[i]; + } + + (*cvar)->hooks.clear(); + } + + // There is no point to enable detour if at next map change + // no plugins hook cvars. + m_HookDetour->DisableDetour(); +} + +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)->hooks.length(); ++i) + { + delete (*cvar)->hooks[i]; + } + + delete (*cvar); + } + + m_Cache.clear(); + m_HookDetour->Destroy(); +} diff --git a/amxmodx/cvars.h b/amxmodx/cvars.h index e4b71891..a5eb7eb7 100644 --- a/amxmodx/cvars.h +++ b/amxmodx/cvars.h @@ -10,12 +10,46 @@ #ifndef CVARS_H #define CVARS_H -#include "cvardef.h" +#include "amxmodx.h" +#include #include #include class CDetour; +struct Forward +{ + enum fwdstate + { + FSTATE_INVALID = 0, + FSTATE_OK, + FSTATE_STOP, + }; + + Forward(int id_, const char* handler) : id(id_), state(FSTATE_OK), callback(handler) {}; + Forward() : id(-1) , state(FSTATE_INVALID) {}; + + ~Forward() + { + unregisterSPForward(id); + } + + int id; + fwdstate state; + ke::AString callback; +}; + +struct CvarPlugin +{ + CvarPlugin(int id, Forward* fwd) : pluginId(id), forward(fwd) {}; + CvarPlugin(int id) : pluginId(id), forward(new Forward()) {}; + + int pluginId; + ke::AutoPtr forward; +}; + +typedef ke::Vector CvarsHook; + struct CvarInfo : public ke::InlineListNode { cvar_t* var; @@ -23,6 +57,7 @@ struct CvarInfo : public ke::InlineListNode ke::AString defaultval; ke::AString plugin; int pluginId; + CvarsHook hooks; bool amxmodx; static inline bool matches(const char *name, const CvarInfo* info) @@ -44,11 +79,19 @@ class CvarManager public: void CreateCvarHook(); + cvar_t* CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId); cvar_t* FindCvar(const char* name); CvarInfo* FindCvar(size_t index); + bool CacheLookup(const char* name, CvarInfo** info); + + Forward* HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback); + size_t GetRegCvarsCount(); + void OnConsoleCommand(); + void OnPluginUnloaded(); + void OnAmxxShutdown(); private: diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index fc4bc89d..82fae683 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -697,6 +697,9 @@ void C_ServerDeactivate_Post() g_vault.clear(); g_xvars.clear(); g_plugins.clear(); + + g_CvarManager.OnPluginUnloaded(); + ClearPluginLibraries(); modules_callPluginsUnloaded(); diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 3e8dcc62..c5803aed 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -13,12 +13,40 @@ #define _cvars_included /** - * Called when a console variable's value is changed. - * - * @param cvarHandle Handle to the cvar that was changed - * @param oldValue String containing the value of the cvar before it was changed - * @param newValue String containing the new value of the cvar - * @param cvarName String containing the name of the cvar + * Creates a hook for when a console variable's value is changed. * - * public OnCvarChanged(cvarHandle, const oldValue[], const newValue[], const cvarName[]); + * @note Callback will be called in the following manner: + * + * public cvar_change_callback(pcvar, const old_value[], const new_value[]) + * + * pcvar - Pointer to cvar that was changed + * old_value - String containing the value of the cvar before it was changed + * new_value - String containing the new value of the cvar + * + * @param pcvar Pointer to cvar + * @param callback Name of callback function + * @error Invalid pointer or invalid callback function */ +native cvarhook:hook_cvar_change(pcvar, const callback[]); + +/** + * Stops a cvar hook forward from triggering. + * + * @note Use the return value from hook_cvar_change as the parameter here. + * + * @param handle Forward to stop + * @error Invalid hook handle + */ +native disable_cvar_hook(cvarhook:handle); + +/** + * Starts a cvar hook forward back up. + * + * @note Use the return value from hook_cvar_change as the parameter here. + * + * @param handle Forward to back up + * @error Invalid hook handle + */ +native enable_cvar_hook(cvarhook:handle); + + From 15ad1d22474ab5e0a8d4d2d13a2ab8740e9cbaa8 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Wed, 21 Jan 2015 23:58:01 +0100 Subject: [PATCH 08/21] Cvars: Moving cvars stuffs in its own files cvars.cpp renamed to CvarManager.cpp all cvars natives moved to a new cvars.cpp file Pawn include is updated as wall. --- amxmodx/AMBuilder | 1 + amxmodx/CvarManager.cpp | 348 ++++++++++ amxmodx/{cvars.h => CvarManager.h} | 0 amxmodx/Makefile | 3 +- amxmodx/amxmodx.cpp | 493 -------------- amxmodx/amxmodx.h | 3 +- amxmodx/cvars.cpp | 742 ++++++++++++++-------- amxmodx/meta_api.cpp | 2 +- amxmodx/modules.cpp | 1 + amxmodx/msvc12/amxmodx_mm.vcxproj | 3 +- amxmodx/msvc12/amxmodx_mm.vcxproj.filters | 15 +- amxmodx/nongpl_matches.cpp | 16 + amxmodx/nongpl_matches.h | 1 + plugins/include/amxconst.inc | 14 - plugins/include/amxmodx.inc | 328 ---------- plugins/include/cvars.inc | 340 ++++++++++ 16 files changed, 1183 insertions(+), 1127 deletions(-) create mode 100644 amxmodx/CvarManager.cpp rename amxmodx/{cvars.h => CvarManager.h} (100%) diff --git a/amxmodx/AMBuilder b/amxmodx/AMBuilder index 00a8c2b5..a6b7c61a 100644 --- a/amxmodx/AMBuilder +++ b/amxmodx/AMBuilder @@ -96,6 +96,7 @@ binary.sources = [ 'stackstructs.cpp', 'CTextParsers.cpp', 'textparse.cpp', + 'CvarManager.cpp', 'cvars.cpp', '../public/memtools/CDetour/detours.cpp', '../public/memtools/CDetour/asm/asm.c', diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp new file mode 100644 index 00000000..92549105 --- /dev/null +++ b/amxmodx/CvarManager.cpp @@ -0,0 +1,348 @@ +// 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 + +CvarManager g_CvarManager; + +/** + * Returns true to call original function, otherwise false to block it. + */ +bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) +{ + CvarInfo* info = nullptr; + + if (!var || !value // Sanity checks against bogus pointers. + || strcmp(var->string, value) == 0 // Make sure old and new values are different to not trigger callbacks. + || !g_CvarManager.CacheLookup(var->name, &info) // No data in cache, nothing to call. + || info->hooks.empty()) // No hooked cvars, nothing to call. + { + return true; + } + + int lastResult = 0; + int result; + + for (size_t i = 0; i < info->hooks.length(); ++i) + { + CvarPlugin* p = info->hooks[i]; + + if (p->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives. + { + result = executeForwards(p->forward->id, reinterpret_cast(var), var->string, value); + + if (result >= lastResult) + { + lastResult = result; + } + } + } + + return !!!lastResult; +} + +DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) +{ + if (Cvar_DirectSet_Custom(var, value)) + { + DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + } +} + +CvarManager::CvarManager() : m_AmxmodxCvars(0), m_HookDetour(nullptr) +{ +} + +CvarManager::~CvarManager() +{ + OnAmxxShutdown(); +} + +void CvarManager::CreateCvarHook(void) +{ + // void PF_Cvar_DirectSet(struct cvar_s *var, const char *value) // = pfnCvar_DirectSet + // { + // Cvar_DirectSet(var, value); // <- We want to hook this. + // } + + byte *baseAddress = (byte *)g_engfuncs.pfnCvar_DirectSet; + uintptr_t *functionAddress = nullptr; + +#if defined(WIN32) + // 55 push ebp + // 8B EC mov ebp, esp + // 8B 45 0C mov eax, [ebp+arg_4] + // 8B 4D 08 mov ecx, [ebp+arg_0] + // 50 push eax + // 51 push ecx + // E8 XX XX XX XX call Cvar_DirectSet + const byte opcodeJump = 0xE8; +#else + // E9 XX XX XX XX jmp Cvar_DirectSet + const byte opcodeJump = 0xE9; +#endif + + const byte opcodeJumpSize = 5; + const byte opcodeJumpByteSize = 1; + + const int maxBytesLimit = 20; + + for (size_t i = 0; i < maxBytesLimit; ++i, ++baseAddress) + { + if (*baseAddress == opcodeJump) + { + functionAddress = (uintptr_t *)(&baseAddress[opcodeJumpSize] + *(uintptr_t *)&baseAddress[opcodeJumpByteSize]); + break; + } + } + + if (functionAddress) + { + m_HookDetour = DETOUR_CREATE_STATIC_FIXED(Cvar_DirectSet, (void *)functionAddress); + + if (m_HookDetour) + { + m_HookDetour->EnableDetour(); + } + } +} + +cvar_t* CvarManager::CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId) +{ + cvar_t* var = nullptr; + CvarInfo* info = nullptr; + + if (!CacheLookup(name, &info)) + { + // Not cached - Is cvar already exist? + var = CVAR_GET_POINTER(name); + + // Whether it exists, we need to prepare a new entry. + info = new CvarInfo(); + + // Shared datas. + info->name = name; + info->plugin = plugin; + info->pluginId = plugnId; + + if (var) + { + // Cvar already exists. Just copy. + // "string" will be set after. "value" and "next" are automatically set. + info->var = var; + info->defaultval = var->string; + info->amxmodx = false; + } + else + { + // Registers a new cvar. + static cvar_t cvar_reg_helper; + + // "string" will be set after. "value" and "next" are automatically set. + cvar_reg_helper.name = info->name.chars(); + cvar_reg_helper.string = ""; + cvar_reg_helper.flags = flags; + + // Adds cvar to global list. + CVAR_REGISTER(&cvar_reg_helper); + + // Registering can fail if name is already a registered command. + var = CVAR_GET_POINTER(name); + + // If so, we can't go further. + if (!var) + { + delete info; + return nullptr; + } + + // If ok, we got a valid pointer, we can copy. + info->var = var; + info->defaultval = value; + info->amxmodx = true; + + // Keeps track count of cvars registered by AMXX. + ++m_AmxmodxCvars; + } + + // Add a new entry in the caches. + m_Cvars.append(info); + m_Cache.insert(name, info); + + // Make sure that whether an existing or new cvar is set to the given value. + CVAR_DIRECTSET(var, value); + } + + return info->var; +} + +cvar_t* CvarManager::FindCvar(const char* name) +{ + cvar_t* var = nullptr; + CvarInfo* info = nullptr; + + // Do we have already cvar in cache? + if (CacheLookup(name, &info)) + { + return info->var; + } + + // Cvar doesn't exist. + if (!(var = CVAR_GET_POINTER(name))) + { + return nullptr; + } + + // Create a new entry. + info = new CvarInfo(); + info->var = var; + info->name = name; + info->plugin = ""; + info->pluginId = -1; + info->amxmodx = false; + + // Add entry in the caches. + m_Cvars.append(info); + m_Cache.insert(name, info); + + return var; +} + +CvarInfo* CvarManager::FindCvar(size_t index) +{ + // Used by get_plugins_cvar native. + // For compatibility, only cvars registered by AMXX are concerned. + + size_t iter_id = 0; + + for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) + { + if (iter->amxmodx && iter_id++ == index) + { + return *(iter); + } + } + + return nullptr; +} + +bool CvarManager::CacheLookup(const char* name, CvarInfo** info) +{ + return m_Cache.retrieve(name, info); +} + +Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback) +{ + CvarInfo* info = nullptr; + + // A cvar is guaranteed to be in cache if pointer is got from + // get_cvar_pointer and register_cvar natives. Though it might be + // provided by another way. If by any chance we run in such + // situation, we create a new entry right now. + + if (!CacheLookup(var->name, &info)) + { + // Create a new entry. + info = new CvarInfo(); + info->var = var; + info->name = var->name; + info->plugin = ""; + info->pluginId = -1; + info->amxmodx = false; + + // Add entry in the caches. + m_Cvars.append(info); + m_Cache.insert(info->name.chars(), info); + } + + int length; + *callback = get_amxstring(amx, param, 0, length); + + int forwardId = registerSPForwardByName(amx, *callback, FP_CELL, FP_STRING, FP_STRING, FP_DONE); + + // Invalid callback, it could be: not a public function, wrongly named, or simply missing. + if (forwardId == -1) + { + return nullptr; + } + + // Detour is disabled on map change. + m_HookDetour->EnableDetour(); + + Forward* forward = new Forward(forwardId, *callback); + info->hooks.append(new CvarPlugin(g_plugins.findPlugin(amx)->getId(), forward)); + + return forward; +} + +size_t CvarManager::GetRegCvarsCount() +{ + return m_AmxmodxCvars; +} + +void CvarManager::OnConsoleCommand() +{ + print_srvconsole("Registered cvars:\n"); + print_srvconsole(" %-24.23s %-24.23s %-16.15s\n", "name", "value", "plugin"); + + size_t index = 0; + ke::AString pluginName; + + if (CMD_ARGC() > 2) // Searching for cvars registered to a plugin + { + pluginName = CMD_ARGV(2); + } + + for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) + { + CvarInfo* ci = (*iter); + + if (ci->amxmodx && (!pluginName.length() || strncmp(ci->name.chars(), pluginName.chars(), pluginName.length()) == 0)) + { + print_srvconsole(" [%3d] %-24.23s %-24.23s %-16.15s\n", ++index, ci->name.chars(), ci->var->string, ci->plugin.chars()); + } + } +} + +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)->hooks.length(); ++i) + { + delete (*cvar)->hooks[i]; + } + + (*cvar)->hooks.clear(); + } + + // There is no point to enable detour if at next map change + // no plugins hook cvars. + m_HookDetour->DisableDetour(); +} + +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)->hooks.length(); ++i) + { + delete (*cvar)->hooks[i]; + } + + delete (*cvar); + } + + m_Cache.clear(); + m_HookDetour->Destroy(); +} diff --git a/amxmodx/cvars.h b/amxmodx/CvarManager.h similarity index 100% rename from amxmodx/cvars.h rename to amxmodx/CvarManager.h diff --git a/amxmodx/Makefile b/amxmodx/Makefile index 10aae948..e83bcbfa 100755 --- a/amxmodx/Makefile +++ b/amxmodx/Makefile @@ -22,7 +22,8 @@ OBJECTS = meta_api.cpp CFile.cpp CVault.cpp vault.cpp float.cpp file.cpp modules optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp sorting.cpp \ nongpl_matches.cpp CFlagManager.cpp datastructs.cpp \ trie_natives.cpp CDataPack.cpp datapacks.cpp stackstructs.cpp \ - CTextParsers.cpp textparse.cpp \ + CTextParsers.cpp textparse.cpp CvarManager.cpp cvars.cpp \ + ../public/memtools/CDetour/detours.cpp ../public/memtools/CDetour/asm/asm.c ############################################## ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### diff --git a/amxmodx/amxmodx.cpp b/amxmodx/amxmodx.cpp index 5956214f..495a2361 100755 --- a/amxmodx/amxmodx.cpp +++ b/amxmodx/amxmodx.cpp @@ -22,25 +22,6 @@ extern CFlagManager FlagMan; CVector DynamicAdmins; -char CVarTempBuffer[64]; - -const char *invis_cvar_list[5] = {"amxmodx_version", "amxmodx_modules", "amx_debug", "amx_mldebug", "amx_client_languages"}; - -bool CheckBadConList(const char *cvar, int type) -{ - int i = 0; - while (NONGPL_CVAR_LIST[i].cvar != NULL) - { - if (NONGPL_CVAR_LIST[i].type == type - && strcmp(NONGPL_CVAR_LIST[i].cvar, cvar) == 0) - { - return true; - } - i++; - } - - return false; -} static cell AMX_NATIVE_CALL get_xvar_id(AMX *amx, cell *params) { @@ -112,36 +93,6 @@ static cell AMX_NATIVE_CALL emit_sound(AMX *amx, cell *params) /* 7 param */ return 1; } -static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params) /* 7 param */ -{ - Forward* forward = reinterpret_cast(params[1]); - - if (!forward) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward); - return 0; - } - - forward->state = Forward::FSTATE_OK; - - return 1; -} - -static cell AMX_NATIVE_CALL disable_cvar_hook(AMX *amx, cell *params) /* 7 param */ -{ - Forward* forward = reinterpret_cast(params[1]); - - if (!forward) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward); - return 0; - } - - forward->state = Forward::FSTATE_STOP; - - return 1; -} - static cell AMX_NATIVE_CALL server_print(AMX *amx, cell *params) /* 1 param */ { int len; @@ -1939,170 +1890,6 @@ static cell AMX_NATIVE_CALL client_cmd(AMX *amx, cell *params) /* 2 param */ return len; } -static cell AMX_NATIVE_CALL get_pcvar_string(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(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] + 1); // EOS -} - -static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params) /* 3 param */ -{ - int length; - const char* name = get_amxstring(amx, params[1], 0, length); - - cvar_t* var = g_CvarManager.FindCvar(name); - - const char *value = var ? var->string : ""; - length = var ? strlen(value) : 0; - - return set_amxstring_utf8(amx, params[2], value, length, params[3] + 1); // + EOS -} - -static cell AMX_NATIVE_CALL get_pcvar_float(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(params[1]); - if (!ptr) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); - return 0; - } - - return amx_ftoc(ptr->value); -} - -static cell AMX_NATIVE_CALL get_cvar_float(AMX *amx, cell *params) /* 1 param */ -{ - int length; - const char* name = get_amxstring(amx, params[1], 0, length); - - cvar_t* var = g_CvarManager.FindCvar(name); - - return var ? amx_ftoc(var->value) : 0; -} - -static cell AMX_NATIVE_CALL set_pcvar_float(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(params[1]); - if (!ptr) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); - return 0; - } - - UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%f", amx_ctof(params[2])); - CVAR_DIRECTSET(ptr, &CVarTempBuffer[0]); - - return 1; -} - -static cell AMX_NATIVE_CALL set_cvar_float(AMX *amx, cell *params) /* 2 param */ -{ - int length; - const char* name = get_amxstring(amx, params[1], 0, length); - - cvar_t* var = g_CvarManager.FindCvar(name); - - if (var) - { - UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%f", amx_ctof(params[2])); - CVAR_DIRECTSET(var, &CVarTempBuffer[0]); - } - - return 1; -} - -static cell AMX_NATIVE_CALL get_pcvar_num(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(params[1]); - if (!ptr) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); - return 0; - } - - return (int)ptr->value; -} - -static cell AMX_NATIVE_CALL get_cvar_num(AMX *amx, cell *params) /* 1 param */ -{ - int length; - const char* name = get_amxstring(amx, params[1], 0, length); - - cvar_t* var = g_CvarManager.FindCvar(name); - - return var ? (int)var->value : 0; - -} - -static cell AMX_NATIVE_CALL set_pcvar_num(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(params[1]); - if (!ptr) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); - return 0; - } - - UTIL_Format(CVarTempBuffer,sizeof(CVarTempBuffer)-1,"%d",params[2]); - CVAR_DIRECTSET(ptr, &CVarTempBuffer[0]); - - return 1; -} - -static cell AMX_NATIVE_CALL set_cvar_num(AMX *amx, cell *params) /* 2 param */ -{ - int length; - const char* name = get_amxstring(amx, params[1], 0, length); - int value = params[2]; - - cvar_t* var = g_CvarManager.FindCvar(name); - - if (var) - { - UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%d", value); - CVAR_DIRECTSET(var, &CVarTempBuffer[0]); - } - - return 1; -} - -static cell AMX_NATIVE_CALL set_cvar_string(AMX *amx, cell *params) /* 2 param */ -{ - int length; - const char* name = get_amxstring(amx, params[1], 0, length); - - cvar_t* var = g_CvarManager.FindCvar(name); - - if (var) - { - CVAR_DIRECTSET(var, get_amxstring(amx, params[2], 1, length)); - } - - return 1; -} - -static cell AMX_NATIVE_CALL set_pcvar_string(AMX *amx, cell *params) /* 2 param */ -{ - cvar_t *ptr = reinterpret_cast(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; -} - static cell AMX_NATIVE_CALL log_message(AMX *amx, cell *params) /* 1 param */ { int len; @@ -2655,33 +2442,6 @@ static cell AMX_NATIVE_CALL task_exists(AMX *amx, cell *params) /* 1 param */ return g_tasksMngr.taskExists(params[1], params[2] ? 0 : amx); } -static cell AMX_NATIVE_CALL cvar_exists(AMX *amx, cell *params) /* 1 param */ -{ - int ilen; - return (g_CvarManager.FindCvar(get_amxstring(amx, params[1], 0, ilen)) ? 1 : 0); -} - -static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params) /* 3 param */ -{ - 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); - } - - cvar_t* var = g_CvarManager.CreateCvar(name, value, fvalue, flags, plugin->getName(), plugin->getId()); - - return reinterpret_cast(var); -} - static cell AMX_NATIVE_CALL get_user_ping(AMX *amx, cell *params) /* 3 param */ { int index = params[1]; @@ -3067,30 +2827,6 @@ static cell AMX_NATIVE_CALL remove_quotes(AMX *amx, cell *params) /* 1 param */ return 0; } -//native get_plugins_cvar(id, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=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(info->var); - - return 1; - } - - return 0; -} - -//native get_plugins_cvarsnum(); -static cell AMX_NATIVE_CALL get_plugins_cvarsnum(AMX *amx, cell *params) -{ - return g_CvarManager.GetRegCvarsCount(); -} - static cell AMX_NATIVE_CALL get_user_aiming(AMX *amx, cell *params) /* 4 param */ { int index = params[1]; @@ -3134,80 +2870,6 @@ static cell AMX_NATIVE_CALL get_user_aiming(AMX *amx, cell *params) /* 4 param * return amx_ftoc(pfloat); } -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; - - cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); - - if (pCvar) - { - pCvar->flags &= ~((int)(params[2])); - return 1; - } - - return 0; -} - -static cell AMX_NATIVE_CALL get_pcvar_flags(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(params[1]); - if (!ptr) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); - return 0; - } - - return ptr->flags; -} - -static cell AMX_NATIVE_CALL get_cvar_flags(AMX *amx, cell *params) -{ - int ilen; - char* sCvar = get_amxstring(amx, params[1], 0, ilen); - - cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); - - return pCvar ? pCvar->flags : 0; -} - -static cell AMX_NATIVE_CALL set_pcvar_flags(AMX *amx, cell *params) -{ - cvar_t *ptr = reinterpret_cast(params[1]); - if (!ptr) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); - return 0; - } - - ptr->flags = static_cast(params[2]); - - return 1; -} - -static cell AMX_NATIVE_CALL set_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; - - cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); - - if (pCvar) - { - pCvar->flags |= (int)(params[2]); - return 1; - } - - return 0; -} - static cell AMX_NATIVE_CALL force_unmodified(AMX *amx, cell *params) { int a; @@ -4156,102 +3818,6 @@ static cell AMX_NATIVE_CALL int3(AMX *amx, cell *params) /*********************************************************************/ -#if defined AMD64 -static bool g_warned_ccqv = false; -#endif -// native 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(queryObject->params), reinterpret_cast(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; -} - static cell AMX_NATIVE_CALL amx_abort(AMX *amx, cell *params) { int err = params[1]; @@ -4480,16 +4046,6 @@ static cell AMX_NATIVE_CALL DestroyForward(AMX *amx, cell *params) return 1; } -static cell AMX_NATIVE_CALL get_cvar_pointer(AMX *amx, cell *params) -{ - int len; - char *name = get_amxstring(amx, params[1], 0, len); - - cvar_t *ptr = g_CvarManager.FindCvar(name); - - return reinterpret_cast(ptr); -} - CVector g_hudsync; static cell AMX_NATIVE_CALL CreateHudSyncObj(AMX *amx, cell *params) @@ -4861,29 +4417,6 @@ static cell AMX_NATIVE_CALL has_map_ent_class(AMX *amx, cell *params) return len && !FNullEnt(FIND_ENTITY_BY_STRING(NULL, "classname", name)); }; -// hook_cvar_change(cvarHandle, const callback[]) -static cell AMX_NATIVE_CALL hook_cvar_change(AMX *amx, cell *params) -{ - cvar_t* var = reinterpret_cast(params[1]); - - if (!var) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cvar handle: %p", var); - return 0; - } - - const char* callback; - Forward* forward = g_CvarManager.HookCvarChange(var, amx, params[2], &callback); - - if (!forward) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid callback function: %s", callback); - return 0; - } - - return reinterpret_cast(forward); -} - static cell AMX_NATIVE_CALL is_rukia_a_hag(AMX *amx, cell *params) { return 1; @@ -4918,10 +4451,7 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"client_print_color", client_print_color}, {"console_cmd", console_cmd}, {"console_print", console_print}, - {"cvar_exists", cvar_exists}, {"emit_sound", emit_sound}, - {"enable_cvar_hook", enable_cvar_hook}, - {"disable_cvar_hook", disable_cvar_hook}, {"engclient_cmd", engclient_cmd}, {"engclient_print", engclient_print}, {"find_player", find_player}, @@ -4933,11 +4463,6 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"get_concmd", get_concmd}, {"get_concmdsnum", get_concmdsnum}, {"get_concmd_plid", get_concmd_plid}, - {"get_cvar_flags", get_cvar_flags}, - {"get_cvar_float", get_cvar_float}, - {"get_cvar_num", get_cvar_num}, - {"get_cvar_pointer", get_cvar_pointer}, - {"get_cvar_string", get_cvar_string}, {"get_flags", get_flags}, {"get_func_id", get_func_id}, {"get_gametime", get_gametime}, @@ -4949,16 +4474,10 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"get_modname", get_modname}, {"get_module", get_module}, {"get_modulesnum", get_modulesnum}, - {"get_pcvar_flags", get_pcvar_flags}, - {"get_pcvar_float", get_pcvar_float}, - {"get_pcvar_num", get_pcvar_num}, - {"get_pcvar_string", get_pcvar_string}, {"get_players", get_players}, {"get_playersnum", get_playersnum}, {"get_plugin", get_plugin}, {"get_pluginsnum", get_pluginsnum}, - {"get_plugins_cvar", get_plugins_cvar}, - {"get_plugins_cvarsnum", get_plugins_cvarsnum}, {"get_srvcmd", get_srvcmd}, {"get_srvcmdsnum", get_srvcmdsnum}, {"get_systime", get_systime}, @@ -4995,7 +4514,6 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"get_xvar_id", get_xvar_id}, {"get_xvar_num", get_xvar_num}, {"has_map_ent_class", has_map_ent_class}, - {"hook_cvar_change", hook_cvar_change}, {"int3", int3}, {"is_amd64_server", is_amd64_server}, {"is_dedicated_server", is_dedicated_server}, @@ -5028,7 +4546,6 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"precache_model", precache_model}, {"precache_sound", precache_sound}, {"precache_generic", precache_generic}, - {"query_client_cvar", query_client_cvar}, {"random_float", random_float}, {"random_num", random_num}, {"read_argc", read_argc}, @@ -5043,7 +4560,6 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"read_logdata", read_logdata}, {"register_clcmd", register_clcmd}, {"register_concmd", register_concmd}, - {"register_cvar", register_cvar}, {"register_dictionary", register_dictionary}, {"register_event", register_event}, {"register_logevent", register_logevent}, @@ -5052,25 +4568,16 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"register_plugin", register_plugin}, {"register_srvcmd", register_srvcmd}, {"require_module", require_module}, - {"remove_cvar_flags", remove_cvar_flags}, {"remove_quotes", remove_quotes}, {"remove_task", remove_task}, {"remove_user_flags", remove_user_flags}, {"server_cmd", server_cmd}, {"server_exec", server_exec}, {"server_print", server_print}, - {"set_cvar_flags", set_cvar_flags}, - {"set_cvar_float", set_cvar_float}, - {"set_cvar_num", set_cvar_num}, - {"set_cvar_string", set_cvar_string}, {"set_fail_state", set_fail_state}, {"set_dhudmessage", set_dhudmessage}, {"set_hudmessage", set_hudmessage}, {"set_localinfo", set_localinfo}, - {"set_pcvar_flags", set_pcvar_flags}, - {"set_pcvar_float", set_pcvar_float}, - {"set_pcvar_string", set_pcvar_string}, - {"set_pcvar_num", set_pcvar_num}, {"set_task", set_task}, {"set_user_flags", set_user_flags}, {"set_user_info", set_user_info}, diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 8592c9c3..dcc0ed24 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -47,7 +47,7 @@ #include "CLang.h" #include "fakemeta.h" #include "amxxlog.h" -#include "cvars.h" +#include "CvarManager.h" #define AMXXLOG_Log g_log.Log #define AMXXLOG_Error g_log.LogError @@ -66,6 +66,7 @@ extern AMX_NATIVE_INFO g_SortNatives[]; extern AMX_NATIVE_INFO g_DataStructNatives[]; extern AMX_NATIVE_INFO g_StackNatives[]; extern AMX_NATIVE_INFO g_TextParserNatives[]; +extern AMX_NATIVE_INFO g_CvarNatives[]; #if defined(_WIN32) #define DLLOAD(path) (DLHANDLE)LoadLibrary(path) diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 83b67c06..19dba289 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -7,342 +7,520 @@ // Additional exceptions apply. For full license details, see LICENSE.txt or visit: // https://alliedmods.net/amxmodx-license -#include "cvars.h" +#include "CvarManager.h" #include "amxmodx.h" -#include +#include "nongpl_matches.h" -CvarManager g_CvarManager; +char CVarTempBuffer[64]; +const char *invis_cvar_list[5] = { "amxmodx_version", "amxmodx_modules", "amx_debug", "amx_mldebug", "amx_client_languages" }; -/** - * Returns true to call original function, otherwise false to block it. - */ -bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) +// register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0) +static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params) { - CvarInfo* info = nullptr; + int length; + const char* name = get_amxstring(amx, params[1], 0, length); + const char* value = get_amxstring(amx, params[2], 1, length); - if (!var || !value // Sanity checks against bogus pointers. - || strcmp(var->string, value) == 0 // Make sure old and new values are different to not trigger callbacks. - || !g_CvarManager.CacheLookup(var->name, &info) // No data in cache, nothing to call. - || info->hooks.empty()) // No hooked cvars, nothing to call. + int flags = params[3]; + float fvalue = amx_ctof(params[4]); + + CPluginMngr::CPlugin *plugin = g_plugins.findPluginFast(amx); + + if (CheckBadConList(name, 0)) { - return true; + plugin->AddToFailCounter(1); } - int lastResult = 0; - int result; + cvar_t* var = g_CvarManager.CreateCvar(name, value, fvalue, flags, plugin->getName(), plugin->getId()); - for (size_t i = 0; i < info->hooks.length(); ++i) + return reinterpret_cast(var); +} + +// 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; + char *name = get_amxstring(amx, params[1], 0, len); + + cvar_t *ptr = g_CvarManager.FindCvar(name); + + return reinterpret_cast(ptr); +} + +// hook_cvar_change(cvarHandle, const callback[]) +static cell AMX_NATIVE_CALL hook_cvar_change(AMX *amx, cell *params) +{ + cvar_t* var = reinterpret_cast(params[1]); + + if (!var) { - CvarPlugin* p = info->hooks[i]; - - if (p->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives. - { - result = executeForwards(p->forward->id, reinterpret_cast(var), var->string, value); - - if (result >= lastResult) - { - lastResult = result; - } - } + LogError(amx, AMX_ERR_NATIVE, "Invalid cvar handle: %p", var); + return 0; } - return !!!lastResult; -} + const char* callback; + Forward* forward = g_CvarManager.HookCvarChange(var, amx, params[2], &callback); -DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) -{ - if (Cvar_DirectSet_Custom(var, value)) + if (!forward) { - DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + LogError(amx, AMX_ERR_NATIVE, "Function \"%s\" is not present", callback); + return 0; } + + return reinterpret_cast(forward); } -CvarManager::CvarManager() : m_AmxmodxCvars(0), m_HookDetour(nullptr) +// enable_cvar_hook(cvarhook:handle); +static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params) { + Forward* forward = reinterpret_cast(params[1]); + + if (!forward) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward); + return 0; + } + + forward->state = Forward::FSTATE_OK; + + return 1; } -CvarManager::~CvarManager() +// disable_cvar_hook(cvarhook:handle); +static cell AMX_NATIVE_CALL disable_cvar_hook(AMX *amx, cell *params) { - OnAmxxShutdown(); + Forward* forward = reinterpret_cast(params[1]); + + if (!forward) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cvar hook handle: %p", forward); + return 0; + } + + forward->state = Forward::FSTATE_STOP; + + return 1; } -void CvarManager::CreateCvarHook(void) +// get_cvar_flags(const cvar[]) +static cell AMX_NATIVE_CALL get_cvar_flags(AMX *amx, cell *params) { - // void PF_Cvar_DirectSet(struct cvar_s *var, const char *value) // = pfnCvar_DirectSet - // { - // Cvar_DirectSet(var, value); // <- We want to hook this. - // } + int ilen; + char* sCvar = get_amxstring(amx, params[1], 0, ilen); - byte *baseAddress = (byte *)g_engfuncs.pfnCvar_DirectSet; - uintptr_t *functionAddress = nullptr; + cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); -#if defined(WIN32) - // 55 push ebp - // 8B EC mov ebp, esp - // 8B 45 0C mov eax, [ebp+arg_4] - // 8B 4D 08 mov ecx, [ebp+arg_0] - // 50 push eax - // 51 push ecx - // E8 XX XX XX XX call Cvar_DirectSet - const byte opcodeJump = 0xE8; -#else - // E9 XX XX XX XX jmp Cvar_DirectSet - const byte opcodeJump = 0xE9; + return pCvar ? pCvar->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); + + cvar_t* var = g_CvarManager.FindCvar(name); + + return var ? amx_ftoc(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); + + cvar_t* var = g_CvarManager.FindCvar(name); + + return var ? (int)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); + + cvar_t* var = g_CvarManager.FindCvar(name); + + const char *value = var ? var->string : ""; + length = var ? strlen(value) : 0; + + return set_amxstring_utf8(amx, params[2], value, length, params[3] + 1); // + EOS +} + +// set_cvar_flags(const cvar[], flags) +static cell AMX_NATIVE_CALL set_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; + + cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); + + if (pCvar) + { + pCvar->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); + + cvar_t* var = g_CvarManager.FindCvar(name); + + if (var) + { + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%f", amx_ctof(params[2])); + CVAR_DIRECTSET(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]; + + cvar_t* var = g_CvarManager.FindCvar(name); + + if (var) + { + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%d", value); + CVAR_DIRECTSET(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); + + cvar_t* var = g_CvarManager.FindCvar(name); + + if (var) + { + CVAR_DIRECTSET(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(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(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(params[1]); + if (!ptr) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); + return 0; + } + + return (int)ptr->value; +} + +// get_pcvar_string(pcvar, string[], maxlen) +static cell AMX_NATIVE_CALL get_pcvar_string(AMX *amx, cell *params) +{ + cvar_t *ptr = reinterpret_cast(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] + 1); // EOS +} + +// set_pcvar_flags(pcvar, flags) +static cell AMX_NATIVE_CALL set_pcvar_flags(AMX *amx, cell *params) +{ + cvar_t *ptr = reinterpret_cast(params[1]); + if (!ptr) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); + return 0; + } + + ptr->flags = static_cast(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(params[1]); + if (!ptr) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); + return 0; + } + + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%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(params[1]); + if (!ptr) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid CVAR pointer"); + return 0; + } + + UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%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(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; +} + +// 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; + + cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); + + if (pCvar) + { + pCvar->flags &= ~((int)(params[2])); + return 1; + } + + return 0; +} + +// get_plugins_cvar(id, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=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(info->var); + + 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 - const byte opcodeJumpSize = 5; - const byte opcodeJumpByteSize = 1; - - const int maxBytesLimit = 20; - - for (size_t i = 0; i < maxBytesLimit; ++i, ++baseAddress) + if (!g_NewDLL_Available) { - if (*baseAddress == opcodeJump) - { - functionAddress = (uintptr_t *)(&baseAddress[opcodeJumpSize] + *(uintptr_t *)&baseAddress[opcodeJumpByteSize]); - break; - } + LogError(amx, AMX_ERR_NATIVE, "Client CVAR querying is not enabled - check MM version!"); + return 0; } - if (functionAddress) - { - m_HookDetour = DETOUR_CREATE_STATIC_FIXED(Cvar_DirectSet, (void *)functionAddress); + int id = params[1]; - if (m_HookDetour) - { - m_HookDetour->EnableDetour(); - } + if (id < 1 || id > gpGlobals->maxClients) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid player id %d", id); + return 0; } -} -cvar_t* CvarManager::CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId) -{ - cvar_t* var = nullptr; - CvarInfo* info = nullptr; + CPlayer *pPlayer = GET_PLAYER_POINTER_I(id); - if (!CacheLookup(name, &info)) + if (!pPlayer->initialized || pPlayer->IsBot()) { - // Not cached - Is cvar already exist? - var = CVAR_GET_POINTER(name); + LogError(amx, AMX_ERR_NATIVE, "Player %d is either not connected or a bot", id); + return 0; + } - // Whether it exists, we need to prepare a new entry. - info = new CvarInfo(); + int dummy; + const char *cvarname = get_amxstring(amx, params[2], 0, dummy); + const char *resultfuncname = get_amxstring(amx, params[3], 1, dummy); - // Shared datas. - info->name = name; - info->plugin = plugin; - info->pluginId = plugnId; + // public clientcvarquery_result(id, const cvar[], const result[], [const param[]]) + int iFunc; - if (var) + 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) { - // Cvar already exists. Just copy. - // "string" will be set after. "value" and "next" are automatically set. - info->var = var; - info->defaultval = var->string; - info->amxmodx = false; - } - else - { - // Registers a new cvar. - static cvar_t cvar_reg_helper; - - // "string" will be set after. "value" and "next" are automatically set. - cvar_reg_helper.name = info->name.chars(); - cvar_reg_helper.string = ""; - cvar_reg_helper.flags = flags; - - // Adds cvar to global list. - CVAR_REGISTER(&cvar_reg_helper); - - // Registering can fail if name is already a registered command. - var = CVAR_GET_POINTER(name); - - // If so, we can't go further. - if (!var) - { - delete info; - return nullptr; - } - - // If ok, we got a valid pointer, we can copy. - info->var = var; - info->defaultval = value; - info->amxmodx = true; - - // Keeps track count of cvars registered by AMXX. - ++m_AmxmodxCvars; + delete queryObject; + unregisterSPForward(iFunc); + LogError(amx, AMX_ERR_MEMORY, "Hmm. Out of memory?"); + return 0; } - // Add a new entry in the caches. - m_Cvars.append(info); - m_Cache.insert(name, info); + memcpy(reinterpret_cast(queryObject->params), reinterpret_cast(get_amxaddr(amx, params[5])), queryObject->paramLen * sizeof(cell)); - // Make sure that whether an existing or new cvar is set to the given value. - CVAR_DIRECTSET(var, value); + queryObject->params[queryObject->paramLen - 1] = 0; + } + else { + queryObject->params = NULL; + queryObject->paramLen = 0; } - return info->var; + pPlayer->queries.push_back(queryObject); + + QUERY_CLIENT_CVAR_VALUE2(pPlayer->pEdict, cvarname, queryObject->requestId); + + return 1; } -cvar_t* CvarManager::FindCvar(const char* name) + +AMX_NATIVE_INFO g_CvarNatives[] = { - cvar_t* var = nullptr; - CvarInfo* info = nullptr; + {"register_cvar", register_cvar}, + {"cvar_exists", cvar_exists}, + {"get_cvar_pointer", get_cvar_pointer}, - // Do we have already cvar in cache? - if (CacheLookup(name, &info)) - { - return info->var; - } + {"hook_cvar_change", hook_cvar_change}, + {"enable_cvar_hook", enable_cvar_hook}, + {"disable_cvar_hook", disable_cvar_hook}, - // Cvar doesn't exist. - if (!(var = CVAR_GET_POINTER(name))) - { - return nullptr; - } + {"get_cvar_flags", get_cvar_flags}, + {"get_cvar_float", get_cvar_float}, + {"get_cvar_num", get_cvar_num}, + {"get_cvar_string", get_cvar_string}, - // Create a new entry. - info = new CvarInfo(); - info->var = var; - info->name = name; - info->plugin = ""; - info->pluginId = -1; - info->amxmodx = false; + {"set_cvar_flags", set_cvar_flags}, + {"set_cvar_float", set_cvar_float}, + {"set_cvar_num", set_cvar_num}, + {"set_cvar_string", set_cvar_string}, - // Add entry in the caches. - m_Cvars.append(info); - m_Cache.insert(name, info); + {"get_pcvar_flags", get_pcvar_flags}, + {"get_pcvar_float", get_pcvar_float}, + {"get_pcvar_num", get_pcvar_num}, + {"get_pcvar_string", get_pcvar_string}, - return var; -} + {"set_pcvar_flags", set_pcvar_flags}, + {"set_pcvar_float", set_pcvar_float}, + {"set_pcvar_string", set_pcvar_string}, + {"set_pcvar_num", set_pcvar_num}, -CvarInfo* CvarManager::FindCvar(size_t index) -{ - // Used by get_plugins_cvar native. - // For compatibility, only cvars registered by AMXX are concerned. + {"remove_cvar_flags", remove_cvar_flags}, - size_t iter_id = 0; - - for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) - { - if (iter->amxmodx && iter_id++ == index) - { - return *(iter); - } - } - - return nullptr; -} - -bool CvarManager::CacheLookup(const char* name, CvarInfo** info) -{ - return m_Cache.retrieve(name, info); -} - -Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback) -{ - CvarInfo* info = nullptr; - - // A cvar is guaranteed to be in cache if pointer is got from - // get_cvar_pointer and register_cvar natives. Though it might be - // provided by another way. If by any chance we run in such - // situation, we create a new entry right now. - - if (!CacheLookup(var->name, &info)) - { - // Create a new entry. - info = new CvarInfo(); - info->var = var; - info->name = var->name; - info->plugin = ""; - info->pluginId = -1; - info->amxmodx = false; - - // Add entry in the caches. - m_Cvars.append(info); - m_Cache.insert(info->name.chars(), info); - } - - int length; - *callback = get_amxstring(amx, param, 0, length); - - int forwardId = registerSPForwardByName(amx, *callback, FP_CELL, FP_STRING, FP_STRING, FP_DONE); - - // Invalid callback, it could be: not a public function, wrongly named, or simply missing. - if (forwardId == -1) - { - return nullptr; - } - - // Detour is disabled on map change. - m_HookDetour->EnableDetour(); + {"get_plugins_cvar", get_plugins_cvar}, + {"get_plugins_cvarsnum", get_plugins_cvarsnum}, - Forward* forward = new Forward(forwardId, *callback); - info->hooks.append(new CvarPlugin(g_plugins.findPlugin(amx)->getId(), forward)); + {"query_client_cvar", query_client_cvar}, - return forward; -} - -size_t CvarManager::GetRegCvarsCount() -{ - return m_AmxmodxCvars; -} - -void CvarManager::OnConsoleCommand() -{ - print_srvconsole("Registered cvars:\n"); - print_srvconsole(" %-24.23s %-24.23s %-16.15s\n", "name", "value", "plugin"); - - size_t index = 0; - ke::AString pluginName; - - if (CMD_ARGC() > 2) // Searching for cvars registered to a plugin - { - pluginName = CMD_ARGV(2); - } - - for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) - { - CvarInfo* ci = (*iter); - - if (ci->amxmodx && (!pluginName.length() || strncmp(ci->name.chars(), pluginName.chars(), pluginName.length()) == 0)) - { - print_srvconsole(" [%3d] %-24.23s %-24.23s %-16.15s\n", ++index, ci->name.chars(), ci->var->string, ci->plugin.chars()); - } - } -} - -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)->hooks.length(); ++i) - { - delete (*cvar)->hooks[i]; - } - - (*cvar)->hooks.clear(); - } - - // There is no point to enable detour if at next map change - // no plugins hook cvars. - m_HookDetour->DisableDetour(); -} - -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)->hooks.length(); ++i) - { - delete (*cvar)->hooks[i]; - } - - delete (*cvar); - } - - m_Cache.clear(); - m_HookDetour->Destroy(); -} + {NULL, NULL} +}; \ No newline at end of file diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 82fae683..41507cbb 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -31,7 +31,7 @@ #include "trie_natives.h" #include "CDataPack.h" #include "textparse.h" -#include "cvars.h" +#include "CvarManager.h" plugin_info_t Plugin_info = { diff --git a/amxmodx/modules.cpp b/amxmodx/modules.cpp index a6089af6..4b065c32 100755 --- a/amxmodx/modules.cpp +++ b/amxmodx/modules.cpp @@ -553,6 +553,7 @@ int set_amxnatives(AMX* amx, char error[128]) amx_Register(amx, g_DatapackNatives, -1); amx_Register(amx, g_StackNatives, -1); amx_Register(amx, g_TextParserNatives, -1); + amx_Register(amx, g_CvarNatives, -1); //we're not actually gonna check these here anymore amx->flags |= AMX_FLAG_PRENIT; diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj b/amxmodx/msvc12/amxmodx_mm.vcxproj index b871d45f..1cc3ed54 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj @@ -319,6 +319,7 @@ + @@ -389,7 +390,7 @@ - + diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters index e0c0e65a..6c60ac4a 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters @@ -192,15 +192,18 @@ SDK - - Source Files - Memtools\CDetour Memtools\CDetour\asm + + Source Files + + + Source Files + @@ -347,9 +350,6 @@ SDK - - Header Files - Memtools\CDetour @@ -359,6 +359,9 @@ Memtools\CDetour\asm + + Header Files + diff --git a/amxmodx/nongpl_matches.cpp b/amxmodx/nongpl_matches.cpp index b0d31dd1..d8bec891 100644 --- a/amxmodx/nongpl_matches.cpp +++ b/amxmodx/nongpl_matches.cpp @@ -30,3 +30,19 @@ NONGPL_CVAR_T NONGPL_CVAR_LIST[] = {"amx_gg", 1}, {NULL, 0}, }; + +bool CheckBadConList(const char *cvar, int type) +{ + int i = 0; + while (NONGPL_CVAR_LIST[i].cvar != NULL) + { + if (NONGPL_CVAR_LIST[i].type == type + && strcmp(NONGPL_CVAR_LIST[i].cvar, cvar) == 0) + { + return true; + } + i++; + } + + return false; +} diff --git a/amxmodx/nongpl_matches.h b/amxmodx/nongpl_matches.h index 1ea1d2df..18c0f756 100644 --- a/amxmodx/nongpl_matches.h +++ b/amxmodx/nongpl_matches.h @@ -25,5 +25,6 @@ struct NONGPL_CVAR_T extern NONGPL_PLUGIN_T NONGPL_PLUGIN_LIST[]; extern NONGPL_CVAR_T NONGPL_CVAR_LIST[]; +extern bool CheckBadConList(const char *cvar, int type); #endif //_INCLUDE_AMXMODX_NONGPL_MATCHES_H_ diff --git a/plugins/include/amxconst.inc b/plugins/include/amxconst.inc index e6830eef..7616d819 100755 --- a/plugins/include/amxconst.inc +++ b/plugins/include/amxconst.inc @@ -105,20 +105,6 @@ public stock const MaxClients; /* Maximum number of players the server supports #define PLUGIN_HANDLED 1 /* stop other plugins */ #define PLUGIN_HANDLED_MAIN 2 /* to use in client_command(), continue all plugins but stop the command */ -/** - * CVAR flags for register_cvar() - */ -#define FCVAR_ARCHIVE 1 /* Set to cause it to be saved to vars.rc */ -#define FCVAR_USERINFO 2 /* Changes the client's info string */ -#define FCVAR_SERVER 4 /* Notifies players when changed */ -#define FCVAR_EXTDLL 8 /* Defined by external DLL */ -#define FCVAR_CLIENTDLL 16 /* Defined by the client dll */ -#define FCVAR_PROTECTED 32 /* It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value */ -#define FCVAR_SPONLY 64 /* This cvar cannot be changed by clients connected to a multiplayer server. */ -#define FCVAR_PRINTABLEONLY 128 /* This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). */ -#define FCVAR_UNLOGGED 256 /* If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log */ -#define FCVAR_NOEXTRAWHITEPACE 512 /* Strip trailing/leading white space from this cvar */ - /** * IDs of weapons in CS */ diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index 78107435..fc9468b3 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -1404,159 +1404,6 @@ native amxclient_cmd(index, const command[], const arg1[]="", const arg2[]=""); */ native server_cmd(const command[], any:...); -/** - * Sets a cvar to a given string value. The cvar is accessed by name. - * - * @note Accessing a cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the set_pcvar_string function. - * - * @param cvar Cvar name to set value of - * @param value Value to set cvar to - * - * @noreturn - */ -native set_cvar_string(const cvar[], const value[]); - -/** - * Returns if a cvar is registered on the server. - * - * @param cvar Cvar name to check - * - * @return 1 if the cvar exists, 0 otherwise - */ -native cvar_exists(const cvar[]); - -/** - * Removes specified flags from a cvar. The cvar is accessed by name. - * - * @note Not permitted for the "amx_version", "amxmodx_version", "fun_version" - * and "sv_cheats" cvars. - * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc - * @note This function removes the flags using a bitwise-and operation. - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the set_pcvar_flags function. - * - * - * @param cvar Cvar name to remove flags from - * @param flags Bitflag sum of flags to remove - * - * @return 1 on success, 0 if cvar does not exist or is not permitted - */ -native remove_cvar_flags(const cvar[], flags=-1); - -/** - * Sets specified flags to a cvar. The cvar is accessed by name. - * - * @note Not permitted for the "amx_version", "amxmodx_version", "fun_version" - * and "sv_cheats" cvars. - * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc - * @note This function just adds the flags using a bitwise-or operation. After - * it has run the flags may not exactly equal the specified bitflag sum. - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the set_pcvar_flags function. - * - * @param cvar Cvar name to remove flags from - * @param flags Bitflag sum of flags to set - * - * @return 1 on success, 0 if cvar does not exist or is not permitted - */ -native set_cvar_flags(const cvar[], flags); - -/** - * Returns flags of a cvar. The cvar is accessed by name. - * - * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the get_pcvar_flags function. - * - * @param cvar Cvar name to retrieve flags from - * - * @return 1 on success, 0 if cvar does not exist or is not permitted - */ -native get_cvar_flags(const cvar[]); - -/** - * Sets a cvar to a given float value. The cvar is accessed by name. - * - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the set_pcvar_float function. - * - * @param cvar Cvar name to set value of - * @param value Value to set cvar to - * - * @noreturn - */ -native set_cvar_float(const cvar[], Float:value); - -/** - * Returns a floating value from a cvar. The cvar is accessed by name. - * - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the get_pcvar_float function. - * - * @param cvarname Cvar name to retrieve value from - * - * @return Cvar value, converted to float - */ -native Float:get_cvar_float(const cvarname[]); - -/** - * Returns an integer value from a cvar. The cvar is accessed by name. - * - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the get_pcvar_num function. - * - * @param cvarname Cvar name to retrieve value from - * - * @return Cvar value, converted to int - */ -native get_cvar_num(const cvarname[]); - -/** - * Sets a cvar to a given integer value. The cvar is accessed by name. - * - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the set_pcvar_num function. - * - * @param cvar Cvar name to set value of - * @param value Value to set cvar to - * - * @noreturn - */ -native set_cvar_num(const cvarname[], value); - -/** - * Gets a string value from a cvar. The cvar is accessed by name. - * - * @note Accessing a Cvar by name requires this function to walk through the - * engine's cvar list every time, which can result in a considerable waste - * of processing time, especially if many cvars have been registered. For - * a vastly superior alternative look at the get_pcvar_string function. - * - * @param cvar Cvar name to retrieve value from - * @param output Buffer to copy cvar value to - * @param iLen Maximum size of the buffer - * - * @return Number of cells written to buffer. - */ -native get_cvar_string(const cvarname[], output[], iLen); - /** * Retrieves the name of the currently played map. * @@ -1964,31 +1811,6 @@ native get_concmd_plid(cid, flag_mask, id_type); */ native get_concmdsnum(flag, id=-1); -/** - * Returns the number of plugin-registered cvars. - * - * @return Number of registered cvars - */ -native get_plugins_cvarsnum(); - -/** - * Retrieves information about a plugin-registered cvar. - * - * @note The returned cvar pointer should be used with the get_pcvar_* and - * set_pcvar_* set of functions. - * - * @param num Cvar index, this does not equal the cvar pointer, it is - * the internal index, incremented for each registered cvar - * @param name Buffer to copy cvar name to - * @param namelen Maximum buffer size - * @param flags Variable to store cvar flags to - * @param plugin_id Variable to store id of the registering plugin to - * @param pcvar_handle Variable to store cvar pointer to - * - * @return 1 on success, 0 if index is invalid - */ -native get_plugins_cvar(num, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=0); - /** * Returns unique menu id of a menu. * @@ -2058,23 +1880,6 @@ native server_exec(); */ native emit_sound(index, channel, const sample[], Float:vol, Float:att, flags, pitch); -/** - * Registers a new cvar for the engine. - * - * @note For a list of possible cvar flags see FCVAR_* constants in amxconst.inc - * @note If an already existing cvar is registered it will not be duplicated. - * @note The returned cvar pointer should be used with the get_pcvar_* and - * set_pcvar_* set of functions. - * - * @param name Cvar name - * @param string Default cvar value - * @param flags Cvar flags - * @param fvalue Unused - * - * @return Unique cvar pointer - */ -native register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0); - /** * Returns a random floating point value generated by the engine. * @@ -2806,35 +2611,6 @@ native set_array(param, const source[], size); */ native set_array_f(param, const Float:source[], size); -/** - * Dispatches a client cvar query, allowing the plugin to query for its value on - * the client. - * - * @note The callback will be called in the following manner: - * - * public cvar_query_callback(id, const cvar[], const value[], const param[]) - * - * id - Client index - * cvar - Cvar queried - * value - Cvar value on the client - * param - Extra data [optional] - * - * @param id Client index - * @param cvar Cvar to query - * @param resultFunc Callback function - * @param paramlen Size of extra data - * @param params Extra data to pass through to callback - * - * @noreturn - * @error If the client index is not within the range of 1 to - * MaxClients or the client is not connected, an error - * will be thrown. - * If the callback function is invalid, cvar querying is - * unavailable or the querying process runs out of memory, - * an error will be thrown. - */ -native query_client_cvar(id, const cvar[], const resultFunc[], paramlen=0, const params[]=""); - /** * Allows to trap error messages that occur in a plugin. * @@ -3220,110 +2996,6 @@ native ExecuteForward(forward_handle, &ret, any:...); */ native DestroyForward(forward_handle); -/** - * Returns the cvar pointer of the specified cvar. - * - * @note A pointer is also returned by register_cvar(). Plugins can (and should) - * retrieve and use pointers for already existing mod cvars. - * - * @param cvar Cvar name to find - * - * @return Cvar pointer on success, 0 if cvar was not found - */ -native get_cvar_pointer(const cvar[]); - -/** - * Returns flags of a cvar via direct pointer access. - * - * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc - * - * @param pcvar Pointer to cvar to retrieve flags from - * - * @return 1 on success, 0 if cvar pointer is invalid - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native get_pcvar_flags(pcvar); - -/** - * Sets specified flags to a cvar via direct pointer access. - * - * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc - * @note This function directly sets the provided bitflag, unlike set_cvar_flags - * which adds them using a bitwise OR. - * - * @param pcvar Pointer to cvar to set flags of - * @param flags Bitflag sum of flags to set - * - * @return 1 on success, 0 if cvar does not exist or is not permitted - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native set_pcvar_flags(pcvar, flags); - -/** - * Returns an integer value from a cvar via direct pointer access. - * - * @param pcvar Pointer to cvar to retrieve value from - * - * @return Cvar value, converted to int - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native get_pcvar_num(pcvar); - -/** - * Sets an integer value to a cvar via direct pointer access. - * - * @param pcvar Pointer to cvar to set value of - * @param num Value to set cvar to - * - * @noreturn - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native set_pcvar_num(pcvar, num); - -/** - * Returns a float value from a cvar via direct pointer access. - * - * @param pcvar Pointer to cvar to retrieve value from - * - * @return Cvar value, converted to float - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native Float:get_pcvar_float(pcvar); - -/** - * Sets a float value to a cvar via direct pointer access. - * - * @param pcvar Pointer to cvar to set value of - * @param num Value to set cvar to - * - * @noreturn - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native set_pcvar_float(pcvar, Float:num); - -/** - * Returns a string value from a cvar via direct pointer access. - * - * @param pcvar Pointer to cvar to retrieve value from - * @param string Buffer to copy cvar value to - * @param maxlen Maximum size of the buffer - * - * @return Number of cells written to buffer. - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native get_pcvar_string(pcvar, string[], maxlen); - -/** - * Sets a string value to a cvar via direct pointer access. - * - * @param pcvar Pointer to cvar to retrieve value from - * @param string Value to set cvar to - * - * @noreturn - * @error If an invalid cvar pointer is specified, an error is thrown. - */ -native set_pcvar_string(pcvar, const string[]); - /** * Sets all elements of array to a specified value. * diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index c5803aed..8a8fb83d 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -12,6 +12,58 @@ #endif #define _cvars_included +/** + * CVAR flags for register_cvar() + */ +#define FCVAR_ARCHIVE 1 /* Set to cause it to be saved to vars.rc */ +#define FCVAR_USERINFO 2 /* Changes the client's info string */ +#define FCVAR_SERVER 4 /* Notifies players when changed */ +#define FCVAR_EXTDLL 8 /* Defined by external DLL */ +#define FCVAR_CLIENTDLL 16 /* Defined by the client dll */ +#define FCVAR_PROTECTED 32 /* It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value */ +#define FCVAR_SPONLY 64 /* This cvar cannot be changed by clients connected to a multiplayer server. */ +#define FCVAR_PRINTABLEONLY 128 /* This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). */ +#define FCVAR_UNLOGGED 256 /* If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log */ +#define FCVAR_NOEXTRAWHITEPACE 512 /* Strip trailing/leading white space from this cvar */ + +/** + * Registers a new cvar for the engine. + * + * @note For a list of possible cvar flags see FCVAR_* constants in amxconst.inc + * @note If an already existing cvar is registered it will not be duplicated. + * @note The returned cvar pointer should be used with the get_pcvar_* and + * set_pcvar_* set of functions. + * + * @param name Cvar name + * @param string Default cvar value + * @param flags Cvar flags + * @param fvalue Unused + * + * @return Unique cvar pointer + */ +native register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0); + +/** + * Returns if a cvar is registered on the server. + * + * @param cvar Cvar name to check + * + * @return 1 if the cvar exists, 0 otherwise + */ +native cvar_exists(const cvar[]); + +/** + * Returns the cvar pointer of the specified cvar. + * + * @note A pointer is also returned by register_cvar(). Plugins can (and should) + * retrieve and use pointers for already existing mod cvars. + * + * @param cvar Cvar name to find + * + * @return Cvar pointer on success, 0 if cvar was not found + */ +native get_cvar_pointer(const cvar[]); + /** * Creates a hook for when a console variable's value is changed. * @@ -49,4 +101,292 @@ native disable_cvar_hook(cvarhook:handle); */ native enable_cvar_hook(cvarhook:handle); +/** + * Returns flags of a cvar. The cvar is accessed by name. + * + * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the get_pcvar_flags function. + * + * @param cvar Cvar name to retrieve flags from + * + * @return 1 on success, 0 if cvar does not exist or is not permitted + */ +native get_cvar_flags(const cvar[]); +/** + * Sets specified flags to a cvar. The cvar is accessed by name. + * + * @note Not permitted for the "amx_version", "amxmodx_version", "fun_version" + * and "sv_cheats" cvars. + * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc + * @note This function just adds the flags using a bitwise-or operation. After + * it has run the flags may not exactly equal the specified bitflag sum. + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the set_pcvar_flags function. + * + * @param cvar Cvar name to remove flags from + * @param flags Bitflag sum of flags to set + * + * @return 1 on success, 0 if cvar does not exist or is not permitted + */ +native set_cvar_flags(const cvar[], flags); + +/** + * Removes specified flags from a cvar. The cvar is accessed by name. + * + * @note Not permitted for the "amx_version", "amxmodx_version", "fun_version" + * and "sv_cheats" cvars. + * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc + * @note This function removes the flags using a bitwise-and operation. + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the set_pcvar_flags function. + * + * + * @param cvar Cvar name to remove flags from + * @param flags Bitflag sum of flags to remove + * + * @return 1 on success, 0 if cvar does not exist or is not permitted + */ +native remove_cvar_flags(const cvar[], flags=-1); + +/** + * Gets a string value from a cvar. The cvar is accessed by name. + * + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the get_pcvar_string function. + * + * @param cvar Cvar name to retrieve value from + * @param output Buffer to copy cvar value to + * @param iLen Maximum size of the buffer + * + * @return Number of cells written to buffer. + */ +native get_cvar_string(const cvarname[], output[], iLen); + +/** + * Sets a cvar to a given string value. The cvar is accessed by name. + * + * @note Accessing a cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the set_pcvar_string function. + * + * @param cvar Cvar name to set value of + * @param value Value to set cvar to + * + * @noreturn + */ +native set_cvar_string(const cvar[], const value[]); + +/** + * Returns a floating value from a cvar. The cvar is accessed by name. + * + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the get_pcvar_float function. + * + * @param cvarname Cvar name to retrieve value from + * + * @return Cvar value, converted to float + */ +native Float:get_cvar_float(const cvarname[]); + +/** + * Sets a cvar to a given float value. The cvar is accessed by name. + * + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the set_pcvar_float function. + * + * @param cvar Cvar name to set value of + * @param value Value to set cvar to + * + * @noreturn + */ +native set_cvar_float(const cvar[], Float:value); + +/** + * Returns an integer value from a cvar. The cvar is accessed by name. + * + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the get_pcvar_num function. + * + * @param cvarname Cvar name to retrieve value from + * + * @return Cvar value, converted to int + */ +native get_cvar_num(const cvarname[]); + +/** + * Sets a cvar to a given integer value. The cvar is accessed by name. + * + * @note Accessing a Cvar by name requires this function to walk through the + * engine's cvar list every time, which can result in a considerable waste + * of processing time, especially if many cvars have been registered. For + * a vastly superior alternative look at the set_pcvar_num function. + * + * @param cvar Cvar name to set value of + * @param value Value to set cvar to + * + * @noreturn + */ +native set_cvar_num(const cvarname[], value); + +/** + * Returns flags of a cvar via direct pointer access. + * + * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc + * + * @param pcvar Pointer to cvar to retrieve flags from + * + * @return 1 on success, 0 if cvar pointer is invalid + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native get_pcvar_flags(pcvar); + +/** + * Sets specified flags to a cvar via direct pointer access. + * + * @note For a list of possible flags see the FCVAR_* constants in amxconst.inc + * @note This function directly sets the provided bitflag, unlike set_cvar_flags + * which adds them using a bitwise OR. + * + * @param pcvar Pointer to cvar to set flags of + * @param flags Bitflag sum of flags to set + * + * @return 1 on success, 0 if cvar does not exist or is not permitted + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native set_pcvar_flags(pcvar, flags); + +/** + * Returns an integer value from a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to retrieve value from + * + * @return Cvar value, converted to int + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native get_pcvar_num(pcvar); + +/** + * Sets an integer value to a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to set value of + * @param num Value to set cvar to + * + * @noreturn + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native set_pcvar_num(pcvar, num); + +/** + * Returns a float value from a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to retrieve value from + * + * @return Cvar value, converted to float + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native Float:get_pcvar_float(pcvar); + +/** + * Sets a float value to a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to set value of + * @param num Value to set cvar to + * + * @noreturn + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native set_pcvar_float(pcvar, Float:num); + +/** + * Returns a string value from a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to retrieve value from + * @param string Buffer to copy cvar value to + * @param maxlen Maximum size of the buffer + * + * @return Number of cells written to buffer. + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native get_pcvar_string(pcvar, string[], maxlen); + +/** + * Sets a string value to a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to retrieve value from + * @param string Value to set cvar to + * + * @noreturn + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native set_pcvar_string(pcvar, const string[]); + +/** + * Returns the number of plugin-registered cvars. + * + * @return Number of registered cvars + */ +native get_plugins_cvarsnum(); + +/** + * Retrieves information about a plugin-registered cvar. + * + * @note The returned cvar pointer should be used with the get_pcvar_* and + * set_pcvar_* set of functions. + * + * @param num Cvar index, this does not equal the cvar pointer, it is + * the internal index, incremented for each registered cvar + * @param name Buffer to copy cvar name to + * @param namelen Maximum buffer size + * @param flags Variable to store cvar flags to + * @param plugin_id Variable to store id of the registering plugin to + * @param pcvar_handle Variable to store cvar pointer to + * + * @return 1 on success, 0 if index is invalid + */ +native get_plugins_cvar(num, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=0); + +/** + * Dispatches a client cvar query, allowing the plugin to query for its value on + * the client. + * + * @note The callback will be called in the following manner: + * + * public cvar_query_callback(id, const cvar[], const value[], const param[]) + * + * id - Client index + * cvar - Cvar queried + * value - Cvar value on the client + * param - Extra data [optional] + * + * @param id Client index + * @param cvar Cvar to query + * @param resultFunc Callback function + * @param paramlen Size of extra data + * @param params Extra data to pass through to callback + * + * @noreturn + * @error If the client index is not within the range of 1 to + * MaxClients or the client is not connected, an error + * will be thrown. + * If the callback function is invalid, cvar querying is + * unavailable or the querying process runs out of memory, + * an error will be thrown. + */ +native query_client_cvar(id, const cvar[], const resultFunc[], paramlen=0, const params[]=""); \ No newline at end of file From a05d0df50e0f0e997c8fb9339245670af819a0e5 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Fri, 23 Jan 2015 00:58:57 +0100 Subject: [PATCH 09/21] Cvars: Add create_cvar native with more options (description, bounds) --- amxmodx/CvarManager.cpp | 57 ++++++++++++++++++++++++--------------- amxmodx/CvarManager.h | 23 +++++++++++++++- amxmodx/cvars.cpp | 29 +++++++++++++++++++- plugins/include/cvars.inc | 26 +++++++++++++++++- 4 files changed, 111 insertions(+), 24 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index 92549105..00ed7bc5 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -22,8 +22,35 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) if (!var || !value // Sanity checks against bogus pointers. || strcmp(var->string, value) == 0 // Make sure old and new values are different to not trigger callbacks. - || !g_CvarManager.CacheLookup(var->name, &info) // No data in cache, nothing to call. - || info->hooks.empty()) // No hooked cvars, nothing to call. + || !g_CvarManager.CacheLookup(var->name, &info)) // No data in cache, nothing to do. + { + return true; + } + + if (info->hasMin || info->hasMax) // cvar_s doesn't have min/max mechanism, so we check things here. + { + float fvalue = atof(value); + bool oob = false; + + if (info->hasMin && fvalue < info->minVal) + { + oob = true; + fvalue = info->minVal; + } + else if (info->hasMax && fvalue > info->maxVal) + { + oob = true; + fvalue = info->maxVal; + } + + if (oob) // Found value out of bound, set new value and block original call. + { + CVAR_SET_FLOAT(var->name, fvalue); + return false; + } + } + + if (info->hooks.empty()) // No hooked cvars, nothing to call. { return true; } @@ -115,7 +142,8 @@ void CvarManager::CreateCvarHook(void) } } -cvar_t* CvarManager::CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId) +cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, int flags, + const char* helpText, bool hasMin, float min, bool hasMax, float max) { cvar_t* var = nullptr; CvarInfo* info = nullptr; @@ -126,12 +154,7 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, float fvalu var = CVAR_GET_POINTER(name); // Whether it exists, we need to prepare a new entry. - info = new CvarInfo(); - - // Shared datas. - info->name = name; - info->plugin = plugin; - info->pluginId = plugnId; + info = new CvarInfo(name, helpText, hasMin, min, hasMax, max, plugin, pluginId); if (var) { @@ -202,12 +225,8 @@ cvar_t* CvarManager::FindCvar(const char* name) } // Create a new entry. - info = new CvarInfo(); - info->var = var; - info->name = name; - info->plugin = ""; - info->pluginId = -1; - info->amxmodx = false; + info = new CvarInfo(name); + info->var = var; // Add entry in the caches. m_Cvars.append(info); @@ -251,12 +270,8 @@ Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const ch if (!CacheLookup(var->name, &info)) { // Create a new entry. - info = new CvarInfo(); - info->var = var; - info->name = var->name; - info->plugin = ""; - info->pluginId = -1; - info->amxmodx = false; + info = new CvarInfo(var->name); + info->var = var; // Add entry in the caches. m_Cvars.append(info); diff --git a/amxmodx/CvarManager.h b/amxmodx/CvarManager.h index a5eb7eb7..0a8c631c 100644 --- a/amxmodx/CvarManager.h +++ b/amxmodx/CvarManager.h @@ -52,11 +52,29 @@ typedef ke::Vector CvarsHook; struct CvarInfo : public ke::InlineListNode { + 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_), + plugin(plugin_), pluginId(pluginId_) {}; + + CvarInfo(const char* name_) + : + name(name_), defaultval(""), description(""), hasMin(false), minVal(0), hasMax(false), maxVal(0), + plugin(""), pluginId(-1), amxmodx(false) {}; + cvar_t* var; ke::AString name; ke::AString defaultval; + ke::AString description; + bool hasMin; + bool hasMax; + float minVal; + float maxVal; + ke::AString plugin; int pluginId; + CvarsHook hooks; bool amxmodx; @@ -80,7 +98,10 @@ class CvarManager void CreateCvarHook(); - cvar_t* CreateCvar(const char* name, const char* value, float fvalue, int flags, const char* plugin, int plugnId); + cvar_t* CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, + int flags = 0, const char* helpText = "", + bool hasMin = false, float min = 0, + bool hasMax = false, float max = 0); cvar_t* FindCvar(const char* name); CvarInfo* FindCvar(size_t index); bool CacheLookup(const char* name, CvarInfo** info); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 19dba289..16f99e58 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -14,6 +14,32 @@ 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]; + bool hasMin = params[5] > 0 ? true : false; + bool hasMax = params[7] > 0 ? true : false; + float minVal = amx_ctof(params[6]); + float maxVal = amx_ctof(params[8]); + + CPluginMngr::CPlugin *plugin = g_plugins.findPluginFast(amx); + + if (CheckBadConList(name, 0)) + { + plugin->AddToFailCounter(1); + } + + cvar_t* var = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags, helpText, hasMin, minVal, hasMax, maxVal); + + return reinterpret_cast(var); +} + // register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0) static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params) { @@ -31,7 +57,7 @@ static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params) plugin->AddToFailCounter(1); } - cvar_t* var = g_CvarManager.CreateCvar(name, value, fvalue, flags, plugin->getName(), plugin->getId()); + cvar_t* var = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags); return reinterpret_cast(var); } @@ -487,6 +513,7 @@ static cell AMX_NATIVE_CALL query_client_cvar(AMX *amx, cell *params) AMX_NATIVE_INFO g_CvarNatives[] = { + {"create_cvar", create_cvar}, {"register_cvar", register_cvar}, {"cvar_exists", cvar_exists}, {"get_cvar_pointer", get_cvar_pointer}, diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 8a8fb83d..1f9aa2a1 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -15,6 +15,7 @@ /** * CVAR flags for register_cvar() */ +#define FCVAR_NONE 0 /* No flags */ #define FCVAR_ARCHIVE 1 /* Set to cause it to be saved to vars.rc */ #define FCVAR_USERINFO 2 /* Changes the client's info string */ #define FCVAR_SERVER 4 /* Notifies players when changed */ @@ -26,9 +27,32 @@ #define FCVAR_UNLOGGED 256 /* If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log */ #define FCVAR_NOEXTRAWHITEPACE 512 /* Strip trailing/leading white space from this cvar */ +/** + * Creates a new cvar for the engine. + * + * @note This is same as regitser_cvar but with more options. + * @note For a list of possible cvar flags see FCVAR_* constants above. + * @note If an already existing cvar is registered it will not be duplicated. + * @note The returned cvar pointer should be used with the get_pcvar_* and + * set_pcvar_* set of functions. + * + * @param name Cvar name + * @param string Default cvar value + * @param flags Optional bitstring of flags determining how the convar should be handled + * @param help_text Optional description of the cvar + * @param has_min Optional boolean that determines if the convar has a minimum valu + * @param min_val Minimum floating point value that the convar can have if has_min is true + * @param has_max Optional boolean that determines if the convar has a maximum value + * @param max_val Maximum floating point value that the convar can have if has_max is true + * + * @return Unique cvar pointer + */ +native create_cvar(const name[], const string[], flags = FCVAR_NONE, const help_text[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0); + /** * Registers a new cvar for the engine. * + * @note Deprecated. Consider to use create_cvar for more options. * @note For a list of possible cvar flags see FCVAR_* constants in amxconst.inc * @note If an already existing cvar is registered it will not be duplicated. * @note The returned cvar pointer should be used with the get_pcvar_* and @@ -41,7 +65,7 @@ * * @return Unique cvar pointer */ -native register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0); +native register_cvar(const name[], const string[], flags = FCVAR_NONE, Float:fvalue=0.0); /** * Returns if a cvar is registered on the server. From 8ebb7be36d237f2625558e4fe18b94cc5192f66b Mon Sep 17 00:00:00 2001 From: Arkshine Date: Fri, 23 Jan 2015 16:45:28 +0100 Subject: [PATCH 10/21] Cvars: Add get|set_pcvar_bounds natives --- amxmodx/CvarManager.cpp | 6 +- amxmodx/CvarManager.h | 9 ++- amxmodx/cvars.cpp | 127 +++++++++++++++++++++++++++++--------- plugins/include/cvars.inc | 35 ++++++++++- 4 files changed, 143 insertions(+), 34 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index 00ed7bc5..a1654c16 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -207,7 +207,7 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char* return info->var; } -cvar_t* CvarManager::FindCvar(const char* name) +CvarInfo* CvarManager::FindCvar(const char* name) { cvar_t* var = nullptr; CvarInfo* info = nullptr; @@ -215,7 +215,7 @@ cvar_t* CvarManager::FindCvar(const char* name) // Do we have already cvar in cache? if (CacheLookup(name, &info)) { - return info->var; + return info; } // Cvar doesn't exist. @@ -232,7 +232,7 @@ cvar_t* CvarManager::FindCvar(const char* name) m_Cvars.append(info); m_Cache.insert(name, info); - return var; + return info; } CvarInfo* CvarManager::FindCvar(size_t index) diff --git a/amxmodx/CvarManager.h b/amxmodx/CvarManager.h index 0a8c631c..68012f8e 100644 --- a/amxmodx/CvarManager.h +++ b/amxmodx/CvarManager.h @@ -17,6 +17,12 @@ class CDetour; +enum CvarBounds +{ + CvarBound_Upper = 0, + CvarBound_Lower +}; + struct Forward { enum fwdstate @@ -102,7 +108,8 @@ class CvarManager int flags = 0, const char* helpText = "", bool hasMin = false, float min = 0, bool hasMax = false, float max = 0); - cvar_t* FindCvar(const char* name); + + CvarInfo* FindCvar(const char* name); CvarInfo* FindCvar(size_t index); bool CacheLookup(const char* name, CvarInfo** info); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 16f99e58..d77bba29 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -73,11 +73,11 @@ static cell AMX_NATIVE_CALL cvar_exists(AMX *amx, cell *params) static cell AMX_NATIVE_CALL get_cvar_pointer(AMX *amx, cell *params) { int len; - char *name = get_amxstring(amx, params[1], 0, len); + const char *name = get_amxstring(amx, params[1], 0, len); - cvar_t *ptr = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - return reinterpret_cast(ptr); + return reinterpret_cast(info ? info->var : 0); } // hook_cvar_change(cvarHandle, const callback[]) @@ -141,9 +141,9 @@ static cell AMX_NATIVE_CALL get_cvar_flags(AMX *amx, cell *params) int ilen; char* sCvar = get_amxstring(amx, params[1], 0, ilen); - cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); + CvarInfo* info = g_CvarManager.FindCvar(sCvar); - return pCvar ? pCvar->flags : 0; + return info ? info->var->flags : 0; } // get_cvar_float(const cvarname[]) @@ -152,9 +152,9 @@ static cell AMX_NATIVE_CALL get_cvar_float(AMX *amx, cell *params) int length; const char* name = get_amxstring(amx, params[1], 0, length); - cvar_t* var = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - return var ? amx_ftoc(var->value) : 0; + return info ? amx_ftoc(info->var->value) : 0; } // get_cvar_num(const cvarname[]) @@ -163,9 +163,9 @@ static cell AMX_NATIVE_CALL get_cvar_num(AMX *amx, cell *params) int length; const char* name = get_amxstring(amx, params[1], 0, length); - cvar_t* var = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - return var ? (int)var->value : 0; + return info ? (int)info->var->value : 0; } // get_cvar_string(const cvarname[], output[], iLen) @@ -174,10 +174,10 @@ static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params) int length; const char* name = get_amxstring(amx, params[1], 0, length); - cvar_t* var = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - const char *value = var ? var->string : ""; - length = var ? strlen(value) : 0; + const char *value = info ? info->var->string : ""; + length = info ? strlen(value) : 0; return set_amxstring_utf8(amx, params[2], value, length, params[3] + 1); // + EOS } @@ -186,16 +186,16 @@ static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params) static cell AMX_NATIVE_CALL set_cvar_flags(AMX *amx, cell *params) { int ilen; - char* sCvar = get_amxstring(amx, params[1], 0, 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; - cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); + CvarInfo* info = g_CvarManager.FindCvar(sCvar); - if (pCvar) + if (info) { - pCvar->flags |= (int)(params[2]); + info->var->flags |= (int)(params[2]); return 1; } @@ -208,12 +208,12 @@ static cell AMX_NATIVE_CALL set_cvar_float(AMX *amx, cell *params) int length; const char* name = get_amxstring(amx, params[1], 0, length); - cvar_t* var = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - if (var) + if (info) { UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%f", amx_ctof(params[2])); - CVAR_DIRECTSET(var, &CVarTempBuffer[0]); + CVAR_DIRECTSET(info->var, &CVarTempBuffer[0]); } return 1; @@ -226,12 +226,12 @@ static cell AMX_NATIVE_CALL set_cvar_num(AMX *amx, cell *params) const char* name = get_amxstring(amx, params[1], 0, length); int value = params[2]; - cvar_t* var = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - if (var) + if (info) { UTIL_Format(CVarTempBuffer, sizeof(CVarTempBuffer) - 1, "%d", value); - CVAR_DIRECTSET(var, &CVarTempBuffer[0]); + CVAR_DIRECTSET(info->var, &CVarTempBuffer[0]); } return 1; @@ -243,11 +243,11 @@ static cell AMX_NATIVE_CALL set_cvar_string(AMX *amx, cell *params) int length; const char* name = get_amxstring(amx, params[1], 0, length); - cvar_t* var = g_CvarManager.FindCvar(name); + CvarInfo* info = g_CvarManager.FindCvar(name); - if (var) + if (info) { - CVAR_DIRECTSET(var, get_amxstring(amx, params[2], 1, length)); + CVAR_DIRECTSET(info->var, get_amxstring(amx, params[2], 1, length)); } return 1; @@ -305,6 +305,41 @@ static cell AMX_NATIVE_CALL get_pcvar_string(AMX *amx, cell *params) return set_amxstring_utf8(amx, params[2], ptr->string ? ptr->string : "", ptr->string ? strlen(ptr->string) : 0, params[3] + 1); // EOS } +// 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(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->hasMin; + bound = info->minVal; + break; + case CvarBound_Upper: + hasBound = info->hasMax; + bound = info->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; +} + // set_pcvar_flags(pcvar, flags) static cell AMX_NATIVE_CALL set_pcvar_flags(AMX *amx, cell *params) { @@ -369,6 +404,38 @@ static cell AMX_NATIVE_CALL set_pcvar_string(AMX *amx, cell *params) 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(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 ? true : false; + + switch (params[2]) + { + case CvarBound_Lower: + info->hasMin = set; + info->minVal = set ? amx_ctof(params[4]) : 0; + break; + case CvarBound_Upper: + info->hasMax = set; + info->maxVal = set ? amx_ctof(params[4]) : 0; + 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) { @@ -378,11 +445,11 @@ static cell AMX_NATIVE_CALL remove_cvar_flags(AMX *amx, cell *params) if (!strcmp(sCvar, "amx_version") || !strcmp(sCvar, "amxmodx_version") || !strcmp(sCvar, "fun_version") || !strcmp(sCvar, "sv_cheats")) return 0; - cvar_t* pCvar = g_CvarManager.FindCvar(sCvar); + CvarInfo* info = g_CvarManager.FindCvar(sCvar); - if (pCvar) + if (info) { - pCvar->flags &= ~((int)(params[2])); + info->var->flags &= ~((int)(params[2])); return 1; } @@ -536,11 +603,13 @@ AMX_NATIVE_INFO g_CvarNatives[] = {"get_pcvar_float", get_pcvar_float}, {"get_pcvar_num", get_pcvar_num}, {"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_string", set_pcvar_string}, {"set_pcvar_num", set_pcvar_num}, + {"set_pcvar_string", set_pcvar_string}, + {"set_pcvar_bounds", set_pcvar_bounds}, {"remove_cvar_flags", remove_cvar_flags}, diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 1f9aa2a1..8498d16d 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -13,7 +13,7 @@ #define _cvars_included /** - * CVAR flags for register_cvar() + * CVAR flags for create_cvar() */ #define FCVAR_NONE 0 /* No flags */ #define FCVAR_ARCHIVE 1 /* Set to cause it to be saved to vars.rc */ @@ -361,6 +361,39 @@ native get_pcvar_string(pcvar, string[], maxlen); */ native set_pcvar_string(pcvar, const string[]); +/** + * Cvar bound values used with get/set_pcvar_bounds() + */ +enum CvarBounds +{ + CvarBound_Upper = 0, + CvarBound_Lower +}; + +/** + * Retrieves the specified bound of a cvar. + * + * @param pcvar Pointer to cvar + * @param type Type of bound to retrieve, CvarBound_Lower or CvarBound_Upper + * @param value By-reference cell to store the specified floating point bound value + * + * @return True if the cvar has the specified bound set, false otherwise. + * @error If an invalid cvar pointer or CvarBounds value, an error is thrown. + */ +native bool:get_pcvar_bounds(pcvar, CvarBounds:type, &Float:value); + +/** + * Sets the specified bound of a cvar. + * + * @param pcvar Pointer to cvar + * @param type Type of bound to set, CvarBound_Lower or CvarBound_Upper + * @param set If set to true, cvar will use specified bound. If false, bound will be removed + * @param value Floating point value to use as the specified bound + * + * @error If an invalid cvar pointer or CvarBounds value, an error is thrown. + */ +native set_pcvar_bounds(pcvar, CvarBounds:type, bool:set, Float:value = 0.0); + /** * Returns the number of plugin-registered cvars. * From cb0b9da51f70f2eddae74d90d7f6828003ace7d7 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sat, 24 Jan 2015 21:28:43 +0100 Subject: [PATCH 11/21] Cvars: Add bind_pcvar_num/float/string --- amxmodx/CvarManager.cpp | 76 ++++++++++++++++++++++------ amxmodx/CvarManager.h | 38 +++++++++++--- amxmodx/amxmodx.h | 1 + amxmodx/cvars.cpp | 101 ++++++++++++++++++++++++++++++++++++++ amxmodx/string.cpp | 14 ++++++ plugins/include/cvars.inc | 36 +++++++++++++- 6 files changed, 244 insertions(+), 22 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index a1654c16..b5668007 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -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(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(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]; diff --git a/amxmodx/CvarManager.h b/amxmodx/CvarManager.h index 68012f8e..f6c250a5 100644 --- a/amxmodx/CvarManager.h +++ b/amxmodx/CvarManager.h @@ -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; }; -typedef ke::Vector 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 CvarsHook; +typedef ke::Vector CvarsBind; struct CvarInfo : public ke::InlineListNode { - 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 ke::AString plugin; int pluginId; + CvarsBind binds; CvarsHook hooks; bool amxmodx; diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index dcc0ed24..2596a1ee 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -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 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); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index d77bba29..acd2ffd4 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -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(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(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(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}, diff --git a/amxmodx/string.cpp b/amxmodx/string.cpp index 877d54cb..e9ca20f7 100755 --- a/amxmodx/string.cpp +++ b/amxmodx/string.cpp @@ -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)); diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 8498d16d..68a62246 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -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. * From 8a241ed0fdcd668bc28105f2eb1c038ecd810abf Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sun, 25 Jan 2015 11:38:01 +0100 Subject: [PATCH 12/21] Cvars: Add new description param in get_plugins_cvar --- amxmodx/CvarManager.cpp | 11 +++++++++++ amxmodx/cvars.cpp | 7 ++++++- plugins/include/cvars.inc | 22 ++++++++++++---------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index b5668007..d303c063 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -234,6 +234,17 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char* // Make sure that whether an existing or new cvar is set to the given value. CVAR_DIRECTSET(var, value); } + else + { + // In case a plugin has been recompiling with some changes + // after a change map occurred, we keep default data up to date. + info->description = helpText; + info->defaultval = value; + info->hasMin = hasMin; + info->minVal = min; + info->hasMax = hasMin; + info->maxVal = max; + } // Detour is disabled on map change. m_HookDetour->EnableDetour(); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index acd2ffd4..34747121 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -553,7 +553,7 @@ static cell AMX_NATIVE_CALL remove_cvar_flags(AMX *amx, cell *params) return 0; } -// get_plugins_cvar(id, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=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]); @@ -565,6 +565,11 @@ static cell AMX_NATIVE_CALL get_plugins_cvar(AMX *amx, cell *params) *get_amxaddr(amx, params[5]) = info->pluginId; *get_amxaddr(amx, params[6]) = reinterpret_cast(info->var); + if (*params / sizeof(cell) >= 7) + { + set_amxstring(amx, params[7], info->description.chars(), params[8]); + } + return 1; } diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 68a62246..2965f9fb 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -36,18 +36,18 @@ * @note The returned cvar pointer should be used with the get_pcvar_* and * set_pcvar_* set of functions. * - * @param name Cvar name - * @param string Default cvar value - * @param flags Optional bitstring of flags determining how the convar should be handled - * @param help_text Optional description of the cvar - * @param has_min Optional boolean that determines if the convar has a minimum valu - * @param min_val Minimum floating point value that the convar can have if has_min is true - * @param has_max Optional boolean that determines if the convar has a maximum value - * @param max_val Maximum floating point value that the convar can have if has_max is true + * @param name Cvar name + * @param string Default cvar value + * @param flags Optional bitstring of flags determining how the convar should be handled + * @param description Optional description of the cvar + * @param has_min Optional boolean that determines if the convar has a minimum valu + * @param min_val Minimum floating point value that the convar can have if has_min is true + * @param has_max Optional boolean that determines if the convar has a maximum value + * @param max_val Maximum floating point value that the convar can have if has_max is true * * @return Unique cvar pointer */ -native create_cvar(const name[], const string[], flags = FCVAR_NONE, const help_text[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0); +native create_cvar(const name[], const string[], flags = FCVAR_NONE, const description[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0); /** * Registers a new cvar for the engine. @@ -448,10 +448,12 @@ native get_plugins_cvarsnum(); * @param flags Variable to store cvar flags to * @param plugin_id Variable to store id of the registering plugin to * @param pcvar_handle Variable to store cvar pointer to + * @param description Variable to store cvar description to + * @param desc_len Maximum length of string buffer * * @return 1 on success, 0 if index is invalid */ -native get_plugins_cvar(num, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=0); +native get_plugins_cvar(num, name[], namelen, &flags=0, &plugin_id=0, &pcvar_handle=0, description[]="", desc_len=0); /** * Dispatches a client cvar query, allowing the plugin to query for its value on From 356a98116443b0513e7e3ca4fd7b764bfefb9a3e Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sun, 25 Jan 2015 20:15:35 +0100 Subject: [PATCH 13/21] Cvars: Add get/set_pcvar_bool natives --- amxmodx/CvarManager.cpp | 11 ----------- amxmodx/cvars.cpp | 8 ++++++++ plugins/include/cvars.inc | 23 ++++++++++++++++++++++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index d303c063..b5668007 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -234,17 +234,6 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char* // Make sure that whether an existing or new cvar is set to the given value. CVAR_DIRECTSET(var, value); } - else - { - // In case a plugin has been recompiling with some changes - // after a change map occurred, we keep default data up to date. - info->description = helpText; - info->defaultval = value; - info->hasMin = hasMin; - info->minVal = min; - info->hasMax = hasMin; - info->maxVal = max; - } // Detour is disabled on map change. m_HookDetour->EnableDetour(); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 34747121..bf387a50 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -292,6 +292,12 @@ static cell AMX_NATIVE_CALL get_pcvar_num(AMX *amx, cell *params) 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) { @@ -704,12 +710,14 @@ AMX_NATIVE_INFO g_CvarNatives[] = {"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}, diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index 2965f9fb..c7f352d4 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -306,6 +306,16 @@ native set_pcvar_flags(pcvar, flags); */ native get_pcvar_num(pcvar); +/** + * Returns an boolean value from a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to retrieve value from + * + * @return Cvar value, converted to bool + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native bool:get_pcvar_bool(pcvar); + /** * Sets an integer value to a cvar via direct pointer access. * @@ -317,6 +327,17 @@ native get_pcvar_num(pcvar); */ native set_pcvar_num(pcvar, num); +/** + * Sets a boolean value to a cvar via direct pointer access. + * + * @param pcvar Pointer to cvar to set value of + * @param num Value to set cvar to + * + * @noreturn + * @error If an invalid cvar pointer is specified, an error is thrown. + */ +native set_pcvar_bool(pcvar, bool:num); + /** * Returns a float value from a cvar via direct pointer access. * @@ -482,4 +503,4 @@ native get_plugins_cvar(num, name[], namelen, &flags=0, &plugin_id=0, &pcvar_han * unavailable or the querying process runs out of memory, * an error will be thrown. */ -native query_client_cvar(id, const cvar[], const resultFunc[], paramlen=0, const params[]=""); \ No newline at end of file +native query_client_cvar(id, const cvar[], const resultFunc[], paramlen=0, const params[]=""); From d75b14d4afc4f9e402ddde5f4e5dce885838f67f Mon Sep 17 00:00:00 2001 From: Arkshine Date: Tue, 27 Jan 2015 01:49:57 +0100 Subject: [PATCH 14/21] Cvars: Extend "amxx cvars" command output --- amxmodx/CvarManager.cpp | 149 +++++++++++++++++++++++++++++++++----- amxmodx/CvarManager.h | 56 ++++++++++----- amxmodx/cvars.cpp | 29 ++++---- amxmodx/srvcmd.cpp | 16 ++--- public/auto-string.h | 153 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 349 insertions(+), 54 deletions(-) create mode 100644 public/auto-string.h diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index b5668007..b24d79f8 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -10,6 +10,7 @@ #include "CvarManager.h" #include "amxmodx.h" #include +#include CvarManager g_CvarManager; @@ -27,20 +28,20 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) return true; } - if (info->hasMin || info->hasMax) // cvar_s doesn't have min/max mechanism, so we check things here. + if (info->bound.hasMin || info->bound.hasMax) // cvar_s doesn't have min/max mechanism, so we check things here. { float fvalue = atof(value); bool oob = false; - if (info->hasMin && fvalue < info->minVal) + if (info->bound.hasMin && fvalue < info->bound.minVal) { oob = true; - fvalue = info->minVal; + fvalue = info->bound.minVal; } - else if (info->hasMax && fvalue > info->maxVal) + else if (info->bound.hasMax && fvalue > info->bound.maxVal) { oob = true; - fvalue = info->maxVal; + fvalue = info->bound.maxVal; } if (oob) // Found value out of bound, set new value and block original call. @@ -58,7 +59,7 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) { CvarHook* hook = info->hooks[i]; - if (hook->forward->state == Forward::FSTATE_OK) // Our callback can be enable/disabled by natives. + if (hook->forward->state == AutoForward::FSTATE_OK) // Our callback can be enable/disabled by natives. { int result = executeForwards(hook->forward->id, reinterpret_cast(var), var->string, value); @@ -295,7 +296,7 @@ bool CvarManager::CacheLookup(const char* name, CvarInfo** info) return m_Cache.retrieve(name, info); } -Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback) +AutoForward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback) { CvarInfo* info = nullptr; @@ -329,7 +330,7 @@ Forward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, const ch // Detour is disabled on map change. m_HookDetour->EnableDetour(); - Forward* forward = new Forward(forwardId, *callback); + AutoForward* forward = new AutoForward(forwardId, *callback); info->hooks.append(new CvarHook(g_plugins.findPlugin(amx)->getId(), forward)); return forward; @@ -340,26 +341,141 @@ size_t CvarManager::GetRegCvarsCount() return m_AmxmodxCvars; } +AutoString convertFlagsToString(int flags) +{ + AutoString flagsName; + + if (flags > 0) + { + if (flags & FCVAR_ARCHIVE) flagsName = flagsName + "FCVAR_ARCHIVE "; + if (flags & FCVAR_USERINFO) flagsName = flagsName + "FCVAR_USERINFO "; + if (flags & FCVAR_SERVER) flagsName = flagsName + "FCVAR_SERVER "; + if (flags & FCVAR_EXTDLL) flagsName = flagsName + "FCVAR_EXTDLL "; + if (flags & FCVAR_CLIENTDLL) flagsName = flagsName + "FCVAR_CLIENTDLL "; + if (flags & FCVAR_PROTECTED) flagsName = flagsName + "FCVAR_PROTECTED "; + if (flags & FCVAR_SPONLY) flagsName = flagsName + "FCVAR_SPONLY "; + if (flags & FCVAR_PRINTABLEONLY) flagsName = flagsName + "FCVAR_PRINTABLEONLY "; + if (flags & FCVAR_UNLOGGED) flagsName = flagsName + "FCVAR_UNLOGGED "; + if (flags & FCVAR_NOEXTRAWHITEPACE) flagsName = flagsName + "FCVAR_NOEXTRAWHITEPACE "; + } + + if (!flagsName.length()) + { + flagsName = "-"; + } + + return flagsName; +} + void CvarManager::OnConsoleCommand() { - print_srvconsole("Registered cvars:\n"); - print_srvconsole(" %-24.23s %-24.23s %-16.15s\n", "name", "value", "plugin"); - size_t index = 0; - ke::AString pluginName; + size_t indexToSearch = 0; + ke::AString partialName; - if (CMD_ARGC() > 2) // Searching for cvars registered to a plugin + int argcount = CMD_ARGC(); + + // amxx cvars + // E.g.: + // amxx cvars test <- list all cvars from plugin name starting by "test" + // amxx cvars 2 <- show informations about cvar in position 2 from "amxx cvars" list + // amxx cvars test 2 <- show informations about cvar in position 2 from "amxx cvars test" list + + if (argcount > 2) { - pluginName = CMD_ARGV(2); + const char* argument = CMD_ARGV(2); + + indexToSearch = atoi(argument); // amxx cvars 2 + + if (!indexToSearch) + { + partialName = argument; // amxx cvars test + + if (argcount > 3) // amxx cvars test 2 + { + indexToSearch = atoi(CMD_ARGV(3)); + } + } + } + + if (!indexToSearch) + { + print_srvconsole("\nManaged cvars:\n"); + print_srvconsole(" %-24.23s %-24.23s %-18.17s %-8.7s %-8.7s %-8.7s\n", "NAME", "VALUE", "PLUGIN", "BINDED", "HOOKED", "BOUNDED"); + print_srvconsole(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n"); + } for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) { CvarInfo* ci = (*iter); - if (ci->amxmodx && (!pluginName.length() || strncmp(ci->name.chars(), pluginName.chars(), pluginName.length()) == 0)) + // List any cvars having a status either created, hooked, binded or bounded by a plugin. + bool in_list = ci->amxmodx || !ci->binds.empty() || !ci->hooks.empty() || ci->bound.hasMin || ci->bound.hasMax; + + if (in_list && (!partialName.length() || strncmp(ci->plugin.chars(), partialName.chars(), partialName.length()) == 0)) { - print_srvconsole(" [%3d] %-24.23s %-24.23s %-16.15s\n", ++index, ci->name.chars(), ci->var->string, ci->plugin.chars()); + if (!indexToSearch) + { + print_srvconsole(" [%3d] %-24.23s %-24.23s %-18.17s %-8.7s %-8.7s %-8.7s\n", ++index, ci->name.chars(), ci->var->string, + ci->plugin.length() ? ci->plugin.chars() : "-", + ci->binds.empty() ? "no" : "yes", + ci->hooks.empty() ? "no" : "yes", + ci->bound.hasMin || ci->bound.hasMax ? "yes" : "no"); + } + else + { + if (++index != indexToSearch) + { + continue; + } + + print_srvconsole("\nCvar details :\n\n"); + print_srvconsole(" Cvar name : %s\n", ci->var->name); + print_srvconsole(" Value : %s\n", ci->var->string); + print_srvconsole(" Def. value : %s\n", ci->defaultval.chars()); + print_srvconsole(" Description : %s\n", ci->description.chars()); + print_srvconsole(" Flags : %s\n\n", convertFlagsToString(ci->var->flags).ptr()); + + print_srvconsole(" %-12s %-26.25s %s\n", "STATUS", "PLUGIN", "INFOS"); + print_srvconsole(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"); + + if (ci->amxmodx) + { + print_srvconsole(" Registered %-26.25s %s\n", ci->plugin.chars(), "-"); + } + + if (ci->bound.hasMin) + { + print_srvconsole(" Min Bounded %-26.25s %f\n", g_plugins.findPlugin(ci->bound.minPluginId)->getName(), ci->bound.minVal); + } + + if (ci->bound.hasMax) + { + print_srvconsole(" Max Bounded %-26.25s %f\n", g_plugins.findPlugin(ci->bound.maxPluginId)->getName(), ci->bound.maxVal); + } + + if (!ci->binds.empty()) + { + for (size_t i = 0; i < ci->binds.length(); ++i) + { + print_srvconsole(" Binded %-26.25s %s\n", g_plugins.findPlugin(ci->binds[i]->pluginId)->getName(), "-"); + } + } + + if (!ci->hooks.empty()) + { + for (size_t i = 0; i < ci->hooks.length(); ++i) + { + CvarHook* hook = ci->hooks[i]; + + print_srvconsole(" Hooked %-26.25s %s (%s)\n", g_plugins.findPlugin(hook->pluginId)->getName(), + hook->forward->callback.chars(), + hook->forward->state == AutoForward::FSTATE_OK ? "active" : "inactive"); + } + } + break; + } } } } @@ -391,6 +507,7 @@ void CvarManager::OnPluginUnloaded() 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) diff --git a/amxmodx/CvarManager.h b/amxmodx/CvarManager.h index f6c250a5..5da648c7 100644 --- a/amxmodx/CvarManager.h +++ b/amxmodx/CvarManager.h @@ -23,7 +23,7 @@ enum CvarBounds CvarBound_Lower }; -struct Forward +struct AutoForward { enum fwdstate { @@ -32,10 +32,10 @@ struct Forward FSTATE_STOP, }; - Forward(int id_, const char* handler) : id(id_), state(FSTATE_OK), callback(handler) {}; - Forward() : id(-1) , state(FSTATE_INVALID) {}; + AutoForward(int id_, const char* handler) : id(id_), state(FSTATE_OK), callback(handler) {}; + AutoForward() : id(-1) , state(FSTATE_INVALID) {}; - ~Forward() + ~AutoForward() { unregisterSPForward(id); } @@ -47,11 +47,11 @@ struct Forward struct CvarHook { - CvarHook(int id, Forward* fwd) : pluginId(id), forward(fwd) {}; - CvarHook(int id) : pluginId(id), forward(new Forward()) {}; + CvarHook(int id, AutoForward* fwd) : pluginId(id), forward(fwd) {}; + CvarHook(int id) : pluginId(id), forward(new AutoForward()) {}; int pluginId; - ke::AutoPtr forward; + ke::AutoPtr forward; }; struct CvarBind @@ -65,7 +65,10 @@ struct CvarBind CvarBind(int id_, CvarType type_, cell* varAddress_, size_t varLength_) : - pluginId(id_), type(type_), varAddress(varAddress_), varLength(varLength_) {}; + pluginId(id_), + type(type_), + varAddress(varAddress_), + varLength(varLength_) {}; int pluginId; CvarType type; @@ -73,8 +76,30 @@ struct CvarBind size_t varLength; }; +struct CvarBound +{ + CvarBound(bool hasMin_, float minVal_, bool hasMax_, float maxVal_, int minPluginId_, int maxPluginId_) + : + hasMin(hasMin_), hasMax(hasMax_), + minVal(minVal_), maxVal(maxVal_), + minPluginId(minPluginId_), + maxPluginId(maxPluginId_) {}; + + CvarBound() + : + hasMin(false), hasMax(false), + minVal(0), maxVal(0) {}; + + bool hasMin; + bool hasMax; + float minVal; + float maxVal; + int minPluginId; + int maxPluginId; +}; + typedef ke::Vector CvarsHook; -typedef ke::Vector CvarsBind; +typedef ke::Vector CvarsBind; struct CvarInfo : public ke::InlineListNode { @@ -83,29 +108,26 @@ struct CvarInfo : public ke::InlineListNode const char* plugin_, int pluginId_) : name(name_), description(helpText), - hasMin(hasMin_), minVal(min_), hasMax(hasMax_), maxVal(max_), + bound(hasMin_, min_, hasMax_, max_, pluginId_, pluginId_), plugin(plugin_), pluginId(pluginId_) {}; CvarInfo(const char* name_) : name(name_), defaultval(""), description(""), - hasMin(false), minVal(0), hasMax(false), maxVal(0), - plugin(""), pluginId(-1), amxmodx(false) {}; + bound(), plugin(""), pluginId(-1), amxmodx(false) {}; cvar_t* var; ke::AString name; ke::AString defaultval; ke::AString description; - bool hasMin; - bool hasMax; - float minVal; - float maxVal; ke::AString plugin; int pluginId; + CvarBound bound; CvarsBind binds; CvarsHook hooks; + bool amxmodx; static inline bool matches(const char *name, const CvarInfo* info) @@ -137,7 +159,7 @@ class CvarManager CvarInfo* FindCvar(size_t index); bool CacheLookup(const char* name, CvarInfo** info); - Forward* HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback); + AutoForward* HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback); size_t GetRegCvarsCount(); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index bf387a50..18930737 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -92,7 +92,7 @@ static cell AMX_NATIVE_CALL hook_cvar_change(AMX *amx, cell *params) } const char* callback; - Forward* forward = g_CvarManager.HookCvarChange(var, amx, params[2], &callback); + AutoForward* forward = g_CvarManager.HookCvarChange(var, amx, params[2], &callback); if (!forward) { @@ -106,7 +106,7 @@ static cell AMX_NATIVE_CALL hook_cvar_change(AMX *amx, cell *params) // enable_cvar_hook(cvarhook:handle); static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params) { - Forward* forward = reinterpret_cast(params[1]); + AutoForward* forward = reinterpret_cast(params[1]); if (!forward) { @@ -114,7 +114,7 @@ static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params) return 0; } - forward->state = Forward::FSTATE_OK; + forward->state = AutoForward::FSTATE_OK; return 1; } @@ -122,7 +122,7 @@ static cell AMX_NATIVE_CALL enable_cvar_hook(AMX *amx, cell *params) // disable_cvar_hook(cvarhook:handle); static cell AMX_NATIVE_CALL disable_cvar_hook(AMX *amx, cell *params) { - Forward* forward = reinterpret_cast(params[1]); + AutoForward* forward = reinterpret_cast(params[1]); if (!forward) { @@ -130,7 +130,7 @@ static cell AMX_NATIVE_CALL disable_cvar_hook(AMX *amx, cell *params) return 0; } - forward->state = Forward::FSTATE_STOP; + forward->state = AutoForward::FSTATE_STOP; return 1; } @@ -329,12 +329,12 @@ static cell AMX_NATIVE_CALL get_pcvar_bounds(AMX *amx, cell *params) switch (params[2]) { case CvarBound_Lower: - hasBound = info->hasMin; - bound = info->minVal; + hasBound = info->bound.hasMin; + bound = info->bound.minVal; break; case CvarBound_Upper: - hasBound = info->hasMax; - bound = info->maxVal; + hasBound = info->bound.hasMax; + bound = info->bound.maxVal; break; default: LogError(amx, AMX_ERR_NATIVE, "Invalid CvarBounds value: %d", params[2]); @@ -520,16 +520,19 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) } bool set = params[3] > 0 ? true : false; + int pluginId = g_plugins.findPluginFast(amx)->getId(); switch (params[2]) { case CvarBound_Lower: - info->hasMin = set; - info->minVal = set ? amx_ctof(params[4]) : 0; + info->bound.hasMin = set; + info->bound.minVal = set ? amx_ctof(params[4]) : 0; + info->bound.minPluginId = pluginId; break; case CvarBound_Upper: - info->hasMax = set; - info->maxVal = set ? amx_ctof(params[4]) : 0; + info->bound.hasMax = set; + info->bound.maxVal = set ? amx_ctof(params[4]) : 0; + info->bound.maxPluginId = pluginId; break; default: LogError(amx, AMX_ERR_NATIVE, "Invalid CvarBounds value: %d", params[2]); diff --git a/amxmodx/srvcmd.cpp b/amxmodx/srvcmd.cpp index 847b7f28..be619d44 100755 --- a/amxmodx/srvcmd.cpp +++ b/amxmodx/srvcmd.cpp @@ -232,14 +232,14 @@ void amx_command() } else { print_srvconsole("Usage: amxx < command > [ argument ]\n"); print_srvconsole("Commands:\n"); - print_srvconsole(" version - display amxx version info\n"); - print_srvconsole(" gpl - print the license\n"); - print_srvconsole(" plugins - list plugins currently loaded\n"); - print_srvconsole(" modules - list modules currently loaded\n"); - print_srvconsole(" cvars [ plugin ] - list cvars registered by plugins\n"); - print_srvconsole(" cmds [ plugin ] - list commands registered by plugins\n"); - print_srvconsole(" pause < plugin > - pause a running plugin\n"); - print_srvconsole(" unpause < plugin > - unpause a previously paused plugin\n"); + print_srvconsole(" version - display amxx version info\n"); + print_srvconsole(" gpl - print the license\n"); + print_srvconsole(" plugins - list plugins currently loaded\n"); + print_srvconsole(" modules - list modules currently loaded\n"); + print_srvconsole(" cvars [ plugin ] [ index ] - list cvars handled by amxx or show informations about a cvar if index is provided\n"); + print_srvconsole(" cmds [ plugin ] - list commands registered by plugins\n"); + print_srvconsole(" pause < plugin > - pause a running plugin\n"); + print_srvconsole(" unpause < plugin > - unpause a previously paused plugin\n"); } } diff --git a/public/auto-string.h b/public/auto-string.h new file mode 100644 index 00000000..75e4f8f0 --- /dev/null +++ b/public/auto-string.h @@ -0,0 +1,153 @@ +/* vim: set ts=2 sw=2 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn 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 + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_auto_string_h_ +#define _include_auto_string_h_ + +#include +#include +#include +#include + +using namespace ke; + +class AutoString +{ + public: + AutoString() : ptr_(NULL), length_(0) + { + } + AutoString(AutoString &&other) + : ptr_(other.ptr_), + length_(other.length_) + { + other.ptr_ = nullptr; + other.length_ = 0; + } + AutoString(const char *ptr) + { + assign(ptr); + } + AutoString(const AString &str) + { + assign(str.chars(), str.length()); + } + AutoString(const char *ptr, size_t len) + { + assign(ptr, len); + } + AutoString(const AutoString &other) + { + assign(other.ptr(), other.length()); + } + ~AutoString() + { + free(ptr_); + } + + AutoString &operator =(const char *ptr) { + free(ptr_); + assign(ptr); + return *this; + } + AutoString &operator =(const AutoString &other) { + free(ptr_); + assign(other.ptr(), other.length()); + return *this; + } + AutoString &operator =(AutoString &&other) { + Swap(other.ptr_, ptr_); + Swap(other.length_, length_); + return *this; + } + + AutoString operator +(const AutoString &other) const { + size_t len = length() + other.length(); + char *buf = (char *)malloc(len + 1); + memcpy(buf, ptr(), length()); + memcpy(buf + length(), other.ptr(), other.length()); + buf[len] = '\0'; + + AutoString r; + r.ptr_ = buf; + r.length_ = len; + return r; + } + + AutoString operator +(const char *other) const { + size_t other_length = strlen(other); + size_t len = length() + other_length; + char *buf = (char *)malloc(len + 1); + memcpy(buf, ptr(), length()); + memcpy(buf + length(), other, other_length); + buf[len] = '\0'; + + AutoString r; + r.ptr_ = buf; + r.length_ = len; + return r; + } + + AutoString operator +(unsigned val) const { + char buffer[24]; + _snprintf(buffer, sizeof(buffer), "%d", val); + return *this + buffer; + } + + size_t length() const { + return length_; + } + + bool operator !() const { + return !length_; + } + + const char *ptr() const { + return ptr_ ? ptr_ : ""; + } + operator const char *() const { + return ptr(); + } + + private: + void assign(const char *ptr) { + if (!ptr) { + ptr_ = NULL; + length_ = 0; + return; + } + assign(ptr, strlen(ptr)); + } + void assign(const char *ptr, size_t length) { + if (!ptr) { + ptr_ = NULL; + length_ = 0; + return; + } + length_ = length; + ptr_ = (char *)malloc(length_ + 1); + memcpy(ptr_, ptr, length_); + ptr_[length_] = '\0'; + } + + private: + char *ptr_; + size_t length_; +}; + +#endif // _include_spcomp_auto_string_h_ From 1a5e1928ec51bcb4aa30bf6467a1258135cc0205 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Tue, 27 Jan 2015 16:27:26 +0100 Subject: [PATCH 15/21] Cvars: Fix field orders and linux compilation --- amxmodx/CvarManager.h | 16 ++++++++-------- public/memtools/CDetour/detourhelpers.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/amxmodx/CvarManager.h b/amxmodx/CvarManager.h index 5da648c7..d0312c1c 100644 --- a/amxmodx/CvarManager.h +++ b/amxmodx/CvarManager.h @@ -80,19 +80,19 @@ struct CvarBound { CvarBound(bool hasMin_, float minVal_, bool hasMax_, float maxVal_, int minPluginId_, int maxPluginId_) : - hasMin(hasMin_), hasMax(hasMax_), - minVal(minVal_), maxVal(maxVal_), + hasMin(hasMin_), minVal(minVal_), + hasMax(hasMax_), maxVal(maxVal_), minPluginId(minPluginId_), maxPluginId(maxPluginId_) {}; CvarBound() : - hasMin(false), hasMax(false), - minVal(0), maxVal(0) {}; + hasMin(false), minVal(0), + hasMax(false), maxVal(0) {}; bool hasMin; - bool hasMax; float minVal; + bool hasMax; float maxVal; int minPluginId; int maxPluginId; @@ -108,13 +108,13 @@ struct CvarInfo : public ke::InlineListNode const char* plugin_, int pluginId_) : name(name_), description(helpText), - bound(hasMin_, min_, hasMax_, max_, pluginId_, pluginId_), - plugin(plugin_), pluginId(pluginId_) {}; + plugin(plugin_), pluginId(pluginId_), + bound(hasMin_, min_, hasMax_, max_, pluginId_, pluginId_) {}; CvarInfo(const char* name_) : name(name_), defaultval(""), description(""), - bound(), plugin(""), pluginId(-1), amxmodx(false) {}; + plugin(""), pluginId(-1), bound(), amxmodx(false) {}; cvar_t* var; ke::AString name; diff --git a/public/memtools/CDetour/detourhelpers.h b/public/memtools/CDetour/detourhelpers.h index 291d8df2..1ec49e8e 100644 --- a/public/memtools/CDetour/detourhelpers.h +++ b/public/memtools/CDetour/detourhelpers.h @@ -34,6 +34,7 @@ #if defined(__linux__) || defined(__APPLE__) #include + #include #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif From e4a7e677833895c53aa9437c2abaa4a16916b0e3 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Wed, 28 Jan 2015 00:25:27 +0100 Subject: [PATCH 16/21] Cvars: Adjust few things - Hook is not as post, because allowing blocking change is not really that useful and this makes a context more simpler. - Setting a min bound > max bound and reversely throw an error - set_pcvar_bounds set cvar value on new min/max bound. - Remove error "A cvar can't be binded with several variables", it's okay to no error such situation. --- amxmodx/CvarManager.cpp | 74 +++++++++---------------- amxmodx/cvars.cpp | 58 +++++++++++++++---- plugins/include/cvars.inc | 3 + public/memtools/CDetour/detourhelpers.h | 2 +- 4 files changed, 79 insertions(+), 58 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index b24d79f8..1843b5cf 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -14,10 +14,7 @@ CvarManager g_CvarManager; -/** - * Returns true to call original function, otherwise false to block it. - */ -bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) +DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) { CvarInfo* info = nullptr; @@ -25,12 +22,13 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) || strcmp(var->string, value) == 0 // Make sure old and new values are different to not trigger callbacks. || !g_CvarManager.CacheLookup(var->name, &info)) // No data in cache, nothing to do. { - return true; + DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + return; } if (info->bound.hasMin || info->bound.hasMax) // cvar_s doesn't have min/max mechanism, so we check things here. { - float fvalue = atof(value); + float fvalue = atof(value); bool oob = false; if (info->bound.hasMin && fvalue < info->bound.minVal) @@ -47,36 +45,20 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) if (oob) // Found value out of bound, set new value and block original call. { CVAR_SET_FLOAT(var->name, fvalue); - return false; + return; } } - + + ke::AString oldValue; // We save old value since it will be likely changed after original function called. + if (!info->hooks.empty()) { - int lastResult = 0; - - for (size_t i = 0; i < info->hooks.length(); ++i) - { - CvarHook* hook = info->hooks[i]; - - if (hook->forward->state == AutoForward::FSTATE_OK) // Our callback can be enable/disabled by natives. - { - int result = executeForwards(hook->forward->id, reinterpret_cast(var), var->string, value); - - if (result >= lastResult) - { - lastResult = result; - } - } - } - - if (lastResult) // A plugin wants to block the forward. - { - return false; - } + oldValue = var->string; } - if (!info->binds.empty()) // Time to update our available binds. + DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + + if (!info->binds.empty()) { for (size_t i = 0; i < info->binds.length(); ++i) { @@ -86,33 +68,35 @@ bool Cvar_DirectSet_Custom(cvar_t* var, const char* value) { case CvarBind::CvarType_Int: { - *bind->varAddress = atoi(value); + *bind->varAddress = atoi(var->string); break; } case CvarBind::CvarType_Float: { - float fvalue = atof(value); + float fvalue = atof(var->string); *bind->varAddress = amx_ftoc(fvalue); break; } case CvarBind::CvarType_String: { - set_amxstring_simple(bind->varAddress, value, bind->varLength); + set_amxstring_simple(bind->varAddress, var->string, bind->varLength); break; } } } } - // Nothing to block. - return true; -} - -DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) -{ - if (Cvar_DirectSet_Custom(var, value)) + if (!info->hooks.empty()) { - DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); + for (size_t i = 0; i < info->hooks.length(); ++i) + { + CvarHook* hook = info->hooks[i]; + + if (hook->forward->state == AutoForward::FSTATE_OK) // Our callback can be enable/disabled by natives. + { + executeForwards(hook->forward->id, reinterpret_cast(var), oldValue.chars(), var->string); + } + } } } @@ -165,12 +149,8 @@ void CvarManager::CreateCvarHook(void) if (functionAddress) { + // Disabled by default. m_HookDetour = DETOUR_CREATE_STATIC_FIXED(Cvar_DirectSet, (void *)functionAddress); - - if (m_HookDetour) - { - m_HookDetour->EnableDetour(); - } } } @@ -375,7 +355,7 @@ void CvarManager::OnConsoleCommand() int argcount = CMD_ARGC(); - // amxx cvars + // amxx cvars [partial plugin name] [index from listing] // E.g.: // amxx cvars test <- list all cvars from plugin name starting by "test" // amxx cvars 2 <- show informations about cvar in position 2 from "amxx cvars" list diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 18930737..3d16eea8 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -367,13 +367,8 @@ bool bind_pcvar(CvarInfo* info, CvarBind::CvarType type, AMX* amx, cell varofs, if (bind->varAddress == address) { LogError(amx, AMX_ERR_NATIVE, "A same variable can't be binded with several cvars"); + return false; } - else - { - LogError(amx, AMX_ERR_NATIVE, "A cvar can't be binded with several variables"); - } - - return false; } } @@ -521,22 +516,65 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) bool set = params[3] > 0 ? true : false; int pluginId = g_plugins.findPluginFast(amx)->getId(); + float value = 0; + bool should_update = false; switch (params[2]) { case CvarBound_Lower: + { info->bound.hasMin = set; - info->bound.minVal = set ? amx_ctof(params[4]) : 0; - info->bound.minPluginId = pluginId; + + if (set) + { + value = amx_ctof(params[4]); + + if (info->bound.hasMax && value > info->bound.maxVal) + { + LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); + return 0; + } + + should_update = true; + + info->bound.minVal = value; + info->bound.minPluginId = pluginId; + } + break; + } case CvarBound_Upper: + { info->bound.hasMax = set; - info->bound.maxVal = set ? amx_ctof(params[4]) : 0; - info->bound.maxPluginId = pluginId; + + if (set) + { + value = amx_ctof(params[4]); + + if (info->bound.hasMin && value < info->bound.minVal) + { + LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); + return 0; + } + + should_update = true; + + info->bound.maxVal = value; + info->bound.maxPluginId = pluginId; + } + break; + } default: + { LogError(amx, AMX_ERR_NATIVE, "Invalid CvarBounds value: %d", params[2]); return 0; + } + } + + if (should_update) + { + CVAR_SET_FLOAT(ptr->name, value); } return 1; diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index c7f352d4..ced2f878 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -91,6 +91,9 @@ native get_cvar_pointer(const cvar[]); /** * Creates a hook for when a cvar's value is changed. * + * @note You cannot prevent cvar changes from happening, you can only change the value again. + * @note Be careful not to set a cvar inside a cvar change hook such that + * it re-invokes he same callback. This results in infinite recursion. * @note Callback will be called in the following manner: * * public cvar_change_callback(pcvar, const old_value[], const new_value[]) diff --git a/public/memtools/CDetour/detourhelpers.h b/public/memtools/CDetour/detourhelpers.h index 1ec49e8e..b500cff7 100644 --- a/public/memtools/CDetour/detourhelpers.h +++ b/public/memtools/CDetour/detourhelpers.h @@ -34,7 +34,7 @@ #if defined(__linux__) || defined(__APPLE__) #include - #include + #include #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif From ac50f3ff34f774c25acafd45a6c35e5623457fce Mon Sep 17 00:00:00 2001 From: Arkshine Date: Wed, 28 Jan 2015 00:47:01 +0100 Subject: [PATCH 17/21] Cvars: Check lower/upper bounds in create_cvar as well --- amxmodx/cvars.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 3d16eea8..10486ed4 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -25,8 +25,30 @@ static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params) int flags = params[3]; bool hasMin = params[5] > 0 ? true : false; bool hasMax = params[7] > 0 ? true : false; - float minVal = amx_ctof(params[6]); - float maxVal = amx_ctof(params[8]); + float minVal = 0; + float maxVal = 0; + + if (hasMin) + { + minVal = amx_ctof(params[6]); + + if (hasMax && minVal > maxVal) + { + LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); + return 0; + } + } + + if (hasMax) + { + maxVal = amx_ctof(params[8]); + + if (hasMin && maxVal < minVal) + { + LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); + return 0; + } + } CPluginMngr::CPlugin *plugin = g_plugins.findPluginFast(amx); From 187b187ef284b3f404850b1e800c02377f81193e Mon Sep 17 00:00:00 2001 From: Arkshine Date: Wed, 28 Jan 2015 00:58:07 +0100 Subject: [PATCH 18/21] Cvars: To not traumatize Nextra --- amxmodx/cvars.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 10486ed4..84e37482 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -23,8 +23,8 @@ static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params) const char* helpText = get_amxstring(amx, params[4], 2, length); int flags = params[3]; - bool hasMin = params[5] > 0 ? true : false; - bool hasMax = params[7] > 0 ? true : false; + bool hasMin = params[5] != 0; + bool hasMax = params[7] != 0; float minVal = 0; float maxVal = 0; @@ -536,7 +536,7 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) return 0; } - bool set = params[3] > 0 ? true : false; + bool set = params[3] != 0; int pluginId = g_plugins.findPluginFast(amx)->getId(); float value = 0; bool should_update = false; From f8baef15ca1ad33f7aab62556023f612ab1c3633 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Wed, 28 Jan 2015 23:51:43 +0100 Subject: [PATCH 19/21] Cvars: Adjust more things - Moved some functions in CvarManager - Fixed detour enabled all the time - Fixed bounds not properly handled - Refreshed data of regisetered cvars at map change --- amxmodx/CvarManager.cpp | 128 +++++++++++++++++++++++++++++-- amxmodx/CvarManager.h | 5 +- amxmodx/cvars.cpp | 162 +++++++++++----------------------------- 3 files changed, 168 insertions(+), 127 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index 1843b5cf..88c5c196 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -154,7 +154,7 @@ void CvarManager::CreateCvarHook(void) } } -cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, int flags, +CvarInfo* CvarManager::CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, int flags, const char* helpText, bool hasMin, float min, bool hasMax, float max) { cvar_t* var = nullptr; @@ -215,11 +215,27 @@ cvar_t* CvarManager::CreateCvar(const char* name, const char* value, const char* // Make sure that whether an existing or new cvar is set to the given value. CVAR_DIRECTSET(var, value); } + else if (info->pluginId == -1) + { + // In situation where a plugin has been modified/recompiled + // or new added plugins, and a change map occurs. We want to keep data up to date. + info->bound.hasMin = hasMin; + info->bound.minVal = min; + info->bound.hasMax = hasMax; + info->bound.maxVal = max; + info->defaultval = value; + info->description = helpText; + info->pluginId = pluginId; + } // Detour is disabled on map change. - m_HookDetour->EnableDetour(); + // Don't enable it unless there are things to do. + if (info->bound.hasMin || info->bound.hasMax) + { + m_HookDetour->EnableDetour(); + } - return info->var; + return info; } CvarInfo* CvarManager::FindCvar(const char* name) @@ -227,9 +243,6 @@ 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)) { @@ -316,6 +329,104 @@ AutoForward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, cons return forward; } +bool CvarManager::BindCvar(CvarInfo* info, CvarBind::CvarType type, AMX* amx, cell varofs, size_t varlen) +{ + 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"); + 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; + } + + // Detour is disabled on map change. + m_HookDetour->EnableDetour(); + + return true; +} + +bool CvarManager::SetCvarMin(CvarInfo* info, bool set, float value, int pluginId) +{ + info->bound.hasMin = set; + info->bound.minPluginId = pluginId; + + if (set) + { + if (info->bound.hasMax && value > info->bound.maxVal) + { + return false; + } + + info->bound.minVal = value; + + // Detour is disabled on map change. + m_HookDetour->EnableDetour(); + + // Update if needed. + CVAR_SET_FLOAT(info->var->name, value); + } + + return true; +} + +bool CvarManager::SetCvarMax(CvarInfo* info, bool set, float value, int pluginId) +{ + info->bound.hasMax = set; + info->bound.maxPluginId = pluginId; + + if (set) + { + if (info->bound.hasMin && value < info->bound.minVal) + { + return false; + } + + info->bound.maxVal = value; + + // Detour is disabled on map change. + m_HookDetour->EnableDetour(); + + // Update if needed. + CVAR_SET_FLOAT(info->var->name, value); + } + + return true; +} + size_t CvarManager::GetRegCvarsCount() { return m_AmxmodxCvars; @@ -475,6 +586,11 @@ void CvarManager::OnPluginUnloaded() delete (*cvar)->hooks[i]; } + if ((*cvar)->amxmodx) // Mark registered cvars so we can refresh default datas at next map. + { + (*cvar)->pluginId = -1; + } + (*cvar)->binds.clear(); (*cvar)->hooks.clear(); } diff --git a/amxmodx/CvarManager.h b/amxmodx/CvarManager.h index d0312c1c..9c67f436 100644 --- a/amxmodx/CvarManager.h +++ b/amxmodx/CvarManager.h @@ -150,7 +150,7 @@ class CvarManager void CreateCvarHook(); - cvar_t* CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, + CvarInfo* CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, int flags = 0, const char* helpText = "", bool hasMin = false, float min = 0, bool hasMax = false, float max = 0); @@ -160,6 +160,9 @@ class CvarManager bool CacheLookup(const char* name, CvarInfo** info); AutoForward* HookCvarChange(cvar_t* var, AMX* amx, cell param, const char** callback); + bool BindCvar(CvarInfo* info, CvarBind::CvarType type, AMX* amx, cell varofs, size_t varlen = 0); + bool SetCvarMin(CvarInfo* info, bool set, float value, int pluginId); + bool SetCvarMax(CvarInfo* info, bool set, float value, int pluginId); size_t GetRegCvarsCount(); diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 84e37482..339166da 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -22,34 +22,8 @@ static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params) const char* value = get_amxstring(amx, params[2], 1, length); const char* helpText = get_amxstring(amx, params[4], 2, length); - int flags = params[3]; - bool hasMin = params[5] != 0; - bool hasMax = params[7] != 0; - float minVal = 0; - float maxVal = 0; - - if (hasMin) - { - minVal = amx_ctof(params[6]); - - if (hasMax && minVal > maxVal) - { - LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); - return 0; - } - } + int flags = params[3]; - if (hasMax) - { - maxVal = amx_ctof(params[8]); - - if (hasMin && maxVal < minVal) - { - LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); - return 0; - } - } - CPluginMngr::CPlugin *plugin = g_plugins.findPluginFast(amx); if (CheckBadConList(name, 0)) @@ -57,9 +31,31 @@ static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params) plugin->AddToFailCounter(1); } - cvar_t* var = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags, helpText, hasMin, minVal, hasMax, maxVal); + CvarInfo* info = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags, helpText); - return reinterpret_cast(var); + 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 (!g_CvarManager.SetCvarMin(info, hasMin, minVal, plugin->getId())) + { + LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); + return 0; + } + + if (!g_CvarManager.SetCvarMax(info, hasMax, maxVal, plugin->getId())) + { + LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); + return 0; + } + + return reinterpret_cast(info->var); + } + + return 0; } // register_cvar(const name[], const string[], flags=0, Float:fvalue=0.0) @@ -79,9 +75,14 @@ static cell AMX_NATIVE_CALL register_cvar(AMX *amx, cell *params) plugin->AddToFailCounter(1); } - cvar_t* var = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags); + CvarInfo* info = g_CvarManager.CreateCvar(name, value, plugin->getName(), plugin->getId(), flags); - return reinterpret_cast(var); + if (info) + { + return reinterpret_cast(info->var); + } + + return 0; } // cvar_exists(const cvar[]) @@ -368,53 +369,6 @@ 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"); - 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) { @@ -427,7 +381,7 @@ static cell AMX_NATIVE_CALL bind_pcvar_float(AMX *amx, cell *params) return 0; } - return bind_pcvar(info, CvarBind::CvarType_Float, amx, params[2]); + return g_CvarManager.BindCvar(info, CvarBind::CvarType_Float, amx, params[2]); } // bind_pcvar_num(pcvar, &any:var) @@ -442,7 +396,7 @@ static cell AMX_NATIVE_CALL bind_pcvar_num(AMX *amx, cell *params) return 0; } - return bind_pcvar(info, CvarBind::CvarType_Int, amx, params[2]); + return g_CvarManager.BindCvar(info, CvarBind::CvarType_Int, amx, params[2]); } // bind_pcvar_string(pcvar, any:var[], varlen) @@ -457,7 +411,7 @@ static cell AMX_NATIVE_CALL bind_pcvar_string(AMX *amx, cell *params) return 0; } - return bind_pcvar(info, CvarBind::CvarType_String, amx, params[2], params[3]); + return g_CvarManager.BindCvar(info, CvarBind::CvarType_String, amx, params[2], params[3]); } // set_pcvar_flags(pcvar, flags) @@ -538,53 +492,26 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) bool set = params[3] != 0; int pluginId = g_plugins.findPluginFast(amx)->getId(); - float value = 0; - bool should_update = false; + float value = amx_ctof(params[4]); switch (params[2]) { case CvarBound_Lower: { - info->bound.hasMin = set; - - if (set) + if (!g_CvarManager.SetCvarMin(info, set, value, pluginId)) { - value = amx_ctof(params[4]); - - if (info->bound.hasMax && value > info->bound.maxVal) - { - LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); - return 0; - } - - should_update = true; - - info->bound.minVal = value; - info->bound.minPluginId = pluginId; + LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); + return 0; } - break; } case CvarBound_Upper: { - info->bound.hasMax = set; - - if (set) + if (!g_CvarManager.SetCvarMax(info, set, value, pluginId)) { - value = amx_ctof(params[4]); - - if (info->bound.hasMin && value < info->bound.minVal) - { - LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); - return 0; - } - - should_update = true; - - info->bound.maxVal = value; - info->bound.maxPluginId = pluginId; + LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); + return 0; } - break; } default: @@ -594,11 +521,6 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) } } - if (should_update) - { - CVAR_SET_FLOAT(ptr->name, value); - } - return 1; } From a5b5c7e9cddef0c1081f678d5d61552b0db38c1b Mon Sep 17 00:00:00 2001 From: Arkshine Date: Thu, 29 Jan 2015 14:01:58 +0100 Subject: [PATCH 20/21] Cvars: Fix code to avoid Nextra be confused --- amxmodx/CvarManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index 88c5c196..d0d0d187 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -219,10 +219,10 @@ CvarInfo* CvarManager::CreateCvar(const char* name, const char* value, const cha { // In situation where a plugin has been modified/recompiled // or new added plugins, and a change map occurs. We want to keep data up to date. - info->bound.hasMin = hasMin; - info->bound.minVal = min; - info->bound.hasMax = hasMax; - info->bound.maxVal = max; + info->bound.hasMin = false; + info->bound.minVal = 0; + info->bound.hasMax = false; + info->bound.maxVal = 0; info->defaultval = value; info->description = helpText; info->pluginId = pluginId; From 1488b9747f9d4fcf95f711809a7eb31bc87b73b0 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Thu, 29 Jan 2015 21:44:53 +0100 Subject: [PATCH 21/21] Cvars: De-frenchify by Nextra --- amxmodx/CvarManager.cpp | 41 +++++++++++----------- amxmodx/cvars.cpp | 15 ++++---- amxmodx/srvcmd.cpp | 53 ++++++++++++++-------------- plugins/include/cvars.inc | 73 +++++++++++++++++++++++---------------- 4 files changed, 99 insertions(+), 83 deletions(-) diff --git a/amxmodx/CvarManager.cpp b/amxmodx/CvarManager.cpp index d0d0d187..13cafa36 100644 --- a/amxmodx/CvarManager.cpp +++ b/amxmodx/CvarManager.cpp @@ -48,9 +48,9 @@ DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, valu return; } } - + ke::AString oldValue; // We save old value since it will be likely changed after original function called. - + if (!info->hooks.empty()) { oldValue = var->string; @@ -58,7 +58,7 @@ DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, valu DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); - if (!info->binds.empty()) + if (!info->binds.empty()) { for (size_t i = 0; i < info->binds.length(); ++i) { @@ -155,7 +155,7 @@ void CvarManager::CreateCvarHook(void) } CvarInfo* CvarManager::CreateCvar(const char* name, const char* value, const char* plugin, int pluginId, int flags, - const char* helpText, bool hasMin, float min, bool hasMax, float max) + const char* helpText, bool hasMin, float min, bool hasMax, float max) { cvar_t* var = nullptr; CvarInfo* info = nullptr; @@ -294,8 +294,8 @@ AutoForward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, cons CvarInfo* info = nullptr; // A cvar is guaranteed to be in cache if pointer is got from - // get_cvar_pointer and register_cvar natives. Though it might be - // provided by another way. If by any chance we run in such + // get_cvar_pointer and register_cvar natives. Though it might be + // provided by another way. If by any chance we run in such // situation, we create a new entry right now. if (!CacheLookup(var->name, &info)) @@ -322,7 +322,7 @@ AutoForward* CvarManager::HookCvarChange(cvar_t* var, AMX* amx, cell param, cons // Detour is disabled on map change. m_HookDetour->EnableDetour(); - + AutoForward* forward = new AutoForward(forwardId, *callback); info->hooks.append(new CvarHook(g_plugins.findPlugin(amx)->getId(), forward)); @@ -333,7 +333,7 @@ bool CvarManager::BindCvar(CvarInfo* info, CvarBind::CvarType type, AMX* amx, ce { 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"); + LogError(amx, AMX_ERR_NATIVE, "Cvars can only be bound to global variables"); return false; } @@ -349,7 +349,7 @@ bool CvarManager::BindCvar(CvarInfo* info, CvarBind::CvarType type, AMX* amx, ce { if (bind->varAddress == address) { - LogError(amx, AMX_ERR_NATIVE, "A same variable can't be binded with several cvars"); + LogError(amx, AMX_ERR_NATIVE, "A global variable can not be bound to multiple Cvars"); return false; } } @@ -392,7 +392,7 @@ bool CvarManager::SetCvarMin(CvarInfo* info, bool set, float value, int pluginId } info->bound.minVal = value; - + // Detour is disabled on map change. m_HookDetour->EnableDetour(); @@ -475,7 +475,7 @@ void CvarManager::OnConsoleCommand() if (argcount > 2) { const char* argument = CMD_ARGV(2); - + indexToSearch = atoi(argument); // amxx cvars 2 if (!indexToSearch) @@ -492,16 +492,15 @@ void CvarManager::OnConsoleCommand() if (!indexToSearch) { print_srvconsole("\nManaged cvars:\n"); - print_srvconsole(" %-24.23s %-24.23s %-18.17s %-8.7s %-8.7s %-8.7s\n", "NAME", "VALUE", "PLUGIN", "BINDED", "HOOKED", "BOUNDED"); + print_srvconsole(" %-24.23s %-24.23s %-18.17s %-8.7s %-8.7s %-8.7s\n", "NAME", "VALUE", "PLUGIN", "BOUND", "HOOKED", "BOUNDED"); print_srvconsole(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n"); - } for (CvarsList::iterator iter = m_Cvars.begin(); iter != m_Cvars.end(); iter++) { CvarInfo* ci = (*iter); - // List any cvars having a status either created, hooked, binded or bounded by a plugin. + // List any cvars having a status either created, hooked or bound by a plugin. bool in_list = ci->amxmodx || !ci->binds.empty() || !ci->hooks.empty() || ci->bound.hasMin || ci->bound.hasMax; if (in_list && (!partialName.length() || strncmp(ci->plugin.chars(), partialName.chars(), partialName.length()) == 0)) @@ -514,8 +513,8 @@ void CvarManager::OnConsoleCommand() ci->hooks.empty() ? "no" : "yes", ci->bound.hasMin || ci->bound.hasMax ? "yes" : "no"); } - else - { + else + { if (++index != indexToSearch) { continue; @@ -530,7 +529,7 @@ void CvarManager::OnConsoleCommand() print_srvconsole(" %-12s %-26.25s %s\n", "STATUS", "PLUGIN", "INFOS"); print_srvconsole(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"); - + if (ci->amxmodx) { print_srvconsole(" Registered %-26.25s %s\n", ci->plugin.chars(), "-"); @@ -538,19 +537,19 @@ void CvarManager::OnConsoleCommand() if (ci->bound.hasMin) { - print_srvconsole(" Min Bounded %-26.25s %f\n", g_plugins.findPlugin(ci->bound.minPluginId)->getName(), ci->bound.minVal); + print_srvconsole(" Min value %-26.25s %f\n", g_plugins.findPlugin(ci->bound.minPluginId)->getName(), ci->bound.minVal); } if (ci->bound.hasMax) { - print_srvconsole(" Max Bounded %-26.25s %f\n", g_plugins.findPlugin(ci->bound.maxPluginId)->getName(), ci->bound.maxVal); + print_srvconsole(" Max value %-26.25s %f\n", g_plugins.findPlugin(ci->bound.maxPluginId)->getName(), ci->bound.maxVal); } if (!ci->binds.empty()) { for (size_t i = 0; i < ci->binds.length(); ++i) { - print_srvconsole(" Binded %-26.25s %s\n", g_plugins.findPlugin(ci->binds[i]->pluginId)->getName(), "-"); + print_srvconsole(" Bound %-26.25s %s\n", g_plugins.findPlugin(ci->binds[i]->pluginId)->getName(), "-"); } } @@ -561,7 +560,7 @@ void CvarManager::OnConsoleCommand() CvarHook* hook = ci->hooks[i]; print_srvconsole(" Hooked %-26.25s %s (%s)\n", g_plugins.findPlugin(hook->pluginId)->getName(), - hook->forward->callback.chars(), + hook->forward->callback.chars(), hook->forward->state == AutoForward::FSTATE_OK ? "active" : "inactive"); } } diff --git a/amxmodx/cvars.cpp b/amxmodx/cvars.cpp index 339166da..c3d43690 100644 --- a/amxmodx/cvars.cpp +++ b/amxmodx/cvars.cpp @@ -12,7 +12,7 @@ #include "nongpl_matches.h" char CVarTempBuffer[64]; -const char *invis_cvar_list[5] = { "amxmodx_version", "amxmodx_modules", "amx_debug", "amx_mldebug", "amx_client_languages" }; +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) @@ -23,7 +23,7 @@ static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params) 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)) @@ -42,13 +42,13 @@ static cell AMX_NATIVE_CALL create_cvar(AMX *amx, cell *params) if (!g_CvarManager.SetCvarMin(info, hasMin, minVal, plugin->getId())) { - LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); + LogError(amx, AMX_ERR_NATIVE, "The minimum value can not be above the maximum value"); return 0; } if (!g_CvarManager.SetCvarMax(info, hasMax, maxVal, plugin->getId())) { - LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); + LogError(amx, AMX_ERR_NATIVE, "The maximum value can not be below the minimum value"); return 0; } @@ -500,7 +500,7 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) { if (!g_CvarManager.SetCvarMin(info, set, value, pluginId)) { - LogError(amx, AMX_ERR_NATIVE, "A lower bound can't be above an upper bound"); + LogError(amx, AMX_ERR_NATIVE, "The minimum value can not be above the maximum value"); return 0; } break; @@ -509,7 +509,7 @@ static cell AMX_NATIVE_CALL set_pcvar_bounds(AMX *amx, cell *params) { if (!g_CvarManager.SetCvarMax(info, set, value, pluginId)) { - LogError(amx, AMX_ERR_NATIVE, "An upper bound can't be below a lower bound"); + LogError(amx, AMX_ERR_NATIVE, "The maximum value can not be below the minimum value"); return 0; } break; @@ -670,7 +670,6 @@ static cell AMX_NATIVE_CALL query_client_cvar(AMX *amx, cell *params) return 1; } - AMX_NATIVE_INFO g_CvarNatives[] = { {"create_cvar", create_cvar}, @@ -714,7 +713,7 @@ AMX_NATIVE_INFO g_CvarNatives[] = {"get_plugins_cvar", get_plugins_cvar}, {"get_plugins_cvarsnum", get_plugins_cvarsnum}, - + {"query_client_cvar", query_client_cvar}, {NULL, NULL} diff --git a/amxmodx/srvcmd.cpp b/amxmodx/srvcmd.cpp index be619d44..6c6d5211 100755 --- a/amxmodx/srvcmd.cpp +++ b/amxmodx/srvcmd.cpp @@ -13,10 +13,9 @@ void amx_command() { const char* cmd = CMD_ARGV(1); - + if (!strcmp(cmd, "plugins") || !strcmp(cmd, "list")) { - print_srvconsole("Currently loaded plugins:\n"); print_srvconsole(" %-23.22s %-11.10s %-17.16s %-16.15s %-9.8s\n", "name", "version", "author", "file", "status"); @@ -24,11 +23,11 @@ void amx_command() int running = 0; CPluginMngr::iterator a = g_plugins.begin(); - - while (a) + + while (a) { ++plugins; - if ((*a).isValid() && !(*a).isPaused()) + if ((*a).isValid() && !(*a).isPaused()) ++running; print_srvconsole(" [%3d] %-23.22s %-11.10s %-17.16s %-16.15s %-9.8s\n", plugins, (*a).getTitle(), (*a).getVersion(), (*a).getAuthor(), (*a).getName(), (*a).getStatus()); @@ -45,7 +44,8 @@ void amx_command() { //error print_srvconsole("(%3d) Load fails: %s\n", num, (*a).getError()); - } else if ( (*a).getStatusCode() == ps_error) { + } + else if ((*a).getStatusCode() == ps_error) { //error print_srvconsole("(%3d) Error: %s\n", num, (*a).getError()); } @@ -54,23 +54,23 @@ void amx_command() print_srvconsole("%d plugins, %d running\n", plugins, running); } - else if (!strcmp(cmd, "pause") && CMD_ARGC() > 2) + else if (!strcmp(cmd, "pause") && CMD_ARGC() > 2) { const char* sPlugin = CMD_ARGV(2); CPluginMngr::CPlugin *plugin = g_plugins.findPlugin(sPlugin); - if (plugin && plugin->isValid()) + if (plugin && plugin->isValid()) { if (plugin->isPaused()) { if (plugin->isStopped()) { - print_srvconsole("Plugin \"%s\" is stopped and may not be paused.\n",plugin->getName()); + print_srvconsole("Plugin \"%s\" is stopped and may not be paused.\n", plugin->getName()); } else { - print_srvconsole("Plugin \"%s\" is already paused.\n",plugin->getName()); + print_srvconsole("Plugin \"%s\" is already paused.\n", plugin->getName()); } } else @@ -79,18 +79,18 @@ void amx_command() print_srvconsole("Paused plugin \"%s\"\n", plugin->getName()); } } - else + else { print_srvconsole("Couldn't find plugin matching \"%s\"\n", sPlugin); } } - else if (!strcmp(cmd, "unpause") && CMD_ARGC() > 2) + else if (!strcmp(cmd, "unpause") && CMD_ARGC() > 2) { const char* sPlugin = CMD_ARGV(2); CPluginMngr::CPlugin *plugin = g_plugins.findPlugin(sPlugin); - if (plugin && plugin->isValid() && plugin->isPaused()) + if (plugin && plugin->isValid() && plugin->isPaused()) { if (plugin->isStopped()) { @@ -105,19 +105,20 @@ void amx_command() else if (!plugin) { print_srvconsole("Couldn't find plugin matching \"%s\"\n", sPlugin); - } else { + } + else { print_srvconsole("Plugin %s can't be unpaused right now.\n", sPlugin); } } - else if (!strcmp(cmd, "cvars")) + else if (!strcmp(cmd, "cvars")) { g_CvarManager.OnConsoleCommand(); } - else if (!strcmp(cmd, "cmds")) + else if (!strcmp(cmd, "cmds")) { print_srvconsole("Registered commands:\n"); print_srvconsole(" %-24.23s %-16.15s %-8.7s %-16.15s\n", "name", "access", "type", "plugin"); - + int ammount = 0; char access[32]; @@ -146,9 +147,9 @@ void amx_command() ++a; } } - print_srvconsole("%d commands\n",ammount); + print_srvconsole("%d commands\n", ammount); } - else if (!strcmp(cmd, "version")) + else if (!strcmp(cmd, "version")) { print_srvconsole("%s %s (%s)\n", Plugin_info.name, Plugin_info.version, Plugin_info.url); print_srvconsole("Authors:\n\tDavid \"BAILOPAN\" Anderson, Pavol \"PM OnoTo\" Marko\n"); @@ -178,7 +179,7 @@ void amx_command() int running = 0; int modules = 0; - CList::iterator a = g_modules.begin(); + CList::iterator a = g_modules.begin(); while (a) { @@ -229,14 +230,16 @@ void amx_command() print_srvconsole("\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3A\x78\x78\x24\x40\x4E\x4E\x4D\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5E\x3E\x3E\x3F\x3E\x3E\x3E\x3E\x3B\x3B\x3B\x3A\x3A\x3F\x3E\x3A\x2E\x2E\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2E\x45\x4D\x40\x45\x78\x5E\x33\x68\x33\x2B\n"); print_srvconsole("\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x24\x48\x45\x48\x78\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2B\x4E\x40\x2B\x66\x33\x78\x20\x20\n"); print_srvconsole("\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2B\x2C\x20\x3A\x20\x20\n"); - } else { + } + else + { print_srvconsole("Usage: amxx < command > [ argument ]\n"); print_srvconsole("Commands:\n"); print_srvconsole(" version - display amxx version info\n"); print_srvconsole(" gpl - print the license\n"); print_srvconsole(" plugins - list plugins currently loaded\n"); print_srvconsole(" modules - list modules currently loaded\n"); - print_srvconsole(" cvars [ plugin ] [ index ] - list cvars handled by amxx or show informations about a cvar if index is provided\n"); + print_srvconsole(" cvars [ plugin ] [ index ] - list cvars handled by amxx or show information about a cvar if index is provided\n"); print_srvconsole(" cmds [ plugin ] - list commands registered by plugins\n"); print_srvconsole(" pause < plugin > - pause a running plugin\n"); print_srvconsole(" unpause < plugin > - unpause a previously paused plugin\n"); @@ -246,15 +249,15 @@ void amx_command() void plugin_srvcmd() { const char* cmd = CMD_ARGV(0); - + CmdMngr::iterator a = g_commands.srvcmdbegin(); - + while (a) { if ((*a).matchCommand(cmd) && (*a).getPlugin()->isExecutable((*a).getFunction())) { cell ret = executeForwards((*a).getFunction(), static_cast(g_srvindex), - static_cast((*a).getFlags()), static_cast((*a).getId())); + static_cast((*a).getFlags()), static_cast((*a).getId())); if (ret) break; } ++a; diff --git a/plugins/include/cvars.inc b/plugins/include/cvars.inc index ced2f878..bc256fb4 100644 --- a/plugins/include/cvars.inc +++ b/plugins/include/cvars.inc @@ -8,7 +8,7 @@ // https://alliedmods.net/amxmodx-license #if defined _cvars_included - #endinput + #endinput #endif #define _cvars_included @@ -30,7 +30,7 @@ /** * Creates a new cvar for the engine. * - * @note This is same as regitser_cvar but with more options. + * @note This has the same effect as register_cvar but provides more options. * @note For a list of possible cvar flags see FCVAR_* constants above. * @note If an already existing cvar is registered it will not be duplicated. * @note The returned cvar pointer should be used with the get_pcvar_* and @@ -38,12 +38,12 @@ * * @param name Cvar name * @param string Default cvar value - * @param flags Optional bitstring of flags determining how the convar should be handled + * @param flags Optional bitsum of flags specifying cvar behavior * @param description Optional description of the cvar - * @param has_min Optional boolean that determines if the convar has a minimum valu - * @param min_val Minimum floating point value that the convar can have if has_min is true - * @param has_max Optional boolean that determines if the convar has a maximum value - * @param max_val Maximum floating point value that the convar can have if has_max is true + * @param has_min Optional boolean that specifies if the cvar has a minimum value + * @param min_val Minimum floating point value + * @param has_max Optional boolean that specifies if the cvar has a maximum value + * @param max_val Maximum floating point value * * @return Unique cvar pointer */ @@ -60,7 +60,7 @@ native create_cvar(const name[], const string[], flags = FCVAR_NONE, const descr * * @param name Cvar name * @param string Default cvar value - * @param flags Cvar flags + * @param flags Optional bitsum of flags specifying cvar behavior * @param fvalue Unused * * @return Unique cvar pointer @@ -91,9 +91,8 @@ native get_cvar_pointer(const cvar[]); /** * Creates a hook for when a cvar's value is changed. * - * @note You cannot prevent cvar changes from happening, you can only change the value again. - * @note Be careful not to set a cvar inside a cvar change hook such that - * it re-invokes he same callback. This results in infinite recursion. + * @note Changing the cvar value from within this forward can lead to infinite + * recursion and should be avoided. * @note Callback will be called in the following manner: * * public cvar_change_callback(pcvar, const old_value[], const new_value[]) @@ -102,16 +101,20 @@ native get_cvar_pointer(const cvar[]); * old_value - String containing the value of the cvar before it was changed * new_value - String containing the new value of the cvar * + * The return value is ignored + * * @param pcvar Pointer to cvar * @param callback Name of callback function - * @error Invalid pointer or invalid callback function + * + * @return Callback handle that can be used with [disable|enable]_cvar_hook + * @error Invalid cvar pointer or invalid callback function */ native cvarhook:hook_cvar_change(pcvar, const callback[]); /** * Stops a cvar hook forward from triggering. * - * @note Use the return value from hook_cvar_change as the parameter here. + * @note Use the handle returned by hook_cvar_change as the parameter here. * * @param handle Forward to stop * @error Invalid hook handle @@ -121,7 +124,7 @@ native disable_cvar_hook(cvarhook:handle); /** * Starts a cvar hook forward back up. * - * @note Use the return value from hook_cvar_change as the parameter here. + * @note Use the handle returned by hook_cvar_change as the parameter here. * * @param handle Forward to back up * @error Invalid hook handle @@ -390,16 +393,16 @@ native set_pcvar_string(pcvar, const string[]); */ enum CvarBounds { - CvarBound_Upper = 0, - CvarBound_Lower + CvarBound_Upper = 0, + CvarBound_Lower }; /** - * Retrieves the specified bound of a cvar. + * Retrieves the specified value bounds of a cvar. * * @param pcvar Pointer to cvar * @param type Type of bound to retrieve, CvarBound_Lower or CvarBound_Upper - * @param value By-reference cell to store the specified floating point bound value + * @param value Variable to store the specified bound to * * @return True if the cvar has the specified bound set, false otherwise. * @error If an invalid cvar pointer or CvarBounds value, an error is thrown. @@ -407,7 +410,7 @@ enum CvarBounds native bool:get_pcvar_bounds(pcvar, CvarBounds:type, &Float:value); /** - * Sets the specified bound of a cvar. + * Sets the specified bounds of a cvar. * * @param pcvar Pointer to cvar * @param type Type of bound to set, CvarBound_Lower or CvarBound_Upper @@ -419,34 +422,46 @@ 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. + * Binds a cvar's integer value to a global variable. The variable will then + * always contain the current cvar value as it is automatically kept up to date. + * + * @note The variable *has* to be a global or a static variable. Local variables + * created within functions can not be used for technical reasons. + * @note Variables can not be bound to multiple cvars. * * @param pcvar Pointer to cvar - * @param var Global variable to update to + * @param var Global variable to keep updated * * @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. + * Binds a cvar's float value to a global variable. The variable will then + * always contain the current cvar value as it is automatically kept up to date. + * + * @note The variable *has* to be a global or a static variable. Local variables + * created within functions can not be used for technical reasons. + * @note Variables can not be bound to multiple cvars. * * @param pcvar Pointer to cvar - * @param var Global variable to update to + * @param var Global variable to keep updated * * @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. + * Binds a cvar's string value to a global array. The array will then + * always contain the current cvar value as it is automatically kept up to date. + * + * @note The array *has* to be a global or a static array. Local arrays + * created within functions can not be used for technical reasons. + * @note Arrays can not be bound to multiple cvars. * * @param pcvar Pointer to cvar - * @param var Global variable to update to - * @param varlen Maximum length of string buffer + * @param var Global array to keep updated + * @param varlen Maximum length of string array * * @error Invalid cvar pointer, invalid provided variable or cvar/variable already binded. */