webperimental: killstack decides stack protects.
[freeciv.git] / server / stdinhand.c
blob58d0e94a266019952442242163b08c46b1f93ace
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
23 #ifdef FREECIV_HAVE_LIBREADLINE
24 #include <readline/readline.h>
25 #endif
27 /* utility */
28 #include "astring.h"
29 #include "bitvector.h"
30 #include "fc_cmdline.h"
31 #include "fciconv.h"
32 #include "fcintl.h"
33 #include "log.h"
34 #include "mem.h"
35 #include "registry.h"
36 #include "support.h" /* fc__attribute, bool type, etc. */
37 #include "timing.h"
39 /* common */
40 #include "capability.h"
41 #include "events.h"
42 #include "fc_types.h" /* LINE_BREAK */
43 #include "featured_text.h"
44 #include "game.h"
45 #include "map.h"
46 #include "mapimg.h"
47 #include "packets.h"
48 #include "player.h"
49 #include "research.h"
50 #include "rgbcolor.h"
51 #include "srvdefs.h"
52 #include "unitlist.h"
53 #include "version.h"
55 /* server */
56 #include "aiiface.h"
57 #include "citytools.h"
58 #include "connecthand.h"
59 #include "diplhand.h"
60 #include "gamehand.h"
61 #include "mapgen.h"
62 #include "maphand.h"
63 #include "meta.h"
64 #include "notify.h"
65 #include "plrhand.h"
66 #include "report.h"
67 #include "ruleset.h"
68 #include "sanitycheck.h"
69 #include "savegame.h"
70 #include "score.h"
71 #include "sernet.h"
72 #include "settings.h"
73 #include "srv_log.h"
74 #include "srv_main.h"
75 #include "techtools.h"
76 #include "voting.h"
78 /* server/scripting */
79 #include "script_server.h"
80 #include "script_fcdb.h"
82 /* ai */
83 #include "difficulty.h"
84 #include "handicaps.h"
86 #include "stdinhand.h"
88 #define OPTION_NAME_SPACE 25
90 static enum cmdlevel default_access_level = ALLOW_BASIC;
91 static enum cmdlevel first_access_level = ALLOW_BASIC;
93 static time_t *time_duplicate(const time_t *t);
95 /* 'struct kick_hash' and related functions. */
96 #define SPECHASH_TAG kick
97 #define SPECHASH_ASTR_KEY_TYPE
98 #define SPECHASH_IDATA_TYPE time_t *
99 #define SPECHASH_UDATA_TYPE time_t
100 #define SPECHASH_IDATA_COPY time_duplicate
101 #define SPECHASH_IDATA_FREE (kick_hash_data_free_fn_t) free
102 #define SPECHASH_UDATA_TO_IDATA(t) (&(t))
103 #define SPECHASH_IDATA_TO_UDATA(p) (NULL != p ? *p : 0)
104 #include "spechash.h"
106 static struct kick_hash *kick_table_by_addr = NULL;
107 static struct kick_hash *kick_table_by_user = NULL;
110 static bool cut_client_connection(struct connection *caller, char *name,
111 bool check);
112 static bool show_help(struct connection *caller, char *arg);
113 static bool show_list(struct connection *caller, char *arg);
114 static void show_colors(struct connection *caller);
115 static bool set_ai_level_named(struct connection *caller, const char *name,
116 const char *level_name, bool check);
117 static bool set_ai_level(struct connection *caller, const char *name,
118 enum ai_level level, bool check);
119 static bool away_command(struct connection *caller, bool check);
120 static bool set_rulesetdir(struct connection *caller, char *str, bool check,
121 int read_recursion);
122 static bool show_command(struct connection *caller, char *str, bool check);
123 static bool show_settings(struct connection *caller,
124 enum command_id called_as,
125 char *str, bool check);
126 static void show_settings_one(struct connection *caller, enum command_id cmd,
127 struct setting *pset);
128 static void show_ruleset_info(struct connection *caller, enum command_id cmd,
129 bool check, int read_recursion);
130 static void show_mapimg(struct connection *caller, enum command_id cmd);
131 static bool set_command(struct connection *caller, char *str, bool check);
133 static bool create_command(struct connection *caller, const char *str,
134 bool check);
135 static bool end_command(struct connection *caller, char *str, bool check);
136 static bool surrender_command(struct connection *caller, char *str, bool check);
137 static bool handle_stdin_input_real(struct connection *caller, char *str,
138 bool check, int read_recursion);
139 static bool read_init_script_real(struct connection *caller,
140 char *script_filename, bool from_cmdline,
141 bool check, int read_recursion);
142 static bool reset_command(struct connection *caller, char *arg, bool check,
143 int read_recursion);
144 static bool default_command(struct connection *caller, char *arg, bool check);
145 static bool lua_command(struct connection *caller, char *arg, bool check);
146 static bool kick_command(struct connection *caller, char *name, bool check);
147 static bool delegate_command(struct connection *caller, char *arg,
148 bool check);
149 static const char *delegate_player_str(struct player *pplayer, bool observer);
150 static bool aicmd_command(struct connection *caller, char *arg, bool check);
151 static bool fcdb_command(struct connection *caller, char *arg, bool check);
152 static const char *fcdb_accessor(int i);
153 static char setting_status(struct connection *caller,
154 const struct setting *pset);
155 static bool player_name_check(const char* name, char *buf, size_t buflen);
156 static bool playercolor_command(struct connection *caller,
157 char *str, bool check);
158 static bool mapimg_command(struct connection *caller, char *arg, bool check);
159 static const char *mapimg_accessor(int i);
161 static void show_delegations(struct connection *caller);
163 static const char horiz_line[] =
164 "------------------------------------------------------------------------------";
166 /********************************************************************
167 Are we operating under a restricted security regime? For now
168 this does not do much.
169 *********************************************************************/
170 static bool is_restricted(struct connection *caller)
172 return (caller && caller->access_level != ALLOW_HACK);
175 /**************************************************************************
176 Check the player name. Returns TRUE if the player name is valid else
177 an error message is saved in 'buf'.
178 **************************************************************************/
179 static bool player_name_check(const char* name, char *buf, size_t buflen)
181 size_t len = strlen(name);
183 if (len == 0) {
184 fc_snprintf(buf, buflen, _("Can't use an empty name."));
185 return FALSE;
186 } else if (len > MAX_LEN_NAME-1) {
187 fc_snprintf(buf, buflen, _("That name exceeds the maximum of %d chars."),
188 MAX_LEN_NAME-1);
189 return FALSE;
190 } else if (fc_strcasecmp(name, ANON_PLAYER_NAME) == 0
191 || fc_strcasecmp(name, "Observer") == 0) {
192 fc_snprintf(buf, buflen, _("That name is not allowed."));
193 /* "Observer" used to be illegal and we keep it that way for now. */
194 /* FIXME: This disallows anonymous player name as it appears in English,
195 * but not one in any other language that the user may see. */
196 return FALSE;
199 return TRUE;
202 /**************************************************************************
203 Convert a named command into an id.
204 If accept_ambiguity is true, return the first command in the
205 enum list which matches, else return CMD_AMBIGUOUS on ambiguity.
206 (This is a trick to allow ambiguity to be handled in a flexible way
207 without importing notify_player() messages inside this routine - rp)
208 **************************************************************************/
209 static enum command_id command_named(const char *token, bool accept_ambiguity)
211 enum m_pre_result result;
212 int ind;
214 result = match_prefix(command_name_by_number, CMD_NUM, 0,
215 fc_strncasecmp, NULL, token, &ind);
217 if (result < M_PRE_AMBIGUOUS) {
218 return ind;
219 } else if (result == M_PRE_AMBIGUOUS) {
220 return accept_ambiguity ? ind : CMD_AMBIGUOUS;
221 } else {
222 return CMD_UNRECOGNIZED;
226 /**************************************************************************
227 Initialize stuff related to this code module.
228 **************************************************************************/
229 void stdinhand_init(void)
231 fc_assert(NULL == kick_table_by_addr);
232 kick_table_by_addr = kick_hash_new();
234 fc_assert(NULL == kick_table_by_user);
235 kick_table_by_user = kick_hash_new();
238 /**************************************************************************
239 Update stuff every turn that is related to this code module. Run this
240 on turn end.
241 **************************************************************************/
242 void stdinhand_turn(void)
244 /* Nothing at the moment. */
247 /**************************************************************************
248 Deinitialize stuff related to this code module.
249 **************************************************************************/
250 void stdinhand_free(void)
252 fc_assert(NULL != kick_table_by_addr);
253 if (NULL != kick_table_by_addr) {
254 kick_hash_destroy(kick_table_by_addr);
255 kick_table_by_addr = NULL;
258 fc_assert(NULL != kick_table_by_user);
259 if (NULL != kick_table_by_user) {
260 kick_hash_destroy(kick_table_by_user);
261 kick_table_by_user = NULL;
265 /**************************************************************************
266 Whether the caller can use the specified command. caller == NULL means
267 console.
268 **************************************************************************/
269 static bool may_use(struct connection *caller, enum command_id cmd)
271 if (!caller) {
272 return TRUE; /* on the console, everything is allowed */
274 return (caller->access_level >= command_level(command_by_number(cmd)));
277 /**************************************************************************
278 Whether the caller cannot use any commands at all.
279 caller == NULL means console.
280 **************************************************************************/
281 static bool may_use_nothing(struct connection *caller)
283 if (!caller) {
284 return FALSE; /* on the console, everything is allowed */
286 return (ALLOW_NONE == conn_get_access(caller));
289 /**************************************************************************
290 Return the status of the setting (changeable, locked, fixed).
291 caller == NULL means console.
292 **************************************************************************/
293 static char setting_status(struct connection *caller,
294 const struct setting *pset)
296 /* first check for a ruleset lock as this is included in
297 * setting_is_changeable() */
298 if (setting_locked(pset)) {
299 /* setting is locked by the ruleset */
300 return '!';
303 if (setting_is_changeable(pset, caller, NULL, 0)) {
304 /* setting can be changed */
305 return '+';
308 /* setting is fixed */
309 return ' ';
312 /**************************************************************************
313 feedback related to server commands
314 caller == NULL means console.
315 No longer duplicate all output to console.
317 This lowlevel function takes a single line; prefix is prepended to line.
318 **************************************************************************/
319 static void cmd_reply_line(enum command_id cmd, struct connection *caller,
320 enum rfc_status rfc_status, const char *prefix,
321 const char *line)
323 const char *cmdname = cmd < CMD_NUM
324 ? command_name_by_number(cmd)
325 : cmd == CMD_AMBIGUOUS
326 /* TRANS: ambiguous command */
327 ? _("(ambiguous)")
328 : cmd == CMD_UNRECOGNIZED
329 /* TRANS: unrecognized command */
330 ? _("(unknown)")
331 : "(?!?)"; /* this case is a bug! */
333 if (caller) {
334 notify_conn(caller->self, NULL, E_SETTING, ftc_command,
335 "/%s: %s%s", cmdname, prefix, line);
336 /* cc: to the console - testing has proved it's too verbose - rp
337 con_write(rfc_status, "%s/%s: %s%s", caller->name, cmdname, prefix, line);
339 } else {
340 con_write(rfc_status, "%s%s", prefix, line);
343 if (rfc_status == C_OK) {
344 struct packet_chat_msg packet;
346 package_event(&packet, NULL, E_SETTING, ftc_server, "%s", line);
347 conn_list_iterate(game.est_connections, pconn) {
348 /* Do not tell caller, since he was told above! */
349 if (caller != pconn) {
350 send_packet_chat_msg(pconn, &packet);
352 } conn_list_iterate_end;
353 event_cache_add_for_all(&packet);
355 if (NULL != caller) {
356 /* Echo to the console. */
357 log_normal("%s", line);
362 /**************************************************************************
363 va_list version which allow embedded newlines, and each line is sent
364 separately. 'prefix' is prepended to every line _after_ the first line.
365 **************************************************************************/
366 static void vcmd_reply_prefix(enum command_id cmd, struct connection *caller,
367 enum rfc_status rfc_status, const char *prefix,
368 const char *format, va_list ap)
370 char buf[4096];
371 char *c0, *c1;
373 fc_vsnprintf(buf, sizeof(buf), format, ap);
375 c0 = buf;
376 while ((c1 = strstr(c0, "\n"))) {
377 *c1 = '\0';
378 cmd_reply_line(cmd, caller, rfc_status, (c0 == buf ? "" : prefix), c0);
379 c0 = c1 + 1;
382 cmd_reply_line(cmd, caller, rfc_status, (c0 == buf ? "" : prefix), c0);
385 /**************************************************************************
386 var-args version of above
387 duplicate declaration required for attribute to work...
388 **************************************************************************/
389 static void cmd_reply_prefix(enum command_id cmd, struct connection *caller,
390 enum rfc_status rfc_status, const char *prefix,
391 const char *format, ...)
392 fc__attribute((__format__ (__printf__, 5, 6)));
393 static void cmd_reply_prefix(enum command_id cmd, struct connection *caller,
394 enum rfc_status rfc_status, const char *prefix,
395 const char *format, ...)
397 va_list ap;
398 va_start(ap, format);
399 vcmd_reply_prefix(cmd, caller, rfc_status, prefix, format, ap);
400 va_end(ap);
403 /**************************************************************************
404 var-args version as above, no prefix
405 **************************************************************************/
406 void cmd_reply(enum command_id cmd, struct connection *caller,
407 enum rfc_status rfc_status, const char *format, ...)
409 va_list ap;
410 va_start(ap, format);
411 vcmd_reply_prefix(cmd, caller, rfc_status, "", format, ap);
412 va_end(ap);
415 /**************************************************************************
416 Command specific argument parsing has detected that player argument
417 is invalid. This function is common handling for that situation.
418 **************************************************************************/
419 static void cmd_reply_no_such_player(enum command_id cmd,
420 struct connection *caller,
421 const char *name,
422 enum m_pre_result match_result)
424 switch(match_result) {
425 case M_PRE_EMPTY:
426 cmd_reply(cmd, caller, C_SYNTAX,
427 _("Name is empty, so cannot be a player."));
428 break;
429 case M_PRE_LONG:
430 cmd_reply(cmd, caller, C_SYNTAX,
431 _("Name is too long, so cannot be a player."));
432 break;
433 case M_PRE_AMBIGUOUS:
434 cmd_reply(cmd, caller, C_FAIL,
435 _("Player name prefix '%s' is ambiguous."), name);
436 break;
437 case M_PRE_FAIL:
438 cmd_reply(cmd, caller, C_FAIL,
439 _("No player by the name of '%s'."), name);
440 break;
441 default:
442 cmd_reply(cmd, caller, C_FAIL,
443 _("Unexpected match_result %d (%s) for '%s'."),
444 match_result, _(m_pre_description(match_result)), name);
445 log_error("Unexpected match_result %d (%s) for '%s'.",
446 match_result, m_pre_description(match_result), name);
450 /**************************************************************************
451 Command specific argument parsing has detected that connection argument
452 is invalid. This function is common handling for that situation.
453 **************************************************************************/
454 static void cmd_reply_no_such_conn(enum command_id cmd,
455 struct connection *caller,
456 const char *name,
457 enum m_pre_result match_result)
459 switch(match_result) {
460 case M_PRE_EMPTY:
461 cmd_reply(cmd, caller, C_SYNTAX,
462 _("Name is empty, so cannot be a connection."));
463 break;
464 case M_PRE_LONG:
465 cmd_reply(cmd, caller, C_SYNTAX,
466 _("Name is too long, so cannot be a connection."));
467 break;
468 case M_PRE_AMBIGUOUS:
469 cmd_reply(cmd, caller, C_FAIL,
470 _("Connection name prefix '%s' is ambiguous."), name);
471 break;
472 case M_PRE_FAIL:
473 cmd_reply(cmd, caller, C_FAIL,
474 _("No connection by the name of '%s'."), name);
475 break;
476 default:
477 cmd_reply(cmd, caller, C_FAIL,
478 _("Unexpected match_result %d (%s) for '%s'."),
479 match_result, _(m_pre_description(match_result)), name);
480 log_error("Unexpected match_result %d (%s) for '%s'.",
481 match_result, m_pre_description(match_result), name);
485 /**************************************************************************
486 Start sending game info to metaserver.
487 **************************************************************************/
488 static void open_metaserver_connection(struct connection *caller,
489 bool persistent)
491 server_open_meta(persistent);
492 if (send_server_info_to_metaserver(META_INFO)) {
493 cmd_reply(CMD_METACONN, caller, C_OK,
494 _("Open metaserver connection to [%s]."),
495 meta_addr_port());
499 /**************************************************************************
500 Stop sending game info to metaserver.
501 **************************************************************************/
502 static void close_metaserver_connection(struct connection *caller)
504 if (send_server_info_to_metaserver(META_GOODBYE)) {
505 server_close_meta();
506 cmd_reply(CMD_METACONN, caller, C_OK,
507 _("Close metaserver connection to [%s]."),
508 meta_addr_port());
512 /**************************************************************************
513 Handle metaconnection command.
514 **************************************************************************/
515 static bool metaconnection_command(struct connection *caller, char *arg,
516 bool check)
518 bool persistent = FALSE;
520 if ((*arg == '\0')
521 || (!strcmp(arg, "?"))) {
522 if (is_metaserver_open()) {
523 cmd_reply(CMD_METACONN, caller, C_COMMENT,
524 _("Metaserver connection is open."));
525 } else {
526 cmd_reply(CMD_METACONN, caller, C_COMMENT,
527 _("Metaserver connection is closed."));
529 return TRUE;
532 if (!fc_strcasecmp(arg, "p")
533 || !fc_strcasecmp(arg, "persistent")) {
534 persistent = TRUE;
537 if (persistent
538 || !fc_strcasecmp(arg, "u")
539 || !fc_strcasecmp(arg, "up")) {
540 if (!is_metaserver_open()) {
541 if (!check) {
542 open_metaserver_connection(caller, persistent);
544 } else {
545 cmd_reply(CMD_METACONN, caller, C_METAERROR,
546 _("Metaserver connection is already open."));
547 return FALSE;
549 } else if (!fc_strcasecmp(arg, "d")
550 || !fc_strcasecmp(arg, "down")) {
551 if (is_metaserver_open()) {
552 if (!check) {
553 close_metaserver_connection(caller);
555 } else {
556 cmd_reply(CMD_METACONN, caller, C_METAERROR,
557 _("Metaserver connection is already closed."));
558 return FALSE;
560 } else {
561 cmd_reply(CMD_METACONN, caller, C_METAERROR,
562 _("Argument must be 'u', 'up', 'd', 'down', 'p', 'persistent', or '?'."));
563 return FALSE;
565 return TRUE;
568 /**************************************************************************
569 Handle metapatches command.
570 **************************************************************************/
571 static bool metapatches_command(struct connection *caller,
572 char *arg, bool check)
574 if (check) {
575 return TRUE;
578 set_meta_patches_string(arg);
580 if (is_metaserver_open()) {
581 send_server_info_to_metaserver(META_INFO);
582 cmd_reply(CMD_METAPATCHES, caller, C_OK,
583 _("Metaserver patches string set to '%s'."), arg);
584 } else {
585 cmd_reply(CMD_METAPATCHES, caller, C_OK,
586 _("Metaserver patches string set to '%s', "
587 "not reporting to metaserver."), arg);
590 return TRUE;
593 /**************************************************************************
594 Handle metamessage command.
595 **************************************************************************/
596 static bool metamessage_command(struct connection *caller,
597 char *arg, bool check)
599 struct setting *pset;
601 if (check) {
602 return TRUE;
605 set_user_meta_message_string(arg);
606 if (is_metaserver_open()) {
607 send_server_info_to_metaserver(META_INFO);
608 cmd_reply(CMD_METAMESSAGE, caller, C_OK,
609 _("Metaserver message string set to '%s'."), arg);
610 } else {
611 cmd_reply(CMD_METAMESSAGE, caller, C_OK,
612 _("Metaserver message string set to '%s', "
613 "not reporting to metaserver."), arg);
616 /* Metamessage is also a setting. */
617 pset = setting_by_name("metamessage");
618 setting_changed(pset);
619 send_server_setting(NULL, pset);
621 return TRUE;
624 /**************************************************************************
625 Handle metaserver command.
626 **************************************************************************/
627 static bool metaserver_command(struct connection *caller, char *arg,
628 bool check)
630 if (check) {
631 return TRUE;
633 close_metaserver_connection(caller);
635 sz_strlcpy(srvarg.metaserver_addr, arg);
637 cmd_reply(CMD_METASERVER, caller, C_OK,
638 _("Metaserver is now [%s]."), meta_addr_port());
639 return TRUE;
642 /**************************************************************************
643 Returns the serverid
644 **************************************************************************/
645 static bool show_serverid(struct connection *caller, char *arg)
647 cmd_reply(CMD_SRVID, caller, C_COMMENT, _("Server id: %s"), srvarg.serverid);
649 return TRUE;
652 /**************************************************************************
653 For command "save foo";
654 Save the game, with filename=arg, provided server state is ok.
655 **************************************************************************/
656 static bool save_command(struct connection *caller, char *arg, bool check)
658 if (is_restricted(caller)) {
659 cmd_reply(CMD_SAVE, caller, C_FAIL,
660 _("You cannot save games manually on this server."));
661 return FALSE;
663 if (!check) {
664 save_game(arg, "User request", FALSE);
666 return TRUE;
669 /**************************************************************************
670 For command "scensave foo";
671 Save the game, with filename=arg, provided server state is ok.
672 **************************************************************************/
673 static bool scensave_command(struct connection *caller, char *arg, bool check)
675 if (is_restricted(caller)) {
676 cmd_reply(CMD_SAVE, caller, C_FAIL,
677 _("You cannot save games manually on this server."));
678 return FALSE;
680 if (!check) {
681 save_game(arg, "Scenario", TRUE);
683 return TRUE;
686 /**************************************************************************
687 Handle ai player ai toggling.
688 **************************************************************************/
689 void toggle_ai_player_direct(struct connection *caller, struct player *pplayer)
691 fc_assert_ret(pplayer != NULL);
693 if (is_human(pplayer)) {
694 cmd_reply(CMD_AITOGGLE, caller, C_OK,
695 _("%s is now under AI control."),
696 player_name(pplayer));
697 player_set_to_ai_mode(pplayer,
698 !ai_level_is_valid(pplayer->ai_common.skill_level)
699 ? game.info.skill_level
700 : pplayer->ai_common.skill_level);
701 fc_assert(is_ai(pplayer));
702 } else {
703 cmd_reply(CMD_AITOGGLE, caller, C_OK,
704 _("%s is now under human control."),
705 player_name(pplayer));
706 player_set_under_human_control(pplayer);
707 fc_assert(is_human(pplayer));
711 /**************************************************************************
712 Handle aitoggle command.
713 **************************************************************************/
714 static bool toggle_ai_command(struct connection *caller, char *arg, bool check)
716 enum m_pre_result match_result;
717 struct player *pplayer;
719 pplayer = player_by_name_prefix(arg, &match_result);
721 if (!pplayer) {
722 cmd_reply_no_such_player(CMD_AITOGGLE, caller, arg, match_result);
723 return FALSE;
724 } else if (!check) {
725 toggle_ai_player_direct(caller, pplayer);
726 send_player_info_c(pplayer, game.est_connections);
728 return TRUE;
731 /**************************************************************************
732 Creates a named AI player. The function can be called befor the start
733 of the game (see creat_command_pregame()) and for a running game
734 (see creat_command_newcomer(). In the later case, first free player slots
735 are used before the slots of dead players are (re)used.
736 **************************************************************************/
737 static bool create_command(struct connection *caller, const char *str,
738 bool check)
740 enum rfc_status status;
741 char buf[MAX_LEN_CONSOLE_LINE];
743 /* 2 legal arguments, and extra space for stuffing illegal part */
744 char *arg[3];
745 int ntokens;
746 const char *ai_type_name;
748 sz_strlcpy(buf, str);
749 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
751 if (ntokens == 1) {
752 ai_type_name = default_ai_type_name();
753 } else if (ntokens == 2) {
754 ai_type_name = arg[1];
755 } else {
756 cmd_reply(CMD_CREATE, caller, C_SYNTAX,
757 _("Wrong number of arguments to create command."));
758 free_tokens(arg, ntokens);
759 return FALSE;
762 if (game_was_started()) {
763 status = create_command_newcomer(arg[0], ai_type_name, check,
764 NULL, NULL, buf, sizeof(buf));
765 } else {
766 status = create_command_pregame(arg[0], ai_type_name, check,
767 NULL, buf, sizeof(buf));
770 free_tokens(arg, ntokens);
772 if (status != C_OK) {
773 /* No player created. */
774 cmd_reply(CMD_CREATE, caller, status, "%s", buf);
775 return FALSE;
778 if (strlen(buf) > 0) {
779 /* Send a notification. */
780 cmd_reply(CMD_CREATE, caller, C_OK, "%s", buf);
783 return TRUE;
786 /**************************************************************************
787 Try to add a player to a running game in the following order:
789 1. Try to reuse the slot of a dead player with the username 'name'.
790 2. Try to reuse the slot of a dead player.
791 3. Try to use an empty player slot.
793 If 'pnation' is defined this nation is used for the new player.
794 **************************************************************************/
795 enum rfc_status create_command_newcomer(const char *name,
796 const char *ai,
797 bool check,
798 struct nation_type *pnation,
799 struct player **newplayer,
800 char *buf, size_t buflen)
802 struct player *pplayer = NULL;
803 struct research *presearch;
804 bool new_slot = FALSE;
806 /* Check player name. */
807 if (!player_name_check(name, buf, buflen)) {
808 return C_SYNTAX;
811 /* Check first if we can replace a player with
812 * [1a] - the same username. */
813 pplayer = player_by_user(name);
814 if (pplayer && pplayer->is_alive) {
815 fc_snprintf(buf, buflen,
816 _("A living user already exists by that name."));
817 return C_BOUNCE;
820 /* [1b] - the same player name. */
821 pplayer = player_by_name(name);
822 if (pplayer && pplayer->is_alive) {
823 fc_snprintf(buf, buflen,
824 _("A living player already exists by that name."));
825 return C_BOUNCE;
828 if (pnation) {
829 if (!nation_is_in_current_set(pnation)) {
830 fc_snprintf(buf, buflen,
831 _("Can't create player, requested nation %s not in "
832 "current nation set."),
833 nation_plural_translation(pnation));
834 return C_FAIL;
836 players_iterate(aplayer) {
837 if (0 > nations_match(pnation, nation_of_player(aplayer), FALSE)) {
838 fc_snprintf(buf, buflen,
839 _("Can't create players, nation %s conflicts with %s."),
840 nation_plural_for_player(aplayer),
841 nation_plural_for_player(pplayer));
842 return C_FAIL;
844 } players_iterate_end;
845 } else {
846 /* Try to find a nation. */
847 pnation = pick_a_nation(NULL, FALSE, TRUE, NOT_A_BARBARIAN);
848 if (pnation == NO_NATION_SELECTED) {
849 fc_snprintf(buf, buflen,
850 _("Can't create players, no nations available."));
851 return C_FAIL;
855 if (check) {
856 /* All code below will change the game state. */
858 /* Return an empty string. */
859 buf[0] = '\0';
861 return C_OK;
864 if (pplayer) {
865 /* [1] Replace a player. 'pplayer' was set above. */
866 fc_snprintf(buf, buflen,
867 _("%s is replacing dead player %s as an AI-controlled "
868 "player."), name, player_name(pplayer));
869 /* remove player and thus free a player slot */
870 server_remove_player(pplayer);
871 pplayer = NULL;
872 } else if (player_count() == player_slot_count()) {
873 /* [2] All player slots are used; try to remove a dead player. */
874 players_iterate(aplayer) {
875 if (!aplayer->is_alive) {
876 fc_snprintf(buf, buflen,
877 _("%s is replacing dead player %s as an AI-controlled "
878 "player."), name, player_name(aplayer));
879 /* remove player and thus free a player slot */
880 server_remove_player(aplayer);
882 } players_iterate_end;
883 } else {
884 /* [3] An empty player slot must be used for the new player. */
885 new_slot = TRUE;
888 if (new_slot) {
889 if (normal_player_count() == game.server.max_players) {
891 fc_assert(game.server.max_players < MAX_NUM_PLAYERS);
893 game.server.max_players++;
894 log_debug("Increased 'maxplayers' for creation of a new player.");
898 /* Create the new player. */
899 pplayer = server_create_player(-1, ai, NULL, FALSE);
900 if (!pplayer) {
901 fc_snprintf(buf, buflen, _("Failed to create new player %s."), name);
902 return C_FAIL;
905 if (new_slot) {
906 /* 'buf' must be set if a new player slot is used. */
907 fc_snprintf(buf, buflen, _("New player %s created."), name);
910 /* We have a player; now initialise all needed data. */
911 (void) aifill(game.info.aifill);
913 /* Initialise player. */
914 server_player_init(pplayer, TRUE, TRUE);
916 player_nation_defaults(pplayer, pnation, FALSE);
917 pplayer->government = pplayer->target_government =
918 init_government_of_nation(pnation);
919 /* Find a color for the new player. */
920 assign_player_colors();
922 /* TRANS: keep one space at the beginning of the string. */
923 cat_snprintf(buf, buflen, _(" Nation of the new player: %s."),
924 nation_rule_name(pnation));
926 presearch = research_get(pplayer);
927 init_tech(presearch, TRUE);
928 give_initial_techs(presearch, 0);
930 server_player_set_name(pplayer, name);
931 sz_strlcpy(pplayer->username, _(ANON_USER_NAME));
932 pplayer->unassigned_user = TRUE;
934 pplayer->was_created = TRUE; /* must use /remove explicitly to remove */
935 set_as_ai(pplayer);
936 set_ai_level_directer(pplayer, game.info.skill_level);
938 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
940 send_player_info_c(pplayer, NULL);
941 /* Send updated diplstate information to all players. */
942 send_player_diplstate_c(NULL, NULL);
943 /* Send research info after player info, else the client will complain
944 * about invalid team. */
945 send_research_info(presearch, NULL);
946 (void) send_server_info_to_metaserver(META_INFO);
948 if (newplayer != NULL) {
949 *newplayer = pplayer;
951 return C_OK;
954 /**************************************************************************
955 Create player in pregame.
956 **************************************************************************/
957 enum rfc_status create_command_pregame(const char *name,
958 const char *ai,
959 bool check,
960 struct player **newplayer,
961 char *buf, size_t buflen)
963 char leader_name[MAX_LEN_NAME]; /* Must be in whole function scope */
964 struct player *pplayer = NULL;
965 bool rand_name = FALSE;
967 if (name[0] == '\0') {
968 int filled = 1;
970 do {
971 fc_snprintf(leader_name, sizeof(leader_name), "%s*%d", ai, filled++);
972 } while (player_by_name(leader_name));
974 name = leader_name;
975 rand_name = TRUE;
978 if (!player_name_check(name, buf, buflen)) {
979 return C_SYNTAX;
982 if (NULL != player_by_name(name)) {
983 fc_snprintf(buf, buflen,
984 _("A player already exists by that name."));
985 return C_BOUNCE;
987 if (NULL != player_by_user(name)) {
988 fc_snprintf(buf, buflen,
989 _("A user already exists by that name."));
990 return C_BOUNCE;
993 /* Search for first uncontrolled player */
994 pplayer = find_uncontrolled_player();
996 if (NULL == pplayer) {
997 /* Check that we are not going over max players setting */
998 if (normal_player_count() >= game.server.max_players) {
999 fc_snprintf(buf, buflen,
1000 _("Can't add more players, server is full."));
1001 return C_FAIL;
1003 /* Check that we have nations available */
1004 if (normal_player_count() >= server.playable_nations) {
1005 if (nation_set_count() > 1) {
1006 fc_snprintf(buf, buflen,
1007 _("Can't add more players, not enough playable nations "
1008 "in current nation set (see 'nationset' setting)."));
1009 } else {
1010 fc_snprintf(buf, buflen,
1011 _("Can't add more players, not enough playable nations."));
1013 return C_FAIL;
1017 if (pplayer) {
1018 struct ai_type *ait = ai_type_by_name(ai);
1020 if (ait == NULL) {
1021 fc_snprintf(buf, buflen,
1022 _("There is no AI type %s."), ai);
1023 return C_FAIL;
1027 if (check) {
1028 /* All code below will change the game state. */
1030 /* Return an empty string. */
1031 buf[0] = '\0';
1033 return C_OK;
1036 if (pplayer) {
1037 fc_snprintf(buf, buflen,
1038 /* TRANS: <name> replacing <name> ... */
1039 _("%s replacing %s as an AI-controlled player."),
1040 name, player_name(pplayer));
1042 team_remove_player(pplayer);
1043 pplayer->ai = ai_type_by_name(ai);
1044 } else {
1045 /* add new player */
1046 pplayer = server_create_player(-1, ai, NULL, FALSE);
1047 /* pregame so no need to assign_player_colors() */
1048 if (!pplayer) {
1049 fc_snprintf(buf, buflen,
1050 _("Failed to create new player %s."), name);
1051 return C_GENFAIL;
1054 fc_snprintf(buf, buflen,
1055 _("%s has been added as an AI-controlled player (%s)."),
1056 name, ai_name(pplayer->ai));
1058 server_player_init(pplayer, FALSE, TRUE);
1060 server_player_set_name(pplayer, name);
1061 sz_strlcpy(pplayer->username, _(ANON_USER_NAME));
1062 pplayer->unassigned_user = TRUE;
1064 pplayer->was_created = TRUE; /* must use /remove explicitly to remove */
1065 pplayer->random_name = rand_name;
1066 set_as_ai(pplayer);
1067 set_ai_level_directer(pplayer, game.info.skill_level);
1068 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
1069 send_player_info_c(pplayer, game.est_connections);
1071 (void) aifill(game.info.aifill);
1072 reset_all_start_commands(TRUE);
1073 (void) send_server_info_to_metaserver(META_INFO);
1075 if (newplayer != NULL) {
1076 *newplayer = pplayer;
1078 return C_OK;
1081 /**************************************************************************
1082 Handle remove command.
1083 **************************************************************************/
1084 static bool remove_player_command(struct connection *caller, char *arg,
1085 bool check)
1087 enum m_pre_result match_result;
1088 struct player *pplayer;
1089 char name[MAX_LEN_NAME];
1091 pplayer = player_by_name_prefix(arg, &match_result);
1093 if (NULL == pplayer) {
1094 cmd_reply_no_such_player(CMD_REMOVE, caller, arg, match_result);
1095 return FALSE;
1098 if (game_was_started() && caller && caller->access_level < ALLOW_ADMIN) {
1099 cmd_reply(CMD_REMOVE, caller, C_FAIL,
1100 _("Command level '%s' or greater needed to remove a player "
1101 "once the game has started."), cmdlevel_name(ALLOW_ADMIN));
1102 return FALSE;
1104 if (check) {
1105 return TRUE;
1108 sz_strlcpy(name, player_name(pplayer));
1109 server_remove_player(pplayer);
1110 if (!caller || caller->used) { /* may have removed self */
1111 cmd_reply(CMD_REMOVE, caller, C_OK,
1112 _("Removed player %s from the game."), name);
1114 (void) aifill(game.info.aifill);
1115 return TRUE;
1118 /**************************************************************************
1119 Main entry point for the read command.
1120 **************************************************************************/
1121 static bool read_command(struct connection *caller, char *arg, bool check,
1122 int read_recursion)
1124 return read_init_script_real(caller, arg, FALSE, check, read_recursion);
1127 /**************************************************************************
1128 Main entry point for reading an init script.
1129 **************************************************************************/
1130 bool read_init_script(struct connection *caller, char *script_filename,
1131 bool from_cmdline, bool check)
1133 return read_init_script_real(caller, script_filename, from_cmdline,
1134 check, 0);
1137 /**************************************************************************
1138 Returns FALSE iff there was an error.
1140 Security: We will look for a file with mandatory extension '.serv',
1141 and on public servers we will not look outside the data directories.
1142 As long as the user cannot create files with arbitrary names in the
1143 root of the data directories, this should ensure that we will not be
1144 tricked into loading non-approved content. The script is read with the
1145 permissions of the caller, so it will in any case not lead to elevated
1146 permissions unless there are other bugs.
1147 **************************************************************************/
1148 static bool read_init_script_real(struct connection *caller,
1149 char *script_filename, bool from_cmdline,
1150 bool check, int read_recursion)
1152 FILE *script_file;
1153 const char extension[] = ".serv";
1154 char serv_filename[strlen(extension) + strlen(script_filename) + 2];
1155 char tilde_filename[4096];
1156 const char *real_filename;
1158 /* check recursion depth */
1159 if (read_recursion > GAME_MAX_READ_RECURSION) {
1160 log_error("Error: recursive calls to read!");
1161 return FALSE;
1164 /* abuse real_filename to find if we already have a .serv extension */
1165 real_filename = script_filename + strlen(script_filename)
1166 - MIN(strlen(extension), strlen(script_filename));
1167 if (strcmp(real_filename, extension) != 0) {
1168 fc_snprintf(serv_filename, sizeof(serv_filename), "%s%s",
1169 script_filename, extension);
1170 } else {
1171 sz_strlcpy(serv_filename, script_filename);
1174 if (is_restricted(caller) && !from_cmdline) {
1175 if (!is_safe_filename(serv_filename)) {
1176 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1177 _("Name \"%s\" disallowed for security reasons."),
1178 serv_filename);
1179 return FALSE;
1181 sz_strlcpy(tilde_filename, serv_filename);
1182 } else {
1183 interpret_tilde(tilde_filename, sizeof(tilde_filename), serv_filename);
1186 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
1187 if (!real_filename) {
1188 if (is_restricted(caller) && !from_cmdline) {
1189 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1190 _("No command script found by the name \"%s\"."),
1191 serv_filename);
1192 return FALSE;
1194 /* File is outside data directories */
1195 real_filename = tilde_filename;
1198 log_testmatic_alt(LOG_NORMAL, _("Loading script file '%s'."), real_filename);
1200 if (is_reg_file_for_access(real_filename, FALSE)
1201 && (script_file = fc_fopen(real_filename, "r"))) {
1202 char buffer[MAX_LEN_CONSOLE_LINE];
1204 /* the size is set as to not overflow buffer in handle_stdin_input */
1205 while (fgets(buffer, MAX_LEN_CONSOLE_LINE - 1, script_file)) {
1206 /* Execute script contents with same permissions as caller */
1207 handle_stdin_input_real(caller, buffer, check, read_recursion + 1);
1209 fclose(script_file);
1211 show_ruleset_info(caller, CMD_READ_SCRIPT, check, read_recursion);
1213 return TRUE;
1214 } else {
1215 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1216 _("Cannot read command line scriptfile '%s'."), real_filename);
1217 if (NULL != caller) {
1218 log_error(_("Could not read script file '%s'."), real_filename);
1220 return FALSE;
1224 /**************************************************************************
1225 Write current settings to new init script.
1227 (Should this take a 'caller' argument for output? --dwp)
1228 **************************************************************************/
1229 static void write_init_script(char *script_filename)
1231 char real_filename[1024], buf[256];
1232 FILE *script_file;
1234 interpret_tilde(real_filename, sizeof(real_filename), script_filename);
1236 if (is_reg_file_for_access(real_filename, TRUE)
1237 && (script_file = fc_fopen(real_filename, "w"))) {
1238 fprintf(script_file,
1239 "#FREECIV SERVER COMMAND FILE, version %s\n", VERSION_STRING);
1240 fputs("# These are server options saved from a running freeciv-server.\n",
1241 script_file);
1243 /* first rulesetdir. Setting rulesetdir resets the settings to their
1244 * default value, so they would be lost if placed before this. */
1245 fprintf(script_file, "rulesetdir %s\n", game.server.rulesetdir);
1247 /* some state info from commands (we can't save everything) */
1249 fprintf(script_file, "cmdlevel %s new\n",
1250 cmdlevel_name(default_access_level));
1252 fprintf(script_file, "cmdlevel %s first\n",
1253 cmdlevel_name(first_access_level));
1255 fprintf(script_file, "%s\n",
1256 ai_level_cmd(game.info.skill_level));
1258 if (*srvarg.metaserver_addr != '\0' &&
1259 ((0 != strcmp(srvarg.metaserver_addr, DEFAULT_META_SERVER_ADDR)))) {
1260 fprintf(script_file, "metaserver %s\n", meta_addr_port());
1263 if (0 != strcmp(get_meta_patches_string(), default_meta_patches_string())) {
1264 fprintf(script_file, "metapatches %s\n", get_meta_patches_string());
1266 if (0 != strcmp(get_meta_message_string(), default_meta_message_string())) {
1267 fprintf(script_file, "metamessage %s\n", get_meta_message_string());
1270 /* then, the 'set' option settings */
1272 settings_iterate(SSET_ALL, pset) {
1273 fprintf(script_file, "set %s \"%s\"\n", setting_name(pset),
1274 setting_value_name(pset, FALSE, buf, sizeof(buf)));
1275 } settings_iterate_end;
1277 fclose(script_file);
1279 } else {
1280 log_error(_("Could not write script file '%s'."), real_filename);
1284 /**************************************************************************
1285 Generate init script from settings currently in use
1286 **************************************************************************/
1287 static bool write_command(struct connection *caller, char *arg, bool check)
1289 if (is_restricted(caller)) {
1290 cmd_reply(CMD_WRITE_SCRIPT, caller, C_FAIL,
1291 _("You cannot use the write command on this server"
1292 " for security reasons."));
1293 return FALSE;
1294 } else if (!check) {
1295 write_init_script(arg);
1297 return TRUE;
1300 /**************************************************************************
1301 set ptarget's cmdlevel to level if caller is allowed to do so
1302 **************************************************************************/
1303 static bool set_cmdlevel(struct connection *caller,
1304 struct connection *ptarget,
1305 enum cmdlevel level)
1307 /* Only ever call me for specific connection. */
1308 fc_assert_ret_val(ptarget != NULL, FALSE);
1310 if (caller && ptarget->access_level > caller->access_level) {
1312 * This command is intended to be used at ctrl access level
1313 * and thus this if clause is needed.
1314 * (Imagine a ctrl level access player that wants to change
1315 * access level of a hack level access player)
1316 * At the moment it can be used only by hack access level
1317 * and thus this clause is never used.
1319 cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1320 _("Cannot decrease command access level '%s' "
1321 "for connection '%s'; you only have '%s'."),
1322 cmdlevel_name(ptarget->access_level),
1323 ptarget->username,
1324 cmdlevel_name(caller->access_level));
1325 return FALSE;
1326 } else {
1327 conn_set_access(ptarget, level, TRUE);
1328 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1329 _("Command access level set to '%s' for connection %s."),
1330 cmdlevel_name(level), ptarget->username);
1331 return TRUE;
1335 /********************************************************************
1336 Returns true if there is at least one established connection.
1337 *********************************************************************/
1338 static bool a_connection_exists(void)
1340 return conn_list_size(game.est_connections) > 0;
1343 /********************************************************************
1344 Return whether first access level is already taken.
1345 *********************************************************************/
1346 static bool is_first_access_level_taken(void)
1348 conn_list_iterate(game.est_connections, pconn) {
1349 if (pconn->access_level >= first_access_level) {
1350 return TRUE;
1353 conn_list_iterate_end;
1354 return FALSE;
1357 /********************************************************************
1358 Return access level for next connection
1359 *********************************************************************/
1360 enum cmdlevel access_level_for_next_connection(void)
1362 if ((first_access_level > default_access_level)
1363 && !a_connection_exists()) {
1364 return first_access_level;
1365 } else {
1366 return default_access_level;
1370 /********************************************************************
1371 Check if first access level is available and if it is, notify
1372 connections about it.
1373 *********************************************************************/
1374 void notify_if_first_access_level_is_available(void)
1376 if (first_access_level > default_access_level
1377 && !is_first_access_level_taken()) {
1378 notify_conn(NULL, NULL, E_SETTING, ftc_any,
1379 _("Anyone can now become game organizer "
1380 "'%s' by issuing the 'first' command."),
1381 cmdlevel_name(first_access_level));
1385 /**************************************************************************
1386 Change command access level for individual player, or all, or new.
1387 **************************************************************************/
1388 static bool cmdlevel_command(struct connection *caller, char *str, bool check)
1390 char *arg[2];
1391 int ntokens;
1392 bool ret = FALSE;
1393 enum m_pre_result match_result;
1394 enum cmdlevel level;
1395 struct connection *ptarget;
1397 ntokens = get_tokens(str, arg, 2, TOKEN_DELIMITERS);
1399 if (ntokens == 0) {
1400 /* No argument supplied; list the levels */
1401 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1402 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1403 _("Command access levels in effect:"));
1404 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1405 conn_list_iterate(game.est_connections, pconn) {
1406 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, "cmdlevel %s %s",
1407 cmdlevel_name(conn_get_access(pconn)), pconn->username);
1408 } conn_list_iterate_end;
1409 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1410 _("Command access level for new connections: %s"),
1411 cmdlevel_name(default_access_level));
1412 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1413 _("Command access level for first player to take it: %s"),
1414 cmdlevel_name(first_access_level));
1415 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1416 return TRUE;
1419 /* A level name was supplied; set the level. */
1420 level = cmdlevel_by_name(arg[0], fc_strcasecmp);
1421 if (!cmdlevel_is_valid(level)) {
1422 const char *cmdlevel_names[CMDLEVEL_COUNT];
1423 struct astring astr = ASTRING_INIT;
1424 int i = 0;
1426 for (level = cmdlevel_begin(); level != cmdlevel_end();
1427 level = cmdlevel_next(level)) {
1428 cmdlevel_names[i++] = cmdlevel_name(level);
1430 cmd_reply(CMD_CMDLEVEL, caller, C_SYNTAX,
1431 /* TRANS: comma and 'or' separated list of access levels */
1432 _("Command access level must be one of %s."),
1433 astr_build_or_list(&astr, cmdlevel_names, i));
1434 astr_free(&astr);
1435 goto CLEAN_UP;
1436 } else if (caller && level > conn_get_access(caller)) {
1437 cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1438 _("Cannot increase command access level to '%s';"
1439 " you only have '%s' yourself."),
1440 arg[0], cmdlevel_name(conn_get_access(caller)));
1441 goto CLEAN_UP;
1444 if (check) {
1445 return TRUE; /* looks good */
1448 if (ntokens == 1) {
1449 /* No playername supplied: set for all connections */
1450 conn_list_iterate(game.est_connections, pconn) {
1451 if (pconn != caller) {
1452 (void) set_cmdlevel(caller, pconn, level);
1454 } conn_list_iterate_end;
1456 /* Set the caller access level at last, because it could make the
1457 * previous operations impossible if set before. */
1458 if (caller) {
1459 (void) set_cmdlevel(caller, caller, level);
1462 /* Set default access for new connections. */
1463 default_access_level = level;
1464 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1465 _("Command access level set to '%s' for new players."),
1466 cmdlevel_name(level));
1467 /* Set default access for first connection. */
1468 first_access_level = level;
1469 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1470 _("Command access level set to '%s' "
1471 "for first player to grab it."),
1472 cmdlevel_name(level));
1474 ret = TRUE;
1476 } else if (fc_strcasecmp(arg[1], "new") == 0) {
1477 default_access_level = level;
1478 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1479 _("Command access level set to '%s' for new players."),
1480 cmdlevel_name(level));
1481 if (level > first_access_level) {
1482 first_access_level = level;
1483 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1484 _("Command access level set to '%s' "
1485 "for first player to grab it."),
1486 cmdlevel_name(level));
1489 ret = TRUE;
1491 } else if (fc_strcasecmp(arg[1], "first") == 0) {
1492 first_access_level = level;
1493 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1494 _("Command access level set to '%s' "
1495 "for first player to grab it."),
1496 cmdlevel_name(level));
1497 if (level < default_access_level) {
1498 default_access_level = level;
1499 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1500 _("Command access level set to '%s' for new players."),
1501 cmdlevel_name(level));
1504 ret = TRUE;
1506 } else if ((ptarget = conn_by_user_prefix(arg[1], &match_result))) {
1507 if (set_cmdlevel(caller, ptarget, level)) {
1508 ret = TRUE;
1510 } else {
1511 cmd_reply_no_such_conn(CMD_CMDLEVEL, caller, arg[1], match_result);
1514 CLEAN_UP:
1515 free_tokens(arg, ntokens);
1516 return ret;
1519 /**************************************************************************
1520 This special command to set the command access level is not included into
1521 cmdlevel_command because of its lower access level: it can be used
1522 to promote one's own connection to 'first come' cmdlevel if that isn't
1523 already taken.
1524 **************************************************************************/
1525 static bool firstlevel_command(struct connection *caller, bool check)
1527 if (!caller) {
1528 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1529 _("The 'first' command makes no sense from the server command line."));
1530 return FALSE;
1531 } else if (caller->access_level >= first_access_level) {
1532 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1533 _("You already have command access level '%s' or better."),
1534 cmdlevel_name(first_access_level));
1535 return FALSE;
1536 } else if (is_first_access_level_taken()) {
1537 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1538 _("Someone else is already game organizer."));
1539 return FALSE;
1540 } else if (!check) {
1541 conn_set_access(caller, first_access_level, FALSE);
1542 cmd_reply(CMD_FIRSTLEVEL, caller, C_OK,
1543 _("Connection %s has opted to become the game organizer."),
1544 caller->username);
1546 return TRUE;
1549 /**************************************************************************
1550 Adjust default command level on game start.
1551 **************************************************************************/
1552 void set_running_game_access_level(void)
1554 if (default_access_level > ALLOW_BASIC) {
1555 notify_conn(NULL, NULL, E_SETTING, ftc_server,
1556 _("Default cmdlevel lowered to 'basic' on game start."));
1557 default_access_level = ALLOW_BASIC;
1561 /**************************************************************************
1562 Returns possible parameters for the commands that take server options
1563 as parameters (CMD_EXPLAIN and CMD_SET).
1564 **************************************************************************/
1565 static const char *optname_accessor(int i)
1567 return setting_name(setting_by_number(i));
1570 #ifdef FREECIV_HAVE_LIBREADLINE
1571 /**************************************************************************
1572 Returns possible parameters for the /show command.
1573 **************************************************************************/
1574 static const char *olvlname_accessor(int i)
1576 if (i == 0) {
1577 return "rulesetdir";
1578 } else if (i < OLEVELS_NUM+1) {
1579 return sset_level_name(i-1);
1580 } else {
1581 return optname_accessor(i-OLEVELS_NUM-1);
1584 #endif /* FREECIV_HAVE_LIBREADLINE */
1586 /**************************************************************************
1587 Set timeout options.
1588 **************************************************************************/
1589 static bool timeout_command(struct connection *caller, char *str, bool check)
1591 char buf[MAX_LEN_CONSOLE_LINE];
1592 char *arg[4];
1593 int i = 0, ntokens;
1594 int *timeouts[4];
1596 timeouts[0] = &game.server.timeoutint;
1597 timeouts[1] = &game.server.timeoutintinc;
1598 timeouts[2] = &game.server.timeoutinc;
1599 timeouts[3] = &game.server.timeoutincmult;
1601 sz_strlcpy(buf, str);
1602 ntokens = get_tokens(buf, arg, 4, TOKEN_DELIMITERS);
1604 for (i = 0; i < ntokens; i++) {
1605 if (!str_to_int(arg[i], timeouts[i])) {
1606 cmd_reply(CMD_TIMEOUT, caller, C_FAIL, _("Invalid argument %d."),
1607 i + 1);
1609 free(arg[i]);
1612 if (ntokens == 0) {
1613 cmd_reply(CMD_TIMEOUT, caller, C_SYNTAX, _("Usage:\n%s"),
1614 command_synopsis(command_by_number(CMD_TIMEOUT)));
1615 return FALSE;
1616 } else if (check) {
1617 return TRUE;
1620 cmd_reply(CMD_TIMEOUT, caller, C_OK, _("Dynamic timeout set to "
1621 "%d %d %d %d"),
1622 game.server.timeoutint, game.server.timeoutintinc,
1623 game.server.timeoutinc, game.server.timeoutincmult);
1625 /* if we set anything here, reset the counter */
1626 game.server.timeoutcounter = 1;
1627 return TRUE;
1630 /**************************************************************************
1631 Find option level number by name.
1632 **************************************************************************/
1633 static enum sset_level lookup_option_level(const char *name)
1635 enum sset_level i;
1637 for (i = SSET_ALL; i < OLEVELS_NUM; i++) {
1638 if (0 == fc_strcasecmp(name, sset_level_name(i))) {
1639 return i;
1643 return SSET_NONE;
1646 /* Special return values of lookup options */
1647 #define LOOKUP_OPTION_NO_RESULT (-1)
1648 #define LOOKUP_OPTION_AMBIGUOUS (-2)
1649 #define LOOKUP_OPTION_LEVEL_NAME (-3)
1650 #define LOOKUP_OPTION_RULESETDIR (-4)
1652 /**************************************************************************
1653 Find option index by name. Return index (>=0) on success, else returned
1654 - LOOKUP_OPTION_NO_RESULT if no suitable options were found
1655 - LOOKUP_OPTION_AMBIGUOUS if several matches were found
1656 - LOOKUP_OPTION_LEVEL_NAME if it is an option level
1657 - LOOKUP_OPTION_RULESETDIR if the argument is rulesetdir (special case)
1658 **************************************************************************/
1659 static int lookup_option(const char *name)
1661 enum m_pre_result result;
1662 int ind;
1664 /* Check for option levels, first off */
1665 if (lookup_option_level(name) != SSET_NONE) {
1666 return LOOKUP_OPTION_LEVEL_NAME;
1669 result = match_prefix(optname_accessor, settings_number(),
1670 0, fc_strncasecmp, NULL, name, &ind);
1671 if (M_PRE_AMBIGUOUS > result) {
1672 return ind;
1673 } else if (M_PRE_AMBIGUOUS == result) {
1674 return LOOKUP_OPTION_AMBIGUOUS;
1675 } else if ('\0' != name[0]
1676 && 0 == fc_strncasecmp("rulesetdir", name, strlen(name))) {
1677 return LOOKUP_OPTION_RULESETDIR;
1678 } else {
1679 return LOOKUP_OPTION_NO_RESULT;
1683 /**************************************************************************
1684 Show the caller detailed help for the single OPTION given by id.
1685 help_cmd is the command the player used.
1686 Only show option values for options which the caller can SEE.
1687 **************************************************************************/
1688 static void show_help_option(struct connection *caller,
1689 enum command_id help_cmd, int id)
1691 char val_buf[256], def_buf[256];
1692 struct setting *pset = setting_by_number(id);
1693 const char *sethelp;
1695 if (setting_short_help(pset)) {
1696 cmd_reply(help_cmd, caller, C_COMMENT,
1697 /* TRANS: <untranslated name> - translated short help */
1698 _("Option: %s - %s"), setting_name(pset),
1699 _(setting_short_help(pset)));
1700 } else {
1701 cmd_reply(help_cmd, caller, C_COMMENT,
1702 /* TRANS: <untranslated name> */
1703 _("Option: %s"), setting_name(pset));
1706 sethelp = setting_extra_help(pset, FALSE);
1707 if (strlen(sethelp) > 0) {
1708 char *help = fc_strdup(sethelp);
1710 fc_break_lines(help, LINE_BREAK);
1711 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
1712 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
1713 FC_FREE(help);
1715 cmd_reply(help_cmd, caller, C_COMMENT,
1716 _("Status: %s"), (setting_is_changeable(pset, NULL, NULL, 0)
1717 ? _("changeable") : _("fixed")));
1719 if (setting_is_visible(pset, caller)) {
1720 setting_value_name(pset, TRUE, val_buf, sizeof(val_buf));
1721 setting_default_name(pset, TRUE, def_buf, sizeof(def_buf));
1723 switch (setting_type(pset)) {
1724 case SST_INT:
1725 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %d, %s %s, %s %d",
1726 _("Value:"), val_buf,
1727 _("Minimum:"), setting_int_min(pset),
1728 _("Default:"), def_buf,
1729 _("Maximum:"), setting_int_max(pset));
1730 break;
1731 case SST_ENUM:
1733 int i;
1734 const char *value;
1736 cmd_reply(help_cmd, caller, C_COMMENT, _("Possible values:"));
1737 for (i = 0; (value = setting_enum_val(pset, i, FALSE)); i++) {
1738 cmd_reply(help_cmd, caller, C_COMMENT, "- %s: \"%s\"",
1739 value, setting_enum_val(pset, i, TRUE));
1742 /* Fall through. */
1743 case SST_BOOL:
1744 case SST_STRING:
1745 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %s",
1746 _("Value:"), val_buf, _("Default:"), def_buf);
1747 break;
1748 case SST_BITWISE:
1750 int i;
1751 const char *value;
1753 cmd_reply(help_cmd, caller, C_COMMENT,
1754 _("Possible values (option can take any number of these):"));
1755 for (i = 0; (value = setting_bitwise_bit(pset, i, FALSE)); i++) {
1756 cmd_reply(help_cmd, caller, C_COMMENT, "- %s: \"%s\"",
1757 value, setting_bitwise_bit(pset, i, TRUE));
1759 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s",
1760 _("Value:"), val_buf);
1761 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s",
1762 _("Default:"), def_buf);
1764 break;
1765 case SST_COUNT:
1766 fc_assert(setting_type(pset) != SST_COUNT);
1767 break;
1772 /**************************************************************************
1773 Show the caller list of OPTIONS.
1774 help_cmd is the command the player used.
1775 Only show options which the caller can SEE.
1776 **************************************************************************/
1777 static void show_help_option_list(struct connection *caller,
1778 enum command_id help_cmd)
1780 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1781 cmd_reply(help_cmd, caller, C_COMMENT,
1782 _("Explanations are available for the following server options:"));
1783 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1784 if (!caller && con_get_style()) {
1785 settings_iterate(SSET_ALL, pset) {
1786 cmd_reply(help_cmd, caller, C_COMMENT, "%s", setting_name(pset));
1787 } settings_iterate_end
1788 } else {
1789 char buf[MAX_LEN_CONSOLE_LINE];
1790 int j = 0;
1791 buf[0] = '\0';
1793 settings_iterate(SSET_ALL, pset) {
1794 if (setting_is_visible(pset, caller)) {
1795 cat_snprintf(buf, sizeof(buf), "%-19s", setting_name(pset));
1796 if ((++j % 4) == 0) {
1797 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1798 buf[0] = '\0';
1801 } settings_iterate_end;
1803 if (buf[0] != '\0') {
1804 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1807 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1810 /**************************************************************************
1811 Handle explain command
1812 **************************************************************************/
1813 static bool explain_option(struct connection *caller, char *str, bool check)
1815 int cmd;
1817 remove_leading_trailing_spaces(str);
1819 if (*str != '\0') {
1820 cmd = lookup_option(str);
1821 if (cmd >= 0 && cmd < settings_number()) {
1822 show_help_option(caller, CMD_EXPLAIN, cmd);
1823 } else if (cmd == LOOKUP_OPTION_NO_RESULT
1824 || cmd == LOOKUP_OPTION_LEVEL_NAME
1825 || cmd == LOOKUP_OPTION_RULESETDIR) {
1826 cmd_reply(CMD_EXPLAIN, caller, C_FAIL,
1827 _("No explanation for that yet."));
1828 return FALSE;
1829 } else if (cmd == LOOKUP_OPTION_AMBIGUOUS) {
1830 cmd_reply(CMD_EXPLAIN, caller, C_FAIL, _("Ambiguous option name."));
1831 return FALSE;
1832 } else {
1833 log_error("Unexpected case %d in %s line %d", cmd, __FILE__,
1834 __FC_LINE__);
1835 return FALSE;
1837 } else {
1838 show_help_option_list(caller, CMD_EXPLAIN);
1840 return TRUE;
1843 /******************************************************************
1844 Send a message to all players
1845 ******************************************************************/
1846 static bool wall(char *str, bool check)
1848 if (!check) {
1849 notify_conn(NULL, NULL, E_MESSAGE_WALL, ftc_server_prompt,
1850 _("Server Operator: %s"), str);
1852 return TRUE;
1855 /******************************************************************
1856 Set message to send to all new connections
1857 ******************************************************************/
1858 static bool connectmsg_command(struct connection *caller, char *str,
1859 bool check)
1861 unsigned int bufsize = sizeof(game.server.connectmsg);
1863 if (is_restricted(caller)) {
1864 return FALSE;
1866 if (!check) {
1867 int i;
1868 int c = 0;
1870 for (i = 0; c < bufsize -1 && str[i] != '\0'; i++) {
1871 if (str[i] == '\\') {
1872 i++;
1874 if (str[i] == 'n') {
1875 game.server.connectmsg[c++] = '\n';
1876 } else {
1877 game.server.connectmsg[c++] = str[i];
1879 } else {
1880 game.server.connectmsg[c++] = str[i];
1884 game.server.connectmsg[c++] = '\0';
1886 if (c == bufsize) {
1887 /* Truncated */
1888 cmd_reply(CMD_CONNECTMSG, caller, C_WARNING,
1889 _("Connectmsg truncated to %u bytes."), bufsize);
1892 return TRUE;
1895 /******************************************************************
1896 Translate an AI level back to its CMD_* value.
1897 If we just used /set ailevel <num> we wouldn't have to do this - rp
1898 ******************************************************************/
1899 static enum command_id cmd_of_level(enum ai_level level)
1901 switch (level) {
1902 case AI_LEVEL_AWAY : return CMD_AWAY;
1903 case AI_LEVEL_HANDICAPPED : return CMD_HANDICAPPED;
1904 case AI_LEVEL_NOVICE : return CMD_NOVICE;
1905 case AI_LEVEL_EASY : return CMD_EASY;
1906 case AI_LEVEL_NORMAL : return CMD_NORMAL;
1907 case AI_LEVEL_HARD : return CMD_HARD;
1908 case AI_LEVEL_CHEATING : return CMD_CHEATING;
1909 #ifdef FREECIV_DEBUG
1910 case AI_LEVEL_EXPERIMENTAL : return CMD_EXPERIMENTAL;
1911 #endif /* FREECIV_DEBUG */
1912 case AI_LEVEL_COUNT : return CMD_NORMAL;
1914 log_error("Unknown AI level variant: %d.", level);
1915 return CMD_NORMAL;
1918 /******************************************************************
1919 Set an AI level from the server prompt.
1920 ******************************************************************/
1921 void set_ai_level_direct(struct player *pplayer, enum ai_level level)
1923 set_ai_level_directer(pplayer, level);
1924 send_player_info_c(pplayer, NULL);
1925 cmd_reply(cmd_of_level(level), NULL, C_OK,
1926 _("Player '%s' now has AI skill level '%s'."),
1927 player_name(pplayer),
1928 ai_level_translated_name(level));
1932 /******************************************************************
1933 Handle a user command to set an AI level.
1934 ******************************************************************/
1935 static bool set_ai_level_named(struct connection *caller, const char *name,
1936 const char *level_name, bool check)
1938 enum ai_level level = ai_level_by_name(level_name, fc_strcasecmp);
1940 return set_ai_level(caller, name, level, check);
1943 /******************************************************************
1944 Set AI level
1945 ******************************************************************/
1946 static bool set_ai_level(struct connection *caller, const char *name,
1947 enum ai_level level, bool check)
1949 enum m_pre_result match_result;
1950 struct player *pplayer;
1952 fc_assert_ret_val(level > 0 && level < 11, FALSE);
1954 pplayer = player_by_name_prefix(name, &match_result);
1956 if (pplayer) {
1957 if (is_ai(pplayer)) {
1958 if (check) {
1959 return TRUE;
1961 set_ai_level_directer(pplayer, level);
1962 send_player_info_c(pplayer, NULL);
1963 cmd_reply(cmd_of_level(level), caller, C_OK,
1964 _("Player '%s' now has AI skill level '%s'."),
1965 player_name(pplayer),
1966 ai_level_translated_name(level));
1967 } else {
1968 cmd_reply(cmd_of_level(level), caller, C_FAIL,
1969 _("%s is not controlled by the AI."),
1970 player_name(pplayer));
1971 return FALSE;
1973 } else if (match_result == M_PRE_EMPTY) {
1974 if (check) {
1975 return TRUE;
1977 players_iterate(cplayer) {
1978 if (is_ai(cplayer)) {
1979 set_ai_level_directer(cplayer, level);
1980 send_player_info_c(cplayer, NULL);
1981 cmd_reply(cmd_of_level(level), caller, C_OK,
1982 _("Player '%s' now has AI skill level '%s'."),
1983 player_name(cplayer),
1984 ai_level_translated_name(level));
1986 } players_iterate_end;
1987 game.info.skill_level = level;
1988 cmd_reply(cmd_of_level(level), caller, C_OK,
1989 _("Default AI skill level set to '%s'."),
1990 ai_level_translated_name(level));
1991 } else {
1992 cmd_reply_no_such_player(cmd_of_level(level), caller, name, match_result);
1993 return FALSE;
1995 return TRUE;
1998 /******************************************************************
1999 Set user to away mode.
2000 ******************************************************************/
2001 static bool away_command(struct connection *caller, bool check)
2003 struct player *pplayer;
2005 if (caller == NULL) {
2006 cmd_reply(CMD_AWAY, caller, C_FAIL, _("This command is client only."));
2007 return FALSE;
2010 if (!conn_controls_player(caller)) {
2011 /* This happens for detached or observer connections. */
2012 cmd_reply(CMD_AWAY, caller, C_FAIL,
2013 _("Only players may use the away command."));
2014 return FALSE;
2017 if (check) {
2018 return TRUE;
2021 pplayer = conn_get_player(caller);
2022 if (is_human(pplayer)) {
2023 cmd_reply(CMD_AWAY, caller, C_OK,
2024 _("%s set to away mode."), player_name(pplayer));
2025 player_set_to_ai_mode(pplayer, AI_LEVEL_AWAY);
2026 fc_assert(!is_human(pplayer));
2027 } else {
2028 cmd_reply(CMD_AWAY, caller, C_OK,
2029 _("%s returned to game."), player_name(pplayer));
2030 player_set_under_human_control(pplayer);
2031 fc_assert(is_human(pplayer));
2034 send_player_info_c(caller->playing, game.est_connections);
2036 return TRUE;
2039 /**************************************************************************
2040 Show changed settings and ruleset summary.
2041 **************************************************************************/
2042 static void show_ruleset_info(struct connection *caller, enum command_id cmd,
2043 bool check, int read_recursion)
2045 char *show_arg = "changed";
2047 /* show changed settings only at the top level of recursion */
2048 if (read_recursion != 0) {
2049 return;
2052 show_settings(caller, cmd, show_arg, check);
2054 if (game.ruleset_summary != NULL) {
2055 char *translated = fc_strdup(_(game.ruleset_summary));
2057 fc_break_lines(translated, LINE_BREAK);
2058 cmd_reply(cmd, caller, C_COMMENT, "%s", translated);
2059 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
2060 free(translated);
2064 /**************************************************************************
2065 /show command: show settings and their values.
2066 **************************************************************************/
2067 static bool show_command(struct connection *caller, char *str, bool check)
2069 return show_settings(caller, CMD_SHOW, str, check);
2072 /**************************************************************************
2073 Print a summary of the settings and their values. Note that most values
2074 are at most 4 digits, except seeds, which we let overflow their columns,
2075 plus a sign character. Only show options which the caller can SEE.
2076 **************************************************************************/
2077 static bool show_settings(struct connection *caller,
2078 enum command_id called_as,
2079 char *str, bool check)
2081 int cmd;
2082 enum sset_level level = SSET_ALL;
2083 size_t clen = 0;
2085 remove_leading_trailing_spaces(str);
2086 if (str[0] != '\0') {
2087 /* In "/show forests", figure out that it's the forests option we're
2088 * looking at. */
2089 cmd = lookup_option(str);
2090 if (cmd >= 0) {
2091 /* Ignore levels when a particular option is specified. */
2092 level = SSET_NONE;
2094 if (!setting_is_visible(setting_by_number(cmd), caller)) {
2095 cmd_reply(called_as, caller, C_FAIL,
2096 _("Sorry, you do not have access to view option '%s'."),
2097 str);
2098 return FALSE;
2102 /* Valid negative values for 'cmd' are defined as LOOKUP_OPTION_*. */
2103 switch (cmd) {
2104 case LOOKUP_OPTION_NO_RESULT:
2105 cmd_reply(called_as, caller, C_FAIL, _("Unknown option '%s'."), str);
2106 return FALSE;
2107 case LOOKUP_OPTION_AMBIGUOUS:
2108 /* Allow ambiguous: show all matching. */
2109 clen = strlen(str);
2110 break;
2111 case LOOKUP_OPTION_LEVEL_NAME:
2112 /* Option level. */
2113 level = lookup_option_level(str);
2114 break;
2115 case LOOKUP_OPTION_RULESETDIR:
2116 /* Ruleset. */
2117 cmd_reply(called_as, caller, C_COMMENT,
2118 _("Current ruleset directory is \"%s\""),
2119 game.server.rulesetdir);
2120 return TRUE;
2122 } else {
2123 /* to indicate that no command was specified */
2124 cmd = LOOKUP_OPTION_NO_RESULT;
2125 /* Use vital level by default. */
2126 level = SSET_VITAL;
2129 fc_assert_ret_val(cmd >= 0 || cmd == LOOKUP_OPTION_AMBIGUOUS
2130 || cmd == LOOKUP_OPTION_LEVEL_NAME
2131 || cmd == LOOKUP_OPTION_NO_RESULT, FALSE);
2133 #define cmd_reply_show(string) \
2134 cmd_reply(called_as, caller, C_COMMENT, "%s", string)
2137 const char *heading = NULL;
2138 switch(level) {
2139 case SSET_NONE:
2140 break;
2141 case SSET_CHANGED:
2142 heading = _("All options with non-default values");
2143 break;
2144 case SSET_ALL:
2145 heading = _("All options");
2146 break;
2147 case SSET_VITAL:
2148 heading = _("Vital options");
2149 break;
2150 case SSET_SITUATIONAL:
2151 heading = _("Situational options");
2152 break;
2153 case SSET_RARE:
2154 heading = _("Rarely used options");
2155 break;
2156 case SSET_LOCKED:
2157 heading = _("Options locked by the ruleset");
2158 break;
2159 case OLEVELS_NUM:
2160 /* nothing */
2161 break;
2163 if (heading) {
2164 cmd_reply_show(horiz_line);
2165 cmd_reply_show(heading);
2168 cmd_reply_show(horiz_line);
2169 cmd_reply_show(_("In the column '##' the status of the option is shown:"));
2170 cmd_reply_show(_(" - a '!' means the option is locked by the ruleset."));
2171 cmd_reply_show(_(" - a '+' means you may change the option."));
2172 cmd_reply_show(_(" - a '~' means that option follows default value."));
2173 cmd_reply_show(_(" - a '=' means the value is same as default."));
2174 cmd_reply_show(horiz_line);
2175 cmd_reply(called_as, caller, C_COMMENT, _("%-*s ## value (min, max)"),
2176 OPTION_NAME_SPACE, _("Option"));
2177 cmd_reply_show(horiz_line);
2179 /* Update changed and locked levels. */
2180 settings_list_update();
2182 switch(level) {
2183 case SSET_NONE:
2184 /* Show _one_ setting. */
2185 fc_assert_ret_val(0 <= cmd, FALSE);
2187 struct setting *pset = setting_by_number(cmd);
2189 show_settings_one(caller, called_as, pset);
2191 break;
2192 case SSET_CHANGED:
2193 case SSET_ALL:
2194 case SSET_VITAL:
2195 case SSET_SITUATIONAL:
2196 case SSET_RARE:
2197 case SSET_LOCKED:
2198 settings_iterate(level, pset) {
2199 if (!setting_is_visible(pset, caller)) {
2200 continue;
2203 if (LOOKUP_OPTION_AMBIGUOUS == cmd
2204 && 0 != fc_strncasecmp(setting_name(pset), str, clen)) {
2205 continue;
2208 show_settings_one(caller, called_as, pset);
2209 } settings_iterate_end;
2210 break;
2211 case OLEVELS_NUM:
2212 /* nothing */
2213 break;
2216 cmd_reply_show(horiz_line);
2217 /* Only emit this additional help for bona fide 'show' command */
2218 if (called_as == CMD_SHOW) {
2219 cmd_reply_show(_("A help text for each option is available via 'help "
2220 "<option>'."));
2221 cmd_reply_show(horiz_line);
2222 if (level == SSET_VITAL) {
2223 cmd_reply_show(_("Try 'show situational' or 'show rare' to show "
2224 "more options.\n"
2225 "Try 'show changed' to show settings with "
2226 "non-default values.\n"
2227 "Try 'show locked' to show settings locked "
2228 "by the ruleset."));
2229 cmd_reply_show(horiz_line);
2232 return TRUE;
2233 #undef cmd_reply_show
2236 /*****************************************************************************
2237 Show one setting.
2239 Each option value will be displayed as:
2241 [OPTION_NAME_SPACE length for name] ## [value] ([min], [max])
2243 where '##' is a combination of ' ', '!' or '+' followed by ' ', '*', or '=' with
2244 - '!': the option is locked by the ruleset
2245 - '+': you may change the option
2246 - '~': the option follows default value
2247 - '=': the value is same as default
2248 *****************************************************************************/
2249 static void show_settings_one(struct connection *caller, enum command_id cmd,
2250 struct setting *pset)
2252 char buf[MAX_LEN_CONSOLE_LINE] = "", value[MAX_LEN_CONSOLE_LINE] = "";
2253 bool is_changed;
2254 static char prefix[OPTION_NAME_SPACE + 4 + 1] = "";
2255 char defaultness;
2257 fc_assert_ret(pset != NULL);
2259 is_changed = setting_non_default(pset);
2260 setting_value_name(pset, TRUE, value, sizeof(value));
2262 /* Wrap long option values, such as bitwise options */
2263 fc_break_lines(value, LINE_BREAK - (sizeof(prefix)-1));
2265 if (prefix[0] == '\0') {
2266 memset(prefix, ' ', sizeof(prefix)-1);
2269 if (is_changed) {
2270 /* Emphasizes the changed option. */
2271 /* Apply tags to each line fragment. */
2272 size_t startpos = 0;
2273 char *nl;
2274 do {
2275 nl = strchr(value + startpos, '\n');
2276 featured_text_apply_tag(value, buf, sizeof(buf), TTT_COLOR,
2277 startpos, nl ? nl - value : FT_OFFSET_UNSET,
2278 ftc_changed);
2279 sz_strlcpy(value, buf);
2280 if (nl) {
2281 char *p = strchr(nl, '\n');
2282 fc_assert_action(p != NULL, break);
2283 startpos = p + 1 - value;
2285 } while (nl);
2288 if (SST_INT == setting_type(pset)) {
2289 /* Add the range. */
2290 cat_snprintf(value, sizeof(value), " (%d, %d)",
2291 setting_int_min(pset), setting_int_max(pset));
2294 if (setting_get_setdef(pset) == SETDEF_INTERNAL) {
2295 defaultness = '~';
2296 } else if (is_changed) {
2297 defaultness = ' ';
2298 } else {
2299 defaultness = '=';
2302 cmd_reply_prefix(cmd, caller, C_COMMENT, prefix, "%-*s %c%c %s",
2303 OPTION_NAME_SPACE, setting_name(pset),
2304 setting_status(caller, pset), defaultness,
2305 value);
2308 /******************************************************************
2309 Handle team command
2310 ******************************************************************/
2311 static bool team_command(struct connection *caller, char *str, bool check)
2313 struct player *pplayer;
2314 enum m_pre_result match_result;
2315 char buf[MAX_LEN_CONSOLE_LINE];
2316 char *arg[2];
2317 int ntokens = 0, i;
2318 bool res = FALSE;
2319 struct team_slot *tslot;
2321 if (game_was_started()) {
2322 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2323 _("Cannot change teams once game has begun."));
2324 return FALSE;
2327 if (str != NULL || strlen(str) > 0) {
2328 sz_strlcpy(buf, str);
2329 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2331 if (ntokens != 2) {
2332 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2333 _("Undefined argument. Usage:\n%s"),
2334 command_synopsis(command_by_number(CMD_TEAM)));
2335 goto cleanup;
2338 pplayer = player_by_name_prefix(arg[0], &match_result);
2339 if (pplayer == NULL) {
2340 cmd_reply_no_such_player(CMD_TEAM, caller, arg[0], match_result);
2341 goto cleanup;
2344 tslot = team_slot_by_rule_name(arg[1]);
2345 if (NULL == tslot) {
2346 int teamno;
2348 if (str_to_int(arg[1], &teamno)) {
2349 tslot = team_slot_by_number(teamno);
2352 if (NULL == tslot) {
2353 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2354 _("No such team %s. Please give a "
2355 "valid team name or number."), arg[1]);
2356 goto cleanup;
2359 if (is_barbarian(pplayer)) {
2360 /* This can happen if we change team settings on a loaded game. */
2361 cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Cannot team a barbarian."));
2362 goto cleanup;
2364 if (!check) {
2365 team_add_player(pplayer, team_new(tslot));
2366 send_player_info_c(pplayer, NULL);
2367 cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
2368 player_name(pplayer),
2369 team_slot_name_translation(tslot));
2371 res = TRUE;
2373 cleanup:
2374 for (i = 0; i < ntokens; i++) {
2375 free(arg[i]);
2377 return res;
2380 /**************************************************************************
2381 List all running votes. Moved from /vote command.
2382 **************************************************************************/
2383 static void show_votes(struct connection *caller)
2385 int count = 0;
2386 const char *title;
2388 if (vote_list != NULL) {
2389 vote_list_iterate(vote_list, pvote) {
2390 if (NULL != caller && !conn_can_see_vote(caller, pvote)) {
2391 continue;
2393 /* TRANS: "Vote" or "Teamvote" is voting-as-a-process. Used as
2394 * part of a sentence. */
2395 title = vote_is_team_only(pvote) ? _("Teamvote") : _("Vote");
2396 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2397 /* TRANS: "[Vote|Teamvote] 3 \"proposed change\" (needs ..." */
2398 _("%s %d \"%s\" (needs %0.0f%%%s): %d for, "
2399 "%d against, and %d abstained out of %d players."),
2400 title, pvote->vote_no, pvote->cmdline,
2401 MIN(100, pvote->need_pc * 100 + 1),
2402 pvote->flags & VCF_NODISSENT ? _(" no dissent") : "",
2403 pvote->yes, pvote->no, pvote->abstain, count_voters(pvote));
2404 count++;
2405 } vote_list_iterate_end;
2408 if (count == 0) {
2409 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2410 _("There are no votes going on."));
2414 /**************************************************************************
2415 Vote command argument definitions.
2416 **************************************************************************/
2417 static const char *const vote_args[] = {
2418 "yes",
2419 "no",
2420 "abstain",
2421 NULL
2423 static const char *vote_arg_accessor(int i)
2425 return vote_args[i];
2428 /******************************************************************
2429 Make or participate in a vote.
2430 ******************************************************************/
2431 static bool vote_command(struct connection *caller, char *str,
2432 bool check)
2434 char buf[MAX_LEN_CONSOLE_LINE];
2435 char *arg[2];
2436 int ntokens = 0, i = 0, which = -1;
2437 enum m_pre_result match_result;
2438 struct vote *pvote = NULL;
2439 bool res = FALSE;
2441 if (check) {
2442 /* This should never happen, since /vote must always be
2443 * set to ALLOW_BASIC or less. But just in case... */
2444 return FALSE;
2447 sz_strlcpy(buf, str);
2448 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2450 if (ntokens == 0) {
2451 show_votes(caller);
2452 goto CLEANUP;
2453 } else if (!conn_can_vote(caller, NULL)) {
2454 cmd_reply(CMD_VOTE, caller, C_FAIL,
2455 _("You are not allowed to use this command."));
2456 goto CLEANUP;
2459 match_result = match_prefix(vote_arg_accessor, VOTE_NUM, 0,
2460 fc_strncasecmp, NULL, arg[0], &i);
2462 if (match_result == M_PRE_AMBIGUOUS) {
2463 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2464 _("The argument \"%s\" is ambiguous."), arg[0]);
2465 goto CLEANUP;
2466 } else if (match_result > M_PRE_AMBIGUOUS) {
2467 /* Failed */
2468 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2469 _("Undefined argument. Usage:\n%s"),
2470 command_synopsis(command_by_number(CMD_VOTE)));
2471 goto CLEANUP;
2474 if (ntokens == 1) {
2475 /* Applies to last vote */
2476 if (vote_number_sequence > 0 && get_vote_by_no(vote_number_sequence)) {
2477 which = vote_number_sequence;
2478 } else {
2479 int num_votes = vote_list_size(vote_list);
2480 if (num_votes == 0) {
2481 cmd_reply(CMD_VOTE, caller, C_FAIL, _("There are no votes running."));
2482 } else {
2483 /* TRANS: "vote" as a process */
2484 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote (%d %s)."),
2485 num_votes, PL_("other vote running", "other votes running",
2486 num_votes));
2488 goto CLEANUP;
2490 } else {
2491 if (!str_to_int(arg[1], &which)) {
2492 cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be an integer."));
2493 goto CLEANUP;
2497 if (!(pvote = get_vote_by_no(which))) {
2498 /* TRANS: "vote" as a process */
2499 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), which);
2500 goto CLEANUP;
2503 if (!conn_can_vote(caller, pvote)) {
2504 cmd_reply(CMD_VOTE, caller, C_FAIL,
2505 _("You are not allowed to vote on that."));
2506 goto CLEANUP;
2509 if (i == VOTE_YES) {
2510 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
2511 pvote->cmdline);
2512 connection_vote(caller, pvote, VOTE_YES);
2513 } else if (i == VOTE_NO) {
2514 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
2515 pvote->cmdline);
2516 connection_vote(caller, pvote, VOTE_NO);
2517 } else if (i == VOTE_ABSTAIN) {
2518 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2519 _("You abstained from voting on \"%s\""), pvote->cmdline);
2520 connection_vote(caller, pvote, VOTE_ABSTAIN);
2521 } else {
2522 /* Must never happen. */
2523 fc_assert_action(FALSE, goto CLEANUP);
2526 res = TRUE;
2528 CLEANUP:
2529 free_tokens(arg, ntokens);
2530 return res;
2533 /**************************************************************************
2534 Cancel a vote... /cancelvote <vote number>|all.
2535 **************************************************************************/
2536 static bool cancelvote_command(struct connection *caller,
2537 char *arg, bool check)
2539 struct vote *pvote = NULL;
2540 int vote_no;
2542 if (check) {
2543 /* This should never happen anyway, since /cancelvote
2544 * is set to ALLOW_BASIC in both pregame and while the
2545 * game is running. */
2546 return FALSE;
2549 remove_leading_trailing_spaces(arg);
2551 if (arg[0] == '\0') {
2552 if (caller == NULL) {
2553 /* Server prompt */
2554 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2555 /* TRANS: "vote" as a process */
2556 _("Missing argument <vote number> or "
2557 "the string \"all\"."));
2558 return FALSE;
2560 /* The caller cancel his/her own vote. */
2561 if (!(pvote = get_vote_by_caller(caller))) {
2562 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2563 _("You don't have any vote going on."));
2564 return FALSE;
2566 } else if (fc_strcasecmp(arg, "all") == 0) {
2567 /* Cancel all votes (needs some privileges). */
2568 if (vote_list_size(vote_list) == 0) {
2569 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2570 _("There isn't any vote going on."));
2571 return FALSE;
2572 } else if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
2573 clear_all_votes();
2574 notify_conn(NULL, NULL, E_VOTE_ABORTED, ftc_server,
2575 /* TRANS: "votes" as a process */
2576 _("All votes have been removed."));
2577 return TRUE;
2578 } else {
2579 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2580 _("You are not allowed to use this command."));
2581 return FALSE;
2583 } else if (str_to_int(arg, &vote_no)) {
2584 /* Cancel one particular vote (needs some privileges if the vote
2585 * is not owned). */
2586 if (!(pvote = get_vote_by_no(vote_no))) {
2587 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2588 /* TRANS: "vote" as a process */
2589 _("No such vote (%d)."), vote_no);
2590 return FALSE;
2591 } else if (caller && conn_get_access(caller) < ALLOW_ADMIN
2592 && caller->id != pvote->caller_id) {
2593 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2594 /* TRANS: "vote" as a process */
2595 _("You are not allowed to cancel this vote (%d)."),
2596 vote_no);
2597 return FALSE;
2599 } else {
2600 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2601 /* TRANS: "vote" as a process */
2602 _("Usage: /cancelvote [<vote number>|all]"));
2603 return FALSE;
2606 fc_assert_ret_val(NULL != pvote, FALSE);
2608 if (caller) {
2609 notify_team(conn_get_player(vote_get_caller(pvote)),
2610 NULL, E_VOTE_ABORTED, ftc_server,
2611 /* TRANS: "vote" as a process */
2612 _("%s has canceled the vote \"%s\" (number %d)."),
2613 caller->username, pvote->cmdline, pvote->vote_no);
2614 } else {
2615 /* Server prompt */
2616 notify_team(conn_get_player(vote_get_caller(pvote)),
2617 NULL, E_VOTE_ABORTED, ftc_server,
2618 /* TRANS: "vote" as a process */
2619 _("The vote \"%s\" (number %d) has been canceled."),
2620 pvote->cmdline, pvote->vote_no);
2622 /* Make it after, prevent crashs about a free pointer (pvote). */
2623 remove_vote(pvote);
2625 return TRUE;
2628 /******************************************************************
2629 Turn on selective debugging.
2630 ******************************************************************/
2631 static bool debug_command(struct connection *caller, char *str,
2632 bool check)
2634 char buf[MAX_LEN_CONSOLE_LINE];
2635 char *arg[3];
2636 int ntokens = 0, i;
2638 if (game.info.is_new_game) {
2639 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2640 _("Can only use this command once game has begun."));
2641 return FALSE;
2643 if (check) {
2644 return TRUE; /* whatever! */
2647 if (str != NULL && strlen(str) > 0) {
2648 sz_strlcpy(buf, str);
2649 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
2650 } else {
2651 ntokens = 0;
2654 if (ntokens > 0 && strcmp(arg[0], "diplomacy") == 0) {
2655 struct player *pplayer;
2656 enum m_pre_result match_result;
2658 if (ntokens != 2) {
2659 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2660 _("Undefined argument. Usage:\n%s"),
2661 command_synopsis(command_by_number(CMD_DEBUG)));
2662 goto cleanup;
2664 pplayer = player_by_name_prefix(arg[1], &match_result);
2665 if (pplayer == NULL) {
2666 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2667 goto cleanup;
2669 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY)) {
2670 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2671 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy no longer debugged"),
2672 player_name(pplayer));
2673 } else {
2674 BV_SET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2675 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy debugged"),
2676 player_name(pplayer));
2677 /* TODO: print some info about the player here */
2679 } else if (ntokens > 0 && strcmp(arg[0], "tech") == 0) {
2680 struct player *pplayer;
2681 enum m_pre_result match_result;
2683 if (ntokens != 2) {
2684 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2685 _("Undefined argument. Usage:\n%s"),
2686 command_synopsis(command_by_number(CMD_DEBUG)));
2687 goto cleanup;
2689 pplayer = player_by_name_prefix(arg[1], &match_result);
2690 if (pplayer == NULL) {
2691 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2692 goto cleanup;
2694 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_TECH)) {
2695 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_TECH);
2696 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech no longer debugged"),
2697 player_name(pplayer));
2698 } else {
2699 BV_SET(pplayer->server.debug, PLAYER_DEBUG_TECH);
2700 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech debugged"),
2701 player_name(pplayer));
2702 /* TODO: print some info about the player here */
2704 } else if (ntokens > 0 && strcmp(arg[0], "info") == 0) {
2705 int cities = 0, players = 0, units = 0, citizen_count = 0;
2707 players_iterate(plr) {
2708 players++;
2709 city_list_iterate(plr->cities, pcity) {
2710 cities++;
2711 citizen_count += city_size_get(pcity);
2712 } city_list_iterate_end;
2713 units += unit_list_size(plr->units);
2714 } players_iterate_end;
2715 log_normal(_("players=%d cities=%d citizens=%d units=%d"),
2716 players, cities, citizen_count, units);
2717 notify_conn(game.est_connections, NULL, E_AI_DEBUG, ftc_log,
2718 _("players=%d cities=%d citizens=%d units=%d"),
2719 players, cities, citizen_count, units);
2720 } else if (ntokens > 0 && strcmp(arg[0], "city") == 0) {
2721 int x, y;
2722 struct tile *ptile;
2723 struct city *pcity;
2725 if (ntokens != 3) {
2726 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2727 _("Undefined argument. Usage:\n%s"),
2728 command_synopsis(command_by_number(CMD_DEBUG)));
2729 goto cleanup;
2731 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2732 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2733 goto cleanup;
2735 if (!(ptile = map_pos_to_tile(&(wld.map), x, y))) {
2736 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2737 goto cleanup;
2739 pcity = tile_city(ptile);
2740 if (!pcity) {
2741 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("No city at this coordinate."));
2742 goto cleanup;
2744 if (pcity->server.debug) {
2745 pcity->server.debug = FALSE;
2746 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s no longer debugged"),
2747 city_name_get(pcity));
2748 } else {
2749 pcity->server.debug = TRUE;
2750 CITY_LOG(LOG_NORMAL, pcity, "debugged");
2752 } else if (ntokens > 0 && strcmp(arg[0], "units") == 0) {
2753 int x, y;
2754 struct tile *ptile;
2756 if (ntokens != 3) {
2757 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2758 _("Undefined argument. Usage:\n%s"),
2759 command_synopsis(command_by_number(CMD_DEBUG)));
2760 goto cleanup;
2762 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2763 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2764 goto cleanup;
2766 if (!(ptile = map_pos_to_tile(&(wld.map), x, y))) {
2767 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2768 goto cleanup;
2770 unit_list_iterate(ptile->units, punit) {
2771 if (punit->server.debug) {
2772 punit->server.debug = FALSE;
2773 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2774 nation_adjective_for_player(unit_owner(punit)),
2775 unit_name_translation(punit));
2776 } else {
2777 punit->server.debug = TRUE;
2778 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2779 nation_rule_name(nation_of_unit(punit)),
2780 unit_name_translation(punit));
2782 } unit_list_iterate_end;
2783 } else if (ntokens > 0 && strcmp(arg[0], "timing") == 0) {
2784 TIMING_RESULTS();
2785 } else if (ntokens > 0 && strcmp(arg[0], "ferries") == 0) {
2786 if (game.server.debug[DEBUG_FERRIES]) {
2787 game.server.debug[DEBUG_FERRIES] = FALSE;
2788 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system is no longer "
2789 "in debug mode."));
2790 } else {
2791 game.server.debug[DEBUG_FERRIES] = TRUE;
2792 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system in debug mode."));
2794 } else if (ntokens > 0 && strcmp(arg[0], "unit") == 0) {
2795 int id;
2796 struct unit *punit;
2798 if (ntokens != 2) {
2799 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2800 _("Undefined argument. Usage:\n%s"),
2801 command_synopsis(command_by_number(CMD_DEBUG)));
2802 goto cleanup;
2804 if (!str_to_int(arg[1], &id)) {
2805 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 must be integer."));
2806 goto cleanup;
2808 if (!(punit = game_unit_by_number(id))) {
2809 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Unit %d does not exist."), id);
2810 goto cleanup;
2812 if (punit->server.debug) {
2813 punit->server.debug = FALSE;
2814 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2815 nation_adjective_for_player(unit_owner(punit)),
2816 unit_name_translation(punit));
2817 } else {
2818 punit->server.debug = TRUE;
2819 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2820 nation_rule_name(nation_of_unit(punit)),
2821 unit_name_translation(punit));
2823 } else {
2824 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2825 _("Undefined argument. Usage:\n%s"),
2826 command_synopsis(command_by_number(CMD_DEBUG)));
2828 cleanup:
2829 for (i = 0; i < ntokens; i++) {
2830 free(arg[i]);
2832 return TRUE;
2835 /******************************************************************
2836 Helper to validate an argument referring to a server setting.
2837 Sends error message and returns NULL on failure.
2838 ******************************************************************/
2839 static struct setting *validate_setting_arg(enum command_id cmd,
2840 struct connection *caller,
2841 char *arg)
2843 int opt = lookup_option(arg);
2845 if (opt < 0) {
2846 switch (opt) {
2847 case LOOKUP_OPTION_NO_RESULT:
2848 case LOOKUP_OPTION_LEVEL_NAME:
2849 cmd_reply(cmd, caller, C_SYNTAX, _("Option '%s' not recognized."), arg);
2850 break;
2851 case LOOKUP_OPTION_AMBIGUOUS:
2852 cmd_reply(cmd, caller, C_SYNTAX, _("Ambiguous option name."));
2853 break;
2854 case LOOKUP_OPTION_RULESETDIR:
2855 cmd_reply(cmd, caller, C_SYNTAX,
2856 /* TRANS: 'rulesetdir' is the command. Do not translate. */
2857 _("Use the '%srulesetdir' command to change the ruleset "
2858 "directory."), caller ? "/" : "");
2859 break;
2860 default:
2861 fc_assert(opt >= LOOKUP_OPTION_RULESETDIR);
2862 break;
2864 return NULL;
2867 return setting_by_number(opt);
2870 /******************************************************************
2871 Handle set command
2872 ******************************************************************/
2873 static bool set_command(struct connection *caller, char *str, bool check)
2875 char *args[2];
2876 int val, nargs;
2877 struct setting *pset;
2878 bool do_update;
2879 char reject_msg[256] = "";
2880 bool ret = FALSE;
2882 /* '=' is also a valid delimiter for this function. */
2883 nargs = get_tokens(str, args, ARRAY_SIZE(args), TOKEN_DELIMITERS "=");
2885 if (nargs < 2) {
2886 cmd_reply(CMD_SET, caller, C_SYNTAX,
2887 _("Undefined argument. Usage:\n%s"),
2888 command_synopsis(command_by_number(CMD_SET)));
2889 goto cleanup;
2892 pset = validate_setting_arg(CMD_SET, caller, args[0]);
2894 if (!pset) {
2895 /* Reason already reported. */
2896 goto cleanup;
2899 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))
2900 && !check) {
2901 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2902 goto cleanup;
2905 do_update = FALSE;
2907 switch (setting_type(pset)) {
2908 case SST_BOOL:
2909 if (check) {
2910 if (!setting_is_changeable(pset, caller, reject_msg,
2911 sizeof(reject_msg))
2912 || (!setting_bool_validate(pset, args[1], caller,
2913 reject_msg, sizeof(reject_msg)))) {
2914 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2915 goto cleanup;
2917 } else if (setting_bool_set(pset, args[1], caller,
2918 reject_msg, sizeof(reject_msg))) {
2919 do_update = TRUE;
2920 } else {
2921 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2922 goto cleanup;
2924 break;
2926 case SST_INT:
2927 if (!str_to_int(args[1], &val)) {
2928 cmd_reply(CMD_SET, caller, C_SYNTAX,
2929 _("The parameter %s should only contain +- and 0-9."),
2930 setting_name(pset));
2931 goto cleanup;
2933 if (check) {
2934 if (!setting_is_changeable(pset, caller, reject_msg,
2935 sizeof(reject_msg))
2936 || !setting_int_validate(pset, val, caller, reject_msg,
2937 sizeof(reject_msg))) {
2938 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2939 goto cleanup;
2941 } else {
2942 if (setting_int_set(pset, val, caller, reject_msg,
2943 sizeof(reject_msg))) {
2944 do_update = TRUE;
2945 } else {
2946 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2947 goto cleanup;
2950 break;
2952 case SST_STRING:
2953 if (check) {
2954 if (!setting_is_changeable(pset, caller, reject_msg,
2955 sizeof(reject_msg))
2956 || !setting_str_validate(pset, args[1], caller, reject_msg,
2957 sizeof(reject_msg))) {
2958 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2959 goto cleanup;
2961 } else {
2962 if (setting_str_set(pset, args[1], caller, reject_msg,
2963 sizeof(reject_msg))) {
2964 do_update = TRUE;
2965 } else {
2966 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2967 goto cleanup;
2970 break;
2972 case SST_ENUM:
2973 if (check) {
2974 if (!setting_is_changeable(pset, caller, reject_msg,
2975 sizeof(reject_msg))
2976 || (!setting_enum_validate(pset, args[1], caller,
2977 reject_msg, sizeof(reject_msg)))) {
2978 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2979 goto cleanup;
2981 } else if (setting_enum_set(pset, args[1], caller,
2982 reject_msg, sizeof(reject_msg))) {
2983 do_update = TRUE;
2984 } else {
2985 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2986 goto cleanup;
2988 break;
2990 case SST_BITWISE:
2991 if (check) {
2992 if (!setting_is_changeable(pset, caller, reject_msg,
2993 sizeof(reject_msg))
2994 || (!setting_bitwise_validate(pset, args[1], caller,
2995 reject_msg, sizeof(reject_msg)))) {
2996 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2997 goto cleanup;
2999 } else if (setting_bitwise_set(pset, args[1], caller,
3000 reject_msg, sizeof(reject_msg))) {
3001 do_update = TRUE;
3002 } else {
3003 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
3004 goto cleanup;
3006 break;
3008 case SST_COUNT:
3009 fc_assert(setting_type(pset) != SST_COUNT);
3010 goto cleanup;
3011 break;
3014 ret = TRUE; /* Looks like a success. */
3016 if (!check && do_update) {
3017 /* Send only to connections able to see that. */
3018 char buf[256];
3019 struct packet_chat_msg packet;
3021 package_event(&packet, NULL, E_SETTING, ftc_server,
3022 _("Console: '%s' has been set to %s."), setting_name(pset),
3023 setting_value_name(pset, TRUE, buf, sizeof(buf)));
3024 conn_list_iterate(game.est_connections, pconn) {
3025 if (setting_is_visible(pset, pconn)) {
3026 send_packet_chat_msg(pconn, &packet);
3028 } conn_list_iterate_end;
3029 /* Notify the console. */
3030 con_write(C_OK, "%s", packet.message);
3032 setting_changed(pset);
3033 setting_action(pset);
3034 send_server_setting(NULL, pset);
3036 * send any modified game parameters to the clients -- if sent
3037 * before S_S_RUNNING, triggers a popdown_races_dialog() call
3038 * in client/packhand.c#handle_game_info()
3040 send_game_info(NULL);
3041 reset_all_start_commands(FALSE);
3042 send_server_info_to_metaserver(META_INFO);
3045 cleanup:
3046 free_tokens(args, nargs);
3047 return ret;
3050 /**************************************************************************
3051 Check game.allow_take for permission to take or observe a player.
3053 NB: If this function returns FALSE, then callers expect that 'msg' will
3054 be filled in with a NULL-terminated string containing the reason.
3055 **************************************************************************/
3056 static bool is_allowed_to_take(struct player *pplayer, bool will_obs,
3057 char *msg, size_t msg_len)
3059 const char *allow;
3061 if (!pplayer && will_obs) {
3062 /* Global observer. */
3063 if (!(allow = strchr(game.server.allow_take,
3064 (game.info.is_new_game ? 'O' : 'o')))) {
3065 fc_strlcpy(msg, _("Sorry, one can't observe globally in this game."),
3066 msg_len);
3067 return FALSE;
3069 } else if (!pplayer && !will_obs) {
3070 /* Auto-taking a new player */
3072 if (game_was_started()) {
3073 fc_strlcpy(msg, _("You cannot take a new player at this time."),
3074 msg_len);
3075 return FALSE;
3078 if (normal_player_count() >= game.server.max_players) {
3079 fc_snprintf(msg, msg_len,
3080 /* TRANS: Do not translate "maxplayers". */
3081 PL_("You cannot take a new player because "
3082 "the maximum of %d player has already "
3083 "been reached (maxplayers setting).",
3084 "You cannot take a new player because "
3085 "the maximum of %d players has already "
3086 "been reached (maxplayers setting).",
3087 game.server.max_players),
3088 game.server.max_players);
3089 return FALSE;
3092 if (player_count() >= player_slot_count()) {
3093 fc_strlcpy(msg, _("You cannot take a new player because there "
3094 "are no free player slots."),
3095 msg_len);
3096 return FALSE;
3099 return TRUE;
3101 } else if (is_barbarian(pplayer)) {
3102 if (!(allow = strchr(game.server.allow_take, 'b'))) {
3103 if (will_obs) {
3104 fc_strlcpy(msg,
3105 _("Sorry, one can't observe barbarians in this game."),
3106 msg_len);
3107 } else {
3108 fc_strlcpy(msg, _("Sorry, one can't take barbarians in this game."),
3109 msg_len);
3111 return FALSE;
3113 } else if (!pplayer->is_alive) {
3114 if (!(allow = strchr(game.server.allow_take, 'd'))) {
3115 if (will_obs) {
3116 fc_strlcpy(msg,
3117 _("Sorry, one can't observe dead players in this game."),
3118 msg_len);
3119 } else {
3120 fc_strlcpy(msg,
3121 _("Sorry, one can't take dead players in this game."),
3122 msg_len);
3124 return FALSE;
3126 } else if (is_ai(pplayer)) {
3127 if (!(allow = strchr(game.server.allow_take,
3128 (game.info.is_new_game ? 'A' : 'a')))) {
3129 if (will_obs) {
3130 fc_strlcpy(msg,
3131 _("Sorry, one can't observe AI players in this game."),
3132 msg_len);
3133 } else {
3134 fc_strlcpy(msg, _("Sorry, one can't take AI players in this game."),
3135 msg_len);
3137 return FALSE;
3139 } else {
3140 if (!(allow = strchr(game.server.allow_take,
3141 (game.info.is_new_game ? 'H' : 'h')))) {
3142 if (will_obs) {
3143 fc_strlcpy(msg,
3144 _("Sorry, one can't observe human players in this game."),
3145 msg_len);
3146 } else {
3147 fc_strlcpy(msg,
3148 _("Sorry, one can't take human players in this game."),
3149 msg_len);
3151 return FALSE;
3155 allow++;
3157 if (will_obs && (*allow == '2' || *allow == '3')) {
3158 fc_strlcpy(msg, _("Sorry, one can't observe in this game."), msg_len);
3159 return FALSE;
3162 if (!will_obs && *allow == '4') {
3163 fc_strlcpy(msg, _("Sorry, one can't take players in this game."),
3164 MAX_LEN_MSG);
3165 return FALSE;
3168 if (!will_obs && pplayer->is_connected
3169 && (*allow == '1' || *allow == '3')) {
3170 fc_strlcpy(msg, _("Sorry, one can't take players already "
3171 "connected in this game."), msg_len);
3172 return FALSE;
3175 return TRUE;
3178 /**************************************************************************
3179 Observe another player. If we were already attached, detach
3180 (see connection_detach()). The console and those with ALLOW_HACK can
3181 use the two-argument command and force others to observe.
3182 **************************************************************************/
3183 static bool observe_command(struct connection *caller, char *str, bool check)
3185 int i = 0, ntokens = 0;
3186 char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
3187 bool is_newgame = !game_was_started();
3188 enum m_pre_result result;
3189 struct connection *pconn = NULL;
3190 struct player *pplayer = NULL;
3191 bool res = FALSE;
3193 /******** PART I: fill pconn and pplayer ********/
3195 sz_strlcpy(buf, str);
3196 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
3198 /* check syntax, only certain syntax if allowed depending on the caller */
3199 if (!caller && ntokens < 1) {
3200 cmd_reply(CMD_OBSERVE, caller, C_SYNTAX, _("Usage:\n%s"),
3201 command_synopsis(command_by_number(CMD_OBSERVE)));
3202 goto end;
3205 if (ntokens == 2 && (caller && caller->access_level != ALLOW_HACK)) {
3206 cmd_reply(CMD_OBSERVE, caller, C_SYNTAX,
3207 _("Only the player name form is allowed."));
3208 goto end;
3211 /* match connection if we're console, match a player if we're not */
3212 if (ntokens == 1) {
3213 if (!caller && !(pconn = conn_by_user_prefix(arg[0], &result))) {
3214 cmd_reply_no_such_conn(CMD_OBSERVE, caller, arg[0], result);
3215 goto end;
3216 } else if (caller
3217 && !(pplayer = player_by_name_prefix(arg[0], &result))) {
3218 cmd_reply_no_such_player(CMD_OBSERVE, caller, arg[0], result);
3219 goto end;
3223 /* get connection name then player name */
3224 if (ntokens == 2) {
3225 if (!(pconn = conn_by_user_prefix(arg[0], &result))) {
3226 cmd_reply_no_such_conn(CMD_OBSERVE, caller, arg[0], result);
3227 goto end;
3229 if (!(pplayer = player_by_name_prefix(arg[1], &result))) {
3230 cmd_reply_no_such_player(CMD_OBSERVE, caller, arg[1], result);
3231 goto end;
3235 /* if we can't force other connections to observe, assign us to be pconn. */
3236 if (!pconn) {
3237 pconn = caller;
3240 /* if we have no pplayer, it means that we want to be a global observer */
3242 /******** PART II: do the observing ********/
3244 /* check allowtake for permission */
3245 if (!is_allowed_to_take(pplayer, TRUE, msg, sizeof(msg))) {
3246 cmd_reply(CMD_OBSERVE, caller, C_FAIL, "%s", msg);
3247 goto end;
3250 /* observing your own player (during pregame) makes no sense. */
3251 if (NULL != pplayer
3252 && pplayer == pconn->playing
3253 && !pconn->observer
3254 && is_newgame
3255 && !pplayer->was_created) {
3256 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3257 _("%s already controls %s. Using 'observe' would remove %s"),
3258 pconn->username,
3259 player_name(pplayer),
3260 player_name(pplayer));
3261 goto end;
3264 /* attempting to observe a player you're already observing should fail. */
3265 if (pplayer == pconn->playing && pconn->observer) {
3266 if (pplayer) {
3267 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3268 _("%s is already observing %s."),
3269 pconn->username,
3270 player_name(pplayer));
3271 } else {
3272 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3273 _("%s is already observing."),
3274 pconn->username);
3276 goto end;
3279 res = TRUE; /* all tests passed */
3280 if (check) {
3281 goto end;
3284 /* if the connection is already attached to a player,
3285 * unattach and cleanup old player (rename, remove, etc) */
3286 if (TRUE) {
3287 char name[MAX_LEN_NAME];
3289 if (pplayer) {
3290 /* if pconn->playing is removed, we'll lose pplayer */
3291 sz_strlcpy(name, player_name(pplayer));
3294 connection_detach(pconn, TRUE);
3296 if (pplayer) {
3297 /* find pplayer again, the pointer might have been changed */
3298 pplayer = player_by_name(name);
3302 /* attach pconn to new player as an observer or as global observer */
3303 if ((res = connection_attach(pconn, pplayer, TRUE))) {
3304 if (pplayer) {
3305 cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes %s"),
3306 pconn->username,
3307 player_name(pplayer));
3308 } else {
3309 cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes"),
3310 pconn->username);
3314 end:;
3315 /* free our args */
3316 for (i = 0; i < ntokens; i++) {
3317 free(arg[i]);
3319 return res;
3322 /**************************************************************************
3323 Take over a player. If a connection already has control of that player,
3324 disallow it.
3326 If there are two arguments, treat the first as the connection name and the
3327 second as the player name (only hack and the console can do this).
3328 Otherwise, there should be one argument, that being the player that the
3329 caller wants to take.
3330 **************************************************************************/
3331 static bool take_command(struct connection *caller, char *str, bool check)
3333 int i = 0, ntokens = 0;
3334 char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
3335 bool is_newgame = !game_was_started();
3336 enum m_pre_result match_result;
3337 struct connection *pconn = caller;
3338 struct player *pplayer = NULL;
3339 bool res = FALSE;
3341 /******** PART I: fill pconn and pplayer ********/
3343 sz_strlcpy(buf, str);
3344 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
3346 /* check syntax */
3347 if (!caller && ntokens != 2) {
3348 cmd_reply(CMD_TAKE, caller, C_SYNTAX, _("Usage:\n%s"),
3349 command_synopsis(command_by_number(CMD_TAKE)));
3350 goto end;
3353 if (caller && caller->access_level != ALLOW_HACK && ntokens != 1) {
3354 cmd_reply(CMD_TAKE, caller, C_SYNTAX,
3355 _("Only the player name form is allowed."));
3356 goto end;
3359 if (ntokens == 0) {
3360 cmd_reply(CMD_TAKE, caller, C_SYNTAX, _("Usage:\n%s"),
3361 command_synopsis(command_by_number(CMD_TAKE)));
3362 goto end;
3365 if (ntokens == 2) {
3366 if (!(pconn = conn_by_user_prefix(arg[i], &match_result))) {
3367 cmd_reply_no_such_conn(CMD_TAKE, caller, arg[i], match_result);
3368 goto end;
3370 i++; /* found a conn, now reference the second argument */
3373 if (strcmp(arg[i], "-") == 0) {
3374 if (!is_newgame) {
3375 cmd_reply(CMD_TAKE, caller, C_FAIL,
3376 _("You cannot issue \"/take -\" when "
3377 "the game has already started."));
3378 goto end;
3381 /* Find first uncontrolled player. This will return NULL if there is
3382 * no free players at the moment. Later call to
3383 * connection_attach() will create new player for such NULL
3384 * cases. */
3385 pplayer = find_uncontrolled_player();
3386 if (pplayer) {
3387 /* Make it human! */
3388 set_as_human(pplayer);
3390 } else if (!(pplayer = player_by_name_prefix(arg[i], &match_result))) {
3391 cmd_reply_no_such_player(CMD_TAKE, caller, arg[i], match_result);
3392 goto end;
3395 /******** PART II: do the attaching ********/
3397 /* Take not possible if the player is involved in a delegation (either
3398 * it's being controlled, or it's been put aside by the delegate). */
3399 if (player_delegation_active(pplayer)) {
3400 cmd_reply(CMD_TAKE, caller, C_FAIL, _("A delegation is active for player "
3401 "'%s'. /take not possible."),
3402 player_name(pplayer));
3403 goto end;
3406 /* check allowtake for permission */
3407 if (!is_allowed_to_take(pplayer, FALSE, msg, sizeof(msg))) {
3408 cmd_reply(CMD_TAKE, caller, C_FAIL, "%s", msg);
3409 goto end;
3412 /* taking your own player makes no sense. */
3413 if ((NULL != pplayer && !pconn->observer && pplayer == pconn->playing)
3414 || (NULL == pplayer && !pconn->observer && NULL != pconn->playing)) {
3415 cmd_reply(CMD_TAKE, caller, C_FAIL, _("%s already controls %s."),
3416 pconn->username,
3417 player_name(pconn->playing));
3418 goto end;
3421 /* Make sure there is free player slot if there is need to
3422 * create new player. This is necessary for previously
3423 * detached connections only. Others can reuse the slot
3424 * they first release. */
3425 if (!pplayer && !pconn->playing
3426 && (normal_player_count() >= game.server.max_players
3427 || normal_player_count() >= server.playable_nations)) {
3428 cmd_reply(CMD_TAKE, caller, C_FAIL,
3429 _("There is no free player slot for %s."),
3430 pconn->username);
3431 goto end;
3433 fc_assert_action(player_count() <= player_slot_count(), goto end);
3435 res = TRUE;
3436 if (check) {
3437 goto end;
3440 /* If the player is controlled by another user, forcibly detach
3441 * the user. */
3442 if (pplayer && pplayer->is_connected) {
3443 if (NULL == caller) {
3444 notify_conn(NULL, NULL, E_CONNECTION, ftc_server,
3445 _("Reassigned nation to %s by server console."),
3446 pconn->username);
3447 } else {
3448 notify_conn(NULL, NULL, E_CONNECTION, ftc_server,
3449 _("Reassigned nation to %s by %s."),
3450 pconn->username,
3451 caller->username);
3454 /* We are reassigning this nation, so we need to detach the current
3455 * user to set a new one. */
3456 conn_list_iterate(pplayer->connections, aconn) {
3457 if (!aconn->observer) {
3458 connection_detach(aconn, FALSE);
3460 } conn_list_iterate_end;
3463 /* if the connection is already attached to another player,
3464 * unattach and cleanup old player (rename, remove, etc)
3465 * We may have been observing the player we now want to take */
3466 if (NULL != pconn->playing || pconn->observer) {
3467 char name[MAX_LEN_NAME];
3469 if (pplayer) {
3470 /* if pconn->playing is removed, we'll lose pplayer */
3471 sz_strlcpy(name, player_name(pplayer));
3474 connection_detach(pconn, TRUE);
3476 if (pplayer) {
3477 /* find pplayer again; the pointer might have been changed */
3478 pplayer = player_by_name(name);
3482 /* Now attach to new player */
3483 if ((res = connection_attach(pconn, pplayer, FALSE))) {
3484 /* Successfully attached */
3485 pplayer = pconn->playing; /* In case pplayer was NULL. */
3487 /* inform about the status before changes */
3488 cmd_reply(CMD_TAKE, caller, C_OK, _("%s now controls %s (%s, %s)."),
3489 pconn->username,
3490 player_name(pplayer),
3491 is_barbarian(pplayer)
3492 ? _("Barbarian")
3493 : is_ai(pplayer)
3494 ? _("AI")
3495 : _("Human"),
3496 pplayer->is_alive
3497 ? _("Alive")
3498 : _("Dead"));
3499 } else {
3500 cmd_reply(CMD_TAKE, caller, C_FAIL,
3501 _("%s failed to attach to any player."),
3502 pconn->username);
3505 end:;
3506 /* free our args */
3507 for (i = 0; i < ntokens; i++) {
3508 free(arg[i]);
3510 return res;
3513 /**************************************************************************
3514 Detach from a player. if that player wasn't /created and you were
3515 controlling the player, remove it (and then detach any observers as well).
3517 If called for a global observer connection (where pconn->playing is NULL)
3518 then it will correctly detach from observing mode.
3519 **************************************************************************/
3520 static bool detach_command(struct connection *caller, char *str, bool check)
3522 int i = 0, ntokens = 0;
3523 char buf[MAX_LEN_CONSOLE_LINE], *arg[1];
3524 enum m_pre_result match_result;
3525 struct connection *pconn = NULL;
3526 struct player *pplayer = NULL;
3527 bool res = FALSE;
3529 sz_strlcpy(buf, str);
3530 ntokens = get_tokens(buf, arg, 1, TOKEN_DELIMITERS);
3532 if (!caller && ntokens == 0) {
3533 cmd_reply(CMD_DETACH, caller, C_SYNTAX, _("Usage:\n%s"),
3534 command_synopsis(command_by_number(CMD_DETACH)));
3535 goto end;
3538 /* match the connection if the argument was given */
3539 if (ntokens == 1
3540 && !(pconn = conn_by_user_prefix(arg[0], &match_result))) {
3541 cmd_reply_no_such_conn(CMD_DETACH, caller, arg[0], match_result);
3542 goto end;
3545 /* if no argument is given, the caller wants to detach himself */
3546 if (!pconn) {
3547 pconn = caller;
3550 /* if pconn and caller are not the same, only continue
3551 * if we're console, or we have ALLOW_HACK */
3552 if (pconn != caller && caller && caller->access_level != ALLOW_HACK) {
3553 cmd_reply(CMD_DETACH, caller, C_FAIL,
3554 _("You can not detach other users."));
3555 goto end;
3558 pplayer = pconn->playing;
3560 /* must have someone to detach from... */
3561 if (!pplayer && !pconn->observer) {
3562 cmd_reply(CMD_DETACH, caller, C_FAIL,
3563 _("%s is not attached to any player."), pconn->username);
3564 goto end;
3567 res = TRUE;
3568 if (check) {
3569 goto end;
3572 if (pplayer) {
3573 cmd_reply(CMD_DETACH, caller, C_OK, _("%s detaching from %s"),
3574 pconn->username, player_name(pplayer));
3575 } else {
3576 cmd_reply(CMD_DETACH, caller, C_OK, _("%s no longer observing."),
3577 pconn->username);
3580 /* Actually do the detaching. */
3581 connection_detach(pconn, TRUE);
3583 /* The user explicitly wanted to detach, so if a player is marked for him,
3584 * reset its username. */
3585 players_iterate(aplayer) {
3586 if (0 == strncmp(aplayer->username, pconn->username, MAX_LEN_NAME)) {
3587 sz_strlcpy(aplayer->username, _(ANON_USER_NAME));
3588 aplayer->unassigned_user = TRUE;
3589 send_player_info_c(aplayer, NULL);
3591 } players_iterate_end;
3593 check_for_full_turn_done();
3595 end:
3596 fc_assert_ret_val(ntokens <= 1, FALSE);
3598 /* free our args */
3599 for (i = 0; i < ntokens; i++) {
3600 free(arg[i]);
3602 return res;
3605 /**************************************************************************
3606 Loads a file, complete with access checks and error messages sent back
3607 to the caller on failure.
3609 * caller is the connection requesting the load, or NULL for a
3610 command-line load. Error messages are sent back to the caller and
3611 an access check is done to make sure they are allowed to load.
3613 * filename is simply the name of the file to be loaded. This may be
3614 approximate; the function will look for appropriate suffixes and will
3615 check in the various directories to see if the file is found.
3617 * if check is set then only a test run is done and no actual loading
3618 is attempted.
3620 * The return value is true if the load succeeds, or would succeed;
3621 false if there's an error in the file or file name. Some errors
3622 in loading however could be unrecoverable (if the save game is
3623 legitimate but has inconsistencies) and would lead to a broken server
3624 afterwards.
3625 **************************************************************************/
3626 bool load_command(struct connection *caller, const char *filename, bool check,
3627 bool cmdline_load)
3629 struct timer *loadtimer, *uloadtimer;
3630 struct section_file *file;
3631 char arg[MAX_LEN_PATH];
3632 struct conn_list *global_observers;
3634 if (!filename || filename[0] == '\0') {
3635 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage:\n%s"),
3636 command_synopsis(command_by_number(CMD_LOAD)));
3637 return FALSE;
3639 if (S_S_INITIAL != server_state()) {
3640 cmd_reply(CMD_LOAD, caller, C_FAIL,
3641 _("Cannot load a game while another is running."));
3642 dlsend_packet_game_load(game.est_connections, TRUE, filename);
3643 return FALSE;
3645 if (!is_safe_filename(filename) && is_restricted(caller)) {
3646 cmd_reply(CMD_LOAD, caller, C_FAIL,
3647 _("Name \"%s\" disallowed for security reasons."),
3648 filename);
3649 return FALSE;
3653 /* it is a normal savegame or maybe a scenario */
3654 char testfile[MAX_LEN_PATH];
3655 const struct strvec *pathes[] = {
3656 get_save_dirs(), get_scenario_dirs(), NULL
3658 const char *exts[] = {
3659 "sav", "gz", "bz2", "xz", "sav.gz", "sav.bz2", "sav.xz", NULL
3661 const char **ext, *found = NULL;
3662 const struct strvec **path;
3664 if (cmdline_load) {
3665 /* Allow plain names being loaded with '--file' option, but not otherwise
3666 * (no loading of arbitrary files by unauthorized users)
3667 * Iterate through ALL paths to check for file with plain name before
3668 * looking any path with an extension, i.e., prefer plain name file
3669 * in later directory over file with extension in name in earlier
3670 * directory. */
3671 for (path = pathes; !found && *path; path++) {
3672 found = fileinfoname(*path, filename);
3673 if (found != NULL) {
3674 sz_strlcpy(arg, found);
3679 for (path = pathes; !found && *path; path++) {
3680 for (ext = exts; !found && *ext; ext++) {
3681 fc_snprintf(testfile, sizeof(testfile), "%s.%s", filename, *ext);
3682 found = fileinfoname(*path, testfile);
3683 if (found != NULL) {
3684 sz_strlcpy(arg, found);
3689 if (is_restricted(caller) && !found) {
3690 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Cannot find savegame or "
3691 "scenario with the name \"%s\"."), filename);
3692 return FALSE;
3695 if (!found) {
3696 sz_strlcpy(arg, filename);
3700 /* attempt to parse the file */
3702 if (!(file = secfile_load(arg, FALSE))) {
3703 log_error("Error loading savefile '%s': %s", arg, secfile_error());
3704 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"),
3705 arg);
3706 dlsend_packet_game_load(game.est_connections, TRUE, arg);
3707 return FALSE;
3710 if (check) {
3711 return TRUE;
3714 /* Detach current players, before we blow them away. */
3715 global_observers = conn_list_new();
3716 conn_list_iterate(game.est_connections, pconn) {
3717 if (pconn->playing != NULL) {
3718 connection_detach(pconn, TRUE);
3719 } else if (pconn->observer) {
3720 conn_list_append(global_observers, pconn);
3721 connection_detach(pconn, TRUE);
3723 } conn_list_iterate_end;
3725 player_info_freeze();
3727 /* Now free all game data. */
3728 server_game_free();
3730 /* Keep old ruleset value. Scenario file will either use the old value,
3731 * or to initialize new value itself. */
3732 server_game_init(TRUE);
3734 loadtimer = timer_new(TIMER_CPU, TIMER_ACTIVE);
3735 timer_start(loadtimer);
3736 uloadtimer = timer_new(TIMER_USER, TIMER_ACTIVE);
3737 timer_start(uloadtimer);
3739 sz_strlcpy(srvarg.load_filename, arg);
3741 savegame_load(file);
3742 secfile_check_unused(file);
3743 secfile_destroy(file);
3745 log_verbose("Load time: %g seconds (%g apparent)",
3746 timer_read_seconds(loadtimer), timer_read_seconds(uloadtimer));
3747 timer_destroy(loadtimer);
3748 timer_destroy(uloadtimer);
3750 sanity_check();
3752 log_verbose("load_command() does send_rulesets()");
3753 conn_list_compression_freeze(game.est_connections);
3754 send_rulesets(game.est_connections);
3755 send_server_settings(game.est_connections);
3756 send_scenario_info(game.est_connections);
3757 send_scenario_description(game.est_connections);
3758 send_game_info(game.est_connections);
3759 conn_list_compression_thaw(game.est_connections);
3761 /* Send information about the new players. */
3762 player_info_thaw();
3763 send_player_diplstate_c(NULL, NULL);
3765 /* Everything seemed to load ok; spread the good news. */
3766 dlsend_packet_game_load(game.est_connections, TRUE, srvarg.load_filename);
3768 /* Attach connections to players. Currently, this applies only
3769 * to connections that have the same username as a player. */
3770 conn_list_iterate(game.est_connections, pconn) {
3771 players_iterate(pplayer) {
3772 if (strcmp(pconn->username, pplayer->username) == 0) {
3773 connection_attach(pconn, pplayer, FALSE);
3774 break;
3776 } players_iterate_end;
3777 } conn_list_iterate_end;
3779 /* Reattach global observers. */
3780 conn_list_iterate(global_observers, pconn) {
3781 if (NULL == pconn->playing) {
3782 /* May have been assigned to a player before. */
3783 connection_attach(pconn, NULL, TRUE);
3785 } conn_list_iterate_end;
3786 conn_list_destroy(global_observers);
3788 (void) aifill(game.info.aifill);
3790 achievements_iterate(pach) {
3791 players_iterate(pplayer) {
3792 struct packet_achievement_info pack;
3794 pack.id = achievement_index(pach);
3795 pack.gained = achievement_player_has(pach, pplayer);
3796 pack.first = (pach->first == pplayer);
3798 lsend_packet_achievement_info(pplayer->connections, &pack);
3799 } players_iterate_end;
3800 } achievements_iterate_end;
3802 return TRUE;
3805 /**************************************************************************
3806 Load rulesets from a given ruleset directory.
3808 Security: There are some rudimentary checks in load_rulesets() to see
3809 if this directory really is a viable ruleset directory. For public
3810 servers, we check against directory redirection (is_safe_filename) and
3811 other bad stuff in the directory name, and will only use directories
3812 inside the data directories.
3813 **************************************************************************/
3814 static bool set_rulesetdir(struct connection *caller, char *str, bool check,
3815 int read_recursion)
3817 char filename[512];
3818 const char *pfilename;
3820 if (NULL == str || '\0' == str[0]) {
3821 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3822 _("You must provide a ruleset name. Use \"/show ruleset\" to "
3823 "see what is the current ruleset."));
3824 return FALSE;
3826 if (game_was_started() || !map_is_empty()) {
3827 cmd_reply(CMD_RULESETDIR, caller, C_FAIL,
3828 _("This setting can't be modified after the game has started."));
3829 return FALSE;
3832 if (strcmp(str, game.server.rulesetdir) == 0) {
3833 cmd_reply(CMD_RULESETDIR, caller, C_COMMENT,
3834 _("Ruleset directory is already \"%s\""), str);
3835 return FALSE;
3838 if (is_restricted(caller)
3839 && (!is_safe_filename(str) || strchr(str, '.'))) {
3840 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3841 _("Name \"%s\" disallowed for security reasons."),
3842 str);
3843 return FALSE;
3846 fc_snprintf(filename, sizeof(filename), "%s", str);
3847 pfilename = fileinfoname(get_data_dirs(), filename);
3848 if (!pfilename) {
3849 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3850 _("Ruleset directory \"%s\" not found"), str);
3851 return FALSE;
3854 if (!check) {
3855 bool success = TRUE;
3856 char old[512];
3858 sz_strlcpy(old, game.server.rulesetdir);
3859 log_verbose("set_rulesetdir() does load_rulesets() with \"%s\"", str);
3860 sz_strlcpy(game.server.rulesetdir, str);
3862 /* load the ruleset (and game settings defined in the ruleset) */
3863 player_info_freeze();
3864 if (!load_rulesets(old, FALSE, TRUE, FALSE)) {
3865 success = FALSE;
3867 /* While loading of the requested ruleset failed, we might
3868 * have changed ruleset from third one to default. Handle
3869 * rest of the ruleset changing accordingly. */
3872 if (game.est_connections) {
3873 /* Now that the rulesets are loaded we immediately send updates to any
3874 * connected clients. */
3875 send_rulesets(game.est_connections);
3877 /* show ruleset summary and list changed values */
3878 show_ruleset_info(caller, CMD_RULESETDIR, check, read_recursion);
3879 player_info_thaw();
3881 if (success) {
3882 cmd_reply(CMD_RULESETDIR, caller, C_OK,
3883 _("Ruleset directory set to \"%s\""), str);
3884 } else {
3885 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3886 _("Failed loading rulesets from directory \"%s\", using \"%s\""),
3887 str, game.server.rulesetdir);
3890 return success;
3893 return TRUE;
3896 /****************************************************************************
3897 /ignore command handler.
3898 ****************************************************************************/
3899 static bool ignore_command(struct connection *caller, char *str, bool check)
3901 char buf[128];
3902 struct conn_pattern *ppattern;
3904 if (NULL == caller) {
3905 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3906 _("That would be rather silly, since you are not a player."));
3907 return FALSE;
3910 ppattern = conn_pattern_from_string(str, CPT_USER, buf, sizeof(buf));
3911 if (NULL == ppattern) {
3912 cmd_reply(CMD_IGNORE, caller, C_SYNTAX,
3913 _("%s. Try /help ignore"), buf);
3914 return FALSE;
3917 if (check) {
3918 conn_pattern_destroy(ppattern);
3919 return TRUE;
3922 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3923 conn_pattern_list_append(caller->server.ignore_list, ppattern);
3924 cmd_reply(CMD_IGNORE, caller, C_COMMENT,
3925 _("Added pattern %s as entry %d to your ignore list."),
3926 buf, conn_pattern_list_size(caller->server.ignore_list));
3928 return TRUE;
3931 /****************************************************************************
3932 /unignore command handler.
3933 ****************************************************************************/
3934 static bool unignore_command(struct connection *caller,
3935 char *str, bool check)
3937 char buf[128], *c;
3938 int first, last, n;
3940 if (!caller) {
3941 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3942 _("That would be rather silly, since you are not a player."));
3943 return FALSE;
3946 sz_strlcpy(buf, str);
3947 remove_leading_trailing_spaces(buf);
3949 n = conn_pattern_list_size(caller->server.ignore_list);
3950 if (n == 0) {
3951 cmd_reply(CMD_UNIGNORE, caller, C_FAIL, _("Your ignore list is empty."));
3952 return FALSE;
3955 /* Parse the range. */
3956 if ('\0' == buf[0]) {
3957 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3958 _("Missing range. Try /help unignore."));
3959 return FALSE;
3960 } else if ((c = strchr(buf, '-'))) {
3961 *c++ = '\0';
3962 if ('\0' == buf[0]) {
3963 first = 1;
3964 } else if (!str_to_int(buf, &first)) {
3965 *--c = '-';
3966 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3967 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3968 return FALSE;
3970 if ('\0' == *c) {
3971 last = n;
3972 } else if (!str_to_int(c, &last)) {
3973 *--c = '-';
3974 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3975 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3976 return FALSE;
3978 } else {
3979 if (!str_to_int(buf, &first)) {
3980 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3981 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3982 return FALSE;
3984 last = first;
3987 if (!(1 <= first && first <= last && last <= n)) {
3988 if (first == last) {
3989 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3990 _("Invalid entry number: %d."), first);
3991 } else {
3992 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3993 _("Invalid range: %d to %d."), first, last);
3995 return FALSE;
3998 if (check) {
3999 return TRUE;
4002 n = 1;
4003 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
4004 if (first <= n) {
4005 conn_pattern_to_string(ppattern, buf, sizeof(buf));
4006 cmd_reply(CMD_UNIGNORE, caller, C_COMMENT,
4007 _("Removed pattern %s (entry %d) from your ignore list."),
4008 buf, n);
4009 conn_pattern_list_remove(caller->server.ignore_list, ppattern);
4011 n++;
4012 if (n > last) {
4013 break;
4015 } conn_pattern_list_iterate_end;
4017 return TRUE;
4020 /****************************************************************************
4021 /playercolor command handler.
4022 ****************************************************************************/
4023 static bool playercolor_command(struct connection *caller,
4024 char *str, bool check)
4026 enum m_pre_result match_result;
4027 struct player *pplayer;
4028 struct rgbcolor *prgbcolor = NULL;
4029 int ntokens = 0;
4030 char *token[2];
4031 bool ret = TRUE;
4033 ntokens = get_tokens(str, token, 2, TOKEN_DELIMITERS);
4035 if (ntokens != 2) {
4036 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4037 _("Two arguments needed. See '/help playercolor'."));
4038 ret = FALSE;
4039 goto cleanup;
4042 pplayer = player_by_name_prefix(token[0], &match_result);
4044 if (!pplayer) {
4045 cmd_reply_no_such_player(CMD_PLAYERCOLOR, caller, token[0], match_result);
4046 ret = FALSE;
4047 goto cleanup;
4051 const char *reason;
4052 if (!player_color_changeable(pplayer, &reason)) {
4053 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL, "%s", reason);
4054 ret = FALSE;
4055 goto cleanup;
4059 if (0 == fc_strcasecmp(token[1], "reset")) {
4060 if (!game_was_started()) {
4061 prgbcolor = NULL;
4062 } else {
4063 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL,
4064 _("Can only unset player color before game starts."));
4065 ret = FALSE;
4066 goto cleanup;
4068 } else if (!rgbcolor_from_hex(&prgbcolor, token[1])) {
4069 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4070 _("Invalid player color definition. See '/help playercolor'."));
4071 ret = FALSE;
4072 goto cleanup;
4075 if (prgbcolor != NULL) {
4076 players_iterate(pother) {
4077 if (pother != pplayer && pother->rgb != NULL
4078 && rgbcolors_are_equal(pother->rgb, prgbcolor)) {
4079 cmd_reply(CMD_PLAYERCOLOR, caller, C_WARNING,
4080 /* TRANS: "... [c0ffee] for Caesar ... to Hammurabi." */
4081 _("Warning: new color [%s] for %s is identical to %s."),
4082 player_color_ftstr(pother), player_name(pplayer),
4083 player_name(pother));
4085 } players_iterate_end;
4088 if (check) {
4089 goto cleanup;
4092 server_player_set_color(pplayer, prgbcolor);
4093 cmd_reply(CMD_PLAYERCOLOR, caller, C_OK,
4094 _("Color of player %s set to [%s]."), player_name(pplayer),
4095 player_color_ftstr(pplayer));
4097 cleanup:
4099 rgbcolor_destroy(prgbcolor);
4100 free_tokens(token, ntokens);
4102 return ret;
4105 /**************************************************************************
4106 Handle quit command
4107 **************************************************************************/
4108 static bool quit_game(struct connection *caller, bool check)
4110 if (!check) {
4111 cmd_reply(CMD_QUIT, caller, C_OK, _("Goodbye."));
4112 server_quit();
4114 return TRUE;
4117 /**************************************************************************
4118 Main entry point for "command input".
4119 **************************************************************************/
4120 bool handle_stdin_input(struct connection *caller, char *str)
4122 return handle_stdin_input_real(caller, str, FALSE, 0);
4125 /**************************************************************************
4126 Handle "command input", which could really come from stdin on console,
4127 or from client chat command, or read from file with -r, etc.
4128 caller == NULL means console, str is the input, which may optionally
4129 start with SERVER_COMMAND_PREFIX character.
4131 If check is TRUE, then do nothing, just check syntax.
4132 **************************************************************************/
4133 static bool handle_stdin_input_real(struct connection *caller, char *str,
4134 bool check, int read_recursion)
4136 char full_command[MAX_LEN_CONSOLE_LINE];
4137 char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE];
4138 char *cptr_s, *cptr_d;
4139 enum command_id cmd;
4140 enum cmdlevel level;
4142 /* Remove leading and trailing spaces, and server command prefix. */
4143 cptr_s = str = skip_leading_spaces(str);
4144 if ('\0' == *cptr_s || '#' == *cptr_s) {
4145 /* This appear to be a comment or blank line. */
4146 return FALSE;
4149 if (SERVER_COMMAND_PREFIX == *cptr_s) {
4150 /* Commands may be prefixed with SERVER_COMMAND_PREFIX, even when
4151 * given on the server command line. */
4152 cptr_s++;
4153 remove_leading_spaces(cptr_s);
4154 if ('\0' == *cptr_s) {
4155 /* This appear to be a blank line. */
4156 return FALSE;
4159 remove_trailing_spaces(cptr_s);
4161 /* notify to the server console */
4162 if (!check && caller) {
4163 con_write(C_COMMENT, "%s: '%s'", caller->username, str);
4166 /* if the caller may not use any commands at all, don't waste any time */
4167 if (may_use_nothing(caller)) {
4168 cmd_reply(CMD_HELP, caller, C_FAIL,
4169 _("Sorry, you are not allowed to use server commands."));
4170 return FALSE;
4173 /* copy the full command, in case we need it for voting purposes. */
4174 sz_strlcpy(full_command, cptr_s);
4177 * cptr_s points now to the beginning of the real command. It has
4178 * skipped leading whitespace, the SERVER_COMMAND_PREFIX and any
4179 * other non-alphanumeric characters.
4181 for (cptr_d = command; *cptr_s != '\0' && fc_isalnum(*cptr_s)
4182 && cptr_d < command + sizeof(command) - 1; cptr_s++, cptr_d++) {
4183 *cptr_d = *cptr_s;
4185 *cptr_d = '\0';
4187 /* cptr_s now contains the arguments. */
4188 sz_strlcpy(arg, skip_leading_spaces(cptr_s));
4190 cmd = command_named(command, FALSE);
4191 if (cmd == CMD_AMBIGUOUS) {
4192 cmd = command_named(command, TRUE);
4193 cmd_reply(cmd, caller, C_SYNTAX,
4194 _("Warning: '%s' interpreted as '%s', but it is ambiguous."
4195 " Try '%shelp'."),
4196 command, command_name_by_number(cmd), caller?"/":"");
4197 } else if (cmd == CMD_UNRECOGNIZED) {
4198 cmd_reply(cmd, caller, C_SYNTAX, _("Unknown command '%s%s'. "
4199 " Try '%shelp'."),
4200 caller ? "/" : "", command, caller ? "/" : "");
4201 return FALSE;
4204 level = command_level(command_by_number(cmd));
4206 if (conn_can_vote(caller, NULL) && level == ALLOW_CTRL
4207 && conn_get_access(caller) == ALLOW_BASIC && !check
4208 && !vote_would_pass_immediately(caller, cmd)) {
4209 struct vote *vote;
4210 bool caller_had_vote = (NULL != get_vote_by_caller(caller));
4212 /* Check if the vote command would succeed. If we already have a vote
4213 * going, cancel it in favour of the new vote command. You can only
4214 * have one vote at a time. This is done by vote_new(). */
4215 if (handle_stdin_input_real(caller, full_command, TRUE,
4216 read_recursion + 1)
4217 && (vote = vote_new(caller, arg, cmd))) {
4218 char votedesc[MAX_LEN_CONSOLE_LINE];
4219 const struct player *teamplr;
4220 const char *what;
4221 struct ft_color color;
4223 if (caller_had_vote) {
4224 cmd_reply(CMD_VOTE, caller, C_COMMENT,
4225 /* TRANS: "vote" as a process */
4226 _("Your new vote canceled your previous vote."));
4229 describe_vote(vote, votedesc, sizeof(votedesc));
4231 if (vote_is_team_only(vote)) {
4232 /* TRANS: "vote" as a process */
4233 what = _("New teamvote");
4234 teamplr = conn_get_player(caller);
4235 color = ftc_vote_team;
4236 } else {
4237 /* TRANS: "vote" as a process */
4238 what = _("New vote");
4239 teamplr = NULL;
4240 color = ftc_vote_public;
4242 notify_team(teamplr, NULL, E_VOTE_NEW, color,
4243 /* TRANS: "[New vote|New teamvote] (number 3)
4244 * by fred: proposed change" */
4245 _("%s (number %d) by %s: %s"), what,
4246 vote->vote_no, caller->username, votedesc);
4248 /* Vote on your own suggestion. */
4249 connection_vote(caller, vote, VOTE_YES);
4250 return TRUE;
4252 } else {
4253 cmd_reply(CMD_VOTE, caller, C_FAIL,
4254 /* TRANS: "vote" as a process */
4255 _("Your new vote (\"%s\") was not "
4256 "legal or was not recognized."), full_command);
4257 return FALSE;
4261 if (caller
4262 && !((check || vote_would_pass_immediately(caller, cmd))
4263 && conn_get_access(caller) >= ALLOW_BASIC
4264 && level == ALLOW_CTRL)
4265 && conn_get_access(caller) < level) {
4266 cmd_reply(cmd, caller, C_FAIL,
4267 _("You are not allowed to use this command."));
4268 return FALSE;
4271 if (!check) {
4272 struct conn_list *echo_list = NULL;
4273 bool echo_list_allocated = FALSE;
4275 switch (command_echo(command_by_number(cmd))) {
4276 case CMD_ECHO_NONE:
4277 break;
4278 case CMD_ECHO_ADMINS:
4279 conn_list_iterate(game.est_connections, pconn) {
4280 if (ALLOW_ADMIN <= conn_get_access(pconn)) {
4281 if (NULL == echo_list) {
4282 echo_list = conn_list_new();
4283 echo_list_allocated = TRUE;
4285 conn_list_append(echo_list, pconn);
4287 } conn_list_iterate_end;
4288 break;
4289 case CMD_ECHO_ALL:
4290 echo_list = game.est_connections;
4291 break;
4294 if (NULL != echo_list) {
4295 if (caller) {
4296 notify_conn(echo_list, NULL, E_SETTING, ftc_any,
4297 "%s: '%s %s'", caller->username, command, arg);
4298 } else {
4299 notify_conn(echo_list, NULL, E_SETTING, ftc_server_prompt,
4300 "%s: '%s %s'", _("(server prompt)"), command, arg);
4302 if (echo_list_allocated) {
4303 conn_list_destroy(echo_list);
4308 switch(cmd) {
4309 case CMD_REMOVE:
4310 return remove_player_command(caller, arg, check);
4311 case CMD_SAVE:
4312 return save_command(caller, arg, check);
4313 case CMD_SCENSAVE:
4314 return scensave_command(caller, arg, check);
4315 case CMD_LOAD:
4316 return load_command(caller, arg, check, FALSE);
4317 case CMD_METAPATCHES:
4318 return metapatches_command(caller, arg, check);
4319 case CMD_METAMESSAGE:
4320 return metamessage_command(caller, arg, check);
4321 case CMD_METACONN:
4322 return metaconnection_command(caller, arg, check);
4323 case CMD_METASERVER:
4324 return metaserver_command(caller, arg, check);
4325 case CMD_HELP:
4326 return show_help(caller, arg);
4327 case CMD_SRVID:
4328 return show_serverid(caller, arg);
4329 case CMD_LIST:
4330 return show_list(caller, arg);
4331 case CMD_AITOGGLE:
4332 return toggle_ai_command(caller, arg, check);
4333 case CMD_TAKE:
4334 return take_command(caller, arg, check);
4335 case CMD_OBSERVE:
4336 return observe_command(caller, arg, check);
4337 case CMD_DETACH:
4338 return detach_command(caller, arg, check);
4339 case CMD_CREATE:
4340 return create_command(caller, arg, check);
4341 case CMD_AWAY:
4342 return away_command(caller, check);
4343 case CMD_HANDICAPPED:
4344 case CMD_NOVICE:
4345 case CMD_EASY:
4346 case CMD_NORMAL:
4347 case CMD_HARD:
4348 case CMD_CHEATING:
4349 #ifdef FREECIV_DEBUG
4350 case CMD_EXPERIMENTAL:
4351 #endif
4352 return set_ai_level_named(caller, arg, command_name_by_number(cmd), check);
4353 case CMD_QUIT:
4354 return quit_game(caller, check);
4355 case CMD_CUT:
4356 return cut_client_connection(caller, arg, check);
4357 case CMD_SHOW:
4358 return show_command(caller, arg, check);
4359 case CMD_EXPLAIN:
4360 return explain_option(caller, arg, check);
4361 case CMD_DEBUG:
4362 return debug_command(caller, arg, check);
4363 case CMD_SET:
4364 return set_command(caller, arg, check);
4365 case CMD_TEAM:
4366 return team_command(caller, arg, check);
4367 case CMD_RULESETDIR:
4368 return set_rulesetdir(caller, arg, check, read_recursion);
4369 case CMD_WALL:
4370 return wall(arg, check);
4371 case CMD_CONNECTMSG:
4372 return connectmsg_command(caller, arg, check);
4373 case CMD_VOTE:
4374 return vote_command(caller, arg, check);
4375 case CMD_CANCELVOTE:
4376 return cancelvote_command(caller, arg, check);
4377 case CMD_READ_SCRIPT:
4378 return read_command(caller, arg, check, read_recursion);
4379 case CMD_WRITE_SCRIPT:
4380 return write_command(caller, arg, check);
4381 case CMD_RESET:
4382 return reset_command(caller, arg, check, read_recursion);
4383 case CMD_DEFAULT:
4384 return default_command(caller, arg, check);
4385 case CMD_LUA:
4386 return lua_command(caller, arg, check);
4387 case CMD_KICK:
4388 return kick_command(caller, arg, check);
4389 case CMD_DELEGATE:
4390 return delegate_command(caller, arg, check);
4391 case CMD_AICMD:
4392 return aicmd_command(caller, arg, check);
4393 case CMD_FCDB:
4394 return fcdb_command(caller, arg, check);
4395 case CMD_MAPIMG:
4396 return mapimg_command(caller, arg, check);
4397 case CMD_RFCSTYLE: /* see console.h for an explanation */
4398 if (!check) {
4399 con_set_style(!con_get_style());
4401 return TRUE;
4402 case CMD_CMDLEVEL:
4403 return cmdlevel_command(caller, arg, check);
4404 case CMD_FIRSTLEVEL:
4405 return firstlevel_command(caller, check);
4406 case CMD_TIMEOUT:
4407 return timeout_command(caller, arg, check);
4408 case CMD_START_GAME:
4409 return start_command(caller, check, FALSE);
4410 case CMD_END_GAME:
4411 return end_command(caller, arg, check);
4412 case CMD_SURRENDER:
4413 return surrender_command(caller, arg, check);
4414 case CMD_IGNORE:
4415 return ignore_command(caller, arg, check);
4416 case CMD_UNIGNORE:
4417 return unignore_command(caller, arg, check);
4418 case CMD_PLAYERCOLOR:
4419 return playercolor_command(caller, arg, check);
4420 case CMD_NUM:
4421 case CMD_UNRECOGNIZED:
4422 case CMD_AMBIGUOUS:
4423 break;
4425 /* should NEVER happen! */
4426 log_error("Unknown command variant: %d.", cmd);
4427 return FALSE;
4430 /**************************************************************************
4431 End the game immediately in a draw.
4432 **************************************************************************/
4433 static bool end_command(struct connection *caller, char *str, bool check)
4435 if (S_S_RUNNING == server_state()) {
4436 if (check) {
4437 return TRUE;
4439 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4440 _("Game is over."));
4441 set_server_state(S_S_OVER);
4442 force_end_of_sniff = TRUE;
4443 cmd_reply(CMD_END_GAME, caller, C_OK,
4444 _("Ending the game. The server will restart once all clients "
4445 "have disconnected."));
4446 return TRUE;
4447 } else {
4448 cmd_reply(CMD_END_GAME, caller, C_FAIL,
4449 _("Cannot end the game: no game running."));
4450 return FALSE;
4454 /**************************************************************************
4455 Concede the game. You still continue playing until all but one player
4456 or team remains un-conceded.
4457 **************************************************************************/
4458 static bool surrender_command(struct connection *caller, char *str, bool check)
4460 struct player *pplayer;
4462 if (caller == NULL || !conn_controls_player(caller)) {
4463 cmd_reply(CMD_SURRENDER, caller, C_FAIL,
4464 _("You are not allowed to use this command."));
4465 return FALSE;
4468 if (S_S_RUNNING != server_state()) {
4469 cmd_reply(CMD_SURRENDER, caller, C_FAIL, _("You cannot surrender now."));
4470 return FALSE;
4473 pplayer = conn_get_player(caller);
4474 if (player_status_check(pplayer, PSTATUS_SURRENDER)) {
4475 cmd_reply(CMD_SURRENDER, caller, C_FAIL,
4476 _("You have already conceded the game."));
4477 return FALSE;
4480 if (check) {
4481 return TRUE;
4484 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4485 _("%s has conceded the game and can no longer win."),
4486 player_name(pplayer));
4487 player_status_add(pplayer, PSTATUS_SURRENDER);
4488 return TRUE;
4491 /* Define the possible arguments to the reset command */
4492 #define SPECENUM_NAME reset_args
4493 #define SPECENUM_VALUE0 RESET_GAME
4494 #define SPECENUM_VALUE0NAME "game"
4495 #define SPECENUM_VALUE1 RESET_RULESET
4496 #define SPECENUM_VALUE1NAME "ruleset"
4497 #define SPECENUM_VALUE2 RESET_SCRIPT
4498 #define SPECENUM_VALUE2NAME "script"
4499 #define SPECENUM_VALUE3 RESET_DEFAULT
4500 #define SPECENUM_VALUE3NAME "default"
4501 #include "specenum_gen.h"
4503 /**************************************************************************
4504 Returns possible parameters for the reset command.
4505 **************************************************************************/
4506 static const char *reset_accessor(int i)
4508 i = CLIP(0, i, reset_args_max());
4509 return reset_args_name((enum reset_args) i);
4512 /**************************************************************************
4513 Reload the game settings from the ruleset and reload the init script if
4514 one was used.
4515 **************************************************************************/
4516 static bool reset_command(struct connection *caller, char *arg, bool check,
4517 int read_recursion)
4519 enum m_pre_result result;
4520 int ind;
4522 /* match the argument */
4523 result = match_prefix(reset_accessor, reset_args_max() + 1, 0,
4524 fc_strncasecmp, NULL, arg, &ind);
4526 switch (result) {
4527 case M_PRE_EXACT:
4528 case M_PRE_ONLY:
4529 /* we have a match */
4530 break;
4531 case M_PRE_AMBIGUOUS:
4532 case M_PRE_EMPTY:
4533 /* use 'ruleset' [1] if the game was not started; else use 'game' [2] */
4534 if (S_S_INITIAL == server_state() && game.info.is_new_game) {
4535 cmd_reply(CMD_RESET, caller, C_WARNING,
4536 _("Guessing argument 'ruleset'."));
4537 ind = RESET_RULESET;
4538 } else {
4539 cmd_reply(CMD_RESET, caller, C_WARNING,
4540 _("Guessing argument 'game'."));
4541 ind = RESET_GAME;
4543 break;
4544 case M_PRE_LONG:
4545 case M_PRE_FAIL:
4546 case M_PRE_LAST:
4547 cmd_reply(CMD_RESET, caller, C_FAIL,
4548 _("The valid arguments are: 'game', 'ruleset', 'script' "
4549 "or 'default'."));
4550 return FALSE;
4551 break;
4554 if (check) {
4555 return TRUE;
4558 switch (ind) {
4559 case RESET_GAME:
4560 if (!game.info.is_new_game) {
4561 if (settings_game_reset()) {
4562 cmd_reply(CMD_RESET, caller, C_OK,
4563 _("Reset all settings to the values at the game start."));
4564 } else {
4565 cmd_reply(CMD_RESET, caller, C_FAIL,
4566 _("No saved settings from the game start available."));
4567 return FALSE;
4569 } else {
4570 cmd_reply(CMD_RESET, caller, C_FAIL, _("No game started..."));
4571 return FALSE;
4573 break;
4575 case RESET_RULESET:
4576 /* Restore game settings save in game.ruleset. */
4577 if (reload_rulesets_settings()) {
4578 cmd_reply(CMD_RESET, caller, C_OK,
4579 _("Reset all settings to ruleset values."));
4580 } else {
4581 cmd_reply(CMD_RESET, caller, C_FAIL,
4582 _("Failed to reset settings to ruleset values."));
4584 break;
4586 case RESET_SCRIPT:
4587 cmd_reply(CMD_RESET, caller, C_OK,
4588 _("Reset all settings and rereading the server start "
4589 "script."));
4590 settings_reset();
4591 /* load initial script */
4592 if (NULL != srvarg.script_filename
4593 && !read_init_script_real(NULL, srvarg.script_filename, TRUE, FALSE,
4594 read_recursion + 1)) {
4595 if (NULL != caller) {
4596 cmd_reply(CMD_RESET, caller, C_FAIL,
4597 _("Could not read script file '%s'."),
4598 srvarg.script_filename);
4600 return FALSE;
4602 break;
4604 case RESET_DEFAULT:
4605 cmd_reply(CMD_RESET, caller, C_OK,
4606 _("Reset all settings to default values."));
4607 settings_reset();
4608 break;
4611 send_server_settings(game.est_connections);
4612 cmd_reply(CMD_RESET, caller, C_OK, _("Settings re-initialized."));
4614 /* show ruleset summary and list changed values */
4615 show_ruleset_info(caller, CMD_RESET, check, read_recursion);
4617 return TRUE;
4620 /**************************************************************************
4621 Set a setting to its default value
4622 **************************************************************************/
4623 static bool default_command(struct connection *caller, char *arg, bool check)
4625 struct setting *pset;
4626 char reject_msg[256] = "";
4628 pset = validate_setting_arg(CMD_DEFAULT, caller, arg);
4630 if (!pset) {
4631 /* Reason already reported. */
4632 return FALSE;
4635 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))) {
4636 cmd_reply(CMD_DEFAULT, caller, C_FAIL, "%s", reject_msg);
4638 return FALSE;
4641 if (!check) {
4642 setting_set_to_default(pset);
4643 cmd_reply(CMD_DEFAULT, caller, C_OK,
4644 _("Option '%s' reset to default value."), arg);
4647 return TRUE;
4650 /* Define the possible arguments to the delegation command */
4651 #define SPECENUM_NAME lua_args
4652 #define SPECENUM_VALUE0 LUA_CMD
4653 #define SPECENUM_VALUE0NAME "cmd"
4654 #define SPECENUM_VALUE1 LUA_FILE
4655 #define SPECENUM_VALUE1NAME "file"
4656 #include "specenum_gen.h"
4658 /*****************************************************************************
4659 Returns possible parameters for the reset command.
4660 *****************************************************************************/
4661 static const char *lua_accessor(int i)
4663 i = CLIP(0, i, lua_args_max());
4664 return lua_args_name((enum lua_args) i);
4667 /*****************************************************************************
4668 Evaluate a line of lua script or a lua script file.
4669 *****************************************************************************/
4670 static bool lua_command(struct connection *caller, char *arg, bool check)
4672 FILE *script_file;
4673 const char extension[] = ".lua", *real_filename = NULL;
4674 char luafile[4096], tilde_filename[4096];
4675 char *tokens[1], *luaarg = NULL;
4676 int ntokens, ind;
4677 enum m_pre_result result;
4678 bool ret = FALSE;
4680 ntokens = get_tokens(arg, tokens, 1, TOKEN_DELIMITERS);
4682 if (ntokens > 0) {
4683 /* match the argument */
4684 result = match_prefix(lua_accessor, lua_args_max() + 1, 0,
4685 fc_strncasecmp, NULL, tokens[0], &ind);
4687 switch (result) {
4688 case M_PRE_EXACT:
4689 case M_PRE_ONLY:
4690 /* We have a match */
4691 luaarg = arg + strlen(lua_args_name(ind));
4692 luaarg = skip_leading_spaces(luaarg);
4693 break;
4694 case M_PRE_EMPTY:
4695 /* Nothing. */
4696 break;
4697 case M_PRE_AMBIGUOUS:
4698 case M_PRE_LONG:
4699 case M_PRE_FAIL:
4700 case M_PRE_LAST:
4701 /* Fall back to depreciated 'lua <script command>' syntax. */
4702 cmd_reply(CMD_LUA, caller, C_SYNTAX,
4703 _("Fall back to old syntax '%slua <script command>'."),
4704 caller ? "/" : "");
4705 ind = LUA_CMD;
4706 luaarg = arg;
4707 break;
4711 if (luaarg == NULL) {
4712 cmd_reply(CMD_LUA, caller, C_FAIL,
4713 _("No lua command or lua script file. See '%shelp lua'."),
4714 caller ? "/" : "");
4715 ret = TRUE;
4716 goto cleanup;
4719 switch (ind) {
4720 case LUA_CMD:
4721 /* Nothing to check. */
4722 break;
4723 case LUA_FILE:
4724 /* Abuse real_filename to find if we already have a .lua extension. */
4725 real_filename = luaarg + strlen(luaarg) - MIN(strlen(extension),
4726 strlen(luaarg));
4727 if (strcmp(real_filename, extension) != 0) {
4728 fc_snprintf(luafile, sizeof(luafile), "%s%s", luaarg, extension);
4729 } else {
4730 sz_strlcpy(luafile, luaarg);
4733 if (is_restricted(caller)) {
4734 if (!is_safe_filename(luafile)) {
4735 cmd_reply(CMD_LUA, caller, C_FAIL,
4736 _("Freeciv script '%s' disallowed for security reasons."),
4737 luafile);
4738 ret = FALSE;
4739 goto cleanup;;
4741 sz_strlcpy(tilde_filename, luafile);
4742 } else {
4743 interpret_tilde(tilde_filename, sizeof(tilde_filename), luafile);
4746 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
4747 if (!real_filename) {
4748 if (is_restricted(caller)) {
4749 cmd_reply(CMD_LUA, caller, C_FAIL,
4750 _("No Freeciv script found by the name '%s'."),
4751 tilde_filename);
4752 ret = FALSE;
4753 goto cleanup;
4755 /* File is outside data directories */
4756 real_filename = tilde_filename;
4758 break;
4761 if (check) {
4762 ret = TRUE;
4763 goto cleanup;
4766 switch (ind) {
4767 case LUA_CMD:
4768 ret = script_server_do_string(caller, luaarg);
4769 break;
4770 case LUA_FILE:
4771 cmd_reply(CMD_LUA, caller, C_COMMENT,
4772 _("Loading Freeciv script file '%s'."), real_filename);
4774 if (is_reg_file_for_access(real_filename, FALSE)
4775 && (script_file = fc_fopen(real_filename, "r"))) {
4776 ret = script_server_do_file(caller, real_filename);
4777 goto cleanup;
4778 } else {
4779 cmd_reply(CMD_LUA, caller, C_FAIL,
4780 _("Cannot read Freeciv script '%s'."), real_filename);
4781 ret = FALSE;
4782 goto cleanup;
4786 cleanup:
4787 free_tokens(tokens, ntokens);
4788 return ret;
4791 /* Define the possible arguments to the delegation command */
4792 #define SPECENUM_NAME delegate_args
4793 #define SPECENUM_VALUE0 DELEGATE_CANCEL
4794 #define SPECENUM_VALUE0NAME "cancel"
4795 #define SPECENUM_VALUE1 DELEGATE_RESTORE
4796 #define SPECENUM_VALUE1NAME "restore"
4797 #define SPECENUM_VALUE2 DELEGATE_SHOW
4798 #define SPECENUM_VALUE2NAME "show"
4799 #define SPECENUM_VALUE3 DELEGATE_TAKE
4800 #define SPECENUM_VALUE3NAME "take"
4801 #define SPECENUM_VALUE4 DELEGATE_TO
4802 #define SPECENUM_VALUE4NAME "to"
4803 #include "specenum_gen.h"
4805 /*****************************************************************************
4806 Returns possible parameters for the 'delegate' command.
4807 *****************************************************************************/
4808 static const char *delegate_accessor(int i)
4810 i = CLIP(0, i, delegate_args_max());
4811 return delegate_args_name((enum delegate_args) i);
4814 /*****************************************************************************
4815 Handle delegation of control.
4816 *****************************************************************************/
4817 static bool delegate_command(struct connection *caller, char *arg,
4818 bool check)
4820 char *tokens[3];
4821 int ntokens, ind = delegate_args_invalid();
4822 enum m_pre_result result;
4823 bool player_specified = FALSE; /* affects messages only */
4824 bool ret = FALSE;
4825 const char *username = NULL;
4826 struct player *dplayer = NULL;
4828 if (!game_was_started()) {
4829 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Game not started - "
4830 "cannot delegate yet."));
4831 return FALSE;
4834 ntokens = get_tokens(arg, tokens, 3, TOKEN_DELIMITERS);
4836 if (ntokens > 0) {
4837 /* match the argument */
4838 result = match_prefix(delegate_accessor, delegate_args_max() + 1, 0,
4839 fc_strncasecmp, NULL, tokens[0], &ind);
4841 switch (result) {
4842 case M_PRE_EXACT:
4843 case M_PRE_ONLY:
4844 /* we have a match */
4845 break;
4846 case M_PRE_EMPTY:
4847 if (caller) {
4848 /* Use 'delegate show' as default. */
4849 ind = DELEGATE_SHOW;
4851 break;
4852 case M_PRE_AMBIGUOUS:
4853 case M_PRE_LONG:
4854 case M_PRE_FAIL:
4855 case M_PRE_LAST:
4856 ind = delegate_args_invalid();
4857 break;
4859 } else {
4860 if (caller) {
4861 /* Use 'delegate show' as default. */
4862 ind = DELEGATE_SHOW;
4866 if (!delegate_args_is_valid(ind)) {
4867 char buf[256] = "";
4868 enum delegate_args valid_args;
4870 for (valid_args = delegate_args_begin();
4871 valid_args != delegate_args_end();
4872 valid_args = delegate_args_next(valid_args)) {
4873 cat_snprintf(buf, sizeof(buf), "'%s'",
4874 delegate_args_name(valid_args));
4875 if (valid_args != delegate_args_max()) {
4876 cat_snprintf(buf, sizeof(buf), ", ");
4880 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4881 /* TRANS: do not translate the command 'delegate'. */
4882 _("Valid arguments for 'delegate' are: %s."), buf);
4883 ret = FALSE;
4884 goto cleanup;
4887 /* Get the data (player, username for delegation) and validate it. */
4888 switch (ind) {
4889 case DELEGATE_CANCEL:
4890 /* delegate cancel [player] */
4891 if (ntokens > 1) {
4892 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4893 player_specified = TRUE;
4894 dplayer = player_by_name_prefix(tokens[1], &result);
4895 if (!dplayer) {
4896 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4897 ret = FALSE;
4898 goto cleanup;
4900 } else {
4901 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4902 _("Command level '%s' or greater needed to modify "
4903 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4904 ret = FALSE;
4905 goto cleanup;
4907 } else {
4908 dplayer = conn_get_player(caller);
4909 if (!dplayer) {
4910 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4911 _("Please specify a player for whom delegation should "
4912 "be canceled."));
4913 ret = FALSE;
4914 goto cleanup;
4917 break;
4918 case DELEGATE_RESTORE:
4919 /* delegate restore */
4920 if (!caller) {
4921 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4922 _("You can't switch players from the console."));
4923 ret = FALSE;
4924 goto cleanup;
4926 break;
4927 case DELEGATE_SHOW:
4928 /* delegate show [player] */
4929 if (ntokens > 1) {
4930 player_specified = TRUE;
4931 dplayer = player_by_name_prefix(tokens[1], &result);
4932 if (!dplayer) {
4933 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4934 ret = FALSE;
4935 goto cleanup;
4937 } else {
4938 dplayer = conn_get_player(caller);
4939 if (!dplayer) {
4940 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4941 _("Please specify a player for whom the delegation should "
4942 "be shown."));
4943 ret = FALSE;
4944 goto cleanup;
4947 break;
4948 case DELEGATE_TAKE:
4949 /* delegate take <player> */
4950 if (!caller) {
4951 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4952 _("You can't switch players from the console."));
4953 ret = FALSE;
4954 goto cleanup;
4956 if (ntokens > 1) {
4957 player_specified = TRUE;
4958 dplayer = player_by_name_prefix(tokens[1], &result);
4959 if (!dplayer) {
4960 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4961 ret = FALSE;
4962 goto cleanup;
4964 } else {
4965 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4966 _("Please specify a player to take control of."));
4967 ret = FALSE;
4968 goto cleanup;
4970 break;
4971 case DELEGATE_TO:
4972 /* delegate to <username> [player] */
4973 if (ntokens > 1) {
4974 username = tokens[1];
4975 } else {
4976 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4977 _("Please specify a user to whom control is to be delegated."));
4978 ret = FALSE;
4979 goto cleanup;
4981 if (ntokens > 2) {
4982 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4983 player_specified = TRUE;
4984 dplayer = player_by_name_prefix(tokens[2], &result);
4985 if (!dplayer) {
4986 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[2], result);
4987 ret = FALSE;
4988 goto cleanup;
4990 } else {
4991 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4992 _("Command level '%s' or greater needed to modify "
4993 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4994 ret = FALSE;
4995 goto cleanup;
4997 } else {
4998 dplayer = conn_controls_player(caller) ? conn_get_player(caller) : NULL;
4999 if (!dplayer) {
5000 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5001 _("You do not control a player."));
5002 ret = FALSE;
5003 goto cleanup;
5006 break;
5009 /* All checks done to this point will give pretty much the same result at
5010 * any time. Checks after this point are more likely to vary over time. */
5011 if (check) {
5012 ret = TRUE;
5013 goto cleanup;
5016 switch (ind) {
5017 case DELEGATE_TO:
5018 /* Delegate control of player to another user. */
5019 fc_assert_ret_val(dplayer, FALSE);
5020 fc_assert_ret_val(username != NULL, FALSE);
5022 /* Forbid delegation of players already controlled by a delegate, and
5023 * those 'put aside' by a delegate.
5024 * For the former, if player is already under active delegate control,
5025 * we wouldn't handle the revocation that would be necessary if their
5026 * delegation changed; and the authority granted to delegates does not
5027 * include the ability to sub-delegate.
5028 * For the latter, allowing control of the 'put aside' player to be
5029 * delegated would break the invariant that whenever a user is connected,
5030 * they are attached to 'their' player. */
5031 if (player_delegation_active(dplayer)) {
5032 if (!player_delegation_get(dplayer)) {
5033 /* Attempting to change a 'put aside' player. Must be admin
5034 * or console. */
5035 fc_assert(player_specified);
5036 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5037 _("Can't delegate control of '%s' belonging to %s while "
5038 "they are controlling another player."),
5039 player_name(dplayer), dplayer->username);
5040 } else if (player_specified) {
5041 /* Admin or console attempting to change a controlled player. */
5042 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5043 _("Can't change delegation of '%s' while controlled by "
5044 "delegate %s."), player_name(dplayer), dplayer->username);
5045 } else {
5046 /* Caller must be the delegate. Give more specific message.
5047 * (We don't know if they thought they were delegating their
5048 * original or delegated player, but we don't allow either.) */
5049 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5050 _("You can't delegate control while you are controlling "
5051 "a delegated player yourself."));
5053 ret = FALSE;
5054 goto cleanup;
5057 /* Forbid delegation to player's original owner
5058 * (from above test we know that dplayer->username is the original now) */
5059 if (fc_strcasecmp(dplayer->username, username) == 0) {
5060 if (player_specified) {
5061 /* Probably admin or console. */
5062 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5063 /* TRANS: don't translate 'delegate cancel' */
5064 _("%s already owns '%s', so cannot also be delegate. "
5065 "Use '%sdelegate cancel' to cancel an existing "
5066 "delegation."),
5067 username, player_name(dplayer), caller?"/":"");
5068 } else {
5069 /* Player not specified on command line, so they must have been trying
5070 * to delegate control to themself. Give more specific message. */
5071 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5072 /* TRANS: don't translate '/delegate cancel' */
5073 _("You can't delegate control to yourself. "
5074 "Use '/delegate cancel' to cancel an existing "
5075 "delegation."));
5077 ret = FALSE;
5078 goto cleanup;
5081 /* FIXME: if control was already delegated to someone else, that
5082 * delegation is implicitly canceled. Perhaps we should tell someone. */
5084 player_delegation_set(dplayer, username);
5085 cmd_reply(CMD_DELEGATE, caller, C_OK,
5086 _("Control of player '%s' delegated to user %s."),
5087 player_name(dplayer), username);
5088 ret = TRUE;
5089 goto cleanup;
5090 break;
5092 case DELEGATE_SHOW:
5093 /* Show delegations. */
5094 fc_assert_ret_val(dplayer, FALSE);
5096 if (player_delegation_get(dplayer) == NULL) {
5097 /* No delegation set. */
5098 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5099 _("No delegation defined for '%s'."),
5100 player_name(dplayer));
5101 } else {
5102 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5103 _("Control of player '%s' delegated to user %s."),
5104 player_name(dplayer), player_delegation_get(dplayer));
5106 ret = TRUE;
5107 goto cleanup;
5108 break;
5110 case DELEGATE_CANCEL:
5111 if (player_delegation_get(dplayer) == NULL) {
5112 /* No delegation set. */
5113 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5114 _("No delegation defined for '%s'."),
5115 player_name(dplayer));
5116 ret = FALSE;
5117 goto cleanup;
5120 if (player_delegation_active(dplayer)) {
5121 /* Delegation is currently in use. Forcibly break connection. */
5122 struct connection *pdelegate;
5123 /* (Can only happen if admin/console issues this command, as owner
5124 * will end use by their mere presence.) */
5125 fc_assert(player_specified);
5126 pdelegate = conn_by_user(player_delegation_get(dplayer));
5127 fc_assert_ret_val(pdelegate != NULL, FALSE);
5128 if (!connection_delegate_restore(pdelegate)) {
5129 /* Should never happen. Generic failure message. */
5130 log_error("Failed to restore %s's connection as %s during "
5131 "'delegate cancel'.", pdelegate->username,
5132 delegate_player_str(pdelegate->server.delegation.playing,
5133 pdelegate->server.delegation.observer));
5134 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5135 ret = FALSE;
5136 goto cleanup;
5138 notify_conn(pdelegate->self, NULL, E_CONNECTION, ftc_server,
5139 _("Your delegated control of player '%s' was canceled."),
5140 player_name(dplayer));
5143 player_delegation_set(dplayer, NULL);
5144 cmd_reply(CMD_DELEGATE, caller, C_OK, _("Delegation of '%s' canceled."),
5145 player_name(dplayer));
5146 ret = TRUE;
5147 goto cleanup;
5148 break;
5150 case DELEGATE_TAKE:
5151 /* Try to take another player. */
5152 fc_assert_ret_val(dplayer, FALSE);
5153 fc_assert_ret_val(caller, FALSE);
5155 if (caller->server.delegation.status) {
5156 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5157 /* TRANS: don't translate '/delegate restore'. */
5158 _("You are already controlling a delegated player. "
5159 "Use '/delegate restore' to relinquish control of your "
5160 "current player first."));
5161 ret = FALSE;
5162 goto cleanup;
5165 /* Don't allow 'put aside' players to be delegated; the invariant is
5166 * that while the owning user is connected to the server, they are
5167 * in sole control of 'their' player. */
5168 if (conn_controls_player(caller)
5169 && player_delegation_get(conn_get_player(caller)) != NULL) {
5170 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5171 /* TRANS: don't translate '/delegate cancel'. */
5172 _("Can't take player while you have delegated control "
5173 "yourself. Use '/delegate cancel' to cancel your own "
5174 "delegation first."));
5175 ret = FALSE;
5176 goto cleanup;
5179 /* Taking your own player makes no sense. */
5180 if (conn_controls_player(caller)
5181 && dplayer == conn_get_player(caller)) {
5182 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("You already control '%s'."),
5183 player_name(conn_get_player(caller)));
5184 ret = FALSE;
5185 goto cleanup;
5188 if (!player_delegation_get(dplayer)
5189 || fc_strcasecmp(player_delegation_get(dplayer), caller->username) != 0) {
5190 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5191 _("Control of player '%s' has not been delegated to you."),
5192 player_name(dplayer));
5193 ret = FALSE;
5194 goto cleanup;
5197 /* If the player is controlled by another user, fail. */
5198 if (dplayer->is_connected) {
5199 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5200 _("Another user already controls player '%s'."),
5201 player_name(dplayer));
5202 ret = FALSE;
5203 goto cleanup;
5206 if (!connection_delegate_take(caller, dplayer)) {
5207 /* Should never happen. Generic failure message. */
5208 log_error("%s failed to take control of '%s' during 'delegate take'.",
5209 caller->username, player_name(dplayer));
5210 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5211 ret = FALSE;
5212 goto cleanup;
5215 cmd_reply(CMD_DELEGATE, caller, C_OK,
5216 _("%s is now controlling player '%s'."), caller->username,
5217 player_name(conn_get_player(caller)));
5218 ret = TRUE;
5219 goto cleanup;
5220 break;
5222 case DELEGATE_RESTORE:
5223 /* Delegate user relinquishes control of delegated player, returning to
5224 * previous view (e.g. observer) if any. */
5225 fc_assert_ret_val(caller, FALSE);
5227 if (!caller->server.delegation.status) {
5228 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5229 _("You are not currently controlling a delegated player."));
5230 ret = FALSE;
5231 goto cleanup;
5234 if (!connection_delegate_restore(caller)) {
5235 /* Should never happen. Generic failure message. */
5236 log_error("Failed to restore %s's connection as %s during "
5237 "'delegate restore'.", caller->username,
5238 delegate_player_str(caller->server.delegation.playing,
5239 caller->server.delegation.observer));
5240 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5241 ret = FALSE;
5242 goto cleanup;
5245 cmd_reply(CMD_DELEGATE, caller, C_OK,
5246 /* TRANS: "<user> is now connected to <player>" where <player>
5247 * can also be "global observer" or "nothing" */
5248 _("%s is now connected as %s."), caller->username,
5249 delegate_player_str(conn_get_player(caller), caller->observer));
5250 ret = TRUE;
5251 goto cleanup;
5252 break;
5255 cleanup:
5256 free_tokens(tokens, ntokens);
5257 return ret;
5260 /*****************************************************************************
5261 Return static string describing what a connection is connected to.
5262 *****************************************************************************/
5263 static const char *delegate_player_str(struct player *pplayer, bool observer)
5265 static struct astring buf;
5267 if (pplayer) {
5268 if (observer) {
5269 astr_set(&buf, _("%s (observer)"), player_name(pplayer));
5270 } else {
5271 astr_set(&buf, "%s", player_name(pplayer));
5273 } else if (observer) {
5274 astr_set(&buf, "%s", _("global observer"));
5275 } else {
5276 /* TRANS: in place of player name or "global observer" */
5277 astr_set(&buf, "%s", _("nothing"));
5280 return astr_str(&buf);
5283 /* Define the possible arguments to the mapimg command */
5284 /* map image layers */
5285 #define SPECENUM_NAME mapimg_args
5286 #define SPECENUM_VALUE0 MAPIMG_COLORTEST
5287 #define SPECENUM_VALUE0NAME "colortest"
5288 #define SPECENUM_VALUE1 MAPIMG_CREATE
5289 #define SPECENUM_VALUE1NAME "create"
5290 #define SPECENUM_VALUE2 MAPIMG_DEFINE
5291 #define SPECENUM_VALUE2NAME "define"
5292 #define SPECENUM_VALUE3 MAPIMG_DELETE
5293 #define SPECENUM_VALUE3NAME "delete"
5294 #define SPECENUM_VALUE4 MAPIMG_SHOW
5295 #define SPECENUM_VALUE4NAME "show"
5296 #define SPECENUM_COUNT MAPIMG_COUNT
5297 #include "specenum_gen.h"
5299 /**************************************************************************
5300 Returns possible parameters for the mapimg command.
5301 **************************************************************************/
5302 static const char *mapimg_accessor(int i)
5304 i = CLIP(0, i, mapimg_args_max());
5305 return mapimg_args_name((enum mapimg_args) i);
5308 /**************************************************************************
5309 Handle mapimg command
5310 **************************************************************************/
5311 static bool mapimg_command(struct connection *caller, char *arg, bool check)
5313 enum m_pre_result result;
5314 int ind, ntokens, id;
5315 char *token[2];
5316 bool ret = TRUE;
5318 ntokens = get_tokens(arg, token, 2, TOKEN_DELIMITERS);
5320 if (ntokens > 0) {
5321 /* match the argument */
5322 result = match_prefix(mapimg_accessor, MAPIMG_COUNT, 0,
5323 fc_strncasecmp, NULL, token[0], &ind);
5325 switch (result) {
5326 case M_PRE_EXACT:
5327 case M_PRE_ONLY:
5328 /* we have a match */
5329 break;
5330 case M_PRE_AMBIGUOUS:
5331 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5332 _("Ambiguous 'mapimg' command."));
5333 ret = FALSE;
5334 goto cleanup;
5335 break;
5336 case M_PRE_EMPTY:
5337 /* use 'show' as default */
5338 ind = MAPIMG_SHOW;
5339 break;
5340 case M_PRE_LONG:
5341 case M_PRE_FAIL:
5342 case M_PRE_LAST:
5344 char buf[256] = "";
5345 enum mapimg_args valid_args;
5347 for (valid_args = mapimg_args_begin();
5348 valid_args != mapimg_args_end();
5349 valid_args = mapimg_args_next(valid_args)) {
5350 cat_snprintf(buf, sizeof(buf), "'%s'",
5351 mapimg_args_name(valid_args));
5352 if (valid_args != mapimg_args_max()) {
5353 cat_snprintf(buf, sizeof(buf), ", ");
5357 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5358 _("The valid arguments are: %s."), buf);
5359 ret = FALSE;
5360 goto cleanup;
5362 break;
5364 } else {
5365 /* use 'show' as default */
5366 ind = MAPIMG_SHOW;
5369 switch (ind) {
5370 case MAPIMG_DEFINE:
5371 if (ntokens == 1) {
5372 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5373 _("Missing argument for 'mapimg define'."));
5374 ret = FALSE;
5375 } else {
5376 /* 'mapimg define <mapstr>' */
5377 if (!mapimg_define(token[1], check)) {
5378 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5379 _("Can't use definition: %s."), mapimg_error());
5380 ret = FALSE;
5381 } else if (check) {
5382 /* Validated OK, bail out now */
5383 goto cleanup;
5384 } else if (game_was_started()
5385 && mapimg_isvalid(mapimg_count() - 1) == NULL) {
5386 /* game was started - error in map image definition check */
5387 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5388 _("Can't use definition: %s."), mapimg_error());
5389 ret = FALSE;
5390 } else {
5391 char str[MAX_LEN_MAPDEF];
5393 id = mapimg_count() - 1;
5395 mapimg_id2str(id, str, sizeof(str));
5396 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Defined as map image "
5397 "definition %d: '%s'."),
5398 id, str);
5401 break;
5403 case MAPIMG_DELETE:
5404 if (ntokens == 1) {
5405 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5406 _("Missing argument for 'mapimg delete'."));
5407 ret = FALSE;
5408 } else if (ntokens == 2 && strcmp(token[1], "all") == 0) {
5409 /* 'mapimg delete all' */
5410 if (check) {
5411 goto cleanup;
5414 while (mapimg_count() > 0) {
5415 mapimg_delete(0);
5417 cmd_reply(CMD_MAPIMG, caller, C_OK, _("All map image definitions "
5418 "deleted."));
5419 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5420 /* 'mapimg delete <id>' */
5421 if (check) {
5422 goto cleanup;
5425 if (!mapimg_delete(id)) {
5426 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5427 _("Couldn't delete definition: %s."), mapimg_error());
5428 ret = FALSE;
5429 } else {
5430 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map image definition %d "
5431 "deleted."), id);
5433 } else {
5434 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5435 _("Bad argument for 'mapimg delete': '%s'."), token[1]);
5436 ret = FALSE;
5438 break;
5440 case MAPIMG_SHOW:
5441 if (ntokens < 2 || (ntokens == 2 && strcmp(token[1], "all") == 0)) {
5442 /* 'mapimg show' or 'mapimg show all' */
5443 if (check) {
5444 goto cleanup;
5446 show_mapimg(caller, CMD_MAPIMG);
5447 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5448 char str[2048];
5449 /* 'mapimg show <id>' */
5450 if (check) {
5451 goto cleanup;
5454 if (mapimg_show(id, str, sizeof(str), TRUE)) {
5455 cmd_reply(CMD_MAPIMG, caller, C_OK, "%s", str);
5456 } else {
5457 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5458 _("Couldn't show definition: %s."), mapimg_error());
5459 ret = FALSE;
5461 } else {
5462 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5463 _("Bad argument for 'mapimg show': '%s'."), token[1]);
5464 ret = FALSE;
5466 break;
5468 case MAPIMG_COLORTEST:
5469 if (check) {
5470 goto cleanup;
5473 mapimg_colortest(game.server.save_name, NULL);
5474 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map color test images saved."));
5475 break;
5477 case MAPIMG_CREATE:
5478 if (ntokens < 2) {
5479 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5480 _("Missing argument for 'mapimg create'."));
5481 ret = FALSE;
5482 goto cleanup;
5485 if (strcmp(token[1], "all") == 0) {
5486 /* 'mapimg create all' */
5487 if (check) {
5488 goto cleanup;
5491 for (id = 0; id < mapimg_count(); id++) {
5492 struct mapdef *pmapdef = mapimg_isvalid(id);
5494 if (pmapdef == NULL
5495 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5496 srvarg.saves_pathname)) {
5497 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5498 _("Error saving map image %d: %s."), id, mapimg_error());
5499 ret = FALSE;
5502 } else if (sscanf(token[1], "%d", &id) != 0) {
5503 struct mapdef *pmapdef;
5505 /* 'mapimg create <id>' */
5506 if (check) {
5507 goto cleanup;
5510 pmapdef = mapimg_isvalid(id);
5511 if (pmapdef == NULL
5512 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5513 srvarg.saves_pathname)) {
5514 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5515 _("Error saving map image %d: %s."), id, mapimg_error());
5516 ret = FALSE;
5518 } else {
5519 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5520 _("Bad argument for 'mapimg create': '%s'."), token[1]);
5521 ret = FALSE;
5523 break;
5526 cleanup:
5528 free_tokens(token, ntokens);
5530 return ret;
5533 /*****************************************************************************
5534 Execute a command in the context of the AI of the player.
5535 *****************************************************************************/
5536 static bool aicmd_command(struct connection *caller, char *arg, bool check)
5538 enum m_pre_result match_result;
5539 struct player *pplayer;
5540 char *tokens[1], *cmd = NULL;
5541 int ntokens;
5542 bool ret = FALSE;
5544 ntokens = get_tokens(arg, tokens, 1, TOKEN_DELIMITERS);
5546 if (ntokens < 1) {
5547 cmd_reply(CMD_AICMD, caller, C_FAIL,
5548 _("No player given for aicmd."));
5549 goto cleanup;
5552 pplayer = player_by_name_prefix(tokens[0], &match_result);
5554 if (NULL == pplayer) {
5555 cmd_reply_no_such_player(CMD_AICMD, caller, tokens[0], match_result);
5556 goto cleanup;
5559 /* We have a player - extract the command. */
5560 cmd = arg + strlen(tokens[0]);
5561 cmd = skip_leading_spaces(cmd);
5563 if (strlen(cmd) == 0) {
5564 cmd_reply(CMD_AICMD, caller, C_FAIL,
5565 _("No command for the AI console defined."));
5566 goto cleanup;
5569 if (check) {
5570 ret = TRUE;
5571 goto cleanup;
5574 /* This check is needed to return a message if the function is not defined
5575 * for the AI of the player. */
5576 if (pplayer && pplayer->ai) {
5577 if (pplayer->ai->funcs.player_console) {
5578 cmd_reply(CMD_AICMD, caller, C_OK,
5579 _("AI console for player %s. Command: '%s'."),
5580 player_name(pplayer), cmd);
5581 CALL_PLR_AI_FUNC(player_console, pplayer, pplayer, cmd);
5582 ret = TRUE;
5583 } else {
5584 cmd_reply(CMD_AICMD, caller, C_FAIL,
5585 _("No AI console defined for the AI '%s' of player %s."),
5586 ai_name(pplayer->ai), player_name(pplayer));
5588 } else {
5589 cmd_reply(CMD_AICMD, caller, C_FAIL, _("No AI defined for player %s."),
5590 player_name(pplayer));
5593 cleanup:
5594 free_tokens(tokens, ntokens);
5595 return ret;
5598 /* Define the possible arguments to the fcdb command */
5599 #define SPECENUM_NAME fcdb_args
5600 #define SPECENUM_VALUE0 FCDB_RELOAD
5601 #define SPECENUM_VALUE0NAME "reload"
5602 #define SPECENUM_VALUE1 FCDB_LUA
5603 #define SPECENUM_VALUE1NAME "lua"
5604 #define SPECENUM_COUNT FCDB_COUNT
5605 #include "specenum_gen.h"
5607 /**************************************************************************
5608 Returns possible parameters for the fcdb command.
5609 **************************************************************************/
5610 static const char *fcdb_accessor(int i)
5612 i = CLIP(0, i, fcdb_args_max());
5613 return fcdb_args_name((enum fcdb_args) i);
5616 /**************************************************************************
5617 Handle the freeciv database script module.
5618 **************************************************************************/
5619 static bool fcdb_command(struct connection *caller, char *arg, bool check)
5621 enum m_pre_result result;
5622 int ind, ntokens;
5623 char *token[1];
5624 bool ret = TRUE;
5625 bool usage = FALSE;
5627 #ifndef HAVE_FCDB
5628 cmd_reply(CMD_FCDB, caller, C_FAIL,
5629 _("Freeciv database script deactivated at compile time."));
5630 return FALSE;
5631 #endif
5633 ntokens = get_tokens(arg, token, 1, TOKEN_DELIMITERS);
5635 if (ntokens > 0) {
5636 /* match the argument */
5637 result = match_prefix(fcdb_accessor, FCDB_COUNT, 0,
5638 fc_strncasecmp, NULL, token[0], &ind);
5640 switch (result) {
5641 case M_PRE_EXACT:
5642 case M_PRE_ONLY:
5643 /* we have a match */
5644 break;
5645 case M_PRE_AMBIGUOUS:
5646 cmd_reply(CMD_FCDB, caller, C_FAIL,
5647 _("Ambiguous fcdb command."));
5648 ret = FALSE;
5649 goto cleanup;
5650 break;
5651 case M_PRE_EMPTY:
5652 case M_PRE_LONG:
5653 case M_PRE_FAIL:
5654 case M_PRE_LAST:
5655 usage = TRUE;
5656 break;
5658 } else {
5659 usage = TRUE;
5662 if (usage) {
5663 char buf[256] = "";
5664 enum fcdb_args valid_args;
5666 for (valid_args = fcdb_args_begin();
5667 valid_args != fcdb_args_end();
5668 valid_args = fcdb_args_next(valid_args)) {
5669 cat_snprintf(buf, sizeof(buf), "'%s'",
5670 fcdb_args_name(valid_args));
5671 if (valid_args != fcdb_args_max()) {
5672 cat_snprintf(buf, sizeof(buf), ", ");
5676 cmd_reply(CMD_FCDB, caller, C_FAIL,
5677 _("The valid arguments are: %s."), buf);
5678 ret = FALSE;
5679 goto cleanup;
5682 if (check) {
5683 ret = TRUE;
5684 goto cleanup;
5687 switch (ind) {
5688 case FCDB_RELOAD:
5689 /* Reload database lua script. */
5690 script_fcdb_free();
5691 script_fcdb_init(NULL);
5692 break;
5694 case FCDB_LUA:
5695 /* Skip whitespaces. */
5696 arg = skip_leading_spaces(arg);
5697 /* Skip the base argument 'lua'. */
5698 arg += 3;
5699 /* Now execute the scriptlet. */
5700 ret = script_fcdb_do_string(caller, arg);
5701 break;
5704 cleanup:
5706 free_tokens(token, ntokens);
5708 return ret;
5711 /**************************************************************************
5712 Send start command related message
5713 **************************************************************************/
5714 static void start_cmd_reply(struct connection *caller, bool notify, char *msg)
5716 cmd_reply(CMD_START_GAME, caller, C_FAIL, "%s", msg);
5717 if (notify) {
5718 notify_conn(NULL, NULL, E_SETTING, ftc_server, "%s", msg);
5722 /**************************************************************************
5723 Handle start command. Notify all players about errors if notify set.
5724 **************************************************************************/
5725 bool start_command(struct connection *caller, bool check, bool notify)
5727 int human_players;
5729 switch (server_state()) {
5730 case S_S_INITIAL:
5731 /* Sanity check scenario */
5732 if (game.info.is_new_game && !check) {
5733 if (0 < map_startpos_count()
5734 && game.server.max_players > map_startpos_count()) {
5735 /* If we load a pre-generated map (i.e., a scenario) it is possible
5736 * to increase the number of players beyond the number supported by
5737 * the scenario. The solution is a hack: cut the extra players
5738 * when the game starts. */
5739 log_verbose("Reduced maxplayers from %d to %d to fit "
5740 "to the number of start positions.",
5741 game.server.max_players, map_startpos_count());
5742 game.server.max_players = map_startpos_count();
5745 if (normal_player_count() > game.server.max_players) {
5746 int i;
5747 struct player *pplayer;
5749 for (i = player_slot_count() - 1; i >= 0; i--) {
5750 pplayer = player_by_number(i);
5751 if (pplayer) {
5752 server_remove_player(pplayer);
5754 if (normal_player_count() <= game.server.max_players) {
5755 break;
5759 log_verbose("Had to cut down the number of players to the "
5760 "number of map start positions, there must be "
5761 "something wrong with the savegame or you "
5762 "adjusted the maxplayers value.");
5766 human_players = 0;
5767 players_iterate(plr) {
5768 if (is_human(plr)) {
5769 human_players++;
5771 } players_iterate_end;
5773 /* check min_players.
5774 * Allow continuing of savegames where some of the original
5775 * players have died */
5776 if (game.info.is_new_game
5777 && human_players < game.server.min_players) {
5778 char buf[512] = "";
5780 fc_snprintf(buf, sizeof(buf),
5781 _("Not enough human players ('minplayers' server setting has value %d); game will not start."),
5782 game.server.min_players);
5783 start_cmd_reply(caller, notify, buf);
5784 return FALSE;
5785 } else if (player_count() < 1) {
5786 /* At least one player required */
5787 start_cmd_reply(caller, notify,
5788 _("No players; game will not start."));
5789 return FALSE;
5790 } else if (normal_player_count() > server.playable_nations) {
5791 if (nation_set_count() > 1) {
5792 start_cmd_reply(caller, notify,
5793 _("Not enough nations in the current nation set "
5794 "for all players; game will not start. "
5795 "(See 'nationset' setting.)"));
5796 } else {
5797 start_cmd_reply(caller, notify,
5798 _("Not enough nations for all players; game will "
5799 "not start."));
5801 return FALSE;
5802 } else if (strlen(game.server.start_units) == 0 && !game.server.start_city) {
5803 start_cmd_reply(caller, notify,
5804 _("Neither 'startcity' nor 'startunits' setting gives "
5805 "players anything to start game with; game will "
5806 "not start."));
5807 return FALSE;
5808 } else if (check) {
5809 return TRUE;
5810 } else if (!caller) {
5811 if (notify) {
5812 /* Called from handle_player_ready()
5813 * Last player just toggled ready-status. */
5814 notify_conn(NULL, NULL, E_SETTING, ftc_game_start,
5815 _("All players are ready; starting game."));
5817 start_game();
5818 return TRUE;
5819 } else if (NULL == caller->playing || caller->observer) {
5820 /* A detached or observer player can't do /start. */
5821 return TRUE;
5822 } else {
5823 /* This might trigger recursive call to start_command() if this is
5824 * last player who gets ready. In that case caller is NULL. */
5825 handle_player_ready(caller->playing, player_number(caller->playing), TRUE);
5826 return TRUE;
5828 case S_S_OVER:
5829 start_cmd_reply(caller, notify,
5830 /* TRANS: given when /start is invoked during gameover. */
5831 _("Cannot start the game: the game is waiting for all clients "
5832 "to disconnect."));
5833 return FALSE;
5834 case S_S_RUNNING:
5835 start_cmd_reply(caller, notify,
5836 /* TRANS: given when /start is invoked while the game
5837 * is running. */
5838 _("Cannot start the game: it is already running."));
5839 return FALSE;
5841 log_error("Unknown server state variant: %d.", server_state());
5842 return FALSE;
5845 /**************************************************************************
5846 Handle cut command
5847 **************************************************************************/
5848 static bool cut_client_connection(struct connection *caller, char *name,
5849 bool check)
5851 enum m_pre_result match_result;
5852 struct connection *ptarget;
5854 ptarget = conn_by_user_prefix(name, &match_result);
5856 if (!ptarget) {
5857 cmd_reply_no_such_conn(CMD_CUT, caller, name, match_result);
5858 return FALSE;
5859 } else if (check) {
5860 return TRUE;
5863 if (conn_controls_player(ptarget)) {
5864 /* If we cut the connection, unassign the login name.*/
5865 sz_strlcpy(ptarget->playing->username, _(ANON_USER_NAME));
5866 ptarget->playing->unassigned_user = TRUE;
5869 cmd_reply(CMD_CUT, caller, C_DISCONNECTED,
5870 _("Cutting connection %s."), ptarget->username);
5871 connection_close_server(ptarget, _("connection cut"));
5873 return TRUE;
5877 /****************************************************************************
5878 Utility for 'kick_hash' tables.
5879 ****************************************************************************/
5880 static time_t *time_duplicate(const time_t *t)
5882 time_t *d = fc_malloc(sizeof(*d));
5883 *d = *t;
5884 return d;
5887 /****************************************************************************
5888 Returns FALSE if the connection isn't kicked and can connect the server
5889 normally.
5890 ****************************************************************************/
5891 bool conn_is_kicked(struct connection *pconn, int *time_remaining)
5893 time_t time_of_addr_kick, time_of_user_kick;
5894 time_t now, time_of_kick = 0;
5896 if (NULL != time_remaining) {
5897 *time_remaining = 0;
5900 fc_assert_ret_val(NULL != kick_table_by_addr, FALSE);
5901 fc_assert_ret_val(NULL != kick_table_by_user, FALSE);
5902 fc_assert_ret_val(NULL != pconn, FALSE);
5904 if (kick_hash_lookup(kick_table_by_addr, pconn->server.ipaddr,
5905 &time_of_addr_kick)) {
5906 time_of_kick = time_of_addr_kick;
5908 if (kick_hash_lookup(kick_table_by_user, pconn->username,
5909 &time_of_user_kick)
5910 && time_of_user_kick > time_of_kick) {
5911 time_of_kick = time_of_user_kick;
5914 if (0 == time_of_kick) {
5915 return FALSE; /* Not found. */
5918 now = time(NULL);
5919 if (now - time_of_kick > game.server.kick_time) {
5920 /* Kick timeout expired. */
5921 if (0 != time_of_addr_kick) {
5922 kick_hash_remove(kick_table_by_addr, pconn->server.ipaddr);
5924 if (0 != time_of_user_kick) {
5925 kick_hash_remove(kick_table_by_user, pconn->username);
5927 return FALSE;
5930 if (NULL != time_remaining) {
5931 *time_remaining = game.server.kick_time - (now - time_of_kick);
5933 return TRUE;
5936 /****************************************************************************
5937 Kick command handler.
5938 ****************************************************************************/
5939 static bool kick_command(struct connection *caller, char *name, bool check)
5941 char ipaddr[FC_MEMBER_SIZEOF(struct connection, server.ipaddr)];
5942 struct connection *pconn;
5943 enum m_pre_result match_result;
5944 time_t now;
5946 remove_leading_trailing_spaces(name);
5947 pconn = conn_by_user_prefix(name, &match_result);
5948 if (NULL == pconn) {
5949 cmd_reply_no_such_conn(CMD_KICK, caller, name, match_result);
5950 return FALSE;
5953 if (NULL != caller && ALLOW_ADMIN > conn_get_access(caller)) {
5954 const int MIN_UNIQUE_CONNS = 3;
5955 const char *unique_ipaddr[MIN_UNIQUE_CONNS];
5956 int i, num_unique_connections = 0;
5958 if (pconn == caller) {
5959 cmd_reply(CMD_KICK, caller, C_FAIL, _("You may not kick yourself."));
5960 return FALSE;
5963 conn_list_iterate(game.est_connections, aconn) {
5964 for (i = 0; i < num_unique_connections; i++) {
5965 if (0 == strcmp(unique_ipaddr[i], aconn->server.ipaddr)) {
5966 /* Already listed. */
5967 break;
5970 if (i >= num_unique_connections) {
5971 num_unique_connections++;
5972 if (MIN_UNIQUE_CONNS <= num_unique_connections) {
5973 /* We have enought already. */
5974 break;
5976 unique_ipaddr[num_unique_connections - 1] = aconn->server.ipaddr;
5978 } conn_list_iterate_end;
5980 if (MIN_UNIQUE_CONNS > num_unique_connections) {
5981 cmd_reply(CMD_KICK, caller, C_FAIL,
5982 _("There must be at least %d unique connections to the "
5983 "server for this command to be valid."), MIN_UNIQUE_CONNS);
5984 return FALSE;
5988 if (check) {
5989 return TRUE;
5992 sz_strlcpy(ipaddr, pconn->server.ipaddr);
5993 now = time(NULL);
5994 kick_hash_replace(kick_table_by_addr, ipaddr, now);
5996 conn_list_iterate(game.all_connections, aconn) {
5997 if (0 != strcmp(ipaddr, aconn->server.ipaddr)) {
5998 continue;
6001 if (conn_controls_player(aconn)) {
6002 /* Unassign the username. */
6003 sz_strlcpy(aconn->playing->username, _(ANON_USER_NAME));
6004 aconn->playing->unassigned_user = TRUE;
6007 kick_hash_replace(kick_table_by_user, aconn->username, now);
6009 connection_close_server(aconn, _("kicked"));
6010 } conn_list_iterate_end;
6012 return TRUE;
6016 /**************************************************************************
6017 Show caller introductory help about the server. help_cmd is the command
6018 the player used.
6019 **************************************************************************/
6020 static void show_help_intro(struct connection *caller,
6021 enum command_id help_cmd)
6023 /* This is formated like extra_help entries for settings and commands: */
6024 char *help = fc_strdup(
6025 _("Welcome - this is the introductory help text for the Freeciv "
6026 "server.\n"
6027 "\n"
6028 "Two important server concepts are Commands and Options. Commands, "
6029 "such as 'help', are used to interact with the server. Some commands "
6030 "take one or more arguments, separated by spaces. In many cases "
6031 "commands and command arguments may be abbreviated. Options are "
6032 "settings which control the server as it is running.\n"
6033 "\n"
6034 "To find out how to get more information about commands and options, "
6035 "use 'help help'.\n"
6036 "\n"
6037 "For the impatient, the main commands to get going are:\n"
6038 " show - to see current options\n"
6039 " set - to set options\n"
6040 " start - to start the game once players have connected\n"
6041 " save - to save the current game\n"
6042 " quit - to exit"));
6044 fc_break_lines(help, LINE_BREAK);
6045 cmd_reply(help_cmd, caller, C_COMMENT, "%s", help);
6046 FC_FREE(help);
6049 /**************************************************************************
6050 Show the caller detailed help for the single COMMAND given by id.
6051 help_cmd is the command the player used.
6052 **************************************************************************/
6053 static void show_help_command(struct connection *caller,
6054 enum command_id help_cmd,
6055 enum command_id id)
6057 const struct command *cmd = command_by_number(id);
6059 if (command_short_help(cmd)) {
6060 cmd_reply(help_cmd, caller, C_COMMENT,
6061 /* TRANS: <untranslated name> - translated short help */
6062 _("Command: %s - %s"),
6063 command_name(cmd),
6064 command_short_help(cmd));
6065 } else {
6066 cmd_reply(help_cmd, caller, C_COMMENT,
6067 /* TRANS: <untranslated name> */
6068 _("Command: %s"),
6069 command_name(cmd));
6071 if (command_synopsis(cmd)) {
6072 /* line up the synopsis lines: */
6073 const char *syn = _("Synopsis: ");
6074 size_t synlen = strlen(syn);
6075 char prefix[40];
6077 fc_snprintf(prefix, sizeof(prefix), "%*s", (int) synlen, " ");
6078 cmd_reply_prefix(help_cmd, caller, C_COMMENT, prefix,
6079 "%s%s", syn, command_synopsis(cmd));
6081 cmd_reply(help_cmd, caller, C_COMMENT,
6082 _("Level: %s"), cmdlevel_name(command_level(cmd)));
6084 char *help = command_extra_help(cmd);
6086 if (help) {
6087 fc_break_lines(help, LINE_BREAK);
6088 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
6089 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
6090 FC_FREE(help);
6095 /**************************************************************************
6096 Show the caller list of COMMANDS.
6097 help_cmd is the command the player used.
6098 **************************************************************************/
6099 static void show_help_command_list(struct connection *caller,
6100 enum command_id help_cmd)
6102 enum command_id i;
6104 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6105 cmd_reply(help_cmd, caller, C_COMMENT,
6106 _("The following server commands are available:"));
6107 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6108 if (!caller && con_get_style()) {
6109 for (i = 0; i < CMD_NUM; i++) {
6110 cmd_reply(help_cmd, caller, C_COMMENT, "%s", command_name_by_number(i));
6112 } else {
6113 char buf[MAX_LEN_CONSOLE_LINE];
6114 int j;
6116 buf[0] = '\0';
6117 for (i=0, j=0; i<CMD_NUM; i++) {
6118 if (may_use(caller, i)) {
6119 cat_snprintf(buf, sizeof(buf), "%-19s", command_name_by_number(i));
6120 if ((++j % 4) == 0) {
6121 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
6122 buf[0] = '\0';
6126 if (buf[0] != '\0') {
6127 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
6130 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6133 /**************************************************************************
6134 Send a reply to the caller listing the matched names from an ambiguous
6135 prefix.
6136 **************************************************************************/
6137 static void cmd_reply_matches(enum command_id cmd,
6138 struct connection *caller,
6139 m_pre_accessor_fn_t accessor_fn,
6140 int *matches, int num_matches)
6142 char buf[MAX_LEN_MSG];
6143 const char *src, *end;
6144 char *dest;
6145 int i;
6147 if (accessor_fn == NULL || matches == NULL || num_matches < 1) {
6148 return;
6151 dest = buf;
6152 end = buf + sizeof(buf) - 1;
6154 for (i = 0; i < num_matches && dest < end; i++) {
6155 src = accessor_fn(matches[i]);
6156 if (!src) {
6157 continue;
6159 if (dest != buf) {
6160 *dest++ = ' ';
6162 while (*src != '\0' && dest < end) {
6163 *dest++ = *src++;
6166 *dest = '\0';
6168 cmd_reply(cmd, caller, C_COMMENT, _("Possible matches: %s"), buf);
6171 /**************************************************************************
6172 Additional 'help' arguments
6173 **************************************************************************/
6174 #define SPECENUM_NAME help_general_args
6175 #define SPECENUM_VALUE0 HELP_GENERAL_COMMANDS
6176 #define SPECENUM_VALUE0NAME "commands"
6177 #define SPECENUM_VALUE1 HELP_GENERAL_OPTIONS
6178 #define SPECENUM_VALUE1NAME "options"
6179 #define SPECENUM_COUNT HELP_GENERAL_COUNT
6180 #include "specenum_gen.h"
6182 /**************************************************************************
6183 Unified indices for help arguments:
6184 CMD_NUM - Server commands
6185 HELP_GENERAL_NUM - General help arguments, above
6186 settings_number() - Server options
6187 **************************************************************************/
6188 #define HELP_ARG_NUM (CMD_NUM + HELP_GENERAL_COUNT + settings_number())
6190 /**************************************************************************
6191 Convert unified helparg index to string; see above.
6192 **************************************************************************/
6193 static const char *helparg_accessor(int i)
6195 if (i < CMD_NUM) {
6196 return command_name_by_number(i);
6199 i -= CMD_NUM;
6200 if (i < HELP_GENERAL_COUNT) {
6201 return help_general_args_name((enum help_general_args) i);
6204 i -= HELP_GENERAL_COUNT;
6205 return optname_accessor(i);
6208 /**************************************************************************
6209 Handle help command
6210 **************************************************************************/
6211 static bool show_help(struct connection *caller, char *arg)
6213 int matches[64], num_matches = 0;
6214 enum m_pre_result match_result;
6215 int ind;
6217 fc_assert_ret_val(!may_use_nothing(caller), FALSE);
6218 /* no commands means no help, either */
6220 match_result = match_prefix_full(helparg_accessor, HELP_ARG_NUM, 0,
6221 fc_strncasecmp, NULL, arg, &ind, matches,
6222 ARRAY_SIZE(matches), &num_matches);
6224 if (match_result == M_PRE_EMPTY) {
6225 show_help_intro(caller, CMD_HELP);
6226 return FALSE;
6228 if (match_result == M_PRE_AMBIGUOUS) {
6229 cmd_reply(CMD_HELP, caller, C_FAIL,
6230 _("Help argument '%s' is ambiguous."), arg);
6231 cmd_reply_matches(CMD_HELP, caller, helparg_accessor,
6232 matches, num_matches);
6233 return FALSE;
6235 if (match_result == M_PRE_FAIL) {
6236 cmd_reply(CMD_HELP, caller, C_FAIL,
6237 _("No match for help argument '%s'."), arg);
6238 return FALSE;
6241 /* other cases should be above */
6242 fc_assert_ret_val(match_result < M_PRE_AMBIGUOUS, FALSE);
6244 if (ind < CMD_NUM) {
6245 show_help_command(caller, CMD_HELP, ind);
6246 return TRUE;
6248 ind -= CMD_NUM;
6250 if (ind == HELP_GENERAL_OPTIONS) {
6251 show_help_option_list(caller, CMD_HELP);
6252 return TRUE;
6254 if (ind == HELP_GENERAL_COMMANDS) {
6255 show_help_command_list(caller, CMD_HELP);
6256 return TRUE;
6258 ind -= HELP_GENERAL_COUNT;
6260 if (ind < settings_number()) {
6261 show_help_option(caller, CMD_HELP, ind);
6262 return TRUE;
6265 /* should have finished by now */
6266 log_error("Bug in show_help!");
6267 return FALSE;
6270 /****************************************************************************
6271 List connections; initially mainly for debugging
6272 ****************************************************************************/
6273 static void show_connections(struct connection *caller)
6275 char buf[MAX_LEN_CONSOLE_LINE];
6277 cmd_reply(CMD_LIST, caller, C_COMMENT,
6278 _("List of connections to server:"));
6279 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6281 if (conn_list_size(game.all_connections) == 0) {
6282 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no connections>"));
6283 } else {
6284 conn_list_iterate(game.all_connections, pconn) {
6285 sz_strlcpy(buf, conn_description(pconn));
6286 if (pconn->established) {
6287 cat_snprintf(buf, sizeof(buf), " command access level %s",
6288 cmdlevel_name(pconn->access_level));
6290 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6291 } conn_list_iterate_end;
6293 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6296 /*****************************************************************************
6297 List all delegations of the current game.
6298 *****************************************************************************/
6299 static void show_delegations(struct connection *caller)
6301 bool empty = TRUE;
6303 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of all delegations:"));
6304 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6306 players_iterate(pplayer) {
6307 const char *delegate_to = player_delegation_get(pplayer);
6308 if (delegate_to != NULL) {
6309 const char *owner =
6310 player_delegation_active(pplayer) ? pplayer->server.orig_username
6311 : pplayer->username;
6312 fc_assert(owner);
6313 cmd_reply(CMD_LIST, caller, C_COMMENT,
6314 /* TRANS: last %s is either " (active)" or empty string */
6315 _("%s delegates control over player '%s' to user %s%s."),
6316 owner, player_name(pplayer), delegate_to,
6317 player_delegation_active(pplayer) ? _(" (active)") : "");
6318 empty = FALSE;
6320 } players_iterate_end;
6322 if (empty) {
6323 cmd_reply(CMD_LIST, caller, C_COMMENT, _("No delegations defined."));
6326 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6329 /****************************************************************************
6330 Show the ignore list of the
6331 ****************************************************************************/
6332 static bool show_ignore(struct connection *caller)
6334 char buf[128];
6335 int n = 1;
6337 if (NULL == caller) {
6338 cmd_reply(CMD_IGNORE, caller, C_FAIL,
6339 _("That would be rather silly, since you are not a player."));
6340 return FALSE;
6343 if (0 == conn_pattern_list_size(caller->server.ignore_list)) {
6344 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list is empty."));
6345 return TRUE;
6348 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list:"));
6349 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6350 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
6351 conn_pattern_to_string(ppattern, buf, sizeof(buf));
6352 cmd_reply(CMD_LIST, caller, C_COMMENT, "%d: %s", n++, buf);
6353 } conn_pattern_list_iterate_end;
6354 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6356 return TRUE;
6359 /****************************************************************************
6360 Show the list of the players of the game.
6361 ****************************************************************************/
6362 void show_players(struct connection *caller)
6364 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of players:"));
6365 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6367 if (player_count() == 0) {
6368 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6369 } else {
6370 players_iterate(pplayer) {
6371 char buf[MAX_LEN_CONSOLE_LINE];
6372 int n;
6374 /* Low access level callers don't get to see barbarians in list: */
6375 if (is_barbarian(pplayer) && caller
6376 && (caller->access_level < ALLOW_CTRL)) {
6377 continue;
6380 /* The output for each player looks like:
6382 * <Player name> [color]: Team[, Nation][, Username][, Status]
6383 * AI/Barbarian/Human[, AI type, skill level][, Connections]
6384 * [Details for each connection]
6387 /* '<Player name> [color]: [Nation][, Username][, Status]' */
6388 buf[0] = '\0';
6389 cat_snprintf(buf, sizeof(buf), "%s [%s]: %s", player_name(pplayer),
6390 player_color_ftstr(pplayer),
6391 team_name_translation(pplayer->team));
6392 if (!game.info.is_new_game) {
6393 cat_snprintf(buf, sizeof(buf), ", %s",
6394 nation_adjective_for_player(pplayer));
6396 if (strlen(pplayer->username) > 0
6397 && strcmp(pplayer->username, "nouser") != 0) {
6398 cat_snprintf(buf, sizeof(buf), _(", user %s"), pplayer->username);
6400 if (S_S_INITIAL == server_state() && pplayer->is_connected) {
6401 if (pplayer->is_ready) {
6402 sz_strlcat(buf, _(", ready"));
6403 } else {
6404 /* Emphasizes this */
6405 n = strlen(buf);
6406 featured_text_apply_tag(_(", not ready"),
6407 buf + n, sizeof(buf) - n,
6408 TTT_COLOR, 1, FT_OFFSET_UNSET,
6409 ftc_changed);
6411 } else if (!pplayer->is_alive) {
6412 sz_strlcat(buf, _(", Dead"));
6414 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6416 /* ' AI/Barbarian/Human[, skill level][, Connections]' */
6417 buf[0] = '\0';
6418 if (is_barbarian(pplayer)) {
6419 sz_strlcat(buf, _("Barbarian"));
6420 } else if (is_ai(pplayer)) {
6421 sz_strlcat(buf, _("AI"));
6422 } else {
6423 sz_strlcat(buf, _("Human"));
6425 if (is_ai(pplayer)) {
6426 cat_snprintf(buf, sizeof(buf), _(", %s"), ai_name(pplayer->ai));
6427 cat_snprintf(buf, sizeof(buf), _(", difficulty level %s"),
6428 ai_level_translated_name(pplayer->ai_common.skill_level));
6430 n = conn_list_size(pplayer->connections);
6431 if (n > 0) {
6432 cat_snprintf(buf, sizeof(buf),
6433 PL_(", %d connection:", ", %d connections:", n), n);
6435 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6437 /* ' [Details for each connection]' */
6438 conn_list_iterate(pplayer->connections, pconn) {
6439 fc_snprintf(buf, sizeof(buf),
6440 _("%s from %s (command access level %s), "
6441 "bufsize=%dkb"), pconn->username, pconn->addr,
6442 cmdlevel_name(pconn->access_level),
6443 (pconn->send_buffer->nsize >> 10));
6444 if (pconn->observer) {
6445 sz_strlcat(buf, _(" (observer mode)"));
6447 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6448 } conn_list_iterate_end;
6449 } players_iterate_end;
6451 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6454 /****************************************************************************
6455 List scenarios. We look both in the DATA_PATH and DATA_PATH/scenario
6456 ****************************************************************************/
6457 static void show_scenarios(struct connection *caller)
6459 char buf[MAX_LEN_CONSOLE_LINE];
6460 struct fileinfo_list *files;
6462 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of scenarios available:"));
6463 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6465 files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE);
6467 fileinfo_list_iterate(files, pfile) {
6468 fc_snprintf(buf, sizeof(buf), "%s", pfile->name);
6469 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6470 } fileinfo_list_iterate_end;
6471 fileinfo_list_destroy(files);
6473 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6476 /****************************************************************************
6477 List nation sets in the current ruleset.
6478 ****************************************************************************/
6479 static void show_nationsets(struct connection *caller)
6481 cmd_reply(CMD_LIST, caller, C_COMMENT,
6482 /* TRANS: don't translate text between '' */
6483 _("List of nation sets available for 'nationset' option:"));
6484 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6486 nation_sets_iterate(pset) {
6487 const char *description = nation_set_description(pset);
6488 int num_nations = 0;
6489 nations_iterate(pnation) {
6490 if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) {
6491 num_nations++;
6493 } nations_iterate_end;
6494 cmd_reply(CMD_LIST, caller, C_COMMENT,
6495 /* TRANS: nation set description; %d refers to number of playable
6496 * nations in set */
6497 PL_(" %-10s %s (%d playable)",
6498 " %-10s %s (%d playable)", num_nations),
6499 nation_set_rule_name(pset), nation_set_name_translation(pset),
6500 num_nations);
6501 if (strlen(description) > 0) {
6502 static const char prefix[] = " ";
6503 char *translated = fc_strdup(_(description));
6504 fc_break_lines(translated, LINE_BREAK);
6505 cmd_reply_prefix(CMD_LIST, caller, C_COMMENT, prefix, "%s%s",
6506 prefix, translated);
6508 } nation_sets_iterate_end;
6510 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6513 /****************************************************************************
6514 Show a list of teams on the command line.
6515 ****************************************************************************/
6516 static void show_teams(struct connection *caller)
6518 /* Currently this just lists all teams (typically 32 of them) with their
6519 * names and # of players on the team. This could probably be improved. */
6520 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of teams:"));
6521 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6523 teams_iterate(pteam) {
6524 const struct player_list *members = team_members(pteam);
6526 /* PL_() is needed here because some languages may differentiate
6527 * between 2 and 3 (although English does not). */
6528 cmd_reply(CMD_LIST, caller, C_COMMENT,
6529 /* TRANS: There will always be at least 2 players here. */
6530 PL_("%2d : '%s' : %d player :",
6531 "%2d : '%s' : %d players :",
6532 player_list_size(members)),
6533 team_index(pteam), team_name_translation(pteam),
6534 player_list_size(members));
6535 player_list_iterate(members, pplayer) {
6536 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", player_name(pplayer));
6537 } player_list_iterate_end;
6538 } teams_iterate_end;
6540 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6543 /****************************************************************************
6544 Show a list of all map image definitions on the command line.
6545 ****************************************************************************/
6546 static void show_mapimg(struct connection *caller, enum command_id cmd)
6548 int id;
6550 if (mapimg_count() == 0) {
6551 cmd_reply(cmd, caller, C_OK, _("No map image definitions."));
6552 } else {
6553 cmd_reply(cmd, caller, C_COMMENT, _("List of map image definitions:"));
6554 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6555 for (id = 0; id < mapimg_count(); id++) {
6556 char str[MAX_LEN_MAPDEF] = "";
6557 mapimg_show(id, str, sizeof(str), FALSE);
6558 cmd_reply(cmd, caller, C_COMMENT, _("[%2d] %s"), id, str);
6560 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6564 /****************************************************************************
6565 Show a list of all players with the assigned color.
6566 ****************************************************************************/
6567 static void show_colors(struct connection *caller)
6569 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of player colors:"));
6570 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6571 if (player_count() == 0) {
6572 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6573 } else {
6574 players_iterate(pplayer) {
6575 cmd_reply(CMD_LIST, caller, C_COMMENT, _("%s (user %s): [%s]"),
6576 player_name(pplayer), pplayer->username,
6577 player_color_ftstr(pplayer));
6578 } players_iterate_end;
6580 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6583 /****************************************************************************
6584 '/list' arguments
6585 **************************************************************************/
6586 #define SPECENUM_NAME list_args
6587 #define SPECENUM_VALUE0 LIST_COLORS
6588 #define SPECENUM_VALUE0NAME "colors"
6589 #define SPECENUM_VALUE1 LIST_CONNECTIONS
6590 #define SPECENUM_VALUE1NAME "connections"
6591 #define SPECENUM_VALUE2 LIST_DELEGATIONS
6592 #define SPECENUM_VALUE2NAME "delegations"
6593 #define SPECENUM_VALUE3 LIST_IGNORE
6594 #define SPECENUM_VALUE3NAME "ignored users"
6595 #define SPECENUM_VALUE4 LIST_MAPIMG
6596 #define SPECENUM_VALUE4NAME "map image definitions"
6597 #define SPECENUM_VALUE5 LIST_PLAYERS
6598 #define SPECENUM_VALUE5NAME "players"
6599 #define SPECENUM_VALUE6 LIST_SCENARIOS
6600 #define SPECENUM_VALUE6NAME "scenarios"
6601 #define SPECENUM_VALUE7 LIST_NATIONSETS
6602 #define SPECENUM_VALUE7NAME "nationsets"
6603 #define SPECENUM_VALUE8 LIST_TEAMS
6604 #define SPECENUM_VALUE8NAME "teams"
6605 #define SPECENUM_VALUE9 LIST_VOTES
6606 #define SPECENUM_VALUE9NAME "votes"
6607 #include "specenum_gen.h"
6609 /**************************************************************************
6610 Returns possible parameters for the list command.
6611 **************************************************************************/
6612 static const char *list_accessor(int i)
6614 i = CLIP(0, i, list_args_max());
6615 return list_args_name((enum list_args) i);
6618 /**************************************************************************
6619 Show list of players or connections, or connection statistics.
6620 **************************************************************************/
6621 static bool show_list(struct connection *caller, char *arg)
6623 enum m_pre_result match_result;
6624 int ind_int;
6625 enum list_args ind;
6627 remove_leading_trailing_spaces(arg);
6628 match_result = match_prefix(list_accessor, list_args_max() + 1, 0,
6629 fc_strncasecmp, NULL, arg, &ind_int);
6630 ind = ind_int;
6632 if (match_result > M_PRE_EMPTY) {
6633 cmd_reply(CMD_LIST, caller, C_SYNTAX,
6634 _("Bad list argument: '%s'. Try '%shelp list'."),
6635 arg, (caller?"/":""));
6636 return FALSE;
6639 if (match_result == M_PRE_EMPTY) {
6640 ind = LIST_PLAYERS;
6643 switch(ind) {
6644 case LIST_COLORS:
6645 show_colors(caller);
6646 return TRUE;
6647 case LIST_CONNECTIONS:
6648 show_connections(caller);
6649 return TRUE;
6650 case LIST_DELEGATIONS:
6651 show_delegations(caller);
6652 return TRUE;
6653 case LIST_IGNORE:
6654 return show_ignore(caller);
6655 case LIST_MAPIMG:
6656 show_mapimg(caller, CMD_LIST);
6657 return TRUE;
6658 case LIST_PLAYERS:
6659 show_players(caller);
6660 return TRUE;
6661 case LIST_SCENARIOS:
6662 show_scenarios(caller);
6663 return TRUE;
6664 case LIST_NATIONSETS:
6665 show_nationsets(caller);
6666 return TRUE;
6667 case LIST_TEAMS:
6668 show_teams(caller);
6669 return TRUE;
6670 case LIST_VOTES:
6671 show_votes(caller);
6672 return TRUE;
6675 cmd_reply(CMD_LIST, caller, C_FAIL,
6676 "Internal error: ind %d in show_list", ind);
6677 log_error("Internal error: ind %d in show_list", ind);
6678 return FALSE;
6681 #ifdef FREECIV_HAVE_LIBREADLINE
6682 /********************* RL completion functions ***************************/
6683 /* To properly complete both commands, player names, options and filenames
6684 there is one array per type of completion with the commands that
6685 the type is relevant for.
6688 /**************************************************************************
6689 A generalised generator function: text and state are "standard"
6690 parameters to a readline generator function;
6691 num is number of possible completions, or -1 if this is not known and
6692 index2str should be iterated until it returns NULL;
6693 index2str is a function which returns each possible completion string
6694 by index (it may return NULL).
6695 **************************************************************************/
6696 static char *generic_generator(const char *text, int state, int num,
6697 const char*(*index2str)(int))
6699 static int list_index, len;
6700 const char *name = ""; /* dummy non-NULL string */
6701 char *mytext = local_to_internal_string_malloc(text);
6703 /* This function takes a string (text) in the local format and must return
6704 * a string in the local format. However comparisons are done against
6705 * names that are in the internal format (UTF-8). Thus we have to convert
6706 * the text function from the local to the internal format before doing
6707 * the comparison, and convert the string we return *back* to the
6708 * local format when returning it. */
6710 /* If this is a new word to complete, initialize now. This includes
6711 saving the length of TEXT for efficiency, and initializing the index
6712 variable to 0. */
6713 if (state == 0) {
6714 list_index = 0;
6715 len = strlen(mytext);
6718 /* Return the next name which partially matches: */
6719 while ((num < 0 && name) || (list_index < num)) {
6720 name = index2str(list_index);
6721 list_index++;
6723 if (name != NULL && fc_strncasecmp(name, mytext, len) == 0) {
6724 free(mytext);
6725 return internal_to_local_string_malloc(name);
6728 free(mytext);
6730 /* If no names matched, then return NULL. */
6731 return ((char *)NULL);
6734 /**************************************************************************
6735 The valid commands at the root of the prompt.
6736 **************************************************************************/
6737 static char *command_generator(const char *text, int state)
6739 return generic_generator(text, state, CMD_NUM, command_name_by_number);
6742 /**************************************************************************
6743 The valid arguments to "set" and "explain"
6744 **************************************************************************/
6745 static char *option_generator(const char *text, int state)
6747 return generic_generator(text, state, settings_number(), optname_accessor);
6750 /**************************************************************************
6751 The valid arguments to "show"
6752 **************************************************************************/
6753 static char *olevel_generator(const char *text, int state)
6755 return generic_generator(text, state, settings_number() + OLEVELS_NUM + 1,
6756 olvlname_accessor);
6759 /**************************************************************************
6760 Accessor for values of the enum/bitwise option defined by
6761 'completion_option'.
6762 **************************************************************************/
6763 static int completion_option;
6764 static const char *option_value_accessor(int idx) {
6765 const struct setting *pset = setting_by_number(completion_option);
6766 switch (setting_type(pset)) {
6767 case SST_ENUM:
6768 return setting_enum_val(pset, idx, FALSE);
6769 break;
6770 case SST_BITWISE:
6771 return setting_bitwise_bit(pset, idx, FALSE);
6772 break;
6773 default:
6774 fc_assert_ret_val(0, NULL);
6778 /**************************************************************************
6779 The valid arguments to "set OPT", where OPT is the enumerated or
6780 bitwise option previously defined by completion_option
6781 **************************************************************************/
6782 static char *option_value_generator(const char *text, int state)
6784 return generic_generator(text, state, -1, option_value_accessor);
6787 /**************************************************************************
6788 Access player name.
6789 **************************************************************************/
6790 static const char *playername_accessor(int idx)
6792 const struct player_slot *pslot = player_slot_by_number(idx);
6794 if (!player_slot_is_used(pslot)) {
6795 return NULL;
6798 return player_name(player_slot_get_player(pslot));
6801 /**************************************************************************
6802 The valid playername arguments.
6803 **************************************************************************/
6804 static char *player_generator(const char *text, int state)
6806 return generic_generator(text, state, player_slot_count(),
6807 playername_accessor);
6810 /**************************************************************************
6811 Access connection user name, from game.all_connections.
6812 **************************************************************************/
6813 static const char *connection_name_accessor(int idx)
6815 return conn_list_get(game.all_connections, idx)->username;
6818 /**************************************************************************
6819 The valid connection user name arguments.
6820 **************************************************************************/
6821 static char *connection_generator(const char *text, int state)
6823 return generic_generator(text, state, conn_list_size(game.all_connections),
6824 connection_name_accessor);
6827 /**************************************************************************
6828 Extra accessor function since cmdlevel_name() takes enum argument, not int.
6829 **************************************************************************/
6830 static const char *cmdlevel_arg1_accessor(int idx)
6832 return cmdlevel_name(idx);
6835 /**************************************************************************
6836 The valid first argument to "cmdlevel"
6837 **************************************************************************/
6838 static char *cmdlevel_arg1_generator(const char *text, int state)
6840 return generic_generator(text, state, cmdlevel_max()+1,
6841 cmdlevel_arg1_accessor);
6844 /**************************************************************************
6845 Accessor for the second argument to "cmdlevel": "first" or "new" or
6846 a connection name.
6847 **************************************************************************/
6848 static const char *cmdlevel_arg2_accessor(int idx)
6850 return ((idx == 0) ? "first" :
6851 (idx == 1) ? "new" :
6852 connection_name_accessor(idx - 2));
6855 /**************************************************************************
6856 The valid arguments for the second argument to "cmdlevel".
6857 **************************************************************************/
6858 static char *cmdlevel_arg2_generator(const char *text, int state)
6860 return generic_generator(text, state,
6861 /* "first", "new", connection names */
6862 2 + conn_list_size(game.all_connections),
6863 cmdlevel_arg2_accessor);
6866 /**************************************************************************
6867 Accessor for the second argument to "create": ai type name
6868 **************************************************************************/
6869 static const char *aitype_accessor(int idx)
6871 return get_ai_type(idx)->name;
6874 /**************************************************************************
6875 The valid arguments for the second argument to "create".
6876 **************************************************************************/
6877 static char *aitype_generator(const char *text, int state)
6879 return generic_generator(text, state, ai_type_get_count(),
6880 aitype_accessor);
6883 /**************************************************************************
6884 The valid arguments for the argument to "reset".
6885 **************************************************************************/
6886 static char *reset_generator(const char *text, int state)
6888 return generic_generator(text, state, reset_args_max() + 1, reset_accessor);
6891 /**************************************************************************
6892 The valid arguments for the argument to "vote".
6893 **************************************************************************/
6894 static char *vote_generator(const char *text, int state)
6896 return generic_generator(text, state, -1, vote_arg_accessor);
6899 /**************************************************************************
6900 The valid arguments for the first argument to "delegate".
6901 **************************************************************************/
6902 static char *delegate_generator(const char *text, int state)
6904 return generic_generator(text, state, delegate_args_max() + 1,
6905 delegate_accessor);
6908 /**************************************************************************
6909 The valid arguments for the first argument to "mapimg".
6910 **************************************************************************/
6911 static char *mapimg_generator(const char *text, int state)
6913 return generic_generator(text, state, mapimg_args_max() + 1,
6914 mapimg_accessor);
6917 /**************************************************************************
6918 The valid arguments for the argument to "fcdb".
6919 **************************************************************************/
6920 static char *fcdb_generator(const char *text, int state)
6922 return generic_generator(text, state, FCDB_COUNT, fcdb_accessor);
6925 /**************************************************************************
6926 The valid arguments for the argument to "lua".
6927 **************************************************************************/
6928 static char *lua_generator(const char *text, int state)
6930 return generic_generator(text, state, lua_args_max() + 1, lua_accessor);
6933 /**************************************************************************
6934 The valid first arguments to "help".
6935 **************************************************************************/
6936 static char *help_generator(const char *text, int state)
6938 return generic_generator(text, state, HELP_ARG_NUM, helparg_accessor);
6941 /**************************************************************************
6942 The valid first arguments to "list".
6943 **************************************************************************/
6944 static char *list_generator(const char *text, int state)
6946 return generic_generator(text, state, list_args_max() + 1, list_accessor);
6949 /**************************************************************************
6950 Generalised version of contains_str_before_start, which searches the
6951 N'th token in rl_line_buffer (0=first).
6952 **************************************************************************/
6953 static bool contains_token_before_start(int start, int token, const char *arg,
6954 bool allow_fluff)
6956 char *str_itr = rl_line_buffer;
6957 int arg_len = strlen(arg);
6959 /* Swallow unwanted tokens and their preceding delimiters */
6960 while (token--) {
6961 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6962 str_itr++;
6964 while (str_itr < rl_line_buffer + start && fc_isalnum(*str_itr)) {
6965 str_itr++;
6969 /* Swallow any delimiters before the token we're interested in */
6970 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6971 str_itr++;
6974 if (fc_strncasecmp(str_itr, arg, arg_len) != 0) {
6975 return FALSE;
6977 str_itr += arg_len;
6979 if (fc_isalnum(*str_itr)) {
6980 /* Not a distinct word. */
6981 return FALSE;
6984 if (!allow_fluff) {
6985 for (; str_itr < rl_line_buffer + start; str_itr++) {
6986 if (fc_isalnum(*str_itr)) {
6987 return FALSE;
6992 return TRUE;
6995 /**************************************************************************
6996 Returns whether the text between the start of rl_line_buffer and the
6997 start position is of the form [non-alpha]*cmd[non-alpha]*
6998 allow_fluff changes the regexp to [non-alpha]*cmd[non-alpha].*
6999 **************************************************************************/
7000 static bool contains_str_before_start(int start, const char *cmd,
7001 bool allow_fluff)
7003 return contains_token_before_start(start, 0, cmd, allow_fluff);
7006 /**************************************************************************
7007 Return whether we are completing command name. This can be either
7008 command itself, or argument to 'help'.
7009 **************************************************************************/
7010 static bool is_command(int start)
7012 char *str_itr;
7014 if (contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE))
7015 return TRUE;
7017 /* if there is only it is also OK */
7018 str_itr = rl_line_buffer;
7019 while (str_itr - rl_line_buffer < start) {
7020 if (fc_isalnum(*str_itr)) {
7021 return FALSE;
7023 str_itr++;
7025 return TRUE;
7028 /**************************************************************************
7029 number of tokens in rl_line_buffer before start
7030 **************************************************************************/
7031 static int num_tokens(int start)
7033 int res = 0;
7034 bool alnum = FALSE;
7035 char *chptr = rl_line_buffer;
7037 while (chptr - rl_line_buffer < start) {
7038 if (fc_isalnum(*chptr)) {
7039 if (!alnum) {
7040 alnum = TRUE;
7041 res++;
7043 } else {
7044 alnum = FALSE;
7046 chptr++;
7049 return res;
7052 /**************************************************************************
7053 Commands that may be followed by a player name
7054 **************************************************************************/
7055 static const int player_cmd[] = {
7056 CMD_AITOGGLE,
7057 CMD_HANDICAPPED,
7058 CMD_NOVICE,
7059 CMD_EASY,
7060 CMD_NORMAL,
7061 CMD_HARD,
7062 CMD_CHEATING,
7063 #ifdef FREECIV_DEBUG
7064 CMD_EXPERIMENTAL,
7065 #endif
7066 CMD_REMOVE,
7067 CMD_TEAM,
7068 CMD_PLAYERCOLOR,
7072 /**************************************************************************
7073 Return whether we are completing player name argument.
7074 **************************************************************************/
7075 static bool is_player(int start)
7077 int i = 0;
7079 while (player_cmd[i] != -1) {
7080 if (contains_str_before_start(start, command_name_by_number(player_cmd[i]), FALSE)) {
7081 return TRUE;
7083 i++;
7086 return FALSE;
7089 /**************************************************************************
7090 Commands that may be followed by a connection name
7091 **************************************************************************/
7092 static const int connection_cmd[] = {
7093 CMD_CUT,
7094 CMD_KICK,
7098 /**************************************************************************
7099 Return whether we are completing connection name argument.
7100 **************************************************************************/
7101 static bool is_connection(int start)
7103 int i = 0;
7105 while (connection_cmd[i] != -1) {
7106 if (contains_str_before_start(start,
7107 command_name_by_number(connection_cmd[i]),
7108 FALSE)) {
7109 return TRUE;
7111 i++;
7114 return FALSE;
7117 /**************************************************************************
7118 Return whether we are completing cmdlevel command argument 2.
7119 **************************************************************************/
7120 static bool is_cmdlevel_arg2(int start)
7122 return (contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), TRUE)
7123 && num_tokens(start) == 2);
7126 /**************************************************************************
7127 Return whether we are completing cmdlevel command argument.
7128 **************************************************************************/
7129 static bool is_cmdlevel_arg1(int start)
7131 return contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), FALSE);
7134 /**************************************************************************
7135 Commands that may be followed by a server option name
7137 CMD_SHOW is handled by option_level_cmd, which is for both option levels
7138 and server options
7139 **************************************************************************/
7140 static const int server_option_cmd[] = {
7141 CMD_EXPLAIN,
7142 CMD_SET,
7143 CMD_DEFAULT,
7147 /**************************************************************************
7148 Returns TRUE if the readline buffer string matches a server option at
7149 the given position.
7150 **************************************************************************/
7151 static bool is_server_option(int start)
7153 int i = 0;
7155 while (server_option_cmd[i] != -1) {
7156 if (contains_str_before_start(start, command_name_by_number(server_option_cmd[i]),
7157 FALSE)) {
7158 return TRUE;
7160 i++;
7163 return FALSE;
7166 /**************************************************************************
7167 Commands that may be followed by an option level or server option
7168 **************************************************************************/
7169 static const int option_level_cmd[] = {
7170 CMD_SHOW,
7174 /**************************************************************************
7175 Returns true if the readline buffer string matches an option level or an
7176 option at the given position.
7177 **************************************************************************/
7178 static bool is_option_level(int start)
7180 int i = 0;
7182 while (option_level_cmd[i] != -1) {
7183 if (contains_str_before_start(start, command_name_by_number(option_level_cmd[i]),
7184 FALSE)) {
7185 return TRUE;
7187 i++;
7190 return FALSE;
7193 /**************************************************************************
7194 Returns TRUE if the readline buffer string is such that we expect an
7195 enumerated value at the given position. The option for which values
7196 should be completed is written to opt_p.
7197 **************************************************************************/
7198 static bool is_enum_option_value(int start, int *opt_p)
7200 if (contains_str_before_start(start, command_name_by_number(CMD_SET),
7201 TRUE)) {
7202 settings_iterate(SSET_ALL, pset) {
7203 if (setting_type(pset) != SST_ENUM
7204 && setting_type(pset) != SST_BITWISE) {
7205 continue;
7207 /* Allow a single token for enum options, multiple for bitwise
7208 * (the separator | will separate tokens for these purposes) */
7209 if (contains_token_before_start(start, 1, setting_name(pset),
7210 setting_type(pset) == SST_BITWISE)) {
7211 *opt_p = setting_number(pset);
7212 /* Suppress appended space for bitwise options (user may want |) */
7213 rl_completion_suppress_append = (setting_type(pset) == SST_BITWISE);
7214 return TRUE;
7216 } settings_iterate_end;
7218 return FALSE;
7221 /**************************************************************************
7222 Commands that may be followed by a filename
7223 **************************************************************************/
7224 static const int filename_cmd[] = {
7225 CMD_LOAD,
7226 CMD_SAVE,
7227 CMD_READ_SCRIPT,
7228 CMD_WRITE_SCRIPT,
7232 /**************************************************************************
7233 Return whether we are completing filename.
7234 **************************************************************************/
7235 static bool is_filename(int start)
7237 int i = 0;
7239 while (filename_cmd[i] != -1) {
7240 if (contains_str_before_start(start, command_name_by_number(filename_cmd[i]), FALSE)) {
7241 return TRUE;
7243 i++;
7246 return FALSE;
7249 /**************************************************************************
7250 Return whether we are completing second argument for create command
7251 **************************************************************************/
7252 static bool is_create_arg2(int start)
7254 return (contains_str_before_start(start, command_name_by_number(CMD_CREATE), TRUE)
7255 && num_tokens(start) == 2);
7258 /**************************************************************************
7259 Return whether we are completing argument for reset command
7260 **************************************************************************/
7261 static bool is_reset(int start)
7263 return contains_str_before_start(start,
7264 command_name_by_number(CMD_RESET),
7265 FALSE);
7268 /**************************************************************************
7269 Return whether we are completing argument for vote command
7270 **************************************************************************/
7271 static bool is_vote(int start)
7273 return contains_str_before_start(start,
7274 command_name_by_number(CMD_VOTE),
7275 FALSE);
7278 /**************************************************************************
7279 Return whether we are completing first argument for delegate command
7280 **************************************************************************/
7281 static bool is_delegate_arg1(int start)
7283 return contains_str_before_start(start,
7284 command_name_by_number(CMD_DELEGATE),
7285 FALSE);
7288 /**************************************************************************
7289 Return whether we are completing first argument for mapimg command
7290 **************************************************************************/
7291 static bool is_mapimg(int start)
7293 return contains_str_before_start(start,
7294 command_name_by_number(CMD_MAPIMG),
7295 FALSE);
7298 /**************************************************************************
7299 Return whether we are completing argument for fcdb command
7300 **************************************************************************/
7301 static bool is_fcdb(int start)
7303 return contains_str_before_start(start,
7304 command_name_by_number(CMD_FCDB),
7305 FALSE);
7308 /**************************************************************************
7309 Return whether we are completing argument for lua command
7310 **************************************************************************/
7311 static bool is_lua(int start)
7313 return contains_str_before_start(start,
7314 command_name_by_number(CMD_LUA),
7315 FALSE);
7318 /**************************************************************************
7319 Return whether we are completing help command argument.
7320 **************************************************************************/
7321 static bool is_help(int start)
7323 return contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE);
7326 /**************************************************************************
7327 Return whether we are completing list command argument.
7328 **************************************************************************/
7329 static bool is_list(int start)
7331 return contains_str_before_start(start, command_name_by_number(CMD_LIST), FALSE);
7334 /**************************************************************************
7335 Attempt to complete on the contents of TEXT. START and END bound the
7336 region of rl_line_buffer that contains the word to complete. TEXT is
7337 the word to complete. We can use the entire contents of rl_line_buffer
7338 in case we want to do some simple parsing. Return the array of matches,
7339 or NULL if there aren't any.
7340 **************************************************************************/
7341 char **freeciv_completion(const char *text, int start, int end)
7343 char **matches = (char **)NULL;
7345 if (is_help(start)) {
7346 matches = rl_completion_matches(text, help_generator);
7347 } else if (is_command(start)) {
7348 matches = rl_completion_matches(text, command_generator);
7349 } else if (is_list(start)) {
7350 matches = rl_completion_matches(text, list_generator);
7351 } else if (is_cmdlevel_arg2(start)) {
7352 matches = rl_completion_matches(text, cmdlevel_arg2_generator);
7353 } else if (is_cmdlevel_arg1(start)) {
7354 matches = rl_completion_matches(text, cmdlevel_arg1_generator);
7355 } else if (is_connection(start)) {
7356 matches = rl_completion_matches(text, connection_generator);
7357 } else if (is_player(start)) {
7358 matches = rl_completion_matches(text, player_generator);
7359 } else if (is_server_option(start)) {
7360 matches = rl_completion_matches(text, option_generator);
7361 } else if (is_option_level(start)) {
7362 matches = rl_completion_matches(text, olevel_generator);
7363 } else if (is_enum_option_value(start, &completion_option)) {
7364 matches = rl_completion_matches(text, option_value_generator);
7365 } else if (is_filename(start)) {
7366 /* This function we get from readline */
7367 matches = rl_completion_matches(text, rl_filename_completion_function);
7368 } else if (is_create_arg2(start)) {
7369 matches = rl_completion_matches(text, aitype_generator);
7370 } else if (is_reset(start)) {
7371 matches = rl_completion_matches(text, reset_generator);
7372 } else if (is_vote(start)) {
7373 matches = rl_completion_matches(text, vote_generator);
7374 } else if (is_delegate_arg1(start)) {
7375 matches = rl_completion_matches(text, delegate_generator);
7376 } else if (is_mapimg(start)) {
7377 matches = rl_completion_matches(text, mapimg_generator);
7378 } else if (is_fcdb(start)) {
7379 matches = rl_completion_matches(text, fcdb_generator);
7380 } else if (is_lua(start)) {
7381 matches = rl_completion_matches(text, lua_generator);
7382 } else {
7383 /* We have no idea what to do */
7384 matches = NULL;
7387 /* Don't automatically try to complete with filenames */
7388 rl_attempted_completion_over = 1;
7390 return (matches);
7393 #endif /* FREECIV_HAVE_LIBREADLINE */