rscompat: the "Provoking" uflag was added in 3.0.
[freeciv.git] / server / stdinhand.c
blob3f66fda467b65fc819572ea2a1668dc048df45f8
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 SSET_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 SSET_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 SSET_BOOL:
1744 case SSET_STRING:
1745 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %s",
1746 _("Value:"), val_buf, _("Default:"), def_buf);
1747 break;
1748 case SSET_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;
1769 /**************************************************************************
1770 Show the caller list of OPTIONS.
1771 help_cmd is the command the player used.
1772 Only show options which the caller can SEE.
1773 **************************************************************************/
1774 static void show_help_option_list(struct connection *caller,
1775 enum command_id help_cmd)
1777 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1778 cmd_reply(help_cmd, caller, C_COMMENT,
1779 _("Explanations are available for the following server options:"));
1780 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1781 if (!caller && con_get_style()) {
1782 settings_iterate(SSET_ALL, pset) {
1783 cmd_reply(help_cmd, caller, C_COMMENT, "%s", setting_name(pset));
1784 } settings_iterate_end
1785 } else {
1786 char buf[MAX_LEN_CONSOLE_LINE];
1787 int j = 0;
1788 buf[0] = '\0';
1790 settings_iterate(SSET_ALL, pset) {
1791 if (setting_is_visible(pset, caller)) {
1792 cat_snprintf(buf, sizeof(buf), "%-19s", setting_name(pset));
1793 if ((++j % 4) == 0) {
1794 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1795 buf[0] = '\0';
1798 } settings_iterate_end;
1800 if (buf[0] != '\0') {
1801 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1804 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1807 /**************************************************************************
1808 Handle explain command
1809 **************************************************************************/
1810 static bool explain_option(struct connection *caller, char *str, bool check)
1812 int cmd;
1814 remove_leading_trailing_spaces(str);
1816 if (*str != '\0') {
1817 cmd = lookup_option(str);
1818 if (cmd >= 0 && cmd < settings_number()) {
1819 show_help_option(caller, CMD_EXPLAIN, cmd);
1820 } else if (cmd == LOOKUP_OPTION_NO_RESULT
1821 || cmd == LOOKUP_OPTION_LEVEL_NAME
1822 || cmd == LOOKUP_OPTION_RULESETDIR) {
1823 cmd_reply(CMD_EXPLAIN, caller, C_FAIL,
1824 _("No explanation for that yet."));
1825 return FALSE;
1826 } else if (cmd == LOOKUP_OPTION_AMBIGUOUS) {
1827 cmd_reply(CMD_EXPLAIN, caller, C_FAIL, _("Ambiguous option name."));
1828 return FALSE;
1829 } else {
1830 log_error("Unexpected case %d in %s line %d", cmd, __FILE__,
1831 __FC_LINE__);
1832 return FALSE;
1834 } else {
1835 show_help_option_list(caller, CMD_EXPLAIN);
1837 return TRUE;
1840 /******************************************************************
1841 Send a message to all players
1842 ******************************************************************/
1843 static bool wall(char *str, bool check)
1845 if (!check) {
1846 notify_conn(NULL, NULL, E_MESSAGE_WALL, ftc_server_prompt,
1847 _("Server Operator: %s"), str);
1849 return TRUE;
1852 /******************************************************************
1853 Set message to send to all new connections
1854 ******************************************************************/
1855 static bool connectmsg_command(struct connection *caller, char *str,
1856 bool check)
1858 unsigned int bufsize = sizeof(game.server.connectmsg);
1860 if (is_restricted(caller)) {
1861 return FALSE;
1863 if (!check) {
1864 int i;
1865 int c = 0;
1867 for (i = 0; c < bufsize -1 && str[i] != '\0'; i++) {
1868 if (str[i] == '\\') {
1869 i++;
1871 if (str[i] == 'n') {
1872 game.server.connectmsg[c++] = '\n';
1873 } else {
1874 game.server.connectmsg[c++] = str[i];
1876 } else {
1877 game.server.connectmsg[c++] = str[i];
1881 game.server.connectmsg[c++] = '\0';
1883 if (c == bufsize) {
1884 /* Truncated */
1885 cmd_reply(CMD_CONNECTMSG, caller, C_WARNING,
1886 _("Connectmsg truncated to %u bytes."), bufsize);
1889 return TRUE;
1892 /******************************************************************
1893 Translate an AI level back to its CMD_* value.
1894 If we just used /set ailevel <num> we wouldn't have to do this - rp
1895 ******************************************************************/
1896 static enum command_id cmd_of_level(enum ai_level level)
1898 switch (level) {
1899 case AI_LEVEL_AWAY : return CMD_AWAY;
1900 case AI_LEVEL_HANDICAPPED : return CMD_HANDICAPPED;
1901 case AI_LEVEL_NOVICE : return CMD_NOVICE;
1902 case AI_LEVEL_EASY : return CMD_EASY;
1903 case AI_LEVEL_NORMAL : return CMD_NORMAL;
1904 case AI_LEVEL_HARD : return CMD_HARD;
1905 case AI_LEVEL_CHEATING : return CMD_CHEATING;
1906 #ifdef FREECIV_DEBUG
1907 case AI_LEVEL_EXPERIMENTAL : return CMD_EXPERIMENTAL;
1908 #endif /* FREECIV_DEBUG */
1909 case AI_LEVEL_COUNT : return CMD_NORMAL;
1911 log_error("Unknown AI level variant: %d.", level);
1912 return CMD_NORMAL;
1915 /******************************************************************
1916 Set an AI level from the server prompt.
1917 ******************************************************************/
1918 void set_ai_level_direct(struct player *pplayer, enum ai_level level)
1920 set_ai_level_directer(pplayer, level);
1921 send_player_info_c(pplayer, NULL);
1922 cmd_reply(cmd_of_level(level), NULL, C_OK,
1923 _("Player '%s' now has AI skill level '%s'."),
1924 player_name(pplayer),
1925 ai_level_translated_name(level));
1929 /******************************************************************
1930 Handle a user command to set an AI level.
1931 ******************************************************************/
1932 static bool set_ai_level_named(struct connection *caller, const char *name,
1933 const char *level_name, bool check)
1935 enum ai_level level = ai_level_by_name(level_name, fc_strcasecmp);
1937 return set_ai_level(caller, name, level, check);
1940 /******************************************************************
1941 Set AI level
1942 ******************************************************************/
1943 static bool set_ai_level(struct connection *caller, const char *name,
1944 enum ai_level level, bool check)
1946 enum m_pre_result match_result;
1947 struct player *pplayer;
1949 fc_assert_ret_val(level > 0 && level < 11, FALSE);
1951 pplayer = player_by_name_prefix(name, &match_result);
1953 if (pplayer) {
1954 if (is_ai(pplayer)) {
1955 if (check) {
1956 return TRUE;
1958 set_ai_level_directer(pplayer, level);
1959 send_player_info_c(pplayer, NULL);
1960 cmd_reply(cmd_of_level(level), caller, C_OK,
1961 _("Player '%s' now has AI skill level '%s'."),
1962 player_name(pplayer),
1963 ai_level_translated_name(level));
1964 } else {
1965 cmd_reply(cmd_of_level(level), caller, C_FAIL,
1966 _("%s is not controlled by the AI."),
1967 player_name(pplayer));
1968 return FALSE;
1970 } else if (match_result == M_PRE_EMPTY) {
1971 if (check) {
1972 return TRUE;
1974 players_iterate(cplayer) {
1975 if (is_ai(cplayer)) {
1976 set_ai_level_directer(cplayer, level);
1977 send_player_info_c(cplayer, NULL);
1978 cmd_reply(cmd_of_level(level), caller, C_OK,
1979 _("Player '%s' now has AI skill level '%s'."),
1980 player_name(cplayer),
1981 ai_level_translated_name(level));
1983 } players_iterate_end;
1984 game.info.skill_level = level;
1985 cmd_reply(cmd_of_level(level), caller, C_OK,
1986 _("Default AI skill level set to '%s'."),
1987 ai_level_translated_name(level));
1988 } else {
1989 cmd_reply_no_such_player(cmd_of_level(level), caller, name, match_result);
1990 return FALSE;
1992 return TRUE;
1995 /******************************************************************
1996 Set user to away mode.
1997 ******************************************************************/
1998 static bool away_command(struct connection *caller, bool check)
2000 struct player *pplayer;
2002 if (caller == NULL) {
2003 cmd_reply(CMD_AWAY, caller, C_FAIL, _("This command is client only."));
2004 return FALSE;
2007 if (!conn_controls_player(caller)) {
2008 /* This happens for detached or observer connections. */
2009 cmd_reply(CMD_AWAY, caller, C_FAIL,
2010 _("Only players may use the away command."));
2011 return FALSE;
2014 if (check) {
2015 return TRUE;
2018 pplayer = conn_get_player(caller);
2019 if (is_human(pplayer)) {
2020 cmd_reply(CMD_AWAY, caller, C_OK,
2021 _("%s set to away mode."), player_name(pplayer));
2022 player_set_to_ai_mode(pplayer, AI_LEVEL_AWAY);
2023 fc_assert(!is_human(pplayer));
2024 } else {
2025 cmd_reply(CMD_AWAY, caller, C_OK,
2026 _("%s returned to game."), player_name(pplayer));
2027 player_set_under_human_control(pplayer);
2028 fc_assert(is_human(pplayer));
2031 send_player_info_c(caller->playing, game.est_connections);
2033 return TRUE;
2036 /**************************************************************************
2037 Show changed settings and ruleset summary.
2038 **************************************************************************/
2039 static void show_ruleset_info(struct connection *caller, enum command_id cmd,
2040 bool check, int read_recursion)
2042 char *show_arg = "changed";
2044 /* show changed settings only at the top level of recursion */
2045 if (read_recursion != 0) {
2046 return;
2049 show_settings(caller, cmd, show_arg, check);
2051 if (game.ruleset_summary != NULL) {
2052 char *translated = fc_strdup(_(game.ruleset_summary));
2054 fc_break_lines(translated, LINE_BREAK);
2055 cmd_reply(cmd, caller, C_COMMENT, "%s", translated);
2056 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
2057 free(translated);
2061 /**************************************************************************
2062 /show command: show settings and their values.
2063 **************************************************************************/
2064 static bool show_command(struct connection *caller, char *str, bool check)
2066 return show_settings(caller, CMD_SHOW, str, check);
2069 /**************************************************************************
2070 Print a summary of the settings and their values. Note that most values
2071 are at most 4 digits, except seeds, which we let overflow their columns,
2072 plus a sign character. Only show options which the caller can SEE.
2073 **************************************************************************/
2074 static bool show_settings(struct connection *caller,
2075 enum command_id called_as,
2076 char *str, bool check)
2078 int cmd;
2079 enum sset_level level = SSET_ALL;
2080 size_t clen = 0;
2082 remove_leading_trailing_spaces(str);
2083 if (str[0] != '\0') {
2084 /* In "/show forests", figure out that it's the forests option we're
2085 * looking at. */
2086 cmd = lookup_option(str);
2087 if (cmd >= 0) {
2088 /* Ignore levels when a particular option is specified. */
2089 level = SSET_NONE;
2091 if (!setting_is_visible(setting_by_number(cmd), caller)) {
2092 cmd_reply(called_as, caller, C_FAIL,
2093 _("Sorry, you do not have access to view option '%s'."),
2094 str);
2095 return FALSE;
2099 /* Valid negative values for 'cmd' are defined as LOOKUP_OPTION_*. */
2100 switch (cmd) {
2101 case LOOKUP_OPTION_NO_RESULT:
2102 cmd_reply(called_as, caller, C_FAIL, _("Unknown option '%s'."), str);
2103 return FALSE;
2104 case LOOKUP_OPTION_AMBIGUOUS:
2105 /* Allow ambiguous: show all matching. */
2106 clen = strlen(str);
2107 break;
2108 case LOOKUP_OPTION_LEVEL_NAME:
2109 /* Option level. */
2110 level = lookup_option_level(str);
2111 break;
2112 case LOOKUP_OPTION_RULESETDIR:
2113 /* Ruleset. */
2114 cmd_reply(called_as, caller, C_COMMENT,
2115 _("Current ruleset directory is \"%s\""),
2116 game.server.rulesetdir);
2117 return TRUE;
2119 } else {
2120 /* to indicate that no command was specified */
2121 cmd = LOOKUP_OPTION_NO_RESULT;
2122 /* Use vital level by default. */
2123 level = SSET_VITAL;
2126 fc_assert_ret_val(cmd >= 0 || cmd == LOOKUP_OPTION_AMBIGUOUS
2127 || cmd == LOOKUP_OPTION_LEVEL_NAME
2128 || cmd == LOOKUP_OPTION_NO_RESULT, FALSE);
2130 #define cmd_reply_show(string) \
2131 cmd_reply(called_as, caller, C_COMMENT, "%s", string)
2134 const char *heading = NULL;
2135 switch(level) {
2136 case SSET_NONE:
2137 break;
2138 case SSET_CHANGED:
2139 heading = _("All options with non-default values");
2140 break;
2141 case SSET_ALL:
2142 heading = _("All options");
2143 break;
2144 case SSET_VITAL:
2145 heading = _("Vital options");
2146 break;
2147 case SSET_SITUATIONAL:
2148 heading = _("Situational options");
2149 break;
2150 case SSET_RARE:
2151 heading = _("Rarely used options");
2152 break;
2153 case SSET_LOCKED:
2154 heading = _("Options locked by the ruleset");
2155 break;
2156 case OLEVELS_NUM:
2157 /* nothing */
2158 break;
2160 if (heading) {
2161 cmd_reply_show(horiz_line);
2162 cmd_reply_show(heading);
2165 cmd_reply_show(horiz_line);
2166 cmd_reply_show(_("In the column '##' the status of the option is shown:"));
2167 cmd_reply_show(_(" - a '!' means the option is locked by the ruleset."));
2168 cmd_reply_show(_(" - a '+' means you may change the option."));
2169 cmd_reply_show(_(" - a '~' means that option follows default value."));
2170 cmd_reply_show(_(" - a '=' means the value is same as default."));
2171 cmd_reply_show(horiz_line);
2172 cmd_reply(called_as, caller, C_COMMENT, _("%-*s ## value (min, max)"),
2173 OPTION_NAME_SPACE, _("Option"));
2174 cmd_reply_show(horiz_line);
2176 /* Update changed and locked levels. */
2177 settings_list_update();
2179 switch(level) {
2180 case SSET_NONE:
2181 /* Show _one_ setting. */
2182 fc_assert_ret_val(0 <= cmd, FALSE);
2184 struct setting *pset = setting_by_number(cmd);
2186 show_settings_one(caller, called_as, pset);
2188 break;
2189 case SSET_CHANGED:
2190 case SSET_ALL:
2191 case SSET_VITAL:
2192 case SSET_SITUATIONAL:
2193 case SSET_RARE:
2194 case SSET_LOCKED:
2195 settings_iterate(level, pset) {
2196 if (!setting_is_visible(pset, caller)) {
2197 continue;
2200 if (LOOKUP_OPTION_AMBIGUOUS == cmd
2201 && 0 != fc_strncasecmp(setting_name(pset), str, clen)) {
2202 continue;
2205 show_settings_one(caller, called_as, pset);
2206 } settings_iterate_end;
2207 break;
2208 case OLEVELS_NUM:
2209 /* nothing */
2210 break;
2213 cmd_reply_show(horiz_line);
2214 /* Only emit this additional help for bona fide 'show' command */
2215 if (called_as == CMD_SHOW) {
2216 cmd_reply_show(_("A help text for each option is available via 'help "
2217 "<option>'."));
2218 cmd_reply_show(horiz_line);
2219 if (level == SSET_VITAL) {
2220 cmd_reply_show(_("Try 'show situational' or 'show rare' to show "
2221 "more options.\n"
2222 "Try 'show changed' to show settings with "
2223 "non-default values.\n"
2224 "Try 'show locked' to show settings locked "
2225 "by the ruleset."));
2226 cmd_reply_show(horiz_line);
2229 return TRUE;
2230 #undef cmd_reply_show
2233 /*****************************************************************************
2234 Show one setting.
2236 Each option value will be displayed as:
2238 [OPTION_NAME_SPACE length for name] ## [value] ([min], [max])
2240 where '##' is a combination of ' ', '!' or '+' followed by ' ', '*', or '=' with
2241 - '!': the option is locked by the ruleset
2242 - '+': you may change the option
2243 - '~': the option follows default value
2244 - '=': the value is same as default
2245 *****************************************************************************/
2246 static void show_settings_one(struct connection *caller, enum command_id cmd,
2247 struct setting *pset)
2249 char buf[MAX_LEN_CONSOLE_LINE] = "", value[MAX_LEN_CONSOLE_LINE] = "";
2250 bool is_changed;
2251 static char prefix[OPTION_NAME_SPACE + 4 + 1] = "";
2252 char defaultness;
2254 fc_assert_ret(pset != NULL);
2256 is_changed = setting_non_default(pset);
2257 setting_value_name(pset, TRUE, value, sizeof(value));
2259 /* Wrap long option values, such as bitwise options */
2260 fc_break_lines(value, LINE_BREAK - (sizeof(prefix)-1));
2262 if (prefix[0] == '\0') {
2263 memset(prefix, ' ', sizeof(prefix)-1);
2266 if (is_changed) {
2267 /* Emphasizes the changed option. */
2268 /* Apply tags to each line fragment. */
2269 size_t startpos = 0;
2270 char *nl;
2271 do {
2272 nl = strchr(value + startpos, '\n');
2273 featured_text_apply_tag(value, buf, sizeof(buf), TTT_COLOR,
2274 startpos, nl ? nl - value : FT_OFFSET_UNSET,
2275 ftc_changed);
2276 sz_strlcpy(value, buf);
2277 if (nl) {
2278 char *p = strchr(nl, '\n');
2279 fc_assert_action(p != NULL, break);
2280 startpos = p + 1 - value;
2282 } while (nl);
2285 if (SSET_INT == setting_type(pset)) {
2286 /* Add the range. */
2287 cat_snprintf(value, sizeof(value), " (%d, %d)",
2288 setting_int_min(pset), setting_int_max(pset));
2291 if (setting_get_setdef(pset) == SETDEF_INTERNAL) {
2292 defaultness = '~';
2293 } else if (is_changed) {
2294 defaultness = ' ';
2295 } else {
2296 defaultness = '=';
2299 cmd_reply_prefix(cmd, caller, C_COMMENT, prefix, "%-*s %c%c %s",
2300 OPTION_NAME_SPACE, setting_name(pset),
2301 setting_status(caller, pset), defaultness,
2302 value);
2305 /******************************************************************
2306 Handle team command
2307 ******************************************************************/
2308 static bool team_command(struct connection *caller, char *str, bool check)
2310 struct player *pplayer;
2311 enum m_pre_result match_result;
2312 char buf[MAX_LEN_CONSOLE_LINE];
2313 char *arg[2];
2314 int ntokens = 0, i;
2315 bool res = FALSE;
2316 struct team_slot *tslot;
2318 if (game_was_started()) {
2319 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2320 _("Cannot change teams once game has begun."));
2321 return FALSE;
2324 if (str != NULL || strlen(str) > 0) {
2325 sz_strlcpy(buf, str);
2326 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2328 if (ntokens != 2) {
2329 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2330 _("Undefined argument. Usage:\n%s"),
2331 command_synopsis(command_by_number(CMD_TEAM)));
2332 goto cleanup;
2335 pplayer = player_by_name_prefix(arg[0], &match_result);
2336 if (pplayer == NULL) {
2337 cmd_reply_no_such_player(CMD_TEAM, caller, arg[0], match_result);
2338 goto cleanup;
2341 tslot = team_slot_by_rule_name(arg[1]);
2342 if (NULL == tslot) {
2343 int teamno;
2345 if (str_to_int(arg[1], &teamno)) {
2346 tslot = team_slot_by_number(teamno);
2349 if (NULL == tslot) {
2350 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2351 _("No such team %s. Please give a "
2352 "valid team name or number."), arg[1]);
2353 goto cleanup;
2356 if (is_barbarian(pplayer)) {
2357 /* This can happen if we change team settings on a loaded game. */
2358 cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Cannot team a barbarian."));
2359 goto cleanup;
2361 if (!check) {
2362 team_add_player(pplayer, team_new(tslot));
2363 send_player_info_c(pplayer, NULL);
2364 cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
2365 player_name(pplayer),
2366 team_slot_name_translation(tslot));
2368 res = TRUE;
2370 cleanup:
2371 for (i = 0; i < ntokens; i++) {
2372 free(arg[i]);
2374 return res;
2377 /**************************************************************************
2378 List all running votes. Moved from /vote command.
2379 **************************************************************************/
2380 static void show_votes(struct connection *caller)
2382 int count = 0;
2383 const char *title;
2385 if (vote_list != NULL) {
2386 vote_list_iterate(vote_list, pvote) {
2387 if (NULL != caller && !conn_can_see_vote(caller, pvote)) {
2388 continue;
2390 /* TRANS: "Vote" or "Teamvote" is voting-as-a-process. Used as
2391 * part of a sentence. */
2392 title = vote_is_team_only(pvote) ? _("Teamvote") : _("Vote");
2393 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2394 /* TRANS: "[Vote|Teamvote] 3 \"proposed change\" (needs ..." */
2395 _("%s %d \"%s\" (needs %0.0f%%%s): %d for, "
2396 "%d against, and %d abstained out of %d players."),
2397 title, pvote->vote_no, pvote->cmdline,
2398 MIN(100, pvote->need_pc * 100 + 1),
2399 pvote->flags & VCF_NODISSENT ? _(" no dissent") : "",
2400 pvote->yes, pvote->no, pvote->abstain, count_voters(pvote));
2401 count++;
2402 } vote_list_iterate_end;
2405 if (count == 0) {
2406 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2407 _("There are no votes going on."));
2411 /**************************************************************************
2412 Vote command argument definitions.
2413 **************************************************************************/
2414 static const char *const vote_args[] = {
2415 "yes",
2416 "no",
2417 "abstain",
2418 NULL
2420 static const char *vote_arg_accessor(int i)
2422 return vote_args[i];
2425 /******************************************************************
2426 Make or participate in a vote.
2427 ******************************************************************/
2428 static bool vote_command(struct connection *caller, char *str,
2429 bool check)
2431 char buf[MAX_LEN_CONSOLE_LINE];
2432 char *arg[2];
2433 int ntokens = 0, i = 0, which = -1;
2434 enum m_pre_result match_result;
2435 struct vote *pvote = NULL;
2436 bool res = FALSE;
2438 if (check) {
2439 /* This should never happen, since /vote must always be
2440 * set to ALLOW_BASIC or less. But just in case... */
2441 return FALSE;
2444 sz_strlcpy(buf, str);
2445 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2447 if (ntokens == 0) {
2448 show_votes(caller);
2449 goto CLEANUP;
2450 } else if (!conn_can_vote(caller, NULL)) {
2451 cmd_reply(CMD_VOTE, caller, C_FAIL,
2452 _("You are not allowed to use this command."));
2453 goto CLEANUP;
2456 match_result = match_prefix(vote_arg_accessor, VOTE_NUM, 0,
2457 fc_strncasecmp, NULL, arg[0], &i);
2459 if (match_result == M_PRE_AMBIGUOUS) {
2460 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2461 _("The argument \"%s\" is ambiguous."), arg[0]);
2462 goto CLEANUP;
2463 } else if (match_result > M_PRE_AMBIGUOUS) {
2464 /* Failed */
2465 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2466 _("Undefined argument. Usage:\n%s"),
2467 command_synopsis(command_by_number(CMD_VOTE)));
2468 goto CLEANUP;
2471 if (ntokens == 1) {
2472 /* Applies to last vote */
2473 if (vote_number_sequence > 0 && get_vote_by_no(vote_number_sequence)) {
2474 which = vote_number_sequence;
2475 } else {
2476 int num_votes = vote_list_size(vote_list);
2477 if (num_votes == 0) {
2478 cmd_reply(CMD_VOTE, caller, C_FAIL, _("There are no votes running."));
2479 } else {
2480 /* TRANS: "vote" as a process */
2481 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote (%d %s)."),
2482 num_votes, PL_("other vote running", "other votes running",
2483 num_votes));
2485 goto CLEANUP;
2487 } else {
2488 if (!str_to_int(arg[1], &which)) {
2489 cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be an integer."));
2490 goto CLEANUP;
2494 if (!(pvote = get_vote_by_no(which))) {
2495 /* TRANS: "vote" as a process */
2496 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), which);
2497 goto CLEANUP;
2500 if (!conn_can_vote(caller, pvote)) {
2501 cmd_reply(CMD_VOTE, caller, C_FAIL,
2502 _("You are not allowed to vote on that."));
2503 goto CLEANUP;
2506 if (i == VOTE_YES) {
2507 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
2508 pvote->cmdline);
2509 connection_vote(caller, pvote, VOTE_YES);
2510 } else if (i == VOTE_NO) {
2511 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
2512 pvote->cmdline);
2513 connection_vote(caller, pvote, VOTE_NO);
2514 } else if (i == VOTE_ABSTAIN) {
2515 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2516 _("You abstained from voting on \"%s\""), pvote->cmdline);
2517 connection_vote(caller, pvote, VOTE_ABSTAIN);
2518 } else {
2519 /* Must never happen. */
2520 fc_assert_action(FALSE, goto CLEANUP);
2523 res = TRUE;
2525 CLEANUP:
2526 free_tokens(arg, ntokens);
2527 return res;
2530 /**************************************************************************
2531 Cancel a vote... /cancelvote <vote number>|all.
2532 **************************************************************************/
2533 static bool cancelvote_command(struct connection *caller,
2534 char *arg, bool check)
2536 struct vote *pvote = NULL;
2537 int vote_no;
2539 if (check) {
2540 /* This should never happen anyway, since /cancelvote
2541 * is set to ALLOW_BASIC in both pregame and while the
2542 * game is running. */
2543 return FALSE;
2546 remove_leading_trailing_spaces(arg);
2548 if (arg[0] == '\0') {
2549 if (caller == NULL) {
2550 /* Server prompt */
2551 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2552 /* TRANS: "vote" as a process */
2553 _("Missing argument <vote number> or "
2554 "the string \"all\"."));
2555 return FALSE;
2557 /* The caller cancel his/her own vote. */
2558 if (!(pvote = get_vote_by_caller(caller))) {
2559 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2560 _("You don't have any vote going on."));
2561 return FALSE;
2563 } else if (fc_strcasecmp(arg, "all") == 0) {
2564 /* Cancel all votes (needs some privileges). */
2565 if (vote_list_size(vote_list) == 0) {
2566 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2567 _("There isn't any vote going on."));
2568 return FALSE;
2569 } else if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
2570 clear_all_votes();
2571 notify_conn(NULL, NULL, E_VOTE_ABORTED, ftc_server,
2572 /* TRANS: "votes" as a process */
2573 _("All votes have been removed."));
2574 return TRUE;
2575 } else {
2576 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2577 _("You are not allowed to use this command."));
2578 return FALSE;
2580 } else if (str_to_int(arg, &vote_no)) {
2581 /* Cancel one particular vote (needs some privileges if the vote
2582 * is not owned). */
2583 if (!(pvote = get_vote_by_no(vote_no))) {
2584 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2585 /* TRANS: "vote" as a process */
2586 _("No such vote (%d)."), vote_no);
2587 return FALSE;
2588 } else if (caller && conn_get_access(caller) < ALLOW_ADMIN
2589 && caller->id != pvote->caller_id) {
2590 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2591 /* TRANS: "vote" as a process */
2592 _("You are not allowed to cancel this vote (%d)."),
2593 vote_no);
2594 return FALSE;
2596 } else {
2597 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2598 /* TRANS: "vote" as a process */
2599 _("Usage: /cancelvote [<vote number>|all]"));
2600 return FALSE;
2603 fc_assert_ret_val(NULL != pvote, FALSE);
2605 if (caller) {
2606 notify_team(conn_get_player(vote_get_caller(pvote)),
2607 NULL, E_VOTE_ABORTED, ftc_server,
2608 /* TRANS: "vote" as a process */
2609 _("%s has canceled the vote \"%s\" (number %d)."),
2610 caller->username, pvote->cmdline, pvote->vote_no);
2611 } else {
2612 /* Server prompt */
2613 notify_team(conn_get_player(vote_get_caller(pvote)),
2614 NULL, E_VOTE_ABORTED, ftc_server,
2615 /* TRANS: "vote" as a process */
2616 _("The vote \"%s\" (number %d) has been canceled."),
2617 pvote->cmdline, pvote->vote_no);
2619 /* Make it after, prevent crashs about a free pointer (pvote). */
2620 remove_vote(pvote);
2622 return TRUE;
2625 /******************************************************************
2626 Turn on selective debugging.
2627 ******************************************************************/
2628 static bool debug_command(struct connection *caller, char *str,
2629 bool check)
2631 char buf[MAX_LEN_CONSOLE_LINE];
2632 char *arg[3];
2633 int ntokens = 0, i;
2635 if (game.info.is_new_game) {
2636 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2637 _("Can only use this command once game has begun."));
2638 return FALSE;
2640 if (check) {
2641 return TRUE; /* whatever! */
2644 if (str != NULL && strlen(str) > 0) {
2645 sz_strlcpy(buf, str);
2646 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
2647 } else {
2648 ntokens = 0;
2651 if (ntokens > 0 && strcmp(arg[0], "diplomacy") == 0) {
2652 struct player *pplayer;
2653 enum m_pre_result match_result;
2655 if (ntokens != 2) {
2656 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2657 _("Undefined argument. Usage:\n%s"),
2658 command_synopsis(command_by_number(CMD_DEBUG)));
2659 goto cleanup;
2661 pplayer = player_by_name_prefix(arg[1], &match_result);
2662 if (pplayer == NULL) {
2663 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2664 goto cleanup;
2666 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY)) {
2667 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2668 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy no longer debugged"),
2669 player_name(pplayer));
2670 } else {
2671 BV_SET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2672 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy debugged"),
2673 player_name(pplayer));
2674 /* TODO: print some info about the player here */
2676 } else if (ntokens > 0 && strcmp(arg[0], "tech") == 0) {
2677 struct player *pplayer;
2678 enum m_pre_result match_result;
2680 if (ntokens != 2) {
2681 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2682 _("Undefined argument. Usage:\n%s"),
2683 command_synopsis(command_by_number(CMD_DEBUG)));
2684 goto cleanup;
2686 pplayer = player_by_name_prefix(arg[1], &match_result);
2687 if (pplayer == NULL) {
2688 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2689 goto cleanup;
2691 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_TECH)) {
2692 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_TECH);
2693 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech no longer debugged"),
2694 player_name(pplayer));
2695 } else {
2696 BV_SET(pplayer->server.debug, PLAYER_DEBUG_TECH);
2697 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech debugged"),
2698 player_name(pplayer));
2699 /* TODO: print some info about the player here */
2701 } else if (ntokens > 0 && strcmp(arg[0], "info") == 0) {
2702 int cities = 0, players = 0, units = 0, citizen_count = 0;
2704 players_iterate(plr) {
2705 players++;
2706 city_list_iterate(plr->cities, pcity) {
2707 cities++;
2708 citizen_count += city_size_get(pcity);
2709 } city_list_iterate_end;
2710 units += unit_list_size(plr->units);
2711 } players_iterate_end;
2712 log_normal(_("players=%d cities=%d citizens=%d units=%d"),
2713 players, cities, citizen_count, units);
2714 notify_conn(game.est_connections, NULL, E_AI_DEBUG, ftc_log,
2715 _("players=%d cities=%d citizens=%d units=%d"),
2716 players, cities, citizen_count, units);
2717 } else if (ntokens > 0 && strcmp(arg[0], "city") == 0) {
2718 int x, y;
2719 struct tile *ptile;
2720 struct city *pcity;
2722 if (ntokens != 3) {
2723 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2724 _("Undefined argument. Usage:\n%s"),
2725 command_synopsis(command_by_number(CMD_DEBUG)));
2726 goto cleanup;
2728 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2729 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2730 goto cleanup;
2732 if (!(ptile = map_pos_to_tile(x, y))) {
2733 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2734 goto cleanup;
2736 pcity = tile_city(ptile);
2737 if (!pcity) {
2738 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("No city at this coordinate."));
2739 goto cleanup;
2741 if (pcity->server.debug) {
2742 pcity->server.debug = FALSE;
2743 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s no longer debugged"),
2744 city_name_get(pcity));
2745 } else {
2746 pcity->server.debug = TRUE;
2747 CITY_LOG(LOG_NORMAL, pcity, "debugged");
2749 } else if (ntokens > 0 && strcmp(arg[0], "units") == 0) {
2750 int x, y;
2751 struct tile *ptile;
2753 if (ntokens != 3) {
2754 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2755 _("Undefined argument. Usage:\n%s"),
2756 command_synopsis(command_by_number(CMD_DEBUG)));
2757 goto cleanup;
2759 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2760 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2761 goto cleanup;
2763 if (!(ptile = map_pos_to_tile(x, y))) {
2764 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2765 goto cleanup;
2767 unit_list_iterate(ptile->units, punit) {
2768 if (punit->server.debug) {
2769 punit->server.debug = FALSE;
2770 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2771 nation_adjective_for_player(unit_owner(punit)),
2772 unit_name_translation(punit));
2773 } else {
2774 punit->server.debug = TRUE;
2775 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2776 nation_rule_name(nation_of_unit(punit)),
2777 unit_name_translation(punit));
2779 } unit_list_iterate_end;
2780 } else if (ntokens > 0 && strcmp(arg[0], "timing") == 0) {
2781 TIMING_RESULTS();
2782 } else if (ntokens > 0 && strcmp(arg[0], "ferries") == 0) {
2783 if (game.server.debug[DEBUG_FERRIES]) {
2784 game.server.debug[DEBUG_FERRIES] = FALSE;
2785 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system is no longer "
2786 "in debug mode."));
2787 } else {
2788 game.server.debug[DEBUG_FERRIES] = TRUE;
2789 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system in debug mode."));
2791 } else if (ntokens > 0 && strcmp(arg[0], "unit") == 0) {
2792 int id;
2793 struct unit *punit;
2795 if (ntokens != 2) {
2796 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2797 _("Undefined argument. Usage:\n%s"),
2798 command_synopsis(command_by_number(CMD_DEBUG)));
2799 goto cleanup;
2801 if (!str_to_int(arg[1], &id)) {
2802 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 must be integer."));
2803 goto cleanup;
2805 if (!(punit = game_unit_by_number(id))) {
2806 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Unit %d does not exist."), id);
2807 goto cleanup;
2809 if (punit->server.debug) {
2810 punit->server.debug = FALSE;
2811 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2812 nation_adjective_for_player(unit_owner(punit)),
2813 unit_name_translation(punit));
2814 } else {
2815 punit->server.debug = TRUE;
2816 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2817 nation_rule_name(nation_of_unit(punit)),
2818 unit_name_translation(punit));
2820 } else {
2821 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2822 _("Undefined argument. Usage:\n%s"),
2823 command_synopsis(command_by_number(CMD_DEBUG)));
2825 cleanup:
2826 for (i = 0; i < ntokens; i++) {
2827 free(arg[i]);
2829 return TRUE;
2832 /******************************************************************
2833 Helper to validate an argument referring to a server setting.
2834 Sends error message and returns NULL on failure.
2835 ******************************************************************/
2836 static struct setting *validate_setting_arg(enum command_id cmd,
2837 struct connection *caller,
2838 char *arg)
2840 int opt = lookup_option(arg);
2842 if (opt < 0) {
2843 switch (opt) {
2844 case LOOKUP_OPTION_NO_RESULT:
2845 case LOOKUP_OPTION_LEVEL_NAME:
2846 cmd_reply(cmd, caller, C_SYNTAX, _("Option '%s' not recognized."), arg);
2847 break;
2848 case LOOKUP_OPTION_AMBIGUOUS:
2849 cmd_reply(cmd, caller, C_SYNTAX, _("Ambiguous option name."));
2850 break;
2851 case LOOKUP_OPTION_RULESETDIR:
2852 cmd_reply(cmd, caller, C_SYNTAX,
2853 /* TRANS: 'rulesetdir' is the command. Do not translate. */
2854 _("Use the '%srulesetdir' command to change the ruleset "
2855 "directory."), caller ? "/" : "");
2856 break;
2857 default:
2858 fc_assert(opt >= LOOKUP_OPTION_RULESETDIR);
2859 break;
2861 return NULL;
2864 return setting_by_number(opt);
2867 /******************************************************************
2868 Handle set command
2869 ******************************************************************/
2870 static bool set_command(struct connection *caller, char *str, bool check)
2872 char *args[2];
2873 int val, nargs;
2874 struct setting *pset;
2875 bool do_update;
2876 char reject_msg[256] = "";
2877 bool ret = FALSE;
2879 /* '=' is also a valid delimiter for this function. */
2880 nargs = get_tokens(str, args, ARRAY_SIZE(args), TOKEN_DELIMITERS "=");
2882 if (nargs < 2) {
2883 cmd_reply(CMD_SET, caller, C_SYNTAX,
2884 _("Undefined argument. Usage:\n%s"),
2885 command_synopsis(command_by_number(CMD_SET)));
2886 goto cleanup;
2889 pset = validate_setting_arg(CMD_SET, caller, args[0]);
2891 if (!pset) {
2892 /* Reason already reported. */
2893 goto cleanup;
2896 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))
2897 && !check) {
2898 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2899 goto cleanup;
2902 do_update = FALSE;
2904 switch (setting_type(pset)) {
2905 case SSET_BOOL:
2906 if (check) {
2907 if (!setting_is_changeable(pset, caller, reject_msg,
2908 sizeof(reject_msg))
2909 || (!setting_bool_validate(pset, args[1], caller,
2910 reject_msg, sizeof(reject_msg)))) {
2911 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2912 goto cleanup;
2914 } else if (setting_bool_set(pset, args[1], caller,
2915 reject_msg, sizeof(reject_msg))) {
2916 do_update = TRUE;
2917 } else {
2918 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2919 goto cleanup;
2921 break;
2923 case SSET_INT:
2924 if (!str_to_int(args[1], &val)) {
2925 cmd_reply(CMD_SET, caller, C_SYNTAX,
2926 _("The parameter %s should only contain +- and 0-9."),
2927 setting_name(pset));
2928 goto cleanup;
2930 if (check) {
2931 if (!setting_is_changeable(pset, caller, reject_msg,
2932 sizeof(reject_msg))
2933 || !setting_int_validate(pset, val, caller, reject_msg,
2934 sizeof(reject_msg))) {
2935 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2936 goto cleanup;
2938 } else {
2939 if (setting_int_set(pset, val, caller, reject_msg,
2940 sizeof(reject_msg))) {
2941 do_update = TRUE;
2942 } else {
2943 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2944 goto cleanup;
2947 break;
2949 case SSET_STRING:
2950 if (check) {
2951 if (!setting_is_changeable(pset, caller, reject_msg,
2952 sizeof(reject_msg))
2953 || !setting_str_validate(pset, args[1], caller, reject_msg,
2954 sizeof(reject_msg))) {
2955 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2956 goto cleanup;
2958 } else {
2959 if (setting_str_set(pset, args[1], caller, reject_msg,
2960 sizeof(reject_msg))) {
2961 do_update = TRUE;
2962 } else {
2963 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2964 goto cleanup;
2967 break;
2969 case SSET_ENUM:
2970 if (check) {
2971 if (!setting_is_changeable(pset, caller, reject_msg,
2972 sizeof(reject_msg))
2973 || (!setting_enum_validate(pset, args[1], caller,
2974 reject_msg, sizeof(reject_msg)))) {
2975 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2976 goto cleanup;
2978 } else if (setting_enum_set(pset, args[1], caller,
2979 reject_msg, sizeof(reject_msg))) {
2980 do_update = TRUE;
2981 } else {
2982 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2983 goto cleanup;
2985 break;
2987 case SSET_BITWISE:
2988 if (check) {
2989 if (!setting_is_changeable(pset, caller, reject_msg,
2990 sizeof(reject_msg))
2991 || (!setting_bitwise_validate(pset, args[1], caller,
2992 reject_msg, sizeof(reject_msg)))) {
2993 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2994 goto cleanup;
2996 } else if (setting_bitwise_set(pset, args[1], caller,
2997 reject_msg, sizeof(reject_msg))) {
2998 do_update = TRUE;
2999 } else {
3000 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
3001 goto cleanup;
3003 break;
3006 ret = TRUE; /* Looks like a success. */
3008 if (!check && do_update) {
3009 /* Send only to connections able to see that. */
3010 char buf[256];
3011 struct packet_chat_msg packet;
3013 package_event(&packet, NULL, E_SETTING, ftc_server,
3014 _("Console: '%s' has been set to %s."), setting_name(pset),
3015 setting_value_name(pset, TRUE, buf, sizeof(buf)));
3016 conn_list_iterate(game.est_connections, pconn) {
3017 if (setting_is_visible(pset, pconn)) {
3018 send_packet_chat_msg(pconn, &packet);
3020 } conn_list_iterate_end;
3021 /* Notify the console. */
3022 con_write(C_OK, "%s", packet.message);
3024 setting_changed(pset);
3025 setting_action(pset);
3026 send_server_setting(NULL, pset);
3028 * send any modified game parameters to the clients -- if sent
3029 * before S_S_RUNNING, triggers a popdown_races_dialog() call
3030 * in client/packhand.c#handle_game_info()
3032 send_game_info(NULL);
3033 reset_all_start_commands(FALSE);
3034 send_server_info_to_metaserver(META_INFO);
3037 cleanup:
3038 free_tokens(args, nargs);
3039 return ret;
3042 /**************************************************************************
3043 Check game.allow_take for permission to take or observe a player.
3045 NB: If this function returns FALSE, then callers expect that 'msg' will
3046 be filled in with a NULL-terminated string containing the reason.
3047 **************************************************************************/
3048 static bool is_allowed_to_take(struct player *pplayer, bool will_obs,
3049 char *msg, size_t msg_len)
3051 const char *allow;
3053 if (!pplayer && will_obs) {
3054 /* Global observer. */
3055 if (!(allow = strchr(game.server.allow_take,
3056 (game.info.is_new_game ? 'O' : 'o')))) {
3057 fc_strlcpy(msg, _("Sorry, one can't observe globally in this game."),
3058 msg_len);
3059 return FALSE;
3061 } else if (!pplayer && !will_obs) {
3062 /* Auto-taking a new player */
3064 if (game_was_started()) {
3065 fc_strlcpy(msg, _("You cannot take a new player at this time."),
3066 msg_len);
3067 return FALSE;
3070 if (normal_player_count() >= game.server.max_players) {
3071 fc_snprintf(msg, msg_len,
3072 /* TRANS: Do not translate "maxplayers". */
3073 PL_("You cannot take a new player because "
3074 "the maximum of %d player has already "
3075 "been reached (maxplayers setting).",
3076 "You cannot take a new player because "
3077 "the maximum of %d players has already "
3078 "been reached (maxplayers setting).",
3079 game.server.max_players),
3080 game.server.max_players);
3081 return FALSE;
3084 if (player_count() >= player_slot_count()) {
3085 fc_strlcpy(msg, _("You cannot take a new player because there "
3086 "are no free player slots."),
3087 msg_len);
3088 return FALSE;
3091 return TRUE;
3093 } else if (is_barbarian(pplayer)) {
3094 if (!(allow = strchr(game.server.allow_take, 'b'))) {
3095 if (will_obs) {
3096 fc_strlcpy(msg,
3097 _("Sorry, one can't observe barbarians in this game."),
3098 msg_len);
3099 } else {
3100 fc_strlcpy(msg, _("Sorry, one can't take barbarians in this game."),
3101 msg_len);
3103 return FALSE;
3105 } else if (!pplayer->is_alive) {
3106 if (!(allow = strchr(game.server.allow_take, 'd'))) {
3107 if (will_obs) {
3108 fc_strlcpy(msg,
3109 _("Sorry, one can't observe dead players in this game."),
3110 msg_len);
3111 } else {
3112 fc_strlcpy(msg,
3113 _("Sorry, one can't take dead players in this game."),
3114 msg_len);
3116 return FALSE;
3118 } else if (is_ai(pplayer)) {
3119 if (!(allow = strchr(game.server.allow_take,
3120 (game.info.is_new_game ? 'A' : 'a')))) {
3121 if (will_obs) {
3122 fc_strlcpy(msg,
3123 _("Sorry, one can't observe AI players in this game."),
3124 msg_len);
3125 } else {
3126 fc_strlcpy(msg, _("Sorry, one can't take AI players in this game."),
3127 msg_len);
3129 return FALSE;
3131 } else {
3132 if (!(allow = strchr(game.server.allow_take,
3133 (game.info.is_new_game ? 'H' : 'h')))) {
3134 if (will_obs) {
3135 fc_strlcpy(msg,
3136 _("Sorry, one can't observe human players in this game."),
3137 msg_len);
3138 } else {
3139 fc_strlcpy(msg,
3140 _("Sorry, one can't take human players in this game."),
3141 msg_len);
3143 return FALSE;
3147 allow++;
3149 if (will_obs && (*allow == '2' || *allow == '3')) {
3150 fc_strlcpy(msg, _("Sorry, one can't observe in this game."), msg_len);
3151 return FALSE;
3154 if (!will_obs && *allow == '4') {
3155 fc_strlcpy(msg, _("Sorry, one can't take players in this game."),
3156 MAX_LEN_MSG);
3157 return FALSE;
3160 if (!will_obs && pplayer->is_connected
3161 && (*allow == '1' || *allow == '3')) {
3162 fc_strlcpy(msg, _("Sorry, one can't take players already "
3163 "connected in this game."), msg_len);
3164 return FALSE;
3167 return TRUE;
3170 /**************************************************************************
3171 Observe another player. If we were already attached, detach
3172 (see connection_detach()). The console and those with ALLOW_HACK can
3173 use the two-argument command and force others to observe.
3174 **************************************************************************/
3175 static bool observe_command(struct connection *caller, char *str, bool check)
3177 int i = 0, ntokens = 0;
3178 char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
3179 bool is_newgame = !game_was_started();
3180 enum m_pre_result result;
3181 struct connection *pconn = NULL;
3182 struct player *pplayer = NULL;
3183 bool res = FALSE;
3185 /******** PART I: fill pconn and pplayer ********/
3187 sz_strlcpy(buf, str);
3188 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
3190 /* check syntax, only certain syntax if allowed depending on the caller */
3191 if (!caller && ntokens < 1) {
3192 cmd_reply(CMD_OBSERVE, caller, C_SYNTAX, _("Usage:\n%s"),
3193 command_synopsis(command_by_number(CMD_OBSERVE)));
3194 goto end;
3197 if (ntokens == 2 && (caller && caller->access_level != ALLOW_HACK)) {
3198 cmd_reply(CMD_OBSERVE, caller, C_SYNTAX,
3199 _("Only the player name form is allowed."));
3200 goto end;
3203 /* match connection if we're console, match a player if we're not */
3204 if (ntokens == 1) {
3205 if (!caller && !(pconn = conn_by_user_prefix(arg[0], &result))) {
3206 cmd_reply_no_such_conn(CMD_OBSERVE, caller, arg[0], result);
3207 goto end;
3208 } else if (caller
3209 && !(pplayer = player_by_name_prefix(arg[0], &result))) {
3210 cmd_reply_no_such_player(CMD_OBSERVE, caller, arg[0], result);
3211 goto end;
3215 /* get connection name then player name */
3216 if (ntokens == 2) {
3217 if (!(pconn = conn_by_user_prefix(arg[0], &result))) {
3218 cmd_reply_no_such_conn(CMD_OBSERVE, caller, arg[0], result);
3219 goto end;
3221 if (!(pplayer = player_by_name_prefix(arg[1], &result))) {
3222 cmd_reply_no_such_player(CMD_OBSERVE, caller, arg[1], result);
3223 goto end;
3227 /* if we can't force other connections to observe, assign us to be pconn. */
3228 if (!pconn) {
3229 pconn = caller;
3232 /* if we have no pplayer, it means that we want to be a global observer */
3234 /******** PART II: do the observing ********/
3236 /* check allowtake for permission */
3237 if (!is_allowed_to_take(pplayer, TRUE, msg, sizeof(msg))) {
3238 cmd_reply(CMD_OBSERVE, caller, C_FAIL, "%s", msg);
3239 goto end;
3242 /* observing your own player (during pregame) makes no sense. */
3243 if (NULL != pplayer
3244 && pplayer == pconn->playing
3245 && !pconn->observer
3246 && is_newgame
3247 && !pplayer->was_created) {
3248 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3249 _("%s already controls %s. Using 'observe' would remove %s"),
3250 pconn->username,
3251 player_name(pplayer),
3252 player_name(pplayer));
3253 goto end;
3256 /* attempting to observe a player you're already observing should fail. */
3257 if (pplayer == pconn->playing && pconn->observer) {
3258 if (pplayer) {
3259 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3260 _("%s is already observing %s."),
3261 pconn->username,
3262 player_name(pplayer));
3263 } else {
3264 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3265 _("%s is already observing."),
3266 pconn->username);
3268 goto end;
3271 res = TRUE; /* all tests passed */
3272 if (check) {
3273 goto end;
3276 /* if the connection is already attached to a player,
3277 * unattach and cleanup old player (rename, remove, etc) */
3278 if (TRUE) {
3279 char name[MAX_LEN_NAME];
3281 if (pplayer) {
3282 /* if pconn->playing is removed, we'll lose pplayer */
3283 sz_strlcpy(name, player_name(pplayer));
3286 connection_detach(pconn, TRUE);
3288 if (pplayer) {
3289 /* find pplayer again, the pointer might have been changed */
3290 pplayer = player_by_name(name);
3294 /* attach pconn to new player as an observer or as global observer */
3295 if ((res = connection_attach(pconn, pplayer, TRUE))) {
3296 if (pplayer) {
3297 cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes %s"),
3298 pconn->username,
3299 player_name(pplayer));
3300 } else {
3301 cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes"),
3302 pconn->username);
3306 end:;
3307 /* free our args */
3308 for (i = 0; i < ntokens; i++) {
3309 free(arg[i]);
3311 return res;
3314 /**************************************************************************
3315 Take over a player. If a connection already has control of that player,
3316 disallow it.
3318 If there are two arguments, treat the first as the connection name and the
3319 second as the player name (only hack and the console can do this).
3320 Otherwise, there should be one argument, that being the player that the
3321 caller wants to take.
3322 **************************************************************************/
3323 static bool take_command(struct connection *caller, char *str, bool check)
3325 int i = 0, ntokens = 0;
3326 char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
3327 bool is_newgame = !game_was_started();
3328 enum m_pre_result match_result;
3329 struct connection *pconn = caller;
3330 struct player *pplayer = NULL;
3331 bool res = FALSE;
3333 /******** PART I: fill pconn and pplayer ********/
3335 sz_strlcpy(buf, str);
3336 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
3338 /* check syntax */
3339 if (!caller && ntokens != 2) {
3340 cmd_reply(CMD_TAKE, caller, C_SYNTAX, _("Usage:\n%s"),
3341 command_synopsis(command_by_number(CMD_TAKE)));
3342 goto end;
3345 if (caller && caller->access_level != ALLOW_HACK && ntokens != 1) {
3346 cmd_reply(CMD_TAKE, caller, C_SYNTAX,
3347 _("Only the player name form is allowed."));
3348 goto end;
3351 if (ntokens == 0) {
3352 cmd_reply(CMD_TAKE, caller, C_SYNTAX, _("Usage:\n%s"),
3353 command_synopsis(command_by_number(CMD_TAKE)));
3354 goto end;
3357 if (ntokens == 2) {
3358 if (!(pconn = conn_by_user_prefix(arg[i], &match_result))) {
3359 cmd_reply_no_such_conn(CMD_TAKE, caller, arg[i], match_result);
3360 goto end;
3362 i++; /* found a conn, now reference the second argument */
3365 if (strcmp(arg[i], "-") == 0) {
3366 if (!is_newgame) {
3367 cmd_reply(CMD_TAKE, caller, C_FAIL,
3368 _("You cannot issue \"/take -\" when "
3369 "the game has already started."));
3370 goto end;
3373 /* Find first uncontrolled player. This will return NULL if there is
3374 * no free players at the moment. Later call to
3375 * connection_attach() will create new player for such NULL
3376 * cases. */
3377 pplayer = find_uncontrolled_player();
3378 if (pplayer) {
3379 /* Make it human! */
3380 set_as_human(pplayer);
3382 } else if (!(pplayer = player_by_name_prefix(arg[i], &match_result))) {
3383 cmd_reply_no_such_player(CMD_TAKE, caller, arg[i], match_result);
3384 goto end;
3387 /******** PART II: do the attaching ********/
3389 /* Take not possible if the player is involved in a delegation (either
3390 * it's being controlled, or it's been put aside by the delegate). */
3391 if (player_delegation_active(pplayer)) {
3392 cmd_reply(CMD_TAKE, caller, C_FAIL, _("A delegation is active for player "
3393 "'%s'. /take not possible."),
3394 player_name(pplayer));
3395 goto end;
3398 /* check allowtake for permission */
3399 if (!is_allowed_to_take(pplayer, FALSE, msg, sizeof(msg))) {
3400 cmd_reply(CMD_TAKE, caller, C_FAIL, "%s", msg);
3401 goto end;
3404 /* taking your own player makes no sense. */
3405 if ((NULL != pplayer && !pconn->observer && pplayer == pconn->playing)
3406 || (NULL == pplayer && !pconn->observer && NULL != pconn->playing)) {
3407 cmd_reply(CMD_TAKE, caller, C_FAIL, _("%s already controls %s."),
3408 pconn->username,
3409 player_name(pconn->playing));
3410 goto end;
3413 /* Make sure there is free player slot if there is need to
3414 * create new player. This is necessary for previously
3415 * detached connections only. Others can reuse the slot
3416 * they first release. */
3417 if (!pplayer && !pconn->playing
3418 && (normal_player_count() >= game.server.max_players
3419 || normal_player_count() >= server.playable_nations)) {
3420 cmd_reply(CMD_TAKE, caller, C_FAIL,
3421 _("There is no free player slot for %s."),
3422 pconn->username);
3423 goto end;
3425 fc_assert_action(player_count() <= player_slot_count(), goto end);
3427 res = TRUE;
3428 if (check) {
3429 goto end;
3432 /* If the player is controlled by another user, forcibly detach
3433 * the user. */
3434 if (pplayer && pplayer->is_connected) {
3435 if (NULL == caller) {
3436 notify_conn(NULL, NULL, E_CONNECTION, ftc_server,
3437 _("Reassigned nation to %s by server console."),
3438 pconn->username);
3439 } else {
3440 notify_conn(NULL, NULL, E_CONNECTION, ftc_server,
3441 _("Reassigned nation to %s by %s."),
3442 pconn->username,
3443 caller->username);
3446 /* We are reassigning this nation, so we need to detach the current
3447 * user to set a new one. */
3448 conn_list_iterate(pplayer->connections, aconn) {
3449 if (!aconn->observer) {
3450 connection_detach(aconn, FALSE);
3452 } conn_list_iterate_end;
3455 /* if the connection is already attached to another player,
3456 * unattach and cleanup old player (rename, remove, etc)
3457 * We may have been observing the player we now want to take */
3458 if (NULL != pconn->playing || pconn->observer) {
3459 char name[MAX_LEN_NAME];
3461 if (pplayer) {
3462 /* if pconn->playing is removed, we'll lose pplayer */
3463 sz_strlcpy(name, player_name(pplayer));
3466 connection_detach(pconn, TRUE);
3468 if (pplayer) {
3469 /* find pplayer again; the pointer might have been changed */
3470 pplayer = player_by_name(name);
3474 /* Now attach to new player */
3475 if ((res = connection_attach(pconn, pplayer, FALSE))) {
3476 /* Successfully attached */
3477 pplayer = pconn->playing; /* In case pplayer was NULL. */
3479 /* inform about the status before changes */
3480 cmd_reply(CMD_TAKE, caller, C_OK, _("%s now controls %s (%s, %s)."),
3481 pconn->username,
3482 player_name(pplayer),
3483 is_barbarian(pplayer)
3484 ? _("Barbarian")
3485 : is_ai(pplayer)
3486 ? _("AI")
3487 : _("Human"),
3488 pplayer->is_alive
3489 ? _("Alive")
3490 : _("Dead"));
3491 } else {
3492 cmd_reply(CMD_TAKE, caller, C_FAIL,
3493 _("%s failed to attach to any player."),
3494 pconn->username);
3497 end:;
3498 /* free our args */
3499 for (i = 0; i < ntokens; i++) {
3500 free(arg[i]);
3502 return res;
3505 /**************************************************************************
3506 Detach from a player. if that player wasn't /created and you were
3507 controlling the player, remove it (and then detach any observers as well).
3509 If called for a global observer connection (where pconn->playing is NULL)
3510 then it will correctly detach from observing mode.
3511 **************************************************************************/
3512 static bool detach_command(struct connection *caller, char *str, bool check)
3514 int i = 0, ntokens = 0;
3515 char buf[MAX_LEN_CONSOLE_LINE], *arg[1];
3516 enum m_pre_result match_result;
3517 struct connection *pconn = NULL;
3518 struct player *pplayer = NULL;
3519 bool res = FALSE;
3521 sz_strlcpy(buf, str);
3522 ntokens = get_tokens(buf, arg, 1, TOKEN_DELIMITERS);
3524 if (!caller && ntokens == 0) {
3525 cmd_reply(CMD_DETACH, caller, C_SYNTAX, _("Usage:\n%s"),
3526 command_synopsis(command_by_number(CMD_DETACH)));
3527 goto end;
3530 /* match the connection if the argument was given */
3531 if (ntokens == 1
3532 && !(pconn = conn_by_user_prefix(arg[0], &match_result))) {
3533 cmd_reply_no_such_conn(CMD_DETACH, caller, arg[0], match_result);
3534 goto end;
3537 /* if no argument is given, the caller wants to detach himself */
3538 if (!pconn) {
3539 pconn = caller;
3542 /* if pconn and caller are not the same, only continue
3543 * if we're console, or we have ALLOW_HACK */
3544 if (pconn != caller && caller && caller->access_level != ALLOW_HACK) {
3545 cmd_reply(CMD_DETACH, caller, C_FAIL,
3546 _("You can not detach other users."));
3547 goto end;
3550 pplayer = pconn->playing;
3552 /* must have someone to detach from... */
3553 if (!pplayer && !pconn->observer) {
3554 cmd_reply(CMD_DETACH, caller, C_FAIL,
3555 _("%s is not attached to any player."), pconn->username);
3556 goto end;
3559 res = TRUE;
3560 if (check) {
3561 goto end;
3564 if (pplayer) {
3565 cmd_reply(CMD_DETACH, caller, C_OK, _("%s detaching from %s"),
3566 pconn->username, player_name(pplayer));
3567 } else {
3568 cmd_reply(CMD_DETACH, caller, C_OK, _("%s no longer observing."),
3569 pconn->username);
3572 /* Actually do the detaching. */
3573 connection_detach(pconn, TRUE);
3575 /* The user explicitly wanted to detach, so if a player is marked for him,
3576 * reset its username. */
3577 players_iterate(aplayer) {
3578 if (0 == strncmp(aplayer->username, pconn->username, MAX_LEN_NAME)) {
3579 sz_strlcpy(aplayer->username, _(ANON_USER_NAME));
3580 aplayer->unassigned_user = TRUE;
3581 send_player_info_c(aplayer, NULL);
3583 } players_iterate_end;
3585 check_for_full_turn_done();
3587 end:
3588 fc_assert_ret_val(ntokens <= 1, FALSE);
3590 /* free our args */
3591 for (i = 0; i < ntokens; i++) {
3592 free(arg[i]);
3594 return res;
3597 /**************************************************************************
3598 Loads a file, complete with access checks and error messages sent back
3599 to the caller on failure.
3601 * caller is the connection requesting the load, or NULL for a
3602 command-line load. Error messages are sent back to the caller and
3603 an access check is done to make sure they are allowed to load.
3605 * filename is simply the name of the file to be loaded. This may be
3606 approximate; the function will look for appropriate suffixes and will
3607 check in the various directories to see if the file is found.
3609 * if check is set then only a test run is done and no actual loading
3610 is attempted.
3612 * The return value is true if the load succeeds, or would succeed;
3613 false if there's an error in the file or file name. Some errors
3614 in loading however could be unrecoverable (if the save game is
3615 legitimate but has inconsistencies) and would lead to a broken server
3616 afterwards.
3617 **************************************************************************/
3618 bool load_command(struct connection *caller, const char *filename, bool check,
3619 bool cmdline_load)
3621 struct timer *loadtimer, *uloadtimer;
3622 struct section_file *file;
3623 char arg[MAX_LEN_PATH];
3624 struct conn_list *global_observers;
3626 if (!filename || filename[0] == '\0') {
3627 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage:\n%s"),
3628 command_synopsis(command_by_number(CMD_LOAD)));
3629 return FALSE;
3631 if (S_S_INITIAL != server_state()) {
3632 cmd_reply(CMD_LOAD, caller, C_FAIL,
3633 _("Cannot load a game while another is running."));
3634 dlsend_packet_game_load(game.est_connections, TRUE, filename);
3635 return FALSE;
3637 if (!is_safe_filename(filename) && is_restricted(caller)) {
3638 cmd_reply(CMD_LOAD, caller, C_FAIL,
3639 _("Name \"%s\" disallowed for security reasons."),
3640 filename);
3641 return FALSE;
3645 /* it is a normal savegame or maybe a scenario */
3646 char testfile[MAX_LEN_PATH];
3647 const struct strvec *pathes[] = {
3648 get_save_dirs(), get_scenario_dirs(), NULL
3650 const char *exts[] = {
3651 "sav", "gz", "bz2", "xz", "sav.gz", "sav.bz2", "sav.xz", NULL
3653 const char **ext, *found = NULL;
3654 const struct strvec **path;
3656 if (cmdline_load) {
3657 /* Allow plain names being loaded with '--file' option, but not otherwise
3658 * (no loading of arbitrary files by unauthorized users)
3659 * Iterate through ALL paths to check for file with plain name before
3660 * looking any path with an extension, i.e., prefer plain name file
3661 * in later directory over file with extension in name in earlier
3662 * directory. */
3663 for (path = pathes; !found && *path; path++) {
3664 found = fileinfoname(*path, filename);
3665 if (found != NULL) {
3666 sz_strlcpy(arg, found);
3671 for (path = pathes; !found && *path; path++) {
3672 for (ext = exts; !found && *ext; ext++) {
3673 fc_snprintf(testfile, sizeof(testfile), "%s.%s", filename, *ext);
3674 found = fileinfoname(*path, testfile);
3675 if (found != NULL) {
3676 sz_strlcpy(arg, found);
3681 if (is_restricted(caller) && !found) {
3682 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Cannot find savegame or "
3683 "scenario with the name \"%s\"."), filename);
3684 return FALSE;
3687 if (!found) {
3688 sz_strlcpy(arg, filename);
3692 /* attempt to parse the file */
3694 if (!(file = secfile_load(arg, FALSE))) {
3695 log_error("Error loading savefile '%s': %s", arg, secfile_error());
3696 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"),
3697 arg);
3698 dlsend_packet_game_load(game.est_connections, TRUE, arg);
3699 return FALSE;
3702 if (check) {
3703 return TRUE;
3706 /* Detach current players, before we blow them away. */
3707 global_observers = conn_list_new();
3708 conn_list_iterate(game.est_connections, pconn) {
3709 if (pconn->playing != NULL) {
3710 connection_detach(pconn, TRUE);
3711 } else if (pconn->observer) {
3712 conn_list_append(global_observers, pconn);
3713 connection_detach(pconn, TRUE);
3715 } conn_list_iterate_end;
3717 player_info_freeze();
3719 /* Now free all game data. */
3720 server_game_free();
3721 server_game_init();
3723 loadtimer = timer_new(TIMER_CPU, TIMER_ACTIVE);
3724 timer_start(loadtimer);
3725 uloadtimer = timer_new(TIMER_USER, TIMER_ACTIVE);
3726 timer_start(uloadtimer);
3728 sz_strlcpy(srvarg.load_filename, arg);
3730 savegame_load(file);
3731 secfile_check_unused(file);
3732 secfile_destroy(file);
3734 log_verbose("Load time: %g seconds (%g apparent)",
3735 timer_read_seconds(loadtimer), timer_read_seconds(uloadtimer));
3736 timer_destroy(loadtimer);
3737 timer_destroy(uloadtimer);
3739 sanity_check();
3741 log_verbose("load_command() does send_rulesets()");
3742 conn_list_compression_freeze(game.est_connections);
3743 send_rulesets(game.est_connections);
3744 send_server_settings(game.est_connections);
3745 send_scenario_info(game.est_connections);
3746 send_scenario_description(game.est_connections);
3747 send_game_info(game.est_connections);
3748 conn_list_compression_thaw(game.est_connections);
3750 /* Send information about the new players. */
3751 player_info_thaw();
3752 send_player_diplstate_c(NULL, NULL);
3754 /* Everything seemed to load ok; spread the good news. */
3755 dlsend_packet_game_load(game.est_connections, TRUE, srvarg.load_filename);
3757 /* Attach connections to players. Currently, this applies only
3758 * to connections that have the same username as a player. */
3759 conn_list_iterate(game.est_connections, pconn) {
3760 players_iterate(pplayer) {
3761 if (strcmp(pconn->username, pplayer->username) == 0) {
3762 connection_attach(pconn, pplayer, FALSE);
3763 break;
3765 } players_iterate_end;
3766 } conn_list_iterate_end;
3768 /* Reattach global observers. */
3769 conn_list_iterate(global_observers, pconn) {
3770 if (NULL == pconn->playing) {
3771 /* May have been assigned to a player before. */
3772 connection_attach(pconn, NULL, TRUE);
3774 } conn_list_iterate_end;
3775 conn_list_destroy(global_observers);
3777 (void) aifill(game.info.aifill);
3779 achievements_iterate(pach) {
3780 players_iterate(pplayer) {
3781 struct packet_achievement_info pack;
3783 pack.id = achievement_index(pach);
3784 pack.gained = achievement_player_has(pach, pplayer);
3785 pack.first = (pach->first == pplayer);
3787 lsend_packet_achievement_info(pplayer->connections, &pack);
3788 } players_iterate_end;
3789 } achievements_iterate_end;
3791 return TRUE;
3794 /**************************************************************************
3795 Load rulesets from a given ruleset directory.
3797 Security: There are some rudimentary checks in load_rulesets() to see
3798 if this directory really is a viable ruleset directory. For public
3799 servers, we check against directory redirection (is_safe_filename) and
3800 other bad stuff in the directory name, and will only use directories
3801 inside the data directories.
3802 **************************************************************************/
3803 static bool set_rulesetdir(struct connection *caller, char *str, bool check,
3804 int read_recursion)
3806 char filename[512];
3807 const char *pfilename;
3809 if (NULL == str || '\0' == str[0]) {
3810 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3811 _("You must provide a ruleset name. Use \"/show ruleset\" to "
3812 "see what is the current ruleset."));
3813 return FALSE;
3815 if (game_was_started() || !map_is_empty()) {
3816 cmd_reply(CMD_RULESETDIR, caller, C_FAIL,
3817 _("This setting can't be modified after the game has started."));
3818 return FALSE;
3821 if (strcmp(str, game.server.rulesetdir) == 0) {
3822 cmd_reply(CMD_RULESETDIR, caller, C_COMMENT,
3823 _("Ruleset directory is already \"%s\""), str);
3824 return FALSE;
3827 if (is_restricted(caller)
3828 && (!is_safe_filename(str) || strchr(str, '.'))) {
3829 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3830 _("Name \"%s\" disallowed for security reasons."),
3831 str);
3832 return FALSE;
3835 fc_snprintf(filename, sizeof(filename), "%s", str);
3836 pfilename = fileinfoname(get_data_dirs(), filename);
3837 if (!pfilename) {
3838 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3839 _("Ruleset directory \"%s\" not found"), str);
3840 return FALSE;
3843 if (!check) {
3844 bool success = TRUE;
3845 char old[512];
3847 sz_strlcpy(old, game.server.rulesetdir);
3848 log_verbose("set_rulesetdir() does load_rulesets() with \"%s\"", str);
3849 sz_strlcpy(game.server.rulesetdir, str);
3851 /* load the ruleset (and game settings defined in the ruleset) */
3852 player_info_freeze();
3853 if (!load_rulesets(old, FALSE, TRUE, FALSE)) {
3854 success = FALSE;
3856 /* While loading of the requested ruleset failed, we might
3857 * have changed ruleset from third one to default. Handle
3858 * rest of the ruleset changing accordingly. */
3861 if (game.est_connections) {
3862 /* Now that the rulesets are loaded we immediately send updates to any
3863 * connected clients. */
3864 send_rulesets(game.est_connections);
3866 /* show ruleset summary and list changed values */
3867 show_ruleset_info(caller, CMD_RULESETDIR, check, read_recursion);
3868 player_info_thaw();
3870 if (success) {
3871 cmd_reply(CMD_RULESETDIR, caller, C_OK,
3872 _("Ruleset directory set to \"%s\""), str);
3873 } else {
3874 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3875 _("Failed loading rulesets from directory \"%s\", using \"%s\""),
3876 str, game.server.rulesetdir);
3879 return success;
3882 return TRUE;
3885 /****************************************************************************
3886 /ignore command handler.
3887 ****************************************************************************/
3888 static bool ignore_command(struct connection *caller, char *str, bool check)
3890 char buf[128];
3891 struct conn_pattern *ppattern;
3893 if (NULL == caller) {
3894 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3895 _("That would be rather silly, since you are not a player."));
3896 return FALSE;
3899 ppattern = conn_pattern_from_string(str, CPT_USER, buf, sizeof(buf));
3900 if (NULL == ppattern) {
3901 cmd_reply(CMD_IGNORE, caller, C_SYNTAX,
3902 _("%s. Try /help ignore"), buf);
3903 return FALSE;
3906 if (check) {
3907 conn_pattern_destroy(ppattern);
3908 return TRUE;
3911 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3912 conn_pattern_list_append(caller->server.ignore_list, ppattern);
3913 cmd_reply(CMD_IGNORE, caller, C_COMMENT,
3914 _("Added pattern %s as entry %d to your ignore list."),
3915 buf, conn_pattern_list_size(caller->server.ignore_list));
3917 return TRUE;
3920 /****************************************************************************
3921 /unignore command handler.
3922 ****************************************************************************/
3923 static bool unignore_command(struct connection *caller,
3924 char *str, bool check)
3926 char buf[128], *c;
3927 int first, last, n;
3929 if (!caller) {
3930 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3931 _("That would be rather silly, since you are not a player."));
3932 return FALSE;
3935 sz_strlcpy(buf, str);
3936 remove_leading_trailing_spaces(buf);
3938 n = conn_pattern_list_size(caller->server.ignore_list);
3939 if (n == 0) {
3940 cmd_reply(CMD_UNIGNORE, caller, C_FAIL, _("Your ignore list is empty."));
3941 return FALSE;
3944 /* Parse the range. */
3945 if ('\0' == buf[0]) {
3946 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3947 _("Missing range. Try /help unignore."));
3948 return FALSE;
3949 } else if ((c = strchr(buf, '-'))) {
3950 *c++ = '\0';
3951 if ('\0' == buf[0]) {
3952 first = 1;
3953 } else if (!str_to_int(buf, &first)) {
3954 *--c = '-';
3955 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3956 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3957 return FALSE;
3959 if ('\0' == *c) {
3960 last = n;
3961 } else if (!str_to_int(c, &last)) {
3962 *--c = '-';
3963 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3964 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3965 return FALSE;
3967 } else {
3968 if (!str_to_int(buf, &first)) {
3969 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3970 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3971 return FALSE;
3973 last = first;
3976 if (!(1 <= first && first <= last && last <= n)) {
3977 if (first == last) {
3978 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3979 _("Invalid entry number: %d."), first);
3980 } else {
3981 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3982 _("Invalid range: %d to %d."), first, last);
3984 return FALSE;
3987 if (check) {
3988 return TRUE;
3991 n = 1;
3992 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
3993 if (first <= n) {
3994 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3995 cmd_reply(CMD_UNIGNORE, caller, C_COMMENT,
3996 _("Removed pattern %s (entry %d) from your ignore list."),
3997 buf, n);
3998 conn_pattern_list_remove(caller->server.ignore_list, ppattern);
4000 n++;
4001 if (n > last) {
4002 break;
4004 } conn_pattern_list_iterate_end;
4006 return TRUE;
4009 /****************************************************************************
4010 /playercolor command handler.
4011 ****************************************************************************/
4012 static bool playercolor_command(struct connection *caller,
4013 char *str, bool check)
4015 enum m_pre_result match_result;
4016 struct player *pplayer;
4017 struct rgbcolor *prgbcolor = NULL;
4018 int ntokens = 0;
4019 char *token[2];
4020 bool ret = TRUE;
4022 ntokens = get_tokens(str, token, 2, TOKEN_DELIMITERS);
4024 if (ntokens != 2) {
4025 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4026 _("Two arguments needed. See '/help playercolor'."));
4027 ret = FALSE;
4028 goto cleanup;
4031 pplayer = player_by_name_prefix(token[0], &match_result);
4033 if (!pplayer) {
4034 cmd_reply_no_such_player(CMD_PLAYERCOLOR, caller, token[0], match_result);
4035 ret = FALSE;
4036 goto cleanup;
4040 const char *reason;
4041 if (!player_color_changeable(pplayer, &reason)) {
4042 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL, "%s", reason);
4043 ret = FALSE;
4044 goto cleanup;
4048 if (0 == fc_strcasecmp(token[1], "reset")) {
4049 if (!game_was_started()) {
4050 prgbcolor = NULL;
4051 } else {
4052 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL,
4053 _("Can only unset player color before game starts."));
4054 ret = FALSE;
4055 goto cleanup;
4057 } else if (!rgbcolor_from_hex(&prgbcolor, token[1])) {
4058 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4059 _("Invalid player color definition. See '/help playercolor'."));
4060 ret = FALSE;
4061 goto cleanup;
4064 if (prgbcolor != NULL) {
4065 players_iterate(pother) {
4066 if (pother != pplayer && pother->rgb != NULL
4067 && rgbcolors_are_equal(pother->rgb, prgbcolor)) {
4068 cmd_reply(CMD_PLAYERCOLOR, caller, C_WARNING,
4069 /* TRANS: "... [c0ffee] for Caesar ... to Hammurabi." */
4070 _("Warning: new color [%s] for %s is identical to %s."),
4071 player_color_ftstr(pother), player_name(pplayer),
4072 player_name(pother));
4074 } players_iterate_end;
4077 if (check) {
4078 goto cleanup;
4081 server_player_set_color(pplayer, prgbcolor);
4082 cmd_reply(CMD_PLAYERCOLOR, caller, C_OK,
4083 _("Color of player %s set to [%s]."), player_name(pplayer),
4084 player_color_ftstr(pplayer));
4086 cleanup:
4088 rgbcolor_destroy(prgbcolor);
4089 free_tokens(token, ntokens);
4091 return ret;
4094 /**************************************************************************
4095 Handle quit command
4096 **************************************************************************/
4097 static bool quit_game(struct connection *caller, bool check)
4099 if (!check) {
4100 cmd_reply(CMD_QUIT, caller, C_OK, _("Goodbye."));
4101 server_quit();
4103 return TRUE;
4106 /**************************************************************************
4107 Main entry point for "command input".
4108 **************************************************************************/
4109 bool handle_stdin_input(struct connection *caller, char *str)
4111 return handle_stdin_input_real(caller, str, FALSE, 0);
4114 /**************************************************************************
4115 Handle "command input", which could really come from stdin on console,
4116 or from client chat command, or read from file with -r, etc.
4117 caller == NULL means console, str is the input, which may optionally
4118 start with SERVER_COMMAND_PREFIX character.
4120 If check is TRUE, then do nothing, just check syntax.
4121 **************************************************************************/
4122 static bool handle_stdin_input_real(struct connection *caller, char *str,
4123 bool check, int read_recursion)
4125 char full_command[MAX_LEN_CONSOLE_LINE];
4126 char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE];
4127 char *cptr_s, *cptr_d;
4128 enum command_id cmd;
4129 enum cmdlevel level;
4131 /* Remove leading and trailing spaces, and server command prefix. */
4132 cptr_s = str = skip_leading_spaces(str);
4133 if ('\0' == *cptr_s || '#' == *cptr_s) {
4134 /* This appear to be a comment or blank line. */
4135 return FALSE;
4138 if (SERVER_COMMAND_PREFIX == *cptr_s) {
4139 /* Commands may be prefixed with SERVER_COMMAND_PREFIX, even when
4140 * given on the server command line. */
4141 cptr_s++;
4142 remove_leading_spaces(cptr_s);
4143 if ('\0' == *cptr_s) {
4144 /* This appear to be a blank line. */
4145 return FALSE;
4148 remove_trailing_spaces(cptr_s);
4150 /* notify to the server console */
4151 if (!check && caller) {
4152 con_write(C_COMMENT, "%s: '%s'", caller->username, str);
4155 /* if the caller may not use any commands at all, don't waste any time */
4156 if (may_use_nothing(caller)) {
4157 cmd_reply(CMD_HELP, caller, C_FAIL,
4158 _("Sorry, you are not allowed to use server commands."));
4159 return FALSE;
4162 /* copy the full command, in case we need it for voting purposes. */
4163 sz_strlcpy(full_command, cptr_s);
4166 * cptr_s points now to the beginning of the real command. It has
4167 * skipped leading whitespace, the SERVER_COMMAND_PREFIX and any
4168 * other non-alphanumeric characters.
4170 for (cptr_d = command; *cptr_s != '\0' && fc_isalnum(*cptr_s)
4171 && cptr_d < command + sizeof(command) - 1; cptr_s++, cptr_d++) {
4172 *cptr_d = *cptr_s;
4174 *cptr_d = '\0';
4176 /* cptr_s now contains the arguments. */
4177 sz_strlcpy(arg, skip_leading_spaces(cptr_s));
4179 cmd = command_named(command, FALSE);
4180 if (cmd == CMD_AMBIGUOUS) {
4181 cmd = command_named(command, TRUE);
4182 cmd_reply(cmd, caller, C_SYNTAX,
4183 _("Warning: '%s' interpreted as '%s', but it is ambiguous."
4184 " Try '%shelp'."),
4185 command, command_name_by_number(cmd), caller?"/":"");
4186 } else if (cmd == CMD_UNRECOGNIZED) {
4187 cmd_reply(cmd, caller, C_SYNTAX, _("Unknown command '%s%s'. "
4188 " Try '%shelp'."),
4189 caller ? "/" : "", command, caller ? "/" : "");
4190 return FALSE;
4193 level = command_level(command_by_number(cmd));
4195 if (conn_can_vote(caller, NULL) && level == ALLOW_CTRL
4196 && conn_get_access(caller) == ALLOW_BASIC && !check
4197 && !vote_would_pass_immediately(caller, cmd)) {
4198 struct vote *vote;
4199 bool caller_had_vote = (NULL != get_vote_by_caller(caller));
4201 /* Check if the vote command would succeed. If we already have a vote
4202 * going, cancel it in favour of the new vote command. You can only
4203 * have one vote at a time. This is done by vote_new(). */
4204 if (handle_stdin_input_real(caller, full_command, TRUE,
4205 read_recursion + 1)
4206 && (vote = vote_new(caller, arg, cmd))) {
4207 char votedesc[MAX_LEN_CONSOLE_LINE];
4208 const struct player *teamplr;
4209 const char *what;
4210 struct ft_color color;
4212 if (caller_had_vote) {
4213 cmd_reply(CMD_VOTE, caller, C_COMMENT,
4214 /* TRANS: "vote" as a process */
4215 _("Your new vote canceled your previous vote."));
4218 describe_vote(vote, votedesc, sizeof(votedesc));
4220 if (vote_is_team_only(vote)) {
4221 /* TRANS: "vote" as a process */
4222 what = _("New teamvote");
4223 teamplr = conn_get_player(caller);
4224 color = ftc_vote_team;
4225 } else {
4226 /* TRANS: "vote" as a process */
4227 what = _("New vote");
4228 teamplr = NULL;
4229 color = ftc_vote_public;
4231 notify_team(teamplr, NULL, E_VOTE_NEW, color,
4232 /* TRANS: "[New vote|New teamvote] (number 3)
4233 * by fred: proposed change" */
4234 _("%s (number %d) by %s: %s"), what,
4235 vote->vote_no, caller->username, votedesc);
4237 /* Vote on your own suggestion. */
4238 connection_vote(caller, vote, VOTE_YES);
4239 return TRUE;
4241 } else {
4242 cmd_reply(CMD_VOTE, caller, C_FAIL,
4243 /* TRANS: "vote" as a process */
4244 _("Your new vote (\"%s\") was not "
4245 "legal or was not recognized."), full_command);
4246 return FALSE;
4250 if (caller
4251 && !((check || vote_would_pass_immediately(caller, cmd))
4252 && conn_get_access(caller) >= ALLOW_BASIC
4253 && level == ALLOW_CTRL)
4254 && conn_get_access(caller) < level) {
4255 cmd_reply(cmd, caller, C_FAIL,
4256 _("You are not allowed to use this command."));
4257 return FALSE;
4260 if (!check) {
4261 struct conn_list *echo_list = NULL;
4262 bool echo_list_allocated = FALSE;
4264 switch (command_echo(command_by_number(cmd))) {
4265 case CMD_ECHO_NONE:
4266 break;
4267 case CMD_ECHO_ADMINS:
4268 conn_list_iterate(game.est_connections, pconn) {
4269 if (ALLOW_ADMIN <= conn_get_access(pconn)) {
4270 if (NULL == echo_list) {
4271 echo_list = conn_list_new();
4272 echo_list_allocated = TRUE;
4274 conn_list_append(echo_list, pconn);
4276 } conn_list_iterate_end;
4277 break;
4278 case CMD_ECHO_ALL:
4279 echo_list = game.est_connections;
4280 break;
4283 if (NULL != echo_list) {
4284 if (caller) {
4285 notify_conn(echo_list, NULL, E_SETTING, ftc_any,
4286 "%s: '%s %s'", caller->username, command, arg);
4287 } else {
4288 notify_conn(echo_list, NULL, E_SETTING, ftc_server_prompt,
4289 "%s: '%s %s'", _("(server prompt)"), command, arg);
4291 if (echo_list_allocated) {
4292 conn_list_destroy(echo_list);
4297 switch(cmd) {
4298 case CMD_REMOVE:
4299 return remove_player_command(caller, arg, check);
4300 case CMD_SAVE:
4301 return save_command(caller, arg, check);
4302 case CMD_SCENSAVE:
4303 return scensave_command(caller, arg, check);
4304 case CMD_LOAD:
4305 return load_command(caller, arg, check, FALSE);
4306 case CMD_METAPATCHES:
4307 return metapatches_command(caller, arg, check);
4308 case CMD_METAMESSAGE:
4309 return metamessage_command(caller, arg, check);
4310 case CMD_METACONN:
4311 return metaconnection_command(caller, arg, check);
4312 case CMD_METASERVER:
4313 return metaserver_command(caller, arg, check);
4314 case CMD_HELP:
4315 return show_help(caller, arg);
4316 case CMD_SRVID:
4317 return show_serverid(caller, arg);
4318 case CMD_LIST:
4319 return show_list(caller, arg);
4320 case CMD_AITOGGLE:
4321 return toggle_ai_command(caller, arg, check);
4322 case CMD_TAKE:
4323 return take_command(caller, arg, check);
4324 case CMD_OBSERVE:
4325 return observe_command(caller, arg, check);
4326 case CMD_DETACH:
4327 return detach_command(caller, arg, check);
4328 case CMD_CREATE:
4329 return create_command(caller, arg, check);
4330 case CMD_AWAY:
4331 return away_command(caller, check);
4332 case CMD_HANDICAPPED:
4333 case CMD_NOVICE:
4334 case CMD_EASY:
4335 case CMD_NORMAL:
4336 case CMD_HARD:
4337 case CMD_CHEATING:
4338 #ifdef FREECIV_DEBUG
4339 case CMD_EXPERIMENTAL:
4340 #endif
4341 return set_ai_level_named(caller, arg, command_name_by_number(cmd), check);
4342 case CMD_QUIT:
4343 return quit_game(caller, check);
4344 case CMD_CUT:
4345 return cut_client_connection(caller, arg, check);
4346 case CMD_SHOW:
4347 return show_command(caller, arg, check);
4348 case CMD_EXPLAIN:
4349 return explain_option(caller, arg, check);
4350 case CMD_DEBUG:
4351 return debug_command(caller, arg, check);
4352 case CMD_SET:
4353 return set_command(caller, arg, check);
4354 case CMD_TEAM:
4355 return team_command(caller, arg, check);
4356 case CMD_RULESETDIR:
4357 return set_rulesetdir(caller, arg, check, read_recursion);
4358 case CMD_WALL:
4359 return wall(arg, check);
4360 case CMD_CONNECTMSG:
4361 return connectmsg_command(caller, arg, check);
4362 case CMD_VOTE:
4363 return vote_command(caller, arg, check);
4364 case CMD_CANCELVOTE:
4365 return cancelvote_command(caller, arg, check);
4366 case CMD_READ_SCRIPT:
4367 return read_command(caller, arg, check, read_recursion);
4368 case CMD_WRITE_SCRIPT:
4369 return write_command(caller, arg, check);
4370 case CMD_RESET:
4371 return reset_command(caller, arg, check, read_recursion);
4372 case CMD_DEFAULT:
4373 return default_command(caller, arg, check);
4374 case CMD_LUA:
4375 return lua_command(caller, arg, check);
4376 case CMD_KICK:
4377 return kick_command(caller, arg, check);
4378 case CMD_DELEGATE:
4379 return delegate_command(caller, arg, check);
4380 case CMD_AICMD:
4381 return aicmd_command(caller, arg, check);
4382 case CMD_FCDB:
4383 return fcdb_command(caller, arg, check);
4384 case CMD_MAPIMG:
4385 return mapimg_command(caller, arg, check);
4386 case CMD_RFCSTYLE: /* see console.h for an explanation */
4387 if (!check) {
4388 con_set_style(!con_get_style());
4390 return TRUE;
4391 case CMD_CMDLEVEL:
4392 return cmdlevel_command(caller, arg, check);
4393 case CMD_FIRSTLEVEL:
4394 return firstlevel_command(caller, check);
4395 case CMD_TIMEOUT:
4396 return timeout_command(caller, arg, check);
4397 case CMD_START_GAME:
4398 return start_command(caller, check, FALSE);
4399 case CMD_END_GAME:
4400 return end_command(caller, arg, check);
4401 case CMD_SURRENDER:
4402 return surrender_command(caller, arg, check);
4403 case CMD_IGNORE:
4404 return ignore_command(caller, arg, check);
4405 case CMD_UNIGNORE:
4406 return unignore_command(caller, arg, check);
4407 case CMD_PLAYERCOLOR:
4408 return playercolor_command(caller, arg, check);
4409 case CMD_NUM:
4410 case CMD_UNRECOGNIZED:
4411 case CMD_AMBIGUOUS:
4412 break;
4414 /* should NEVER happen! */
4415 log_error("Unknown command variant: %d.", cmd);
4416 return FALSE;
4419 /**************************************************************************
4420 End the game immediately in a draw.
4421 **************************************************************************/
4422 static bool end_command(struct connection *caller, char *str, bool check)
4424 if (S_S_RUNNING == server_state()) {
4425 if (check) {
4426 return TRUE;
4428 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4429 _("Game is over."));
4430 set_server_state(S_S_OVER);
4431 force_end_of_sniff = TRUE;
4432 cmd_reply(CMD_END_GAME, caller, C_OK,
4433 _("Ending the game. The server will restart once all clients "
4434 "have disconnected."));
4435 return TRUE;
4436 } else {
4437 cmd_reply(CMD_END_GAME, caller, C_FAIL,
4438 _("Cannot end the game: no game running."));
4439 return FALSE;
4443 /**************************************************************************
4444 Concede the game. You still continue playing until all but one player
4445 or team remains un-conceded.
4446 **************************************************************************/
4447 static bool surrender_command(struct connection *caller, char *str, bool check)
4449 struct player *pplayer;
4451 if (caller == NULL || !conn_controls_player(caller)) {
4452 cmd_reply(CMD_SURRENDER, caller, C_FAIL,
4453 _("You are not allowed to use this command."));
4454 return FALSE;
4457 if (S_S_RUNNING != server_state()) {
4458 cmd_reply(CMD_SURRENDER, caller, C_FAIL, _("You cannot surrender now."));
4459 return FALSE;
4462 pplayer = conn_get_player(caller);
4463 if (player_status_check(pplayer, PSTATUS_SURRENDER)) {
4464 cmd_reply(CMD_SURRENDER, caller, C_FAIL,
4465 _("You have already conceded the game."));
4466 return FALSE;
4469 if (check) {
4470 return TRUE;
4473 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4474 _("%s has conceded the game and can no longer win."),
4475 player_name(pplayer));
4476 player_status_add(pplayer, PSTATUS_SURRENDER);
4477 return TRUE;
4480 /* Define the possible arguments to the reset command */
4481 #define SPECENUM_NAME reset_args
4482 #define SPECENUM_VALUE0 RESET_GAME
4483 #define SPECENUM_VALUE0NAME "game"
4484 #define SPECENUM_VALUE1 RESET_RULESET
4485 #define SPECENUM_VALUE1NAME "ruleset"
4486 #define SPECENUM_VALUE2 RESET_SCRIPT
4487 #define SPECENUM_VALUE2NAME "script"
4488 #define SPECENUM_VALUE3 RESET_DEFAULT
4489 #define SPECENUM_VALUE3NAME "default"
4490 #include "specenum_gen.h"
4492 /**************************************************************************
4493 Returns possible parameters for the reset command.
4494 **************************************************************************/
4495 static const char *reset_accessor(int i)
4497 i = CLIP(0, i, reset_args_max());
4498 return reset_args_name((enum reset_args) i);
4501 /**************************************************************************
4502 Reload the game settings from the ruleset and reload the init script if
4503 one was used.
4504 **************************************************************************/
4505 static bool reset_command(struct connection *caller, char *arg, bool check,
4506 int read_recursion)
4508 enum m_pre_result result;
4509 int ind;
4511 /* match the argument */
4512 result = match_prefix(reset_accessor, reset_args_max() + 1, 0,
4513 fc_strncasecmp, NULL, arg, &ind);
4515 switch (result) {
4516 case M_PRE_EXACT:
4517 case M_PRE_ONLY:
4518 /* we have a match */
4519 break;
4520 case M_PRE_AMBIGUOUS:
4521 case M_PRE_EMPTY:
4522 /* use 'ruleset' [1] if the game was not started; else use 'game' [2] */
4523 if (S_S_INITIAL == server_state() && game.info.is_new_game) {
4524 cmd_reply(CMD_RESET, caller, C_WARNING,
4525 _("Guessing argument 'ruleset'."));
4526 ind = RESET_RULESET;
4527 } else {
4528 cmd_reply(CMD_RESET, caller, C_WARNING,
4529 _("Guessing argument 'game'."));
4530 ind = RESET_GAME;
4532 break;
4533 case M_PRE_LONG:
4534 case M_PRE_FAIL:
4535 case M_PRE_LAST:
4536 cmd_reply(CMD_RESET, caller, C_FAIL,
4537 _("The valid arguments are: 'game', 'ruleset', 'script' "
4538 "or 'default'."));
4539 return FALSE;
4540 break;
4543 if (check) {
4544 return TRUE;
4547 switch (ind) {
4548 case RESET_GAME:
4549 if (!game.info.is_new_game) {
4550 if (settings_game_reset()) {
4551 cmd_reply(CMD_RESET, caller, C_OK,
4552 _("Reset all settings to the values at the game start."));
4553 } else {
4554 cmd_reply(CMD_RESET, caller, C_FAIL,
4555 _("No saved settings from the game start available."));
4556 return FALSE;
4558 } else {
4559 cmd_reply(CMD_RESET, caller, C_FAIL, _("No game started..."));
4560 return FALSE;
4562 break;
4564 case RESET_RULESET:
4565 /* Restore game settings save in game.ruleset. */
4566 if (reload_rulesets_settings()) {
4567 cmd_reply(CMD_RESET, caller, C_OK,
4568 _("Reset all settings to ruleset values."));
4569 } else {
4570 cmd_reply(CMD_RESET, caller, C_FAIL,
4571 _("Failed to reset settings to ruleset values."));
4573 break;
4575 case RESET_SCRIPT:
4576 cmd_reply(CMD_RESET, caller, C_OK,
4577 _("Reset all settings and rereading the server start "
4578 "script."));
4579 settings_reset();
4580 /* load initial script */
4581 if (NULL != srvarg.script_filename
4582 && !read_init_script_real(NULL, srvarg.script_filename, TRUE, FALSE,
4583 read_recursion + 1)) {
4584 if (NULL != caller) {
4585 cmd_reply(CMD_RESET, caller, C_FAIL,
4586 _("Could not read script file '%s'."),
4587 srvarg.script_filename);
4589 return FALSE;
4591 break;
4593 case RESET_DEFAULT:
4594 cmd_reply(CMD_RESET, caller, C_OK,
4595 _("Reset all settings to default values."));
4596 settings_reset();
4597 break;
4600 send_server_settings(game.est_connections);
4601 cmd_reply(CMD_RESET, caller, C_OK, _("Settings re-initialized."));
4603 /* show ruleset summary and list changed values */
4604 show_ruleset_info(caller, CMD_RESET, check, read_recursion);
4606 return TRUE;
4609 /**************************************************************************
4610 Set a setting to its default value
4611 **************************************************************************/
4612 static bool default_command(struct connection *caller, char *arg, bool check)
4614 struct setting *pset;
4615 char reject_msg[256] = "";
4617 pset = validate_setting_arg(CMD_DEFAULT, caller, arg);
4619 if (!pset) {
4620 /* Reason already reported. */
4621 return FALSE;
4624 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))) {
4625 cmd_reply(CMD_DEFAULT, caller, C_FAIL, "%s", reject_msg);
4627 return FALSE;
4630 if (!check) {
4631 setting_set_to_default(pset);
4632 cmd_reply(CMD_DEFAULT, caller, C_OK,
4633 _("Option '%s' reset to default value."), arg);
4636 return TRUE;
4639 /* Define the possible arguments to the delegation command */
4640 #define SPECENUM_NAME lua_args
4641 #define SPECENUM_VALUE0 LUA_CMD
4642 #define SPECENUM_VALUE0NAME "cmd"
4643 #define SPECENUM_VALUE1 LUA_FILE
4644 #define SPECENUM_VALUE1NAME "file"
4645 #include "specenum_gen.h"
4647 /*****************************************************************************
4648 Returns possible parameters for the reset command.
4649 *****************************************************************************/
4650 static const char *lua_accessor(int i)
4652 i = CLIP(0, i, lua_args_max());
4653 return lua_args_name((enum lua_args) i);
4656 /*****************************************************************************
4657 Evaluate a line of lua script or a lua script file.
4658 *****************************************************************************/
4659 static bool lua_command(struct connection *caller, char *arg, bool check)
4661 FILE *script_file;
4662 const char extension[] = ".lua", *real_filename = NULL;
4663 char luafile[4096], tilde_filename[4096];
4664 char *tokens[1], *luaarg = NULL;
4665 int ntokens, ind;
4666 enum m_pre_result result;
4667 bool ret = FALSE;
4669 ntokens = get_tokens(arg, tokens, 1, TOKEN_DELIMITERS);
4671 if (ntokens > 0) {
4672 /* match the argument */
4673 result = match_prefix(lua_accessor, lua_args_max() + 1, 0,
4674 fc_strncasecmp, NULL, tokens[0], &ind);
4676 switch (result) {
4677 case M_PRE_EXACT:
4678 case M_PRE_ONLY:
4679 /* We have a match */
4680 luaarg = arg + strlen(lua_args_name(ind));
4681 luaarg = skip_leading_spaces(luaarg);
4682 break;
4683 case M_PRE_EMPTY:
4684 /* Nothing. */
4685 break;
4686 case M_PRE_AMBIGUOUS:
4687 case M_PRE_LONG:
4688 case M_PRE_FAIL:
4689 case M_PRE_LAST:
4690 /* Fall back to depreciated 'lua <script command>' syntax. */
4691 cmd_reply(CMD_LUA, caller, C_SYNTAX,
4692 _("Fall back to old syntax '%slua <script command>'."),
4693 caller ? "/" : "");
4694 ind = LUA_CMD;
4695 luaarg = arg;
4696 break;
4700 if (luaarg == NULL) {
4701 cmd_reply(CMD_LUA, caller, C_FAIL,
4702 _("No lua command or lua script file. See '%shelp lua'."),
4703 caller ? "/" : "");
4704 ret = TRUE;
4705 goto cleanup;
4708 switch (ind) {
4709 case LUA_CMD:
4710 /* Nothing to check. */
4711 break;
4712 case LUA_FILE:
4713 /* Abuse real_filename to find if we already have a .lua extension. */
4714 real_filename = luaarg + strlen(luaarg) - MIN(strlen(extension),
4715 strlen(luaarg));
4716 if (strcmp(real_filename, extension) != 0) {
4717 fc_snprintf(luafile, sizeof(luafile), "%s%s", luaarg, extension);
4718 } else {
4719 sz_strlcpy(luafile, luaarg);
4722 if (is_restricted(caller)) {
4723 if (!is_safe_filename(luafile)) {
4724 cmd_reply(CMD_LUA, caller, C_FAIL,
4725 _("Freeciv script '%s' disallowed for security reasons."),
4726 luafile);
4727 ret = FALSE;
4728 goto cleanup;;
4730 sz_strlcpy(tilde_filename, luafile);
4731 } else {
4732 interpret_tilde(tilde_filename, sizeof(tilde_filename), luafile);
4735 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
4736 if (!real_filename) {
4737 if (is_restricted(caller)) {
4738 cmd_reply(CMD_LUA, caller, C_FAIL,
4739 _("No Freeciv script found by the name '%s'."),
4740 tilde_filename);
4741 ret = FALSE;
4742 goto cleanup;
4744 /* File is outside data directories */
4745 real_filename = tilde_filename;
4747 break;
4750 if (check) {
4751 ret = TRUE;
4752 goto cleanup;
4755 switch (ind) {
4756 case LUA_CMD:
4757 ret = script_server_do_string(caller, luaarg);
4758 break;
4759 case LUA_FILE:
4760 cmd_reply(CMD_LUA, caller, C_COMMENT,
4761 _("Loading Freeciv script file '%s'."), real_filename);
4763 if (is_reg_file_for_access(real_filename, FALSE)
4764 && (script_file = fc_fopen(real_filename, "r"))) {
4765 ret = script_server_do_file(caller, real_filename);
4766 goto cleanup;
4767 } else {
4768 cmd_reply(CMD_LUA, caller, C_FAIL,
4769 _("Cannot read Freeciv script '%s'."), real_filename);
4770 ret = FALSE;
4771 goto cleanup;
4775 cleanup:
4776 free_tokens(tokens, ntokens);
4777 return ret;
4780 /* Define the possible arguments to the delegation command */
4781 #define SPECENUM_NAME delegate_args
4782 #define SPECENUM_VALUE0 DELEGATE_CANCEL
4783 #define SPECENUM_VALUE0NAME "cancel"
4784 #define SPECENUM_VALUE1 DELEGATE_RESTORE
4785 #define SPECENUM_VALUE1NAME "restore"
4786 #define SPECENUM_VALUE2 DELEGATE_SHOW
4787 #define SPECENUM_VALUE2NAME "show"
4788 #define SPECENUM_VALUE3 DELEGATE_TAKE
4789 #define SPECENUM_VALUE3NAME "take"
4790 #define SPECENUM_VALUE4 DELEGATE_TO
4791 #define SPECENUM_VALUE4NAME "to"
4792 #include "specenum_gen.h"
4794 /*****************************************************************************
4795 Returns possible parameters for the 'delegate' command.
4796 *****************************************************************************/
4797 static const char *delegate_accessor(int i)
4799 i = CLIP(0, i, delegate_args_max());
4800 return delegate_args_name((enum delegate_args) i);
4803 /*****************************************************************************
4804 Handle delegation of control.
4805 *****************************************************************************/
4806 static bool delegate_command(struct connection *caller, char *arg,
4807 bool check)
4809 char *tokens[3];
4810 int ntokens, ind = delegate_args_invalid();
4811 enum m_pre_result result;
4812 bool player_specified = FALSE; /* affects messages only */
4813 bool ret = FALSE;
4814 const char *username = NULL;
4815 struct player *dplayer = NULL;
4817 if (!game_was_started()) {
4818 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Game not started - "
4819 "cannot delegate yet."));
4820 return FALSE;
4823 ntokens = get_tokens(arg, tokens, 3, TOKEN_DELIMITERS);
4825 if (ntokens > 0) {
4826 /* match the argument */
4827 result = match_prefix(delegate_accessor, delegate_args_max() + 1, 0,
4828 fc_strncasecmp, NULL, tokens[0], &ind);
4830 switch (result) {
4831 case M_PRE_EXACT:
4832 case M_PRE_ONLY:
4833 /* we have a match */
4834 break;
4835 case M_PRE_EMPTY:
4836 if (caller) {
4837 /* Use 'delegate show' as default. */
4838 ind = DELEGATE_SHOW;
4840 break;
4841 case M_PRE_AMBIGUOUS:
4842 case M_PRE_LONG:
4843 case M_PRE_FAIL:
4844 case M_PRE_LAST:
4845 ind = delegate_args_invalid();
4846 break;
4848 } else {
4849 if (caller) {
4850 /* Use 'delegate show' as default. */
4851 ind = DELEGATE_SHOW;
4855 if (!delegate_args_is_valid(ind)) {
4856 char buf[256] = "";
4857 enum delegate_args valid_args;
4859 for (valid_args = delegate_args_begin();
4860 valid_args != delegate_args_end();
4861 valid_args = delegate_args_next(valid_args)) {
4862 cat_snprintf(buf, sizeof(buf), "'%s'",
4863 delegate_args_name(valid_args));
4864 if (valid_args != delegate_args_max()) {
4865 cat_snprintf(buf, sizeof(buf), ", ");
4869 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4870 /* TRANS: do not translate the command 'delegate'. */
4871 _("Valid arguments for 'delegate' are: %s."), buf);
4872 ret = FALSE;
4873 goto cleanup;
4876 /* Get the data (player, username for delegation) and validate it. */
4877 switch (ind) {
4878 case DELEGATE_CANCEL:
4879 /* delegate cancel [player] */
4880 if (ntokens > 1) {
4881 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4882 player_specified = TRUE;
4883 dplayer = player_by_name_prefix(tokens[1], &result);
4884 if (!dplayer) {
4885 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4886 ret = FALSE;
4887 goto cleanup;
4889 } else {
4890 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4891 _("Command level '%s' or greater needed to modify "
4892 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4893 ret = FALSE;
4894 goto cleanup;
4896 } else {
4897 dplayer = conn_get_player(caller);
4898 if (!dplayer) {
4899 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4900 _("Please specify a player for whom delegation should "
4901 "be canceled."));
4902 ret = FALSE;
4903 goto cleanup;
4906 break;
4907 case DELEGATE_RESTORE:
4908 /* delegate restore */
4909 if (!caller) {
4910 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4911 _("You can't switch players from the console."));
4912 ret = FALSE;
4913 goto cleanup;
4915 break;
4916 case DELEGATE_SHOW:
4917 /* delegate show [player] */
4918 if (ntokens > 1) {
4919 player_specified = TRUE;
4920 dplayer = player_by_name_prefix(tokens[1], &result);
4921 if (!dplayer) {
4922 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4923 ret = FALSE;
4924 goto cleanup;
4926 } else {
4927 dplayer = conn_get_player(caller);
4928 if (!dplayer) {
4929 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4930 _("Please specify a player for whom the delegation should "
4931 "be shown."));
4932 ret = FALSE;
4933 goto cleanup;
4936 break;
4937 case DELEGATE_TAKE:
4938 /* delegate take <player> */
4939 if (!caller) {
4940 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4941 _("You can't switch players from the console."));
4942 ret = FALSE;
4943 goto cleanup;
4945 if (ntokens > 1) {
4946 player_specified = TRUE;
4947 dplayer = player_by_name_prefix(tokens[1], &result);
4948 if (!dplayer) {
4949 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4950 ret = FALSE;
4951 goto cleanup;
4953 } else {
4954 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4955 _("Please specify a player to take control of."));
4956 ret = FALSE;
4957 goto cleanup;
4959 break;
4960 case DELEGATE_TO:
4961 /* delegate to <username> [player] */
4962 if (ntokens > 1) {
4963 username = tokens[1];
4964 } else {
4965 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4966 _("Please specify a user to whom control is to be delegated."));
4967 ret = FALSE;
4968 goto cleanup;
4970 if (ntokens > 2) {
4971 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4972 player_specified = TRUE;
4973 dplayer = player_by_name_prefix(tokens[2], &result);
4974 if (!dplayer) {
4975 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[2], result);
4976 ret = FALSE;
4977 goto cleanup;
4979 } else {
4980 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4981 _("Command level '%s' or greater needed to modify "
4982 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4983 ret = FALSE;
4984 goto cleanup;
4986 } else {
4987 dplayer = conn_controls_player(caller) ? conn_get_player(caller) : NULL;
4988 if (!dplayer) {
4989 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4990 _("You do not control a player."));
4991 ret = FALSE;
4992 goto cleanup;
4995 break;
4998 /* All checks done to this point will give pretty much the same result at
4999 * any time. Checks after this point are more likely to vary over time. */
5000 if (check) {
5001 ret = TRUE;
5002 goto cleanup;
5005 switch (ind) {
5006 case DELEGATE_TO:
5007 /* Delegate control of player to another user. */
5008 fc_assert_ret_val(dplayer, FALSE);
5009 fc_assert_ret_val(username != NULL, FALSE);
5011 /* Forbid delegation of players already controlled by a delegate, and
5012 * those 'put aside' by a delegate.
5013 * For the former, if player is already under active delegate control,
5014 * we wouldn't handle the revocation that would be necessary if their
5015 * delegation changed; and the authority granted to delegates does not
5016 * include the ability to sub-delegate.
5017 * For the latter, allowing control of the 'put aside' player to be
5018 * delegated would break the invariant that whenever a user is connected,
5019 * they are attached to 'their' player. */
5020 if (player_delegation_active(dplayer)) {
5021 if (!player_delegation_get(dplayer)) {
5022 /* Attempting to change a 'put aside' player. Must be admin
5023 * or console. */
5024 fc_assert(player_specified);
5025 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5026 _("Can't delegate control of '%s' belonging to %s while "
5027 "they are controlling another player."),
5028 player_name(dplayer), dplayer->username);
5029 } else if (player_specified) {
5030 /* Admin or console attempting to change a controlled player. */
5031 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5032 _("Can't change delegation of '%s' while controlled by "
5033 "delegate %s."), player_name(dplayer), dplayer->username);
5034 } else {
5035 /* Caller must be the delegate. Give more specific message.
5036 * (We don't know if they thought they were delegating their
5037 * original or delegated player, but we don't allow either.) */
5038 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5039 _("You can't delegate control while you are controlling "
5040 "a delegated player yourself."));
5042 ret = FALSE;
5043 goto cleanup;
5046 /* Forbid delegation to player's original owner
5047 * (from above test we know that dplayer->username is the original now) */
5048 if (fc_strcasecmp(dplayer->username, username) == 0) {
5049 if (player_specified) {
5050 /* Probably admin or console. */
5051 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5052 /* TRANS: don't translate 'delegate cancel' */
5053 _("%s already owns '%s', so cannot also be delegate. "
5054 "Use '%sdelegate cancel' to cancel an existing "
5055 "delegation."),
5056 username, player_name(dplayer), caller?"/":"");
5057 } else {
5058 /* Player not specified on command line, so they must have been trying
5059 * to delegate control to themself. Give more specific message. */
5060 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5061 /* TRANS: don't translate '/delegate cancel' */
5062 _("You can't delegate control to yourself. "
5063 "Use '/delegate cancel' to cancel an existing "
5064 "delegation."));
5066 ret = FALSE;
5067 goto cleanup;
5070 /* FIXME: if control was already delegated to someone else, that
5071 * delegation is implicitly canceled. Perhaps we should tell someone. */
5073 player_delegation_set(dplayer, username);
5074 cmd_reply(CMD_DELEGATE, caller, C_OK,
5075 _("Control of player '%s' delegated to user %s."),
5076 player_name(dplayer), username);
5077 ret = TRUE;
5078 goto cleanup;
5079 break;
5081 case DELEGATE_SHOW:
5082 /* Show delegations. */
5083 fc_assert_ret_val(dplayer, FALSE);
5085 if (player_delegation_get(dplayer) == NULL) {
5086 /* No delegation set. */
5087 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5088 _("No delegation defined for '%s'."),
5089 player_name(dplayer));
5090 } else {
5091 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5092 _("Control of player '%s' delegated to user %s."),
5093 player_name(dplayer), player_delegation_get(dplayer));
5095 ret = TRUE;
5096 goto cleanup;
5097 break;
5099 case DELEGATE_CANCEL:
5100 if (player_delegation_get(dplayer) == NULL) {
5101 /* No delegation set. */
5102 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5103 _("No delegation defined for '%s'."),
5104 player_name(dplayer));
5105 ret = FALSE;
5106 goto cleanup;
5109 if (player_delegation_active(dplayer)) {
5110 /* Delegation is currently in use. Forcibly break connection. */
5111 struct connection *pdelegate;
5112 /* (Can only happen if admin/console issues this command, as owner
5113 * will end use by their mere presence.) */
5114 fc_assert(player_specified);
5115 pdelegate = conn_by_user(player_delegation_get(dplayer));
5116 fc_assert_ret_val(pdelegate != NULL, FALSE);
5117 if (!connection_delegate_restore(pdelegate)) {
5118 /* Should never happen. Generic failure message. */
5119 log_error("Failed to restore %s's connection as %s during "
5120 "'delegate cancel'.", pdelegate->username,
5121 delegate_player_str(pdelegate->server.delegation.playing,
5122 pdelegate->server.delegation.observer));
5123 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5124 ret = FALSE;
5125 goto cleanup;
5127 notify_conn(pdelegate->self, NULL, E_CONNECTION, ftc_server,
5128 _("Your delegated control of player '%s' was canceled."),
5129 player_name(dplayer));
5132 player_delegation_set(dplayer, NULL);
5133 cmd_reply(CMD_DELEGATE, caller, C_OK, _("Delegation of '%s' canceled."),
5134 player_name(dplayer));
5135 ret = TRUE;
5136 goto cleanup;
5137 break;
5139 case DELEGATE_TAKE:
5140 /* Try to take another player. */
5141 fc_assert_ret_val(dplayer, FALSE);
5142 fc_assert_ret_val(caller, FALSE);
5144 if (caller->server.delegation.status) {
5145 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5146 /* TRANS: don't translate '/delegate restore'. */
5147 _("You are already controlling a delegated player. "
5148 "Use '/delegate restore' to relinquish control of your "
5149 "current player first."));
5150 ret = FALSE;
5151 goto cleanup;
5154 /* Don't allow 'put aside' players to be delegated; the invariant is
5155 * that while the owning user is connected to the server, they are
5156 * in sole control of 'their' player. */
5157 if (conn_controls_player(caller)
5158 && player_delegation_get(conn_get_player(caller)) != NULL) {
5159 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5160 /* TRANS: don't translate '/delegate cancel'. */
5161 _("Can't take player while you have delegated control "
5162 "yourself. Use '/delegate cancel' to cancel your own "
5163 "delegation first."));
5164 ret = FALSE;
5165 goto cleanup;
5168 /* Taking your own player makes no sense. */
5169 if (conn_controls_player(caller)
5170 && dplayer == conn_get_player(caller)) {
5171 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("You already control '%s'."),
5172 player_name(conn_get_player(caller)));
5173 ret = FALSE;
5174 goto cleanup;
5177 if (!player_delegation_get(dplayer)
5178 || fc_strcasecmp(player_delegation_get(dplayer), caller->username) != 0) {
5179 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5180 _("Control of player '%s' has not been delegated to you."),
5181 player_name(dplayer));
5182 ret = FALSE;
5183 goto cleanup;
5186 /* If the player is controlled by another user, fail. */
5187 if (dplayer->is_connected) {
5188 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5189 _("Another user already controls player '%s'."),
5190 player_name(dplayer));
5191 ret = FALSE;
5192 goto cleanup;
5195 if (!connection_delegate_take(caller, dplayer)) {
5196 /* Should never happen. Generic failure message. */
5197 log_error("%s failed to take control of '%s' during 'delegate take'.",
5198 caller->username, player_name(dplayer));
5199 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5200 ret = FALSE;
5201 goto cleanup;
5204 cmd_reply(CMD_DELEGATE, caller, C_OK,
5205 _("%s is now controlling player '%s'."), caller->username,
5206 player_name(conn_get_player(caller)));
5207 ret = TRUE;
5208 goto cleanup;
5209 break;
5211 case DELEGATE_RESTORE:
5212 /* Delegate user relinquishes control of delegated player, returning to
5213 * previous view (e.g. observer) if any. */
5214 fc_assert_ret_val(caller, FALSE);
5216 if (!caller->server.delegation.status) {
5217 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5218 _("You are not currently controlling a delegated player."));
5219 ret = FALSE;
5220 goto cleanup;
5223 if (!connection_delegate_restore(caller)) {
5224 /* Should never happen. Generic failure message. */
5225 log_error("Failed to restore %s's connection as %s during "
5226 "'delegate restore'.", caller->username,
5227 delegate_player_str(caller->server.delegation.playing,
5228 caller->server.delegation.observer));
5229 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5230 ret = FALSE;
5231 goto cleanup;
5234 cmd_reply(CMD_DELEGATE, caller, C_OK,
5235 /* TRANS: "<user> is now connected to <player>" where <player>
5236 * can also be "global observer" or "nothing" */
5237 _("%s is now connected as %s."), caller->username,
5238 delegate_player_str(conn_get_player(caller), caller->observer));
5239 ret = TRUE;
5240 goto cleanup;
5241 break;
5244 cleanup:
5245 free_tokens(tokens, ntokens);
5246 return ret;
5249 /*****************************************************************************
5250 Return static string describing what a connection is connected to.
5251 *****************************************************************************/
5252 static const char *delegate_player_str(struct player *pplayer, bool observer)
5254 static struct astring buf;
5256 if (pplayer) {
5257 if (observer) {
5258 astr_set(&buf, _("%s (observer)"), player_name(pplayer));
5259 } else {
5260 astr_set(&buf, "%s", player_name(pplayer));
5262 } else if (observer) {
5263 astr_set(&buf, "%s", _("global observer"));
5264 } else {
5265 /* TRANS: in place of player name or "global observer" */
5266 astr_set(&buf, "%s", _("nothing"));
5269 return astr_str(&buf);
5272 /* Define the possible arguments to the mapimg command */
5273 /* map image layers */
5274 #define SPECENUM_NAME mapimg_args
5275 #define SPECENUM_VALUE0 MAPIMG_COLORTEST
5276 #define SPECENUM_VALUE0NAME "colortest"
5277 #define SPECENUM_VALUE1 MAPIMG_CREATE
5278 #define SPECENUM_VALUE1NAME "create"
5279 #define SPECENUM_VALUE2 MAPIMG_DEFINE
5280 #define SPECENUM_VALUE2NAME "define"
5281 #define SPECENUM_VALUE3 MAPIMG_DELETE
5282 #define SPECENUM_VALUE3NAME "delete"
5283 #define SPECENUM_VALUE4 MAPIMG_SHOW
5284 #define SPECENUM_VALUE4NAME "show"
5285 #define SPECENUM_COUNT MAPIMG_COUNT
5286 #include "specenum_gen.h"
5288 /**************************************************************************
5289 Returns possible parameters for the mapimg command.
5290 **************************************************************************/
5291 static const char *mapimg_accessor(int i)
5293 i = CLIP(0, i, mapimg_args_max());
5294 return mapimg_args_name((enum mapimg_args) i);
5297 /**************************************************************************
5298 Handle mapimg command
5299 **************************************************************************/
5300 static bool mapimg_command(struct connection *caller, char *arg, bool check)
5302 enum m_pre_result result;
5303 int ind, ntokens, id;
5304 char *token[2];
5305 bool ret = TRUE;
5307 ntokens = get_tokens(arg, token, 2, TOKEN_DELIMITERS);
5309 if (ntokens > 0) {
5310 /* match the argument */
5311 result = match_prefix(mapimg_accessor, MAPIMG_COUNT, 0,
5312 fc_strncasecmp, NULL, token[0], &ind);
5314 switch (result) {
5315 case M_PRE_EXACT:
5316 case M_PRE_ONLY:
5317 /* we have a match */
5318 break;
5319 case M_PRE_AMBIGUOUS:
5320 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5321 _("Ambiguous 'mapimg' command."));
5322 ret = FALSE;
5323 goto cleanup;
5324 break;
5325 case M_PRE_EMPTY:
5326 /* use 'show' as default */
5327 ind = MAPIMG_SHOW;
5328 break;
5329 case M_PRE_LONG:
5330 case M_PRE_FAIL:
5331 case M_PRE_LAST:
5333 char buf[256] = "";
5334 enum mapimg_args valid_args;
5336 for (valid_args = mapimg_args_begin();
5337 valid_args != mapimg_args_end();
5338 valid_args = mapimg_args_next(valid_args)) {
5339 cat_snprintf(buf, sizeof(buf), "'%s'",
5340 mapimg_args_name(valid_args));
5341 if (valid_args != mapimg_args_max()) {
5342 cat_snprintf(buf, sizeof(buf), ", ");
5346 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5347 _("The valid arguments are: %s."), buf);
5348 ret = FALSE;
5349 goto cleanup;
5351 break;
5353 } else {
5354 /* use 'show' as default */
5355 ind = MAPIMG_SHOW;
5358 switch (ind) {
5359 case MAPIMG_DEFINE:
5360 if (ntokens == 1) {
5361 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5362 _("Missing argument for 'mapimg define'."));
5363 ret = FALSE;
5364 } else {
5365 /* 'mapimg define <mapstr>' */
5366 if (!mapimg_define(token[1], check)) {
5367 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5368 _("Can't use definition: %s."), mapimg_error());
5369 ret = FALSE;
5370 } else if (check) {
5371 /* Validated OK, bail out now */
5372 goto cleanup;
5373 } else if (game_was_started()
5374 && mapimg_isvalid(mapimg_count() - 1) == NULL) {
5375 /* game was started - error in map image definition check */
5376 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5377 _("Can't use definition: %s."), mapimg_error());
5378 ret = FALSE;
5379 } else {
5380 char str[MAX_LEN_MAPDEF];
5382 id = mapimg_count() - 1;
5384 mapimg_id2str(id, str, sizeof(str));
5385 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Defined as map image "
5386 "definition %d: '%s'."),
5387 id, str);
5390 break;
5392 case MAPIMG_DELETE:
5393 if (ntokens == 1) {
5394 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5395 _("Missing argument for 'mapimg delete'."));
5396 ret = FALSE;
5397 } else if (ntokens == 2 && strcmp(token[1], "all") == 0) {
5398 /* 'mapimg delete all' */
5399 if (check) {
5400 goto cleanup;
5403 while (mapimg_count() > 0) {
5404 mapimg_delete(0);
5406 cmd_reply(CMD_MAPIMG, caller, C_OK, _("All map image definitions "
5407 "deleted."));
5408 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5409 /* 'mapimg delete <id>' */
5410 if (check) {
5411 goto cleanup;
5414 if (!mapimg_delete(id)) {
5415 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5416 _("Couldn't delete definition: %s."), mapimg_error());
5417 ret = FALSE;
5418 } else {
5419 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map image definition %d "
5420 "deleted."), id);
5422 } else {
5423 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5424 _("Bad argument for 'mapimg delete': '%s'."), token[1]);
5425 ret = FALSE;
5427 break;
5429 case MAPIMG_SHOW:
5430 if (ntokens < 2 || (ntokens == 2 && strcmp(token[1], "all") == 0)) {
5431 /* 'mapimg show' or 'mapimg show all' */
5432 if (check) {
5433 goto cleanup;
5435 show_mapimg(caller, CMD_MAPIMG);
5436 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5437 char str[2048];
5438 /* 'mapimg show <id>' */
5439 if (check) {
5440 goto cleanup;
5443 if (mapimg_show(id, str, sizeof(str), TRUE)) {
5444 cmd_reply(CMD_MAPIMG, caller, C_OK, "%s", str);
5445 } else {
5446 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5447 _("Couldn't show definition: %s."), mapimg_error());
5448 ret = FALSE;
5450 } else {
5451 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5452 _("Bad argument for 'mapimg show': '%s'."), token[1]);
5453 ret = FALSE;
5455 break;
5457 case MAPIMG_COLORTEST:
5458 if (check) {
5459 goto cleanup;
5462 mapimg_colortest(game.server.save_name, NULL);
5463 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map color test images saved."));
5464 break;
5466 case MAPIMG_CREATE:
5467 if (ntokens < 2) {
5468 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5469 _("Missing argument for 'mapimg create'."));
5470 ret = FALSE;
5471 goto cleanup;
5474 if (strcmp(token[1], "all") == 0) {
5475 /* 'mapimg create all' */
5476 if (check) {
5477 goto cleanup;
5480 for (id = 0; id < mapimg_count(); id++) {
5481 struct mapdef *pmapdef = mapimg_isvalid(id);
5483 if (pmapdef == NULL
5484 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5485 srvarg.saves_pathname)) {
5486 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5487 _("Error saving map image %d: %s."), id, mapimg_error());
5488 ret = FALSE;
5491 } else if (sscanf(token[1], "%d", &id) != 0) {
5492 struct mapdef *pmapdef;
5494 /* 'mapimg create <id>' */
5495 if (check) {
5496 goto cleanup;
5499 pmapdef = mapimg_isvalid(id);
5500 if (pmapdef == NULL
5501 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5502 srvarg.saves_pathname)) {
5503 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5504 _("Error saving map image %d: %s."), id, mapimg_error());
5505 ret = FALSE;
5507 } else {
5508 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5509 _("Bad argument for 'mapimg create': '%s'."), token[1]);
5510 ret = FALSE;
5512 break;
5515 cleanup:
5517 free_tokens(token, ntokens);
5519 return ret;
5522 /*****************************************************************************
5523 Execute a command in the context of the AI of the player.
5524 *****************************************************************************/
5525 static bool aicmd_command(struct connection *caller, char *arg, bool check)
5527 enum m_pre_result match_result;
5528 struct player *pplayer;
5529 char *tokens[1], *cmd = NULL;
5530 int ntokens;
5531 bool ret = FALSE;
5533 ntokens = get_tokens(arg, tokens, 1, TOKEN_DELIMITERS);
5535 if (ntokens < 1) {
5536 cmd_reply(CMD_AICMD, caller, C_FAIL,
5537 _("No player given for aicmd."));
5538 goto cleanup;
5541 pplayer = player_by_name_prefix(tokens[0], &match_result);
5543 if (NULL == pplayer) {
5544 cmd_reply_no_such_player(CMD_AICMD, caller, tokens[0], match_result);
5545 goto cleanup;
5548 /* We have a player - extract the command. */
5549 cmd = arg + strlen(tokens[0]);
5550 cmd = skip_leading_spaces(cmd);
5552 if (strlen(cmd) == 0) {
5553 cmd_reply(CMD_AICMD, caller, C_FAIL,
5554 _("No command for the AI console defined."));
5555 goto cleanup;
5558 if (check) {
5559 ret = TRUE;
5560 goto cleanup;
5563 /* This check is needed to return a message if the function is not defined
5564 * for the AI of the player. */
5565 if (pplayer && pplayer->ai) {
5566 if (pplayer->ai->funcs.player_console) {
5567 cmd_reply(CMD_AICMD, caller, C_OK,
5568 _("AI console for player %s. Command: '%s'."),
5569 player_name(pplayer), cmd);
5570 CALL_PLR_AI_FUNC(player_console, pplayer, pplayer, cmd);
5571 ret = TRUE;
5572 } else {
5573 cmd_reply(CMD_AICMD, caller, C_FAIL,
5574 _("No AI console defined for the AI '%s' of player %s."),
5575 ai_name(pplayer->ai), player_name(pplayer));
5577 } else {
5578 cmd_reply(CMD_AICMD, caller, C_FAIL, _("No AI defined for player %s."),
5579 player_name(pplayer));
5582 cleanup:
5583 free_tokens(tokens, ntokens);
5584 return ret;
5587 /* Define the possible arguments to the fcdb command */
5588 #define SPECENUM_NAME fcdb_args
5589 #define SPECENUM_VALUE0 FCDB_RELOAD
5590 #define SPECENUM_VALUE0NAME "reload"
5591 #define SPECENUM_VALUE1 FCDB_LUA
5592 #define SPECENUM_VALUE1NAME "lua"
5593 #define SPECENUM_COUNT FCDB_COUNT
5594 #include "specenum_gen.h"
5596 /**************************************************************************
5597 Returns possible parameters for the fcdb command.
5598 **************************************************************************/
5599 static const char *fcdb_accessor(int i)
5601 i = CLIP(0, i, fcdb_args_max());
5602 return fcdb_args_name((enum fcdb_args) i);
5605 /**************************************************************************
5606 Handle the freeciv database script module.
5607 **************************************************************************/
5608 static bool fcdb_command(struct connection *caller, char *arg, bool check)
5610 enum m_pre_result result;
5611 int ind, ntokens;
5612 char *token[1];
5613 bool ret = TRUE;
5614 bool usage = FALSE;
5616 #ifndef HAVE_FCDB
5617 cmd_reply(CMD_FCDB, caller, C_FAIL,
5618 _("Freeciv database script deactivated at compile time."));
5619 return FALSE;
5620 #endif
5622 ntokens = get_tokens(arg, token, 1, TOKEN_DELIMITERS);
5624 if (ntokens > 0) {
5625 /* match the argument */
5626 result = match_prefix(fcdb_accessor, FCDB_COUNT, 0,
5627 fc_strncasecmp, NULL, token[0], &ind);
5629 switch (result) {
5630 case M_PRE_EXACT:
5631 case M_PRE_ONLY:
5632 /* we have a match */
5633 break;
5634 case M_PRE_AMBIGUOUS:
5635 cmd_reply(CMD_FCDB, caller, C_FAIL,
5636 _("Ambiguous fcdb command."));
5637 ret = FALSE;
5638 goto cleanup;
5639 break;
5640 case M_PRE_EMPTY:
5641 case M_PRE_LONG:
5642 case M_PRE_FAIL:
5643 case M_PRE_LAST:
5644 usage = TRUE;
5645 break;
5647 } else {
5648 usage = TRUE;
5651 if (usage) {
5652 char buf[256] = "";
5653 enum fcdb_args valid_args;
5655 for (valid_args = fcdb_args_begin();
5656 valid_args != fcdb_args_end();
5657 valid_args = fcdb_args_next(valid_args)) {
5658 cat_snprintf(buf, sizeof(buf), "'%s'",
5659 fcdb_args_name(valid_args));
5660 if (valid_args != fcdb_args_max()) {
5661 cat_snprintf(buf, sizeof(buf), ", ");
5665 cmd_reply(CMD_FCDB, caller, C_FAIL,
5666 _("The valid arguments are: %s."), buf);
5667 ret = FALSE;
5668 goto cleanup;
5671 if (check) {
5672 ret = TRUE;
5673 goto cleanup;
5676 switch (ind) {
5677 case FCDB_RELOAD:
5678 /* Reload database lua script. */
5679 script_fcdb_free();
5680 script_fcdb_init(NULL);
5681 break;
5683 case FCDB_LUA:
5684 /* Skip whitespaces. */
5685 arg = skip_leading_spaces(arg);
5686 /* Skip the base argument 'lua'. */
5687 arg += 3;
5688 /* Now execute the scriptlet. */
5689 ret = script_fcdb_do_string(caller, arg);
5690 break;
5693 cleanup:
5695 free_tokens(token, ntokens);
5697 return ret;
5700 /**************************************************************************
5701 Send start command related message
5702 **************************************************************************/
5703 static void start_cmd_reply(struct connection *caller, bool notify, char *msg)
5705 cmd_reply(CMD_START_GAME, caller, C_FAIL, "%s", msg);
5706 if (notify) {
5707 notify_conn(NULL, NULL, E_SETTING, ftc_server, "%s", msg);
5711 /**************************************************************************
5712 Handle start command. Notify all players about errors if notify set.
5713 **************************************************************************/
5714 bool start_command(struct connection *caller, bool check, bool notify)
5716 int human_players;
5718 switch (server_state()) {
5719 case S_S_INITIAL:
5720 /* Sanity check scenario */
5721 if (game.info.is_new_game && !check) {
5722 if (0 < map_startpos_count()
5723 && game.server.max_players > map_startpos_count()) {
5724 /* If we load a pre-generated map (i.e., a scenario) it is possible
5725 * to increase the number of players beyond the number supported by
5726 * the scenario. The solution is a hack: cut the extra players
5727 * when the game starts. */
5728 log_verbose("Reduced maxplayers from %d to %d to fit "
5729 "to the number of start positions.",
5730 game.server.max_players, map_startpos_count());
5731 game.server.max_players = map_startpos_count();
5734 if (normal_player_count() > game.server.max_players) {
5735 int i;
5736 struct player *pplayer;
5738 for (i = player_slot_count() - 1; i >= 0; i--) {
5739 pplayer = player_by_number(i);
5740 if (pplayer) {
5741 server_remove_player(pplayer);
5743 if (normal_player_count() <= game.server.max_players) {
5744 break;
5748 log_verbose("Had to cut down the number of players to the "
5749 "number of map start positions, there must be "
5750 "something wrong with the savegame or you "
5751 "adjusted the maxplayers value.");
5755 human_players = 0;
5756 players_iterate(plr) {
5757 if (is_human(plr)) {
5758 human_players++;
5760 } players_iterate_end;
5762 /* check min_players.
5763 * Allow continuing of savegames where some of the original
5764 * players have died */
5765 if (game.info.is_new_game
5766 && human_players < game.server.min_players) {
5767 char buf[512] = "";
5769 fc_snprintf(buf, sizeof(buf),
5770 _("Not enough human players ('minplayers' server setting has value %d); game will not start."),
5771 game.server.min_players);
5772 start_cmd_reply(caller, notify, buf);
5773 return FALSE;
5774 } else if (player_count() < 1) {
5775 /* At least one player required */
5776 start_cmd_reply(caller, notify,
5777 _("No players; game will not start."));
5778 return FALSE;
5779 } else if (normal_player_count() > server.playable_nations) {
5780 if (nation_set_count() > 1) {
5781 start_cmd_reply(caller, notify,
5782 _("Not enough nations in the current nation set "
5783 "for all players; game will not start. "
5784 "(See 'nationset' setting.)"));
5785 } else {
5786 start_cmd_reply(caller, notify,
5787 _("Not enough nations for all players; game will "
5788 "not start."));
5790 return FALSE;
5791 } else if (strlen(game.server.start_units) == 0 && !game.server.start_city) {
5792 start_cmd_reply(caller, notify,
5793 _("Neither 'startcity' nor 'startunits' setting gives "
5794 "players anything to start game with; game will "
5795 "not start."));
5796 return FALSE;
5797 } else if (check) {
5798 return TRUE;
5799 } else if (!caller) {
5800 if (notify) {
5801 /* Called from handle_player_ready()
5802 * Last player just toggled ready-status. */
5803 notify_conn(NULL, NULL, E_SETTING, ftc_game_start,
5804 _("All players are ready; starting game."));
5806 start_game();
5807 return TRUE;
5808 } else if (NULL == caller->playing || caller->observer) {
5809 /* A detached or observer player can't do /start. */
5810 return TRUE;
5811 } else {
5812 /* This might trigger recursive call to start_command() if this is
5813 * last player who gets ready. In that case caller is NULL. */
5814 handle_player_ready(caller->playing, player_number(caller->playing), TRUE);
5815 return TRUE;
5817 case S_S_OVER:
5818 start_cmd_reply(caller, notify,
5819 /* TRANS: given when /start is invoked during gameover. */
5820 _("Cannot start the game: the game is waiting for all clients "
5821 "to disconnect."));
5822 return FALSE;
5823 case S_S_RUNNING:
5824 start_cmd_reply(caller, notify,
5825 /* TRANS: given when /start is invoked while the game
5826 * is running. */
5827 _("Cannot start the game: it is already running."));
5828 return FALSE;
5830 log_error("Unknown server state variant: %d.", server_state());
5831 return FALSE;
5834 /**************************************************************************
5835 Handle cut command
5836 **************************************************************************/
5837 static bool cut_client_connection(struct connection *caller, char *name,
5838 bool check)
5840 enum m_pre_result match_result;
5841 struct connection *ptarget;
5843 ptarget = conn_by_user_prefix(name, &match_result);
5845 if (!ptarget) {
5846 cmd_reply_no_such_conn(CMD_CUT, caller, name, match_result);
5847 return FALSE;
5848 } else if (check) {
5849 return TRUE;
5852 if (conn_controls_player(ptarget)) {
5853 /* If we cut the connection, unassign the login name.*/
5854 sz_strlcpy(ptarget->playing->username, _(ANON_USER_NAME));
5855 ptarget->playing->unassigned_user = TRUE;
5858 cmd_reply(CMD_CUT, caller, C_DISCONNECTED,
5859 _("Cutting connection %s."), ptarget->username);
5860 connection_close_server(ptarget, _("connection cut"));
5862 return TRUE;
5866 /****************************************************************************
5867 Utility for 'kick_hash' tables.
5868 ****************************************************************************/
5869 static time_t *time_duplicate(const time_t *t)
5871 time_t *d = fc_malloc(sizeof(*d));
5872 *d = *t;
5873 return d;
5876 /****************************************************************************
5877 Returns FALSE if the connection isn't kicked and can connect the server
5878 normally.
5879 ****************************************************************************/
5880 bool conn_is_kicked(struct connection *pconn, int *time_remaining)
5882 time_t time_of_addr_kick, time_of_user_kick;
5883 time_t now, time_of_kick = 0;
5885 if (NULL != time_remaining) {
5886 *time_remaining = 0;
5889 fc_assert_ret_val(NULL != kick_table_by_addr, FALSE);
5890 fc_assert_ret_val(NULL != kick_table_by_user, FALSE);
5891 fc_assert_ret_val(NULL != pconn, FALSE);
5893 if (kick_hash_lookup(kick_table_by_addr, pconn->server.ipaddr,
5894 &time_of_addr_kick)) {
5895 time_of_kick = time_of_addr_kick;
5897 if (kick_hash_lookup(kick_table_by_user, pconn->username,
5898 &time_of_user_kick)
5899 && time_of_user_kick > time_of_kick) {
5900 time_of_kick = time_of_user_kick;
5903 if (0 == time_of_kick) {
5904 return FALSE; /* Not found. */
5907 now = time(NULL);
5908 if (now - time_of_kick > game.server.kick_time) {
5909 /* Kick timeout expired. */
5910 if (0 != time_of_addr_kick) {
5911 kick_hash_remove(kick_table_by_addr, pconn->server.ipaddr);
5913 if (0 != time_of_user_kick) {
5914 kick_hash_remove(kick_table_by_user, pconn->username);
5916 return FALSE;
5919 if (NULL != time_remaining) {
5920 *time_remaining = game.server.kick_time - (now - time_of_kick);
5922 return TRUE;
5925 /****************************************************************************
5926 Kick command handler.
5927 ****************************************************************************/
5928 static bool kick_command(struct connection *caller, char *name, bool check)
5930 char ipaddr[FC_MEMBER_SIZEOF(struct connection, server.ipaddr)];
5931 struct connection *pconn;
5932 enum m_pre_result match_result;
5933 time_t now;
5935 remove_leading_trailing_spaces(name);
5936 pconn = conn_by_user_prefix(name, &match_result);
5937 if (NULL == pconn) {
5938 cmd_reply_no_such_conn(CMD_KICK, caller, name, match_result);
5939 return FALSE;
5942 if (NULL != caller && ALLOW_ADMIN > conn_get_access(caller)) {
5943 const int MIN_UNIQUE_CONNS = 3;
5944 const char *unique_ipaddr[MIN_UNIQUE_CONNS];
5945 int i, num_unique_connections = 0;
5947 if (pconn == caller) {
5948 cmd_reply(CMD_KICK, caller, C_FAIL, _("You may not kick yourself."));
5949 return FALSE;
5952 conn_list_iterate(game.est_connections, aconn) {
5953 for (i = 0; i < num_unique_connections; i++) {
5954 if (0 == strcmp(unique_ipaddr[i], aconn->server.ipaddr)) {
5955 /* Already listed. */
5956 break;
5959 if (i >= num_unique_connections) {
5960 num_unique_connections++;
5961 if (MIN_UNIQUE_CONNS <= num_unique_connections) {
5962 /* We have enought already. */
5963 break;
5965 unique_ipaddr[num_unique_connections - 1] = aconn->server.ipaddr;
5967 } conn_list_iterate_end;
5969 if (MIN_UNIQUE_CONNS > num_unique_connections) {
5970 cmd_reply(CMD_KICK, caller, C_FAIL,
5971 _("There must be at least %d unique connections to the "
5972 "server for this command to be valid."), MIN_UNIQUE_CONNS);
5973 return FALSE;
5977 if (check) {
5978 return TRUE;
5981 sz_strlcpy(ipaddr, pconn->server.ipaddr);
5982 now = time(NULL);
5983 kick_hash_replace(kick_table_by_addr, ipaddr, now);
5985 conn_list_iterate(game.all_connections, aconn) {
5986 if (0 != strcmp(ipaddr, aconn->server.ipaddr)) {
5987 continue;
5990 if (conn_controls_player(aconn)) {
5991 /* Unassign the username. */
5992 sz_strlcpy(aconn->playing->username, _(ANON_USER_NAME));
5993 aconn->playing->unassigned_user = TRUE;
5996 kick_hash_replace(kick_table_by_user, aconn->username, now);
5998 connection_close_server(aconn, _("kicked"));
5999 } conn_list_iterate_end;
6001 return TRUE;
6005 /**************************************************************************
6006 Show caller introductory help about the server. help_cmd is the command
6007 the player used.
6008 **************************************************************************/
6009 static void show_help_intro(struct connection *caller,
6010 enum command_id help_cmd)
6012 /* This is formated like extra_help entries for settings and commands: */
6013 char *help = fc_strdup(
6014 _("Welcome - this is the introductory help text for the Freeciv "
6015 "server.\n"
6016 "\n"
6017 "Two important server concepts are Commands and Options. Commands, "
6018 "such as 'help', are used to interact with the server. Some commands "
6019 "take one or more arguments, separated by spaces. In many cases "
6020 "commands and command arguments may be abbreviated. Options are "
6021 "settings which control the server as it is running.\n"
6022 "\n"
6023 "To find out how to get more information about commands and options, "
6024 "use 'help help'.\n"
6025 "\n"
6026 "For the impatient, the main commands to get going are:\n"
6027 " show - to see current options\n"
6028 " set - to set options\n"
6029 " start - to start the game once players have connected\n"
6030 " save - to save the current game\n"
6031 " quit - to exit"));
6033 fc_break_lines(help, LINE_BREAK);
6034 cmd_reply(help_cmd, caller, C_COMMENT, "%s", help);
6035 FC_FREE(help);
6038 /**************************************************************************
6039 Show the caller detailed help for the single COMMAND given by id.
6040 help_cmd is the command the player used.
6041 **************************************************************************/
6042 static void show_help_command(struct connection *caller,
6043 enum command_id help_cmd,
6044 enum command_id id)
6046 const struct command *cmd = command_by_number(id);
6048 if (command_short_help(cmd)) {
6049 cmd_reply(help_cmd, caller, C_COMMENT,
6050 /* TRANS: <untranslated name> - translated short help */
6051 _("Command: %s - %s"),
6052 command_name(cmd),
6053 command_short_help(cmd));
6054 } else {
6055 cmd_reply(help_cmd, caller, C_COMMENT,
6056 /* TRANS: <untranslated name> */
6057 _("Command: %s"),
6058 command_name(cmd));
6060 if (command_synopsis(cmd)) {
6061 /* line up the synopsis lines: */
6062 const char *syn = _("Synopsis: ");
6063 size_t synlen = strlen(syn);
6064 char prefix[40];
6066 fc_snprintf(prefix, sizeof(prefix), "%*s", (int) synlen, " ");
6067 cmd_reply_prefix(help_cmd, caller, C_COMMENT, prefix,
6068 "%s%s", syn, command_synopsis(cmd));
6070 cmd_reply(help_cmd, caller, C_COMMENT,
6071 _("Level: %s"), cmdlevel_name(command_level(cmd)));
6073 char *help = command_extra_help(cmd);
6075 if (help) {
6076 fc_break_lines(help, LINE_BREAK);
6077 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
6078 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
6079 FC_FREE(help);
6084 /**************************************************************************
6085 Show the caller list of COMMANDS.
6086 help_cmd is the command the player used.
6087 **************************************************************************/
6088 static void show_help_command_list(struct connection *caller,
6089 enum command_id help_cmd)
6091 enum command_id i;
6093 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6094 cmd_reply(help_cmd, caller, C_COMMENT,
6095 _("The following server commands are available:"));
6096 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6097 if (!caller && con_get_style()) {
6098 for (i = 0; i < CMD_NUM; i++) {
6099 cmd_reply(help_cmd, caller, C_COMMENT, "%s", command_name_by_number(i));
6101 } else {
6102 char buf[MAX_LEN_CONSOLE_LINE];
6103 int j;
6105 buf[0] = '\0';
6106 for (i=0, j=0; i<CMD_NUM; i++) {
6107 if (may_use(caller, i)) {
6108 cat_snprintf(buf, sizeof(buf), "%-19s", command_name_by_number(i));
6109 if ((++j % 4) == 0) {
6110 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
6111 buf[0] = '\0';
6115 if (buf[0] != '\0') {
6116 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
6119 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6122 /**************************************************************************
6123 Send a reply to the caller listing the matched names from an ambiguous
6124 prefix.
6125 **************************************************************************/
6126 static void cmd_reply_matches(enum command_id cmd,
6127 struct connection *caller,
6128 m_pre_accessor_fn_t accessor_fn,
6129 int *matches, int num_matches)
6131 char buf[MAX_LEN_MSG];
6132 const char *src, *end;
6133 char *dest;
6134 int i;
6136 if (accessor_fn == NULL || matches == NULL || num_matches < 1) {
6137 return;
6140 dest = buf;
6141 end = buf + sizeof(buf) - 1;
6143 for (i = 0; i < num_matches && dest < end; i++) {
6144 src = accessor_fn(matches[i]);
6145 if (!src) {
6146 continue;
6148 if (dest != buf) {
6149 *dest++ = ' ';
6151 while (*src != '\0' && dest < end) {
6152 *dest++ = *src++;
6155 *dest = '\0';
6157 cmd_reply(cmd, caller, C_COMMENT, _("Possible matches: %s"), buf);
6160 /**************************************************************************
6161 Additional 'help' arguments
6162 **************************************************************************/
6163 #define SPECENUM_NAME help_general_args
6164 #define SPECENUM_VALUE0 HELP_GENERAL_COMMANDS
6165 #define SPECENUM_VALUE0NAME "commands"
6166 #define SPECENUM_VALUE1 HELP_GENERAL_OPTIONS
6167 #define SPECENUM_VALUE1NAME "options"
6168 #define SPECENUM_COUNT HELP_GENERAL_COUNT
6169 #include "specenum_gen.h"
6171 /**************************************************************************
6172 Unified indices for help arguments:
6173 CMD_NUM - Server commands
6174 HELP_GENERAL_NUM - General help arguments, above
6175 settings_number() - Server options
6176 **************************************************************************/
6177 #define HELP_ARG_NUM (CMD_NUM + HELP_GENERAL_COUNT + settings_number())
6179 /**************************************************************************
6180 Convert unified helparg index to string; see above.
6181 **************************************************************************/
6182 static const char *helparg_accessor(int i)
6184 if (i < CMD_NUM) {
6185 return command_name_by_number(i);
6188 i -= CMD_NUM;
6189 if (i < HELP_GENERAL_COUNT) {
6190 return help_general_args_name((enum help_general_args) i);
6193 i -= HELP_GENERAL_COUNT;
6194 return optname_accessor(i);
6197 /**************************************************************************
6198 Handle help command
6199 **************************************************************************/
6200 static bool show_help(struct connection *caller, char *arg)
6202 int matches[64], num_matches = 0;
6203 enum m_pre_result match_result;
6204 int ind;
6206 fc_assert_ret_val(!may_use_nothing(caller), FALSE);
6207 /* no commands means no help, either */
6209 match_result = match_prefix_full(helparg_accessor, HELP_ARG_NUM, 0,
6210 fc_strncasecmp, NULL, arg, &ind, matches,
6211 ARRAY_SIZE(matches), &num_matches);
6213 if (match_result == M_PRE_EMPTY) {
6214 show_help_intro(caller, CMD_HELP);
6215 return FALSE;
6217 if (match_result == M_PRE_AMBIGUOUS) {
6218 cmd_reply(CMD_HELP, caller, C_FAIL,
6219 _("Help argument '%s' is ambiguous."), arg);
6220 cmd_reply_matches(CMD_HELP, caller, helparg_accessor,
6221 matches, num_matches);
6222 return FALSE;
6224 if (match_result == M_PRE_FAIL) {
6225 cmd_reply(CMD_HELP, caller, C_FAIL,
6226 _("No match for help argument '%s'."), arg);
6227 return FALSE;
6230 /* other cases should be above */
6231 fc_assert_ret_val(match_result < M_PRE_AMBIGUOUS, FALSE);
6233 if (ind < CMD_NUM) {
6234 show_help_command(caller, CMD_HELP, ind);
6235 return TRUE;
6237 ind -= CMD_NUM;
6239 if (ind == HELP_GENERAL_OPTIONS) {
6240 show_help_option_list(caller, CMD_HELP);
6241 return TRUE;
6243 if (ind == HELP_GENERAL_COMMANDS) {
6244 show_help_command_list(caller, CMD_HELP);
6245 return TRUE;
6247 ind -= HELP_GENERAL_COUNT;
6249 if (ind < settings_number()) {
6250 show_help_option(caller, CMD_HELP, ind);
6251 return TRUE;
6254 /* should have finished by now */
6255 log_error("Bug in show_help!");
6256 return FALSE;
6259 /****************************************************************************
6260 List connections; initially mainly for debugging
6261 ****************************************************************************/
6262 static void show_connections(struct connection *caller)
6264 char buf[MAX_LEN_CONSOLE_LINE];
6266 cmd_reply(CMD_LIST, caller, C_COMMENT,
6267 _("List of connections to server:"));
6268 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6270 if (conn_list_size(game.all_connections) == 0) {
6271 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no connections>"));
6272 } else {
6273 conn_list_iterate(game.all_connections, pconn) {
6274 sz_strlcpy(buf, conn_description(pconn));
6275 if (pconn->established) {
6276 cat_snprintf(buf, sizeof(buf), " command access level %s",
6277 cmdlevel_name(pconn->access_level));
6279 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6280 } conn_list_iterate_end;
6282 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6285 /*****************************************************************************
6286 List all delegations of the current game.
6287 *****************************************************************************/
6288 static void show_delegations(struct connection *caller)
6290 bool empty = TRUE;
6292 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of all delegations:"));
6293 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6295 players_iterate(pplayer) {
6296 const char *delegate_to = player_delegation_get(pplayer);
6297 if (delegate_to != NULL) {
6298 const char *owner =
6299 player_delegation_active(pplayer) ? pplayer->server.orig_username
6300 : pplayer->username;
6301 fc_assert(owner);
6302 cmd_reply(CMD_LIST, caller, C_COMMENT,
6303 /* TRANS: last %s is either " (active)" or empty string */
6304 _("%s delegates control over player '%s' to user %s%s."),
6305 owner, player_name(pplayer), delegate_to,
6306 player_delegation_active(pplayer) ? _(" (active)") : "");
6307 empty = FALSE;
6309 } players_iterate_end;
6311 if (empty) {
6312 cmd_reply(CMD_LIST, caller, C_COMMENT, _("No delegations defined."));
6315 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6318 /****************************************************************************
6319 Show the ignore list of the
6320 ****************************************************************************/
6321 static bool show_ignore(struct connection *caller)
6323 char buf[128];
6324 int n = 1;
6326 if (NULL == caller) {
6327 cmd_reply(CMD_IGNORE, caller, C_FAIL,
6328 _("That would be rather silly, since you are not a player."));
6329 return FALSE;
6332 if (0 == conn_pattern_list_size(caller->server.ignore_list)) {
6333 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list is empty."));
6334 return TRUE;
6337 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list:"));
6338 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6339 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
6340 conn_pattern_to_string(ppattern, buf, sizeof(buf));
6341 cmd_reply(CMD_LIST, caller, C_COMMENT, "%d: %s", n++, buf);
6342 } conn_pattern_list_iterate_end;
6343 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6345 return TRUE;
6348 /****************************************************************************
6349 Show the list of the players of the game.
6350 ****************************************************************************/
6351 void show_players(struct connection *caller)
6353 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of players:"));
6354 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6356 if (player_count() == 0) {
6357 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6358 } else {
6359 players_iterate(pplayer) {
6360 char buf[MAX_LEN_CONSOLE_LINE];
6361 int n;
6363 /* Low access level callers don't get to see barbarians in list: */
6364 if (is_barbarian(pplayer) && caller
6365 && (caller->access_level < ALLOW_CTRL)) {
6366 continue;
6369 /* The output for each player looks like:
6371 * <Player name> [color]: Team[, Nation][, Username][, Status]
6372 * AI/Barbarian/Human[, AI type, skill level][, Connections]
6373 * [Details for each connection]
6376 /* '<Player name> [color]: [Nation][, Username][, Status]' */
6377 buf[0] = '\0';
6378 cat_snprintf(buf, sizeof(buf), "%s [%s]: %s", player_name(pplayer),
6379 player_color_ftstr(pplayer),
6380 team_name_translation(pplayer->team));
6381 if (!game.info.is_new_game) {
6382 cat_snprintf(buf, sizeof(buf), ", %s",
6383 nation_adjective_for_player(pplayer));
6385 if (strlen(pplayer->username) > 0
6386 && strcmp(pplayer->username, "nouser") != 0) {
6387 cat_snprintf(buf, sizeof(buf), _(", user %s"), pplayer->username);
6389 if (S_S_INITIAL == server_state() && pplayer->is_connected) {
6390 if (pplayer->is_ready) {
6391 sz_strlcat(buf, _(", ready"));
6392 } else {
6393 /* Emphasizes this */
6394 n = strlen(buf);
6395 featured_text_apply_tag(_(", not ready"),
6396 buf + n, sizeof(buf) - n,
6397 TTT_COLOR, 1, FT_OFFSET_UNSET,
6398 ftc_changed);
6400 } else if (!pplayer->is_alive) {
6401 sz_strlcat(buf, _(", Dead"));
6403 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6405 /* ' AI/Barbarian/Human[, skill level][, Connections]' */
6406 buf[0] = '\0';
6407 if (is_barbarian(pplayer)) {
6408 sz_strlcat(buf, _("Barbarian"));
6409 } else if (is_ai(pplayer)) {
6410 sz_strlcat(buf, _("AI"));
6411 } else {
6412 sz_strlcat(buf, _("Human"));
6414 if (is_ai(pplayer)) {
6415 cat_snprintf(buf, sizeof(buf), _(", %s"), ai_name(pplayer->ai));
6416 cat_snprintf(buf, sizeof(buf), _(", difficulty level %s"),
6417 ai_level_translated_name(pplayer->ai_common.skill_level));
6419 n = conn_list_size(pplayer->connections);
6420 if (n > 0) {
6421 cat_snprintf(buf, sizeof(buf),
6422 PL_(", %d connection:", ", %d connections:", n), n);
6424 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6426 /* ' [Details for each connection]' */
6427 conn_list_iterate(pplayer->connections, pconn) {
6428 fc_snprintf(buf, sizeof(buf),
6429 _("%s from %s (command access level %s), "
6430 "bufsize=%dkb"), pconn->username, pconn->addr,
6431 cmdlevel_name(pconn->access_level),
6432 (pconn->send_buffer->nsize >> 10));
6433 if (pconn->observer) {
6434 sz_strlcat(buf, _(" (observer mode)"));
6436 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6437 } conn_list_iterate_end;
6438 } players_iterate_end;
6440 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6443 /****************************************************************************
6444 List scenarios. We look both in the DATA_PATH and DATA_PATH/scenario
6445 ****************************************************************************/
6446 static void show_scenarios(struct connection *caller)
6448 char buf[MAX_LEN_CONSOLE_LINE];
6449 struct fileinfo_list *files;
6451 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of scenarios available:"));
6452 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6454 files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE);
6456 fileinfo_list_iterate(files, pfile) {
6457 fc_snprintf(buf, sizeof(buf), "%s", pfile->name);
6458 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6459 } fileinfo_list_iterate_end;
6460 fileinfo_list_destroy(files);
6462 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6465 /****************************************************************************
6466 List nation sets in the current ruleset.
6467 ****************************************************************************/
6468 static void show_nationsets(struct connection *caller)
6470 cmd_reply(CMD_LIST, caller, C_COMMENT,
6471 /* TRANS: don't translate text between '' */
6472 _("List of nation sets available for 'nationset' option:"));
6473 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6475 nation_sets_iterate(pset) {
6476 const char *description = nation_set_description(pset);
6477 int num_nations = 0;
6478 nations_iterate(pnation) {
6479 if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) {
6480 num_nations++;
6482 } nations_iterate_end;
6483 cmd_reply(CMD_LIST, caller, C_COMMENT,
6484 /* TRANS: nation set description; %d refers to number of playable
6485 * nations in set */
6486 PL_(" %-10s %s (%d playable)",
6487 " %-10s %s (%d playable)", num_nations),
6488 nation_set_rule_name(pset), nation_set_name_translation(pset),
6489 num_nations);
6490 if (strlen(description) > 0) {
6491 static const char prefix[] = " ";
6492 char *translated = fc_strdup(_(description));
6493 fc_break_lines(translated, LINE_BREAK);
6494 cmd_reply_prefix(CMD_LIST, caller, C_COMMENT, prefix, "%s%s",
6495 prefix, translated);
6497 } nation_sets_iterate_end;
6499 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6502 /****************************************************************************
6503 Show a list of teams on the command line.
6504 ****************************************************************************/
6505 static void show_teams(struct connection *caller)
6507 /* Currently this just lists all teams (typically 32 of them) with their
6508 * names and # of players on the team. This could probably be improved. */
6509 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of teams:"));
6510 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6512 teams_iterate(pteam) {
6513 const struct player_list *members = team_members(pteam);
6515 /* PL_() is needed here because some languages may differentiate
6516 * between 2 and 3 (although English does not). */
6517 cmd_reply(CMD_LIST, caller, C_COMMENT,
6518 /* TRANS: There will always be at least 2 players here. */
6519 PL_("%2d : '%s' : %d player :",
6520 "%2d : '%s' : %d players :",
6521 player_list_size(members)),
6522 team_index(pteam), team_name_translation(pteam),
6523 player_list_size(members));
6524 player_list_iterate(members, pplayer) {
6525 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", player_name(pplayer));
6526 } player_list_iterate_end;
6527 } teams_iterate_end;
6529 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6532 /****************************************************************************
6533 Show a list of all map image definitions on the command line.
6534 ****************************************************************************/
6535 static void show_mapimg(struct connection *caller, enum command_id cmd)
6537 int id;
6539 if (mapimg_count() == 0) {
6540 cmd_reply(cmd, caller, C_OK, _("No map image definitions."));
6541 } else {
6542 cmd_reply(cmd, caller, C_COMMENT, _("List of map image definitions:"));
6543 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6544 for (id = 0; id < mapimg_count(); id++) {
6545 char str[MAX_LEN_MAPDEF] = "";
6546 mapimg_show(id, str, sizeof(str), FALSE);
6547 cmd_reply(cmd, caller, C_COMMENT, _("[%2d] %s"), id, str);
6549 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6553 /****************************************************************************
6554 Show a list of all players with the assigned color.
6555 ****************************************************************************/
6556 static void show_colors(struct connection *caller)
6558 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of player colors:"));
6559 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6560 if (player_count() == 0) {
6561 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6562 } else {
6563 players_iterate(pplayer) {
6564 cmd_reply(CMD_LIST, caller, C_COMMENT, _("%s (user %s): [%s]"),
6565 player_name(pplayer), pplayer->username,
6566 player_color_ftstr(pplayer));
6567 } players_iterate_end;
6569 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6572 /****************************************************************************
6573 '/list' arguments
6574 **************************************************************************/
6575 #define SPECENUM_NAME list_args
6576 #define SPECENUM_VALUE0 LIST_COLORS
6577 #define SPECENUM_VALUE0NAME "colors"
6578 #define SPECENUM_VALUE1 LIST_CONNECTIONS
6579 #define SPECENUM_VALUE1NAME "connections"
6580 #define SPECENUM_VALUE2 LIST_DELEGATIONS
6581 #define SPECENUM_VALUE2NAME "delegations"
6582 #define SPECENUM_VALUE3 LIST_IGNORE
6583 #define SPECENUM_VALUE3NAME "ignored users"
6584 #define SPECENUM_VALUE4 LIST_MAPIMG
6585 #define SPECENUM_VALUE4NAME "map image definitions"
6586 #define SPECENUM_VALUE5 LIST_PLAYERS
6587 #define SPECENUM_VALUE5NAME "players"
6588 #define SPECENUM_VALUE6 LIST_SCENARIOS
6589 #define SPECENUM_VALUE6NAME "scenarios"
6590 #define SPECENUM_VALUE7 LIST_NATIONSETS
6591 #define SPECENUM_VALUE7NAME "nationsets"
6592 #define SPECENUM_VALUE8 LIST_TEAMS
6593 #define SPECENUM_VALUE8NAME "teams"
6594 #define SPECENUM_VALUE9 LIST_VOTES
6595 #define SPECENUM_VALUE9NAME "votes"
6596 #include "specenum_gen.h"
6598 /**************************************************************************
6599 Returns possible parameters for the list command.
6600 **************************************************************************/
6601 static const char *list_accessor(int i)
6603 i = CLIP(0, i, list_args_max());
6604 return list_args_name((enum list_args) i);
6607 /**************************************************************************
6608 Show list of players or connections, or connection statistics.
6609 **************************************************************************/
6610 static bool show_list(struct connection *caller, char *arg)
6612 enum m_pre_result match_result;
6613 int ind_int;
6614 enum list_args ind;
6616 remove_leading_trailing_spaces(arg);
6617 match_result = match_prefix(list_accessor, list_args_max() + 1, 0,
6618 fc_strncasecmp, NULL, arg, &ind_int);
6619 ind = ind_int;
6621 if (match_result > M_PRE_EMPTY) {
6622 cmd_reply(CMD_LIST, caller, C_SYNTAX,
6623 _("Bad list argument: '%s'. Try '%shelp list'."),
6624 arg, (caller?"/":""));
6625 return FALSE;
6628 if (match_result == M_PRE_EMPTY) {
6629 ind = LIST_PLAYERS;
6632 switch(ind) {
6633 case LIST_COLORS:
6634 show_colors(caller);
6635 return TRUE;
6636 case LIST_CONNECTIONS:
6637 show_connections(caller);
6638 return TRUE;
6639 case LIST_DELEGATIONS:
6640 show_delegations(caller);
6641 return TRUE;
6642 case LIST_IGNORE:
6643 return show_ignore(caller);
6644 case LIST_MAPIMG:
6645 show_mapimg(caller, CMD_LIST);
6646 return TRUE;
6647 case LIST_PLAYERS:
6648 show_players(caller);
6649 return TRUE;
6650 case LIST_SCENARIOS:
6651 show_scenarios(caller);
6652 return TRUE;
6653 case LIST_NATIONSETS:
6654 show_nationsets(caller);
6655 return TRUE;
6656 case LIST_TEAMS:
6657 show_teams(caller);
6658 return TRUE;
6659 case LIST_VOTES:
6660 show_votes(caller);
6661 return TRUE;
6664 cmd_reply(CMD_LIST, caller, C_FAIL,
6665 "Internal error: ind %d in show_list", ind);
6666 log_error("Internal error: ind %d in show_list", ind);
6667 return FALSE;
6670 #ifdef FREECIV_HAVE_LIBREADLINE
6671 /********************* RL completion functions ***************************/
6672 /* To properly complete both commands, player names, options and filenames
6673 there is one array per type of completion with the commands that
6674 the type is relevant for.
6677 /**************************************************************************
6678 A generalised generator function: text and state are "standard"
6679 parameters to a readline generator function;
6680 num is number of possible completions, or -1 if this is not known and
6681 index2str should be iterated until it returns NULL;
6682 index2str is a function which returns each possible completion string
6683 by index (it may return NULL).
6684 **************************************************************************/
6685 static char *generic_generator(const char *text, int state, int num,
6686 const char*(*index2str)(int))
6688 static int list_index, len;
6689 const char *name = ""; /* dummy non-NULL string */
6690 char *mytext = local_to_internal_string_malloc(text);
6692 /* This function takes a string (text) in the local format and must return
6693 * a string in the local format. However comparisons are done against
6694 * names that are in the internal format (UTF-8). Thus we have to convert
6695 * the text function from the local to the internal format before doing
6696 * the comparison, and convert the string we return *back* to the
6697 * local format when returning it. */
6699 /* If this is a new word to complete, initialize now. This includes
6700 saving the length of TEXT for efficiency, and initializing the index
6701 variable to 0. */
6702 if (state == 0) {
6703 list_index = 0;
6704 len = strlen(mytext);
6707 /* Return the next name which partially matches: */
6708 while ((num < 0 && name) || (list_index < num)) {
6709 name = index2str(list_index);
6710 list_index++;
6712 if (name != NULL && fc_strncasecmp(name, mytext, len) == 0) {
6713 free(mytext);
6714 return internal_to_local_string_malloc(name);
6717 free(mytext);
6719 /* If no names matched, then return NULL. */
6720 return ((char *)NULL);
6723 /**************************************************************************
6724 The valid commands at the root of the prompt.
6725 **************************************************************************/
6726 static char *command_generator(const char *text, int state)
6728 return generic_generator(text, state, CMD_NUM, command_name_by_number);
6731 /**************************************************************************
6732 The valid arguments to "set" and "explain"
6733 **************************************************************************/
6734 static char *option_generator(const char *text, int state)
6736 return generic_generator(text, state, settings_number(), optname_accessor);
6739 /**************************************************************************
6740 The valid arguments to "show"
6741 **************************************************************************/
6742 static char *olevel_generator(const char *text, int state)
6744 return generic_generator(text, state, settings_number() + OLEVELS_NUM + 1,
6745 olvlname_accessor);
6748 /**************************************************************************
6749 Accessor for values of the enum/bitwise option defined by
6750 'completion_option'.
6751 **************************************************************************/
6752 static int completion_option;
6753 static const char *option_value_accessor(int idx) {
6754 const struct setting *pset = setting_by_number(completion_option);
6755 switch (setting_type(pset)) {
6756 case SSET_ENUM:
6757 return setting_enum_val(pset, idx, FALSE);
6758 break;
6759 case SSET_BITWISE:
6760 return setting_bitwise_bit(pset, idx, FALSE);
6761 break;
6762 default:
6763 fc_assert_ret_val(0, NULL);
6767 /**************************************************************************
6768 The valid arguments to "set OPT", where OPT is the enumerated or
6769 bitwise option previously defined by completion_option
6770 **************************************************************************/
6771 static char *option_value_generator(const char *text, int state)
6773 return generic_generator(text, state, -1, option_value_accessor);
6776 /**************************************************************************
6777 Access player name.
6778 **************************************************************************/
6779 static const char *playername_accessor(int idx)
6781 const struct player_slot *pslot = player_slot_by_number(idx);
6783 if (!player_slot_is_used(pslot)) {
6784 return NULL;
6787 return player_name(player_slot_get_player(pslot));
6790 /**************************************************************************
6791 The valid playername arguments.
6792 **************************************************************************/
6793 static char *player_generator(const char *text, int state)
6795 return generic_generator(text, state, player_slot_count(),
6796 playername_accessor);
6799 /**************************************************************************
6800 Access connection user name, from game.all_connections.
6801 **************************************************************************/
6802 static const char *connection_name_accessor(int idx)
6804 return conn_list_get(game.all_connections, idx)->username;
6807 /**************************************************************************
6808 The valid connection user name arguments.
6809 **************************************************************************/
6810 static char *connection_generator(const char *text, int state)
6812 return generic_generator(text, state, conn_list_size(game.all_connections),
6813 connection_name_accessor);
6816 /**************************************************************************
6817 Extra accessor function since cmdlevel_name() takes enum argument, not int.
6818 **************************************************************************/
6819 static const char *cmdlevel_arg1_accessor(int idx)
6821 return cmdlevel_name(idx);
6824 /**************************************************************************
6825 The valid first argument to "cmdlevel"
6826 **************************************************************************/
6827 static char *cmdlevel_arg1_generator(const char *text, int state)
6829 return generic_generator(text, state, cmdlevel_max()+1,
6830 cmdlevel_arg1_accessor);
6833 /**************************************************************************
6834 Accessor for the second argument to "cmdlevel": "first" or "new" or
6835 a connection name.
6836 **************************************************************************/
6837 static const char *cmdlevel_arg2_accessor(int idx)
6839 return ((idx == 0) ? "first" :
6840 (idx == 1) ? "new" :
6841 connection_name_accessor(idx - 2));
6844 /**************************************************************************
6845 The valid arguments for the second argument to "cmdlevel".
6846 **************************************************************************/
6847 static char *cmdlevel_arg2_generator(const char *text, int state)
6849 return generic_generator(text, state,
6850 /* "first", "new", connection names */
6851 2 + conn_list_size(game.all_connections),
6852 cmdlevel_arg2_accessor);
6855 /**************************************************************************
6856 Accessor for the second argument to "create": ai type name
6857 **************************************************************************/
6858 static const char *aitype_accessor(int idx)
6860 return get_ai_type(idx)->name;
6863 /**************************************************************************
6864 The valid arguments for the second argument to "create".
6865 **************************************************************************/
6866 static char *aitype_generator(const char *text, int state)
6868 return generic_generator(text, state, ai_type_get_count(),
6869 aitype_accessor);
6872 /**************************************************************************
6873 The valid arguments for the argument to "reset".
6874 **************************************************************************/
6875 static char *reset_generator(const char *text, int state)
6877 return generic_generator(text, state, reset_args_max() + 1, reset_accessor);
6880 /**************************************************************************
6881 The valid arguments for the argument to "vote".
6882 **************************************************************************/
6883 static char *vote_generator(const char *text, int state)
6885 return generic_generator(text, state, -1, vote_arg_accessor);
6888 /**************************************************************************
6889 The valid arguments for the first argument to "delegate".
6890 **************************************************************************/
6891 static char *delegate_generator(const char *text, int state)
6893 return generic_generator(text, state, delegate_args_max() + 1,
6894 delegate_accessor);
6897 /**************************************************************************
6898 The valid arguments for the first argument to "mapimg".
6899 **************************************************************************/
6900 static char *mapimg_generator(const char *text, int state)
6902 return generic_generator(text, state, mapimg_args_max() + 1,
6903 mapimg_accessor);
6906 /**************************************************************************
6907 The valid arguments for the argument to "fcdb".
6908 **************************************************************************/
6909 static char *fcdb_generator(const char *text, int state)
6911 return generic_generator(text, state, FCDB_COUNT, fcdb_accessor);
6914 /**************************************************************************
6915 The valid arguments for the argument to "lua".
6916 **************************************************************************/
6917 static char *lua_generator(const char *text, int state)
6919 return generic_generator(text, state, lua_args_max() + 1, lua_accessor);
6922 /**************************************************************************
6923 The valid first arguments to "help".
6924 **************************************************************************/
6925 static char *help_generator(const char *text, int state)
6927 return generic_generator(text, state, HELP_ARG_NUM, helparg_accessor);
6930 /**************************************************************************
6931 The valid first arguments to "list".
6932 **************************************************************************/
6933 static char *list_generator(const char *text, int state)
6935 return generic_generator(text, state, list_args_max() + 1, list_accessor);
6938 /**************************************************************************
6939 Generalised version of contains_str_before_start, which searches the
6940 N'th token in rl_line_buffer (0=first).
6941 **************************************************************************/
6942 static bool contains_token_before_start(int start, int token, const char *arg,
6943 bool allow_fluff)
6945 char *str_itr = rl_line_buffer;
6946 int arg_len = strlen(arg);
6948 /* Swallow unwanted tokens and their preceding delimiters */
6949 while (token--) {
6950 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6951 str_itr++;
6953 while (str_itr < rl_line_buffer + start && fc_isalnum(*str_itr)) {
6954 str_itr++;
6958 /* Swallow any delimiters before the token we're interested in */
6959 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6960 str_itr++;
6963 if (fc_strncasecmp(str_itr, arg, arg_len) != 0) {
6964 return FALSE;
6966 str_itr += arg_len;
6968 if (fc_isalnum(*str_itr)) {
6969 /* Not a distinct word. */
6970 return FALSE;
6973 if (!allow_fluff) {
6974 for (; str_itr < rl_line_buffer + start; str_itr++) {
6975 if (fc_isalnum(*str_itr)) {
6976 return FALSE;
6981 return TRUE;
6984 /**************************************************************************
6985 Returns whether the text between the start of rl_line_buffer and the
6986 start position is of the form [non-alpha]*cmd[non-alpha]*
6987 allow_fluff changes the regexp to [non-alpha]*cmd[non-alpha].*
6988 **************************************************************************/
6989 static bool contains_str_before_start(int start, const char *cmd,
6990 bool allow_fluff)
6992 return contains_token_before_start(start, 0, cmd, allow_fluff);
6995 /**************************************************************************
6996 Return whether we are completing command name. This can be either
6997 command itself, or argument to 'help'.
6998 **************************************************************************/
6999 static bool is_command(int start)
7001 char *str_itr;
7003 if (contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE))
7004 return TRUE;
7006 /* if there is only it is also OK */
7007 str_itr = rl_line_buffer;
7008 while (str_itr - rl_line_buffer < start) {
7009 if (fc_isalnum(*str_itr)) {
7010 return FALSE;
7012 str_itr++;
7014 return TRUE;
7017 /**************************************************************************
7018 number of tokens in rl_line_buffer before start
7019 **************************************************************************/
7020 static int num_tokens(int start)
7022 int res = 0;
7023 bool alnum = FALSE;
7024 char *chptr = rl_line_buffer;
7026 while (chptr - rl_line_buffer < start) {
7027 if (fc_isalnum(*chptr)) {
7028 if (!alnum) {
7029 alnum = TRUE;
7030 res++;
7032 } else {
7033 alnum = FALSE;
7035 chptr++;
7038 return res;
7041 /**************************************************************************
7042 Commands that may be followed by a player name
7043 **************************************************************************/
7044 static const int player_cmd[] = {
7045 CMD_AITOGGLE,
7046 CMD_HANDICAPPED,
7047 CMD_NOVICE,
7048 CMD_EASY,
7049 CMD_NORMAL,
7050 CMD_HARD,
7051 CMD_CHEATING,
7052 #ifdef FREECIV_DEBUG
7053 CMD_EXPERIMENTAL,
7054 #endif
7055 CMD_REMOVE,
7056 CMD_TEAM,
7057 CMD_PLAYERCOLOR,
7061 /**************************************************************************
7062 Return whether we are completing player name argument.
7063 **************************************************************************/
7064 static bool is_player(int start)
7066 int i = 0;
7068 while (player_cmd[i] != -1) {
7069 if (contains_str_before_start(start, command_name_by_number(player_cmd[i]), FALSE)) {
7070 return TRUE;
7072 i++;
7075 return FALSE;
7078 /**************************************************************************
7079 Commands that may be followed by a connection name
7080 **************************************************************************/
7081 static const int connection_cmd[] = {
7082 CMD_CUT,
7083 CMD_KICK,
7087 /**************************************************************************
7088 Return whether we are completing connection name argument.
7089 **************************************************************************/
7090 static bool is_connection(int start)
7092 int i = 0;
7094 while (connection_cmd[i] != -1) {
7095 if (contains_str_before_start(start,
7096 command_name_by_number(connection_cmd[i]),
7097 FALSE)) {
7098 return TRUE;
7100 i++;
7103 return FALSE;
7106 /**************************************************************************
7107 Return whether we are completing cmdlevel command argument 2.
7108 **************************************************************************/
7109 static bool is_cmdlevel_arg2(int start)
7111 return (contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), TRUE)
7112 && num_tokens(start) == 2);
7115 /**************************************************************************
7116 Return whether we are completing cmdlevel command argument.
7117 **************************************************************************/
7118 static bool is_cmdlevel_arg1(int start)
7120 return contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), FALSE);
7123 /**************************************************************************
7124 Commands that may be followed by a server option name
7126 CMD_SHOW is handled by option_level_cmd, which is for both option levels
7127 and server options
7128 **************************************************************************/
7129 static const int server_option_cmd[] = {
7130 CMD_EXPLAIN,
7131 CMD_SET,
7132 CMD_DEFAULT,
7136 /**************************************************************************
7137 Returns TRUE if the readline buffer string matches a server option at
7138 the given position.
7139 **************************************************************************/
7140 static bool is_server_option(int start)
7142 int i = 0;
7144 while (server_option_cmd[i] != -1) {
7145 if (contains_str_before_start(start, command_name_by_number(server_option_cmd[i]),
7146 FALSE)) {
7147 return TRUE;
7149 i++;
7152 return FALSE;
7155 /**************************************************************************
7156 Commands that may be followed by an option level or server option
7157 **************************************************************************/
7158 static const int option_level_cmd[] = {
7159 CMD_SHOW,
7163 /**************************************************************************
7164 Returns true if the readline buffer string matches an option level or an
7165 option at the given position.
7166 **************************************************************************/
7167 static bool is_option_level(int start)
7169 int i = 0;
7171 while (option_level_cmd[i] != -1) {
7172 if (contains_str_before_start(start, command_name_by_number(option_level_cmd[i]),
7173 FALSE)) {
7174 return TRUE;
7176 i++;
7179 return FALSE;
7182 /**************************************************************************
7183 Returns TRUE if the readline buffer string is such that we expect an
7184 enumerated value at the given position. The option for which values
7185 should be completed is written to opt_p.
7186 **************************************************************************/
7187 static bool is_enum_option_value(int start, int *opt_p)
7189 if (contains_str_before_start(start, command_name_by_number(CMD_SET),
7190 TRUE)) {
7191 settings_iterate(SSET_ALL, pset) {
7192 if (setting_type(pset) != SSET_ENUM
7193 && setting_type(pset) != SSET_BITWISE) {
7194 continue;
7196 /* Allow a single token for enum options, multiple for bitwise
7197 * (the separator | will separate tokens for these purposes) */
7198 if (contains_token_before_start(start, 1, setting_name(pset),
7199 setting_type(pset) == SSET_BITWISE)) {
7200 *opt_p = setting_number(pset);
7201 /* Suppress appended space for bitwise options (user may want |) */
7202 rl_completion_suppress_append = (setting_type(pset) == SSET_BITWISE);
7203 return TRUE;
7205 } settings_iterate_end;
7207 return FALSE;
7210 /**************************************************************************
7211 Commands that may be followed by a filename
7212 **************************************************************************/
7213 static const int filename_cmd[] = {
7214 CMD_LOAD,
7215 CMD_SAVE,
7216 CMD_READ_SCRIPT,
7217 CMD_WRITE_SCRIPT,
7221 /**************************************************************************
7222 Return whether we are completing filename.
7223 **************************************************************************/
7224 static bool is_filename(int start)
7226 int i = 0;
7228 while (filename_cmd[i] != -1) {
7229 if (contains_str_before_start(start, command_name_by_number(filename_cmd[i]), FALSE)) {
7230 return TRUE;
7232 i++;
7235 return FALSE;
7238 /**************************************************************************
7239 Return whether we are completing second argument for create command
7240 **************************************************************************/
7241 static bool is_create_arg2(int start)
7243 return (contains_str_before_start(start, command_name_by_number(CMD_CREATE), TRUE)
7244 && num_tokens(start) == 2);
7247 /**************************************************************************
7248 Return whether we are completing argument for reset command
7249 **************************************************************************/
7250 static bool is_reset(int start)
7252 return contains_str_before_start(start,
7253 command_name_by_number(CMD_RESET),
7254 FALSE);
7257 /**************************************************************************
7258 Return whether we are completing argument for vote command
7259 **************************************************************************/
7260 static bool is_vote(int start)
7262 return contains_str_before_start(start,
7263 command_name_by_number(CMD_VOTE),
7264 FALSE);
7267 /**************************************************************************
7268 Return whether we are completing first argument for delegate command
7269 **************************************************************************/
7270 static bool is_delegate_arg1(int start)
7272 return contains_str_before_start(start,
7273 command_name_by_number(CMD_DELEGATE),
7274 FALSE);
7277 /**************************************************************************
7278 Return whether we are completing first argument for mapimg command
7279 **************************************************************************/
7280 static bool is_mapimg(int start)
7282 return contains_str_before_start(start,
7283 command_name_by_number(CMD_MAPIMG),
7284 FALSE);
7287 /**************************************************************************
7288 Return whether we are completing argument for fcdb command
7289 **************************************************************************/
7290 static bool is_fcdb(int start)
7292 return contains_str_before_start(start,
7293 command_name_by_number(CMD_FCDB),
7294 FALSE);
7297 /**************************************************************************
7298 Return whether we are completing argument for lua command
7299 **************************************************************************/
7300 static bool is_lua(int start)
7302 return contains_str_before_start(start,
7303 command_name_by_number(CMD_LUA),
7304 FALSE);
7307 /**************************************************************************
7308 Return whether we are completing help command argument.
7309 **************************************************************************/
7310 static bool is_help(int start)
7312 return contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE);
7315 /**************************************************************************
7316 Return whether we are completing list command argument.
7317 **************************************************************************/
7318 static bool is_list(int start)
7320 return contains_str_before_start(start, command_name_by_number(CMD_LIST), FALSE);
7323 /**************************************************************************
7324 Attempt to complete on the contents of TEXT. START and END bound the
7325 region of rl_line_buffer that contains the word to complete. TEXT is
7326 the word to complete. We can use the entire contents of rl_line_buffer
7327 in case we want to do some simple parsing. Return the array of matches,
7328 or NULL if there aren't any.
7329 **************************************************************************/
7330 char **freeciv_completion(const char *text, int start, int end)
7332 char **matches = (char **)NULL;
7334 if (is_help(start)) {
7335 matches = rl_completion_matches(text, help_generator);
7336 } else if (is_command(start)) {
7337 matches = rl_completion_matches(text, command_generator);
7338 } else if (is_list(start)) {
7339 matches = rl_completion_matches(text, list_generator);
7340 } else if (is_cmdlevel_arg2(start)) {
7341 matches = rl_completion_matches(text, cmdlevel_arg2_generator);
7342 } else if (is_cmdlevel_arg1(start)) {
7343 matches = rl_completion_matches(text, cmdlevel_arg1_generator);
7344 } else if (is_connection(start)) {
7345 matches = rl_completion_matches(text, connection_generator);
7346 } else if (is_player(start)) {
7347 matches = rl_completion_matches(text, player_generator);
7348 } else if (is_server_option(start)) {
7349 matches = rl_completion_matches(text, option_generator);
7350 } else if (is_option_level(start)) {
7351 matches = rl_completion_matches(text, olevel_generator);
7352 } else if (is_enum_option_value(start, &completion_option)) {
7353 matches = rl_completion_matches(text, option_value_generator);
7354 } else if (is_filename(start)) {
7355 /* This function we get from readline */
7356 matches = rl_completion_matches(text, rl_filename_completion_function);
7357 } else if (is_create_arg2(start)) {
7358 matches = rl_completion_matches(text, aitype_generator);
7359 } else if (is_reset(start)) {
7360 matches = rl_completion_matches(text, reset_generator);
7361 } else if (is_vote(start)) {
7362 matches = rl_completion_matches(text, vote_generator);
7363 } else if (is_delegate_arg1(start)) {
7364 matches = rl_completion_matches(text, delegate_generator);
7365 } else if (is_mapimg(start)) {
7366 matches = rl_completion_matches(text, mapimg_generator);
7367 } else if (is_fcdb(start)) {
7368 matches = rl_completion_matches(text, fcdb_generator);
7369 } else if (is_lua(start)) {
7370 matches = rl_completion_matches(text, lua_generator);
7371 } else {
7372 /* We have no idea what to do */
7373 matches = NULL;
7376 /* Don't automatically try to complete with filenames */
7377 rl_attempted_completion_over = 1;
7379 return (matches);
7382 #endif /* FREECIV_HAVE_LIBREADLINE */