diff --git a/amxmodx/amxmodx.cpp b/amxmodx/amxmodx.cpp index dc856d34..74d672d9 100755 --- a/amxmodx/amxmodx.cpp +++ b/amxmodx/amxmodx.cpp @@ -2532,19 +2532,20 @@ static cell AMX_NATIVE_CALL set_user_info(AMX *amx, cell *params) /* 3 param */ static cell AMX_NATIVE_CALL read_argc(AMX *amx, cell *params) { - return CMD_ARGC(); + return g_fakecmd.notify ? g_fakecmd.argc : CMD_ARGC(); } static cell AMX_NATIVE_CALL read_argv(AMX *amx, cell *params) /* 3 param */ { - const char *value = CMD_ARGV(params[1]); - return set_amxstring_utf8(amx, params[2], /*(params[1] < 0 || - params[1] >= CMD_ARGC()) ? "" : */value, strlen(value), params[3] + 1); // + EOS + int argc = params[1]; + + const char *value = g_fakecmd.notify ? (argc >= 0 && argc < 3 ? g_fakecmd.argv[argc] : "") : CMD_ARGV(argc); + return set_amxstring_utf8(amx, params[2], value, strlen(value), params[3] + 1); // + EOS } static cell AMX_NATIVE_CALL read_args(AMX *amx, cell *params) /* 2 param */ { - const char* sValue = CMD_ARGS(); + const char* sValue = g_fakecmd.notify ? (g_fakecmd.argc > 1 ? g_fakecmd.args : g_fakecmd.argv[0]) : CMD_ARGS(); return set_amxstring_utf8(amx, params[1], sValue ? sValue : "", sValue ? strlen(sValue) : 0, params[2] + 1); // +EOS } @@ -2715,7 +2716,7 @@ static cell AMX_NATIVE_CALL server_exec(AMX *amx, cell *params) return 1; } -static cell AMX_NATIVE_CALL engclient_cmd(AMX *amx, cell *params) /* 4 param */ +int sendFakeCommand(AMX *amx, cell *params, bool fwd = false) { int ilen; const char* szCmd = get_amxstring(amx, params[2], 0, ilen); @@ -2736,7 +2737,7 @@ static cell AMX_NATIVE_CALL engclient_cmd(AMX *amx, cell *params) /* 4 param */ CPlayer* pPlayer = GET_PLAYER_POINTER_I(i); if (pPlayer->ingame /*&& pPlayer->initialized */) - UTIL_FakeClientCommand(pPlayer->pEdict, szCmd, sArg1, sArg2); + UTIL_FakeClientCommand(pPlayer->pEdict, szCmd, sArg1, sArg2, fwd); } } else { int index = params[1]; @@ -2750,11 +2751,20 @@ static cell AMX_NATIVE_CALL engclient_cmd(AMX *amx, cell *params) /* 4 param */ CPlayer* pPlayer = GET_PLAYER_POINTER_I(index); if (/*pPlayer->initialized && */pPlayer->ingame) - UTIL_FakeClientCommand(pPlayer->pEdict, szCmd, sArg1, sArg2); + UTIL_FakeClientCommand(pPlayer->pEdict, szCmd, sArg1, sArg2, fwd); } return 1; } +static cell AMX_NATIVE_CALL engclient_cmd(AMX *amx, cell *params) /* 4 param */ +{ + return sendFakeCommand(amx, params); +} + +static cell AMX_NATIVE_CALL amxclient_cmd(AMX *amx, cell *params) /* 4 param */ +{ + return sendFakeCommand(amx, params, true); +} static cell AMX_NATIVE_CALL pause(AMX *amx, cell *params) /* 3 param */ { @@ -4886,6 +4896,7 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"admins_lookup", admins_lookup}, {"admins_num", admins_num}, {"admins_push", admins_push}, + {"amxclient_cmd", amxclient_cmd}, {"amxx_setpl_curweap", amxx_setpl_curweap}, {"arrayset", arrayset}, {"get_addr_val", get_addr_val}, diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 8dd476e9..331e433c 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -137,7 +137,7 @@ char* UTIL_SplitHudMessage(register const char *src); int UTIL_ReadFlags(const char* c); void UTIL_ClientPrint(edict_t *pEntity, int msg_dest, char *msg); -void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1 = NULL, const char *arg2 = NULL); +void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1 = NULL, const char *arg2 = NULL, bool fwd = false); void UTIL_GetFlags(char* flags, int flag); void UTIL_HudMessage(edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage); void UTIL_DHudMessage(edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage, unsigned int length); @@ -172,6 +172,7 @@ struct fakecmd_t const char *argv[3]; int argc; bool fake; + bool notify; // notify to plugins. }; extern CLog g_log; diff --git a/amxmodx/util.cpp b/amxmodx/util.cpp index 2d7b0a7d..32427449 100755 --- a/amxmodx/util.cpp +++ b/amxmodx/util.cpp @@ -350,7 +350,7 @@ void UTIL_TeamInfo(edict_t *pEntity, int playerIndex, const char *pszTeamName) // 2) Invokes ClientCommand in GameDLL // 3) meta_api.cpp overrides Cmd_Args, Cmd_Argv, Cmd_Argc and gives them fake values if the "fake" flag is set // 4) unsets the global "fake" flag -void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1, const char *arg2) +void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1, const char *arg2, bool fwd) { if (!cmd) return; // no command @@ -389,6 +389,44 @@ void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1, else g_fakecmd.argc = 1; // no argmuents -> only one command + /* Notify plugins about this command */ + if (fwd) + { + /* Set flag so read_argc/v/s functions will give proper value */ + g_fakecmd.notify = true; + + if (executeForwards(FF_ClientCommand, static_cast(GET_PLAYER_POINTER(pEdict)->index)) > 0) + { + g_fakecmd.notify = false; + return; + } + + /* check for command and if needed also for first argument and call proper function */ + CmdMngr::iterator aa = g_commands.clcmdprefixbegin(cmd); + + if (!aa) + { + aa = g_commands.clcmdbegin(); + } + + while (aa) + { + if ((*aa).matchCommandLine(cmd, arg1) && (*aa).getPlugin()->isExecutable((*aa).getFunction())) + { + if (executeForwards((*aa).getFunction(), static_cast(GET_PLAYER_POINTER(pEdict)->index)), + static_cast((*aa).getFlags()), static_cast((*aa).getId()) > 0) + { + g_fakecmd.notify = false; + return; + } + } + ++aa; + } + + /* Unset flag */ + g_fakecmd.notify = false; + } + // set the global "fake" flag so the Cmd_Arg* functions will be superceded g_fakecmd.fake = true; // tell the GameDLL that the client sent a command diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index ba84ce51..729b4c4e 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -446,12 +446,37 @@ native remove_quotes(text[]); /* Executes command on player. */ native client_cmd(index,const command[],any:...); -/* This is an emulation of a client command (commands aren't send to client!). -* It allows to execute some commands on players and bots. -* Function is excellent for forcing to do an action related to a game (not settings!). -* The command must stand alone but in arguments you can use spaces. */ +/** + * This is an emulation of a client command (commands aren't send to client!). + * It allows to execute some commands on players and bots. + * Function is excellent for forcing to do an action related to a game (not settings!). + * The command must stand alone but in arguments you can use spaces. + * + * @param index Index of the client, use 0 to send to all clients. + * @param command The client command to execute on. + * @param arg1 Optionnal. The command arguments. + * @param arg2 Optionnal. The command arguments. + * @noreturn + */ native engclient_cmd(index,const command[],const arg1[]="",const arg2[]=""); +/** + * This is an emulation of a client command (commands aren't send to client!). + * It allows to execute some commands on players and bots. + * Function is excellent for forcing to do an action related to a game (not settings!). + * The command must stand alone but in arguments you can use spaces. + * + * @note This is similar to engclient_cmd with the difference all plugins hooking + * the command will be notified as well. + * + * @param index Index of the client, use 0 to send to all clients. + * @param command The client command to execute on. + * @param arg1 Optionnal. The command arguments. + * @param arg2 Optionnal. The command arguments. + * @noreturn + */ +native amxclient_cmd(index, const command[], const arg1[] = "", const arg2[] = ""); + /* Executes command on a server console. */ native server_cmd(const command[],any:...);