When mixer is not available, recommend SDL2_mixer instead of SDL1.2 mixer
[freeciv.git] / server / stdinhand.c
blobce5d43a84c860470416e43993adbb094b98232d0
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 "savegame2.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 fcdb_command(struct connection *caller, char *arg, bool check);
151 static const char *fcdb_accessor(int i);
152 static char setting_status(struct connection *caller,
153 const struct setting *pset);
154 static bool player_name_check(const char* name, char *buf, size_t buflen);
155 static bool playercolor_command(struct connection *caller,
156 char *str, bool check);
157 static bool mapimg_command(struct connection *caller, char *arg, bool check);
158 static const char *mapimg_accessor(int i);
160 static void show_delegations(struct connection *caller);
162 static const char horiz_line[] =
163 "------------------------------------------------------------------------------";
165 /********************************************************************
166 Are we operating under a restricted security regime? For now
167 this does not do much.
168 *********************************************************************/
169 static bool is_restricted(struct connection *caller)
171 return (caller && caller->access_level != ALLOW_HACK);
174 /**************************************************************************
175 Check the player name. Returns TRUE if the player name is valid else
176 an error message is saved in 'buf'.
177 **************************************************************************/
178 static bool player_name_check(const char* name, char *buf, size_t buflen)
180 size_t len = strlen(name);
182 if (len == 0) {
183 fc_snprintf(buf, buflen, _("Can't use an empty name."));
184 return FALSE;
185 } else if (len > MAX_LEN_NAME-1) {
186 fc_snprintf(buf, buflen, _("That name exceeds the maximum of %d chars."),
187 MAX_LEN_NAME-1);
188 return FALSE;
189 } else if (fc_strcasecmp(name, ANON_PLAYER_NAME) == 0
190 || fc_strcasecmp(name, "Observer") == 0) {
191 fc_snprintf(buf, buflen, _("That name is not allowed."));
192 /* "Observer" used to be illegal and we keep it that way for now. */
193 /* FIXME: This disallows anonymous player name as it appears in English,
194 * but not one in any other language that the user may see. */
195 return FALSE;
198 return TRUE;
201 /**************************************************************************
202 Convert a named command into an id.
203 If accept_ambiguity is true, return the first command in the
204 enum list which matches, else return CMD_AMBIGUOUS on ambiguity.
205 (This is a trick to allow ambiguity to be handled in a flexible way
206 without importing notify_player() messages inside this routine - rp)
207 **************************************************************************/
208 static enum command_id command_named(const char *token, bool accept_ambiguity)
210 enum m_pre_result result;
211 int ind;
213 result = match_prefix(command_name_by_number, CMD_NUM, 0,
214 fc_strncasecmp, NULL, token, &ind);
216 if (result < M_PRE_AMBIGUOUS) {
217 return ind;
218 } else if (result == M_PRE_AMBIGUOUS) {
219 return accept_ambiguity ? ind : CMD_AMBIGUOUS;
220 } else {
221 return CMD_UNRECOGNIZED;
225 /**************************************************************************
226 Initialize stuff related to this code module.
227 **************************************************************************/
228 void stdinhand_init(void)
230 fc_assert(NULL == kick_table_by_addr);
231 kick_table_by_addr = kick_hash_new();
233 fc_assert(NULL == kick_table_by_user);
234 kick_table_by_user = kick_hash_new();
237 /**************************************************************************
238 Update stuff every turn that is related to this code module. Run this
239 on turn end.
240 **************************************************************************/
241 void stdinhand_turn(void)
243 /* Nothing at the moment. */
246 /**************************************************************************
247 Deinitialize stuff related to this code module.
248 **************************************************************************/
249 void stdinhand_free(void)
251 fc_assert(NULL != kick_table_by_addr);
252 if (NULL != kick_table_by_addr) {
253 kick_hash_destroy(kick_table_by_addr);
254 kick_table_by_addr = NULL;
257 fc_assert(NULL != kick_table_by_user);
258 if (NULL != kick_table_by_user) {
259 kick_hash_destroy(kick_table_by_user);
260 kick_table_by_user = NULL;
264 /**************************************************************************
265 Whether the caller can use the specified command. caller == NULL means
266 console.
267 **************************************************************************/
268 static bool may_use(struct connection *caller, enum command_id cmd)
270 if (!caller) {
271 return TRUE; /* on the console, everything is allowed */
273 return (caller->access_level >= command_level(command_by_number(cmd)));
276 /**************************************************************************
277 Whether the caller cannot use any commands at all.
278 caller == NULL means console.
279 **************************************************************************/
280 static bool may_use_nothing(struct connection *caller)
282 if (!caller) {
283 return FALSE; /* on the console, everything is allowed */
285 return (ALLOW_NONE == conn_get_access(caller));
288 /**************************************************************************
289 Return the status of the setting (changeable, locked, fixed).
290 caller == NULL means console.
291 **************************************************************************/
292 static char setting_status(struct connection *caller,
293 const struct setting *pset)
295 /* first check for a ruleset lock as this is included in
296 * setting_is_changeable() */
297 if (setting_locked(pset)) {
298 /* setting is locked by the ruleset */
299 return '!';
302 if (setting_is_changeable(pset, caller, NULL, 0)) {
303 /* setting can be changed */
304 return '+';
307 /* setting is fixed */
308 return ' ';
311 /**************************************************************************
312 feedback related to server commands
313 caller == NULL means console.
314 No longer duplicate all output to console.
316 This lowlevel function takes a single line; prefix is prepended to line.
317 **************************************************************************/
318 static void cmd_reply_line(enum command_id cmd, struct connection *caller,
319 enum rfc_status rfc_status, const char *prefix,
320 const char *line)
322 const char *cmdname = cmd < CMD_NUM
323 ? command_name_by_number(cmd)
324 : cmd == CMD_AMBIGUOUS
325 /* TRANS: ambiguous command */
326 ? _("(ambiguous)")
327 : cmd == CMD_UNRECOGNIZED
328 /* TRANS: unrecognized command */
329 ? _("(unknown)")
330 : "(?!?)"; /* this case is a bug! */
332 if (caller) {
333 notify_conn(caller->self, NULL, E_SETTING, ftc_command,
334 "/%s: %s%s", cmdname, prefix, line);
335 /* cc: to the console - testing has proved it's too verbose - rp
336 con_write(rfc_status, "%s/%s: %s%s", caller->name, cmdname, prefix, line);
338 } else {
339 con_write(rfc_status, "%s%s", prefix, line);
342 if (rfc_status == C_OK) {
343 struct packet_chat_msg packet;
345 package_event(&packet, NULL, E_SETTING, ftc_server, "%s", line);
346 conn_list_iterate(game.est_connections, pconn) {
347 /* Do not tell caller, since he was told above! */
348 if (caller != pconn) {
349 send_packet_chat_msg(pconn, &packet);
351 } conn_list_iterate_end;
352 event_cache_add_for_all(&packet);
354 if (NULL != caller) {
355 /* Echo to the console. */
356 log_normal("%s", line);
361 /**************************************************************************
362 va_list version which allow embedded newlines, and each line is sent
363 separately. 'prefix' is prepended to every line _after_ the first line.
364 **************************************************************************/
365 static void vcmd_reply_prefix(enum command_id cmd, struct connection *caller,
366 enum rfc_status rfc_status, const char *prefix,
367 const char *format, va_list ap)
369 char buf[4096];
370 char *c0, *c1;
372 fc_vsnprintf(buf, sizeof(buf), format, ap);
374 c0 = buf;
375 while ((c1=strstr(c0, "\n"))) {
376 *c1 = '\0';
377 cmd_reply_line(cmd, caller, rfc_status, (c0==buf?"":prefix), c0);
378 c0 = c1+1;
380 cmd_reply_line(cmd, caller, rfc_status, (c0==buf?"":prefix), c0);
383 /**************************************************************************
384 var-args version of above
385 duplicate declaration required for attribute to work...
386 **************************************************************************/
387 static void cmd_reply_prefix(enum command_id cmd, struct connection *caller,
388 enum rfc_status rfc_status, const char *prefix,
389 const char *format, ...)
390 fc__attribute((__format__ (__printf__, 5, 6)));
391 static void cmd_reply_prefix(enum command_id cmd, struct connection *caller,
392 enum rfc_status rfc_status, const char *prefix,
393 const char *format, ...)
395 va_list ap;
396 va_start(ap, format);
397 vcmd_reply_prefix(cmd, caller, rfc_status, prefix, format, ap);
398 va_end(ap);
401 /**************************************************************************
402 var-args version as above, no prefix
403 **************************************************************************/
404 void cmd_reply(enum command_id cmd, struct connection *caller,
405 enum rfc_status rfc_status, const char *format, ...)
407 va_list ap;
408 va_start(ap, format);
409 vcmd_reply_prefix(cmd, caller, rfc_status, "", format, ap);
410 va_end(ap);
413 /**************************************************************************
414 Command specific argument parsing has detected that player argument
415 is invalid. This function is common handling for that situation.
416 **************************************************************************/
417 static void cmd_reply_no_such_player(enum command_id cmd,
418 struct connection *caller,
419 const char *name,
420 enum m_pre_result match_result)
422 switch(match_result) {
423 case M_PRE_EMPTY:
424 cmd_reply(cmd, caller, C_SYNTAX,
425 _("Name is empty, so cannot be a player."));
426 break;
427 case M_PRE_LONG:
428 cmd_reply(cmd, caller, C_SYNTAX,
429 _("Name is too long, so cannot be a player."));
430 break;
431 case M_PRE_AMBIGUOUS:
432 cmd_reply(cmd, caller, C_FAIL,
433 _("Player name prefix '%s' is ambiguous."), name);
434 break;
435 case M_PRE_FAIL:
436 cmd_reply(cmd, caller, C_FAIL,
437 _("No player by the name of '%s'."), name);
438 break;
439 default:
440 cmd_reply(cmd, caller, C_FAIL,
441 _("Unexpected match_result %d (%s) for '%s'."),
442 match_result, _(m_pre_description(match_result)), name);
443 log_error("Unexpected match_result %d (%s) for '%s'.",
444 match_result, m_pre_description(match_result), name);
448 /**************************************************************************
449 Command specific argument parsing has detected that connection argument
450 is invalid. This function is common handling for that situation.
451 **************************************************************************/
452 static void cmd_reply_no_such_conn(enum command_id cmd,
453 struct connection *caller,
454 const char *name,
455 enum m_pre_result match_result)
457 switch(match_result) {
458 case M_PRE_EMPTY:
459 cmd_reply(cmd, caller, C_SYNTAX,
460 _("Name is empty, so cannot be a connection."));
461 break;
462 case M_PRE_LONG:
463 cmd_reply(cmd, caller, C_SYNTAX,
464 _("Name is too long, so cannot be a connection."));
465 break;
466 case M_PRE_AMBIGUOUS:
467 cmd_reply(cmd, caller, C_FAIL,
468 _("Connection name prefix '%s' is ambiguous."), name);
469 break;
470 case M_PRE_FAIL:
471 cmd_reply(cmd, caller, C_FAIL,
472 _("No connection by the name of '%s'."), name);
473 break;
474 default:
475 cmd_reply(cmd, caller, C_FAIL,
476 _("Unexpected match_result %d (%s) for '%s'."),
477 match_result, _(m_pre_description(match_result)), name);
478 log_error("Unexpected match_result %d (%s) for '%s'.",
479 match_result, m_pre_description(match_result), name);
483 /**************************************************************************
484 Start sending game info to metaserver.
485 **************************************************************************/
486 static void open_metaserver_connection(struct connection *caller,
487 bool persistent)
489 server_open_meta(persistent);
490 if (send_server_info_to_metaserver(META_INFO)) {
491 cmd_reply(CMD_METACONN, caller, C_OK,
492 _("Open metaserver connection to [%s]."),
493 meta_addr_port());
497 /**************************************************************************
498 Stop sending game info to metaserver.
499 **************************************************************************/
500 static void close_metaserver_connection(struct connection *caller)
502 if (send_server_info_to_metaserver(META_GOODBYE)) {
503 server_close_meta();
504 cmd_reply(CMD_METACONN, caller, C_OK,
505 _("Close metaserver connection to [%s]."),
506 meta_addr_port());
510 /**************************************************************************
511 Handle metaconnection command.
512 **************************************************************************/
513 static bool metaconnection_command(struct connection *caller, char *arg,
514 bool check)
516 bool persistent = FALSE;
518 if ((*arg == '\0')
519 || (!strcmp(arg, "?"))) {
520 if (is_metaserver_open()) {
521 cmd_reply(CMD_METACONN, caller, C_COMMENT,
522 _("Metaserver connection is open."));
523 } else {
524 cmd_reply(CMD_METACONN, caller, C_COMMENT,
525 _("Metaserver connection is closed."));
527 return TRUE;
530 if (!fc_strcasecmp(arg, "p")
531 || !fc_strcasecmp(arg, "persistent")) {
532 persistent = TRUE;
535 if (persistent
536 || !fc_strcasecmp(arg, "u")
537 || !fc_strcasecmp(arg, "up")) {
538 if (!is_metaserver_open()) {
539 if (!check) {
540 open_metaserver_connection(caller, persistent);
542 } else {
543 cmd_reply(CMD_METACONN, caller, C_METAERROR,
544 _("Metaserver connection is already open."));
545 return FALSE;
547 } else if (!fc_strcasecmp(arg, "d")
548 || !fc_strcasecmp(arg, "down")) {
549 if (is_metaserver_open()) {
550 if (!check) {
551 close_metaserver_connection(caller);
553 } else {
554 cmd_reply(CMD_METACONN, caller, C_METAERROR,
555 _("Metaserver connection is already closed."));
556 return FALSE;
558 } else {
559 cmd_reply(CMD_METACONN, caller, C_METAERROR,
560 _("Argument must be 'u', 'up', 'd', 'down', 'p', 'persistent', or '?'."));
561 return FALSE;
563 return TRUE;
566 /**************************************************************************
567 Handle metapatches command.
568 **************************************************************************/
569 static bool metapatches_command(struct connection *caller,
570 char *arg, bool check)
572 if (check) {
573 return TRUE;
576 set_meta_patches_string(arg);
578 if (is_metaserver_open()) {
579 send_server_info_to_metaserver(META_INFO);
580 cmd_reply(CMD_METAPATCHES, caller, C_OK,
581 _("Metaserver patches string set to '%s'."), arg);
582 } else {
583 cmd_reply(CMD_METAPATCHES, caller, C_OK,
584 _("Metaserver patches string set to '%s', "
585 "not reporting to metaserver."), arg);
588 return TRUE;
591 /**************************************************************************
592 Handle metamessage command.
593 **************************************************************************/
594 static bool metamessage_command(struct connection *caller,
595 char *arg, bool check)
597 if (check) {
598 return TRUE;
601 set_user_meta_message_string(arg);
602 if (is_metaserver_open()) {
603 send_server_info_to_metaserver(META_INFO);
604 cmd_reply(CMD_METAMESSAGE, caller, C_OK,
605 _("Metaserver message string set to '%s'."), arg);
606 } else {
607 cmd_reply(CMD_METAMESSAGE, caller, C_OK,
608 _("Metaserver message string set to '%s', "
609 "not reporting to metaserver."), arg);
612 return TRUE;
615 /**************************************************************************
616 Handle metaserver command.
617 **************************************************************************/
618 static bool metaserver_command(struct connection *caller, char *arg,
619 bool check)
621 if (check) {
622 return TRUE;
624 close_metaserver_connection(caller);
626 sz_strlcpy(srvarg.metaserver_addr, arg);
628 cmd_reply(CMD_METASERVER, caller, C_OK,
629 _("Metaserver is now [%s]."), meta_addr_port());
630 return TRUE;
633 /**************************************************************************
634 Returns the serverid
635 **************************************************************************/
636 static bool show_serverid(struct connection *caller, char *arg)
638 cmd_reply(CMD_SRVID, caller, C_COMMENT, _("Server id: %s"), srvarg.serverid);
640 return TRUE;
643 /**************************************************************************
644 For command "save foo";
645 Save the game, with filename=arg, provided server state is ok.
646 **************************************************************************/
647 static bool save_command(struct connection *caller, char *arg, bool check)
649 if (is_restricted(caller)) {
650 cmd_reply(CMD_SAVE, caller, C_FAIL,
651 _("You cannot save games manually on this server."));
652 return FALSE;
654 if (!check) {
655 save_game(arg, "User request", FALSE);
657 return TRUE;
660 /**************************************************************************
661 For command "scensave foo";
662 Save the game, with filename=arg, provided server state is ok.
663 **************************************************************************/
664 static bool scensave_command(struct connection *caller, char *arg, bool check)
666 if (is_restricted(caller)) {
667 cmd_reply(CMD_SAVE, caller, C_FAIL,
668 _("You cannot save games manually on this server."));
669 return FALSE;
671 if (!check) {
672 save_game(arg, "Scenario", TRUE);
674 return TRUE;
677 /**************************************************************************
678 Handle ai player ai toggling.
679 **************************************************************************/
680 void toggle_ai_player_direct(struct connection *caller, struct player *pplayer)
682 fc_assert_ret(pplayer != NULL);
684 if (!pplayer->ai_controlled) {
685 cmd_reply(CMD_AITOGGLE, caller, C_OK,
686 _("%s is now under AI control."),
687 player_name(pplayer));
688 player_set_to_ai_mode(pplayer,
689 !ai_level_is_valid(pplayer->ai_common.skill_level)
690 ? game.info.skill_level
691 : pplayer->ai_common.skill_level);
692 fc_assert(pplayer->ai_controlled == TRUE);
693 } else {
694 cmd_reply(CMD_AITOGGLE, caller, C_OK,
695 _("%s is now under human control."),
696 player_name(pplayer));
697 player_set_under_human_control(pplayer);
698 fc_assert(pplayer->ai_controlled == FALSE);
702 /**************************************************************************
703 Handle aitoggle command.
704 **************************************************************************/
705 static bool toggle_ai_command(struct connection *caller, char *arg, bool check)
707 enum m_pre_result match_result;
708 struct player *pplayer;
710 pplayer = player_by_name_prefix(arg, &match_result);
712 if (!pplayer) {
713 cmd_reply_no_such_player(CMD_AITOGGLE, caller, arg, match_result);
714 return FALSE;
715 } else if (!check) {
716 toggle_ai_player_direct(caller, pplayer);
717 send_player_info_c(pplayer, game.est_connections);
719 return TRUE;
722 /**************************************************************************
723 Creates a named AI player. The function can be called befor the start
724 of the game (see creat_command_pregame()) and for a running game
725 (see creat_command_newcomer(). In the later case, first free player slots
726 are used before the slots of dead players are (re)used.
727 **************************************************************************/
728 static bool create_command(struct connection *caller, const char *str,
729 bool check)
731 enum rfc_status status;
732 char buf[MAX_LEN_CONSOLE_LINE];
734 /* 2 legal arguments, and extra space for stuffing illegal part */
735 char *arg[3];
736 int ntokens;
737 const char *ai_type_name;
739 sz_strlcpy(buf, str);
740 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
742 if (ntokens == 1) {
743 ai_type_name = default_ai_type_name();
744 } else if (ntokens == 2) {
745 ai_type_name = arg[1];
746 } else {
747 cmd_reply(CMD_CREATE, caller, C_SYNTAX,
748 _("Wrong number of arguments to create command."));
749 free_tokens(arg, ntokens);
750 return FALSE;
753 if (game_was_started()) {
754 status = create_command_newcomer(arg[0], ai_type_name, check,
755 NULL, NULL, buf, sizeof(buf));
756 } else {
757 status = create_command_pregame(arg[0], ai_type_name, check,
758 NULL, buf, sizeof(buf));
761 free_tokens(arg, ntokens);
763 if (status != C_OK) {
764 /* No player created. */
765 cmd_reply(CMD_CREATE, caller, status, "%s", buf);
766 return FALSE;
769 if (strlen(buf) > 0) {
770 /* Send a notification. */
771 cmd_reply(CMD_CREATE, caller, C_OK, "%s", buf);
774 return TRUE;
777 /**************************************************************************
778 Try to add a player to a running game in the following order:
780 1. Try to reuse the slot of a dead player with the username 'name'.
781 2. Try to reuse the slot of a dead player.
782 3. Try to use an empty player slot.
784 If 'pnation' is defined this nation is used for the new player.
785 **************************************************************************/
786 enum rfc_status create_command_newcomer(const char *name,
787 const char *ai,
788 bool check,
789 struct nation_type *pnation,
790 struct player **newplayer,
791 char *buf, size_t buflen)
793 struct player *pplayer = NULL;
794 struct research *presearch;
795 bool new_slot = FALSE;
797 /* Check player name. */
798 if (!player_name_check(name, buf, buflen)) {
799 return C_SYNTAX;
802 /* Check first if we can replace a player with
803 * [1a] - the same username. */
804 pplayer = player_by_user(name);
805 if (pplayer && pplayer->is_alive) {
806 fc_snprintf(buf, buflen,
807 _("A living user already exists by that name."));
808 return C_BOUNCE;
811 /* [1b] - the same player name. */
812 pplayer = player_by_name(name);
813 if (pplayer && pplayer->is_alive) {
814 fc_snprintf(buf, buflen,
815 _("A living player already exists by that name."));
816 return C_BOUNCE;
819 if (pnation) {
820 if (!nation_is_in_current_set(pnation)) {
821 fc_snprintf(buf, buflen,
822 _("Can't create player, requested nation %s not in "
823 "current nation set."),
824 nation_plural_translation(pnation));
825 return C_FAIL;
827 players_iterate(aplayer) {
828 if (0 > nations_match(pnation, nation_of_player(aplayer), FALSE)) {
829 fc_snprintf(buf, buflen,
830 _("Can't create players, nation %s conflicts with %s."),
831 nation_plural_for_player(aplayer),
832 nation_plural_for_player(pplayer));
833 return C_FAIL;
835 } players_iterate_end;
836 } else {
837 /* Try to find a nation. */
838 pnation = pick_a_nation(NULL, FALSE, TRUE, NOT_A_BARBARIAN);
839 if (pnation == NO_NATION_SELECTED) {
840 fc_snprintf(buf, buflen,
841 _("Can't create players, no nations available."));
842 return C_FAIL;
846 if (check) {
847 /* All code below will change the game state. */
849 /* Return an empty string. */
850 buf[0] = '\0';
852 return C_OK;
855 if (pplayer) {
856 /* [1] Replace a player. 'pplayer' was set above. */
857 fc_snprintf(buf, buflen,
858 _("%s is replacing dead player %s as an AI-controlled "
859 "player."), name, player_name(pplayer));
860 /* remove player and thus free a player slot */
861 server_remove_player(pplayer);
862 pplayer = NULL;
863 } else if (player_count() == player_slot_count()) {
864 /* [2] All player slots are used; try to remove a dead player. */
865 players_iterate(aplayer) {
866 if (!aplayer->is_alive) {
867 fc_snprintf(buf, buflen,
868 _("%s is replacing dead player %s as an AI-controlled "
869 "player."), name, player_name(aplayer));
870 /* remove player and thus free a player slot */
871 server_remove_player(aplayer);
873 } players_iterate_end;
874 } else {
875 /* [3] An empty player slot must be used for the new player. */
876 new_slot = TRUE;
879 if (new_slot) {
880 if (normal_player_count() == game.server.max_players) {
882 fc_assert(game.server.max_players < MAX_NUM_PLAYERS);
884 game.server.max_players++;
885 log_debug("Increased 'maxplayers' for creation of a new player.");
889 /* Create the new player. */
890 pplayer = server_create_player(-1, ai, NULL, FALSE);
891 if (!pplayer) {
892 fc_snprintf(buf, buflen, _("Failed to create new player %s."), name);
893 return C_FAIL;
896 if (new_slot) {
897 /* 'buf' must be set if a new player slot is used. */
898 fc_snprintf(buf, buflen, _("New player %s created."), name);
901 /* We have a player; now initialise all needed data. */
902 (void) aifill(game.info.aifill);
904 /* Initialise player. */
905 server_player_init(pplayer, TRUE, TRUE);
907 player_nation_defaults(pplayer, pnation, FALSE);
908 pplayer->government = pplayer->target_government =
909 init_government_of_nation(pnation);
910 /* Find a color for the new player. */
911 assign_player_colors();
913 /* TRANS: keep one space at the beginning of the string. */
914 cat_snprintf(buf, buflen, _(" Nation of the new player: %s."),
915 nation_rule_name(pnation));
917 presearch = research_get(pplayer);
918 init_tech(presearch, TRUE);
919 give_initial_techs(presearch, 0);
921 server_player_set_name(pplayer, name);
922 sz_strlcpy(pplayer->username, _(ANON_USER_NAME));
923 pplayer->unassigned_user = TRUE;
925 pplayer->was_created = TRUE; /* must use /remove explicitly to remove */
926 pplayer->ai_controlled = TRUE;
927 set_ai_level_directer(pplayer, game.info.skill_level);
929 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
931 send_player_info_c(pplayer, NULL);
932 /* Send updated diplstate information to all players. */
933 send_player_diplstate_c(NULL, NULL);
934 /* Send research info after player info, else the client will complain
935 * about invalid team. */
936 send_research_info(presearch, NULL);
937 (void) send_server_info_to_metaserver(META_INFO);
939 if (newplayer != NULL) {
940 *newplayer = pplayer;
942 return C_OK;
945 /**************************************************************************
946 Create player in pregame.
947 **************************************************************************/
948 enum rfc_status create_command_pregame(const char *name,
949 const char *ai,
950 bool check,
951 struct player **newplayer,
952 char *buf, size_t buflen)
954 char leader_name[MAX_LEN_NAME]; /* Must be in whole function scope */
955 struct player *pplayer = NULL;
956 bool rand_name = FALSE;
958 if (name[0] == '\0') {
959 int filled = 1;
961 do {
962 fc_snprintf(leader_name, sizeof(leader_name), "%s*%d", ai, filled++);
963 } while (player_by_name(leader_name));
965 name = leader_name;
966 rand_name = TRUE;
969 if (!player_name_check(name, buf, buflen)) {
970 return C_SYNTAX;
973 if (NULL != player_by_name(name)) {
974 fc_snprintf(buf, buflen,
975 _("A player already exists by that name."));
976 return C_BOUNCE;
978 if (NULL != player_by_user(name)) {
979 fc_snprintf(buf, buflen,
980 _("A user already exists by that name."));
981 return C_BOUNCE;
984 /* Search for first uncontrolled player */
985 pplayer = find_uncontrolled_player();
987 if (NULL == pplayer) {
988 /* Check that we are not going over max players setting */
989 if (normal_player_count() >= game.server.max_players) {
990 fc_snprintf(buf, buflen,
991 _("Can't add more players, server is full."));
992 return C_FAIL;
994 /* Check that we have nations available */
995 if (normal_player_count() >= server.playable_nations) {
996 if (nation_set_count() > 1) {
997 fc_snprintf(buf, buflen,
998 _("Can't add more players, not enough playable nations "
999 "in current nation set (see 'nationset' setting)."));
1000 } else {
1001 fc_snprintf(buf, buflen,
1002 _("Can't add more players, not enough playable nations."));
1004 return C_FAIL;
1008 if (pplayer) {
1009 struct ai_type *ait = ai_type_by_name(ai);
1011 if (ait == NULL) {
1012 fc_snprintf(buf, buflen,
1013 _("There is no AI type %s."), ai);
1014 return C_FAIL;
1018 if (check) {
1019 /* All code below will change the game state. */
1021 /* Return an empty string. */
1022 buf[0] = '\0';
1024 return C_OK;
1027 if (pplayer) {
1028 fc_snprintf(buf, buflen,
1029 /* TRANS: <name> replacing <name> ... */
1030 _("%s replacing %s as an AI-controlled player."),
1031 name, player_name(pplayer));
1033 team_remove_player(pplayer);
1034 pplayer->ai = ai_type_by_name(ai);
1035 } else {
1036 /* add new player */
1037 pplayer = server_create_player(-1, ai, NULL, FALSE);
1038 /* pregame so no need to assign_player_colors() */
1039 if (!pplayer) {
1040 fc_snprintf(buf, buflen,
1041 _("Failed to create new player %s."), name);
1042 return C_GENFAIL;
1045 fc_snprintf(buf, buflen,
1046 _("%s has been added as an AI-controlled player (%s)."),
1047 name, ai_name(pplayer->ai));
1049 server_player_init(pplayer, FALSE, TRUE);
1051 server_player_set_name(pplayer, name);
1052 sz_strlcpy(pplayer->username, _(ANON_USER_NAME));
1053 pplayer->unassigned_user = TRUE;
1055 pplayer->was_created = TRUE; /* must use /remove explicitly to remove */
1056 pplayer->random_name = rand_name;
1057 pplayer->ai_controlled = TRUE;
1058 set_ai_level_directer(pplayer, game.info.skill_level);
1059 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
1060 send_player_info_c(pplayer, game.est_connections);
1062 (void) aifill(game.info.aifill);
1063 reset_all_start_commands(TRUE);
1064 (void) send_server_info_to_metaserver(META_INFO);
1066 if (newplayer != NULL) {
1067 *newplayer = pplayer;
1069 return C_OK;
1072 /**************************************************************************
1073 Handle remove command.
1074 **************************************************************************/
1075 static bool remove_player_command(struct connection *caller, char *arg,
1076 bool check)
1078 enum m_pre_result match_result;
1079 struct player *pplayer;
1080 char name[MAX_LEN_NAME];
1082 pplayer = player_by_name_prefix(arg, &match_result);
1084 if (NULL == pplayer) {
1085 cmd_reply_no_such_player(CMD_REMOVE, caller, arg, match_result);
1086 return FALSE;
1089 if (game_was_started() && caller && caller->access_level < ALLOW_ADMIN) {
1090 cmd_reply(CMD_REMOVE, caller, C_FAIL,
1091 _("Command level '%s' or greater needed to remove a player "
1092 "once the game has started."), cmdlevel_name(ALLOW_ADMIN));
1093 return FALSE;
1095 if (check) {
1096 return TRUE;
1099 sz_strlcpy(name, player_name(pplayer));
1100 server_remove_player(pplayer);
1101 if (!caller || caller->used) { /* may have removed self */
1102 cmd_reply(CMD_REMOVE, caller, C_OK,
1103 _("Removed player %s from the game."), name);
1105 (void) aifill(game.info.aifill);
1106 return TRUE;
1109 /**************************************************************************
1110 Main entry point for the read command.
1111 **************************************************************************/
1112 static bool read_command(struct connection *caller, char *arg, bool check,
1113 int read_recursion)
1115 return read_init_script_real(caller, arg, FALSE, check, read_recursion);
1118 /**************************************************************************
1119 Main entry point for reading an init script.
1120 **************************************************************************/
1121 bool read_init_script(struct connection *caller, char *script_filename,
1122 bool from_cmdline, bool check)
1124 return read_init_script_real(caller, script_filename, from_cmdline,
1125 check, 0);
1128 /**************************************************************************
1129 Returns FALSE iff there was an error.
1131 Security: We will look for a file with mandatory extension '.serv',
1132 and on public servers we will not look outside the data directories.
1133 As long as the user cannot create files with arbitrary names in the
1134 root of the data directories, this should ensure that we will not be
1135 tricked into loading non-approved content. The script is read with the
1136 permissions of the caller, so it will in any case not lead to elevated
1137 permissions unless there are other bugs.
1138 **************************************************************************/
1139 static bool read_init_script_real(struct connection *caller,
1140 char *script_filename, bool from_cmdline,
1141 bool check, int read_recursion)
1143 FILE *script_file;
1144 const char extension[] = ".serv";
1145 char serv_filename[strlen(extension) + strlen(script_filename) + 2];
1146 char tilde_filename[4096];
1147 const char *real_filename;
1149 /* check recursion depth */
1150 if (read_recursion > GAME_MAX_READ_RECURSION) {
1151 log_error("Error: recursive calls to read!");
1152 return FALSE;
1155 /* abuse real_filename to find if we already have a .serv extension */
1156 real_filename = script_filename + strlen(script_filename)
1157 - MIN(strlen(extension), strlen(script_filename));
1158 if (strcmp(real_filename, extension) != 0) {
1159 fc_snprintf(serv_filename, sizeof(serv_filename), "%s%s",
1160 script_filename, extension);
1161 } else {
1162 sz_strlcpy(serv_filename, script_filename);
1165 if (is_restricted(caller) && !from_cmdline) {
1166 if (!is_safe_filename(serv_filename)) {
1167 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1168 _("Name \"%s\" disallowed for security reasons."),
1169 serv_filename);
1170 return FALSE;
1172 sz_strlcpy(tilde_filename, serv_filename);
1173 } else {
1174 interpret_tilde(tilde_filename, sizeof(tilde_filename), serv_filename);
1177 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
1178 if (!real_filename) {
1179 if (is_restricted(caller) && !from_cmdline) {
1180 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1181 _("No command script found by the name \"%s\"."),
1182 serv_filename);
1183 return FALSE;
1185 /* File is outside data directories */
1186 real_filename = tilde_filename;
1189 log_testmatic_alt(LOG_NORMAL, _("Loading script file '%s'."), real_filename);
1191 if (is_reg_file_for_access(real_filename, FALSE)
1192 && (script_file = fc_fopen(real_filename, "r"))) {
1193 char buffer[MAX_LEN_CONSOLE_LINE];
1195 /* the size is set as to not overflow buffer in handle_stdin_input */
1196 while (fgets(buffer, MAX_LEN_CONSOLE_LINE - 1, script_file)) {
1197 /* Execute script contents with same permissions as caller */
1198 handle_stdin_input_real(caller, buffer, check, read_recursion + 1);
1200 fclose(script_file);
1202 show_ruleset_info(caller, CMD_READ_SCRIPT, check, read_recursion);
1204 return TRUE;
1205 } else {
1206 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1207 _("Cannot read command line scriptfile '%s'."), real_filename);
1208 if (NULL != caller) {
1209 log_error(_("Could not read script file '%s'."), real_filename);
1211 return FALSE;
1215 /**************************************************************************
1216 Write current settings to new init script.
1218 (Should this take a 'caller' argument for output? --dwp)
1219 **************************************************************************/
1220 static void write_init_script(char *script_filename)
1222 char real_filename[1024], buf[256];
1223 FILE *script_file;
1225 interpret_tilde(real_filename, sizeof(real_filename), script_filename);
1227 if (is_reg_file_for_access(real_filename, TRUE)
1228 && (script_file = fc_fopen(real_filename, "w"))) {
1229 fprintf(script_file,
1230 "#FREECIV SERVER COMMAND FILE, version %s\n", VERSION_STRING);
1231 fputs("# These are server options saved from a running freeciv-server.\n",
1232 script_file);
1234 /* first rulesetdir. Setting rulesetdir resets the settings to their
1235 * default value, so they would be lost if placed before this. */
1236 fprintf(script_file, "rulesetdir %s\n", game.server.rulesetdir);
1238 /* some state info from commands (we can't save everything) */
1240 fprintf(script_file, "cmdlevel %s new\n",
1241 cmdlevel_name(default_access_level));
1243 fprintf(script_file, "cmdlevel %s first\n",
1244 cmdlevel_name(first_access_level));
1246 fprintf(script_file, "%s\n",
1247 ai_level_cmd(game.info.skill_level));
1249 if (*srvarg.metaserver_addr != '\0' &&
1250 ((0 != strcmp(srvarg.metaserver_addr, DEFAULT_META_SERVER_ADDR)))) {
1251 fprintf(script_file, "metaserver %s\n", meta_addr_port());
1254 if (0 != strcmp(get_meta_patches_string(), default_meta_patches_string())) {
1255 fprintf(script_file, "metapatches %s\n", get_meta_patches_string());
1257 if (0 != strcmp(get_meta_message_string(), default_meta_message_string())) {
1258 fprintf(script_file, "metamessage %s\n", get_meta_message_string());
1261 /* then, the 'set' option settings */
1263 settings_iterate(SSET_ALL, pset) {
1264 fprintf(script_file, "set %s \"%s\"\n", setting_name(pset),
1265 setting_value_name(pset, FALSE, buf, sizeof(buf)));
1266 } settings_iterate_end;
1268 fclose(script_file);
1270 } else {
1271 log_error(_("Could not write script file '%s'."), real_filename);
1275 /**************************************************************************
1276 Generate init script from settings currently in use
1277 **************************************************************************/
1278 static bool write_command(struct connection *caller, char *arg, bool check)
1280 if (is_restricted(caller)) {
1281 cmd_reply(CMD_WRITE_SCRIPT, caller, C_FAIL,
1282 _("You cannot use the write command on this server"
1283 " for security reasons."));
1284 return FALSE;
1285 } else if (!check) {
1286 write_init_script(arg);
1288 return TRUE;
1291 /**************************************************************************
1292 set ptarget's cmdlevel to level if caller is allowed to do so
1293 **************************************************************************/
1294 static bool set_cmdlevel(struct connection *caller,
1295 struct connection *ptarget,
1296 enum cmdlevel level)
1298 /* Only ever call me for specific connection. */
1299 fc_assert_ret_val(ptarget != NULL, FALSE);
1301 if (caller && ptarget->access_level > caller->access_level) {
1303 * This command is intended to be used at ctrl access level
1304 * and thus this if clause is needed.
1305 * (Imagine a ctrl level access player that wants to change
1306 * access level of a hack level access player)
1307 * At the moment it can be used only by hack access level
1308 * and thus this clause is never used.
1310 cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1311 _("Cannot decrease command access level '%s' "
1312 "for connection '%s'; you only have '%s'."),
1313 cmdlevel_name(ptarget->access_level),
1314 ptarget->username,
1315 cmdlevel_name(caller->access_level));
1316 return FALSE;
1317 } else {
1318 conn_set_access(ptarget, level, TRUE);
1319 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1320 _("Command access level set to '%s' for connection %s."),
1321 cmdlevel_name(level), ptarget->username);
1322 return TRUE;
1326 /********************************************************************
1327 Returns true if there is at least one established connection.
1328 *********************************************************************/
1329 static bool a_connection_exists(void)
1331 return conn_list_size(game.est_connections) > 0;
1334 /********************************************************************
1335 Return whether first access level is already taken.
1336 *********************************************************************/
1337 static bool is_first_access_level_taken(void)
1339 conn_list_iterate(game.est_connections, pconn) {
1340 if (pconn->access_level >= first_access_level) {
1341 return TRUE;
1344 conn_list_iterate_end;
1345 return FALSE;
1348 /********************************************************************
1349 Return access level for next connection
1350 *********************************************************************/
1351 enum cmdlevel access_level_for_next_connection(void)
1353 if ((first_access_level > default_access_level)
1354 && !a_connection_exists()) {
1355 return first_access_level;
1356 } else {
1357 return default_access_level;
1361 /********************************************************************
1362 Check if first access level is available and if it is, notify
1363 connections about it.
1364 *********************************************************************/
1365 void notify_if_first_access_level_is_available(void)
1367 if (first_access_level > default_access_level
1368 && !is_first_access_level_taken()) {
1369 notify_conn(NULL, NULL, E_SETTING, ftc_any,
1370 _("Anyone can now become game organizer "
1371 "'%s' by issuing the 'first' command."),
1372 cmdlevel_name(first_access_level));
1376 /**************************************************************************
1377 Change command access level for individual player, or all, or new.
1378 **************************************************************************/
1379 static bool cmdlevel_command(struct connection *caller, char *str, bool check)
1381 char *arg[2];
1382 int ntokens;
1383 bool ret = FALSE;
1384 enum m_pre_result match_result;
1385 enum cmdlevel level;
1386 struct connection *ptarget;
1388 ntokens = get_tokens(str, arg, 2, TOKEN_DELIMITERS);
1390 if (ntokens == 0) {
1391 /* No argument supplied; list the levels */
1392 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1393 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1394 _("Command access levels in effect:"));
1395 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1396 conn_list_iterate(game.est_connections, pconn) {
1397 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, "cmdlevel %s %s",
1398 cmdlevel_name(conn_get_access(pconn)), pconn->username);
1399 } conn_list_iterate_end;
1400 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1401 _("Command access level for new connections: %s"),
1402 cmdlevel_name(default_access_level));
1403 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1404 _("Command access level for first player to take it: %s"),
1405 cmdlevel_name(first_access_level));
1406 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1407 return TRUE;
1410 /* A level name was supplied; set the level. */
1411 level = cmdlevel_by_name(arg[0], fc_strcasecmp);
1412 if (!cmdlevel_is_valid(level)) {
1413 const char *cmdlevel_names[CMDLEVEL_COUNT];
1414 struct astring astr = ASTRING_INIT;
1415 int i = 0;
1417 for (level = cmdlevel_begin(); level != cmdlevel_end();
1418 level = cmdlevel_next(level)) {
1419 cmdlevel_names[i++] = cmdlevel_name(level);
1421 cmd_reply(CMD_CMDLEVEL, caller, C_SYNTAX,
1422 /* TRANS: comma and 'or' separated list of access levels */
1423 _("Command access level must be one of %s."),
1424 astr_build_or_list(&astr, cmdlevel_names, i));
1425 astr_free(&astr);
1426 goto CLEAN_UP;
1427 } else if (caller && level > conn_get_access(caller)) {
1428 cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1429 _("Cannot increase command access level to '%s';"
1430 " you only have '%s' yourself."),
1431 arg[0], cmdlevel_name(conn_get_access(caller)));
1432 goto CLEAN_UP;
1435 if (check) {
1436 return TRUE; /* looks good */
1439 if (ntokens == 1) {
1440 /* No playername supplied: set for all connections */
1441 conn_list_iterate(game.est_connections, pconn) {
1442 if (pconn != caller) {
1443 (void) set_cmdlevel(caller, pconn, level);
1445 } conn_list_iterate_end;
1447 /* Set the caller access level at last, because it could make the
1448 * previous operations impossible if set before. */
1449 if (caller) {
1450 (void) set_cmdlevel(caller, caller, level);
1453 /* Set default access for new connections. */
1454 default_access_level = level;
1455 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1456 _("Command access level set to '%s' for new players."),
1457 cmdlevel_name(level));
1458 /* Set default access for first connection. */
1459 first_access_level = level;
1460 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1461 _("Command access level set to '%s' "
1462 "for first player to grab it."),
1463 cmdlevel_name(level));
1465 ret = TRUE;
1467 } else if (fc_strcasecmp(arg[1], "new") == 0) {
1468 default_access_level = level;
1469 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1470 _("Command access level set to '%s' for new players."),
1471 cmdlevel_name(level));
1472 if (level > first_access_level) {
1473 first_access_level = level;
1474 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1475 _("Command access level set to '%s' "
1476 "for first player to grab it."),
1477 cmdlevel_name(level));
1480 ret = TRUE;
1482 } else if (fc_strcasecmp(arg[1], "first") == 0) {
1483 first_access_level = level;
1484 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1485 _("Command access level set to '%s' "
1486 "for first player to grab it."),
1487 cmdlevel_name(level));
1488 if (level < default_access_level) {
1489 default_access_level = level;
1490 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1491 _("Command access level set to '%s' for new players."),
1492 cmdlevel_name(level));
1495 ret = TRUE;
1497 } else if ((ptarget = conn_by_user_prefix(arg[1], &match_result))) {
1498 if (set_cmdlevel(caller, ptarget, level)) {
1499 ret = TRUE;
1501 } else {
1502 cmd_reply_no_such_conn(CMD_CMDLEVEL, caller, arg[1], match_result);
1505 CLEAN_UP:
1506 free_tokens(arg, ntokens);
1507 return ret;
1510 /**************************************************************************
1511 This special command to set the command access level is not included into
1512 cmdlevel_command because of its lower access level: it can be used
1513 to promote one's own connection to 'first come' cmdlevel if that isn't
1514 already taken.
1515 **************************************************************************/
1516 static bool firstlevel_command(struct connection *caller, bool check)
1518 if (!caller) {
1519 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1520 _("The 'first' command makes no sense from the server command line."));
1521 return FALSE;
1522 } else if (caller->access_level >= first_access_level) {
1523 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1524 _("You already have command access level '%s' or better."),
1525 cmdlevel_name(first_access_level));
1526 return FALSE;
1527 } else if (is_first_access_level_taken()) {
1528 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1529 _("Someone else is already game organizer."));
1530 return FALSE;
1531 } else if (!check) {
1532 conn_set_access(caller, first_access_level, FALSE);
1533 cmd_reply(CMD_FIRSTLEVEL, caller, C_OK,
1534 _("Connection %s has opted to become the game organizer."),
1535 caller->username);
1537 return TRUE;
1540 /**************************************************************************
1541 Adjust default command level on game start.
1542 **************************************************************************/
1543 void set_running_game_access_level(void)
1545 if (default_access_level > ALLOW_BASIC) {
1546 notify_conn(NULL, NULL, E_SETTING, ftc_server,
1547 _("Default cmdlevel lowered to 'basic' on game start."));
1548 default_access_level = ALLOW_BASIC;
1552 /**************************************************************************
1553 Returns possible parameters for the commands that take server options
1554 as parameters (CMD_EXPLAIN and CMD_SET).
1555 **************************************************************************/
1556 static const char *optname_accessor(int i)
1558 return setting_name(setting_by_number(i));
1561 #ifdef FREECIV_HAVE_LIBREADLINE
1562 /**************************************************************************
1563 Returns possible parameters for the /show command.
1564 **************************************************************************/
1565 static const char *olvlname_accessor(int i)
1567 if (i == 0) {
1568 return "rulesetdir";
1569 } else if (i < OLEVELS_NUM+1) {
1570 return sset_level_name(i-1);
1571 } else {
1572 return optname_accessor(i-OLEVELS_NUM-1);
1575 #endif /* FREECIV_HAVE_LIBREADLINE */
1577 /**************************************************************************
1578 Set timeout options.
1579 **************************************************************************/
1580 static bool timeout_command(struct connection *caller, char *str, bool check)
1582 char buf[MAX_LEN_CONSOLE_LINE];
1583 char *arg[4];
1584 int i = 0, ntokens;
1585 int *timeouts[4];
1587 timeouts[0] = &game.server.timeoutint;
1588 timeouts[1] = &game.server.timeoutintinc;
1589 timeouts[2] = &game.server.timeoutinc;
1590 timeouts[3] = &game.server.timeoutincmult;
1592 sz_strlcpy(buf, str);
1593 ntokens = get_tokens(buf, arg, 4, TOKEN_DELIMITERS);
1595 for (i = 0; i < ntokens; i++) {
1596 if (!str_to_int(arg[i], timeouts[i])) {
1597 cmd_reply(CMD_TIMEOUT, caller, C_FAIL, _("Invalid argument %d."),
1598 i + 1);
1600 free(arg[i]);
1603 if (ntokens == 0) {
1604 cmd_reply(CMD_TIMEOUT, caller, C_SYNTAX, _("Usage:\n%s"),
1605 command_synopsis(command_by_number(CMD_TIMEOUT)));
1606 return FALSE;
1607 } else if (check) {
1608 return TRUE;
1611 cmd_reply(CMD_TIMEOUT, caller, C_OK, _("Dynamic timeout set to "
1612 "%d %d %d %d"),
1613 game.server.timeoutint, game.server.timeoutintinc,
1614 game.server.timeoutinc, game.server.timeoutincmult);
1616 /* if we set anything here, reset the counter */
1617 game.server.timeoutcounter = 1;
1618 return TRUE;
1621 /**************************************************************************
1622 Find option level number by name.
1623 **************************************************************************/
1624 static enum sset_level lookup_option_level(const char *name)
1626 enum sset_level i;
1628 for (i = SSET_ALL; i < OLEVELS_NUM; i++) {
1629 if (0 == fc_strcasecmp(name, sset_level_name(i))) {
1630 return i;
1634 return SSET_NONE;
1637 /* Special return values of lookup options */
1638 #define LOOKUP_OPTION_NO_RESULT (-1)
1639 #define LOOKUP_OPTION_AMBIGUOUS (-2)
1640 #define LOOKUP_OPTION_LEVEL_NAME (-3)
1641 #define LOOKUP_OPTION_RULESETDIR (-4)
1643 /**************************************************************************
1644 Find option index by name. Return index (>=0) on success, else returned
1645 - LOOKUP_OPTION_NO_RESULT if no suitable options were found
1646 - LOOKUP_OPTION_AMBIGUOUS if several matches were found
1647 - LOOKUP_OPTION_LEVEL_NAME if it is an option level
1648 - LOOKUP_OPTION_RULESETDIR if the argument is rulesetdir (special case)
1649 **************************************************************************/
1650 static int lookup_option(const char *name)
1652 enum m_pre_result result;
1653 int ind;
1655 /* Check for option levels, first off */
1656 if (lookup_option_level(name) != SSET_NONE) {
1657 return LOOKUP_OPTION_LEVEL_NAME;
1660 result = match_prefix(optname_accessor, settings_number(),
1661 0, fc_strncasecmp, NULL, name, &ind);
1662 if (M_PRE_AMBIGUOUS > result) {
1663 return ind;
1664 } else if (M_PRE_AMBIGUOUS == result) {
1665 return LOOKUP_OPTION_AMBIGUOUS;
1666 } else if ('\0' != name[0]
1667 && 0 == fc_strncasecmp("rulesetdir", name, strlen(name))) {
1668 return LOOKUP_OPTION_RULESETDIR;
1669 } else {
1670 return LOOKUP_OPTION_NO_RESULT;
1674 /**************************************************************************
1675 Show the caller detailed help for the single OPTION given by id.
1676 help_cmd is the command the player used.
1677 Only show option values for options which the caller can SEE.
1678 **************************************************************************/
1679 static void show_help_option(struct connection *caller,
1680 enum command_id help_cmd, int id)
1682 char val_buf[256], def_buf[256];
1683 struct setting *pset = setting_by_number(id);
1684 const char *sethelp;
1686 if (setting_short_help(pset)) {
1687 cmd_reply(help_cmd, caller, C_COMMENT,
1688 /* TRANS: <untranslated name> - translated short help */
1689 _("Option: %s - %s"), setting_name(pset),
1690 _(setting_short_help(pset)));
1691 } else {
1692 cmd_reply(help_cmd, caller, C_COMMENT,
1693 /* TRANS: <untranslated name> */
1694 _("Option: %s"), setting_name(pset));
1697 sethelp = setting_extra_help(pset, FALSE);
1698 if (strlen(sethelp) > 0) {
1699 char *help = fc_strdup(sethelp);
1701 fc_break_lines(help, LINE_BREAK);
1702 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
1703 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
1704 FC_FREE(help);
1706 cmd_reply(help_cmd, caller, C_COMMENT,
1707 _("Status: %s"), (setting_is_changeable(pset, NULL, NULL, 0)
1708 ? _("changeable") : _("fixed")));
1710 if (setting_is_visible(pset, caller)) {
1711 setting_value_name(pset, TRUE, val_buf, sizeof(val_buf));
1712 setting_default_name(pset, TRUE, def_buf, sizeof(def_buf));
1714 switch (setting_type(pset)) {
1715 case SSET_INT:
1716 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %d, %s %s, %s %d",
1717 _("Value:"), val_buf,
1718 _("Minimum:"), setting_int_min(pset),
1719 _("Default:"), def_buf,
1720 _("Maximum:"), setting_int_max(pset));
1721 break;
1722 case SSET_ENUM:
1724 int i;
1725 const char *value;
1727 cmd_reply(help_cmd, caller, C_COMMENT, _("Possible values:"));
1728 for (i = 0; (value = setting_enum_val(pset, i, FALSE)); i++) {
1729 cmd_reply(help_cmd, caller, C_COMMENT, "- %s: \"%s\"",
1730 value, setting_enum_val(pset, i, TRUE));
1733 /* Fall through. */
1734 case SSET_BOOL:
1735 case SSET_STRING:
1736 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %s",
1737 _("Value:"), val_buf, _("Default:"), def_buf);
1738 break;
1739 case SSET_BITWISE:
1741 int i;
1742 const char *value;
1744 cmd_reply(help_cmd, caller, C_COMMENT,
1745 _("Possible values (option can take any number of these):"));
1746 for (i = 0; (value = setting_bitwise_bit(pset, i, FALSE)); i++) {
1747 cmd_reply(help_cmd, caller, C_COMMENT, "- %s: \"%s\"",
1748 value, setting_bitwise_bit(pset, i, TRUE));
1750 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s",
1751 _("Value:"), val_buf);
1752 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s",
1753 _("Default:"), def_buf);
1755 break;
1760 /**************************************************************************
1761 Show the caller list of OPTIONS.
1762 help_cmd is the command the player used.
1763 Only show options which the caller can SEE.
1764 **************************************************************************/
1765 static void show_help_option_list(struct connection *caller,
1766 enum command_id help_cmd)
1768 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1769 cmd_reply(help_cmd, caller, C_COMMENT,
1770 _("Explanations are available for the following server options:"));
1771 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1772 if(!caller && con_get_style()) {
1773 settings_iterate(SSET_ALL, pset) {
1774 cmd_reply(help_cmd, caller, C_COMMENT, "%s", setting_name(pset));
1775 } settings_iterate_end
1776 } else {
1777 char buf[MAX_LEN_CONSOLE_LINE];
1778 int j = 0;
1779 buf[0] = '\0';
1781 settings_iterate(SSET_ALL, pset) {
1782 if (setting_is_visible(pset, caller)) {
1783 cat_snprintf(buf, sizeof(buf), "%-19s", setting_name(pset));
1784 if ((++j % 4) == 0) {
1785 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1786 buf[0] = '\0';
1789 } settings_iterate_end;
1791 if (buf[0] != '\0') {
1792 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1795 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1798 /**************************************************************************
1799 Handle explain command
1800 **************************************************************************/
1801 static bool explain_option(struct connection *caller, char *str, bool check)
1803 int cmd;
1805 remove_leading_trailing_spaces(str);
1807 if (*str != '\0') {
1808 cmd = lookup_option(str);
1809 if (cmd >= 0 && cmd < settings_number()) {
1810 show_help_option(caller, CMD_EXPLAIN, cmd);
1811 } else if (cmd == LOOKUP_OPTION_NO_RESULT
1812 || cmd == LOOKUP_OPTION_LEVEL_NAME
1813 || cmd == LOOKUP_OPTION_RULESETDIR) {
1814 cmd_reply(CMD_EXPLAIN, caller, C_FAIL,
1815 _("No explanation for that yet."));
1816 return FALSE;
1817 } else if (cmd == LOOKUP_OPTION_AMBIGUOUS) {
1818 cmd_reply(CMD_EXPLAIN, caller, C_FAIL, _("Ambiguous option name."));
1819 return FALSE;
1820 } else {
1821 log_error("Unexpected case %d in %s line %d", cmd, __FILE__,
1822 __FC_LINE__);
1823 return FALSE;
1825 } else {
1826 show_help_option_list(caller, CMD_EXPLAIN);
1828 return TRUE;
1831 /******************************************************************
1832 Send a message to all players
1833 ******************************************************************/
1834 static bool wall(char *str, bool check)
1836 if (!check) {
1837 notify_conn(NULL, NULL, E_MESSAGE_WALL, ftc_server_prompt,
1838 _("Server Operator: %s"), str);
1840 return TRUE;
1843 /******************************************************************
1844 Set message to send to all new connections
1845 ******************************************************************/
1846 static bool connectmsg_command(struct connection *caller, char *str,
1847 bool check)
1849 unsigned int bufsize = sizeof(game.server.connectmsg);
1851 if (is_restricted(caller)) {
1852 return FALSE;
1854 if (!check) {
1855 int i;
1856 int c = 0;
1858 for (i = 0; c < bufsize -1 && str[i] != '\0'; i++) {
1859 if (str[i] == '\\') {
1860 i++;
1862 if (str[i] == 'n') {
1863 game.server.connectmsg[c++] = '\n';
1864 } else {
1865 game.server.connectmsg[c++] = str[i];
1867 } else {
1868 game.server.connectmsg[c++] = str[i];
1872 game.server.connectmsg[c++] = '\0';
1874 if (c == bufsize) {
1875 /* Truncated */
1876 cmd_reply(CMD_CONNECTMSG, caller, C_WARNING,
1877 _("Connectmsg truncated to %u bytes."), bufsize);
1880 return TRUE;
1883 /******************************************************************
1884 Translate an AI level back to its CMD_* value.
1885 If we just used /set ailevel <num> we wouldn't have to do this - rp
1886 ******************************************************************/
1887 static enum command_id cmd_of_level(enum ai_level level)
1889 switch (level) {
1890 case AI_LEVEL_AWAY : return CMD_AWAY;
1891 case AI_LEVEL_HANDICAPPED : return CMD_HANDICAPPED;
1892 case AI_LEVEL_NOVICE : return CMD_NOVICE;
1893 case AI_LEVEL_EASY : return CMD_EASY;
1894 case AI_LEVEL_NORMAL : return CMD_NORMAL;
1895 case AI_LEVEL_HARD : return CMD_HARD;
1896 case AI_LEVEL_CHEATING : return CMD_CHEATING;
1897 #ifdef DEBUG
1898 case AI_LEVEL_EXPERIMENTAL : return CMD_EXPERIMENTAL;
1899 #endif /* DEBUG */
1900 case AI_LEVEL_COUNT : return CMD_NORMAL;
1902 log_error("Unknown AI level variant: %d.", level);
1903 return CMD_NORMAL;
1906 /******************************************************************
1907 Set an AI level from the server prompt.
1908 ******************************************************************/
1909 void set_ai_level_direct(struct player *pplayer, enum ai_level level)
1911 set_ai_level_directer(pplayer, level);
1912 send_player_info_c(pplayer, NULL);
1913 cmd_reply(cmd_of_level(level), NULL, C_OK,
1914 _("Player '%s' now has AI skill level '%s'."),
1915 player_name(pplayer),
1916 ai_level_translated_name(level));
1920 /******************************************************************
1921 Handle a user command to set an AI level.
1922 ******************************************************************/
1923 static bool set_ai_level_named(struct connection *caller, const char *name,
1924 const char *level_name, bool check)
1926 enum ai_level level = ai_level_by_name(level_name, fc_strcasecmp);
1928 return set_ai_level(caller, name, level, check);
1931 /******************************************************************
1932 Set AI level
1933 ******************************************************************/
1934 static bool set_ai_level(struct connection *caller, const char *name,
1935 enum ai_level level, bool check)
1937 enum m_pre_result match_result;
1938 struct player *pplayer;
1940 fc_assert_ret_val(level > 0 && level < 11, FALSE);
1942 pplayer = player_by_name_prefix(name, &match_result);
1944 if (pplayer) {
1945 if (pplayer->ai_controlled) {
1946 if (check) {
1947 return TRUE;
1949 set_ai_level_directer(pplayer, level);
1950 send_player_info_c(pplayer, NULL);
1951 cmd_reply(cmd_of_level(level), caller, C_OK,
1952 _("Player '%s' now has AI skill level '%s'."),
1953 player_name(pplayer),
1954 ai_level_translated_name(level));
1955 } else {
1956 cmd_reply(cmd_of_level(level), caller, C_FAIL,
1957 _("%s is not controlled by the AI."),
1958 player_name(pplayer));
1959 return FALSE;
1961 } else if (match_result == M_PRE_EMPTY) {
1962 if (check) {
1963 return TRUE;
1965 players_iterate(cplayer) {
1966 if (cplayer->ai_controlled) {
1967 set_ai_level_directer(cplayer, level);
1968 send_player_info_c(cplayer, NULL);
1969 cmd_reply(cmd_of_level(level), caller, C_OK,
1970 _("Player '%s' now has AI skill level '%s'."),
1971 player_name(cplayer),
1972 ai_level_translated_name(level));
1974 } players_iterate_end;
1975 game.info.skill_level = level;
1976 cmd_reply(cmd_of_level(level), caller, C_OK,
1977 _("Default AI skill level set to '%s'."),
1978 ai_level_translated_name(level));
1979 } else {
1980 cmd_reply_no_such_player(cmd_of_level(level), caller, name, match_result);
1981 return FALSE;
1983 return TRUE;
1986 /******************************************************************
1987 Set user to away mode.
1988 ******************************************************************/
1989 static bool away_command(struct connection *caller, bool check)
1991 struct player *pplayer;
1993 if (caller == NULL) {
1994 cmd_reply(CMD_AWAY, caller, C_FAIL, _("This command is client only."));
1995 return FALSE;
1998 if (!conn_controls_player(caller)) {
1999 /* This happens for detached or observer connections. */
2000 cmd_reply(CMD_AWAY, caller, C_FAIL,
2001 _("Only players may use the away command."));
2002 return FALSE;
2005 if (check) {
2006 return TRUE;
2009 pplayer = conn_get_player(caller);
2010 if (!pplayer->ai_controlled) {
2011 cmd_reply(CMD_AWAY, caller, C_OK,
2012 _("%s set to away mode."), player_name(pplayer));
2013 player_set_to_ai_mode(pplayer, AI_LEVEL_AWAY);
2014 fc_assert(pplayer->ai_controlled == TRUE);
2015 } else {
2016 cmd_reply(CMD_AWAY, caller, C_OK,
2017 _("%s returned to game."), player_name(pplayer));
2018 player_set_under_human_control(pplayer);
2019 fc_assert(pplayer->ai_controlled == FALSE);
2022 send_player_info_c(caller->playing, game.est_connections);
2024 return TRUE;
2027 /**************************************************************************
2028 Show changed settings and ruleset summary.
2029 **************************************************************************/
2030 static void show_ruleset_info(struct connection *caller, enum command_id cmd,
2031 bool check, int read_recursion)
2033 char *show_arg = "changed";
2035 /* show changed settings only at the top level of recursion */
2036 if (read_recursion != 0) {
2037 return;
2040 show_settings(caller, cmd, show_arg, check);
2042 if (game.ruleset_summary != NULL) {
2043 char *translated = fc_strdup(_(game.ruleset_summary));
2045 fc_break_lines(translated, LINE_BREAK);
2046 cmd_reply(cmd, caller, C_COMMENT, "%s", translated);
2047 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
2048 free(translated);
2052 /**************************************************************************
2053 /show command: show settings and their values.
2054 **************************************************************************/
2055 static bool show_command(struct connection *caller, char *str, bool check)
2057 return show_settings(caller, CMD_SHOW, str, check);
2060 /**************************************************************************
2061 Print a summary of the settings and their values. Note that most values
2062 are at most 4 digits, except seeds, which we let overflow their columns,
2063 plus a sign character. Only show options which the caller can SEE.
2064 **************************************************************************/
2065 static bool show_settings(struct connection *caller,
2066 enum command_id called_as,
2067 char *str, bool check)
2069 int cmd;
2070 enum sset_level level = SSET_ALL;
2071 size_t clen = 0;
2073 remove_leading_trailing_spaces(str);
2074 if (str[0] != '\0') {
2075 /* In "/show forests", figure out that it's the forests option we're
2076 * looking at. */
2077 cmd = lookup_option(str);
2078 if (cmd >= 0) {
2079 /* Ignore levels when a particular option is specified. */
2080 level = SSET_NONE;
2082 if (!setting_is_visible(setting_by_number(cmd), caller)) {
2083 cmd_reply(called_as, caller, C_FAIL,
2084 _("Sorry, you do not have access to view option '%s'."),
2085 str);
2086 return FALSE;
2090 /* Valid negative values for 'cmd' are defined as LOOKUP_OPTION_*. */
2091 switch (cmd) {
2092 case LOOKUP_OPTION_NO_RESULT:
2093 cmd_reply(called_as, caller, C_FAIL, _("Unknown option '%s'."), str);
2094 return FALSE;
2095 case LOOKUP_OPTION_AMBIGUOUS:
2096 /* Allow ambiguous: show all matching. */
2097 clen = strlen(str);
2098 break;
2099 case LOOKUP_OPTION_LEVEL_NAME:
2100 /* Option level. */
2101 level = lookup_option_level(str);
2102 break;
2103 case LOOKUP_OPTION_RULESETDIR:
2104 /* Ruleset. */
2105 cmd_reply(called_as, caller, C_COMMENT,
2106 _("Current ruleset directory is \"%s\""),
2107 game.server.rulesetdir);
2108 return TRUE;
2110 } else {
2111 /* to indicate that no command was specified */
2112 cmd = LOOKUP_OPTION_NO_RESULT;
2113 /* Use vital level by default. */
2114 level = SSET_VITAL;
2117 fc_assert_ret_val(cmd >= 0 || cmd == LOOKUP_OPTION_AMBIGUOUS
2118 || cmd == LOOKUP_OPTION_LEVEL_NAME
2119 || cmd == LOOKUP_OPTION_NO_RESULT, FALSE);
2121 #define cmd_reply_show(string) \
2122 cmd_reply(called_as, caller, C_COMMENT, "%s", string)
2125 const char *heading = NULL;
2126 switch(level) {
2127 case SSET_NONE:
2128 break;
2129 case SSET_CHANGED:
2130 heading = _("All options with non-default values");
2131 break;
2132 case SSET_ALL:
2133 heading = _("All options");
2134 break;
2135 case SSET_VITAL:
2136 heading = _("Vital options");
2137 break;
2138 case SSET_SITUATIONAL:
2139 heading = _("Situational options");
2140 break;
2141 case SSET_RARE:
2142 heading = _("Rarely used options");
2143 break;
2144 case SSET_LOCKED:
2145 heading = _("Options locked by the ruleset");
2146 break;
2147 case OLEVELS_NUM:
2148 /* nothing */
2149 break;
2151 if (heading) {
2152 cmd_reply_show(horiz_line);
2153 cmd_reply_show(heading);
2156 cmd_reply_show(horiz_line);
2157 cmd_reply_show(_("In the column '##' the status of the option is shown:"));
2158 cmd_reply_show(_(" - a '!' means the option is locked by the ruleset."));
2159 cmd_reply_show(_(" - a '+' means you may change the option."));
2160 cmd_reply_show(_(" - a '~' means that option follows default value."));
2161 cmd_reply_show(_(" - a '=' means the value is same as default."));
2162 cmd_reply_show(horiz_line);
2163 cmd_reply(called_as, caller, C_COMMENT, _("%-*s ## value (min, max)"),
2164 OPTION_NAME_SPACE, _("Option"));
2165 cmd_reply_show(horiz_line);
2167 /* Update changed and locked levels. */
2168 settings_list_update();
2170 switch(level) {
2171 case SSET_NONE:
2172 /* Show _one_ setting. */
2173 fc_assert_ret_val(0 <= cmd, FALSE);
2175 struct setting *pset = setting_by_number(cmd);
2177 show_settings_one(caller, called_as, pset);
2179 break;
2180 case SSET_CHANGED:
2181 case SSET_ALL:
2182 case SSET_VITAL:
2183 case SSET_SITUATIONAL:
2184 case SSET_RARE:
2185 case SSET_LOCKED:
2186 settings_iterate(level, pset) {
2187 if (!setting_is_visible(pset, caller)) {
2188 continue;
2191 if (LOOKUP_OPTION_AMBIGUOUS == cmd
2192 && 0 != fc_strncasecmp(setting_name(pset), str, clen)) {
2193 continue;
2196 show_settings_one(caller, called_as, pset);
2197 } settings_iterate_end;
2198 break;
2199 case OLEVELS_NUM:
2200 /* nothing */
2201 break;
2204 cmd_reply_show(horiz_line);
2205 /* Only emit this additional help for bona fide 'show' command */
2206 if (called_as == CMD_SHOW) {
2207 cmd_reply_show(_("A help text for each option is available via 'help "
2208 "<option>'."));
2209 cmd_reply_show(horiz_line);
2210 if (level == SSET_VITAL) {
2211 cmd_reply_show(_("Try 'show situational' or 'show rare' to show "
2212 "more options.\n"
2213 "Try 'show changed' to show settings with "
2214 "non-default values.\n"
2215 "Try 'show locked' to show settings locked "
2216 "by the ruleset."));
2217 cmd_reply_show(horiz_line);
2220 return TRUE;
2221 #undef cmd_reply_show
2224 /*****************************************************************************
2225 Show one setting.
2227 Each option value will be displayed as:
2229 [OPTION_NAME_SPACE length for name] ## [value] ([min], [max])
2231 where '##' is a combination of ' ', '!' or '+' followed by ' ', '*', or '=' with
2232 - '!': the option is locked by the ruleset
2233 - '+': you may change the option
2234 - '~': the option follows default value
2235 - '=': the value is same as default
2236 *****************************************************************************/
2237 static void show_settings_one(struct connection *caller, enum command_id cmd,
2238 struct setting *pset)
2240 char buf[MAX_LEN_CONSOLE_LINE] = "", value[MAX_LEN_CONSOLE_LINE] = "";
2241 bool is_changed;
2242 static char prefix[OPTION_NAME_SPACE + 4 + 1] = "";
2243 char defaultness;
2245 fc_assert_ret(pset != NULL);
2247 is_changed = setting_non_default(pset);
2248 setting_value_name(pset, TRUE, value, sizeof(value));
2250 /* Wrap long option values, such as bitwise options */
2251 fc_break_lines(value, LINE_BREAK - (sizeof(prefix)-1));
2253 if (prefix[0] == '\0') {
2254 memset(prefix, ' ', sizeof(prefix)-1);
2257 if (is_changed) {
2258 /* Emphasizes the changed option. */
2259 /* Apply tags to each line fragment. */
2260 size_t startpos = 0;
2261 char *nl;
2262 do {
2263 nl = strchr(value + startpos, '\n');
2264 featured_text_apply_tag(value, buf, sizeof(buf), TTT_COLOR,
2265 startpos, nl ? nl - value : FT_OFFSET_UNSET,
2266 ftc_changed);
2267 sz_strlcpy(value, buf);
2268 if (nl) {
2269 char *p = strchr(nl, '\n');
2270 fc_assert_action(p != NULL, break);
2271 startpos = p + 1 - value;
2273 } while (nl);
2276 if (SSET_INT == setting_type(pset)) {
2277 /* Add the range. */
2278 cat_snprintf(value, sizeof(value), " (%d, %d)",
2279 setting_int_min(pset), setting_int_max(pset));
2282 if (setting_get_setdef(pset) == SETDEF_INTERNAL) {
2283 defaultness = '~';
2284 } else if (is_changed) {
2285 defaultness = ' ';
2286 } else {
2287 defaultness = '=';
2290 cmd_reply_prefix(cmd, caller, C_COMMENT, prefix, "%-*s %c%c %s",
2291 OPTION_NAME_SPACE, setting_name(pset),
2292 setting_status(caller, pset), defaultness,
2293 value);
2296 /******************************************************************
2297 Handle team command
2298 ******************************************************************/
2299 static bool team_command(struct connection *caller, char *str, bool check)
2301 struct player *pplayer;
2302 enum m_pre_result match_result;
2303 char buf[MAX_LEN_CONSOLE_LINE];
2304 char *arg[2];
2305 int ntokens = 0, i;
2306 bool res = FALSE;
2307 struct team_slot *tslot;
2309 if (game_was_started()) {
2310 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2311 _("Cannot change teams once game has begun."));
2312 return FALSE;
2315 if (str != NULL || strlen(str) > 0) {
2316 sz_strlcpy(buf, str);
2317 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2319 if (ntokens != 2) {
2320 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2321 _("Undefined argument. Usage:\n%s"),
2322 command_synopsis(command_by_number(CMD_TEAM)));
2323 goto cleanup;
2326 pplayer = player_by_name_prefix(arg[0], &match_result);
2327 if (pplayer == NULL) {
2328 cmd_reply_no_such_player(CMD_TEAM, caller, arg[0], match_result);
2329 goto cleanup;
2332 tslot = team_slot_by_rule_name(arg[1]);
2333 if (NULL == tslot) {
2334 int teamno;
2336 if (str_to_int(arg[1], &teamno)) {
2337 tslot = team_slot_by_number(teamno);
2340 if (NULL == tslot) {
2341 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2342 _("No such team %s. Please give a "
2343 "valid team name or number."), arg[1]);
2344 goto cleanup;
2347 if (is_barbarian(pplayer)) {
2348 /* This can happen if we change team settings on a loaded game. */
2349 cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Cannot team a barbarian."));
2350 goto cleanup;
2352 if (!check) {
2353 team_add_player(pplayer, team_new(tslot));
2354 send_player_info_c(pplayer, NULL);
2355 cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
2356 player_name(pplayer),
2357 team_slot_name_translation(tslot));
2359 res = TRUE;
2361 cleanup:
2362 for (i = 0; i < ntokens; i++) {
2363 free(arg[i]);
2365 return res;
2368 /**************************************************************************
2369 List all running votes. Moved from /vote command.
2370 **************************************************************************/
2371 static void show_votes(struct connection *caller)
2373 int count = 0;
2374 const char *title;
2376 if (vote_list != NULL) {
2377 vote_list_iterate(vote_list, pvote) {
2378 if (NULL != caller && !conn_can_see_vote(caller, pvote)) {
2379 continue;
2381 /* TRANS: "Vote" or "Teamvote" is voting-as-a-process. Used as
2382 * part of a sentence. */
2383 title = vote_is_team_only(pvote) ? _("Teamvote") : _("Vote");
2384 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2385 /* TRANS: "[Vote|Teamvote] 3 \"proposed change\" (needs ..." */
2386 _("%s %d \"%s\" (needs %0.0f%%%s): %d for, "
2387 "%d against, and %d abstained out of %d players."),
2388 title, pvote->vote_no, pvote->cmdline,
2389 MIN(100, pvote->need_pc * 100 + 1),
2390 pvote->flags & VCF_NODISSENT ? _(" no dissent") : "",
2391 pvote->yes, pvote->no, pvote->abstain, count_voters(pvote));
2392 count++;
2393 } vote_list_iterate_end;
2396 if (count == 0) {
2397 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2398 _("There are no votes going on."));
2402 /**************************************************************************
2403 Vote command argument definitions.
2404 **************************************************************************/
2405 static const char *const vote_args[] = {
2406 "yes",
2407 "no",
2408 "abstain",
2409 NULL
2411 static const char *vote_arg_accessor(int i)
2413 return vote_args[i];
2416 /******************************************************************
2417 Make or participate in a vote.
2418 ******************************************************************/
2419 static bool vote_command(struct connection *caller, char *str,
2420 bool check)
2422 char buf[MAX_LEN_CONSOLE_LINE];
2423 char *arg[2];
2424 int ntokens = 0, i = 0, which = -1;
2425 enum m_pre_result match_result;
2426 struct vote *pvote = NULL;
2427 bool res = FALSE;
2429 if (check) {
2430 /* This should never happen, since /vote must always be
2431 * set to ALLOW_BASIC or less. But just in case... */
2432 return FALSE;
2435 sz_strlcpy(buf, str);
2436 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2438 if (ntokens == 0) {
2439 show_votes(caller);
2440 goto CLEANUP;
2441 } else if (!conn_can_vote(caller, NULL)) {
2442 cmd_reply(CMD_VOTE, caller, C_FAIL,
2443 _("You are not allowed to use this command."));
2444 goto CLEANUP;
2447 match_result = match_prefix(vote_arg_accessor, VOTE_NUM, 0,
2448 fc_strncasecmp, NULL, arg[0], &i);
2450 if (match_result == M_PRE_AMBIGUOUS) {
2451 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2452 _("The argument \"%s\" is ambiguous."), arg[0]);
2453 goto CLEANUP;
2454 } else if (match_result > M_PRE_AMBIGUOUS) {
2455 /* Failed */
2456 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2457 _("Undefined argument. Usage:\n%s"),
2458 command_synopsis(command_by_number(CMD_VOTE)));
2459 goto CLEANUP;
2462 if (ntokens == 1) {
2463 /* Applies to last vote */
2464 if (vote_number_sequence > 0 && get_vote_by_no(vote_number_sequence)) {
2465 which = vote_number_sequence;
2466 } else {
2467 int num_votes = vote_list_size(vote_list);
2468 if (num_votes == 0) {
2469 cmd_reply(CMD_VOTE, caller, C_FAIL, _("There are no votes running."));
2470 } else {
2471 /* TRANS: "vote" as a process */
2472 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote (%d %s)."),
2473 num_votes, PL_("other vote running", "other votes running",
2474 num_votes));
2476 goto CLEANUP;
2478 } else {
2479 if (!str_to_int(arg[1], &which)) {
2480 cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be an integer."));
2481 goto CLEANUP;
2485 if (!(pvote = get_vote_by_no(which))) {
2486 /* TRANS: "vote" as a process */
2487 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), which);
2488 goto CLEANUP;
2491 if (!conn_can_vote(caller, pvote)) {
2492 cmd_reply(CMD_VOTE, caller, C_FAIL,
2493 _("You are not allowed to vote on that."));
2494 goto CLEANUP;
2497 if (i == VOTE_YES) {
2498 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
2499 pvote->cmdline);
2500 connection_vote(caller, pvote, VOTE_YES);
2501 } else if (i == VOTE_NO) {
2502 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
2503 pvote->cmdline);
2504 connection_vote(caller, pvote, VOTE_NO);
2505 } else if (i == VOTE_ABSTAIN) {
2506 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2507 _("You abstained from voting on \"%s\""), pvote->cmdline);
2508 connection_vote(caller, pvote, VOTE_ABSTAIN);
2509 } else {
2510 /* Must never happen. */
2511 fc_assert_action(FALSE, goto CLEANUP);
2514 res = TRUE;
2516 CLEANUP:
2517 free_tokens(arg, ntokens);
2518 return res;
2521 /**************************************************************************
2522 Cancel a vote... /cancelvote <vote number>|all.
2523 **************************************************************************/
2524 static bool cancelvote_command(struct connection *caller,
2525 char *arg, bool check)
2527 struct vote *pvote = NULL;
2528 int vote_no;
2530 if (check) {
2531 /* This should never happen anyway, since /cancelvote
2532 * is set to ALLOW_BASIC in both pregame and while the
2533 * game is running. */
2534 return FALSE;
2537 remove_leading_trailing_spaces(arg);
2539 if (arg[0] == '\0') {
2540 if (caller == NULL) {
2541 /* Server prompt */
2542 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2543 /* TRANS: "vote" as a process */
2544 _("Missing argument <vote number> or "
2545 "the string \"all\"."));
2546 return FALSE;
2548 /* The caller cancel his/her own vote. */
2549 if (!(pvote = get_vote_by_caller(caller))) {
2550 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2551 _("You don't have any vote going on."));
2552 return FALSE;
2554 } else if (fc_strcasecmp(arg, "all") == 0) {
2555 /* Cancel all votes (needs some privileges). */
2556 if (vote_list_size(vote_list) == 0) {
2557 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2558 _("There isn't any vote going on."));
2559 return FALSE;
2560 } else if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
2561 clear_all_votes();
2562 notify_conn(NULL, NULL, E_VOTE_ABORTED, ftc_server,
2563 /* TRANS: "votes" as a process */
2564 _("All votes have been removed."));
2565 return TRUE;
2566 } else {
2567 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2568 _("You are not allowed to use this command."));
2569 return FALSE;
2571 } else if (str_to_int(arg, &vote_no)) {
2572 /* Cancel one particular vote (needs some privileges if the vote
2573 * is not owned). */
2574 if (!(pvote = get_vote_by_no(vote_no))) {
2575 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2576 /* TRANS: "vote" as a process */
2577 _("No such vote (%d)."), vote_no);
2578 return FALSE;
2579 } else if (caller && conn_get_access(caller) < ALLOW_ADMIN
2580 && caller->id != pvote->caller_id) {
2581 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2582 /* TRANS: "vote" as a process */
2583 _("You are not allowed to cancel this vote (%d)."),
2584 vote_no);
2585 return FALSE;
2587 } else {
2588 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2589 /* TRANS: "vote" as a process */
2590 _("Usage: /cancelvote [<vote number>|all]"));
2591 return FALSE;
2594 fc_assert_ret_val(NULL != pvote, FALSE);
2596 if (caller) {
2597 notify_team(conn_get_player(vote_get_caller(pvote)),
2598 NULL, E_VOTE_ABORTED, ftc_server,
2599 /* TRANS: "vote" as a process */
2600 _("%s has canceled the vote \"%s\" (number %d)."),
2601 caller->username, pvote->cmdline, pvote->vote_no);
2602 } else {
2603 /* Server prompt */
2604 notify_team(conn_get_player(vote_get_caller(pvote)),
2605 NULL, E_VOTE_ABORTED, ftc_server,
2606 /* TRANS: "vote" as a process */
2607 _("The vote \"%s\" (number %d) has been canceled."),
2608 pvote->cmdline, pvote->vote_no);
2610 /* Make it after, prevent crashs about a free pointer (pvote). */
2611 remove_vote(pvote);
2613 return TRUE;
2616 /******************************************************************
2617 Turn on selective debugging.
2618 ******************************************************************/
2619 static bool debug_command(struct connection *caller, char *str,
2620 bool check)
2622 char buf[MAX_LEN_CONSOLE_LINE];
2623 char *arg[3];
2624 int ntokens = 0, i;
2626 if (game.info.is_new_game) {
2627 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2628 _("Can only use this command once game has begun."));
2629 return FALSE;
2631 if (check) {
2632 return TRUE; /* whatever! */
2635 if (str != NULL && strlen(str) > 0) {
2636 sz_strlcpy(buf, str);
2637 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
2638 } else {
2639 ntokens = 0;
2642 if (ntokens > 0 && strcmp(arg[0], "diplomacy") == 0) {
2643 struct player *pplayer;
2644 enum m_pre_result match_result;
2646 if (ntokens != 2) {
2647 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2648 _("Undefined argument. Usage:\n%s"),
2649 command_synopsis(command_by_number(CMD_DEBUG)));
2650 goto cleanup;
2652 pplayer = player_by_name_prefix(arg[1], &match_result);
2653 if (pplayer == NULL) {
2654 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2655 goto cleanup;
2657 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY)) {
2658 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2659 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy no longer debugged"),
2660 player_name(pplayer));
2661 } else {
2662 BV_SET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2663 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy debugged"),
2664 player_name(pplayer));
2665 /* TODO: print some info about the player here */
2667 } else if (ntokens > 0 && strcmp(arg[0], "tech") == 0) {
2668 struct player *pplayer;
2669 enum m_pre_result match_result;
2671 if (ntokens != 2) {
2672 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2673 _("Undefined argument. Usage:\n%s"),
2674 command_synopsis(command_by_number(CMD_DEBUG)));
2675 goto cleanup;
2677 pplayer = player_by_name_prefix(arg[1], &match_result);
2678 if (pplayer == NULL) {
2679 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2680 goto cleanup;
2682 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_TECH)) {
2683 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_TECH);
2684 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech no longer debugged"),
2685 player_name(pplayer));
2686 } else {
2687 BV_SET(pplayer->server.debug, PLAYER_DEBUG_TECH);
2688 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech debugged"),
2689 player_name(pplayer));
2690 /* TODO: print some info about the player here */
2692 } else if (ntokens > 0 && strcmp(arg[0], "info") == 0) {
2693 int cities = 0, players = 0, units = 0, citizen_count = 0;
2695 players_iterate(plr) {
2696 players++;
2697 city_list_iterate(plr->cities, pcity) {
2698 cities++;
2699 citizen_count += city_size_get(pcity);
2700 } city_list_iterate_end;
2701 units += unit_list_size(plr->units);
2702 } players_iterate_end;
2703 log_normal(_("players=%d cities=%d citizens=%d units=%d"),
2704 players, cities, citizen_count, units);
2705 notify_conn(game.est_connections, NULL, E_AI_DEBUG, ftc_log,
2706 _("players=%d cities=%d citizens=%d units=%d"),
2707 players, cities, citizen_count, units);
2708 } else if (ntokens > 0 && strcmp(arg[0], "city") == 0) {
2709 int x, y;
2710 struct tile *ptile;
2711 struct city *pcity;
2713 if (ntokens != 3) {
2714 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2715 _("Undefined argument. Usage:\n%s"),
2716 command_synopsis(command_by_number(CMD_DEBUG)));
2717 goto cleanup;
2719 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2720 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2721 goto cleanup;
2723 if (!(ptile = map_pos_to_tile(x, y))) {
2724 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2725 goto cleanup;
2727 pcity = tile_city(ptile);
2728 if (!pcity) {
2729 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("No city at this coordinate."));
2730 goto cleanup;
2732 if (pcity->server.debug) {
2733 pcity->server.debug = FALSE;
2734 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s no longer debugged"),
2735 city_name_get(pcity));
2736 } else {
2737 pcity->server.debug = TRUE;
2738 CITY_LOG(LOG_NORMAL, pcity, "debugged");
2740 } else if (ntokens > 0 && strcmp(arg[0], "units") == 0) {
2741 int x, y;
2742 struct tile *ptile;
2744 if (ntokens != 3) {
2745 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2746 _("Undefined argument. Usage:\n%s"),
2747 command_synopsis(command_by_number(CMD_DEBUG)));
2748 goto cleanup;
2750 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2751 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2752 goto cleanup;
2754 if (!(ptile = map_pos_to_tile(x, y))) {
2755 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2756 goto cleanup;
2758 unit_list_iterate(ptile->units, punit) {
2759 if (punit->server.debug) {
2760 punit->server.debug = FALSE;
2761 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2762 nation_adjective_for_player(unit_owner(punit)),
2763 unit_name_translation(punit));
2764 } else {
2765 punit->server.debug = TRUE;
2766 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2767 nation_rule_name(nation_of_unit(punit)),
2768 unit_name_translation(punit));
2770 } unit_list_iterate_end;
2771 } else if (ntokens > 0 && strcmp(arg[0], "timing") == 0) {
2772 TIMING_RESULTS();
2773 } else if (ntokens > 0 && strcmp(arg[0], "ferries") == 0) {
2774 if (game.server.debug[DEBUG_FERRIES]) {
2775 game.server.debug[DEBUG_FERRIES] = FALSE;
2776 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system is no longer "
2777 "in debug mode."));
2778 } else {
2779 game.server.debug[DEBUG_FERRIES] = TRUE;
2780 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system in debug mode."));
2782 } else if (ntokens > 0 && strcmp(arg[0], "unit") == 0) {
2783 int id;
2784 struct unit *punit;
2786 if (ntokens != 2) {
2787 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2788 _("Undefined argument. Usage:\n%s"),
2789 command_synopsis(command_by_number(CMD_DEBUG)));
2790 goto cleanup;
2792 if (!str_to_int(arg[1], &id)) {
2793 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 must be integer."));
2794 goto cleanup;
2796 if (!(punit = game_unit_by_number(id))) {
2797 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Unit %d does not exist."), id);
2798 goto cleanup;
2800 if (punit->server.debug) {
2801 punit->server.debug = FALSE;
2802 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2803 nation_adjective_for_player(unit_owner(punit)),
2804 unit_name_translation(punit));
2805 } else {
2806 punit->server.debug = TRUE;
2807 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2808 nation_rule_name(nation_of_unit(punit)),
2809 unit_name_translation(punit));
2811 } else {
2812 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2813 _("Undefined argument. Usage:\n%s"),
2814 command_synopsis(command_by_number(CMD_DEBUG)));
2816 cleanup:
2817 for (i = 0; i < ntokens; i++) {
2818 free(arg[i]);
2820 return TRUE;
2823 /******************************************************************
2824 Helper to validate an argument referring to a server setting.
2825 Sends error message and returns NULL on failure.
2826 ******************************************************************/
2827 static struct setting *validate_setting_arg(enum command_id cmd,
2828 struct connection *caller,
2829 char *arg)
2831 int opt = lookup_option(arg);
2833 if (opt < 0) {
2834 switch (opt) {
2835 case LOOKUP_OPTION_NO_RESULT:
2836 case LOOKUP_OPTION_LEVEL_NAME:
2837 cmd_reply(cmd, caller, C_SYNTAX, _("Option '%s' not recognized."), arg);
2838 break;
2839 case LOOKUP_OPTION_AMBIGUOUS:
2840 cmd_reply(cmd, caller, C_SYNTAX, _("Ambiguous option name."));
2841 break;
2842 case LOOKUP_OPTION_RULESETDIR:
2843 cmd_reply(cmd, caller, C_SYNTAX,
2844 /* TRANS: 'rulesetdir' is the command. Do not translate. */
2845 _("Use the '%srulesetdir' command to change the ruleset "
2846 "directory."), caller ? "/" : "");
2847 break;
2848 default:
2849 fc_assert(opt >= LOOKUP_OPTION_RULESETDIR);
2850 break;
2852 return NULL;
2855 return setting_by_number(opt);
2858 /******************************************************************
2859 Handle set command
2860 ******************************************************************/
2861 static bool set_command(struct connection *caller, char *str, bool check)
2863 char *args[2];
2864 int val, nargs;
2865 struct setting *pset;
2866 bool do_update;
2867 char reject_msg[256] = "";
2868 bool ret = FALSE;
2870 /* '=' is also a valid delimiter for this function. */
2871 nargs = get_tokens(str, args, ARRAY_SIZE(args), TOKEN_DELIMITERS "=");
2873 if (nargs < 2) {
2874 cmd_reply(CMD_SET, caller, C_SYNTAX,
2875 _("Undefined argument. Usage:\n%s"),
2876 command_synopsis(command_by_number(CMD_SET)));
2877 goto cleanup;
2880 pset = validate_setting_arg(CMD_SET, caller, args[0]);
2882 if (!pset) {
2883 /* Reason already reported. */
2884 goto cleanup;
2887 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))
2888 && !check) {
2889 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2890 goto cleanup;
2893 do_update = FALSE;
2895 switch (setting_type(pset)) {
2896 case SSET_BOOL:
2897 if (check) {
2898 if (!setting_is_changeable(pset, caller, reject_msg,
2899 sizeof(reject_msg))
2900 || (!setting_bool_validate(pset, args[1], caller,
2901 reject_msg, sizeof(reject_msg)))) {
2902 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2903 goto cleanup;
2905 } else if (setting_bool_set(pset, args[1], caller,
2906 reject_msg, sizeof(reject_msg))) {
2907 do_update = TRUE;
2908 } else {
2909 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2910 goto cleanup;
2912 break;
2914 case SSET_INT:
2915 if (!str_to_int(args[1], &val)) {
2916 cmd_reply(CMD_SET, caller, C_SYNTAX,
2917 _("The parameter %s should only contain +- and 0-9."),
2918 setting_name(pset));
2919 goto cleanup;
2921 if (check) {
2922 if (!setting_is_changeable(pset, caller, reject_msg,
2923 sizeof(reject_msg))
2924 || !setting_int_validate(pset, val, caller, reject_msg,
2925 sizeof(reject_msg))) {
2926 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2927 goto cleanup;
2929 } else {
2930 if (setting_int_set(pset, val, caller, reject_msg,
2931 sizeof(reject_msg))) {
2932 do_update = TRUE;
2933 } else {
2934 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2935 goto cleanup;
2938 break;
2940 case SSET_STRING:
2941 if (check) {
2942 if (!setting_is_changeable(pset, caller, reject_msg,
2943 sizeof(reject_msg))
2944 || !setting_str_validate(pset, args[1], caller, reject_msg,
2945 sizeof(reject_msg))) {
2946 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2947 goto cleanup;
2949 } else {
2950 if (setting_str_set(pset, args[1], caller, reject_msg,
2951 sizeof(reject_msg))) {
2952 do_update = TRUE;
2953 } else {
2954 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2955 goto cleanup;
2958 break;
2960 case SSET_ENUM:
2961 if (check) {
2962 if (!setting_is_changeable(pset, caller, reject_msg,
2963 sizeof(reject_msg))
2964 || (!setting_enum_validate(pset, args[1], caller,
2965 reject_msg, sizeof(reject_msg)))) {
2966 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2967 goto cleanup;
2969 } else if (setting_enum_set(pset, args[1], caller,
2970 reject_msg, sizeof(reject_msg))) {
2971 do_update = TRUE;
2972 } else {
2973 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2974 goto cleanup;
2976 break;
2978 case SSET_BITWISE:
2979 if (check) {
2980 if (!setting_is_changeable(pset, caller, reject_msg,
2981 sizeof(reject_msg))
2982 || (!setting_bitwise_validate(pset, args[1], caller,
2983 reject_msg, sizeof(reject_msg)))) {
2984 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2985 goto cleanup;
2987 } else if (setting_bitwise_set(pset, args[1], caller,
2988 reject_msg, sizeof(reject_msg))) {
2989 do_update = TRUE;
2990 } else {
2991 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2992 goto cleanup;
2994 break;
2997 ret = TRUE; /* Looks like a success. */
2999 if (!check && do_update) {
3000 /* Send only to connections able to see that. */
3001 char buf[256];
3002 struct packet_chat_msg packet;
3004 package_event(&packet, NULL, E_SETTING, ftc_server,
3005 _("Console: '%s' has been set to %s."), setting_name(pset),
3006 setting_value_name(pset, TRUE, buf, sizeof(buf)));
3007 conn_list_iterate(game.est_connections, pconn) {
3008 if (setting_is_visible(pset, pconn)) {
3009 send_packet_chat_msg(pconn, &packet);
3011 } conn_list_iterate_end;
3012 /* Notify the console. */
3013 con_write(C_OK, "%s", packet.message);
3015 setting_changed(pset);
3016 setting_action(pset);
3017 send_server_setting(NULL, pset);
3019 * send any modified game parameters to the clients -- if sent
3020 * before S_S_RUNNING, triggers a popdown_races_dialog() call
3021 * in client/packhand.c#handle_game_info()
3023 send_game_info(NULL);
3024 reset_all_start_commands(FALSE);
3025 send_server_info_to_metaserver(META_INFO);
3028 cleanup:
3029 free_tokens(args, nargs);
3030 return ret;
3033 /**************************************************************************
3034 Check game.allow_take for permission to take or observe a player.
3036 NB: If this function returns FALSE, then callers expect that 'msg' will
3037 be filled in with a NULL-terminated string containing the reason.
3038 **************************************************************************/
3039 static bool is_allowed_to_take(struct player *pplayer, bool will_obs,
3040 char *msg, size_t msg_len)
3042 const char *allow;
3044 if (!pplayer && will_obs) {
3045 /* Global observer. */
3046 if (!(allow = strchr(game.server.allow_take,
3047 (game.info.is_new_game ? 'O' : 'o')))) {
3048 fc_strlcpy(msg, _("Sorry, one can't observe globally in this game."),
3049 msg_len);
3050 return FALSE;
3052 } else if (!pplayer && !will_obs) {
3053 /* Auto-taking a new player */
3055 if (game_was_started()) {
3056 fc_strlcpy(msg, _("You cannot take a new player at this time."),
3057 msg_len);
3058 return FALSE;
3061 if (normal_player_count() >= game.server.max_players) {
3062 fc_snprintf(msg, msg_len,
3063 /* TRANS: Do not translate "maxplayers". */
3064 PL_("You cannot take a new player because "
3065 "the maximum of %d player has already "
3066 "been reached (maxplayers setting).",
3067 "You cannot take a new player because "
3068 "the maximum of %d players has already "
3069 "been reached (maxplayers setting).",
3070 game.server.max_players),
3071 game.server.max_players);
3072 return FALSE;
3075 if (player_count() >= player_slot_count()) {
3076 fc_strlcpy(msg, _("You cannot take a new player because there "
3077 "are no free player slots."),
3078 msg_len);
3079 return FALSE;
3082 return TRUE;
3084 } else if (is_barbarian(pplayer)) {
3085 if (!(allow = strchr(game.server.allow_take, 'b'))) {
3086 if (will_obs) {
3087 fc_strlcpy(msg,
3088 _("Sorry, one can't observe barbarians in this game."),
3089 msg_len);
3090 } else {
3091 fc_strlcpy(msg, _("Sorry, one can't take barbarians in this game."),
3092 msg_len);
3094 return FALSE;
3096 } else if (!pplayer->is_alive) {
3097 if (!(allow = strchr(game.server.allow_take, 'd'))) {
3098 if (will_obs) {
3099 fc_strlcpy(msg,
3100 _("Sorry, one can't observe dead players in this game."),
3101 msg_len);
3102 } else {
3103 fc_strlcpy(msg,
3104 _("Sorry, one can't take dead players in this game."),
3105 msg_len);
3107 return FALSE;
3109 } else if (pplayer->ai_controlled) {
3110 if (!(allow = strchr(game.server.allow_take,
3111 (game.info.is_new_game ? 'A' : 'a')))) {
3112 if (will_obs) {
3113 fc_strlcpy(msg,
3114 _("Sorry, one can't observe AI players in this game."),
3115 msg_len);
3116 } else {
3117 fc_strlcpy(msg, _("Sorry, one can't take AI players in this game."),
3118 msg_len);
3120 return FALSE;
3122 } else {
3123 if (!(allow = strchr(game.server.allow_take,
3124 (game.info.is_new_game ? 'H' : 'h')))) {
3125 if (will_obs) {
3126 fc_strlcpy(msg,
3127 _("Sorry, one can't observe human players in this game."),
3128 msg_len);
3129 } else {
3130 fc_strlcpy(msg,
3131 _("Sorry, one can't take human players in this game."),
3132 msg_len);
3134 return FALSE;
3138 allow++;
3140 if (will_obs && (*allow == '2' || *allow == '3')) {
3141 fc_strlcpy(msg, _("Sorry, one can't observe in this game."), msg_len);
3142 return FALSE;
3145 if (!will_obs && *allow == '4') {
3146 fc_strlcpy(msg, _("Sorry, one can't take players in this game."),
3147 MAX_LEN_MSG);
3148 return FALSE;
3151 if (!will_obs && pplayer->is_connected
3152 && (*allow == '1' || *allow == '3')) {
3153 fc_strlcpy(msg, _("Sorry, one can't take players already "
3154 "connected in this game."), msg_len);
3155 return FALSE;
3158 return TRUE;
3161 /**************************************************************************
3162 Observe another player. If we were already attached, detach
3163 (see connection_detach()). The console and those with ALLOW_HACK can
3164 use the two-argument command and force others to observe.
3165 **************************************************************************/
3166 static bool observe_command(struct connection *caller, char *str, bool check)
3168 int i = 0, ntokens = 0;
3169 char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
3170 bool is_newgame = !game_was_started();
3171 enum m_pre_result result;
3172 struct connection *pconn = NULL;
3173 struct player *pplayer = NULL;
3174 bool res = FALSE;
3176 /******** PART I: fill pconn and pplayer ********/
3178 sz_strlcpy(buf, str);
3179 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
3181 /* check syntax, only certain syntax if allowed depending on the caller */
3182 if (!caller && ntokens < 1) {
3183 cmd_reply(CMD_OBSERVE, caller, C_SYNTAX, _("Usage:\n%s"),
3184 command_synopsis(command_by_number(CMD_OBSERVE)));
3185 goto end;
3188 if (ntokens == 2 && (caller && caller->access_level != ALLOW_HACK)) {
3189 cmd_reply(CMD_OBSERVE, caller, C_SYNTAX,
3190 _("Only the player name form is allowed."));
3191 goto end;
3194 /* match connection if we're console, match a player if we're not */
3195 if (ntokens == 1) {
3196 if (!caller && !(pconn = conn_by_user_prefix(arg[0], &result))) {
3197 cmd_reply_no_such_conn(CMD_OBSERVE, caller, arg[0], result);
3198 goto end;
3199 } else if (caller
3200 && !(pplayer = player_by_name_prefix(arg[0], &result))) {
3201 cmd_reply_no_such_player(CMD_OBSERVE, caller, arg[0], result);
3202 goto end;
3206 /* get connection name then player name */
3207 if (ntokens == 2) {
3208 if (!(pconn = conn_by_user_prefix(arg[0], &result))) {
3209 cmd_reply_no_such_conn(CMD_OBSERVE, caller, arg[0], result);
3210 goto end;
3212 if (!(pplayer = player_by_name_prefix(arg[1], &result))) {
3213 cmd_reply_no_such_player(CMD_OBSERVE, caller, arg[1], result);
3214 goto end;
3218 /* if we can't force other connections to observe, assign us to be pconn. */
3219 if (!pconn) {
3220 pconn = caller;
3223 /* if we have no pplayer, it means that we want to be a global observer */
3225 /******** PART II: do the observing ********/
3227 /* check allowtake for permission */
3228 if (!is_allowed_to_take(pplayer, TRUE, msg, sizeof(msg))) {
3229 cmd_reply(CMD_OBSERVE, caller, C_FAIL, "%s", msg);
3230 goto end;
3233 /* observing your own player (during pregame) makes no sense. */
3234 if (NULL != pplayer
3235 && pplayer == pconn->playing
3236 && !pconn->observer
3237 && is_newgame
3238 && !pplayer->was_created) {
3239 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3240 _("%s already controls %s. Using 'observe' would remove %s"),
3241 pconn->username,
3242 player_name(pplayer),
3243 player_name(pplayer));
3244 goto end;
3247 /* attempting to observe a player you're already observing should fail. */
3248 if (pplayer == pconn->playing && pconn->observer) {
3249 if (pplayer) {
3250 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3251 _("%s is already observing %s."),
3252 pconn->username,
3253 player_name(pplayer));
3254 } else {
3255 cmd_reply(CMD_OBSERVE, caller, C_FAIL,
3256 _("%s is already observing."),
3257 pconn->username);
3259 goto end;
3262 res = TRUE; /* all tests passed */
3263 if (check) {
3264 goto end;
3267 /* if the connection is already attached to a player,
3268 * unattach and cleanup old player (rename, remove, etc) */
3269 if (TRUE) {
3270 char name[MAX_LEN_NAME];
3272 if (pplayer) {
3273 /* if pconn->playing is removed, we'll lose pplayer */
3274 sz_strlcpy(name, player_name(pplayer));
3277 connection_detach(pconn, TRUE);
3279 if (pplayer) {
3280 /* find pplayer again, the pointer might have been changed */
3281 pplayer = player_by_name(name);
3285 /* attach pconn to new player as an observer or as global observer */
3286 if ((res = connection_attach(pconn, pplayer, TRUE))) {
3287 if (pplayer) {
3288 cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes %s"),
3289 pconn->username,
3290 player_name(pplayer));
3291 } else {
3292 cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes"),
3293 pconn->username);
3297 end:;
3298 /* free our args */
3299 for (i = 0; i < ntokens; i++) {
3300 free(arg[i]);
3302 return res;
3305 /**************************************************************************
3306 Take over a player. If a connection already has control of that player,
3307 disallow it.
3309 If there are two arguments, treat the first as the connection name and the
3310 second as the player name (only hack and the console can do this).
3311 Otherwise, there should be one argument, that being the player that the
3312 caller wants to take.
3313 **************************************************************************/
3314 static bool take_command(struct connection *caller, char *str, bool check)
3316 int i = 0, ntokens = 0;
3317 char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
3318 bool is_newgame = !game_was_started();
3319 enum m_pre_result match_result;
3320 struct connection *pconn = caller;
3321 struct player *pplayer = NULL;
3322 bool res = FALSE;
3324 /******** PART I: fill pconn and pplayer ********/
3326 sz_strlcpy(buf, str);
3327 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
3329 /* check syntax */
3330 if (!caller && ntokens != 2) {
3331 cmd_reply(CMD_TAKE, caller, C_SYNTAX, _("Usage:\n%s"),
3332 command_synopsis(command_by_number(CMD_TAKE)));
3333 goto end;
3336 if (caller && caller->access_level != ALLOW_HACK && ntokens != 1) {
3337 cmd_reply(CMD_TAKE, caller, C_SYNTAX,
3338 _("Only the player name form is allowed."));
3339 goto end;
3342 if (ntokens == 0) {
3343 cmd_reply(CMD_TAKE, caller, C_SYNTAX, _("Usage:\n%s"),
3344 command_synopsis(command_by_number(CMD_TAKE)));
3345 goto end;
3348 if (ntokens == 2) {
3349 if (!(pconn = conn_by_user_prefix(arg[i], &match_result))) {
3350 cmd_reply_no_such_conn(CMD_TAKE, caller, arg[i], match_result);
3351 goto end;
3353 i++; /* found a conn, now reference the second argument */
3356 if (strcmp(arg[i], "-") == 0) {
3357 if (!is_newgame) {
3358 cmd_reply(CMD_TAKE, caller, C_FAIL,
3359 _("You cannot issue \"/take -\" when "
3360 "the game has already started."));
3361 goto end;
3364 /* Find first uncontrolled player. This will return NULL if there is
3365 * no free players at the moment. Later call to
3366 * connection_attach() will create new player for such NULL
3367 * cases. */
3368 pplayer = find_uncontrolled_player();
3369 if (pplayer) {
3370 /* Make it human! */
3371 pplayer->ai_controlled = FALSE;
3373 } else if (!(pplayer = player_by_name_prefix(arg[i], &match_result))) {
3374 cmd_reply_no_such_player(CMD_TAKE, caller, arg[i], match_result);
3375 goto end;
3378 /******** PART II: do the attaching ********/
3380 /* Take not possible if the player is involved in a delegation (either
3381 * it's being controlled, or it's been put aside by the delegate). */
3382 if (player_delegation_active(pplayer)) {
3383 cmd_reply(CMD_TAKE, caller, C_FAIL, _("A delegation is active for player "
3384 "'%s'. /take not possible."),
3385 player_name(pplayer));
3386 goto end;
3389 /* check allowtake for permission */
3390 if (!is_allowed_to_take(pplayer, FALSE, msg, sizeof(msg))) {
3391 cmd_reply(CMD_TAKE, caller, C_FAIL, "%s", msg);
3392 goto end;
3395 /* taking your own player makes no sense. */
3396 if ((NULL != pplayer && !pconn->observer && pplayer == pconn->playing)
3397 || (NULL == pplayer && !pconn->observer && NULL != pconn->playing)) {
3398 cmd_reply(CMD_TAKE, caller, C_FAIL, _("%s already controls %s."),
3399 pconn->username,
3400 player_name(pconn->playing));
3401 goto end;
3404 /* Make sure there is free player slot if there is need to
3405 * create new player. This is necessary for previously
3406 * detached connections only. Others can reuse the slot
3407 * they first release. */
3408 if (!pplayer && !pconn->playing
3409 && (normal_player_count() >= game.server.max_players
3410 || normal_player_count() >= server.playable_nations)) {
3411 cmd_reply(CMD_TAKE, caller, C_FAIL,
3412 _("There is no free player slot for %s."),
3413 pconn->username);
3414 goto end;
3416 fc_assert_action(player_count() <= player_slot_count(), goto end);
3418 res = TRUE;
3419 if (check) {
3420 goto end;
3423 /* If the player is controlled by another user, forcibly detach
3424 * the user. */
3425 if (pplayer && pplayer->is_connected) {
3426 if (NULL == caller) {
3427 notify_conn(NULL, NULL, E_CONNECTION, ftc_server,
3428 _("Reassigned nation to %s by server console."),
3429 pconn->username);
3430 } else {
3431 notify_conn(NULL, NULL, E_CONNECTION, ftc_server,
3432 _("Reassigned nation to %s by %s."),
3433 pconn->username,
3434 caller->username);
3437 /* We are reassigning this nation, so we need to detach the current
3438 * user to set a new one. */
3439 conn_list_iterate(pplayer->connections, aconn) {
3440 if (!aconn->observer) {
3441 connection_detach(aconn, FALSE);
3443 } conn_list_iterate_end;
3446 /* if the connection is already attached to another player,
3447 * unattach and cleanup old player (rename, remove, etc)
3448 * We may have been observing the player we now want to take */
3449 if (NULL != pconn->playing || pconn->observer) {
3450 char name[MAX_LEN_NAME];
3452 if (pplayer) {
3453 /* if pconn->playing is removed, we'll lose pplayer */
3454 sz_strlcpy(name, player_name(pplayer));
3457 connection_detach(pconn, TRUE);
3459 if (pplayer) {
3460 /* find pplayer again; the pointer might have been changed */
3461 pplayer = player_by_name(name);
3465 /* Now attach to new player */
3466 if ((res = connection_attach(pconn, pplayer, FALSE))) {
3467 /* Successfully attached */
3468 pplayer = pconn->playing; /* In case pplayer was NULL. */
3470 /* inform about the status before changes */
3471 cmd_reply(CMD_TAKE, caller, C_OK, _("%s now controls %s (%s, %s)."),
3472 pconn->username,
3473 player_name(pplayer),
3474 is_barbarian(pplayer)
3475 ? _("Barbarian")
3476 : pplayer->ai_controlled
3477 ? _("AI")
3478 : _("Human"),
3479 pplayer->is_alive
3480 ? _("Alive")
3481 : _("Dead"));
3482 } else {
3483 cmd_reply(CMD_TAKE, caller, C_FAIL,
3484 _("%s failed to attach to any player."),
3485 pconn->username);
3488 end:;
3489 /* free our args */
3490 for (i = 0; i < ntokens; i++) {
3491 free(arg[i]);
3493 return res;
3496 /**************************************************************************
3497 Detach from a player. if that player wasn't /created and you were
3498 controlling the player, remove it (and then detach any observers as well).
3500 If called for a global observer connection (where pconn->playing is NULL)
3501 then it will correctly detach from observing mode.
3502 **************************************************************************/
3503 static bool detach_command(struct connection *caller, char *str, bool check)
3505 int i = 0, ntokens = 0;
3506 char buf[MAX_LEN_CONSOLE_LINE], *arg[1];
3507 enum m_pre_result match_result;
3508 struct connection *pconn = NULL;
3509 struct player *pplayer = NULL;
3510 bool res = FALSE;
3512 sz_strlcpy(buf, str);
3513 ntokens = get_tokens(buf, arg, 1, TOKEN_DELIMITERS);
3515 if (!caller && ntokens == 0) {
3516 cmd_reply(CMD_DETACH, caller, C_SYNTAX, _("Usage:\n%s"),
3517 command_synopsis(command_by_number(CMD_DETACH)));
3518 goto end;
3521 /* match the connection if the argument was given */
3522 if (ntokens == 1
3523 && !(pconn = conn_by_user_prefix(arg[0], &match_result))) {
3524 cmd_reply_no_such_conn(CMD_DETACH, caller, arg[0], match_result);
3525 goto end;
3528 /* if no argument is given, the caller wants to detach himself */
3529 if (!pconn) {
3530 pconn = caller;
3533 /* if pconn and caller are not the same, only continue
3534 * if we're console, or we have ALLOW_HACK */
3535 if (pconn != caller && caller && caller->access_level != ALLOW_HACK) {
3536 cmd_reply(CMD_DETACH, caller, C_FAIL,
3537 _("You can not detach other users."));
3538 goto end;
3541 pplayer = pconn->playing;
3543 /* must have someone to detach from... */
3544 if (!pplayer && !pconn->observer) {
3545 cmd_reply(CMD_DETACH, caller, C_FAIL,
3546 _("%s is not attached to any player."), pconn->username);
3547 goto end;
3550 res = TRUE;
3551 if (check) {
3552 goto end;
3555 if (pplayer) {
3556 cmd_reply(CMD_DETACH, caller, C_OK, _("%s detaching from %s"),
3557 pconn->username, player_name(pplayer));
3558 } else {
3559 cmd_reply(CMD_DETACH, caller, C_OK, _("%s no longer observing."),
3560 pconn->username);
3563 /* Actually do the detaching. */
3564 connection_detach(pconn, TRUE);
3566 /* The user explicitly wanted to detach, so if a player is marked for him,
3567 * reset its username. */
3568 players_iterate(aplayer) {
3569 if (0 == strncmp(aplayer->username, pconn->username, MAX_LEN_NAME)) {
3570 sz_strlcpy(aplayer->username, _(ANON_USER_NAME));
3571 aplayer->unassigned_user = TRUE;
3572 send_player_info_c(aplayer, NULL);
3574 } players_iterate_end;
3576 check_for_full_turn_done();
3578 end:
3579 fc_assert_ret_val(ntokens <= 1, FALSE);
3581 /* free our args */
3582 for (i = 0; i < ntokens; i++) {
3583 free(arg[i]);
3585 return res;
3588 /**************************************************************************
3589 Loads a file, complete with access checks and error messages sent back
3590 to the caller on failure.
3592 * caller is the connection requesting the load, or NULL for a
3593 command-line load. Error messages are sent back to the caller and
3594 an access check is done to make sure they are allowed to load.
3596 * filename is simply the name of the file to be loaded. This may be
3597 approximate; the function will look for appropriate suffixes and will
3598 check in the various directories to see if the file is found.
3600 * if check is set then only a test run is done and no actual loading
3601 is attempted.
3603 * The return value is true if the load succeeds, or would succeed;
3604 false if there's an error in the file or file name. Some errors
3605 in loading however could be unrecoverable (if the save game is
3606 legitimate but has inconsistencies) and would lead to a broken server
3607 afterwards.
3608 **************************************************************************/
3609 bool load_command(struct connection *caller, const char *filename, bool check,
3610 bool cmdline_load)
3612 struct timer *loadtimer, *uloadtimer;
3613 struct section_file *file;
3614 char arg[MAX_LEN_PATH];
3615 struct conn_list *global_observers;
3617 if (!filename || filename[0] == '\0') {
3618 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage:\n%s"),
3619 command_synopsis(command_by_number(CMD_LOAD)));
3620 return FALSE;
3622 if (S_S_INITIAL != server_state()) {
3623 cmd_reply(CMD_LOAD, caller, C_FAIL,
3624 _("Cannot load a game while another is running."));
3625 dlsend_packet_game_load(game.est_connections, TRUE, filename);
3626 return FALSE;
3628 if (!is_safe_filename(filename) && is_restricted(caller)) {
3629 cmd_reply(CMD_LOAD, caller, C_FAIL,
3630 _("Name \"%s\" disallowed for security reasons."),
3631 filename);
3632 return FALSE;
3636 /* it is a normal savegame or maybe a scenario */
3637 char testfile[MAX_LEN_PATH];
3638 const struct strvec *pathes[] = {
3639 get_save_dirs(), get_scenario_dirs(), NULL
3641 const char *exts[] = {
3642 "sav", "gz", "bz2", "xz", "sav.gz", "sav.bz2", "sav.xz", NULL
3644 const char **ext, *found = NULL;
3645 const struct strvec **path;
3647 if (cmdline_load) {
3648 /* Allow plain names being loaded with '--file' option, but not otherwise
3649 * (no loading of arbitrary files by unauthorized users)
3650 * Iterate through ALL paths to check for file with plain name before
3651 * looking any path with an extension, i.e., prefer plain name file
3652 * in later directory over file with extension in name in earlier
3653 * directory. */
3654 for (path = pathes; !found && *path; path++) {
3655 found = fileinfoname(*path, filename);
3656 if (found != NULL) {
3657 sz_strlcpy(arg, found);
3662 for (path = pathes; !found && *path; path++) {
3663 for (ext = exts; !found && *ext; ext++) {
3664 fc_snprintf(testfile, sizeof(testfile), "%s.%s", filename, *ext);
3665 found = fileinfoname(*path, testfile);
3666 if (found != NULL) {
3667 sz_strlcpy(arg, found);
3672 if (is_restricted(caller) && !found) {
3673 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Cannot find savegame or "
3674 "scenario with the name \"%s\"."), filename);
3675 return FALSE;
3678 if (!found) {
3679 sz_strlcpy(arg, filename);
3683 /* attempt to parse the file */
3685 if (!(file = secfile_load(arg, FALSE))) {
3686 log_error("Error loading savefile '%s': %s", arg, secfile_error());
3687 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"),
3688 arg);
3689 dlsend_packet_game_load(game.est_connections, TRUE, arg);
3690 return FALSE;
3693 if (check) {
3694 return TRUE;
3697 /* Detach current players, before we blow them away. */
3698 global_observers = conn_list_new();
3699 conn_list_iterate(game.est_connections, pconn) {
3700 if (pconn->playing != NULL) {
3701 connection_detach(pconn, TRUE);
3702 } else if (pconn->observer) {
3703 conn_list_append(global_observers, pconn);
3704 connection_detach(pconn, TRUE);
3706 } conn_list_iterate_end;
3708 player_info_freeze();
3710 /* Now free all game data. */
3711 server_game_free();
3712 server_game_init();
3714 loadtimer = timer_new(TIMER_CPU, TIMER_ACTIVE);
3715 timer_start(loadtimer);
3716 uloadtimer = timer_new(TIMER_USER, TIMER_ACTIVE);
3717 timer_start(uloadtimer);
3719 sz_strlcpy(srvarg.load_filename, arg);
3721 savegame2_load(file);
3722 secfile_check_unused(file);
3723 secfile_destroy(file);
3725 log_verbose("Load time: %g seconds (%g apparent)",
3726 timer_read_seconds(loadtimer), timer_read_seconds(uloadtimer));
3727 timer_destroy(loadtimer);
3728 timer_destroy(uloadtimer);
3730 sanity_check();
3732 log_verbose("load_command() does send_rulesets()");
3733 conn_list_compression_freeze(game.est_connections);
3734 send_rulesets(game.est_connections);
3735 send_server_settings(game.est_connections);
3736 send_scenario_info(game.est_connections);
3737 send_scenario_description(game.est_connections);
3738 send_game_info(game.est_connections);
3739 conn_list_compression_thaw(game.est_connections);
3741 /* Send information about the new players. */
3742 player_info_thaw();
3743 send_player_diplstate_c(NULL, NULL);
3745 /* Everything seemed to load ok; spread the good news. */
3746 dlsend_packet_game_load(game.est_connections, TRUE, srvarg.load_filename);
3748 /* Attach connections to players. Currently, this applies only
3749 * to connections that have the same username as a player. */
3750 conn_list_iterate(game.est_connections, pconn) {
3751 players_iterate(pplayer) {
3752 if (strcmp(pconn->username, pplayer->username) == 0) {
3753 connection_attach(pconn, pplayer, FALSE);
3754 break;
3756 } players_iterate_end;
3757 } conn_list_iterate_end;
3759 /* Reattach global observers. */
3760 conn_list_iterate(global_observers, pconn) {
3761 if (NULL == pconn->playing) {
3762 /* May have been assigned to a player before. */
3763 connection_attach(pconn, NULL, TRUE);
3765 } conn_list_iterate_end;
3766 conn_list_destroy(global_observers);
3768 (void) aifill(game.info.aifill);
3770 achievements_iterate(pach) {
3771 players_iterate(pplayer) {
3772 struct packet_achievement_info pack;
3774 pack.id = achievement_index(pach);
3775 pack.gained = achievement_player_has(pach, pplayer);
3776 pack.first = (pach->first == pplayer);
3778 lsend_packet_achievement_info(pplayer->connections, &pack);
3779 } players_iterate_end;
3780 } achievements_iterate_end;
3782 return TRUE;
3785 /**************************************************************************
3786 Load rulesets from a given ruleset directory.
3788 Security: There are some rudimentary checks in load_rulesets() to see
3789 if this directory really is a viable ruleset directory. For public
3790 servers, we check against directory redirection (is_safe_filename) and
3791 other bad stuff in the directory name, and will only use directories
3792 inside the data directories.
3793 **************************************************************************/
3794 static bool set_rulesetdir(struct connection *caller, char *str, bool check,
3795 int read_recursion)
3797 char filename[512];
3798 const char *pfilename;
3800 if (NULL == str || '\0' == str[0]) {
3801 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3802 _("You must provide a ruleset name. Use \"/show ruleset\" to "
3803 "see what is the current ruleset."));
3804 return FALSE;
3806 if (game_was_started() || !map_is_empty()) {
3807 cmd_reply(CMD_RULESETDIR, caller, C_FAIL,
3808 _("This setting can't be modified after the game has started."));
3809 return FALSE;
3812 if (strcmp(str, game.server.rulesetdir) == 0) {
3813 cmd_reply(CMD_RULESETDIR, caller, C_COMMENT,
3814 _("Ruleset directory is already \"%s\""), str);
3815 return FALSE;
3818 if (is_restricted(caller)
3819 && (!is_safe_filename(str) || strchr(str, '.'))) {
3820 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3821 _("Name \"%s\" disallowed for security reasons."),
3822 str);
3823 return FALSE;
3826 fc_snprintf(filename, sizeof(filename), "%s", str);
3827 pfilename = fileinfoname(get_data_dirs(), filename);
3828 if (!pfilename) {
3829 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3830 _("Ruleset directory \"%s\" not found"), str);
3831 return FALSE;
3834 if (!check) {
3835 bool success = TRUE;
3836 char old[512];
3838 sz_strlcpy(old, game.server.rulesetdir);
3839 log_verbose("set_rulesetdir() does load_rulesets() with \"%s\"", str);
3840 sz_strlcpy(game.server.rulesetdir, str);
3842 /* load the ruleset (and game settings defined in the ruleset) */
3843 player_info_freeze();
3844 if (!load_rulesets(old, TRUE, FALSE)) {
3845 success = FALSE;
3847 /* While loading of the requested ruleset failed, we might
3848 * have changed ruleset from third one to default. Handle
3849 * rest of the ruleset changing accordingly. */
3852 if (game.est_connections) {
3853 /* Now that the rulesets are loaded we immediately send updates to any
3854 * connected clients. */
3855 send_rulesets(game.est_connections);
3857 /* show ruleset summary and list changed values */
3858 show_ruleset_info(caller, CMD_RULESETDIR, check, read_recursion);
3859 player_info_thaw();
3861 if (success) {
3862 cmd_reply(CMD_RULESETDIR, caller, C_OK,
3863 _("Ruleset directory set to \"%s\""), str);
3864 } else {
3865 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3866 _("Failed loading rulesets from directory \"%s\", using \"%s\""),
3867 str, game.server.rulesetdir);
3870 return success;
3873 return TRUE;
3876 /****************************************************************************
3877 /ignore command handler.
3878 ****************************************************************************/
3879 static bool ignore_command(struct connection *caller, char *str, bool check)
3881 char buf[128];
3882 struct conn_pattern *ppattern;
3884 if (NULL == caller) {
3885 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3886 _("That would be rather silly, since you are not a player."));
3887 return FALSE;
3890 ppattern = conn_pattern_from_string(str, CPT_USER, buf, sizeof(buf));
3891 if (NULL == ppattern) {
3892 cmd_reply(CMD_IGNORE, caller, C_SYNTAX,
3893 _("%s. Try /help ignore"), buf);
3894 return FALSE;
3897 if (check) {
3898 conn_pattern_destroy(ppattern);
3899 return TRUE;
3902 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3903 conn_pattern_list_append(caller->server.ignore_list, ppattern);
3904 cmd_reply(CMD_IGNORE, caller, C_COMMENT,
3905 _("Added pattern %s as entry %d to your ignore list."),
3906 buf, conn_pattern_list_size(caller->server.ignore_list));
3908 return TRUE;
3911 /****************************************************************************
3912 /unignore command handler.
3913 ****************************************************************************/
3914 static bool unignore_command(struct connection *caller,
3915 char *str, bool check)
3917 char buf[128], *c;
3918 int first, last, n;
3920 if (!caller) {
3921 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3922 _("That would be rather silly, since you are not a player."));
3923 return FALSE;
3926 sz_strlcpy(buf, str);
3927 remove_leading_trailing_spaces(buf);
3929 n = conn_pattern_list_size(caller->server.ignore_list);
3930 if (n == 0) {
3931 cmd_reply(CMD_UNIGNORE, caller, C_FAIL, _("Your ignore list is empty."));
3932 return FALSE;
3935 /* Parse the range. */
3936 if ('\0' == buf[0]) {
3937 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3938 _("Missing range. Try /help unignore."));
3939 return FALSE;
3940 } else if ((c = strchr(buf, '-'))) {
3941 *c++ = '\0';
3942 if ('\0' == buf[0]) {
3943 first = 1;
3944 } else if (!str_to_int(buf, &first)) {
3945 *--c = '-';
3946 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3947 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3948 return FALSE;
3950 if ('\0' == *c) {
3951 last = n;
3952 } else if (!str_to_int(c, &last)) {
3953 *--c = '-';
3954 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3955 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3956 return FALSE;
3958 } else {
3959 if (!str_to_int(buf, &first)) {
3960 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3961 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3962 return FALSE;
3964 last = first;
3967 if (!(1 <= first && first <= last && last <= n)) {
3968 if (first == last) {
3969 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3970 _("Invalid entry number: %d."), first);
3971 } else {
3972 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3973 _("Invalid range: %d to %d."), first, last);
3975 return FALSE;
3978 if (check) {
3979 return TRUE;
3982 n = 1;
3983 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
3984 if (first <= n) {
3985 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3986 cmd_reply(CMD_UNIGNORE, caller, C_COMMENT,
3987 _("Removed pattern %s (entry %d) from your ignore list."),
3988 buf, n);
3989 conn_pattern_list_remove(caller->server.ignore_list, ppattern);
3991 n++;
3992 if (n > last) {
3993 break;
3995 } conn_pattern_list_iterate_end;
3997 return TRUE;
4000 /****************************************************************************
4001 /playercolor command handler.
4002 ****************************************************************************/
4003 static bool playercolor_command(struct connection *caller,
4004 char *str, bool check)
4006 enum m_pre_result match_result;
4007 struct player *pplayer;
4008 struct rgbcolor *prgbcolor = NULL;
4009 int ntokens = 0;
4010 char *token[2];
4011 bool ret = TRUE;
4013 ntokens = get_tokens(str, token, 2, TOKEN_DELIMITERS);
4015 if (ntokens != 2) {
4016 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4017 _("Two arguments needed. See '/help playercolor'."));
4018 ret = FALSE;
4019 goto cleanup;
4022 pplayer = player_by_name_prefix(token[0], &match_result);
4024 if (!pplayer) {
4025 cmd_reply_no_such_player(CMD_PLAYERCOLOR, caller, token[0], match_result);
4026 ret = FALSE;
4027 goto cleanup;
4031 const char *reason;
4032 if (!player_color_changeable(pplayer, &reason)) {
4033 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL, "%s", reason);
4034 ret = FALSE;
4035 goto cleanup;
4039 if (0 == fc_strcasecmp(token[1], "reset")) {
4040 if (!game_was_started()) {
4041 prgbcolor = NULL;
4042 } else {
4043 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL,
4044 _("Can only unset player color before game starts."));
4045 ret = FALSE;
4046 goto cleanup;
4048 } else if (!rgbcolor_from_hex(&prgbcolor, token[1])) {
4049 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4050 _("Invalid player color definition. See '/help playercolor'."));
4051 ret = FALSE;
4052 goto cleanup;
4055 if (prgbcolor != NULL) {
4056 players_iterate(pother) {
4057 if (pother != pplayer && pother->rgb != NULL
4058 && rgbcolors_are_equal(pother->rgb, prgbcolor)) {
4059 cmd_reply(CMD_PLAYERCOLOR, caller, C_WARNING,
4060 /* TRANS: "... [c0ffee] for Caesar ... to Hammurabi." */
4061 _("Warning: new color [%s] for %s is identical to %s."),
4062 player_color_ftstr(pother), player_name(pplayer),
4063 player_name(pother));
4065 } players_iterate_end;
4068 if (check) {
4069 goto cleanup;
4072 server_player_set_color(pplayer, prgbcolor);
4073 cmd_reply(CMD_PLAYERCOLOR, caller, C_OK,
4074 _("Color of player %s set to [%s]."), player_name(pplayer),
4075 player_color_ftstr(pplayer));
4077 cleanup:
4079 rgbcolor_destroy(prgbcolor);
4080 free_tokens(token, ntokens);
4082 return ret;
4085 /**************************************************************************
4086 Handle quit command
4087 **************************************************************************/
4088 static bool quit_game(struct connection *caller, bool check)
4090 if (!check) {
4091 cmd_reply(CMD_QUIT, caller, C_OK, _("Goodbye."));
4092 server_quit();
4094 return TRUE;
4097 /**************************************************************************
4098 Main entry point for "command input".
4099 **************************************************************************/
4100 bool handle_stdin_input(struct connection *caller, char *str)
4102 return handle_stdin_input_real(caller, str, FALSE, 0);
4105 /**************************************************************************
4106 Handle "command input", which could really come from stdin on console,
4107 or from client chat command, or read from file with -r, etc.
4108 caller==NULL means console, str is the input, which may optionally
4109 start with SERVER_COMMAND_PREFIX character.
4111 If check is TRUE, then do nothing, just check syntax.
4112 **************************************************************************/
4113 static bool handle_stdin_input_real(struct connection *caller, char *str,
4114 bool check, int read_recursion)
4116 char full_command[MAX_LEN_CONSOLE_LINE];
4117 char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE];
4118 char *cptr_s, *cptr_d;
4119 enum command_id cmd;
4120 enum cmdlevel level;
4122 /* Remove leading and trailing spaces, and server command prefix. */
4123 cptr_s = str = skip_leading_spaces(str);
4124 if ('\0' == *cptr_s || '#' == *cptr_s) {
4125 /* This appear to be a comment or blank line. */
4126 return FALSE;
4129 if (SERVER_COMMAND_PREFIX == *cptr_s) {
4130 /* Commands may be prefixed with SERVER_COMMAND_PREFIX, even when
4131 * given on the server command line. */
4132 cptr_s++;
4133 remove_leading_spaces(cptr_s);
4134 if ('\0' == *cptr_s) {
4135 /* This appear to be a blank line. */
4136 return FALSE;
4139 remove_trailing_spaces(cptr_s);
4141 /* notify to the server console */
4142 if (!check && caller) {
4143 con_write(C_COMMENT, "%s: '%s'", caller->username, str);
4146 /* if the caller may not use any commands at all, don't waste any time */
4147 if (may_use_nothing(caller)) {
4148 cmd_reply(CMD_HELP, caller, C_FAIL,
4149 _("Sorry, you are not allowed to use server commands."));
4150 return FALSE;
4153 /* copy the full command, in case we need it for voting purposes. */
4154 sz_strlcpy(full_command, cptr_s);
4157 * cptr_s points now to the beginning of the real command. It has
4158 * skipped leading whitespace, the SERVER_COMMAND_PREFIX and any
4159 * other non-alphanumeric characters.
4161 for (cptr_d = command; *cptr_s != '\0' && fc_isalnum(*cptr_s)
4162 && cptr_d < command + sizeof(command) - 1; cptr_s++, cptr_d++) {
4163 *cptr_d = *cptr_s;
4165 *cptr_d = '\0';
4167 /* cptr_s now contains the arguments. */
4168 sz_strlcpy(arg, skip_leading_spaces(cptr_s));
4170 cmd = command_named(command, FALSE);
4171 if (cmd == CMD_AMBIGUOUS) {
4172 cmd = command_named(command, TRUE);
4173 cmd_reply(cmd, caller, C_SYNTAX,
4174 _("Warning: '%s' interpreted as '%s', but it is ambiguous."
4175 " Try '%shelp'."),
4176 command, command_name_by_number(cmd), caller?"/":"");
4177 } else if (cmd == CMD_UNRECOGNIZED) {
4178 cmd_reply(cmd, caller, C_SYNTAX, _("Unknown command '%s%s'. "
4179 " Try '%shelp'."),
4180 caller ? "/" : "", command, caller ? "/" : "");
4181 return FALSE;
4184 level = command_level(command_by_number(cmd));
4186 if (conn_can_vote(caller, NULL) && level == ALLOW_CTRL
4187 && conn_get_access(caller) == ALLOW_BASIC && !check
4188 && !vote_would_pass_immediately(caller, cmd)) {
4189 struct vote *vote;
4190 bool caller_had_vote = (NULL != get_vote_by_caller(caller));
4192 /* Check if the vote command would succeed. If we already have a vote
4193 * going, cancel it in favour of the new vote command. You can only
4194 * have one vote at a time. This is done by vote_new(). */
4195 if (handle_stdin_input_real(caller, full_command, TRUE,
4196 read_recursion + 1)
4197 && (vote = vote_new(caller, arg, cmd))) {
4198 char votedesc[MAX_LEN_CONSOLE_LINE];
4199 const struct player *teamplr;
4200 const char *what;
4201 struct ft_color color;
4203 if (caller_had_vote) {
4204 cmd_reply(CMD_VOTE, caller, C_COMMENT,
4205 /* TRANS: "vote" as a process */
4206 _("Your new vote canceled your previous vote."));
4209 describe_vote(vote, votedesc, sizeof(votedesc));
4211 if (vote_is_team_only(vote)) {
4212 /* TRANS: "vote" as a process */
4213 what = _("New teamvote");
4214 teamplr = conn_get_player(caller);
4215 color = ftc_vote_team;
4216 } else {
4217 /* TRANS: "vote" as a process */
4218 what = _("New vote");
4219 teamplr = NULL;
4220 color = ftc_vote_public;
4222 notify_team(teamplr, NULL, E_VOTE_NEW, color,
4223 /* TRANS: "[New vote|New teamvote] (number 3)
4224 * by fred: proposed change" */
4225 _("%s (number %d) by %s: %s"), what,
4226 vote->vote_no, caller->username, votedesc);
4228 /* Vote on your own suggestion. */
4229 connection_vote(caller, vote, VOTE_YES);
4230 return TRUE;
4232 } else {
4233 cmd_reply(CMD_VOTE, caller, C_FAIL,
4234 /* TRANS: "vote" as a process */
4235 _("Your new vote (\"%s\") was not "
4236 "legal or was not recognized."), full_command);
4237 return FALSE;
4241 if (caller
4242 && !((check || vote_would_pass_immediately(caller, cmd))
4243 && conn_get_access(caller) >= ALLOW_BASIC
4244 && level == ALLOW_CTRL)
4245 && conn_get_access(caller) < level) {
4246 cmd_reply(cmd, caller, C_FAIL,
4247 _("You are not allowed to use this command."));
4248 return FALSE;
4251 if (!check) {
4252 struct conn_list *echo_list = NULL;
4253 bool echo_list_allocated = FALSE;
4255 switch (command_echo(command_by_number(cmd))) {
4256 case CMD_ECHO_NONE:
4257 break;
4258 case CMD_ECHO_ADMINS:
4259 conn_list_iterate(game.est_connections, pconn) {
4260 if (ALLOW_ADMIN <= conn_get_access(pconn)) {
4261 if (NULL == echo_list) {
4262 echo_list = conn_list_new();
4263 echo_list_allocated = TRUE;
4265 conn_list_append(echo_list, pconn);
4267 } conn_list_iterate_end;
4268 break;
4269 case CMD_ECHO_ALL:
4270 echo_list = game.est_connections;
4271 break;
4274 if (NULL != echo_list) {
4275 if (caller) {
4276 notify_conn(echo_list, NULL, E_SETTING, ftc_any,
4277 "%s: '%s %s'", caller->username, command, arg);
4278 } else {
4279 notify_conn(echo_list, NULL, E_SETTING, ftc_server_prompt,
4280 "%s: '%s %s'", _("(server prompt)"), command, arg);
4282 if (echo_list_allocated) {
4283 conn_list_destroy(echo_list);
4288 switch(cmd) {
4289 case CMD_REMOVE:
4290 return remove_player_command(caller, arg, check);
4291 case CMD_SAVE:
4292 return save_command(caller, arg, check);
4293 case CMD_SCENSAVE:
4294 return scensave_command(caller, arg, check);
4295 case CMD_LOAD:
4296 return load_command(caller, arg, check, FALSE);
4297 case CMD_METAPATCHES:
4298 return metapatches_command(caller, arg, check);
4299 case CMD_METAMESSAGE:
4300 return metamessage_command(caller, arg, check);
4301 case CMD_METACONN:
4302 return metaconnection_command(caller, arg, check);
4303 case CMD_METASERVER:
4304 return metaserver_command(caller, arg, check);
4305 case CMD_HELP:
4306 return show_help(caller, arg);
4307 case CMD_SRVID:
4308 return show_serverid(caller, arg);
4309 case CMD_LIST:
4310 return show_list(caller, arg);
4311 case CMD_AITOGGLE:
4312 return toggle_ai_command(caller, arg, check);
4313 case CMD_TAKE:
4314 return take_command(caller, arg, check);
4315 case CMD_OBSERVE:
4316 return observe_command(caller, arg, check);
4317 case CMD_DETACH:
4318 return detach_command(caller, arg, check);
4319 case CMD_CREATE:
4320 return create_command(caller, arg, check);
4321 case CMD_AWAY:
4322 return away_command(caller, check);
4323 case CMD_HANDICAPPED:
4324 case CMD_NOVICE:
4325 case CMD_EASY:
4326 case CMD_NORMAL:
4327 case CMD_HARD:
4328 case CMD_CHEATING:
4329 #ifdef DEBUG
4330 case CMD_EXPERIMENTAL:
4331 #endif
4332 return set_ai_level_named(caller, arg, command_name_by_number(cmd), check);
4333 case CMD_QUIT:
4334 return quit_game(caller, check);
4335 case CMD_CUT:
4336 return cut_client_connection(caller, arg, check);
4337 case CMD_SHOW:
4338 return show_command(caller, arg, check);
4339 case CMD_EXPLAIN:
4340 return explain_option(caller, arg, check);
4341 case CMD_DEBUG:
4342 return debug_command(caller, arg, check);
4343 case CMD_SET:
4344 return set_command(caller, arg, check);
4345 case CMD_TEAM:
4346 return team_command(caller, arg, check);
4347 case CMD_RULESETDIR:
4348 return set_rulesetdir(caller, arg, check, read_recursion);
4349 case CMD_WALL:
4350 return wall(arg, check);
4351 case CMD_CONNECTMSG:
4352 return connectmsg_command(caller, arg, check);
4353 case CMD_VOTE:
4354 return vote_command(caller, arg, check);
4355 case CMD_CANCELVOTE:
4356 return cancelvote_command(caller, arg, check);
4357 case CMD_READ_SCRIPT:
4358 return read_command(caller, arg, check, read_recursion);
4359 case CMD_WRITE_SCRIPT:
4360 return write_command(caller, arg, check);
4361 case CMD_RESET:
4362 return reset_command(caller, arg, check, read_recursion);
4363 case CMD_DEFAULT:
4364 return default_command(caller, arg, check);
4365 case CMD_LUA:
4366 return lua_command(caller, arg, check);
4367 case CMD_KICK:
4368 return kick_command(caller, arg, check);
4369 case CMD_DELEGATE:
4370 return delegate_command(caller, arg, check);
4371 case CMD_FCDB:
4372 return fcdb_command(caller, arg, check);
4373 case CMD_MAPIMG:
4374 return mapimg_command(caller, arg, check);
4375 case CMD_RFCSTYLE: /* see console.h for an explanation */
4376 if (!check) {
4377 con_set_style(!con_get_style());
4379 return TRUE;
4380 case CMD_CMDLEVEL:
4381 return cmdlevel_command(caller, arg, check);
4382 case CMD_FIRSTLEVEL:
4383 return firstlevel_command(caller, check);
4384 case CMD_TIMEOUT:
4385 return timeout_command(caller, arg, check);
4386 case CMD_START_GAME:
4387 return start_command(caller, check, FALSE);
4388 case CMD_END_GAME:
4389 return end_command(caller, arg, check);
4390 case CMD_SURRENDER:
4391 return surrender_command(caller, arg, check);
4392 case CMD_IGNORE:
4393 return ignore_command(caller, arg, check);
4394 case CMD_UNIGNORE:
4395 return unignore_command(caller, arg, check);
4396 case CMD_PLAYERCOLOR:
4397 return playercolor_command(caller, arg, check);
4398 case CMD_NUM:
4399 case CMD_UNRECOGNIZED:
4400 case CMD_AMBIGUOUS:
4401 break;
4403 /* should NEVER happen! */
4404 log_error("Unknown command variant: %d.", cmd);
4405 return FALSE;
4408 /**************************************************************************
4409 End the game immediately in a draw.
4410 **************************************************************************/
4411 static bool end_command(struct connection *caller, char *str, bool check)
4413 if (S_S_RUNNING == server_state()) {
4414 if (check) {
4415 return TRUE;
4417 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4418 _("Game is over."));
4419 set_server_state(S_S_OVER);
4420 force_end_of_sniff = TRUE;
4421 cmd_reply(CMD_END_GAME, caller, C_OK,
4422 _("Ending the game. The server will restart once all clients "
4423 "have disconnected."));
4424 return TRUE;
4425 } else {
4426 cmd_reply(CMD_END_GAME, caller, C_FAIL,
4427 _("Cannot end the game: no game running."));
4428 return FALSE;
4432 /**************************************************************************
4433 Concede the game. You still continue playing until all but one player
4434 or team remains un-conceded.
4435 **************************************************************************/
4436 static bool surrender_command(struct connection *caller, char *str, bool check)
4438 struct player *pplayer;
4440 if (caller == NULL || !conn_controls_player(caller)) {
4441 cmd_reply(CMD_SURRENDER, caller, C_FAIL,
4442 _("You are not allowed to use this command."));
4443 return FALSE;
4446 if (S_S_RUNNING != server_state()) {
4447 cmd_reply(CMD_SURRENDER, caller, C_FAIL, _("You cannot surrender now."));
4448 return FALSE;
4451 pplayer = conn_get_player(caller);
4452 if (player_status_check(pplayer, PSTATUS_SURRENDER)) {
4453 cmd_reply(CMD_SURRENDER, caller, C_FAIL,
4454 _("You have already conceded the game."));
4455 return FALSE;
4458 if (check) {
4459 return TRUE;
4462 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4463 _("%s has conceded the game and can no longer win."),
4464 player_name(pplayer));
4465 player_status_add(pplayer, PSTATUS_SURRENDER);
4466 return TRUE;
4469 /* Define the possible arguments to the reset command */
4470 #define SPECENUM_NAME reset_args
4471 #define SPECENUM_VALUE0 RESET_GAME
4472 #define SPECENUM_VALUE0NAME "game"
4473 #define SPECENUM_VALUE1 RESET_RULESET
4474 #define SPECENUM_VALUE1NAME "ruleset"
4475 #define SPECENUM_VALUE2 RESET_SCRIPT
4476 #define SPECENUM_VALUE2NAME "script"
4477 #define SPECENUM_VALUE3 RESET_DEFAULT
4478 #define SPECENUM_VALUE3NAME "default"
4479 #include "specenum_gen.h"
4481 /**************************************************************************
4482 Returns possible parameters for the reset command.
4483 **************************************************************************/
4484 static const char *reset_accessor(int i)
4486 i = CLIP(0, i, reset_args_max());
4487 return reset_args_name((enum reset_args) i);
4490 /**************************************************************************
4491 Reload the game settings from the ruleset and reload the init script if
4492 one was used.
4493 **************************************************************************/
4494 static bool reset_command(struct connection *caller, char *arg, bool check,
4495 int read_recursion)
4497 enum m_pre_result result;
4498 int ind;
4500 /* match the argument */
4501 result = match_prefix(reset_accessor, reset_args_max() + 1, 0,
4502 fc_strncasecmp, NULL, arg, &ind);
4504 switch (result) {
4505 case M_PRE_EXACT:
4506 case M_PRE_ONLY:
4507 /* we have a match */
4508 break;
4509 case M_PRE_AMBIGUOUS:
4510 case M_PRE_EMPTY:
4511 /* use 'ruleset' [1] if the game was not started; else use 'game' [2] */
4512 if (S_S_INITIAL == server_state() && game.info.is_new_game) {
4513 cmd_reply(CMD_RESET, caller, C_WARNING,
4514 _("Guessing argument 'ruleset'."));
4515 ind = RESET_RULESET;
4516 } else {
4517 cmd_reply(CMD_RESET, caller, C_WARNING,
4518 _("Guessing argument 'game'."));
4519 ind = RESET_GAME;
4521 break;
4522 case M_PRE_LONG:
4523 case M_PRE_FAIL:
4524 case M_PRE_LAST:
4525 cmd_reply(CMD_RESET, caller, C_FAIL,
4526 _("The valid arguments are: 'game', 'ruleset', 'script' "
4527 "or 'default'."));
4528 return FALSE;
4529 break;
4532 if (check) {
4533 return TRUE;
4536 switch (ind) {
4537 case RESET_GAME:
4538 if (!game.info.is_new_game) {
4539 if (settings_game_reset()) {
4540 cmd_reply(CMD_RESET, caller, C_OK,
4541 _("Reset all settings to the values at the game start."));
4542 } else {
4543 cmd_reply(CMD_RESET, caller, C_FAIL,
4544 _("No saved settings from the game start available."));
4545 return FALSE;
4547 } else {
4548 cmd_reply(CMD_RESET, caller, C_FAIL, _("No game started..."));
4549 return FALSE;
4551 break;
4553 case RESET_RULESET:
4554 /* Restore game settings save in game.ruleset. */
4555 if (reload_rulesets_settings()) {
4556 cmd_reply(CMD_RESET, caller, C_OK,
4557 _("Reset all settings to ruleset values."));
4558 } else {
4559 cmd_reply(CMD_RESET, caller, C_FAIL,
4560 _("Failed to reset settings to ruleset values."));
4562 break;
4564 case RESET_SCRIPT:
4565 cmd_reply(CMD_RESET, caller, C_OK,
4566 _("Reset all settings and rereading the server start "
4567 "script."));
4568 settings_reset();
4569 /* load initial script */
4570 if (NULL != srvarg.script_filename
4571 && !read_init_script_real(NULL, srvarg.script_filename, TRUE, FALSE,
4572 read_recursion + 1)) {
4573 if (NULL != caller) {
4574 cmd_reply(CMD_RESET, caller, C_FAIL,
4575 _("Could not read script file '%s'."),
4576 srvarg.script_filename);
4578 return FALSE;
4580 break;
4582 case RESET_DEFAULT:
4583 cmd_reply(CMD_RESET, caller, C_OK,
4584 _("Reset all settings to default values."));
4585 settings_reset();
4586 break;
4589 send_server_settings(game.est_connections);
4590 cmd_reply(CMD_RESET, caller, C_OK, _("Settings re-initialized."));
4592 /* show ruleset summary and list changed values */
4593 show_ruleset_info(caller, CMD_RESET, check, read_recursion);
4595 return TRUE;
4598 /**************************************************************************
4599 Set a setting to its default value
4600 **************************************************************************/
4601 static bool default_command(struct connection *caller, char *arg, bool check)
4603 struct setting *pset;
4604 char reject_msg[256] = "";
4606 pset = validate_setting_arg(CMD_DEFAULT, caller, arg);
4608 if (!pset) {
4609 /* Reason already reported. */
4610 return FALSE;
4613 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))) {
4614 cmd_reply(CMD_DEFAULT, caller, C_FAIL, "%s", reject_msg);
4616 return FALSE;
4619 if (!check) {
4620 setting_set_to_default(pset);
4621 cmd_reply(CMD_DEFAULT, caller, C_OK,
4622 _("Option '%s' reset to default value."), arg);
4625 return TRUE;
4628 /* Define the possible arguments to the delegation command */
4629 #define SPECENUM_NAME lua_args
4630 #define SPECENUM_VALUE0 LUA_CMD
4631 #define SPECENUM_VALUE0NAME "cmd"
4632 #define SPECENUM_VALUE1 LUA_FILE
4633 #define SPECENUM_VALUE1NAME "file"
4634 #include "specenum_gen.h"
4636 /*****************************************************************************
4637 Returns possible parameters for the reset command.
4638 *****************************************************************************/
4639 static const char *lua_accessor(int i)
4641 i = CLIP(0, i, lua_args_max());
4642 return lua_args_name((enum lua_args) i);
4645 /*****************************************************************************
4646 Evaluate a line of lua script or a lua script file.
4647 *****************************************************************************/
4648 static bool lua_command(struct connection *caller, char *arg, bool check)
4650 FILE *script_file;
4651 const char extension[] = ".lua", *real_filename = NULL;
4652 char luafile[4096], tilde_filename[4096];
4653 char *tokens[1], *luaarg = NULL;
4654 int ntokens, ind;
4655 enum m_pre_result result;
4656 bool ret = FALSE;
4658 ntokens = get_tokens(arg, tokens, 1, TOKEN_DELIMITERS);
4660 if (ntokens > 0) {
4661 /* match the argument */
4662 result = match_prefix(lua_accessor, lua_args_max() + 1, 0,
4663 fc_strncasecmp, NULL, tokens[0], &ind);
4665 switch (result) {
4666 case M_PRE_EXACT:
4667 case M_PRE_ONLY:
4668 /* We have a match */
4669 luaarg = arg + strlen(lua_args_name(ind));
4670 luaarg = skip_leading_spaces(luaarg);
4671 break;
4672 case M_PRE_EMPTY:
4673 /* Nothing. */
4674 break;
4675 case M_PRE_AMBIGUOUS:
4676 case M_PRE_LONG:
4677 case M_PRE_FAIL:
4678 case M_PRE_LAST:
4679 /* Fall back to depreciated 'lua <script command>' syntax. */
4680 cmd_reply(CMD_LUA, caller, C_SYNTAX,
4681 _("Fall back to old syntax '%slua <script command>'."),
4682 caller ? "/" : "");
4683 ind = LUA_CMD;
4684 luaarg = arg;
4685 break;
4689 if (luaarg == NULL) {
4690 cmd_reply(CMD_LUA, caller, C_FAIL,
4691 _("No lua command or lua script file. See '%shelp lua'."),
4692 caller ? "/" : "");
4693 ret = TRUE;
4694 goto cleanup;
4697 switch (ind) {
4698 case LUA_CMD:
4699 /* Nothing to check. */
4700 break;
4701 case LUA_FILE:
4702 /* Abuse real_filename to find if we already have a .lua extension. */
4703 real_filename = luaarg + strlen(luaarg) - MIN(strlen(extension),
4704 strlen(luaarg));
4705 if (strcmp(real_filename, extension) != 0) {
4706 fc_snprintf(luafile, sizeof(luafile), "%s%s", luaarg, extension);
4707 } else {
4708 sz_strlcpy(luafile, luaarg);
4711 if (is_restricted(caller)) {
4712 if (!is_safe_filename(luafile)) {
4713 cmd_reply(CMD_LUA, caller, C_FAIL,
4714 _("Freeciv script '%s' disallowed for security reasons."),
4715 luafile);
4716 ret = FALSE;
4717 goto cleanup;;
4719 sz_strlcpy(tilde_filename, luafile);
4720 } else {
4721 interpret_tilde(tilde_filename, sizeof(tilde_filename), luafile);
4724 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
4725 if (!real_filename) {
4726 if (is_restricted(caller)) {
4727 cmd_reply(CMD_LUA, caller, C_FAIL,
4728 _("No Freeciv script found by the name '%s'."),
4729 tilde_filename);
4730 ret = FALSE;
4731 goto cleanup;
4733 /* File is outside data directories */
4734 real_filename = tilde_filename;
4736 break;
4739 if (check) {
4740 ret = TRUE;
4741 goto cleanup;
4744 switch (ind) {
4745 case LUA_CMD:
4746 ret = script_server_do_string(caller, luaarg);
4747 break;
4748 case LUA_FILE:
4749 cmd_reply(CMD_LUA, caller, C_COMMENT,
4750 _("Loading Freeciv script file '%s'."), real_filename);
4752 if (is_reg_file_for_access(real_filename, FALSE)
4753 && (script_file = fc_fopen(real_filename, "r"))) {
4754 ret = script_server_do_file(caller, real_filename);
4755 goto cleanup;
4756 } else {
4757 cmd_reply(CMD_LUA, caller, C_FAIL,
4758 _("Cannot read Freeciv script '%s'."), real_filename);
4759 ret = FALSE;
4760 goto cleanup;
4764 cleanup:
4765 free_tokens(tokens, ntokens);
4766 return ret;
4769 /* Define the possible arguments to the delegation command */
4770 #define SPECENUM_NAME delegate_args
4771 #define SPECENUM_VALUE0 DELEGATE_CANCEL
4772 #define SPECENUM_VALUE0NAME "cancel"
4773 #define SPECENUM_VALUE1 DELEGATE_RESTORE
4774 #define SPECENUM_VALUE1NAME "restore"
4775 #define SPECENUM_VALUE2 DELEGATE_SHOW
4776 #define SPECENUM_VALUE2NAME "show"
4777 #define SPECENUM_VALUE3 DELEGATE_TAKE
4778 #define SPECENUM_VALUE3NAME "take"
4779 #define SPECENUM_VALUE4 DELEGATE_TO
4780 #define SPECENUM_VALUE4NAME "to"
4781 #include "specenum_gen.h"
4783 /*****************************************************************************
4784 Returns possible parameters for the 'delegate' command.
4785 *****************************************************************************/
4786 static const char *delegate_accessor(int i)
4788 i = CLIP(0, i, delegate_args_max());
4789 return delegate_args_name((enum delegate_args) i);
4792 /*****************************************************************************
4793 Handle delegation of control.
4794 *****************************************************************************/
4795 static bool delegate_command(struct connection *caller, char *arg,
4796 bool check)
4798 char *tokens[3];
4799 int ntokens, ind = delegate_args_invalid();
4800 enum m_pre_result result;
4801 bool player_specified = FALSE; /* affects messages only */
4802 bool ret = FALSE;
4803 const char *username = NULL;
4804 struct player *dplayer = NULL;
4806 if (!game_was_started()) {
4807 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Game not started - "
4808 "cannot delegate yet."));
4809 return FALSE;
4812 ntokens = get_tokens(arg, tokens, 3, TOKEN_DELIMITERS);
4814 if (ntokens > 0) {
4815 /* match the argument */
4816 result = match_prefix(delegate_accessor, delegate_args_max() + 1, 0,
4817 fc_strncasecmp, NULL, tokens[0], &ind);
4819 switch (result) {
4820 case M_PRE_EXACT:
4821 case M_PRE_ONLY:
4822 /* we have a match */
4823 break;
4824 case M_PRE_EMPTY:
4825 if (caller) {
4826 /* Use 'delegate show' as default. */
4827 ind = DELEGATE_SHOW;
4829 break;
4830 case M_PRE_AMBIGUOUS:
4831 case M_PRE_LONG:
4832 case M_PRE_FAIL:
4833 case M_PRE_LAST:
4834 ind = delegate_args_invalid();
4835 break;
4837 } else {
4838 if (caller) {
4839 /* Use 'delegate show' as default. */
4840 ind = DELEGATE_SHOW;
4844 if (!delegate_args_is_valid(ind)) {
4845 char buf[256] = "";
4846 enum delegate_args valid_args;
4848 for (valid_args = delegate_args_begin();
4849 valid_args != delegate_args_end();
4850 valid_args = delegate_args_next(valid_args)) {
4851 cat_snprintf(buf, sizeof(buf), "'%s'",
4852 delegate_args_name(valid_args));
4853 if (valid_args != delegate_args_max()) {
4854 cat_snprintf(buf, sizeof(buf), ", ");
4858 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4859 /* TRANS: do not translate the command 'delegate'. */
4860 _("Valid arguments for 'delegate' are: %s."), buf);
4861 ret = FALSE;
4862 goto cleanup;
4865 /* Get the data (player, username for delegation) and validate it. */
4866 switch (ind) {
4867 case DELEGATE_CANCEL:
4868 /* delegate cancel [player] */
4869 if (ntokens > 1) {
4870 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4871 player_specified = TRUE;
4872 dplayer = player_by_name_prefix(tokens[1], &result);
4873 if (!dplayer) {
4874 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4875 ret = FALSE;
4876 goto cleanup;
4878 } else {
4879 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4880 _("Command level '%s' or greater needed to modify "
4881 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4882 ret = FALSE;
4883 goto cleanup;
4885 } else {
4886 dplayer = conn_get_player(caller);
4887 if (!dplayer) {
4888 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4889 _("Please specify a player for whom delegation should "
4890 "be canceled."));
4891 ret = FALSE;
4892 goto cleanup;
4895 break;
4896 case DELEGATE_RESTORE:
4897 /* delegate restore */
4898 if (!caller) {
4899 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4900 _("You can't switch players from the console."));
4901 ret = FALSE;
4902 goto cleanup;
4904 break;
4905 case DELEGATE_SHOW:
4906 /* delegate show [player] */
4907 if (ntokens > 1) {
4908 player_specified = TRUE;
4909 dplayer = player_by_name_prefix(tokens[1], &result);
4910 if (!dplayer) {
4911 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4912 ret = FALSE;
4913 goto cleanup;
4915 } else {
4916 dplayer = conn_get_player(caller);
4917 if (!dplayer) {
4918 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4919 _("Please specify a player for whom the delegation should "
4920 "be shown."));
4921 ret = FALSE;
4922 goto cleanup;
4925 break;
4926 case DELEGATE_TAKE:
4927 /* delegate take <player> */
4928 if (!caller) {
4929 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4930 _("You can't switch players from the console."));
4931 ret = FALSE;
4932 goto cleanup;
4934 if (ntokens > 1) {
4935 player_specified = TRUE;
4936 dplayer = player_by_name_prefix(tokens[1], &result);
4937 if (!dplayer) {
4938 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4939 ret = FALSE;
4940 goto cleanup;
4942 } else {
4943 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4944 _("Please specify a player to take control of."));
4945 ret = FALSE;
4946 goto cleanup;
4948 break;
4949 case DELEGATE_TO:
4950 /* delegate to <username> [player] */
4951 if (ntokens > 1) {
4952 username = tokens[1];
4953 } else {
4954 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4955 _("Please specify a user to whom control is to be delegated."));
4956 ret = FALSE;
4957 goto cleanup;
4959 if (ntokens > 2) {
4960 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4961 player_specified = TRUE;
4962 dplayer = player_by_name_prefix(tokens[2], &result);
4963 if (!dplayer) {
4964 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[2], result);
4965 ret = FALSE;
4966 goto cleanup;
4968 } else {
4969 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4970 _("Command level '%s' or greater needed to modify "
4971 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4972 ret = FALSE;
4973 goto cleanup;
4975 } else {
4976 dplayer = conn_controls_player(caller) ? conn_get_player(caller) : NULL;
4977 if (!dplayer) {
4978 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4979 _("You do not control a player."));
4980 ret = FALSE;
4981 goto cleanup;
4984 break;
4987 /* All checks done to this point will give pretty much the same result at
4988 * any time. Checks after this point are more likely to vary over time. */
4989 if (check) {
4990 ret = TRUE;
4991 goto cleanup;
4994 switch (ind) {
4995 case DELEGATE_TO:
4996 /* Delegate control of player to another user. */
4997 fc_assert_ret_val(dplayer, FALSE);
4998 fc_assert_ret_val(username != NULL, FALSE);
5000 /* Forbid delegation of players already controlled by a delegate, and
5001 * those 'put aside' by a delegate.
5002 * For the former, if player is already under active delegate control,
5003 * we wouldn't handle the revocation that would be necessary if their
5004 * delegation changed; and the authority granted to delegates does not
5005 * include the ability to sub-delegate.
5006 * For the latter, allowing control of the 'put aside' player to be
5007 * delegated would break the invariant that whenever a user is connected,
5008 * they are attached to 'their' player. */
5009 if (player_delegation_active(dplayer)) {
5010 if (!player_delegation_get(dplayer)) {
5011 /* Attempting to change a 'put aside' player. Must be admin
5012 * or console. */
5013 fc_assert(player_specified);
5014 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5015 _("Can't delegate control of '%s' belonging to %s while "
5016 "they are controlling another player."),
5017 player_name(dplayer), dplayer->username);
5018 } else if (player_specified) {
5019 /* Admin or console attempting to change a controlled player. */
5020 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5021 _("Can't change delegation of '%s' while controlled by "
5022 "delegate %s."), player_name(dplayer), dplayer->username);
5023 } else {
5024 /* Caller must be the delegate. Give more specific message.
5025 * (We don't know if they thought they were delegating their
5026 * original or delegated player, but we don't allow either.) */
5027 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5028 _("You can't delegate control while you are controlling "
5029 "a delegated player yourself."));
5031 ret = FALSE;
5032 goto cleanup;
5035 /* Forbid delegation to player's original owner
5036 * (from above test we know that dplayer->username is the original now) */
5037 if (fc_strcasecmp(dplayer->username, username) == 0) {
5038 if (player_specified) {
5039 /* Probably admin or console. */
5040 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5041 /* TRANS: don't translate 'delegate cancel' */
5042 _("%s already owns '%s', so cannot also be delegate. "
5043 "Use '%sdelegate cancel' to cancel an existing "
5044 "delegation."),
5045 username, player_name(dplayer), caller?"/":"");
5046 } else {
5047 /* Player not specified on command line, so they must have been trying
5048 * to delegate control to themself. Give more specific message. */
5049 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5050 /* TRANS: don't translate '/delegate cancel' */
5051 _("You can't delegate control to yourself. "
5052 "Use '/delegate cancel' to cancel an existing "
5053 "delegation."));
5055 ret = FALSE;
5056 goto cleanup;
5059 /* FIXME: if control was already delegated to someone else, that
5060 * delegation is implicitly canceled. Perhaps we should tell someone. */
5062 player_delegation_set(dplayer, username);
5063 cmd_reply(CMD_DELEGATE, caller, C_OK,
5064 _("Control of player '%s' delegated to user %s."),
5065 player_name(dplayer), username);
5066 ret = TRUE;
5067 goto cleanup;
5068 break;
5070 case DELEGATE_SHOW:
5071 /* Show delegations. */
5072 fc_assert_ret_val(dplayer, FALSE);
5074 if (player_delegation_get(dplayer) == NULL) {
5075 /* No delegation set. */
5076 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5077 _("No delegation defined for '%s'."),
5078 player_name(dplayer));
5079 } else {
5080 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5081 _("Control of player '%s' delegated to user %s."),
5082 player_name(dplayer), player_delegation_get(dplayer));
5084 ret = TRUE;
5085 goto cleanup;
5086 break;
5088 case DELEGATE_CANCEL:
5089 if (player_delegation_get(dplayer) == NULL) {
5090 /* No delegation set. */
5091 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5092 _("No delegation defined for '%s'."),
5093 player_name(dplayer));
5094 ret = FALSE;
5095 goto cleanup;
5098 if (player_delegation_active(dplayer)) {
5099 /* Delegation is currently in use. Forcibly break connection. */
5100 struct connection *pdelegate;
5101 /* (Can only happen if admin/console issues this command, as owner
5102 * will end use by their mere presence.) */
5103 fc_assert(player_specified);
5104 pdelegate = conn_by_user(player_delegation_get(dplayer));
5105 fc_assert_ret_val(pdelegate != NULL, FALSE);
5106 if (!connection_delegate_restore(pdelegate)) {
5107 /* Should never happen. Generic failure message. */
5108 log_error("Failed to restore %s's connection as %s during "
5109 "'delegate cancel'.", pdelegate->username,
5110 delegate_player_str(pdelegate->server.delegation.playing,
5111 pdelegate->server.delegation.observer));
5112 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5113 ret = FALSE;
5114 goto cleanup;
5116 notify_conn(pdelegate->self, NULL, E_CONNECTION, ftc_server,
5117 _("Your delegated control of player '%s' was canceled."),
5118 player_name(dplayer));
5121 player_delegation_set(dplayer, NULL);
5122 cmd_reply(CMD_DELEGATE, caller, C_OK, _("Delegation of '%s' canceled."),
5123 player_name(dplayer));
5124 ret = TRUE;
5125 goto cleanup;
5126 break;
5128 case DELEGATE_TAKE:
5129 /* Try to take another player. */
5130 fc_assert_ret_val(dplayer, FALSE);
5131 fc_assert_ret_val(caller, FALSE);
5133 if (caller->server.delegation.status) {
5134 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5135 /* TRANS: don't translate '/delegate restore'. */
5136 _("You are already controlling a delegated player. "
5137 "Use '/delegate restore' to relinquish control of your "
5138 "current player first."));
5139 ret = FALSE;
5140 goto cleanup;
5143 /* Don't allow 'put aside' players to be delegated; the invariant is
5144 * that while the owning user is connected to the server, they are
5145 * in sole control of 'their' player. */
5146 if (conn_controls_player(caller)
5147 && player_delegation_get(conn_get_player(caller)) != NULL) {
5148 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5149 /* TRANS: don't translate '/delegate cancel'. */
5150 _("Can't take player while you have delegated control "
5151 "yourself. Use '/delegate cancel' to cancel your own "
5152 "delegation first."));
5153 ret = FALSE;
5154 goto cleanup;
5157 /* Taking your own player makes no sense. */
5158 if (conn_controls_player(caller)
5159 && dplayer == conn_get_player(caller)) {
5160 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("You already control '%s'."),
5161 player_name(conn_get_player(caller)));
5162 ret = FALSE;
5163 goto cleanup;
5166 if (!player_delegation_get(dplayer)
5167 || fc_strcasecmp(player_delegation_get(dplayer), caller->username) != 0) {
5168 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5169 _("Control of player '%s' has not been delegated to you."),
5170 player_name(dplayer));
5171 ret = FALSE;
5172 goto cleanup;
5175 /* If the player is controlled by another user, fail. */
5176 if (dplayer->is_connected) {
5177 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5178 _("Another user already controls player '%s'."),
5179 player_name(dplayer));
5180 ret = FALSE;
5181 goto cleanup;
5184 if (!connection_delegate_take(caller, dplayer)) {
5185 /* Should never happen. Generic failure message. */
5186 log_error("%s failed to take control of '%s' during 'delegate take'.",
5187 caller->username, player_name(dplayer));
5188 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5189 ret = FALSE;
5190 goto cleanup;
5193 cmd_reply(CMD_DELEGATE, caller, C_OK,
5194 _("%s is now controlling player '%s'."), caller->username,
5195 player_name(conn_get_player(caller)));
5196 ret = TRUE;
5197 goto cleanup;
5198 break;
5200 case DELEGATE_RESTORE:
5201 /* Delegate user relinquishes control of delegated player, returning to
5202 * previous view (e.g. observer) if any. */
5203 fc_assert_ret_val(caller, FALSE);
5205 if (!caller->server.delegation.status) {
5206 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5207 _("You are not currently controlling a delegated player."));
5208 ret = FALSE;
5209 goto cleanup;
5212 if (!connection_delegate_restore(caller)) {
5213 /* Should never happen. Generic failure message. */
5214 log_error("Failed to restore %s's connection as %s during "
5215 "'delegate restore'.", caller->username,
5216 delegate_player_str(caller->server.delegation.playing,
5217 caller->server.delegation.observer));
5218 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5219 ret = FALSE;
5220 goto cleanup;
5223 cmd_reply(CMD_DELEGATE, caller, C_OK,
5224 /* TRANS: "<user> is now connected to <player>" where <player>
5225 * can also be "global observer" or "nothing" */
5226 _("%s is now connected as %s."), caller->username,
5227 delegate_player_str(conn_get_player(caller), caller->observer));
5228 ret = TRUE;
5229 goto cleanup;
5230 break;
5233 cleanup:
5234 free_tokens(tokens, ntokens);
5235 return ret;
5238 /*****************************************************************************
5239 Return static string describing what a connection is connected to.
5240 *****************************************************************************/
5241 static const char *delegate_player_str(struct player *pplayer, bool observer)
5243 static struct astring buf;
5245 if (pplayer) {
5246 if (observer) {
5247 astr_set(&buf, _("%s (observer)"), player_name(pplayer));
5248 } else {
5249 astr_set(&buf, "%s", player_name(pplayer));
5251 } else if (observer) {
5252 astr_set(&buf, "%s", _("global observer"));
5253 } else {
5254 /* TRANS: in place of player name or "global observer" */
5255 astr_set(&buf, "%s", _("nothing"));
5258 return astr_str(&buf);
5261 /* Define the possible arguments to the mapimg command */
5262 /* map image layers */
5263 #define SPECENUM_NAME mapimg_args
5264 #define SPECENUM_VALUE0 MAPIMG_COLORTEST
5265 #define SPECENUM_VALUE0NAME "colortest"
5266 #define SPECENUM_VALUE1 MAPIMG_CREATE
5267 #define SPECENUM_VALUE1NAME "create"
5268 #define SPECENUM_VALUE2 MAPIMG_DEFINE
5269 #define SPECENUM_VALUE2NAME "define"
5270 #define SPECENUM_VALUE3 MAPIMG_DELETE
5271 #define SPECENUM_VALUE3NAME "delete"
5272 #define SPECENUM_VALUE4 MAPIMG_SHOW
5273 #define SPECENUM_VALUE4NAME "show"
5274 #define SPECENUM_COUNT MAPIMG_COUNT
5275 #include "specenum_gen.h"
5277 /**************************************************************************
5278 Returns possible parameters for the mapimg command.
5279 **************************************************************************/
5280 static const char *mapimg_accessor(int i)
5282 i = CLIP(0, i, mapimg_args_max());
5283 return mapimg_args_name((enum mapimg_args) i);
5286 /**************************************************************************
5287 Handle mapimg command
5288 **************************************************************************/
5289 static bool mapimg_command(struct connection *caller, char *arg, bool check)
5291 enum m_pre_result result;
5292 int ind, ntokens, id;
5293 char *token[2];
5294 bool ret = TRUE;
5296 ntokens = get_tokens(arg, token, 2, TOKEN_DELIMITERS);
5298 if (ntokens > 0) {
5299 /* match the argument */
5300 result = match_prefix(mapimg_accessor, MAPIMG_COUNT, 0,
5301 fc_strncasecmp, NULL, token[0], &ind);
5303 switch (result) {
5304 case M_PRE_EXACT:
5305 case M_PRE_ONLY:
5306 /* we have a match */
5307 break;
5308 case M_PRE_AMBIGUOUS:
5309 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5310 _("Ambiguous 'mapimg' command."));
5311 ret = FALSE;
5312 goto cleanup;
5313 break;
5314 case M_PRE_EMPTY:
5315 /* use 'show' as default */
5316 ind = MAPIMG_SHOW;
5317 break;
5318 case M_PRE_LONG:
5319 case M_PRE_FAIL:
5320 case M_PRE_LAST:
5322 char buf[256] = "";
5323 enum mapimg_args valid_args;
5325 for (valid_args = mapimg_args_begin();
5326 valid_args != mapimg_args_end();
5327 valid_args = mapimg_args_next(valid_args)) {
5328 cat_snprintf(buf, sizeof(buf), "'%s'",
5329 mapimg_args_name(valid_args));
5330 if (valid_args != mapimg_args_max()) {
5331 cat_snprintf(buf, sizeof(buf), ", ");
5335 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5336 _("The valid arguments are: %s."), buf);
5337 ret = FALSE;
5338 goto cleanup;
5340 break;
5342 } else {
5343 /* use 'show' as default */
5344 ind = MAPIMG_SHOW;
5347 switch (ind) {
5348 case MAPIMG_DEFINE:
5349 if (ntokens == 1) {
5350 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5351 _("Missing argument for 'mapimg define'."));
5352 ret = FALSE;
5353 } else {
5354 /* 'mapimg define <mapstr>' */
5355 if (!mapimg_define(token[1], check)) {
5356 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5357 _("Can't use definition: %s."), mapimg_error());
5358 ret = FALSE;
5359 } else if (check) {
5360 /* Validated OK, bail out now */
5361 goto cleanup;
5362 } else if (game_was_started()
5363 && mapimg_isvalid(mapimg_count() - 1) == NULL) {
5364 /* game was started - error in map image definition check */
5365 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5366 _("Can't use definition: %s."), mapimg_error());
5367 ret = FALSE;
5368 } else {
5369 char str[MAX_LEN_MAPDEF];
5371 id = mapimg_count() - 1;
5373 mapimg_id2str(id, str, sizeof(str));
5374 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Defined as map image "
5375 "definition %d: '%s'."),
5376 id, str);
5379 break;
5381 case MAPIMG_DELETE:
5382 if (ntokens == 1) {
5383 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5384 _("Missing argument for 'mapimg delete'."));
5385 ret = FALSE;
5386 } else if (ntokens == 2 && strcmp(token[1], "all") == 0) {
5387 /* 'mapimg delete all' */
5388 if (check) {
5389 goto cleanup;
5392 while (mapimg_count() > 0) {
5393 mapimg_delete(0);
5395 cmd_reply(CMD_MAPIMG, caller, C_OK, _("All map image definitions "
5396 "deleted."));
5397 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5398 /* 'mapimg delete <id>' */
5399 if (check) {
5400 goto cleanup;
5403 if (!mapimg_delete(id)) {
5404 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5405 _("Couldn't delete definition: %s."), mapimg_error());
5406 ret = FALSE;
5407 } else {
5408 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map image definition %d "
5409 "deleted."), id);
5411 } else {
5412 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5413 _("Bad argument for 'mapimg delete': '%s'."), token[1]);
5414 ret = FALSE;
5416 break;
5418 case MAPIMG_SHOW:
5419 if (ntokens < 2 || (ntokens == 2 && strcmp(token[1], "all") == 0)) {
5420 /* 'mapimg show' or 'mapimg show all' */
5421 if (check) {
5422 goto cleanup;
5424 show_mapimg(caller, CMD_MAPIMG);
5425 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5426 char str[2048];
5427 /* 'mapimg show <id>' */
5428 if (check) {
5429 goto cleanup;
5432 if (mapimg_show(id, str, sizeof(str), TRUE)) {
5433 cmd_reply(CMD_MAPIMG, caller, C_OK, "%s", str);
5434 } else {
5435 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5436 _("Couldn't show definition: %s."), mapimg_error());
5437 ret = FALSE;
5439 } else {
5440 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5441 _("Bad argument for 'mapimg show': '%s'."), token[1]);
5442 ret = FALSE;
5444 break;
5446 case MAPIMG_COLORTEST:
5447 if (check) {
5448 goto cleanup;
5451 mapimg_colortest(game.server.save_name, NULL);
5452 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map color test images saved."));
5453 break;
5455 case MAPIMG_CREATE:
5456 if (ntokens < 2) {
5457 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5458 _("Missing argument for 'mapimg create'."));
5459 ret = FALSE;
5460 goto cleanup;
5463 if (strcmp(token[1], "all") == 0) {
5464 /* 'mapimg create all' */
5465 if (check) {
5466 goto cleanup;
5469 for (id = 0; id < mapimg_count(); id++) {
5470 struct mapdef *pmapdef = mapimg_isvalid(id);
5472 if (pmapdef == NULL
5473 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5474 srvarg.saves_pathname)) {
5475 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5476 _("Error saving map image %d: %s."), id, mapimg_error());
5477 ret = FALSE;
5480 } else if (sscanf(token[1], "%d", &id) != 0) {
5481 struct mapdef *pmapdef;
5483 /* 'mapimg create <id>' */
5484 if (check) {
5485 goto cleanup;
5488 pmapdef = mapimg_isvalid(id);
5489 if (pmapdef == NULL
5490 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5491 srvarg.saves_pathname)) {
5492 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5493 _("Error saving map image %d: %s."), id, mapimg_error());
5494 ret = FALSE;
5496 } else {
5497 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5498 _("Bad argument for 'mapimg create': '%s'."), token[1]);
5499 ret = FALSE;
5501 break;
5504 cleanup:
5506 free_tokens(token, ntokens);
5508 return ret;
5511 /* Define the possible arguments to the fcdb command */
5512 #define SPECENUM_NAME fcdb_args
5513 #define SPECENUM_VALUE0 FCDB_RELOAD
5514 #define SPECENUM_VALUE0NAME "reload"
5515 #define SPECENUM_VALUE1 FCDB_LUA
5516 #define SPECENUM_VALUE1NAME "lua"
5517 #define SPECENUM_COUNT FCDB_COUNT
5518 #include "specenum_gen.h"
5520 /**************************************************************************
5521 Returns possible parameters for the fcdb command.
5522 **************************************************************************/
5523 static const char *fcdb_accessor(int i)
5525 i = CLIP(0, i, fcdb_args_max());
5526 return fcdb_args_name((enum fcdb_args) i);
5529 /**************************************************************************
5530 Handle the freeciv database script module.
5531 **************************************************************************/
5532 static bool fcdb_command(struct connection *caller, char *arg, bool check)
5534 enum m_pre_result result;
5535 int ind, ntokens;
5536 char *token[1];
5537 bool ret = TRUE;
5538 bool usage = FALSE;
5540 #ifndef HAVE_FCDB
5541 cmd_reply(CMD_FCDB, caller, C_FAIL,
5542 _("Freeciv database script deactivated at compile time."));
5543 return FALSE;
5544 #endif
5546 ntokens = get_tokens(arg, token, 1, TOKEN_DELIMITERS);
5548 if (ntokens > 0) {
5549 /* match the argument */
5550 result = match_prefix(fcdb_accessor, FCDB_COUNT, 0,
5551 fc_strncasecmp, NULL, token[0], &ind);
5553 switch (result) {
5554 case M_PRE_EXACT:
5555 case M_PRE_ONLY:
5556 /* we have a match */
5557 break;
5558 case M_PRE_AMBIGUOUS:
5559 cmd_reply(CMD_FCDB, caller, C_FAIL,
5560 _("Ambiguous fcdb command."));
5561 ret = FALSE;
5562 goto cleanup;
5563 break;
5564 case M_PRE_EMPTY:
5565 case M_PRE_LONG:
5566 case M_PRE_FAIL:
5567 case M_PRE_LAST:
5568 usage = TRUE;
5569 break;
5571 } else {
5572 usage = TRUE;
5575 if (usage) {
5576 char buf[256] = "";
5577 enum fcdb_args valid_args;
5579 for (valid_args = fcdb_args_begin();
5580 valid_args != fcdb_args_end();
5581 valid_args = fcdb_args_next(valid_args)) {
5582 cat_snprintf(buf, sizeof(buf), "'%s'",
5583 fcdb_args_name(valid_args));
5584 if (valid_args != fcdb_args_max()) {
5585 cat_snprintf(buf, sizeof(buf), ", ");
5589 cmd_reply(CMD_FCDB, caller, C_FAIL,
5590 _("The valid arguments are: %s."), buf);
5591 ret = FALSE;
5592 goto cleanup;
5595 if (check) {
5596 ret = TRUE;
5597 goto cleanup;
5600 switch (ind) {
5601 case FCDB_RELOAD:
5602 /* Reload database lua script. */
5603 script_fcdb_free();
5604 script_fcdb_init(NULL);
5605 break;
5607 case FCDB_LUA:
5608 /* Skip whitespaces. */
5609 arg = skip_leading_spaces(arg);
5610 /* Skip the base argument 'lua'. */
5611 arg += 3;
5612 /* Now execute the scriptlet. */
5613 ret = script_fcdb_do_string(caller, arg);
5614 break;
5617 cleanup:
5619 free_tokens(token, ntokens);
5621 return ret;
5624 /**************************************************************************
5625 Send start command related message
5626 **************************************************************************/
5627 static void start_cmd_reply(struct connection *caller, bool notify, char *msg)
5629 cmd_reply(CMD_START_GAME, caller, C_FAIL, "%s", msg);
5630 if (notify) {
5631 notify_conn(NULL, NULL, E_SETTING, ftc_server, "%s", msg);
5635 /**************************************************************************
5636 Handle start command. Notify all players about errors if notify set.
5637 **************************************************************************/
5638 bool start_command(struct connection *caller, bool check, bool notify)
5640 int human_players;
5642 switch (server_state()) {
5643 case S_S_INITIAL:
5644 /* Sanity check scenario */
5645 if (game.info.is_new_game && !check) {
5646 if (0 < map_startpos_count()
5647 && game.server.max_players > map_startpos_count()) {
5648 /* If we load a pre-generated map (i.e., a scenario) it is possible
5649 * to increase the number of players beyond the number supported by
5650 * the scenario. The solution is a hack: cut the extra players
5651 * when the game starts. */
5652 log_verbose("Reduced maxplayers from %d to %d to fit "
5653 "to the number of start positions.",
5654 game.server.max_players, map_startpos_count());
5655 game.server.max_players = map_startpos_count();
5658 if (normal_player_count() > game.server.max_players) {
5659 int i;
5660 struct player *pplayer;
5662 for (i = player_slot_count() - 1; i >= 0; i--) {
5663 pplayer = player_by_number(i);
5664 if (pplayer) {
5665 server_remove_player(pplayer);
5667 if (normal_player_count() <= game.server.max_players) {
5668 break;
5672 log_verbose("Had to cut down the number of players to the "
5673 "number of map start positions, there must be "
5674 "something wrong with the savegame or you "
5675 "adjusted the maxplayers value.");
5679 human_players = 0;
5680 players_iterate(plr) {
5681 if (!plr->ai_controlled) {
5682 human_players++;
5684 } players_iterate_end;
5686 /* check min_players.
5687 * Allow continuing of savegames where some of the original
5688 * players have died */
5689 if (game.info.is_new_game
5690 && human_players < game.server.min_players) {
5691 char buf[512] = "";
5693 fc_snprintf(buf, sizeof(buf),
5694 _("Not enough human players ('minplayers' server setting has value %d); game will not start."),
5695 game.server.min_players);
5696 start_cmd_reply(caller, notify, buf);
5697 return FALSE;
5698 } else if (player_count() < 1) {
5699 /* At least one player required */
5700 start_cmd_reply(caller, notify,
5701 _("No players; game will not start."));
5702 return FALSE;
5703 } else if (normal_player_count() > server.playable_nations) {
5704 if (nation_set_count() > 1) {
5705 start_cmd_reply(caller, notify,
5706 _("Not enough nations in the current nation set "
5707 "for all players; game will not start. "
5708 "(See 'nationset' setting.)"));
5709 } else {
5710 start_cmd_reply(caller, notify,
5711 _("Not enough nations for all players; game will "
5712 "not start."));
5714 return FALSE;
5715 } else if (strlen(game.server.start_units) == 0 && !game.server.start_city) {
5716 start_cmd_reply(caller, notify,
5717 _("Neither 'startcity' nor 'startunits' setting gives "
5718 "players anything to start game with; game will "
5719 "not start."));
5720 return FALSE;
5721 } else if (check) {
5722 return TRUE;
5723 } else if (!caller) {
5724 if (notify) {
5725 /* Called from handle_player_ready()
5726 * Last player just toggled ready-status. */
5727 notify_conn(NULL, NULL, E_SETTING, ftc_game_start,
5728 _("All players are ready; starting game."));
5730 start_game();
5731 return TRUE;
5732 } else if (NULL == caller->playing || caller->observer) {
5733 /* A detached or observer player can't do /start. */
5734 return TRUE;
5735 } else {
5736 /* This might trigger recursive call to start_command() if this is
5737 * last player who gets ready. In that case caller is NULL. */
5738 handle_player_ready(caller->playing, player_number(caller->playing), TRUE);
5739 return TRUE;
5741 case S_S_OVER:
5742 start_cmd_reply(caller, notify,
5743 /* TRANS: given when /start is invoked during gameover. */
5744 _("Cannot start the game: the game is waiting for all clients "
5745 "to disconnect."));
5746 return FALSE;
5747 case S_S_RUNNING:
5748 start_cmd_reply(caller, notify,
5749 /* TRANS: given when /start is invoked while the game
5750 * is running. */
5751 _("Cannot start the game: it is already running."));
5752 return FALSE;
5754 log_error("Unknown server state variant: %d.", server_state());
5755 return FALSE;
5758 /**************************************************************************
5759 Handle cut command
5760 **************************************************************************/
5761 static bool cut_client_connection(struct connection *caller, char *name,
5762 bool check)
5764 enum m_pre_result match_result;
5765 struct connection *ptarget;
5767 ptarget = conn_by_user_prefix(name, &match_result);
5769 if (!ptarget) {
5770 cmd_reply_no_such_conn(CMD_CUT, caller, name, match_result);
5771 return FALSE;
5772 } else if (check) {
5773 return TRUE;
5776 if (conn_controls_player(ptarget)) {
5777 /* If we cut the connection, unassign the login name.*/
5778 sz_strlcpy(ptarget->playing->username, _(ANON_USER_NAME));
5779 ptarget->playing->unassigned_user = TRUE;
5782 cmd_reply(CMD_CUT, caller, C_DISCONNECTED,
5783 _("Cutting connection %s."), ptarget->username);
5784 connection_close_server(ptarget, _("connection cut"));
5786 return TRUE;
5790 /****************************************************************************
5791 Utility for 'kick_hash' tables.
5792 ****************************************************************************/
5793 static time_t *time_duplicate(const time_t *t)
5795 time_t *d = fc_malloc(sizeof(*d));
5796 *d = *t;
5797 return d;
5800 /****************************************************************************
5801 Returns FALSE if the connection isn't kicked and can connect the server
5802 normally.
5803 ****************************************************************************/
5804 bool conn_is_kicked(struct connection *pconn, int *time_remaining)
5806 time_t time_of_addr_kick, time_of_user_kick;
5807 time_t now, time_of_kick = 0;
5809 if (NULL != time_remaining) {
5810 *time_remaining = 0;
5813 fc_assert_ret_val(NULL != kick_table_by_addr, FALSE);
5814 fc_assert_ret_val(NULL != kick_table_by_user, FALSE);
5815 fc_assert_ret_val(NULL != pconn, FALSE);
5817 if (kick_hash_lookup(kick_table_by_addr, pconn->server.ipaddr,
5818 &time_of_addr_kick)) {
5819 time_of_kick = time_of_addr_kick;
5821 if (kick_hash_lookup(kick_table_by_user, pconn->username,
5822 &time_of_user_kick)
5823 && time_of_user_kick > time_of_kick) {
5824 time_of_kick = time_of_user_kick;
5827 if (0 == time_of_kick) {
5828 return FALSE; /* Not found. */
5831 now = time(NULL);
5832 if (now - time_of_kick > game.server.kick_time) {
5833 /* Kick timeout expired. */
5834 if (0 != time_of_addr_kick) {
5835 kick_hash_remove(kick_table_by_addr, pconn->server.ipaddr);
5837 if (0 != time_of_user_kick) {
5838 kick_hash_remove(kick_table_by_user, pconn->username);
5840 return FALSE;
5843 if (NULL != time_remaining) {
5844 *time_remaining = game.server.kick_time - (now - time_of_kick);
5846 return TRUE;
5849 /****************************************************************************
5850 Kick command handler.
5851 ****************************************************************************/
5852 static bool kick_command(struct connection *caller, char *name, bool check)
5854 char ipaddr[FC_MEMBER_SIZEOF(struct connection, server.ipaddr)];
5855 struct connection *pconn;
5856 enum m_pre_result match_result;
5857 time_t now;
5859 remove_leading_trailing_spaces(name);
5860 pconn = conn_by_user_prefix(name, &match_result);
5861 if (NULL == pconn) {
5862 cmd_reply_no_such_conn(CMD_KICK, caller, name, match_result);
5863 return FALSE;
5866 if (NULL != caller && ALLOW_ADMIN > conn_get_access(caller)) {
5867 const int MIN_UNIQUE_CONNS = 3;
5868 const char *unique_ipaddr[MIN_UNIQUE_CONNS];
5869 int i, num_unique_connections = 0;
5871 if (pconn == caller) {
5872 cmd_reply(CMD_KICK, caller, C_FAIL, _("You may not kick yourself."));
5873 return FALSE;
5876 conn_list_iterate(game.est_connections, aconn) {
5877 for (i = 0; i < num_unique_connections; i++) {
5878 if (0 == strcmp(unique_ipaddr[i], aconn->server.ipaddr)) {
5879 /* Already listed. */
5880 break;
5883 if (i >= num_unique_connections) {
5884 num_unique_connections++;
5885 if (MIN_UNIQUE_CONNS <= num_unique_connections) {
5886 /* We have enought already. */
5887 break;
5889 unique_ipaddr[num_unique_connections - 1] = aconn->server.ipaddr;
5891 } conn_list_iterate_end;
5893 if (MIN_UNIQUE_CONNS > num_unique_connections) {
5894 cmd_reply(CMD_KICK, caller, C_FAIL,
5895 _("There must be at least %d unique connections to the "
5896 "server for this command to be valid."), MIN_UNIQUE_CONNS);
5897 return FALSE;
5901 if (check) {
5902 return TRUE;
5905 sz_strlcpy(ipaddr, pconn->server.ipaddr);
5906 now = time(NULL);
5907 kick_hash_replace(kick_table_by_addr, ipaddr, now);
5909 conn_list_iterate(game.all_connections, aconn) {
5910 if (0 != strcmp(ipaddr, aconn->server.ipaddr)) {
5911 continue;
5914 if (conn_controls_player(aconn)) {
5915 /* Unassign the username. */
5916 sz_strlcpy(aconn->playing->username, _(ANON_USER_NAME));
5917 aconn->playing->unassigned_user = TRUE;
5920 kick_hash_replace(kick_table_by_user, aconn->username, now);
5922 connection_close_server(aconn, _("kicked"));
5923 } conn_list_iterate_end;
5925 return TRUE;
5929 /**************************************************************************
5930 Show caller introductory help about the server. help_cmd is the command
5931 the player used.
5932 **************************************************************************/
5933 static void show_help_intro(struct connection *caller,
5934 enum command_id help_cmd)
5936 /* This is formated like extra_help entries for settings and commands: */
5937 char *help = fc_strdup(
5938 _("Welcome - this is the introductory help text for the Freeciv "
5939 "server.\n"
5940 "\n"
5941 "Two important server concepts are Commands and Options. Commands, "
5942 "such as 'help', are used to interact with the server. Some commands "
5943 "take one or more arguments, separated by spaces. In many cases "
5944 "commands and command arguments may be abbreviated. Options are "
5945 "settings which control the server as it is running.\n"
5946 "\n"
5947 "To find out how to get more information about commands and options, "
5948 "use 'help help'.\n"
5949 "\n"
5950 "For the impatient, the main commands to get going are:\n"
5951 " show - to see current options\n"
5952 " set - to set options\n"
5953 " start - to start the game once players have connected\n"
5954 " save - to save the current game\n"
5955 " quit - to exit"));
5957 fc_break_lines(help, LINE_BREAK);
5958 cmd_reply(help_cmd, caller, C_COMMENT, "%s", help);
5959 FC_FREE(help);
5962 /**************************************************************************
5963 Show the caller detailed help for the single COMMAND given by id.
5964 help_cmd is the command the player used.
5965 **************************************************************************/
5966 static void show_help_command(struct connection *caller,
5967 enum command_id help_cmd,
5968 enum command_id id)
5970 const struct command *cmd = command_by_number(id);
5972 if (command_short_help(cmd)) {
5973 cmd_reply(help_cmd, caller, C_COMMENT,
5974 /* TRANS: <untranslated name> - translated short help */
5975 _("Command: %s - %s"),
5976 command_name(cmd),
5977 command_short_help(cmd));
5978 } else {
5979 cmd_reply(help_cmd, caller, C_COMMENT,
5980 /* TRANS: <untranslated name> */
5981 _("Command: %s"),
5982 command_name(cmd));
5984 if (command_synopsis(cmd)) {
5985 /* line up the synopsis lines: */
5986 const char *syn = _("Synopsis: ");
5987 size_t synlen = strlen(syn);
5988 char prefix[40];
5990 fc_snprintf(prefix, sizeof(prefix), "%*s", (int) synlen, " ");
5991 cmd_reply_prefix(help_cmd, caller, C_COMMENT, prefix,
5992 "%s%s", syn, command_synopsis(cmd));
5994 cmd_reply(help_cmd, caller, C_COMMENT,
5995 _("Level: %s"), cmdlevel_name(command_level(cmd)));
5997 char *help = command_extra_help(cmd);
5999 if (help) {
6000 fc_break_lines(help, LINE_BREAK);
6001 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
6002 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
6003 FC_FREE(help);
6008 /**************************************************************************
6009 Show the caller list of COMMANDS.
6010 help_cmd is the command the player used.
6011 **************************************************************************/
6012 static void show_help_command_list(struct connection *caller,
6013 enum command_id help_cmd)
6015 enum command_id i;
6017 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6018 cmd_reply(help_cmd, caller, C_COMMENT,
6019 _("The following server commands are available:"));
6020 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6021 if(!caller && con_get_style()) {
6022 for (i=0; i<CMD_NUM; i++) {
6023 cmd_reply(help_cmd, caller, C_COMMENT, "%s", command_name_by_number(i));
6025 } else {
6026 char buf[MAX_LEN_CONSOLE_LINE];
6027 int j;
6029 buf[0] = '\0';
6030 for (i=0, j=0; i<CMD_NUM; i++) {
6031 if (may_use(caller, i)) {
6032 cat_snprintf(buf, sizeof(buf), "%-19s", command_name_by_number(i));
6033 if((++j % 4) == 0) {
6034 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
6035 buf[0] = '\0';
6039 if (buf[0] != '\0')
6040 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
6042 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
6045 /**************************************************************************
6046 Send a reply to the caller listing the matched names from an ambiguous
6047 prefix.
6048 **************************************************************************/
6049 static void cmd_reply_matches(enum command_id cmd,
6050 struct connection *caller,
6051 m_pre_accessor_fn_t accessor_fn,
6052 int *matches, int num_matches)
6054 char buf[MAX_LEN_MSG];
6055 const char *src, *end;
6056 char *dest;
6057 int i;
6059 if (accessor_fn == NULL || matches == NULL || num_matches < 1) {
6060 return;
6063 dest = buf;
6064 end = buf + sizeof(buf) - 1;
6066 for (i = 0; i < num_matches && dest < end; i++) {
6067 src = accessor_fn(matches[i]);
6068 if (!src) {
6069 continue;
6071 if (dest != buf) {
6072 *dest++ = ' ';
6074 while (*src != '\0' && dest < end) {
6075 *dest++ = *src++;
6078 *dest = '\0';
6080 cmd_reply(cmd, caller, C_COMMENT, _("Possible matches: %s"), buf);
6083 /**************************************************************************
6084 Additional 'help' arguments
6085 **************************************************************************/
6086 #define SPECENUM_NAME help_general_args
6087 #define SPECENUM_VALUE0 HELP_GENERAL_COMMANDS
6088 #define SPECENUM_VALUE0NAME "commands"
6089 #define SPECENUM_VALUE1 HELP_GENERAL_OPTIONS
6090 #define SPECENUM_VALUE1NAME "options"
6091 #define SPECENUM_COUNT HELP_GENERAL_COUNT
6092 #include "specenum_gen.h"
6094 /**************************************************************************
6095 Unified indices for help arguments:
6096 CMD_NUM - Server commands
6097 HELP_GENERAL_NUM - General help arguments, above
6098 settings_number() - Server options
6099 **************************************************************************/
6100 #define HELP_ARG_NUM (CMD_NUM + HELP_GENERAL_COUNT + settings_number())
6102 /**************************************************************************
6103 Convert unified helparg index to string; see above.
6104 **************************************************************************/
6105 static const char *helparg_accessor(int i)
6107 if (i < CMD_NUM) {
6108 return command_name_by_number(i);
6111 i -= CMD_NUM;
6112 if (i < HELP_GENERAL_COUNT) {
6113 return help_general_args_name((enum help_general_args) i);
6116 i -= HELP_GENERAL_COUNT;
6117 return optname_accessor(i);
6120 /**************************************************************************
6121 Handle help command
6122 **************************************************************************/
6123 static bool show_help(struct connection *caller, char *arg)
6125 int matches[64], num_matches = 0;
6126 enum m_pre_result match_result;
6127 int ind;
6129 fc_assert_ret_val(!may_use_nothing(caller), FALSE);
6130 /* no commands means no help, either */
6132 match_result = match_prefix_full(helparg_accessor, HELP_ARG_NUM, 0,
6133 fc_strncasecmp, NULL, arg, &ind, matches,
6134 ARRAY_SIZE(matches), &num_matches);
6136 if (match_result==M_PRE_EMPTY) {
6137 show_help_intro(caller, CMD_HELP);
6138 return FALSE;
6140 if (match_result==M_PRE_AMBIGUOUS) {
6141 cmd_reply(CMD_HELP, caller, C_FAIL,
6142 _("Help argument '%s' is ambiguous."), arg);
6143 cmd_reply_matches(CMD_HELP, caller, helparg_accessor,
6144 matches, num_matches);
6145 return FALSE;
6147 if (match_result==M_PRE_FAIL) {
6148 cmd_reply(CMD_HELP, caller, C_FAIL,
6149 _("No match for help argument '%s'."), arg);
6150 return FALSE;
6153 /* other cases should be above */
6154 fc_assert_ret_val(match_result < M_PRE_AMBIGUOUS, FALSE);
6156 if (ind < CMD_NUM) {
6157 show_help_command(caller, CMD_HELP, ind);
6158 return TRUE;
6160 ind -= CMD_NUM;
6162 if (ind == HELP_GENERAL_OPTIONS) {
6163 show_help_option_list(caller, CMD_HELP);
6164 return TRUE;
6166 if (ind == HELP_GENERAL_COMMANDS) {
6167 show_help_command_list(caller, CMD_HELP);
6168 return TRUE;
6170 ind -= HELP_GENERAL_COUNT;
6172 if (ind < settings_number()) {
6173 show_help_option(caller, CMD_HELP, ind);
6174 return TRUE;
6177 /* should have finished by now */
6178 log_error("Bug in show_help!");
6179 return FALSE;
6182 /****************************************************************************
6183 List connections; initially mainly for debugging
6184 ****************************************************************************/
6185 static void show_connections(struct connection *caller)
6187 char buf[MAX_LEN_CONSOLE_LINE];
6189 cmd_reply(CMD_LIST, caller, C_COMMENT,
6190 _("List of connections to server:"));
6191 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6193 if (conn_list_size(game.all_connections) == 0) {
6194 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no connections>"));
6195 } else {
6196 conn_list_iterate(game.all_connections, pconn) {
6197 sz_strlcpy(buf, conn_description(pconn));
6198 if (pconn->established) {
6199 cat_snprintf(buf, sizeof(buf), " command access level %s",
6200 cmdlevel_name(pconn->access_level));
6202 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6203 } conn_list_iterate_end;
6205 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6208 /*****************************************************************************
6209 List all delegations of the current game.
6210 *****************************************************************************/
6211 static void show_delegations(struct connection *caller)
6213 bool empty = TRUE;
6215 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of all delegations:"));
6216 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6218 players_iterate(pplayer) {
6219 const char *delegate_to = player_delegation_get(pplayer);
6220 if (delegate_to != NULL) {
6221 const char *owner =
6222 player_delegation_active(pplayer) ? pplayer->server.orig_username
6223 : pplayer->username;
6224 fc_assert(owner);
6225 cmd_reply(CMD_LIST, caller, C_COMMENT,
6226 /* TRANS: last %s is either " (active)" or empty string */
6227 _("%s delegates control over player '%s' to user %s%s."),
6228 owner, player_name(pplayer), delegate_to,
6229 player_delegation_active(pplayer) ? _(" (active)") : "");
6230 empty = FALSE;
6232 } players_iterate_end;
6234 if (empty) {
6235 cmd_reply(CMD_LIST, caller, C_COMMENT, _("No delegations defined."));
6238 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6241 /****************************************************************************
6242 Show the ignore list of the
6243 ****************************************************************************/
6244 static bool show_ignore(struct connection *caller)
6246 char buf[128];
6247 int n = 1;
6249 if (NULL == caller) {
6250 cmd_reply(CMD_IGNORE, caller, C_FAIL,
6251 _("That would be rather silly, since you are not a player."));
6252 return FALSE;
6255 if (0 == conn_pattern_list_size(caller->server.ignore_list)) {
6256 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list is empty."));
6257 return TRUE;
6260 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list:"));
6261 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6262 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
6263 conn_pattern_to_string(ppattern, buf, sizeof(buf));
6264 cmd_reply(CMD_LIST, caller, C_COMMENT, "%d: %s", n++, buf);
6265 } conn_pattern_list_iterate_end;
6266 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6268 return TRUE;
6271 /****************************************************************************
6272 Show the list of the players of the game.
6273 ****************************************************************************/
6274 void show_players(struct connection *caller)
6276 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of players:"));
6277 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6279 if (player_count() == 0) {
6280 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6281 } else {
6282 players_iterate(pplayer) {
6283 char buf[MAX_LEN_CONSOLE_LINE];
6284 int n;
6286 /* Low access level callers don't get to see barbarians in list: */
6287 if (is_barbarian(pplayer) && caller
6288 && (caller->access_level < ALLOW_CTRL)) {
6289 continue;
6292 /* The output for each player looks like:
6294 * <Player name> [color]: Team[, Nation][, Username][, Status]
6295 * AI/Barbarian/Human[, AI type, skill level][, Connections]
6296 * [Details for each connection]
6299 /* '<Player name> [color]: [Nation][, Username][, Status]' */
6300 buf[0] = '\0';
6301 cat_snprintf(buf, sizeof(buf), "%s [%s]: %s", player_name(pplayer),
6302 player_color_ftstr(pplayer),
6303 team_name_translation(pplayer->team));
6304 if (!game.info.is_new_game) {
6305 cat_snprintf(buf, sizeof(buf), ", %s",
6306 nation_adjective_for_player(pplayer));
6308 if (strlen(pplayer->username) > 0
6309 && strcmp(pplayer->username, "nouser") != 0) {
6310 cat_snprintf(buf, sizeof(buf), _(", user %s"), pplayer->username);
6312 if (S_S_INITIAL == server_state() && pplayer->is_connected) {
6313 if (pplayer->is_ready) {
6314 sz_strlcat(buf, _(", ready"));
6315 } else {
6316 /* Emphasizes this */
6317 n = strlen(buf);
6318 featured_text_apply_tag(_(", not ready"),
6319 buf + n, sizeof(buf) - n,
6320 TTT_COLOR, 1, FT_OFFSET_UNSET,
6321 ftc_changed);
6323 } else if (!pplayer->is_alive) {
6324 sz_strlcat(buf, _(", Dead"));
6326 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6328 /* ' AI/Barbarian/Human[, skill level][, Connections]' */
6329 buf[0] = '\0';
6330 if (is_barbarian(pplayer)) {
6331 sz_strlcat(buf, _("Barbarian"));
6332 } else if (pplayer->ai_controlled) {
6333 sz_strlcat(buf, _("AI"));
6334 } else {
6335 sz_strlcat(buf, _("Human"));
6337 if (pplayer->ai_controlled) {
6338 cat_snprintf(buf, sizeof(buf), _(", %s"), ai_name(pplayer->ai));
6339 cat_snprintf(buf, sizeof(buf), _(", difficulty level %s"),
6340 ai_level_translated_name(pplayer->ai_common.skill_level));
6342 n = conn_list_size(pplayer->connections);
6343 if (n > 0) {
6344 cat_snprintf(buf, sizeof(buf),
6345 PL_(", %d connection:", ", %d connections:", n), n);
6347 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6349 /* ' [Details for each connection]' */
6350 conn_list_iterate(pplayer->connections, pconn) {
6351 fc_snprintf(buf, sizeof(buf),
6352 _("%s from %s (command access level %s), "
6353 "bufsize=%dkb"), pconn->username, pconn->addr,
6354 cmdlevel_name(pconn->access_level),
6355 (pconn->send_buffer->nsize >> 10));
6356 if (pconn->observer) {
6357 sz_strlcat(buf, _(" (observer mode)"));
6359 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6360 } conn_list_iterate_end;
6361 } players_iterate_end;
6363 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6366 /****************************************************************************
6367 List scenarios. We look both in the DATA_PATH and DATA_PATH/scenario
6368 ****************************************************************************/
6369 static void show_scenarios(struct connection *caller)
6371 char buf[MAX_LEN_CONSOLE_LINE];
6372 struct fileinfo_list *files;
6374 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of scenarios available:"));
6375 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6377 files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE);
6379 fileinfo_list_iterate(files, pfile) {
6380 fc_snprintf(buf, sizeof(buf), "%s", pfile->name);
6381 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6382 } fileinfo_list_iterate_end;
6383 fileinfo_list_destroy(files);
6385 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6388 /****************************************************************************
6389 List nation sets in the current ruleset.
6390 ****************************************************************************/
6391 static void show_nationsets(struct connection *caller)
6393 cmd_reply(CMD_LIST, caller, C_COMMENT,
6394 /* TRANS: don't translate text between '' */
6395 _("List of nation sets available for 'nationset' option:"));
6396 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6398 nation_sets_iterate(pset) {
6399 const char *description = nation_set_description(pset);
6400 int num_nations = 0;
6401 nations_iterate(pnation) {
6402 if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) {
6403 num_nations++;
6405 } nations_iterate_end;
6406 cmd_reply(CMD_LIST, caller, C_COMMENT,
6407 /* TRANS: nation set description; %d refers to number of playable
6408 * nations in set */
6409 PL_(" %-10s %s (%d playable)",
6410 " %-10s %s (%d playable)", num_nations),
6411 nation_set_rule_name(pset), nation_set_name_translation(pset),
6412 num_nations);
6413 if (strlen(description) > 0) {
6414 static const char prefix[] = " ";
6415 char *translated = fc_strdup(_(description));
6416 fc_break_lines(translated, LINE_BREAK);
6417 cmd_reply_prefix(CMD_LIST, caller, C_COMMENT, prefix, "%s%s",
6418 prefix, translated);
6420 } nation_sets_iterate_end;
6422 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6425 /****************************************************************************
6426 Show a list of teams on the command line.
6427 ****************************************************************************/
6428 static void show_teams(struct connection *caller)
6430 /* Currently this just lists all teams (typically 32 of them) with their
6431 * names and # of players on the team. This could probably be improved. */
6432 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of teams:"));
6433 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6435 teams_iterate(pteam) {
6436 const struct player_list *members = team_members(pteam);
6438 /* PL_() is needed here because some languages may differentiate
6439 * between 2 and 3 (although English does not). */
6440 cmd_reply(CMD_LIST, caller, C_COMMENT,
6441 /* TRANS: There will always be at least 2 players here. */
6442 PL_("%2d : '%s' : %d player :",
6443 "%2d : '%s' : %d players :",
6444 player_list_size(members)),
6445 team_index(pteam), team_name_translation(pteam),
6446 player_list_size(members));
6447 player_list_iterate(members, pplayer) {
6448 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", player_name(pplayer));
6449 } player_list_iterate_end;
6450 } teams_iterate_end;
6452 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6455 /****************************************************************************
6456 Show a list of all map image definitions on the command line.
6457 ****************************************************************************/
6458 static void show_mapimg(struct connection *caller, enum command_id cmd)
6460 int id;
6462 if (mapimg_count() == 0) {
6463 cmd_reply(cmd, caller, C_OK, _("No map image definitions."));
6464 } else {
6465 cmd_reply(cmd, caller, C_COMMENT, _("List of map image definitions:"));
6466 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6467 for (id = 0; id < mapimg_count(); id++) {
6468 char str[MAX_LEN_MAPDEF] = "";
6469 mapimg_show(id, str, sizeof(str), FALSE);
6470 cmd_reply(cmd, caller, C_COMMENT, _("[%2d] %s"), id, str);
6472 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6476 /****************************************************************************
6477 Show a list of all players with the assigned color.
6478 ****************************************************************************/
6479 static void show_colors(struct connection *caller)
6481 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of player colors:"));
6482 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6483 if (player_count() == 0) {
6484 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6485 } else {
6486 players_iterate(pplayer) {
6487 cmd_reply(CMD_LIST, caller, C_COMMENT, _("%s (user %s): [%s]"),
6488 player_name(pplayer), pplayer->username,
6489 player_color_ftstr(pplayer));
6490 } players_iterate_end;
6492 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6495 /****************************************************************************
6496 '/list' arguments
6497 **************************************************************************/
6498 #define SPECENUM_NAME list_args
6499 #define SPECENUM_VALUE0 LIST_COLORS
6500 #define SPECENUM_VALUE0NAME "colors"
6501 #define SPECENUM_VALUE1 LIST_CONNECTIONS
6502 #define SPECENUM_VALUE1NAME "connections"
6503 #define SPECENUM_VALUE2 LIST_DELEGATIONS
6504 #define SPECENUM_VALUE2NAME "delegations"
6505 #define SPECENUM_VALUE3 LIST_IGNORE
6506 #define SPECENUM_VALUE3NAME "ignored users"
6507 #define SPECENUM_VALUE4 LIST_MAPIMG
6508 #define SPECENUM_VALUE4NAME "map image definitions"
6509 #define SPECENUM_VALUE5 LIST_PLAYERS
6510 #define SPECENUM_VALUE5NAME "players"
6511 #define SPECENUM_VALUE6 LIST_SCENARIOS
6512 #define SPECENUM_VALUE6NAME "scenarios"
6513 #define SPECENUM_VALUE7 LIST_NATIONSETS
6514 #define SPECENUM_VALUE7NAME "nationsets"
6515 #define SPECENUM_VALUE8 LIST_TEAMS
6516 #define SPECENUM_VALUE8NAME "teams"
6517 #define SPECENUM_VALUE9 LIST_VOTES
6518 #define SPECENUM_VALUE9NAME "votes"
6519 #include "specenum_gen.h"
6521 /**************************************************************************
6522 Returns possible parameters for the list command.
6523 **************************************************************************/
6524 static const char *list_accessor(int i)
6526 i = CLIP(0, i, list_args_max());
6527 return list_args_name((enum list_args) i);
6530 /**************************************************************************
6531 Show list of players or connections, or connection statistics.
6532 **************************************************************************/
6533 static bool show_list(struct connection *caller, char *arg)
6535 enum m_pre_result match_result;
6536 int ind_int;
6537 enum list_args ind;
6539 remove_leading_trailing_spaces(arg);
6540 match_result = match_prefix(list_accessor, list_args_max() + 1, 0,
6541 fc_strncasecmp, NULL, arg, &ind_int);
6542 ind = ind_int;
6544 if (match_result > M_PRE_EMPTY) {
6545 cmd_reply(CMD_LIST, caller, C_SYNTAX,
6546 _("Bad list argument: '%s'. Try '%shelp list'."),
6547 arg, (caller?"/":""));
6548 return FALSE;
6551 if (match_result == M_PRE_EMPTY) {
6552 ind = LIST_PLAYERS;
6555 switch(ind) {
6556 case LIST_COLORS:
6557 show_colors(caller);
6558 return TRUE;
6559 case LIST_CONNECTIONS:
6560 show_connections(caller);
6561 return TRUE;
6562 case LIST_DELEGATIONS:
6563 show_delegations(caller);
6564 return TRUE;
6565 case LIST_IGNORE:
6566 return show_ignore(caller);
6567 case LIST_MAPIMG:
6568 show_mapimg(caller, CMD_LIST);
6569 return TRUE;
6570 case LIST_PLAYERS:
6571 show_players(caller);
6572 return TRUE;
6573 case LIST_SCENARIOS:
6574 show_scenarios(caller);
6575 return TRUE;
6576 case LIST_NATIONSETS:
6577 show_nationsets(caller);
6578 return TRUE;
6579 case LIST_TEAMS:
6580 show_teams(caller);
6581 return TRUE;
6582 case LIST_VOTES:
6583 show_votes(caller);
6584 return TRUE;
6587 cmd_reply(CMD_LIST, caller, C_FAIL,
6588 "Internal error: ind %d in show_list", ind);
6589 log_error("Internal error: ind %d in show_list", ind);
6590 return FALSE;
6593 #ifdef FREECIV_HAVE_LIBREADLINE
6594 /********************* RL completion functions ***************************/
6595 /* To properly complete both commands, player names, options and filenames
6596 there is one array per type of completion with the commands that
6597 the type is relevant for.
6600 /**************************************************************************
6601 A generalised generator function: text and state are "standard"
6602 parameters to a readline generator function;
6603 num is number of possible completions, or -1 if this is not known and
6604 index2str should be iterated until it returns NULL;
6605 index2str is a function which returns each possible completion string
6606 by index (it may return NULL).
6607 **************************************************************************/
6608 static char *generic_generator(const char *text, int state, int num,
6609 const char*(*index2str)(int))
6611 static int list_index, len;
6612 const char *name = ""; /* dummy non-NULL string */
6613 char *mytext = local_to_internal_string_malloc(text);
6615 /* This function takes a string (text) in the local format and must return
6616 * a string in the local format. However comparisons are done against
6617 * names that are in the internal format (UTF-8). Thus we have to convert
6618 * the text function from the local to the internal format before doing
6619 * the comparison, and convert the string we return *back* to the
6620 * local format when returning it. */
6622 /* If this is a new word to complete, initialize now. This includes
6623 saving the length of TEXT for efficiency, and initializing the index
6624 variable to 0. */
6625 if (state == 0) {
6626 list_index = 0;
6627 len = strlen(mytext);
6630 /* Return the next name which partially matches: */
6631 while ((num < 0 && name) || (list_index < num)) {
6632 name = index2str(list_index);
6633 list_index++;
6635 if (name != NULL && fc_strncasecmp(name, mytext, len) == 0) {
6636 free(mytext);
6637 return internal_to_local_string_malloc(name);
6640 free(mytext);
6642 /* If no names matched, then return NULL. */
6643 return ((char *)NULL);
6646 /**************************************************************************
6647 The valid commands at the root of the prompt.
6648 **************************************************************************/
6649 static char *command_generator(const char *text, int state)
6651 return generic_generator(text, state, CMD_NUM, command_name_by_number);
6654 /**************************************************************************
6655 The valid arguments to "set" and "explain"
6656 **************************************************************************/
6657 static char *option_generator(const char *text, int state)
6659 return generic_generator(text, state, settings_number(), optname_accessor);
6662 /**************************************************************************
6663 The valid arguments to "show"
6664 **************************************************************************/
6665 static char *olevel_generator(const char *text, int state)
6667 return generic_generator(text, state, settings_number() + OLEVELS_NUM + 1,
6668 olvlname_accessor);
6671 /**************************************************************************
6672 Accessor for values of the enum/bitwise option defined by
6673 'completion_option'.
6674 **************************************************************************/
6675 static int completion_option;
6676 static const char *option_value_accessor(int idx) {
6677 const struct setting *pset = setting_by_number(completion_option);
6678 switch (setting_type(pset)) {
6679 case SSET_ENUM:
6680 return setting_enum_val(pset, idx, FALSE);
6681 break;
6682 case SSET_BITWISE:
6683 return setting_bitwise_bit(pset, idx, FALSE);
6684 break;
6685 default:
6686 fc_assert_ret_val(0, NULL);
6690 /**************************************************************************
6691 The valid arguments to "set OPT", where OPT is the enumerated or
6692 bitwise option previously defined by completion_option
6693 **************************************************************************/
6694 static char *option_value_generator(const char *text, int state)
6696 return generic_generator(text, state, -1, option_value_accessor);
6699 /**************************************************************************
6700 Access player name.
6701 **************************************************************************/
6702 static const char *playername_accessor(int idx)
6704 const struct player_slot *pslot = player_slot_by_number(idx);
6706 if (!player_slot_is_used(pslot)) {
6707 return NULL;
6710 return player_name(player_slot_get_player(pslot));
6713 /**************************************************************************
6714 The valid playername arguments.
6715 **************************************************************************/
6716 static char *player_generator(const char *text, int state)
6718 return generic_generator(text, state, player_slot_count(),
6719 playername_accessor);
6722 /**************************************************************************
6723 Access connection user name, from game.all_connections.
6724 **************************************************************************/
6725 static const char *connection_name_accessor(int idx)
6727 return conn_list_get(game.all_connections, idx)->username;
6730 /**************************************************************************
6731 The valid connection user name arguments.
6732 **************************************************************************/
6733 static char *connection_generator(const char *text, int state)
6735 return generic_generator(text, state, conn_list_size(game.all_connections),
6736 connection_name_accessor);
6739 /**************************************************************************
6740 Extra accessor function since cmdlevel_name() takes enum argument, not int.
6741 **************************************************************************/
6742 static const char *cmdlevel_arg1_accessor(int idx)
6744 return cmdlevel_name(idx);
6747 /**************************************************************************
6748 The valid first argument to "cmdlevel"
6749 **************************************************************************/
6750 static char *cmdlevel_arg1_generator(const char *text, int state)
6752 return generic_generator(text, state, cmdlevel_max()+1,
6753 cmdlevel_arg1_accessor);
6756 /**************************************************************************
6757 Accessor for the second argument to "cmdlevel": "first" or "new" or
6758 a connection name.
6759 **************************************************************************/
6760 static const char *cmdlevel_arg2_accessor(int idx)
6762 return ((idx==0) ? "first" :
6763 (idx==1) ? "new" :
6764 connection_name_accessor(idx-2));
6767 /**************************************************************************
6768 The valid arguments for the second argument to "cmdlevel".
6769 **************************************************************************/
6770 static char *cmdlevel_arg2_generator(const char *text, int state)
6772 return generic_generator(text, state,
6773 /* "first", "new", connection names */
6774 2 + conn_list_size(game.all_connections),
6775 cmdlevel_arg2_accessor);
6778 /**************************************************************************
6779 Accessor for the second argument to "create": ai type name
6780 **************************************************************************/
6781 static const char *aitype_accessor(int idx)
6783 return get_ai_type(idx)->name;
6786 /**************************************************************************
6787 The valid arguments for the second argument to "create".
6788 **************************************************************************/
6789 static char *aitype_generator(const char *text, int state)
6791 return generic_generator(text, state, ai_type_get_count(),
6792 aitype_accessor);
6795 /**************************************************************************
6796 The valid arguments for the argument to "reset".
6797 **************************************************************************/
6798 static char *reset_generator(const char *text, int state)
6800 return generic_generator(text, state, reset_args_max() + 1, reset_accessor);
6803 /**************************************************************************
6804 The valid arguments for the argument to "vote".
6805 **************************************************************************/
6806 static char *vote_generator(const char *text, int state)
6808 return generic_generator(text, state, -1, vote_arg_accessor);
6811 /**************************************************************************
6812 The valid arguments for the first argument to "delegate".
6813 **************************************************************************/
6814 static char *delegate_generator(const char *text, int state)
6816 return generic_generator(text, state, delegate_args_max() + 1,
6817 delegate_accessor);
6820 /**************************************************************************
6821 The valid arguments for the first argument to "mapimg".
6822 **************************************************************************/
6823 static char *mapimg_generator(const char *text, int state)
6825 return generic_generator(text, state, mapimg_args_max() + 1,
6826 mapimg_accessor);
6829 /**************************************************************************
6830 The valid arguments for the argument to "fcdb".
6831 **************************************************************************/
6832 static char *fcdb_generator(const char *text, int state)
6834 return generic_generator(text, state, FCDB_COUNT, fcdb_accessor);
6837 /**************************************************************************
6838 The valid arguments for the argument to "lua".
6839 **************************************************************************/
6840 static char *lua_generator(const char *text, int state)
6842 return generic_generator(text, state, lua_args_max() + 1, lua_accessor);
6845 /**************************************************************************
6846 The valid first arguments to "help".
6847 **************************************************************************/
6848 static char *help_generator(const char *text, int state)
6850 return generic_generator(text, state, HELP_ARG_NUM, helparg_accessor);
6853 /**************************************************************************
6854 The valid first arguments to "list".
6855 **************************************************************************/
6856 static char *list_generator(const char *text, int state)
6858 return generic_generator(text, state, list_args_max() + 1, list_accessor);
6861 /**************************************************************************
6862 Generalised version of contains_str_before_start, which searches the
6863 N'th token in rl_line_buffer (0=first).
6864 **************************************************************************/
6865 static bool contains_token_before_start(int start, int token, const char *arg,
6866 bool allow_fluff)
6868 char *str_itr = rl_line_buffer;
6869 int arg_len = strlen(arg);
6871 /* Swallow unwanted tokens and their preceding delimiters */
6872 while (token--) {
6873 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6874 str_itr++;
6876 while (str_itr < rl_line_buffer + start && fc_isalnum(*str_itr)) {
6877 str_itr++;
6881 /* Swallow any delimiters before the token we're interested in */
6882 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6883 str_itr++;
6886 if (fc_strncasecmp(str_itr, arg, arg_len) != 0) {
6887 return FALSE;
6889 str_itr += arg_len;
6891 if (fc_isalnum(*str_itr)) {
6892 /* Not a distinct word. */
6893 return FALSE;
6896 if (!allow_fluff) {
6897 for (; str_itr < rl_line_buffer + start; str_itr++) {
6898 if (fc_isalnum(*str_itr)) {
6899 return FALSE;
6904 return TRUE;
6907 /**************************************************************************
6908 Returns whether the text between the start of rl_line_buffer and the
6909 start position is of the form [non-alpha]*cmd[non-alpha]*
6910 allow_fluff changes the regexp to [non-alpha]*cmd[non-alpha].*
6911 **************************************************************************/
6912 static bool contains_str_before_start(int start, const char *cmd,
6913 bool allow_fluff)
6915 return contains_token_before_start(start, 0, cmd, allow_fluff);
6918 /**************************************************************************
6919 Return whether we are completing command name. This can be either
6920 command itself, or argument to 'help'.
6921 **************************************************************************/
6922 static bool is_command(int start)
6924 char *str_itr;
6926 if (contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE))
6927 return TRUE;
6929 /* if there is only it is also OK */
6930 str_itr = rl_line_buffer;
6931 while (str_itr - rl_line_buffer < start) {
6932 if (fc_isalnum(*str_itr)) {
6933 return FALSE;
6935 str_itr++;
6937 return TRUE;
6940 /**************************************************************************
6941 number of tokens in rl_line_buffer before start
6942 **************************************************************************/
6943 static int num_tokens(int start)
6945 int res = 0;
6946 bool alnum = FALSE;
6947 char *chptr = rl_line_buffer;
6949 while (chptr - rl_line_buffer < start) {
6950 if (fc_isalnum(*chptr)) {
6951 if (!alnum) {
6952 alnum = TRUE;
6953 res++;
6955 } else {
6956 alnum = FALSE;
6958 chptr++;
6961 return res;
6964 /**************************************************************************
6965 Commands that may be followed by a player name
6966 **************************************************************************/
6967 static const int player_cmd[] = {
6968 CMD_AITOGGLE,
6969 CMD_HANDICAPPED,
6970 CMD_NOVICE,
6971 CMD_EASY,
6972 CMD_NORMAL,
6973 CMD_HARD,
6974 CMD_CHEATING,
6975 #ifdef DEBUG
6976 CMD_EXPERIMENTAL,
6977 #endif
6978 CMD_REMOVE,
6979 CMD_TEAM,
6980 CMD_PLAYERCOLOR,
6984 /**************************************************************************
6985 Return whether we are completing player name argument.
6986 **************************************************************************/
6987 static bool is_player(int start)
6989 int i = 0;
6991 while (player_cmd[i] != -1) {
6992 if (contains_str_before_start(start, command_name_by_number(player_cmd[i]), FALSE)) {
6993 return TRUE;
6995 i++;
6998 return FALSE;
7001 /**************************************************************************
7002 Commands that may be followed by a connection name
7003 **************************************************************************/
7004 static const int connection_cmd[] = {
7005 CMD_CUT,
7006 CMD_KICK,
7010 /**************************************************************************
7011 Return whether we are completing connection name argument.
7012 **************************************************************************/
7013 static bool is_connection(int start)
7015 int i = 0;
7017 while (connection_cmd[i] != -1) {
7018 if (contains_str_before_start(start,
7019 command_name_by_number(connection_cmd[i]),
7020 FALSE)) {
7021 return TRUE;
7023 i++;
7026 return FALSE;
7029 /**************************************************************************
7030 Return whether we are completing cmdlevel command argument 2.
7031 **************************************************************************/
7032 static bool is_cmdlevel_arg2(int start)
7034 return (contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), TRUE)
7035 && num_tokens(start) == 2);
7038 /**************************************************************************
7039 Return whether we are completing cmdlevel command argument.
7040 **************************************************************************/
7041 static bool is_cmdlevel_arg1(int start)
7043 return contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), FALSE);
7046 /**************************************************************************
7047 Commands that may be followed by a server option name
7049 CMD_SHOW is handled by option_level_cmd, which is for both option levels
7050 and server options
7051 **************************************************************************/
7052 static const int server_option_cmd[] = {
7053 CMD_EXPLAIN,
7054 CMD_SET,
7055 CMD_DEFAULT,
7059 /**************************************************************************
7060 Returns TRUE if the readline buffer string matches a server option at
7061 the given position.
7062 **************************************************************************/
7063 static bool is_server_option(int start)
7065 int i = 0;
7067 while (server_option_cmd[i] != -1) {
7068 if (contains_str_before_start(start, command_name_by_number(server_option_cmd[i]),
7069 FALSE)) {
7070 return TRUE;
7072 i++;
7075 return FALSE;
7078 /**************************************************************************
7079 Commands that may be followed by an option level or server option
7080 **************************************************************************/
7081 static const int option_level_cmd[] = {
7082 CMD_SHOW,
7086 /**************************************************************************
7087 Returns true if the readline buffer string matches an option level or an
7088 option at the given position.
7089 **************************************************************************/
7090 static bool is_option_level(int start)
7092 int i = 0;
7094 while (option_level_cmd[i] != -1) {
7095 if (contains_str_before_start(start, command_name_by_number(option_level_cmd[i]),
7096 FALSE)) {
7097 return TRUE;
7099 i++;
7102 return FALSE;
7105 /**************************************************************************
7106 Returns TRUE if the readline buffer string is such that we expect an
7107 enumerated value at the given position. The option for which values
7108 should be completed is written to opt_p.
7109 **************************************************************************/
7110 static bool is_enum_option_value(int start, int *opt_p)
7112 if (contains_str_before_start(start, command_name_by_number(CMD_SET),
7113 TRUE)) {
7114 settings_iterate(SSET_ALL, pset) {
7115 if (setting_type(pset) != SSET_ENUM
7116 && setting_type(pset) != SSET_BITWISE) {
7117 continue;
7119 /* Allow a single token for enum options, multiple for bitwise
7120 * (the separator | will separate tokens for these purposes) */
7121 if (contains_token_before_start(start, 1, setting_name(pset),
7122 setting_type(pset) == SSET_BITWISE)) {
7123 *opt_p = setting_number(pset);
7124 /* Suppress appended space for bitwise options (user may want |) */
7125 rl_completion_suppress_append = (setting_type(pset) == SSET_BITWISE);
7126 return TRUE;
7128 } settings_iterate_end;
7130 return FALSE;
7133 /**************************************************************************
7134 Commands that may be followed by a filename
7135 **************************************************************************/
7136 static const int filename_cmd[] = {
7137 CMD_LOAD,
7138 CMD_SAVE,
7139 CMD_READ_SCRIPT,
7140 CMD_WRITE_SCRIPT,
7144 /**************************************************************************
7145 Return whether we are completing filename.
7146 **************************************************************************/
7147 static bool is_filename(int start)
7149 int i = 0;
7151 while (filename_cmd[i] != -1) {
7152 if (contains_str_before_start(start, command_name_by_number(filename_cmd[i]), FALSE)) {
7153 return TRUE;
7155 i++;
7158 return FALSE;
7161 /**************************************************************************
7162 Return whether we are completing second argument for create command
7163 **************************************************************************/
7164 static bool is_create_arg2(int start)
7166 return (contains_str_before_start(start, command_name_by_number(CMD_CREATE), TRUE)
7167 && num_tokens(start) == 2);
7170 /**************************************************************************
7171 Return whether we are completing argument for reset command
7172 **************************************************************************/
7173 static bool is_reset(int start)
7175 return contains_str_before_start(start,
7176 command_name_by_number(CMD_RESET),
7177 FALSE);
7180 /**************************************************************************
7181 Return whether we are completing argument for vote command
7182 **************************************************************************/
7183 static bool is_vote(int start)
7185 return contains_str_before_start(start,
7186 command_name_by_number(CMD_VOTE),
7187 FALSE);
7190 /**************************************************************************
7191 Return whether we are completing first argument for delegate command
7192 **************************************************************************/
7193 static bool is_delegate_arg1(int start)
7195 return contains_str_before_start(start,
7196 command_name_by_number(CMD_DELEGATE),
7197 FALSE);
7200 /**************************************************************************
7201 Return whether we are completing first argument for mapimg command
7202 **************************************************************************/
7203 static bool is_mapimg(int start)
7205 return contains_str_before_start(start,
7206 command_name_by_number(CMD_MAPIMG),
7207 FALSE);
7210 /**************************************************************************
7211 Return whether we are completing argument for fcdb command
7212 **************************************************************************/
7213 static bool is_fcdb(int start)
7215 return contains_str_before_start(start,
7216 command_name_by_number(CMD_FCDB),
7217 FALSE);
7220 /**************************************************************************
7221 Return whether we are completing argument for lua command
7222 **************************************************************************/
7223 static bool is_lua(int start)
7225 return contains_str_before_start(start,
7226 command_name_by_number(CMD_LUA),
7227 FALSE);
7230 /**************************************************************************
7231 Return whether we are completing help command argument.
7232 **************************************************************************/
7233 static bool is_help(int start)
7235 return contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE);
7238 /**************************************************************************
7239 Return whether we are completing list command argument.
7240 **************************************************************************/
7241 static bool is_list(int start)
7243 return contains_str_before_start(start, command_name_by_number(CMD_LIST), FALSE);
7246 /**************************************************************************
7247 Attempt to complete on the contents of TEXT. START and END bound the
7248 region of rl_line_buffer that contains the word to complete. TEXT is
7249 the word to complete. We can use the entire contents of rl_line_buffer
7250 in case we want to do some simple parsing. Return the array of matches,
7251 or NULL if there aren't any.
7252 **************************************************************************/
7253 char **freeciv_completion(const char *text, int start, int end)
7255 char **matches = (char **)NULL;
7257 if (is_help(start)) {
7258 matches = rl_completion_matches(text, help_generator);
7259 } else if (is_command(start)) {
7260 matches = rl_completion_matches(text, command_generator);
7261 } else if (is_list(start)) {
7262 matches = rl_completion_matches(text, list_generator);
7263 } else if (is_cmdlevel_arg2(start)) {
7264 matches = rl_completion_matches(text, cmdlevel_arg2_generator);
7265 } else if (is_cmdlevel_arg1(start)) {
7266 matches = rl_completion_matches(text, cmdlevel_arg1_generator);
7267 } else if (is_connection(start)) {
7268 matches = rl_completion_matches(text, connection_generator);
7269 } else if (is_player(start)) {
7270 matches = rl_completion_matches(text, player_generator);
7271 } else if (is_server_option(start)) {
7272 matches = rl_completion_matches(text, option_generator);
7273 } else if (is_option_level(start)) {
7274 matches = rl_completion_matches(text, olevel_generator);
7275 } else if (is_enum_option_value(start, &completion_option)) {
7276 matches = rl_completion_matches(text, option_value_generator);
7277 } else if (is_filename(start)) {
7278 /* This function we get from readline */
7279 matches = rl_completion_matches(text, rl_filename_completion_function);
7280 } else if (is_create_arg2(start)) {
7281 matches = rl_completion_matches(text, aitype_generator);
7282 } else if (is_reset(start)) {
7283 matches = rl_completion_matches(text, reset_generator);
7284 } else if (is_vote(start)) {
7285 matches = rl_completion_matches(text, vote_generator);
7286 } else if (is_delegate_arg1(start)) {
7287 matches = rl_completion_matches(text, delegate_generator);
7288 } else if (is_mapimg(start)) {
7289 matches = rl_completion_matches(text, mapimg_generator);
7290 } else if (is_fcdb(start)) {
7291 matches = rl_completion_matches(text, fcdb_generator);
7292 } else if (is_lua(start)) {
7293 matches = rl_completion_matches(text, lua_generator);
7294 } else {
7295 /* We have no idea what to do */
7296 matches = NULL;
7299 /* Don't automatically try to complete with filenames */
7300 rl_attempted_completion_over = 1;
7302 return (matches);
7305 #endif /* FREECIV_HAVE_LIBREADLINE */