From 6b418797671ba371992181835046c727f70e4534 Mon Sep 17 00:00:00 2001 From: div0 Date: Fri, 12 Jun 2009 07:46:18 +0000 Subject: [PATCH] bots scripting: use a string buffer now new command "barrier": waits till all bots have executed this command (for syncing actions) git-svn-id: svn://svn.icculus.org/nexuiz/trunk@6993 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/server/bots_scripting.qc | 393 ++++++++++++++++------------ data/qcsrc/server/gamecommand.qc | 16 +- 2 files changed, 228 insertions(+), 181 deletions(-) diff --git a/data/qcsrc/server/bots_scripting.qc b/data/qcsrc/server/bots_scripting.qc index 6e46a147a..7163bfb5d 100755 --- a/data/qcsrc/server/bots_scripting.qc +++ b/data/qcsrc/server/bots_scripting.qc @@ -1,3 +1,68 @@ +.float bot_cmdqueuebuf_allocated; +.float bot_cmdqueuebuf; +.float bot_cmdqueuebuf_start; +.float bot_cmdqueuebuf_end; + +void bot_clearqueue(entity bot) +{ + if(!bot.bot_cmdqueuebuf_allocated) + error("readcommand but no queue allocated"); + buf_del(bot.bot_cmdqueuebuf); + bot.bot_cmdqueuebuf_allocated = FALSE; +} + +void bot_queuecommand(entity bot, string cmdstring) +{ + if(!bot.bot_cmdqueuebuf_allocated) + { + bot.bot_cmdqueuebuf = buf_create(); + bot.bot_cmdqueuebuf_start = 0; + bot.bot_cmdqueuebuf_end = 0; + bot.bot_cmdqueuebuf_allocated = TRUE; + } + + bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring); + bot.bot_cmdqueuebuf_end += 1; +} + +void bot_dequeuecommand(entity bot, float idx) +{ + if(!bot.bot_cmdqueuebuf_allocated) + error("dequeuecommand but no queue allocated"); + if(idx < bot.bot_cmdqueuebuf_start) + error("dequeueing a command in the past"); + if(idx >= bot.bot_cmdqueuebuf_end) + error("dequeueing a command in the future"); + bufstr_set(bot.bot_cmdqueuebuf, idx, ""); + if(idx == bot.bot_cmdqueuebuf_start) + bot.bot_cmdqueuebuf_start += 1; + if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end) + bot_clearqueue(bot); +} + +string bot_readcommand(entity bot, float idx) +{ + if(!bot.bot_cmdqueuebuf_allocated) + error("readcommand but no queue allocated"); + if(idx < bot.bot_cmdqueuebuf_start) + error("reading a command in the past"); + if(idx >= bot.bot_cmdqueuebuf_end) + error("reading a command in the future"); + return bufstr_get(bot.bot_cmdqueuebuf, idx); +} + +float bot_havecommand(entity bot, float idx) +{ + if(!bot.bot_cmdqueuebuf_allocated) + return 0; + if(idx < bot.bot_cmdqueuebuf_start) + return 0; + if(idx >= bot.bot_cmdqueuebuf_end) + return 0; + return 1; +} + + // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER #define BOT_CMD_NULL 0 #define BOT_CMD_PAUSE 1 @@ -17,9 +82,9 @@ #define BOT_CMD_SELECTWEAPON 15 #define BOT_CMD_IMPULSE 16 #define BOT_CMD_WAIT_UNTIL 17 -#define BOT_CMD_RESETQUEUE 18 -#define BOT_CMD_MOVETOTARGET 19 -#define BOT_CMD_AIMTARGET 20 +#define BOT_CMD_MOVETOTARGET 18 +#define BOT_CMD_AIMTARGET 19 +#define BOT_CMD_BARRIER 20 #define BOT_CMD_WHILE 21 // TODO: Not implemented yet #define BOT_CMD_WEND 22 // TODO: Not implemented yet #define BOT_CMD_CHASE 23 // TODO: Not implemented yet @@ -36,14 +101,14 @@ #define BOT_CMD_PARAMETER_FLOAT 1 #define BOT_CMD_PARAMETER_STRING 2 #define BOT_CMD_PARAMETER_VECTOR 3 -#define BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME 4 float bot_cmds_initialized; float bot_cmd_parm_type[BOT_CMD_COUNTER]; string bot_cmd_string[BOT_CMD_COUNTER]; // Bots command queue -entity bot_cmd; // Current command +entity bot_cmd; // global current command +.entity bot_cmd_current; // current command of this bot .float is_bot_cmd; // Tells if the entity is a bot command .float bot_cmd_index; // Position of the command in the queue @@ -51,80 +116,78 @@ entity bot_cmd; // Current command .float bot_cmd_parm_float; // Field to store a float parameter .string bot_cmd_parm_string; // Field to store a string parameter .vector bot_cmd_parm_vector; // Field to store a vector parameter -.float bot_cmd_execution_counter; // How many times this command on the queue was executed -.float bot_cmd_execution_index; // Position in the queue of the command to be executed -.float bot_cmd_queue_index; // Position of the last command in the queue +float bot_barriertime; +.float bot_barrier; -.entity bot_cmd_next; // next command of the same bot (field on a command), or next command for this bot to execute (field on a bot) -.entity bot_cmd_newest; // last command of this bot (field on a bot, used for adding new commands) +.float bot_cmd_execution_index; // Position in the queue of the command to be executed // Initialize global commands list // NOTE: New commands should be initialized here void bot_commands_init() { - bot_cmd_string[BOT_CMD_NULL] = ""; - bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_NULL] = ""; + bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_PAUSE] = "pause"; - bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_PAUSE] = "pause"; + bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_CONTINUE] = "continue"; - bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_CONTINUE] = "continue"; + bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_WAIT] = "wait"; - bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT; + bot_cmd_string[BOT_CMD_WAIT] = "wait"; + bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT; - bot_cmd_string[BOT_CMD_TURN] = "turn"; - bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT; + bot_cmd_string[BOT_CMD_TURN] = "turn"; + bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT; - bot_cmd_string[BOT_CMD_MOVETO] = "moveto"; - bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR; + bot_cmd_string[BOT_CMD_MOVETO] = "moveto"; + bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR; - bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget"; - bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget"; + bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal"; - bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal"; + bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_CC] = "cc"; - bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_CC] = "cc"; + bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_IF] = "if"; - bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_IF] = "if"; + bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_ELSE] = "else"; - bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_ELSE] = "else"; + bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_FI] = "fi"; - bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_FI] = "fi"; + bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim"; - bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim"; + bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE; - bot_cmd_string[BOT_CMD_AIM] = "aim"; - bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_AIM] = "aim"; + bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget"; - bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget"; + bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey"; - bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey"; + bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey"; - bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING; + bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey"; + bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING; - bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon"; - bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT; + bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon"; + bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT; - bot_cmd_string[BOT_CMD_IMPULSE] = "impulse"; - bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT; + bot_cmd_string[BOT_CMD_IMPULSE] = "impulse"; + bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT; - bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until"; - bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME; + bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until"; + bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT; - bot_cmd_string[BOT_CMD_RESETQUEUE] = "resetqueue"; - bot_cmd_parm_type[BOT_CMD_RESETQUEUE] = BOT_CMD_PARAMETER_NONE; + bot_cmd_string[BOT_CMD_BARRIER] = "barrier"; + bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE; bot_cmds_initialized = TRUE; } @@ -167,67 +230,22 @@ entity find_bot_by_number(float number) return world; } -void bot_clearqueue() +float bot_decodecommand(string cmdstring) { - entity head, newhead; - - head = findchainfloat(is_bot_cmd, TRUE); - newhead = head; + local float cmd_parm_type, i; + float sp; + string parm; - while(newhead) + sp = strstrofs(cmdstring, " ", 0); + if(sp < 0) { - newhead = head.chain; - - if(head.owner==self) - remove(head); - - head = newhead; + parm = ""; + } + else + { + parm = substring(cmdstring, sp + 1, -1); + cmdstring = substring(cmdstring, 0, sp); } - - self.bot_cmd_queue_index = 0; - self.bot_cmd_execution_index = 0; -} - -entity bot_spawncmd(entity bot, float type) -{ - entity cmd; - - bot.bot_cmd_queue_index++; - - cmd = spawn(); - cmd.owner = bot; - cmd.is_bot_cmd = TRUE; - cmd.bot_cmd_type = type; - cmd.bot_cmd_index = bot.bot_cmd_queue_index; - - if(bot.bot_cmd_newest) - bot.bot_cmd_newest.bot_cmd_next = cmd; - bot.bot_cmd_newest = cmd; - - return cmd; -} - -void bot_debugcmd(entity cmd) -{ - print(strcat("Owner: ",cmd.owner.netname, "\n")); - print(strcat("Cmd Type: ",ftos(cmd.bot_cmd_type), "\n")); - print(strcat("Cmd Index: ",ftos(cmd.bot_cmd_index), "\n")); - - print(strcat("Param Float: ",ftos(cmd.bot_cmd_parm_float), "\n")); - print(strcat("Param String: ",cmd.bot_cmd_parm_string, "\n")); - print(strcat("Param Vector: ",vtos(cmd.bot_cmd_parm_vector), "\n")); - - print(strcat("Bot queue index: ", ftos(cmd.owner.bot_cmd_queue_index), "\n")); - print(strcat("Bot execution index: ", ftos(cmd.owner.bot_cmd_execution_index), "\n\n")); -} - -void bot_queuecommand(entity bot, string cmdstring, string parm) -{ - entity cmd; - local float cmd_parm_type, i; - - if not(bot.isbot) - return; if(!bot_cmds_initialized) bot_commands_init(); @@ -242,33 +260,31 @@ void bot_queuecommand(entity bot, string cmdstring, string parm) if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="") { print("ERROR: A parameter is required for this command\n"); - return; + return 0; } // Load command into queue - cmd = bot_spawncmd(bot, i); + bot_cmd.bot_cmd_type = i; // Attach parameter switch(cmd_parm_type) { case BOT_CMD_PARAMETER_FLOAT: - cmd.bot_cmd_parm_float = stof(parm); - break; - case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME: - cmd.bot_cmd_parm_float = stof(parm) + time; + bot_cmd.bot_cmd_parm_float = stof(parm); break; case BOT_CMD_PARAMETER_STRING: - cmd.bot_cmd_parm_string = strzone(parm); + bot_cmd.bot_cmd_parm_string = strzone(parm); break; case BOT_CMD_PARAMETER_VECTOR: - cmd.bot_cmd_parm_vector = stov(parm); + bot_cmd.bot_cmd_parm_vector = stov(parm); break; default: break; } - return; + return 1; } print("ERROR: No such command '", cmdstring, "'\n"); + return 0; } void bot_cmdhelp(string scmd) @@ -289,7 +305,6 @@ void bot_cmdhelp(string scmd) switch(ntype) { case BOT_CMD_PARAMETER_FLOAT: - case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME: stype = "float number"; break; case BOT_CMD_PARAMETER_STRING: @@ -318,7 +333,7 @@ void bot_cmdhelp(string scmd) print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed"); break; case BOT_CMD_WAIT_UNTIL: - print("Pause command parsing and bot ai until time is N from NOW (when the command is added). Pressed key will remain pressed"); + print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed"); break; case BOT_CMD_TURN: print("Look to the right or left N degrees. For turning to the left use positive numbers."); @@ -394,7 +409,6 @@ void bot_list_commands() switch(bot_cmd_parm_type[i]) { case BOT_CMD_PARAMETER_FLOAT: - case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME: ptype = "float number"; break; case BOT_CMD_PARAMETER_STRING: @@ -462,7 +476,7 @@ float bot_cmd_wait() float bot_cmd_wait_until() { - if(time < bot_cmd.bot_cmd_parm_float) + if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime) { self.bot_exec_status |= BOT_EXEC_STATUS_WAITING; return CMD_STATUS_EXECUTING; @@ -471,6 +485,41 @@ float bot_cmd_wait_until() return CMD_STATUS_FINISHED; } +float bot_cmd_barrier() +{ + entity cl; + + // 0 = no barrier, 1 = waiting, 2 = waiting finished + + if(self.bot_barrier == 0) // initialization + { + self.bot_barrier = 1; + } + + if(self.bot_barrier == 1) // find other bots + { + FOR_EACH_CLIENT(cl) if(cl.isbot) + { + if(cl.bot_barrier == 0) + return CMD_STATUS_EXECUTING; // not all are at the barrier yet + } + + // all bots hit the barrier! + FOR_EACH_CLIENT(cl) if(cl.isbot) + { + cl.bot_barrier = 2; // acknowledge barrier + } + + bot_barriertime = time; + } + + // if we get here, the barrier is finished + // so end it... + self.bot_barrier = 0; + + return CMD_STATUS_FINISHED; +} + float bot_cmd_turn() { self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float; @@ -541,7 +590,7 @@ float bot_cmd_if() { // Only one "if" block is allowed at time print("ERROR: Only one conditional block can be processed at time"); - bot_clearqueue(); + bot_clearqueue(self); return CMD_STATUS_ERROR; } @@ -931,78 +980,76 @@ float bot_cmd_resetgoal() return self.cmd_resetgoal(); } + + + + + + + + void bot_command_executed(float rm) { entity cmd; cmd = bot_cmd; - self.bot_cmd_execution_index++; - if(rm) - { - if(bot_cmd_parm_type[cmd.bot_cmd_type]==BOT_CMD_PARAMETER_STRING) - { - strunzone(cmd.bot_cmd_parm_string); - } - self.bot_cmd_next = cmd.bot_cmd_next; - if(cmd == self.bot_cmd_newest) - self.bot_cmd_newest = world; - remove(cmd); - return; - } + bot_dequeuecommand(self, self.bot_cmd_execution_index); - cmd.bot_cmd_execution_counter++; + self.bot_cmd_execution_index++; } void bot_setcurrentcommand() { - entity cmd; - bot_cmd = world; - if(!self.bot_cmd_newest) // no command in the queue? - return; - - if(self.bot_cmd_execution_index==0) - self.bot_cmd_execution_index=1; - - cmd = self.bot_cmd_next; - if(cmd) - if(cmd.owner == self) - if(cmd.bot_cmd_index == self.bot_cmd_execution_index) + if(self.bot_cmd_execution_index == 0) + self.bot_cmd_execution_index = 1; + + if(!self.bot_cmd_current) { - bot_cmd = cmd; - return; + self.bot_cmd_current = spawn(); + self.bot_cmd_current.classname = "bot_cmd"; + self.bot_cmd_current.is_bot_cmd = 1; + self.bot_cmd_current.bot_cmd_index = 0; } -/* - if(!cmd) + bot_cmd = self.bot_cmd_current; + if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index) { - print("next bot cmd not set\n"); - } - - if(cmd && cmd.owner != self) - { - print("next bot cmd has wrong owner ", etos(cmd.owner), " for ", etos(self), "\n"); + if(bot_havecommand(self, self.bot_cmd_execution_index)) + { + string cmdstring; + cmdstring = bot_readcommand(self, self.bot_cmd_execution_index); + if(bot_decodecommand(cmdstring)) + { + bot_cmd.owner = self; + bot_cmd.bot_cmd_index = self.bot_cmd_execution_index; + } + else + bot_cmd = world; + } + else + bot_cmd = world; } +} - if(cmd && cmd.owner == self && cmd.bot_cmd_index != self.bot_cmd_execution_index) - { - print("next bot cmd has wrong index ", ftos(cmd.bot_cmd_execution_index), " for ", ftos(self.bot_cmd_execution_index), "\n"); - } -*/ +void bot_resetqueues() +{ + entity cl; - for (cmd = findchainfloat(bot_cmd_index, self.bot_cmd_execution_index); cmd; cmd = cmd.chain) + FOR_EACH_CLIENT(cl) if(cl.isbot) { - if(cmd.owner==self) + bot_clearqueue(cl); + // also, cancel all barriers + FOR_EACH_CLIENT(cl) if(cl.isbot) { - bot_cmd = cmd; - self.bot_cmd_next = cmd; - //print(etos(self), " probably a jump...\n"); - return; + cl.bot_barrier = 0; } } + + bot_barriertime = time; } // This function should be (the only) called directly from the bot ai loop @@ -1115,9 +1162,8 @@ float bot_execute_commands() case BOT_CMD_IMPULSE: status = bot_cmd_impulse(); break; - case BOT_CMD_RESETQUEUE: - bot_clearqueue(); - status = CMD_STATUS_FINISHED; + case BOT_CMD_BARRIER: + status = bot_cmd_barrier(); break; default: print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n")); @@ -1141,7 +1187,6 @@ float bot_execute_commands() switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type]) { case BOT_CMD_PARAMETER_FLOAT: - case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME: parms = ftos(bot_cmd.bot_cmd_parm_float); break; case BOT_CMD_PARAMETER_STRING: diff --git a/data/qcsrc/server/gamecommand.qc b/data/qcsrc/server/gamecommand.qc index 697867b50..e783c1dcb 100644 --- a/data/qcsrc/server/gamecommand.qc +++ b/data/qcsrc/server/gamecommand.qc @@ -937,6 +937,12 @@ void GameCommand(string command) return; } + if(argv(1) == "reset") + { + bot_resetqueues(); + return; + } + if(argc < 3) { print("Usage: sv_cmd bot_cmd [argument]\n"); @@ -952,15 +958,11 @@ void GameCommand(string command) bot = find_bot_by_number(stof(argv(1))); if(bot) - { - if(argc==4) - bot_queuecommand(bot,argv(2),argv(3)); - else - bot_queuecommand(bot,argv(2),""); - } + bot_queuecommand(bot, strcat(argv(2), " ", argv(3))); else print(strcat("Error: Unable to find a bot with the name or number '",argv(1),"'\n")); - return; + + return; } print("Invalid command. For a list of supported commands, try sv_cmd help.\n"); -- 2.39.2