Some miscellaneous updates to main help, for new features and otherwise.
[freeciv.git] / server / stdinhand.c
blobe60134a64f4eeba0ef039aa4e8001c52c5a49edd
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 HAVE_LIBREADLINE
24 #include <readline/readline.h>
25 #endif
27 /* utility */
28 #include "astring.h"
29 #include "bitvector.h"
30 #include "fciconv.h"
31 #include "fcintl.h"
32 #include "log.h"
33 #include "mem.h"
34 #include "registry.h"
35 #include "shared.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 "rgbcolor.h"
50 #include "unitlist.h"
51 #include "version.h"
53 /* server */
54 #include "aiiface.h"
55 #include "citytools.h"
56 #include "connecthand.h"
57 #include "diplhand.h"
58 #include "gamehand.h"
59 #include "ggzserver.h"
60 #include "mapgen.h"
61 #include "maphand.h"
62 #include "meta.h"
63 #include "notify.h"
64 #include "plrhand.h"
65 #include "report.h"
66 #include "ruleset.h"
67 #include "sanitycheck.h"
68 #include "savegame2.h"
69 #include "score.h"
70 #include "sernet.h"
71 #include "settings.h"
72 #include "srv_log.h"
73 #include "srv_main.h"
74 #include "techtools.h"
75 #include "voting.h"
77 /* server/scripting */
78 #include "script_server.h"
79 #include "script_fcdb.h"
81 #include "stdinhand.h"
83 #define TOKEN_DELIMITERS " \t\n,"
85 #define OPTION_NAME_SPACE 25
87 static enum cmdlevel default_access_level = ALLOW_BASIC;
88 static enum cmdlevel first_access_level = ALLOW_BASIC;
90 static time_t *time_duplicate(const time_t *t);
92 /* 'struct kick_hash' and related functions. */
93 #define SPECHASH_TAG kick
94 #define SPECHASH_KEY_TYPE char *
95 #define SPECHASH_DATA_TYPE time_t
96 #define SPECHASH_KEY_VAL genhash_str_val_func
97 #define SPECHASH_KEY_COMP genhash_str_comp_func
98 #define SPECHASH_KEY_COPY genhash_str_copy_func
99 #define SPECHASH_KEY_FREE genhash_str_free_func
100 #define SPECHASH_DATA_COPY time_duplicate
101 #define SPECHASH_DATA_FREE free
102 #define SPECHASH_DATA_TO_PTR(t) (&(t))
103 #define SPECHASH_PTR_TO_DATA(p) (NULL != p ? *(time_t *) (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 set_away(struct connection *caller, char *name, 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 void show_command_one(struct connection *caller, struct setting *pset);
124 static void show_changed(struct connection *caller, bool check,
125 int read_recursion);
126 static void show_mapimg(struct connection *caller, enum command_id cmd);
127 static bool set_command(struct connection *caller, char *str, bool check);
129 static bool create_command(struct connection *caller, const char *str,
130 bool check);
131 static bool end_command(struct connection *caller, char *str, bool check);
132 static bool surrender_command(struct connection *caller, char *str, bool check);
133 static bool handle_stdin_input_real(struct connection *caller, const char *str,
134 bool check, int read_recursion);
135 static bool read_init_script_real(struct connection *caller,
136 char *script_filename, bool from_cmdline,
137 bool check, int read_recursion);
138 static bool reset_command(struct connection *caller, char *arg, bool check,
139 int read_recursion);
140 static bool lua_command(struct connection *caller, char *arg, bool check);
141 static bool kick_command(struct connection *caller, char *name, bool check);
142 static bool delegate_command(struct connection *caller, char *arg,
143 bool check);
144 static const char *delegate_player_str(struct player *pplayer, bool observer);
145 static bool fcdb_command(struct connection *caller, char *arg, bool check);
146 static const char *fcdb_accessor(int i);
147 static char setting_status(struct connection *caller,
148 const struct setting *pset);
149 static bool player_name_check(const char* name, char *buf, size_t buflen);
150 static bool playercolor_command(struct connection *caller,
151 char *str, bool check);
152 static bool mapimg_command(struct connection *caller, char *arg, bool check);
153 static const char *mapimg_accessor(int i);
155 static void show_delegations(struct connection *caller);
157 static const char horiz_line[] =
158 "------------------------------------------------------------------------------";
160 /********************************************************************
161 Are we operating under a restricted security regime? For now
162 this does not do much.
163 *********************************************************************/
164 static bool is_restricted(struct connection *caller)
166 return (caller && caller->access_level != ALLOW_HACK);
169 /**************************************************************************
170 Check the player name. Returns TRUE if the player name is valid else
171 an error message is saved in 'buf'.
172 **************************************************************************/
173 static bool player_name_check(const char* name, char *buf, size_t buflen)
175 size_t len = strlen(name);
177 if (len == 0) {
178 fc_snprintf(buf, buflen, _("Can't use an empty name."));
179 return FALSE;
180 } else if (len > MAX_LEN_NAME-1) {
181 fc_snprintf(buf, buflen, _("That name exceeds the maximum of %d chars."),
182 MAX_LEN_NAME-1);
183 return FALSE;
184 } else if (fc_strcasecmp(name, ANON_PLAYER_NAME) == 0
185 || fc_strcasecmp(name, "Observer") == 0) {
186 fc_snprintf(buf, buflen, _("That name is not allowed."));
187 /* "Observer" used to be illegal and we keep it that way for now. */
188 return FALSE;
191 return TRUE;
194 /**************************************************************************
195 Convert a named command into an id.
196 If accept_ambiguity is true, return the first command in the
197 enum list which matches, else return CMD_AMBIGOUS on ambiguity.
198 (This is a trick to allow ambiguity to be handled in a flexible way
199 without importing notify_player() messages inside this routine - rp)
200 **************************************************************************/
201 static enum command_id command_named(const char *token, bool accept_ambiguity)
203 enum m_pre_result result;
204 int ind;
206 result = match_prefix(command_name_by_number, CMD_NUM, 0,
207 fc_strncasecmp, NULL, token, &ind);
209 if (result < M_PRE_AMBIGUOUS) {
210 return ind;
211 } else if (result == M_PRE_AMBIGUOUS) {
212 return accept_ambiguity ? ind : CMD_AMBIGUOUS;
213 } else {
214 return CMD_UNRECOGNIZED;
218 /**************************************************************************
219 Initialize stuff related to this code module.
220 **************************************************************************/
221 void stdinhand_init(void)
223 fc_assert(NULL == kick_table_by_addr);
224 kick_table_by_addr = kick_hash_new();
226 fc_assert(NULL == kick_table_by_user);
227 kick_table_by_user = kick_hash_new();
230 /**************************************************************************
231 Update stuff every turn that is related to this code module. Run this
232 on turn end.
233 **************************************************************************/
234 void stdinhand_turn(void)
236 /* Nothing at the moment. */
239 /**************************************************************************
240 Deinitialize stuff related to this code module.
241 **************************************************************************/
242 void stdinhand_free(void)
244 fc_assert(NULL != kick_table_by_addr);
245 if (NULL != kick_table_by_addr) {
246 kick_hash_destroy(kick_table_by_addr);
247 kick_table_by_addr = NULL;
250 fc_assert(NULL != kick_table_by_user);
251 if (NULL != kick_table_by_user) {
252 kick_hash_destroy(kick_table_by_user);
253 kick_table_by_user = NULL;
257 /**************************************************************************
258 Whether the caller can use the specified command. caller == NULL means
259 console.
260 **************************************************************************/
261 static bool may_use(struct connection *caller, enum command_id cmd)
263 if (!caller) {
264 return TRUE; /* on the console, everything is allowed */
266 return (caller->access_level >= command_level(command_by_number(cmd)));
269 /**************************************************************************
270 Whether the caller cannot use any commands at all.
271 caller == NULL means console.
272 **************************************************************************/
273 static bool may_use_nothing(struct connection *caller)
275 if (!caller) {
276 return FALSE; /* on the console, everything is allowed */
278 return (ALLOW_NONE == conn_get_access(caller));
281 /**************************************************************************
282 Return the status of the setting (changeable, locked, fixed).
283 caller == NULL means console.
284 **************************************************************************/
285 static char setting_status(struct connection *caller,
286 const struct setting *pset)
288 /* first check for a ruleset lock as this is included in
289 * setting_is_changeable() */
290 if (setting_locked(pset)) {
291 /* setting is locked by the ruleset */
292 return '!';
295 if (setting_is_changeable(pset, caller, NULL, 0)) {
296 /* setting can be changed */
297 return '+';
300 /* setting is fixed */
301 return ' ';
304 /**************************************************************************
305 feedback related to server commands
306 caller == NULL means console.
307 No longer duplicate all output to console.
309 This lowlevel function takes a single line; prefix is prepended to line.
310 **************************************************************************/
311 static void cmd_reply_line(enum command_id cmd, struct connection *caller,
312 enum rfc_status rfc_status, const char *prefix,
313 const char *line)
315 const char *cmdname = cmd < CMD_NUM
316 ? command_name_by_number(cmd)
317 : cmd == CMD_AMBIGUOUS
318 /* TRANS: ambiguous command */
319 ? _("(ambiguous)")
320 : cmd == CMD_UNRECOGNIZED
321 /* TRANS: unrecognized command */
322 ? _("(unknown)")
323 : "(?!?)"; /* this case is a bug! */
325 if (caller) {
326 notify_conn(caller->self, NULL, E_SETTING, ftc_command,
327 "/%s: %s%s", cmdname, prefix, line);
328 /* cc: to the console - testing has proved it's too verbose - rp
329 con_write(rfc_status, "%s/%s: %s%s", caller->name, cmdname, prefix, line);
331 } else {
332 con_write(rfc_status, "%s%s", prefix, line);
335 if (rfc_status == C_OK) {
336 struct packet_chat_msg packet;
338 package_event(&packet, NULL, E_SETTING, ftc_server, "%s", line);
339 conn_list_iterate(game.est_connections, pconn) {
340 /* Do not tell caller, since he was told above! */
341 if (caller != pconn) {
342 send_packet_chat_msg(pconn, &packet);
344 } conn_list_iterate_end;
345 event_cache_add_for_all(&packet);
347 if (NULL != caller) {
348 /* Echo to the console. */
349 log_normal("%s", line);
354 /**************************************************************************
355 va_list version which allow embedded newlines, and each line is sent
356 separately. 'prefix' is prepended to every line _after_ the first line.
357 **************************************************************************/
358 static void vcmd_reply_prefix(enum command_id cmd, struct connection *caller,
359 enum rfc_status rfc_status, const char *prefix,
360 const char *format, va_list ap)
362 char buf[4096];
363 char *c0, *c1;
365 fc_vsnprintf(buf, sizeof(buf), format, ap);
367 c0 = buf;
368 while ((c1=strstr(c0, "\n"))) {
369 *c1 = '\0';
370 cmd_reply_line(cmd, caller, rfc_status, (c0==buf?"":prefix), c0);
371 c0 = c1+1;
373 cmd_reply_line(cmd, caller, rfc_status, (c0==buf?"":prefix), c0);
376 /**************************************************************************
377 var-args version of above
378 duplicate declaration required for attribute to work...
379 **************************************************************************/
380 static void cmd_reply_prefix(enum command_id cmd, struct connection *caller,
381 enum rfc_status rfc_status, const char *prefix,
382 const char *format, ...)
383 fc__attribute((__format__ (__printf__, 5, 6)));
384 static void cmd_reply_prefix(enum command_id cmd, struct connection *caller,
385 enum rfc_status rfc_status, const char *prefix,
386 const char *format, ...)
388 va_list ap;
389 va_start(ap, format);
390 vcmd_reply_prefix(cmd, caller, rfc_status, prefix, format, ap);
391 va_end(ap);
394 /**************************************************************************
395 var-args version as above, no prefix
396 **************************************************************************/
397 void cmd_reply(enum command_id cmd, struct connection *caller,
398 enum rfc_status rfc_status, const char *format, ...)
400 va_list ap;
401 va_start(ap, format);
402 vcmd_reply_prefix(cmd, caller, rfc_status, "", format, ap);
403 va_end(ap);
406 /**************************************************************************
407 Command specific argument parsing has detected that player argument
408 is invalid. This function is common handling for that situation.
409 **************************************************************************/
410 static void cmd_reply_no_such_player(enum command_id cmd,
411 struct connection *caller,
412 const char *name,
413 enum m_pre_result match_result)
415 switch(match_result) {
416 case M_PRE_EMPTY:
417 cmd_reply(cmd, caller, C_SYNTAX,
418 _("Name is empty, so cannot be a player."));
419 break;
420 case M_PRE_LONG:
421 cmd_reply(cmd, caller, C_SYNTAX,
422 _("Name is too long, so cannot be a player."));
423 break;
424 case M_PRE_AMBIGUOUS:
425 cmd_reply(cmd, caller, C_FAIL,
426 _("Player name prefix '%s' is ambiguous."), name);
427 break;
428 case M_PRE_FAIL:
429 cmd_reply(cmd, caller, C_FAIL,
430 _("No player by the name of '%s'."), name);
431 break;
432 default:
433 cmd_reply(cmd, caller, C_FAIL,
434 _("Unexpected match_result %d (%s) for '%s'."),
435 match_result, _(m_pre_description(match_result)), name);
436 log_error("Unexpected match_result %d (%s) for '%s'.",
437 match_result, m_pre_description(match_result), name);
441 /**************************************************************************
442 Command specific argument parsing has detected that connection argument
443 is invalid. This function is common handling for that situation.
444 **************************************************************************/
445 static void cmd_reply_no_such_conn(enum command_id cmd,
446 struct connection *caller,
447 const char *name,
448 enum m_pre_result match_result)
450 switch(match_result) {
451 case M_PRE_EMPTY:
452 cmd_reply(cmd, caller, C_SYNTAX,
453 _("Name is empty, so cannot be a connection."));
454 break;
455 case M_PRE_LONG:
456 cmd_reply(cmd, caller, C_SYNTAX,
457 _("Name is too long, so cannot be a connection."));
458 break;
459 case M_PRE_AMBIGUOUS:
460 cmd_reply(cmd, caller, C_FAIL,
461 _("Connection name prefix '%s' is ambiguous."), name);
462 break;
463 case M_PRE_FAIL:
464 cmd_reply(cmd, caller, C_FAIL,
465 _("No connection by the name of '%s'."), name);
466 break;
467 default:
468 cmd_reply(cmd, caller, C_FAIL,
469 _("Unexpected match_result %d (%s) for '%s'."),
470 match_result, _(m_pre_description(match_result)), name);
471 log_error("Unexpected match_result %d (%s) for '%s'.",
472 match_result, m_pre_description(match_result), name);
476 /**************************************************************************
477 Start sending game info to metaserver.
478 **************************************************************************/
479 static void open_metaserver_connection(struct connection *caller)
481 server_open_meta();
482 if (send_server_info_to_metaserver(META_INFO)) {
483 cmd_reply(CMD_METACONN, caller, C_OK,
484 _("Open metaserver connection to [%s]."),
485 meta_addr_port());
489 /**************************************************************************
490 Stop sending game info to metaserver.
491 **************************************************************************/
492 static void close_metaserver_connection(struct connection *caller)
494 if (send_server_info_to_metaserver(META_GOODBYE)) {
495 server_close_meta();
496 cmd_reply(CMD_METACONN, caller, C_OK,
497 _("Close metaserver connection to [%s]."),
498 meta_addr_port());
502 /**************************************************************************
503 Handle metaconnection command.
504 **************************************************************************/
505 static bool metaconnection_command(struct connection *caller, char *arg,
506 bool check)
508 if ((*arg == '\0') ||
509 (0 == strcmp (arg, "?"))) {
510 if (is_metaserver_open()) {
511 cmd_reply(CMD_METACONN, caller, C_COMMENT,
512 _("Metaserver connection is open."));
513 } else {
514 cmd_reply(CMD_METACONN, caller, C_COMMENT,
515 _("Metaserver connection is closed."));
517 } else if (0 == fc_strcasecmp(arg, "u")
518 || 0 == fc_strcasecmp(arg, "up")) {
519 if (!is_metaserver_open()) {
520 if (!check) {
521 open_metaserver_connection(caller);
523 } else {
524 cmd_reply(CMD_METACONN, caller, C_METAERROR,
525 _("Metaserver connection is already open."));
526 return FALSE;
528 } else if (0 == fc_strcasecmp(arg, "d")
529 || 0 == fc_strcasecmp(arg, "down")) {
530 if (is_metaserver_open()) {
531 if (!check) {
532 close_metaserver_connection(caller);
534 } else {
535 cmd_reply(CMD_METACONN, caller, C_METAERROR,
536 _("Metaserver connection is already closed."));
537 return FALSE;
539 } else {
540 cmd_reply(CMD_METACONN, caller, C_METAERROR,
541 _("Argument must be 'u', 'up', 'd', 'down', or '?'."));
542 return FALSE;
544 return TRUE;
547 /**************************************************************************
548 Handle metapatches command.
549 **************************************************************************/
550 static bool metapatches_command(struct connection *caller,
551 char *arg, bool check)
553 if (check) {
554 return TRUE;
557 set_meta_patches_string(arg);
559 if (is_metaserver_open()) {
560 send_server_info_to_metaserver(META_INFO);
561 cmd_reply(CMD_METAPATCHES, caller, C_OK,
562 _("Metaserver patches string set to '%s'."), arg);
563 } else {
564 cmd_reply(CMD_METAPATCHES, caller, C_OK,
565 _("Metaserver patches string set to '%s', "
566 "not reporting to metaserver."), arg);
569 return TRUE;
572 /**************************************************************************
573 Handle metamessage command.
574 **************************************************************************/
575 static bool metamessage_command(struct connection *caller,
576 char *arg, bool check)
578 if (check) {
579 return TRUE;
582 set_user_meta_message_string(arg);
583 if (is_metaserver_open()) {
584 send_server_info_to_metaserver(META_INFO);
585 cmd_reply(CMD_METAMESSAGE, caller, C_OK,
586 _("Metaserver message string set to '%s'."), arg);
587 } else {
588 cmd_reply(CMD_METAMESSAGE, caller, C_OK,
589 _("Metaserver message string set to '%s', "
590 "not reporting to metaserver."), arg);
593 return TRUE;
596 /**************************************************************************
597 Handle metaserver command.
598 **************************************************************************/
599 static bool metaserver_command(struct connection *caller, char *arg,
600 bool check)
602 if (check) {
603 return TRUE;
605 close_metaserver_connection(caller);
607 sz_strlcpy(srvarg.metaserver_addr, arg);
609 cmd_reply(CMD_METASERVER, caller, C_OK,
610 _("Metaserver is now [%s]."), meta_addr_port());
611 return TRUE;
614 /**************************************************************************
615 Returns the serverid
616 **************************************************************************/
617 static bool show_serverid(struct connection *caller, char *arg)
619 cmd_reply(CMD_SRVID, caller, C_COMMENT, _("Server id: %s"), srvarg.serverid);
621 return TRUE;
624 /***************************************************************
625 Returns handicap bitvector for given AI skill level
626 ***************************************************************/
627 static bv_handicap handicap_of_skill_level(int level)
629 bv_handicap handicap;
631 fc_assert(level > 0 && level <= 10);
633 BV_CLR_ALL(handicap);
635 switch (level) {
636 case AI_LEVEL_AWAY:
637 BV_SET(handicap, H_AWAY);
638 BV_SET(handicap, H_FOG);
639 BV_SET(handicap, H_MAP);
640 BV_SET(handicap, H_RATES);
641 BV_SET(handicap, H_TARGETS);
642 BV_SET(handicap, H_HUTS);
643 BV_SET(handicap, H_REVOLUTION);
644 break;
645 case AI_LEVEL_NOVICE:
646 BV_SET(handicap, H_RATES);
647 BV_SET(handicap, H_TARGETS);
648 BV_SET(handicap, H_HUTS);
649 BV_SET(handicap, H_NOPLANES);
650 BV_SET(handicap, H_DIPLOMAT);
651 BV_SET(handicap, H_LIMITEDHUTS);
652 BV_SET(handicap, H_DEFENSIVE);
653 BV_SET(handicap, H_REVOLUTION);
654 BV_SET(handicap, H_EXPANSION);
655 BV_SET(handicap, H_DANGER);
656 break;
657 case AI_LEVEL_EASY:
658 BV_SET(handicap, H_RATES);
659 BV_SET(handicap, H_TARGETS);
660 BV_SET(handicap, H_HUTS);
661 BV_SET(handicap, H_NOPLANES);
662 BV_SET(handicap, H_DIPLOMAT);
663 BV_SET(handicap, H_LIMITEDHUTS);
664 BV_SET(handicap, H_DEFENSIVE);
665 BV_SET(handicap, H_DIPLOMACY);
666 BV_SET(handicap, H_REVOLUTION);
667 BV_SET(handicap, H_EXPANSION);
668 break;
669 case AI_LEVEL_NORMAL:
670 BV_SET(handicap, H_RATES);
671 BV_SET(handicap, H_TARGETS);
672 BV_SET(handicap, H_HUTS);
673 BV_SET(handicap, H_DIPLOMAT);
674 break;
675 case AI_LEVEL_EXPERIMENTAL:
676 BV_SET(handicap, H_EXPERIMENTAL);
677 break;
678 case AI_LEVEL_CHEATING:
679 BV_SET(handicap, H_RATES);
680 break;
683 return handicap;
686 /**************************************************************************
687 Return the AI fuzziness (0 to 1000) corresponding to a given skill
688 level (1 to 10). See ai_fuzzy() in common/player.c
689 **************************************************************************/
690 static int fuzzy_of_skill_level(int level)
692 int f[11] = { -1, 0, 400/*novice*/, 300/*easy*/, 0, 0, 0, 0, 0, 0, 0 };
694 fc_assert_ret_val(level > 0 && level <= 10, 0);
695 return f[level];
698 /**************************************************************************
699 Return the AI's science development cost; a science development cost of 100
700 means that the AI develops science at the same speed as a human; a science
701 development cost of 200 means that the AI develops science at half the speed
702 of a human, and a sceence development cost of 50 means that the AI develops
703 science twice as fast as the human
704 **************************************************************************/
705 static int science_cost_of_skill_level(int level)
707 int x[11] = { -1, 100, 250/*novice*/, 100/*easy*/, 100, 100, 100, 100,
708 100, 100, 100 };
710 fc_assert_ret_val(level > 0 && level <= 10, 0);
711 return x[level];
714 /**************************************************************************
715 Return the AI expansion tendency, a percentage factor to value new cities,
716 compared to defaults. 0 means _never_ build new cities, > 100 means to
717 (over?)value them even more than the default (already expansionistic) AI.
718 **************************************************************************/
719 static int expansionism_of_skill_level(int level)
721 int x[11] = { -1, 100, 10/*novice*/, 10/*easy*/, 100, 100, 100, 100,
722 100, 100, 100 };
724 fc_assert_ret_val(level > 0 && level <= 10, 0);
725 return x[level];
728 /**************************************************************************
729 For command "save foo";
730 Save the game, with filename=arg, provided server state is ok.
731 **************************************************************************/
732 static bool save_command(struct connection *caller, char *arg, bool check)
734 if (is_restricted(caller)) {
735 cmd_reply(CMD_SAVE, caller, C_FAIL,
736 _("You cannot save games manually on this server."));
737 return FALSE;
739 if (!check) {
740 save_game(arg, "User request", FALSE);
742 return TRUE;
745 /**************************************************************************
746 For command "scensave foo";
747 Save the game, with filename=arg, provided server state is ok.
748 **************************************************************************/
749 #ifdef DEBUG
750 static bool scensave_command(struct connection *caller, char *arg, bool check)
752 if (is_restricted(caller)) {
753 cmd_reply(CMD_SAVE, caller, C_FAIL,
754 _("You cannot save games manually on this server."));
755 return FALSE;
757 if (!check) {
758 save_game(arg, "Scenario", TRUE);
760 return TRUE;
762 #endif /* DEBUG */
764 /**************************************************************************
765 Handle ai player ai toggling.
766 **************************************************************************/
767 void toggle_ai_player_direct(struct connection *caller, struct player *pplayer)
769 fc_assert_ret(pplayer != NULL);
770 if (is_barbarian(pplayer)) {
771 cmd_reply(CMD_AITOGGLE, caller, C_FAIL,
772 _("Cannot toggle a barbarian player."));
773 return;
776 pplayer->ai_controlled = !pplayer->ai_controlled;
777 if (pplayer->ai_controlled) {
778 cmd_reply(CMD_AITOGGLE, caller, C_OK,
779 _("%s is now under AI control."),
780 player_name(pplayer));
781 if (pplayer->ai_common.skill_level == 0) {
782 pplayer->ai_common.skill_level = game.info.skill_level;
784 /* Set the skill level explicitly, because eg: the player skill
785 level could have been set as AI, then toggled, then saved,
786 then reloaded. */
787 set_ai_level(caller, player_name(pplayer),
788 pplayer->ai_common.skill_level, FALSE);
789 /* the AI can't do active diplomacy */
790 cancel_all_meetings(pplayer);
792 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
794 if (S_S_RUNNING == server_state()) {
795 /* In case this was last player who has not pressed turn done. */
796 check_for_full_turn_done();
798 } else {
799 cmd_reply(CMD_AITOGGLE, caller, C_OK,
800 _("%s is now under human control."),
801 player_name(pplayer));
803 CALL_PLR_AI_FUNC(lost_control, pplayer, pplayer);
805 /* because the AI `cheats' with government rates but humans shouldn't */
806 if (!game.info.is_new_game) {
807 check_player_max_rates(pplayer);
809 /* Remove hidden dialogs from clients. This way the player can initiate
810 * new meeting */
811 cancel_all_meetings(pplayer);
815 /**************************************************************************
816 Handle aitoggle command.
817 **************************************************************************/
818 static bool toggle_ai_command(struct connection *caller, char *arg, bool check)
820 enum m_pre_result match_result;
821 struct player *pplayer;
823 pplayer = player_by_name_prefix(arg, &match_result);
825 if (!pplayer) {
826 cmd_reply_no_such_player(CMD_AITOGGLE, caller, arg, match_result);
827 return FALSE;
828 } else if (!check) {
829 toggle_ai_player_direct(caller, pplayer);
830 send_player_info_c(pplayer, game.est_connections);
832 return TRUE;
835 /**************************************************************************
836 Creates a named AI player. The function can be called befor the start
837 of the game (see creat_command_pregame()) and for a running game
838 (see creat_command_newcomer(). In the later case, first free player slots
839 are used before the slots of dead players are (re)used.
840 **************************************************************************/
841 static bool create_command(struct connection *caller, const char *str,
842 bool check)
844 enum rfc_status status;
845 char buf[MAX_LEN_CONSOLE_LINE];
847 /* 2 legal arguments, and extra space for stuffing illegal part */
848 char *arg[3];
849 int ntokens;
850 const char *ai_type_name;
852 sz_strlcpy(buf, str);
853 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
855 if (ntokens == 1) {
856 ai_type_name = default_ai_type_name();
857 } else if (ntokens == 2) {
858 ai_type_name = arg[1];
859 } else {
860 cmd_reply(CMD_CREATE, caller, C_SYNTAX,
861 _("Wrong number of arguments to create command."));
862 return FALSE;
865 if (game_was_started()) {
866 status = create_command_newcomer(arg[0], ai_type_name, check,
867 NULL, NULL, buf, sizeof(buf));
868 } else {
869 status = create_command_pregame(arg[0], ai_type_name, check,
870 NULL, buf, sizeof(buf));
873 if (status != C_OK) {
874 /* No player created. */
875 cmd_reply(CMD_CREATE, caller, status, "%s", buf);
876 return FALSE;
879 if (strlen(buf) > 0) {
880 /* Send a notification. */
881 cmd_reply(CMD_CREATE, caller, C_OK, "%s", buf);
884 return TRUE;
887 /**************************************************************************
888 Try to add a player to a running game in the following order:
890 1. Try to reuse the slot of a dead player with the username 'name'.
891 2. Try to reuse the slot of a dead player.
892 3. Try to use an empty player slot.
894 If 'pnation' is defined this nation is used for the new player.
895 **************************************************************************/
896 enum rfc_status create_command_newcomer(const char *name,
897 const char *ai,
898 bool check,
899 struct nation_type *pnation,
900 struct player **newplayer,
901 char *buf, size_t buflen)
903 struct player *pplayer = NULL;
904 bool new_slot = FALSE;
906 /* Check player name. */
907 if (!player_name_check(name, buf, buflen)) {
908 return C_SYNTAX;
911 /* Check first if we can replace a player with
912 * [1a] - the same username. */
913 pplayer = player_by_user(name);
914 if (pplayer && pplayer->is_alive) {
915 fc_snprintf(buf, buflen,
916 _("A living user already exists by that name."));
917 return C_BOUNCE;
920 /* [1b] - the same player name. */
921 pplayer = player_by_name(name);
922 if (pplayer && pplayer->is_alive) {
923 fc_snprintf(buf, buflen,
924 _("A living player already exists by that name."));
925 return C_BOUNCE;
928 if (pnation) {
929 if (!nation_is_in_current_set(pnation)) {
930 fc_snprintf(buf, buflen,
931 _("Can't create player, requested nation %s not in "
932 "current nationset."),
933 nation_plural_translation(pnation));
934 return C_FAIL;
936 players_iterate(aplayer) {
937 if (0 > nations_match(pnation, nation_of_player(aplayer), FALSE)) {
938 fc_snprintf(buf, buflen,
939 _("Can't create players, nation %s conflicts with %s."),
940 nation_plural_for_player(aplayer),
941 nation_plural_for_player(pplayer));
942 return C_FAIL;
944 } players_iterate_end;
945 } else {
946 /* Try to find a nation. */
947 pnation = pick_a_nation(NULL, FALSE, TRUE, NOT_A_BARBARIAN);
948 if (pnation == NO_NATION_SELECTED) {
949 fc_snprintf(buf, buflen,
950 _("Can't create players, no nations available."));
951 return C_FAIL;
955 if (check) {
956 /* All code below will change the game state. */
958 /* Return an empty string. */
959 buf[0] = '\0';
961 return C_OK;
964 if (pplayer) {
965 /* [1] Replace a player. 'pplayer' was set above. */
966 fc_snprintf(buf, buflen,
967 _("%s is replacing dead player %s as an AI-controlled "
968 "player."), name, player_name(pplayer));
969 /* remove player and thus free a player slot */
970 server_remove_player(pplayer);
971 pplayer = NULL;
972 } else if (player_count() == player_slot_count()) {
973 /* [2] All player slots are used; try to remove a dead player. */
974 players_iterate(aplayer) {
975 if (!aplayer->is_alive) {
976 fc_snprintf(buf, buflen,
977 _("%s is replacing dead player %s as an AI-controlled "
978 "player."), name, player_name(aplayer));
979 /* remove player and thus free a player slot */
980 server_remove_player(aplayer);
982 } players_iterate_end;
983 } else {
984 /* [3] An empty player slot must be used for the new player. */
985 new_slot = TRUE;
988 if (new_slot) {
989 if (normal_player_count() == game.server.max_players) {
991 fc_assert(game.server.max_players < MAX_NUM_PLAYERS);
993 game.server.max_players++;
994 log_debug("Increased 'maxplayers' for creation of a new player.");
998 /* Create the new player. */
999 pplayer = server_create_player(-1, ai, NULL);
1000 if (!pplayer) {
1001 fc_snprintf(buf, buflen, _("Failed to create new player %s."), name);
1002 return C_FAIL;
1005 if (new_slot) {
1006 /* 'buf' must be set if a new player slot is used. */
1007 fc_snprintf(buf, buflen, _("New player %s created."), name);
1010 /* We have a player; now initialise all needed data. */
1011 aifill(game.info.aifill);
1013 /* Initialise player. */
1014 server_player_init(pplayer, TRUE, TRUE);
1016 player_set_nation(pplayer, pnation);
1017 pplayer->government = pnation->init_government;
1018 pplayer->target_government = pnation->init_government;
1019 /* Find a color for the new player. */
1020 assign_player_colors();
1022 /* TRANS: keep one space at the beginning of the string. */
1023 cat_snprintf(buf, buflen, _(" Nation of the new player: %s."),
1024 nation_rule_name(pnation));
1026 init_tech(pplayer, TRUE);
1027 give_global_initial_techs(pplayer);
1028 give_nation_initial_techs(pplayer);
1030 server_player_set_name(pplayer, name);
1031 sz_strlcpy(pplayer->username, ANON_USER_NAME);
1033 pplayer->was_created = TRUE; /* must use /remove explicitly to remove */
1034 pplayer->ai_controlled = TRUE;
1035 set_ai_level_directer(pplayer, game.info.skill_level);
1037 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
1039 send_player_info_c(pplayer, NULL);
1040 /* Send updated diplstate information to all players. */
1041 send_player_diplstate_c(NULL, NULL);
1042 (void) send_server_info_to_metaserver(META_INFO);
1044 if (newplayer != NULL) {
1045 *newplayer = pplayer;
1047 return C_OK;
1050 /**************************************************************************
1051 Create player in pregame.
1052 **************************************************************************/
1053 enum rfc_status create_command_pregame(const char *name,
1054 const char *ai,
1055 bool check,
1056 struct player **newplayer,
1057 char *buf, size_t buflen)
1059 struct player *pplayer = NULL;
1061 if (!player_name_check(name, buf, buflen)) {
1062 return C_SYNTAX;
1065 if (NULL != player_by_name(name)) {
1066 fc_snprintf(buf, buflen,
1067 _("A player already exists by that name."));
1068 return C_BOUNCE;
1070 if (NULL != player_by_user(name)) {
1071 fc_snprintf(buf, buflen,
1072 _("A user already exists by that name."));
1073 return C_BOUNCE;
1076 /* Search for first uncontrolled player */
1077 pplayer = find_uncontrolled_player();
1079 if (NULL == pplayer) {
1080 /* Check that we are not going over max players setting */
1081 if (normal_player_count() >= game.server.max_players) {
1082 fc_snprintf(buf, buflen,
1083 _("Can't add more players, server is full."));
1084 return C_FAIL;
1086 /* Check that we have nations available */
1087 if (normal_player_count() >= server.playable_nations) {
1088 fc_snprintf(buf, buflen,
1089 _("Can't add more players, not enough nations."));
1090 return C_FAIL;
1094 if (pplayer) {
1095 struct ai_type *ait = ai_type_by_name(ai);
1097 if (ait == NULL) {
1098 fc_snprintf(buf, buflen,
1099 _("There is no AI type %s."), ai);
1100 return C_FAIL;
1104 if (check) {
1105 /* All code below will change the game state. */
1107 /* Return an empty string. */
1108 buf[0] = '\0';
1110 return C_OK;
1113 if (pplayer) {
1114 fc_snprintf(buf, buflen,
1115 /* TRANS: <name> replacing <name> ... */
1116 _("%s replacing %s as an AI-controlled player."),
1117 name, player_name(pplayer));
1119 team_remove_player(pplayer);
1120 pplayer->ai = ai_type_by_name(ai);
1121 } else {
1122 /* add new player */
1123 pplayer = server_create_player(-1, ai, NULL);
1124 /* pregame so no need to assign_player_colors() */
1125 if (!pplayer) {
1126 fc_snprintf(buf, buflen,
1127 _("Failed to create new player %s."), name);
1128 return C_GENFAIL;
1131 fc_snprintf(buf, buflen,
1132 _("%s has been added as an AI-controlled player (%s)."),
1133 name, ai_name(pplayer->ai));
1135 server_player_init(pplayer, FALSE, TRUE);
1137 server_player_set_name(pplayer, name);
1138 sz_strlcpy(pplayer->username, ANON_USER_NAME);
1140 pplayer->was_created = TRUE; /* must use /remove explicitly to remove */
1141 pplayer->ai_controlled = TRUE;
1142 set_ai_level_directer(pplayer, game.info.skill_level);
1143 CALL_PLR_AI_FUNC(gained_control, pplayer, pplayer);
1144 send_player_info_c(pplayer, game.est_connections);
1146 aifill(game.info.aifill);
1147 reset_all_start_commands();
1148 (void) send_server_info_to_metaserver(META_INFO);
1150 if (newplayer != NULL) {
1151 *newplayer = pplayer;
1153 return C_OK;
1156 /**************************************************************************
1157 Handle remove command.
1158 **************************************************************************/
1159 static bool remove_player_command(struct connection *caller, char *arg,
1160 bool check)
1162 enum m_pre_result match_result;
1163 struct player *pplayer;
1164 char name[MAX_LEN_NAME];
1166 pplayer = player_by_name_prefix(arg, &match_result);
1168 if (NULL == pplayer) {
1169 cmd_reply_no_such_player(CMD_REMOVE, caller, arg, match_result);
1170 return FALSE;
1173 if (game_was_started() && caller && caller->access_level < ALLOW_ADMIN) {
1174 cmd_reply(CMD_REMOVE, caller, C_FAIL,
1175 _("Command level '%s' or greater needed to remove a player "
1176 "once the game has started."), cmdlevel_name(ALLOW_ADMIN));
1177 return FALSE;
1179 if (check) {
1180 return TRUE;
1183 sz_strlcpy(name, player_name(pplayer));
1184 server_remove_player(pplayer);
1185 if (!caller || caller->used) { /* may have removed self */
1186 cmd_reply(CMD_REMOVE, caller, C_OK,
1187 _("Removed player %s from the game."), name);
1189 aifill(game.info.aifill);
1190 return TRUE;
1193 /**************************************************************************
1194 Main entry point for the read command.
1195 **************************************************************************/
1196 static bool read_command(struct connection *caller, char *arg, bool check,
1197 int read_recursion)
1199 return read_init_script_real(caller, arg, FALSE, check, read_recursion);
1202 /**************************************************************************
1203 Main entry point for reading an init script.
1204 **************************************************************************/
1205 bool read_init_script(struct connection *caller, char *script_filename,
1206 bool from_cmdline, bool check)
1208 return read_init_script_real(caller, script_filename, from_cmdline,
1209 check, 0);
1212 /**************************************************************************
1213 Returns FALSE iff there was an error.
1215 Security: We will look for a file with mandatory extension '.serv',
1216 and on public servers we will not look outside the data directories.
1217 As long as the user cannot create files with arbitrary names in the
1218 root of the data directories, this should ensure that we will not be
1219 tricked into loading non-approved content. The script is read with the
1220 permissions of the caller, so it will in any case not lead to elevated
1221 permissions unless there are other bugs.
1222 **************************************************************************/
1223 static bool read_init_script_real(struct connection *caller,
1224 char *script_filename, bool from_cmdline,
1225 bool check, int read_recursion)
1227 FILE *script_file;
1228 const char extension[] = ".serv";
1229 char serv_filename[strlen(extension) + strlen(script_filename) + 2];
1230 char tilde_filename[4096];
1231 const char *real_filename;
1233 /* check recursion depth */
1234 if (read_recursion > GAME_MAX_READ_RECURSION) {
1235 log_error("Error: recursive calls to read!");
1236 return FALSE;
1239 /* abuse real_filename to find if we already have a .serv extension */
1240 real_filename = script_filename + strlen(script_filename)
1241 - MIN(strlen(extension), strlen(script_filename));
1242 if (strcmp(real_filename, extension) != 0) {
1243 fc_snprintf(serv_filename, sizeof(serv_filename), "%s%s",
1244 script_filename, extension);
1245 } else {
1246 sz_strlcpy(serv_filename, script_filename);
1249 if (is_restricted(caller) && !from_cmdline) {
1250 if (!is_safe_filename(serv_filename)) {
1251 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1252 _("Name \"%s\" disallowed for security reasons."),
1253 serv_filename);
1254 return FALSE;
1256 sz_strlcpy(tilde_filename, serv_filename);
1257 } else {
1258 interpret_tilde(tilde_filename, sizeof(tilde_filename), serv_filename);
1261 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
1262 if (!real_filename) {
1263 if (is_restricted(caller) && !from_cmdline) {
1264 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1265 _("No command script found by the name \"%s\"."),
1266 serv_filename);
1267 return FALSE;
1269 /* File is outside data directories */
1270 real_filename = tilde_filename;
1273 log_normal(_("Loading script file '%s'."), real_filename);
1275 if (is_reg_file_for_access(real_filename, FALSE)
1276 && (script_file = fc_fopen(real_filename, "r"))) {
1277 char buffer[MAX_LEN_CONSOLE_LINE];
1279 /* the size is set as to not overflow buffer in handle_stdin_input */
1280 while (fgets(buffer, MAX_LEN_CONSOLE_LINE - 1, script_file)) {
1281 /* Execute script contents with same permissions as caller */
1282 handle_stdin_input_real(caller, buffer, check, read_recursion + 1);
1284 fclose(script_file);
1286 show_changed(caller, check, read_recursion);
1288 return TRUE;
1289 } else {
1290 cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
1291 _("Cannot read command line scriptfile '%s'."), real_filename);
1292 if (NULL != caller) {
1293 log_error(_("Could not read script file '%s'."), real_filename);
1295 return FALSE;
1299 /**************************************************************************
1300 Write current settings to new init script.
1302 (Should this take a 'caller' argument for output? --dwp)
1303 **************************************************************************/
1304 static void write_init_script(char *script_filename)
1306 char real_filename[1024], buf[256];
1307 FILE *script_file;
1309 interpret_tilde(real_filename, sizeof(real_filename), script_filename);
1311 if (is_reg_file_for_access(real_filename, TRUE)
1312 && (script_file = fc_fopen(real_filename, "w"))) {
1313 fprintf(script_file,
1314 "#FREECIV SERVER COMMAND FILE, version %s\n", VERSION_STRING);
1315 fputs("# These are server options saved from a running freeciv-server.\n",
1316 script_file);
1318 /* first, some state info from commands (we can't save everything) */
1320 fprintf(script_file, "cmdlevel %s new\n",
1321 cmdlevel_name(default_access_level));
1323 fprintf(script_file, "cmdlevel %s first\n",
1324 cmdlevel_name(first_access_level));
1326 fprintf(script_file, "%s\n",
1327 ai_level_cmd(game.info.skill_level));
1329 if (*srvarg.metaserver_addr != '\0' &&
1330 ((0 != strcmp(srvarg.metaserver_addr, DEFAULT_META_SERVER_ADDR)))) {
1331 fprintf(script_file, "metaserver %s\n", meta_addr_port());
1334 if (0 != strcmp(get_meta_patches_string(), default_meta_patches_string())) {
1335 fprintf(script_file, "metapatches %s\n", get_meta_patches_string());
1337 if (0 != strcmp(get_meta_message_string(), default_meta_message_string())) {
1338 fprintf(script_file, "metamessage %s\n", get_meta_message_string());
1341 /* then, the 'set' option settings */
1343 settings_iterate(SSET_ALL, pset) {
1344 fprintf(script_file, "set %s \"%s\"\n", setting_name(pset),
1345 setting_value_name(pset, FALSE, buf, sizeof(buf)));
1346 } settings_iterate_end;
1348 /* rulesetdir */
1349 fprintf(script_file, "rulesetdir %s\n", game.server.rulesetdir);
1351 fclose(script_file);
1353 } else {
1354 log_error(_("Could not write script file '%s'."), real_filename);
1358 /**************************************************************************
1359 Generate init script from settings currently in use
1360 **************************************************************************/
1361 static bool write_command(struct connection *caller, char *arg, bool check)
1363 if (is_restricted(caller)) {
1364 cmd_reply(CMD_WRITE_SCRIPT, caller, C_FAIL,
1365 _("You cannot use the write command on this server"
1366 " for security reasons."));
1367 return FALSE;
1368 } else if (!check) {
1369 write_init_script(arg);
1371 return TRUE;
1374 /**************************************************************************
1375 set ptarget's cmdlevel to level if caller is allowed to do so
1376 **************************************************************************/
1377 static bool set_cmdlevel(struct connection *caller,
1378 struct connection *ptarget,
1379 enum cmdlevel level)
1381 /* Only ever call me for specific connection. */
1382 fc_assert_ret_val(ptarget != NULL, FALSE);
1384 if (caller && ptarget->access_level > caller->access_level) {
1386 * This command is intended to be used at ctrl access level
1387 * and thus this if clause is needed.
1388 * (Imagine a ctrl level access player that wants to change
1389 * access level of a hack level access player)
1390 * At the moment it can be used only by hack access level
1391 * and thus this clause is never used.
1393 cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1394 _("Cannot decrease command access level '%s' "
1395 "for connection '%s'; you only have '%s'."),
1396 cmdlevel_name(ptarget->access_level),
1397 ptarget->username,
1398 cmdlevel_name(caller->access_level));
1399 return FALSE;
1400 } else {
1401 conn_set_access(ptarget, level, TRUE);
1402 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1403 _("Command access level set to '%s' for connection %s."),
1404 cmdlevel_name(level), ptarget->username);
1405 return TRUE;
1409 /********************************************************************
1410 Returns true if there is at least one established connection.
1411 *********************************************************************/
1412 static bool a_connection_exists(void)
1414 return conn_list_size(game.est_connections) > 0;
1417 /********************************************************************
1418 Return whether first access level is already taken.
1419 *********************************************************************/
1420 static bool is_first_access_level_taken(void)
1422 conn_list_iterate(game.est_connections, pconn) {
1423 if (pconn->access_level >= first_access_level) {
1424 return TRUE;
1427 conn_list_iterate_end;
1428 return FALSE;
1431 /********************************************************************
1432 Return access level for next connection
1433 *********************************************************************/
1434 enum cmdlevel access_level_for_next_connection(void)
1436 if ((first_access_level > default_access_level)
1437 && !a_connection_exists()) {
1438 return first_access_level;
1439 } else {
1440 return default_access_level;
1444 /********************************************************************
1445 Check if first access level is available and if it is, notify
1446 connections about it.
1447 *********************************************************************/
1448 void notify_if_first_access_level_is_available(void)
1450 if (first_access_level > default_access_level
1451 && !is_first_access_level_taken()) {
1452 notify_conn(NULL, NULL, E_SETTING, ftc_any,
1453 _("Anyone can now become game organizer "
1454 "'%s' by issuing the 'first' command."),
1455 cmdlevel_name(first_access_level));
1459 /**************************************************************************
1460 Change command access level for individual player, or all, or new.
1461 **************************************************************************/
1462 static bool cmdlevel_command(struct connection *caller, char *str, bool check)
1464 char *arg[2];
1465 int ntokens;
1466 bool ret = FALSE;
1467 enum m_pre_result match_result;
1468 enum cmdlevel level;
1469 struct connection *ptarget;
1471 ntokens = get_tokens(str, arg, 2, TOKEN_DELIMITERS);
1473 if (ntokens == 0) {
1474 /* No argument supplied; list the levels */
1475 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1476 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1477 _("Command access levels in effect:"));
1478 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1479 conn_list_iterate(game.est_connections, pconn) {
1480 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, "cmdlevel %s %s",
1481 cmdlevel_name(conn_get_access(pconn)), pconn->username);
1482 } conn_list_iterate_end;
1483 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1484 _("Command access level for new connections: %s"),
1485 cmdlevel_name(default_access_level));
1486 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1487 _("Command access level for first player to take it: %s"),
1488 cmdlevel_name(first_access_level));
1489 cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1490 return TRUE;
1493 /* A level name was supplied; set the level. */
1494 level = cmdlevel_by_name(arg[0], fc_strcasecmp);
1495 if (!cmdlevel_is_valid(level)) {
1496 const char *cmdlevel_names[CMDLEVEL_COUNT];
1497 struct astring astr = ASTRING_INIT;
1498 int i = 0;
1500 for (level = cmdlevel_begin(); level != cmdlevel_end();
1501 level = cmdlevel_next(level)) {
1502 cmdlevel_names[i++] = cmdlevel_name(level);
1504 cmd_reply(CMD_CMDLEVEL, caller, C_SYNTAX,
1505 /* TRANS: comma and 'or' separated list of access levels */
1506 _("Command access level must be one of %s."),
1507 astr_build_or_list(&astr, cmdlevel_names, i));
1508 astr_free(&astr);
1509 goto CLEAN_UP;
1510 } else if (caller && level > conn_get_access(caller)) {
1511 cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1512 _("Cannot increase command access level to '%s';"
1513 " you only have '%s' yourself."),
1514 arg[0], cmdlevel_name(conn_get_access(caller)));
1515 goto CLEAN_UP;
1518 if (check) {
1519 return TRUE; /* looks good */
1522 if (ntokens == 1) {
1523 /* No playername supplied: set for all connections */
1524 conn_list_iterate(game.est_connections, pconn) {
1525 if (pconn != caller) {
1526 (void) set_cmdlevel(caller, pconn, level);
1528 } conn_list_iterate_end;
1530 /* Set the caller access level at last, because it could make the
1531 * previous operations impossible if set before. */
1532 if (caller) {
1533 (void) set_cmdlevel(caller, caller, level);
1536 /* Set default access for new connections. */
1537 default_access_level = level;
1538 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1539 _("Command access level set to '%s' for new players."),
1540 cmdlevel_name(level));
1541 /* Set default access for first connection. */
1542 first_access_level = level;
1543 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1544 _("Command access level set to '%s' "
1545 "for first player to grab it."),
1546 cmdlevel_name(level));
1548 ret = TRUE;
1550 } else if (fc_strcasecmp(arg[1], "new") == 0) {
1551 default_access_level = level;
1552 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1553 _("Command access level set to '%s' for new players."),
1554 cmdlevel_name(level));
1555 if (level > first_access_level) {
1556 first_access_level = level;
1557 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1558 _("Command access level set to '%s' "
1559 "for first player to grab it."),
1560 cmdlevel_name(level));
1563 ret = TRUE;
1565 } else if (fc_strcasecmp(arg[1], "first") == 0) {
1566 first_access_level = level;
1567 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1568 _("Command access level set to '%s' "
1569 "for first player to grab it."),
1570 cmdlevel_name(level));
1571 if (level < default_access_level) {
1572 default_access_level = level;
1573 cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1574 _("Command access level set to '%s' for new players."),
1575 cmdlevel_name(level));
1578 ret = TRUE;
1580 } else if ((ptarget = conn_by_user_prefix(arg[1], &match_result))) {
1581 if (set_cmdlevel(caller, ptarget, level)) {
1582 ret = TRUE;
1584 } else {
1585 cmd_reply_no_such_conn(CMD_CMDLEVEL, caller, arg[1], match_result);
1588 CLEAN_UP:
1589 free_tokens(arg, ntokens);
1590 return ret;
1593 /**************************************************************************
1594 This special command to set the command access level is not included into
1595 cmdlevel_command because of its lower access level: it can be used
1596 to promote one's own connection to 'first come' cmdlevel if that isn't
1597 already taken.
1598 **************************************************************************/
1599 static bool firstlevel_command(struct connection *caller, bool check)
1601 if (!caller) {
1602 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1603 _("The 'first' command makes no sense from the server command line."));
1604 return FALSE;
1605 } else if (caller->access_level >= first_access_level) {
1606 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1607 _("You already have command access level '%s' or better."),
1608 cmdlevel_name(first_access_level));
1609 return FALSE;
1610 } else if (is_first_access_level_taken()) {
1611 cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
1612 _("Someone else is already game organizer."));
1613 return FALSE;
1614 } else if (!check) {
1615 conn_set_access(caller, first_access_level, FALSE);
1616 cmd_reply(CMD_FIRSTLEVEL, caller, C_OK,
1617 _("Connection %s has opted to become the game organizer."),
1618 caller->username);
1620 return TRUE;
1624 /**************************************************************************
1625 Returns possible parameters for the commands that take server options
1626 as parameters (CMD_EXPLAIN and CMD_SET).
1627 **************************************************************************/
1628 static const char *optname_accessor(int i)
1630 return setting_name(setting_by_number(i));
1633 #ifdef HAVE_LIBREADLINE
1634 /**************************************************************************
1635 Returns possible parameters for the /show command.
1636 **************************************************************************/
1637 static const char *olvlname_accessor(int i)
1639 if (i == 0) {
1640 return "rulesetdir";
1641 } else if (i < OLEVELS_NUM+1) {
1642 return sset_level_name(i-1);
1643 } else {
1644 return optname_accessor(i-OLEVELS_NUM-1);
1647 #endif /* HAVE_LIBREADLINE */
1649 /**************************************************************************
1650 Set timeout options.
1651 **************************************************************************/
1652 static bool timeout_command(struct connection *caller, char *str, bool check)
1654 char buf[MAX_LEN_CONSOLE_LINE];
1655 char *arg[4];
1656 int i = 0, ntokens;
1657 int *timeouts[4];
1659 timeouts[0] = &game.server.timeoutint;
1660 timeouts[1] = &game.server.timeoutintinc;
1661 timeouts[2] = &game.server.timeoutinc;
1662 timeouts[3] = &game.server.timeoutincmult;
1664 sz_strlcpy(buf, str);
1665 ntokens = get_tokens(buf, arg, 4, TOKEN_DELIMITERS);
1667 for (i = 0; i < ntokens; i++) {
1668 if (!str_to_int(arg[i], timeouts[i])) {
1669 cmd_reply(CMD_TIMEOUT, caller, C_FAIL, _("Invalid argument %d."),
1670 i + 1);
1672 free(arg[i]);
1675 if (ntokens == 0) {
1676 cmd_reply(CMD_TIMEOUT, caller, C_SYNTAX, _("Usage:\n%s"),
1677 command_synopsis(command_by_number(CMD_TIMEOUT)));
1678 return FALSE;
1679 } else if (check) {
1680 return TRUE;
1683 cmd_reply(CMD_TIMEOUT, caller, C_OK, _("Dynamic timeout set to "
1684 "%d %d %d %d"),
1685 game.server.timeoutint, game.server.timeoutintinc,
1686 game.server.timeoutinc, game.server.timeoutincmult);
1688 /* if we set anything here, reset the counter */
1689 game.server.timeoutcounter = 1;
1690 return TRUE;
1693 /**************************************************************************
1694 Find option level number by name.
1695 **************************************************************************/
1696 static enum sset_level lookup_option_level(const char *name)
1698 enum sset_level i;
1700 for (i = SSET_ALL; i < OLEVELS_NUM; i++) {
1701 if (0 == fc_strcasecmp(name, sset_level_name(i))) {
1702 return i;
1706 return SSET_NONE;
1709 /* Special return values of lookup options */
1710 #define LOOKUP_OPTION_NO_RESULT (-1)
1711 #define LOOKUP_OPTION_AMBIGUOUS (-2)
1712 #define LOOKUP_OPTION_LEVEL_NAME (-3)
1713 #define LOOKUP_OPTION_RULESETDIR (-4)
1715 /**************************************************************************
1716 Find option index by name. Return index (>=0) on success, else returned
1717 - LOOKUP_OPTION_NO_RESULT if no suitable options were found
1718 - LOOKUP_OPTION_AMBIGUOUS if several matches were found
1719 - LOOKUP_OPTION_LEVEL_NAME if it is an option level
1720 - LOOKUP_OPTION_RULESETDIR if the argument is rulesetdir (special case)
1721 **************************************************************************/
1722 static int lookup_option(const char *name)
1724 enum m_pre_result result;
1725 int ind;
1727 /* Check for option levels, first off */
1728 if (lookup_option_level(name) != SSET_NONE) {
1729 return LOOKUP_OPTION_LEVEL_NAME;
1732 result = match_prefix(optname_accessor, settings_number(),
1733 0, fc_strncasecmp, NULL, name, &ind);
1734 if (M_PRE_AMBIGUOUS > result) {
1735 return ind;
1736 } else if (M_PRE_AMBIGUOUS == result) {
1737 return LOOKUP_OPTION_AMBIGUOUS;
1738 } else if ('\0' != name[0]
1739 && 0 == fc_strncasecmp("rulesetdir", name, strlen(name))) {
1740 return LOOKUP_OPTION_RULESETDIR;
1741 } else {
1742 return LOOKUP_OPTION_NO_RESULT;
1746 /**************************************************************************
1747 Show the caller detailed help for the single OPTION given by id.
1748 help_cmd is the command the player used.
1749 Only show option values for options which the caller can SEE.
1750 **************************************************************************/
1751 static void show_help_option(struct connection *caller,
1752 enum command_id help_cmd, int id)
1754 char val_buf[256], def_buf[256];
1755 struct setting *pset = setting_by_number(id);
1757 if (setting_short_help(pset)) {
1758 cmd_reply(help_cmd, caller, C_COMMENT,
1759 /* TRANS: <untranslated name> - translated short help */
1760 _("Option: %s - %s"), setting_name(pset),
1761 _(setting_short_help(pset)));
1762 } else {
1763 cmd_reply(help_cmd, caller, C_COMMENT,
1764 /* TRANS: <untranslated name> */
1765 _("Option: %s"), setting_name(pset));
1768 if (strlen(setting_extra_help(pset)) > 0) {
1769 char *help = fc_strdup(_(setting_extra_help(pset)));
1771 fc_break_lines(help, LINE_BREAK);
1772 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
1773 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
1774 FC_FREE(help);
1776 cmd_reply(help_cmd, caller, C_COMMENT,
1777 _("Status: %s"), (setting_is_changeable(pset, NULL, NULL, 0)
1778 ? _("changeable") : _("fixed")));
1780 if (setting_is_visible(pset, caller)) {
1781 setting_value_name(pset, TRUE, val_buf, sizeof(val_buf));
1782 setting_default_name(pset, TRUE, def_buf, sizeof(def_buf));
1784 switch (setting_type(pset)) {
1785 case SSET_INT:
1786 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %d, %s %s, %s %d",
1787 _("Value:"), val_buf,
1788 _("Minimum:"), setting_int_min(pset),
1789 _("Default:"), def_buf,
1790 _("Maximum:"), setting_int_max(pset));
1791 break;
1792 case SSET_ENUM:
1794 int i;
1795 const char *value;
1797 cmd_reply(help_cmd, caller, C_COMMENT, _("Possible values:"));
1798 for (i = 0; (value = setting_enum_val(pset, i, FALSE)); i++) {
1799 cmd_reply(help_cmd, caller, C_COMMENT, "- %s: \"%s\"",
1800 value, setting_enum_val(pset, i, TRUE));
1803 /* Fall through. */
1804 case SSET_BOOL:
1805 case SSET_STRING:
1806 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s, %s %s",
1807 _("Value:"), val_buf, _("Default:"), def_buf);
1808 break;
1809 case SSET_BITWISE:
1811 int i;
1812 const char *value;
1814 cmd_reply(help_cmd, caller, C_COMMENT,
1815 _("Possible values (option can take any number of these):"));
1816 for (i = 0; (value = setting_bitwise_bit(pset, i, FALSE)); i++) {
1817 cmd_reply(help_cmd, caller, C_COMMENT, "- %s: \"%s\"",
1818 value, setting_bitwise_bit(pset, i, TRUE));
1820 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s",
1821 _("Value:"), val_buf);
1822 cmd_reply(help_cmd, caller, C_COMMENT, "%s %s",
1823 _("Default:"), def_buf);
1825 break;
1830 /**************************************************************************
1831 Show the caller list of OPTIONS.
1832 help_cmd is the command the player used.
1833 Only show options which the caller can SEE.
1834 **************************************************************************/
1835 static void show_help_option_list(struct connection *caller,
1836 enum command_id help_cmd)
1838 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1839 cmd_reply(help_cmd, caller, C_COMMENT,
1840 _("Explanations are available for the following server options:"));
1841 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1842 if(!caller && con_get_style()) {
1843 settings_iterate(SSET_ALL, pset) {
1844 cmd_reply(help_cmd, caller, C_COMMENT, "%s", setting_name(pset));
1845 } settings_iterate_end
1846 } else {
1847 char buf[MAX_LEN_CONSOLE_LINE];
1848 int j = 0;
1849 buf[0] = '\0';
1851 settings_iterate(SSET_ALL, pset) {
1852 if (setting_is_visible(pset, caller)) {
1853 cat_snprintf(buf, sizeof(buf), "%-19s", setting_name(pset));
1854 if ((++j % 4) == 0) {
1855 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1856 buf[0] = '\0';
1859 } settings_iterate_end;
1861 if (buf[0] != '\0') {
1862 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
1865 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
1868 /**************************************************************************
1869 Handle explain command
1870 **************************************************************************/
1871 static bool explain_option(struct connection *caller, char *str, bool check)
1873 int cmd;
1875 remove_leading_trailing_spaces(str);
1877 if (*str != '\0') {
1878 cmd = lookup_option(str);
1879 if (cmd >= 0 && cmd < settings_number()) {
1880 show_help_option(caller, CMD_EXPLAIN, cmd);
1881 } else if (cmd == LOOKUP_OPTION_NO_RESULT
1882 || cmd == LOOKUP_OPTION_LEVEL_NAME
1883 || cmd == LOOKUP_OPTION_RULESETDIR) {
1884 cmd_reply(CMD_EXPLAIN, caller, C_FAIL,
1885 _("No explanation for that yet."));
1886 return FALSE;
1887 } else if (cmd == LOOKUP_OPTION_AMBIGUOUS) {
1888 cmd_reply(CMD_EXPLAIN, caller, C_FAIL, _("Ambiguous option name."));
1889 return FALSE;
1890 } else {
1891 log_error("Unexpected case %d in %s line %d", cmd, __FILE__,
1892 __FC_LINE__);
1893 return FALSE;
1895 } else {
1896 show_help_option_list(caller, CMD_EXPLAIN);
1898 return TRUE;
1901 /******************************************************************
1902 Send a message to all players
1903 ******************************************************************/
1904 static bool wall(char *str, bool check)
1906 if (!check) {
1907 notify_conn(NULL, NULL, E_MESSAGE_WALL, ftc_server_prompt,
1908 _("Server Operator: %s"), str);
1910 return TRUE;
1913 /******************************************************************
1914 Set message to send to all new connections
1915 ******************************************************************/
1916 static bool connectmsg_command(struct connection *caller, char *str,
1917 bool check)
1919 unsigned int bufsize = sizeof(game.server.connectmsg);
1921 if (is_restricted(caller)) {
1922 return FALSE;
1924 if (!check) {
1925 int i;
1926 int c = 0;
1928 for (i = 0; c < bufsize -1 && str[i] != '\0'; i++) {
1929 if (str[i] == '\\') {
1930 i++;
1932 if (str[i] == 'n') {
1933 game.server.connectmsg[c++] = '\n';
1934 } else {
1935 game.server.connectmsg[c++] = str[i];
1937 } else {
1938 game.server.connectmsg[c++] = str[i];
1942 game.server.connectmsg[c++] = '\0';
1944 if (c == bufsize) {
1945 /* Truncated */
1946 cmd_reply(CMD_CONNECTMSG, caller, C_WARNING,
1947 _("Connectmsg truncated to %u bytes."), bufsize);
1950 return TRUE;
1953 /******************************************************************
1954 Set an AI level and related quantities, with no feedback.
1955 ******************************************************************/
1956 void set_ai_level_directer(struct player *pplayer, enum ai_level level)
1958 pplayer->ai_common.handicaps = handicap_of_skill_level(level);
1959 pplayer->ai_common.fuzzy = fuzzy_of_skill_level(level);
1960 pplayer->ai_common.expand = expansionism_of_skill_level(level);
1961 pplayer->ai_common.science_cost = science_cost_of_skill_level(level);
1962 pplayer->ai_common.skill_level = level;
1965 /******************************************************************
1966 Translate an AI level back to its CMD_* value.
1967 If we just used /set ailevel <num> we wouldn't have to do this - rp
1968 ******************************************************************/
1969 static enum command_id cmd_of_level(enum ai_level level)
1971 switch (level) {
1972 case AI_LEVEL_AWAY : return CMD_AWAY;
1973 case AI_LEVEL_NOVICE : return CMD_NOVICE;
1974 case AI_LEVEL_EASY : return CMD_EASY;
1975 case AI_LEVEL_NORMAL : return CMD_NORMAL;
1976 case AI_LEVEL_HARD : return CMD_HARD;
1977 case AI_LEVEL_CHEATING : return CMD_CHEATING;
1978 case AI_LEVEL_EXPERIMENTAL : return CMD_EXPERIMENTAL;
1979 case AI_LEVEL_LAST : return CMD_NORMAL;
1981 log_error("Unknown AI level variant: %d.", level);
1982 return CMD_NORMAL;
1985 /******************************************************************
1986 Set an AI level from the server prompt.
1987 ******************************************************************/
1988 void set_ai_level_direct(struct player *pplayer, enum ai_level level)
1990 set_ai_level_directer(pplayer, level);
1991 send_player_info_c(pplayer, NULL);
1992 cmd_reply(cmd_of_level(level), NULL, C_OK,
1993 _("Player '%s' now has AI skill level '%s'."),
1994 player_name(pplayer),
1995 ai_level_name(level));
1999 /******************************************************************
2000 Handle a user command to set an AI level.
2001 ******************************************************************/
2002 static bool set_ai_level_named(struct connection *caller, const char *name,
2003 const char *level_name, bool check)
2005 enum ai_level level = ai_level_by_name(level_name);
2006 return set_ai_level(caller, name, level, check);
2009 /******************************************************************
2010 Set AI level
2011 ******************************************************************/
2012 static bool set_ai_level(struct connection *caller, const char *name,
2013 enum ai_level level, bool check)
2015 enum m_pre_result match_result;
2016 struct player *pplayer;
2018 fc_assert_ret_val(level > 0 && level < 11, FALSE);
2020 pplayer = player_by_name_prefix(name, &match_result);
2022 if (pplayer) {
2023 if (pplayer->ai_controlled) {
2024 if (check) {
2025 return TRUE;
2027 set_ai_level_directer(pplayer, level);
2028 send_player_info_c(pplayer, NULL);
2029 cmd_reply(cmd_of_level(level), caller, C_OK,
2030 _("Player '%s' now has AI skill level '%s'."),
2031 player_name(pplayer),
2032 ai_level_name(level));
2033 } else {
2034 cmd_reply(cmd_of_level(level), caller, C_FAIL,
2035 _("%s is not controlled by the AI."),
2036 player_name(pplayer));
2037 return FALSE;
2039 } else if(match_result == M_PRE_EMPTY) {
2040 if (check) {
2041 return TRUE;
2043 players_iterate(pplayer) {
2044 if (pplayer->ai_controlled) {
2045 set_ai_level_directer(pplayer, level);
2046 send_player_info_c(pplayer, NULL);
2047 cmd_reply(cmd_of_level(level), caller, C_OK,
2048 _("Player '%s' now has AI skill level '%s'."),
2049 player_name(pplayer),
2050 ai_level_name(level));
2052 } players_iterate_end;
2053 game.info.skill_level = level;
2054 cmd_reply(cmd_of_level(level), caller, C_OK,
2055 _("Default AI skill level set to '%s'."),
2056 ai_level_name(level));
2057 } else {
2058 cmd_reply_no_such_player(cmd_of_level(level), caller, name, match_result);
2059 return FALSE;
2061 return TRUE;
2064 /******************************************************************
2065 Set user to away mode.
2066 ******************************************************************/
2067 static bool set_away(struct connection *caller, char *name, bool check)
2069 if (caller == NULL) {
2070 cmd_reply(CMD_AWAY, caller, C_FAIL, _("This command is client only."));
2071 return FALSE;
2072 } else if (name && strlen(name) > 0) {
2073 cmd_reply(CMD_AWAY, caller, C_SYNTAX, _("Usage:\n%s"),
2074 command_synopsis(command_by_number(CMD_AWAY)));
2075 return FALSE;
2076 } else if (NULL == caller->playing || caller->observer) {
2077 /* This happens for detached or observer connections. */
2078 cmd_reply(CMD_AWAY, caller, C_FAIL,
2079 _("Only players may use the away command."));
2080 return FALSE;
2081 } else if (!caller->playing->ai_controlled && !check) {
2082 cmd_reply(CMD_AWAY, caller, C_OK,
2083 _("%s set to away mode."), player_name(caller->playing));
2084 set_ai_level_directer(caller->playing, AI_LEVEL_AWAY);
2085 caller->playing->ai_controlled = TRUE;
2086 cancel_all_meetings(caller->playing);
2087 } else if (!check) {
2088 cmd_reply(CMD_AWAY, caller, C_OK,
2089 _("%s returned to game."), player_name(caller->playing));
2090 caller->playing->ai_controlled = FALSE;
2091 /* We have to do it, because the client doesn't display
2092 * dialogs for meetings in AI mode. */
2093 cancel_all_meetings(caller->playing);
2096 send_player_info_c(caller->playing, game.est_connections);
2098 return TRUE;
2101 /**************************************************************************
2102 Show changed settings.
2103 **************************************************************************/
2104 static void show_changed(struct connection *caller, bool check,
2105 int read_recursion)
2107 if (read_recursion != 0) {
2108 return;
2111 /* show changed settings only at the top level of recursion */
2112 char *show_arg = "changed";
2113 show_command(caller, show_arg, check);
2116 /**************************************************************************
2117 Print a summary of the settings and their values. Note that most values
2118 are at most 4 digits, except seeds, which we let overflow their columns,
2119 plus a sign character. Only show options which the caller can SEE.
2120 **************************************************************************/
2121 static bool show_command(struct connection *caller, char *str, bool check)
2123 int cmd;
2124 enum sset_level level = SSET_ALL;
2125 size_t clen = 0;
2127 remove_leading_trailing_spaces(str);
2128 if (str[0] != '\0') {
2129 /* In "/show forests", figure out that it's the forests option we're
2130 * looking at. */
2131 cmd = lookup_option(str);
2132 if (cmd >= 0) {
2133 /* Ignore levels when a particular option is specified. */
2134 level = SSET_NONE;
2136 if (!setting_is_visible(setting_by_number(cmd), caller)) {
2137 cmd_reply(CMD_SHOW, caller, C_FAIL,
2138 _("Sorry, you do not have access to view option '%s'."),
2139 str);
2140 return FALSE;
2144 /* Valid negative values for 'cmd' are defined as LOOKUP_OPTION_*. */
2145 switch (cmd) {
2146 case LOOKUP_OPTION_NO_RESULT:
2147 cmd_reply(CMD_SHOW, caller, C_FAIL, _("Unknown option '%s'."), str);
2148 return FALSE;
2149 case LOOKUP_OPTION_AMBIGUOUS:
2150 /* Allow ambiguous: show all matching. */
2151 clen = strlen(str);
2152 break;
2153 case LOOKUP_OPTION_LEVEL_NAME:
2154 /* Option level. */
2155 level = lookup_option_level(str);
2156 break;
2157 case LOOKUP_OPTION_RULESETDIR:
2158 /* Ruleset. */
2159 cmd_reply(CMD_SHOW, caller, C_COMMENT,
2160 _("Current ruleset directory is \"%s\""),
2161 game.server.rulesetdir);
2162 return TRUE;
2164 } else {
2165 /* to indicate that no command was specified */
2166 cmd = LOOKUP_OPTION_NO_RESULT;
2167 /* Use vital level by default. */
2168 level = SSET_VITAL;
2171 fc_assert_ret_val(cmd >= 0 || cmd == LOOKUP_OPTION_AMBIGUOUS
2172 || cmd == LOOKUP_OPTION_LEVEL_NAME
2173 || cmd == LOOKUP_OPTION_NO_RESULT, FALSE);
2175 #define cmd_reply_show(string) \
2176 cmd_reply(CMD_SHOW, caller, C_COMMENT, "%s", string)
2179 const char *heading = NULL;
2180 switch(level) {
2181 case SSET_NONE:
2182 break;
2183 case SSET_CHANGED:
2184 heading = _("All options with non-default values");
2185 break;
2186 case SSET_ALL:
2187 heading = _("All options");
2188 break;
2189 case SSET_VITAL:
2190 heading = _("Vital options");
2191 break;
2192 case SSET_SITUATIONAL:
2193 heading = _("Situational options");
2194 break;
2195 case SSET_RARE:
2196 heading = _("Rarely used options");
2197 break;
2198 case SSET_LOCKED:
2199 heading = _("Options locked by the ruleset");
2200 break;
2201 case OLEVELS_NUM:
2202 /* nothing */
2203 break;
2205 if (heading) {
2206 cmd_reply_show(horiz_line);
2207 cmd_reply_show(heading);
2210 cmd_reply_show(horiz_line);
2211 cmd_reply_show(_("In the column '##' the status of the option is shown:"));
2212 cmd_reply_show(_(" - a '!' means the option is locked by the ruleset."));
2213 cmd_reply_show(_(" - a '+' means you may change the option."));
2214 cmd_reply_show(_(" - a '=' means the option is on its default value."));
2215 cmd_reply_show(horiz_line);
2216 cmd_reply(CMD_SHOW, caller, C_COMMENT, _("%-*s ## value (min, max)"),
2217 OPTION_NAME_SPACE, _("Option"));
2218 cmd_reply_show(horiz_line);
2220 /* Update changed and locked levels. */
2221 settings_list_update();
2223 switch(level) {
2224 case SSET_NONE:
2225 /* Show _one_ setting. */
2226 fc_assert_ret_val(0 <= cmd, FALSE);
2228 struct setting *pset = setting_by_number(cmd);
2230 show_command_one(caller, pset);
2232 break;
2233 case SSET_CHANGED:
2234 case SSET_ALL:
2235 case SSET_VITAL:
2236 case SSET_SITUATIONAL:
2237 case SSET_RARE:
2238 case SSET_LOCKED:
2239 settings_iterate(level, pset) {
2240 if (!setting_is_visible(pset, caller)) {
2241 continue;
2244 if (LOOKUP_OPTION_AMBIGUOUS == cmd
2245 && 0 != fc_strncasecmp(setting_name(pset), str, clen)) {
2246 continue;
2249 show_command_one(caller, pset);
2250 } settings_iterate_end;
2251 break;
2252 case OLEVELS_NUM:
2253 /* nothing */
2254 break;
2257 cmd_reply_show(horiz_line);
2258 cmd_reply_show(_("A help text for each option is available via 'help "
2259 "<option>'."));
2260 cmd_reply_show(horiz_line);
2261 if (level == SSET_VITAL) {
2262 cmd_reply_show(_("Try 'show situational' or 'show rare' to show "
2263 "more options.\n"
2264 "Try 'show changed' to show settings with "
2265 "non-default values.\n"
2266 "Try 'show locked' to show settings locked "
2267 "by the ruleset."));
2268 cmd_reply_show(horiz_line);
2270 return TRUE;
2271 #undef cmd_reply_show
2274 /*****************************************************************************
2275 Show one setting.
2277 Each option value will be displayed as:
2279 [OPTION_NAME_SPACE length for name] ## [value] ([min], [max])
2281 where '##' is a combination of ' ', '!' or '+' followed by ' ' or '=' with
2282 - '!': the option is locked by the ruleset
2283 - '+': you may change the option
2284 - '=': the option is on its default value
2285 *****************************************************************************/
2286 static void show_command_one(struct connection *caller, struct setting *pset)
2288 char buf[MAX_LEN_CONSOLE_LINE] = "", value[MAX_LEN_CONSOLE_LINE] = "";
2289 bool is_changed;
2291 fc_assert_ret(pset != NULL);
2293 is_changed = setting_changed(pset);
2294 setting_value_name(pset, TRUE, value, sizeof(value));
2296 if (is_changed) {
2297 /* Emphasizes the changed option. */
2298 featured_text_apply_tag(value, buf, sizeof(buf), TTT_COLOR,
2299 0, FT_OFFSET_UNSET, ftc_changed);
2300 sz_strlcpy(value, buf);
2303 if (SSET_INT == setting_type(pset)) {
2304 /* Add the range. */
2305 cat_snprintf(value, sizeof(value), " (%d, %d)",
2306 setting_int_min(pset), setting_int_max(pset));
2309 cmd_reply(CMD_SHOW, caller, C_COMMENT, "%-*s %c%c %s",
2310 OPTION_NAME_SPACE, setting_name(pset),
2311 setting_status(caller, pset), is_changed ? ' ' : '=', value);
2314 /******************************************************************
2315 Handle team command
2316 ******************************************************************/
2317 static bool team_command(struct connection *caller, char *str, bool check)
2319 struct player *pplayer;
2320 enum m_pre_result match_result;
2321 char buf[MAX_LEN_CONSOLE_LINE];
2322 char *arg[2];
2323 int ntokens = 0, i;
2324 bool res = FALSE;
2325 struct team_slot *tslot;
2327 if (game_was_started()) {
2328 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2329 _("Cannot change teams once game has begun."));
2330 return FALSE;
2333 if (str != NULL || strlen(str) > 0) {
2334 sz_strlcpy(buf, str);
2335 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2337 if (ntokens != 2) {
2338 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2339 _("Undefined argument. Usage:\n%s"),
2340 command_synopsis(command_by_number(CMD_TEAM)));
2341 goto cleanup;
2344 pplayer = player_by_name_prefix(arg[0], &match_result);
2345 if (pplayer == NULL) {
2346 cmd_reply_no_such_player(CMD_TEAM, caller, arg[0], match_result);
2347 goto cleanup;
2350 tslot = team_slot_by_rule_name(arg[1]);
2351 if (NULL == tslot) {
2352 int teamno;
2354 if (str_to_int(arg[1], &teamno)) {
2355 tslot = team_slot_by_number(teamno);
2358 if (NULL == tslot) {
2359 cmd_reply(CMD_TEAM, caller, C_SYNTAX,
2360 _("No such team %s. Please give a "
2361 "valid team name or number."), arg[1]);
2362 goto cleanup;
2365 if (is_barbarian(pplayer)) {
2366 /* This can happen if we change team settings on a loaded game. */
2367 cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Cannot team a barbarian."));
2368 goto cleanup;
2370 if (!check) {
2371 team_add_player(pplayer, team_new(tslot));
2372 send_player_info_c(pplayer, NULL);
2373 cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
2374 player_name(pplayer),
2375 team_slot_name_translation(tslot));
2377 res = TRUE;
2379 cleanup:
2380 for (i = 0; i < ntokens; i++) {
2381 free(arg[i]);
2383 return res;
2386 /**************************************************************************
2387 List all running votes. Moved from /vote command.
2388 **************************************************************************/
2389 static void show_votes(struct connection *caller)
2391 int count = 0;
2392 const char *title;
2394 if (vote_list != NULL) {
2395 vote_list_iterate(vote_list, pvote) {
2396 if (NULL != caller && !conn_can_see_vote(caller, pvote)) {
2397 continue;
2399 /* TRANS: "Vote" or "Teamvote" is voting-as-a-process. Used as
2400 * part of a sentence. */
2401 title = vote_is_team_only(pvote) ? _("Teamvote") : _("Vote");
2402 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2403 /* TRANS: "[Vote|Teamvote] 3 \"proposed change\" (needs ..." */
2404 _("%s %d \"%s\" (needs %0.0f%%%s): %d for, "
2405 "%d against, and %d abstained out of %d players."),
2406 title, pvote->vote_no, pvote->cmdline,
2407 MIN(100, pvote->need_pc * 100 + 1),
2408 pvote->flags & VCF_NODISSENT ? _(" no dissent") : "",
2409 pvote->yes, pvote->no, pvote->abstain, count_voters(pvote));
2410 count++;
2411 } vote_list_iterate_end;
2414 if (count == 0) {
2415 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2416 _("There are no votes going on."));
2420 /**************************************************************************
2421 Vote command argument definitions.
2422 **************************************************************************/
2423 static const char *const vote_args[] = {
2424 "yes",
2425 "no",
2426 "abstain",
2427 NULL
2429 static const char *vote_arg_accessor(int i)
2431 return vote_args[i];
2434 /******************************************************************
2435 Make or participate in a vote.
2436 ******************************************************************/
2437 static bool vote_command(struct connection *caller, char *str,
2438 bool check)
2440 char buf[MAX_LEN_CONSOLE_LINE];
2441 char *arg[2];
2442 int ntokens = 0, i = 0, which = -1;
2443 enum m_pre_result match_result;
2444 struct vote *pvote = NULL;
2445 bool res = FALSE;
2447 if (check) {
2448 /* This should never happen, since /vote must always be
2449 * set to ALLOW_BASIC or less. But just in case... */
2450 return FALSE;
2453 sz_strlcpy(buf, str);
2454 ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2456 if (ntokens == 0) {
2457 show_votes(caller);
2458 goto CLEANUP;
2459 } else if (!conn_can_vote(caller, NULL)) {
2460 cmd_reply(CMD_VOTE, caller, C_FAIL,
2461 _("You are not allowed to use this command."));
2462 goto CLEANUP;
2465 match_result = match_prefix(vote_arg_accessor, VOTE_NUM, 0,
2466 fc_strncasecmp, NULL, arg[0], &i);
2468 if (match_result == M_PRE_AMBIGUOUS) {
2469 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2470 _("The argument \"%s\" is ambiguous."), arg[0]);
2471 goto CLEANUP;
2472 } else if (match_result > M_PRE_AMBIGUOUS) {
2473 /* Failed */
2474 cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2475 _("Undefined argument. Usage:\n%s"),
2476 command_synopsis(command_by_number(CMD_VOTE)));
2477 goto CLEANUP;
2480 if (ntokens == 1) {
2481 /* Applies to last vote */
2482 if (vote_number_sequence > 0 && get_vote_by_no(vote_number_sequence)) {
2483 which = vote_number_sequence;
2484 } else {
2485 int num_votes = vote_list_size(vote_list);
2486 if (num_votes == 0) {
2487 cmd_reply(CMD_VOTE, caller, C_FAIL, _("There are no votes running."));
2488 } else {
2489 /* TRANS: "vote" as a process */
2490 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote (%d %s)."),
2491 num_votes, PL_("other vote running", "other votes running",
2492 num_votes));
2494 goto CLEANUP;
2496 } else {
2497 if (!str_to_int(arg[1], &which)) {
2498 cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be an integer."));
2499 goto CLEANUP;
2503 if (!(pvote = get_vote_by_no(which))) {
2504 /* TRANS: "vote" as a process */
2505 cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), which);
2506 goto CLEANUP;
2509 if (!conn_can_vote(caller, pvote)) {
2510 cmd_reply(CMD_VOTE, caller, C_FAIL,
2511 _("You are not allowed to vote on that."));
2512 goto CLEANUP;
2515 if (i == VOTE_YES) {
2516 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
2517 pvote->cmdline);
2518 connection_vote(caller, pvote, VOTE_YES);
2519 } else if (i == VOTE_NO) {
2520 cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
2521 pvote->cmdline);
2522 connection_vote(caller, pvote, VOTE_NO);
2523 } else if (i == VOTE_ABSTAIN) {
2524 cmd_reply(CMD_VOTE, caller, C_COMMENT,
2525 _("You abstained from voting on \"%s\""), pvote->cmdline);
2526 connection_vote(caller, pvote, VOTE_ABSTAIN);
2527 } else {
2528 /* Must never happen. */
2529 fc_assert_action(FALSE, goto CLEANUP);
2532 res = TRUE;
2534 CLEANUP:
2535 free_tokens(arg, ntokens);
2536 return res;
2539 /**************************************************************************
2540 Cancel a vote... /cancelvote <vote number>|all.
2541 **************************************************************************/
2542 static bool cancelvote_command(struct connection *caller,
2543 char *arg, bool check)
2545 struct vote *pvote = NULL;
2546 int vote_no;
2548 if (check) {
2549 /* This should never happen anyway, since /cancelvote
2550 * is set to ALLOW_BASIC in both pregame and while the
2551 * game is running. */
2552 return FALSE;
2555 remove_leading_trailing_spaces(arg);
2557 if (arg[0] == '\0') {
2558 if (caller == NULL) {
2559 /* Server prompt */
2560 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2561 /* TRANS: "vote" as a process */
2562 _("Missing argument <vote number> or "
2563 "the string \"all\"."));
2564 return FALSE;
2566 /* The caller cancel his/her own vote. */
2567 if (!(pvote = get_vote_by_caller(caller))) {
2568 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2569 _("You don't have any vote going on."));
2570 return FALSE;
2572 } else if (fc_strcasecmp(arg, "all") == 0) {
2573 /* Cancel all votes (needs some privileges). */
2574 if (vote_list_size(vote_list) == 0) {
2575 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2576 _("There isn't any vote going on."));
2577 return FALSE;
2578 } else if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
2579 clear_all_votes();
2580 notify_conn(NULL, NULL, E_VOTE_ABORTED, ftc_server,
2581 /* TRANS: "votes" as a process */
2582 _("All votes have been removed."));
2583 return TRUE;
2584 } else {
2585 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2586 _("You are not allowed to use this command."));
2587 return FALSE;
2589 } else if (str_to_int(arg, &vote_no)) {
2590 /* Cancel one particular vote (needs some privileges if the vote
2591 * is not owned). */
2592 if (!(pvote = get_vote_by_no(vote_no))) {
2593 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2594 /* TRANS: "vote" as a process */
2595 _("No such vote (%d)."), vote_no);
2596 return FALSE;
2597 } else if (caller && conn_get_access(caller) < ALLOW_ADMIN
2598 && caller->id != pvote->caller_id) {
2599 cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2600 /* TRANS: "vote" as a process */
2601 _("You are not allowed to cancel this vote (%d)."),
2602 vote_no);
2603 return FALSE;
2605 } else {
2606 cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2607 /* TRANS: "vote" as a process */
2608 _("Usage: /cancelvote [<vote number>|all]"));
2609 return FALSE;
2612 fc_assert_ret_val(NULL != pvote, FALSE);
2614 if (caller) {
2615 notify_team(conn_get_player(vote_get_caller(pvote)),
2616 NULL, E_VOTE_ABORTED, ftc_server,
2617 /* TRANS: "vote" as a process */
2618 _("%s has canceled the vote \"%s\" (number %d)."),
2619 caller->username, pvote->cmdline, pvote->vote_no);
2620 } else {
2621 /* Server prompt */
2622 notify_team(conn_get_player(vote_get_caller(pvote)),
2623 NULL, E_VOTE_ABORTED, ftc_server,
2624 /* TRANS: "vote" as a process */
2625 _("The vote \"%s\" (number %d) has been canceled."),
2626 pvote->cmdline, pvote->vote_no);
2628 /* Make it after, prevent crashs about a free pointer (pvote). */
2629 remove_vote(pvote);
2631 return TRUE;
2634 /******************************************************************
2635 Turn on selective debugging.
2636 ******************************************************************/
2637 static bool debug_command(struct connection *caller, char *str,
2638 bool check)
2640 char buf[MAX_LEN_CONSOLE_LINE];
2641 char *arg[3];
2642 int ntokens = 0, i;
2644 if (game.info.is_new_game) {
2645 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2646 _("Can only use this command once game has begun."));
2647 return FALSE;
2649 if (check) {
2650 return TRUE; /* whatever! */
2653 if (str != NULL && strlen(str) > 0) {
2654 sz_strlcpy(buf, str);
2655 ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS);
2656 } else {
2657 ntokens = 0;
2660 if (ntokens > 0 && strcmp(arg[0], "diplomacy") == 0) {
2661 struct player *pplayer;
2662 enum m_pre_result match_result;
2664 if (ntokens != 2) {
2665 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2666 _("Undefined argument. Usage:\n%s"),
2667 command_synopsis(command_by_number(CMD_DEBUG)));
2668 goto cleanup;
2670 pplayer = player_by_name_prefix(arg[1], &match_result);
2671 if (pplayer == NULL) {
2672 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2673 goto cleanup;
2675 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY)) {
2676 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2677 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy no longer debugged"),
2678 player_name(pplayer));
2679 } else {
2680 BV_SET(pplayer->server.debug, PLAYER_DEBUG_DIPLOMACY);
2681 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s diplomacy debugged"),
2682 player_name(pplayer));
2683 /* TODO: print some info about the player here */
2685 } else if (ntokens > 0 && strcmp(arg[0], "tech") == 0) {
2686 struct player *pplayer;
2687 enum m_pre_result match_result;
2689 if (ntokens != 2) {
2690 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2691 _("Undefined argument. Usage:\n%s"),
2692 command_synopsis(command_by_number(CMD_DEBUG)));
2693 goto cleanup;
2695 pplayer = player_by_name_prefix(arg[1], &match_result);
2696 if (pplayer == NULL) {
2697 cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
2698 goto cleanup;
2700 if (BV_ISSET(pplayer->server.debug, PLAYER_DEBUG_TECH)) {
2701 BV_CLR(pplayer->server.debug, PLAYER_DEBUG_TECH);
2702 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech no longer debugged"),
2703 player_name(pplayer));
2704 } else {
2705 BV_SET(pplayer->server.debug, PLAYER_DEBUG_TECH);
2706 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s tech debugged"),
2707 player_name(pplayer));
2708 /* TODO: print some info about the player here */
2710 } else if (ntokens > 0 && strcmp(arg[0], "info") == 0) {
2711 int cities = 0, players = 0, units = 0, citizens = 0;
2712 players_iterate(plr) {
2713 players++;
2714 city_list_iterate(plr->cities, pcity) {
2715 cities++;
2716 citizens += city_size_get(pcity);
2717 } city_list_iterate_end;
2718 units += unit_list_size(plr->units);
2719 } players_iterate_end;
2720 log_normal(_("players=%d cities=%d citizens=%d units=%d"),
2721 players, cities, citizens, units);
2722 notify_conn(game.est_connections, NULL, E_AI_DEBUG, ftc_log,
2723 _("players=%d cities=%d citizens=%d units=%d"),
2724 players, cities, citizens, units);
2725 } else if (ntokens > 0 && strcmp(arg[0], "city") == 0) {
2726 int x, y;
2727 struct tile *ptile;
2728 struct city *pcity;
2730 if (ntokens != 3) {
2731 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2732 _("Undefined argument. Usage:\n%s"),
2733 command_synopsis(command_by_number(CMD_DEBUG)));
2734 goto cleanup;
2736 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2737 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2738 goto cleanup;
2740 if (!(ptile = map_pos_to_tile(x, y))) {
2741 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2742 goto cleanup;
2744 pcity = tile_city(ptile);
2745 if (!pcity) {
2746 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("No city at this coordinate."));
2747 goto cleanup;
2749 if (pcity->server.debug) {
2750 pcity->server.debug = FALSE;
2751 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s no longer debugged"),
2752 city_name(pcity));
2753 } else {
2754 pcity->server.debug = TRUE;
2755 CITY_LOG(LOG_NORMAL, pcity, "debugged");
2757 } else if (ntokens > 0 && strcmp(arg[0], "units") == 0) {
2758 int x, y;
2759 struct tile *ptile;
2761 if (ntokens != 3) {
2762 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2763 _("Undefined argument. Usage:\n%s"),
2764 command_synopsis(command_by_number(CMD_DEBUG)));
2765 goto cleanup;
2767 if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
2768 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
2769 goto cleanup;
2771 if (!(ptile = map_pos_to_tile(x, y))) {
2772 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Bad map coordinates."));
2773 goto cleanup;
2775 unit_list_iterate(ptile->units, punit) {
2776 if (punit->server.debug) {
2777 punit->server.debug = FALSE;
2778 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2779 nation_adjective_for_player(unit_owner(punit)),
2780 unit_name_translation(punit));
2781 } else {
2782 punit->server.debug = TRUE;
2783 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2784 nation_rule_name(nation_of_unit(punit)),
2785 unit_name_translation(punit));
2787 } unit_list_iterate_end;
2788 } else if (ntokens > 0 && strcmp(arg[0], "timing") == 0) {
2789 TIMING_RESULTS();
2790 } else if (ntokens > 0 && strcmp(arg[0], "ferries") == 0) {
2791 if (game.server.debug[DEBUG_FERRIES]) {
2792 game.server.debug[DEBUG_FERRIES] = FALSE;
2793 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system is no longer "
2794 "in debug mode."));
2795 } else {
2796 game.server.debug[DEBUG_FERRIES] = TRUE;
2797 cmd_reply(CMD_DEBUG, caller, C_OK, _("Ferry system in debug mode."));
2799 } else if (ntokens > 0 && strcmp(arg[0], "unit") == 0) {
2800 int id;
2801 struct unit *punit;
2803 if (ntokens != 2) {
2804 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2805 _("Undefined argument. Usage:\n%s"),
2806 command_synopsis(command_by_number(CMD_DEBUG)));
2807 goto cleanup;
2809 if (!str_to_int(arg[1], &id)) {
2810 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 must be integer."));
2811 goto cleanup;
2813 if (!(punit = game_unit_by_number(id))) {
2814 cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Unit %d does not exist."), id);
2815 goto cleanup;
2817 if (punit->server.debug) {
2818 punit->server.debug = FALSE;
2819 cmd_reply(CMD_DEBUG, caller, C_OK, _("%s %s no longer debugged."),
2820 nation_adjective_for_player(unit_owner(punit)),
2821 unit_name_translation(punit));
2822 } else {
2823 punit->server.debug = TRUE;
2824 UNIT_LOG(LOG_NORMAL, punit, "%s %s debugged.",
2825 nation_rule_name(nation_of_unit(punit)),
2826 unit_name_translation(punit));
2828 } else {
2829 cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
2830 _("Undefined argument. Usage:\n%s"),
2831 command_synopsis(command_by_number(CMD_DEBUG)));
2833 cleanup:
2834 for (i = 0; i < ntokens; i++) {
2835 free(arg[i]);
2837 return TRUE;
2840 /******************************************************************
2841 Handle set command
2842 ******************************************************************/
2843 static bool set_command(struct connection *caller, char *str, bool check)
2845 char *args[2];
2846 int val, cmd, nargs;
2847 struct setting *pset;
2848 bool do_update;
2849 char reject_msg[256] = "";
2850 bool ret = FALSE;
2852 /* '=' is also a valid delimiter for this function. */
2853 nargs = get_tokens(str, args, ARRAY_SIZE(args), TOKEN_DELIMITERS "=");
2855 if (nargs < 2) {
2856 cmd_reply(CMD_SET, caller, C_SYNTAX,
2857 _("Undefined argument. Usage:\n%s"),
2858 command_synopsis(command_by_number(CMD_SET)));
2859 goto cleanup;
2862 cmd = lookup_option(args[0]);
2863 if (cmd < 0) {
2864 switch (cmd) {
2865 case LOOKUP_OPTION_NO_RESULT:
2866 case LOOKUP_OPTION_LEVEL_NAME:
2867 cmd_reply(CMD_SET, caller, C_SYNTAX,
2868 _("Option '%s' not recognized."), args[0]);
2869 break;
2870 case LOOKUP_OPTION_AMBIGUOUS:
2871 cmd_reply(CMD_SET, caller, C_SYNTAX, _("Ambiguous option name."));
2872 break;
2873 case LOOKUP_OPTION_RULESETDIR:
2874 cmd_reply(CMD_SET, caller, C_SYNTAX,
2875 /* TRANS: 'rulesetdir' is the command. Do not translate. */
2876 _("Use the '%srulesetdir' command to change the ruleset "
2877 "directory."), caller ? "/" : "");
2878 break;
2879 default:
2880 fc_assert_ret_val(cmd >= LOOKUP_OPTION_RULESETDIR, FALSE);
2881 break;
2883 goto cleanup;
2886 pset = setting_by_number(cmd);
2888 if (!setting_is_changeable(pset, caller, reject_msg, sizeof(reject_msg))
2889 && !check) {
2890 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2891 goto cleanup;
2894 do_update = FALSE;
2896 switch (setting_type(pset)) {
2897 case SSET_BOOL:
2898 if (check) {
2899 if (!setting_is_changeable(pset, caller, reject_msg,
2900 sizeof(reject_msg))
2901 || (!setting_bool_validate(pset, args[1], caller,
2902 reject_msg, sizeof(reject_msg)))) {
2903 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2904 goto cleanup;
2906 } else if (setting_bool_set(pset, args[1], caller,
2907 reject_msg, sizeof(reject_msg))) {
2908 do_update = TRUE;
2909 } else {
2910 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2911 goto cleanup;
2913 break;
2915 case SSET_INT:
2916 if (!str_to_int(args[1], &val)) {
2917 cmd_reply(CMD_SET, caller, C_SYNTAX,
2918 _("The parameter %s should only contain +- and 0-9."),
2919 setting_name(pset));
2920 goto cleanup;
2922 if (check) {
2923 if (!setting_is_changeable(pset, caller, reject_msg,
2924 sizeof(reject_msg))
2925 || !setting_int_validate(pset, val, caller, reject_msg,
2926 sizeof(reject_msg))) {
2927 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2928 goto cleanup;
2930 } else {
2931 if (setting_int_set(pset, val, caller, reject_msg,
2932 sizeof(reject_msg))) {
2933 do_update = TRUE;
2934 } else {
2935 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2936 goto cleanup;
2939 break;
2941 case SSET_STRING:
2942 if (check) {
2943 if (!setting_is_changeable(pset, caller, reject_msg,
2944 sizeof(reject_msg))
2945 || !setting_str_validate(pset, args[1], caller, reject_msg,
2946 sizeof(reject_msg))) {
2947 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2948 goto cleanup;
2950 } else {
2951 if (setting_str_set(pset, args[1], caller, reject_msg,
2952 sizeof(reject_msg))) {
2953 do_update = TRUE;
2954 } else {
2955 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2956 goto cleanup;
2959 break;
2961 case SSET_ENUM:
2962 if (check) {
2963 if (!setting_is_changeable(pset, caller, reject_msg,
2964 sizeof(reject_msg))
2965 || (!setting_enum_validate(pset, args[1], caller,
2966 reject_msg, sizeof(reject_msg)))) {
2967 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2968 goto cleanup;
2970 } else if (setting_enum_set(pset, args[1], caller,
2971 reject_msg, sizeof(reject_msg))) {
2972 do_update = TRUE;
2973 } else {
2974 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2975 goto cleanup;
2977 break;
2979 case SSET_BITWISE:
2980 if (check) {
2981 if (!setting_is_changeable(pset, caller, reject_msg,
2982 sizeof(reject_msg))
2983 || (!setting_bitwise_validate(pset, args[1], caller,
2984 reject_msg, sizeof(reject_msg)))) {
2985 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2986 goto cleanup;
2988 } else if (setting_bitwise_set(pset, args[1], caller,
2989 reject_msg, sizeof(reject_msg))) {
2990 do_update = TRUE;
2991 } else {
2992 cmd_reply(CMD_SET, caller, C_FAIL, "%s", reject_msg);
2993 goto cleanup;
2995 break;
2998 ret = TRUE; /* Looks like a success. */
3000 if (!check && do_update) {
3001 /* Send only to connections able to see that. */
3002 char buf[256];
3003 struct packet_chat_msg packet;
3005 package_event(&packet, NULL, E_SETTING, ftc_server,
3006 _("Console: '%s' has been set to %s."), setting_name(pset),
3007 setting_value_name(pset, TRUE, buf, sizeof(buf)));
3008 conn_list_iterate(game.est_connections, pconn) {
3009 if (setting_is_visible(pset, pconn)) {
3010 send_packet_chat_msg(pconn, &packet);
3012 } conn_list_iterate_end;
3013 /* Notify the console. */
3014 con_write(C_OK, "%s", packet.message);
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();
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 send_player_info_c(aplayer, NULL);
3573 } players_iterate_end;
3575 check_for_full_turn_done();
3577 end:;
3578 /* free our args */
3579 for (i = 0; i < ntokens; i++) {
3580 free(arg[i]);
3582 return res;
3585 /**************************************************************************
3586 Loads a file, complete with access checks and error messages sent back
3587 to the caller on failure.
3589 * caller is the connection requesting the load, or NULL for a
3590 command-line load. Error messages are sent back to the caller and
3591 an access check is done to make sure they are allowed to load.
3593 * filename is simply the name of the file to be loaded. This may be
3594 approximate; the function will look for appropriate suffixes and will
3595 check in the various directories to see if the file is found.
3597 * if check is set then only a test run is done and no actual loading
3598 is attempted.
3600 * The return value is true if the load succeeds, or would succeed;
3601 false if there's an error in the file or file name. Some errors
3602 in loading however could be unrecoverable (if the save game is
3603 legitimate but has inconsistencies) and would lead to a broken server
3604 afterwards.
3605 **************************************************************************/
3606 bool load_command(struct connection *caller, const char *filename, bool check)
3608 struct timer *loadtimer, *uloadtimer;
3609 struct section_file *file;
3610 char arg[MAX_LEN_PATH];
3611 struct conn_list *global_observers;
3613 if (!filename || filename[0] == '\0') {
3614 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage:\n%s"),
3615 command_synopsis(command_by_number(CMD_LOAD)));
3616 return FALSE;
3618 if (S_S_INITIAL != server_state()) {
3619 cmd_reply(CMD_LOAD, caller, C_FAIL,
3620 _("Cannot load a game while another is running."));
3621 dlsend_packet_game_load(game.est_connections, TRUE, filename);
3622 return FALSE;
3624 if (!is_safe_filename(filename) && is_restricted(caller)) {
3625 cmd_reply(CMD_LOAD, caller, C_FAIL,
3626 _("Name \"%s\" disallowed for security reasons."),
3627 filename);
3628 return FALSE;
3632 /* it is a normal savegame or maybe a scenario */
3633 char testfile[MAX_LEN_PATH];
3634 const struct strvec *pathes[] = {
3635 get_save_dirs(), get_scenario_dirs(), NULL
3637 const char *exts[] = {
3638 "sav", "gz", "bz2", "sav.gz", "sav.bz2", NULL
3640 const char **ext, *found = NULL;
3641 const struct strvec **path;
3643 for (path = pathes; !found && *path; path++) {
3644 for (ext = exts; !found && *ext; ext++) {
3645 fc_snprintf(testfile, sizeof(testfile), "%s.%s", filename, *ext);
3646 if ((found = fileinfoname(*path, testfile))) {
3647 sz_strlcpy(arg, found);
3652 if (is_restricted(caller) && !found) {
3653 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Cannot find savegame or "
3654 "scenario with the name \"%s\"."), filename);
3655 return FALSE;
3658 if (!found) {
3659 sz_strlcpy(arg, filename);
3663 /* attempt to parse the file */
3665 if (!(file = secfile_load(arg, FALSE))) {
3666 cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"),
3667 arg);
3668 log_debug("Error loading savefile '%s':\n%s", arg, secfile_error());
3669 dlsend_packet_game_load(game.est_connections, TRUE, arg);
3670 return FALSE;
3673 if (check) {
3674 return TRUE;
3677 /* Detach current players, before we blow them away. */
3678 global_observers = conn_list_new();
3679 conn_list_iterate(game.est_connections, pconn) {
3680 if (pconn->playing != NULL) {
3681 connection_detach(pconn, TRUE);
3682 } else if (pconn->observer) {
3683 conn_list_append(global_observers, pconn);
3684 connection_detach(pconn, TRUE);
3686 } conn_list_iterate_end;
3688 player_info_freeze();
3690 /* Now free all game data. */
3691 server_game_free();
3692 server_game_init();
3694 loadtimer = timer_new(TIMER_CPU, TIMER_ACTIVE);
3695 timer_start(loadtimer);
3696 uloadtimer = timer_new(TIMER_USER, TIMER_ACTIVE);
3697 timer_start(uloadtimer);
3699 sz_strlcpy(srvarg.load_filename, arg);
3701 savegame2_load(file);
3702 secfile_check_unused(file);
3703 secfile_destroy(file);
3705 log_verbose("Load time: %g seconds (%g apparent)",
3706 timer_read_seconds(loadtimer), timer_read_seconds(uloadtimer));
3707 timer_destroy(loadtimer);
3708 timer_destroy(uloadtimer);
3710 sanity_check();
3712 log_verbose("load_command() does send_rulesets()");
3713 conn_list_compression_freeze(game.est_connections);
3714 send_rulesets(game.est_connections);
3715 send_server_settings(game.est_connections);
3716 send_scenario_info(game.est_connections);
3717 send_game_info(game.est_connections);
3718 conn_list_compression_thaw(game.est_connections);
3720 /* Send information about the new players. */
3721 player_info_thaw();
3722 send_player_diplstate_c(NULL, NULL);
3724 /* Everything seemed to load ok; spread the good news. */
3725 dlsend_packet_game_load(game.est_connections, TRUE, srvarg.load_filename);
3727 /* Attach connections to players. Currently, this applies only
3728 * to connections that have the same username as a player. */
3729 conn_list_iterate(game.est_connections, pconn) {
3730 players_iterate(pplayer) {
3731 if (strcmp(pconn->username, pplayer->username) == 0) {
3732 connection_attach(pconn, pplayer, FALSE);
3733 break;
3735 } players_iterate_end;
3736 } conn_list_iterate_end;
3738 /* Reattach global observers. */
3739 conn_list_iterate(global_observers, pconn) {
3740 if (NULL == pconn->playing) {
3741 /* May have been assigned to a player before. */
3742 connection_attach(pconn, NULL, TRUE);
3744 } conn_list_iterate_end;
3745 conn_list_destroy(global_observers);
3747 aifill(game.info.aifill);
3748 return TRUE;
3751 /**************************************************************************
3752 Load rulesets from a given ruleset directory.
3754 Security: There are some rudimentary checks in load_rulesets() to see
3755 if this directory really is a viable ruleset directory. For public
3756 servers, we check against directory redirection (is_safe_filename) and
3757 other bad stuff in the directory name, and will only use directories
3758 inside the data directories.
3759 **************************************************************************/
3760 static bool set_rulesetdir(struct connection *caller, char *str, bool check,
3761 int read_recursion)
3763 char filename[512];
3764 const char *pfilename;
3766 if (NULL == str || '\0' == str[0]) {
3767 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3768 _("You must provide a ruleset name. Use \"/show ruleset\" to "
3769 "see what is the current ruleset."));
3770 return FALSE;
3772 if (game_was_started() || !map_is_empty()) {
3773 cmd_reply(CMD_RULESETDIR, caller, C_FAIL,
3774 _("This setting can't be modified after the game has started."));
3775 return FALSE;
3778 if (strcmp(str, game.server.rulesetdir) == 0) {
3779 cmd_reply(CMD_RULESETDIR, caller, C_COMMENT,
3780 _("Ruleset directory is already \"%s\""), str);
3781 return FALSE;
3784 if (is_restricted(caller)
3785 && (!is_safe_filename(str) || strchr(str, '.'))) {
3786 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3787 _("Name \"%s\" disallowed for security reasons."),
3788 str);
3789 return FALSE;
3792 fc_snprintf(filename, sizeof(filename), "%s", str);
3793 pfilename = fileinfoname(get_data_dirs(), filename);
3794 if (!pfilename) {
3795 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3796 _("Ruleset directory \"%s\" not found"), str);
3797 return FALSE;
3800 if (!check) {
3801 bool success = TRUE;
3802 char old[512];
3804 sz_strlcpy(old, game.server.rulesetdir);
3805 log_verbose("set_rulesetdir() does load_rulesets() with \"%s\"", str);
3806 sz_strlcpy(game.server.rulesetdir, str);
3808 /* load the ruleset (and game settings defined in the ruleset) */
3809 player_info_freeze();
3810 if (!load_rulesets(old, TRUE)) {
3811 success = FALSE;
3813 /* While loading of the requested ruleset failed, we might
3814 * have changed ruleset from third one to default. Handle
3815 * rest of the ruleset changing accordingly. */
3818 if (game.est_connections) {
3819 /* Now that the rulesets are loaded we immediately send updates to any
3820 * connected clients. */
3821 send_rulesets(game.est_connections);
3823 /* list changed values */
3824 show_changed(caller, check, read_recursion);
3825 player_info_thaw();
3827 if (success) {
3828 cmd_reply(CMD_RULESETDIR, caller, C_OK,
3829 _("Ruleset directory set to \"%s\""), str);
3830 } else {
3831 cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
3832 _("Failed loading rulesets from directory \"%s\", using \"%s\""),
3833 str, game.server.rulesetdir);
3836 return success;
3839 return TRUE;
3842 /****************************************************************************
3843 /ignore command handler.
3844 ****************************************************************************/
3845 static bool ignore_command(struct connection *caller, char *str, bool check)
3847 char buf[128];
3848 struct conn_pattern *ppattern;
3850 if (NULL == caller) {
3851 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3852 _("That would be rather silly, since you are not a player."));
3853 return FALSE;
3856 ppattern = conn_pattern_from_string(str, CPT_USER, buf, sizeof(buf));
3857 if (NULL == ppattern) {
3858 cmd_reply(CMD_IGNORE, caller, C_SYNTAX,
3859 _("%s. Try /help ignore"), buf);
3860 return FALSE;
3863 if (check) {
3864 conn_pattern_destroy(ppattern);
3865 return TRUE;
3868 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3869 conn_pattern_list_append(caller->server.ignore_list, ppattern);
3870 cmd_reply(CMD_IGNORE, caller, C_COMMENT,
3871 _("Added pattern %s as entry %d to your ignore list."),
3872 buf, conn_pattern_list_size(caller->server.ignore_list));
3874 return TRUE;
3877 /****************************************************************************
3878 /unignore command handler.
3879 ****************************************************************************/
3880 static bool unignore_command(struct connection *caller,
3881 char *str, bool check)
3883 char buf[128], *c;
3884 int first, last, n;
3886 if (!caller) {
3887 cmd_reply(CMD_IGNORE, caller, C_FAIL,
3888 _("That would be rather silly, since you are not a player."));
3889 return FALSE;
3892 sz_strlcpy(buf, str);
3893 remove_leading_trailing_spaces(buf);
3895 n = conn_pattern_list_size(caller->server.ignore_list);
3896 if (n == 0) {
3897 cmd_reply(CMD_UNIGNORE, caller, C_FAIL, _("Your ignore list is empty."));
3898 return FALSE;
3901 /* Parse the range. */
3902 if ('\0' == buf[0]) {
3903 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3904 _("Missing range. Try /help unignore."));
3905 return FALSE;
3906 } else if ((c = strchr(buf, '-'))) {
3907 *c++ = '\0';
3908 if ('\0' == buf[0]) {
3909 first = 1;
3910 } else if (!str_to_int(buf, &first)) {
3911 *--c = '-';
3912 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3913 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3914 return FALSE;
3916 if ('\0' == *c) {
3917 last = n;
3918 } else if (!str_to_int(c, &last)) {
3919 *--c = '-';
3920 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3921 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3922 return FALSE;
3924 } else {
3925 if (!str_to_int(buf, &first)) {
3926 cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
3927 _("\"%s\" is not a valid range. Try /help unignore."), buf);
3928 return FALSE;
3930 last = first;
3933 if (!(1 <= first && first <= last && last <= n)) {
3934 if (first == last) {
3935 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3936 _("Invalid entry number: %d."), first);
3937 } else {
3938 cmd_reply(CMD_UNIGNORE, caller, C_FAIL,
3939 _("Invalid range: %d to %d."), first, last);
3941 return FALSE;
3944 if (check) {
3945 return TRUE;
3948 n = 1;
3949 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
3950 if (first <= n) {
3951 conn_pattern_to_string(ppattern, buf, sizeof(buf));
3952 cmd_reply(CMD_UNIGNORE, caller, C_COMMENT,
3953 _("Removed pattern %s (entry %d) from your ignore list."),
3954 buf, n);
3955 conn_pattern_list_remove(caller->server.ignore_list, ppattern);
3957 n++;
3958 if (n > last) {
3959 break;
3961 } conn_pattern_list_iterate_end;
3963 return TRUE;
3966 /****************************************************************************
3967 /playercolor command handler.
3968 ****************************************************************************/
3969 static bool playercolor_command(struct connection *caller,
3970 char *str, bool check)
3972 enum m_pre_result match_result;
3973 struct player *pplayer;
3974 struct rgbcolor *prgbcolor = NULL;
3975 int ntokens = 0;
3976 char *token[2];
3977 bool ret = TRUE;
3979 ntokens = get_tokens(str, token, 2, TOKEN_DELIMITERS);
3981 if (ntokens != 2) {
3982 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
3983 _("Two arguments needed. See '/help playercolor'."));
3984 ret = FALSE;
3985 goto cleanup;
3988 pplayer = player_by_name_prefix(token[0], &match_result);
3990 if (!pplayer) {
3991 cmd_reply_no_such_player(CMD_PLAYERCOLOR, caller, token[0], match_result);
3992 ret = FALSE;
3993 goto cleanup;
3996 if (!game_was_started() && game.server.plrcolormode != PLRCOL_PLR_SET) {
3997 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL,
3998 _("Can only set player color prior to game start if "
3999 "'plrcolormode' is PLR_SET."));
4000 ret = FALSE;
4001 goto cleanup;
4004 if (0 == fc_strcasecmp(token[1], "reset")) {
4005 if (!game_was_started()) {
4006 prgbcolor = NULL;
4007 } else {
4008 cmd_reply(CMD_PLAYERCOLOR, caller, C_FAIL,
4009 _("Can only unset player color before game starts."));
4010 ret = FALSE;
4011 goto cleanup;
4013 } else if (!rgbcolor_from_hex(&prgbcolor, token[1])) {
4014 cmd_reply(CMD_PLAYERCOLOR, caller, C_SYNTAX,
4015 _("Invalid player color definition. See '/help playercolor'."));
4016 ret = FALSE;
4017 goto cleanup;
4020 if (prgbcolor != NULL) {
4021 players_iterate(pother) {
4022 if (pother != pplayer && pother->rgb != NULL
4023 && rgbcolors_are_equal(pother->rgb, prgbcolor)) {
4024 cmd_reply(CMD_PLAYERCOLOR, caller, C_WARNING,
4025 /* TRANS: "... [c0ffee] for Caesar ... to Hammurabi." */
4026 _("Warning: new color [%s] for %s is identical to %s."),
4027 player_color_ftstr(pother), player_name(pplayer),
4028 player_name(pother));
4030 } players_iterate_end;
4033 if (check) {
4034 goto cleanup;
4037 server_player_set_color(pplayer, prgbcolor);
4038 cmd_reply(CMD_PLAYERCOLOR, caller, C_OK,
4039 _("Color of player %s set to [%s]."), player_name(pplayer),
4040 player_color_ftstr(pplayer));
4042 cleanup:
4044 rgbcolor_destroy(prgbcolor);
4045 free_tokens(token, ntokens);
4047 return ret;
4050 /**************************************************************************
4051 Cutting away a trailing comment by putting a '\0' on the '#'. The
4052 method handles # in single or double quotes. It also takes care of
4053 "\#".
4054 **************************************************************************/
4055 static void cut_comment(char *str)
4057 int i;
4058 bool in_single_quotes = FALSE, in_double_quotes = FALSE;
4060 log_debug("cut_comment(str =' %s')", str);
4062 for (i = 0; i < strlen(str); i++) {
4063 if (str[i] == '"' && !in_single_quotes) {
4064 in_double_quotes = !in_double_quotes;
4065 } else if (str[i] == '\'' && !in_double_quotes) {
4066 in_single_quotes = !in_single_quotes;
4067 } else if (str[i] == '#' && !(in_single_quotes || in_double_quotes)
4068 && (i == 0 || str[i - 1] != '\\')) {
4069 str[i] = '\0';
4070 break;
4073 log_debug("cut_comment: returning '%s'", str);
4076 /**************************************************************************
4077 Handle quit command
4078 **************************************************************************/
4079 static bool quit_game(struct connection *caller, bool check)
4081 if (!check) {
4082 cmd_reply(CMD_QUIT, caller, C_OK, _("Goodbye."));
4083 ggz_report_victory();
4084 server_quit();
4086 return TRUE;
4089 /**************************************************************************
4090 Main entry point for "command input".
4091 **************************************************************************/
4092 bool handle_stdin_input(struct connection *caller, const char *str,
4093 bool check)
4095 return handle_stdin_input_real(caller, str, check, 0);
4098 /**************************************************************************
4099 Handle "command input", which could really come from stdin on console,
4100 or from client chat command, or read from file with -r, etc.
4101 caller==NULL means console, str is the input, which may optionally
4102 start with SERVER_COMMAND_PREFIX character.
4104 If check is TRUE, then do nothing, just check syntax.
4105 **************************************************************************/
4106 static bool handle_stdin_input_real(struct connection *caller,
4107 const char *str, bool check,
4108 int read_recursion)
4110 char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE],
4111 allargs[MAX_LEN_CONSOLE_LINE], full_command[MAX_LEN_CONSOLE_LINE],
4112 *cptr_s, *cptr_d;
4113 int i;
4114 enum command_id cmd;
4115 enum cmdlevel level;
4117 /* notify to the server console */
4118 if (!check && caller) {
4119 con_write(C_COMMENT, "%s: '%s'", caller->username, str);
4122 /* if the caller may not use any commands at all, don't waste any time */
4123 if (may_use_nothing(caller)) {
4124 cmd_reply(CMD_HELP, caller, C_FAIL,
4125 _("Sorry, you are not allowed to use server commands."));
4126 return FALSE;
4129 /* Is it a comment or a blank line? */
4130 /* line is comment if the first non-whitespace character is '#': */
4131 cptr_s = skip_leading_spaces((char *)str);
4132 if (*cptr_s == '\0' || *cptr_s == '#') {
4133 return FALSE;
4136 /* commands may be prefixed with SERVER_COMMAND_PREFIX, even when
4137 given on the server command line - rp */
4138 if (*cptr_s == SERVER_COMMAND_PREFIX) cptr_s++;
4140 for (; *cptr_s != '\0' && !fc_isalnum(*cptr_s); cptr_s++) {
4141 /* nothing */
4144 /* copy the full command, in case we need it for voting purposes. */
4145 sz_strlcpy(full_command, cptr_s);
4148 * cptr_s points now to the beginning of the real command. It has
4149 * skipped leading whitespace, the SERVER_COMMAND_PREFIX and any
4150 * other non-alphanumeric characters.
4152 for (cptr_d = command; *cptr_s != '\0' && fc_isalnum(*cptr_s)
4153 && cptr_d < command + sizeof(command) - 1; cptr_s++, cptr_d++) {
4154 *cptr_d = *cptr_s;
4156 *cptr_d = '\0';
4158 cptr_s = skip_leading_spaces(cptr_s);
4160 /* keep this before we cut everything after a space */
4161 sz_strlcpy(allargs, cptr_s);
4162 cut_comment(allargs);
4164 sz_strlcpy(arg, cptr_s);
4165 cut_comment(arg);
4167 i = strlen(arg) - 1;
4168 while (i > 0 && fc_isspace(arg[i])) {
4169 arg[i--] = '\0';
4172 cmd = command_named(command, FALSE);
4173 if (cmd == CMD_AMBIGUOUS) {
4174 cmd = command_named(command, TRUE);
4175 cmd_reply(cmd, caller, C_SYNTAX,
4176 _("Warning: '%s' interpreted as '%s', but it is ambiguous."
4177 " Try '%shelp'."),
4178 command, command_name_by_number(cmd), caller?"/":"");
4179 } else if (cmd == CMD_UNRECOGNIZED) {
4180 cmd_reply(cmd, caller, C_SYNTAX, _("Unknown command '%s%s'. "
4181 " Try '%shelp'."),
4182 caller ? "/" : "", command, caller ? "/" : "");
4183 return FALSE;
4186 level = command_level(command_by_number(cmd));
4188 if (conn_can_vote(caller, NULL) && level == ALLOW_CTRL
4189 && conn_get_access(caller) == ALLOW_BASIC && !check) {
4190 struct vote *vote;
4191 bool caller_had_vote = (NULL != get_vote_by_caller(caller));
4193 /* Check if the vote command would succeed. If we already have a vote
4194 * going, cancel it in favour of the new vote command. You can only
4195 * have one vote at a time. This is done by vote_new(). */
4196 if (handle_stdin_input_real(caller, full_command, TRUE,
4197 read_recursion + 1)
4198 && (vote = vote_new(caller, allargs, cmd))) {
4199 char votedesc[MAX_LEN_CONSOLE_LINE];
4200 const struct player *teamplr;
4201 const char *what;
4202 struct ft_color color;
4204 if (caller_had_vote) {
4205 cmd_reply(CMD_VOTE, caller, C_COMMENT,
4206 /* TRANS: "vote" as a process */
4207 _("Your new vote canceled your previous vote."));
4210 describe_vote(vote, votedesc, sizeof(votedesc));
4212 if (vote_is_team_only(vote)) {
4213 /* TRANS: "vote" as a process */
4214 what = _("New teamvote");
4215 teamplr = conn_get_player(caller);
4216 color = ftc_vote_team;
4217 } else {
4218 /* TRANS: "vote" as a process */
4219 what = _("New vote");
4220 teamplr = NULL;
4221 color = ftc_vote_public;
4223 notify_team(teamplr, NULL, E_VOTE_NEW, color,
4224 /* TRANS: "[New vote|New teamvote] (number 3)
4225 * by fred: proposed change" */
4226 _("%s (number %d) by %s: %s"), what,
4227 vote->vote_no, caller->username, votedesc);
4229 /* Vote on your own suggestion. */
4230 connection_vote(caller, vote, VOTE_YES);
4231 return TRUE;
4233 } else {
4234 cmd_reply(CMD_VOTE, caller, C_FAIL,
4235 /* TRANS: "vote" as a process */
4236 _("Your new vote (\"%s\") was not "
4237 "legal or was not recognized."), full_command);
4238 return FALSE;
4242 if (caller && !(check && conn_get_access(caller) >= ALLOW_BASIC
4243 && level == ALLOW_CTRL)
4244 && conn_get_access(caller) < level) {
4245 cmd_reply(cmd, caller, C_FAIL,
4246 _("You are not allowed to use this command."));
4247 return FALSE;
4250 if (!check) {
4251 struct conn_list *echo_list = NULL;
4252 bool echo_list_allocated = FALSE;
4254 switch (command_echo(command_by_number(cmd))) {
4255 case CMD_ECHO_NONE:
4256 break;
4257 case CMD_ECHO_ADMINS:
4258 conn_list_iterate(game.est_connections, pconn) {
4259 if (ALLOW_ADMIN <= conn_get_access(pconn)) {
4260 if (NULL == echo_list) {
4261 echo_list = conn_list_new();
4262 echo_list_allocated = TRUE;
4264 conn_list_append(echo_list, pconn);
4266 } conn_list_iterate_end;
4267 break;
4268 case CMD_ECHO_ALL:
4269 echo_list = game.est_connections;
4270 break;
4273 if (NULL != echo_list) {
4274 if (caller) {
4275 notify_conn(echo_list, NULL, E_SETTING, ftc_any,
4276 "%s: '%s %s'", caller->username, command, arg);
4277 } else {
4278 notify_conn(echo_list, NULL, E_SETTING, ftc_server_prompt,
4279 "%s: '%s %s'", _("(server prompt)"), command, arg);
4281 if (echo_list_allocated) {
4282 conn_list_destroy(echo_list);
4287 switch(cmd) {
4288 case CMD_REMOVE:
4289 return remove_player_command(caller, arg, check);
4290 case CMD_SAVE:
4291 return save_command(caller, arg, check);
4292 #ifdef DEBUG
4293 case CMD_SCENSAVE:
4294 return scensave_command(caller, arg, check);
4295 #endif
4296 case CMD_LOAD:
4297 return load_command(caller, arg, check);
4298 case CMD_METAPATCHES:
4299 return metapatches_command(caller, arg, check);
4300 case CMD_METAMESSAGE:
4301 return metamessage_command(caller, arg, check);
4302 case CMD_METACONN:
4303 return metaconnection_command(caller, arg, check);
4304 case CMD_METASERVER:
4305 return metaserver_command(caller, arg, check);
4306 case CMD_HELP:
4307 return show_help(caller, arg);
4308 case CMD_SRVID:
4309 return show_serverid(caller, arg);
4310 case CMD_LIST:
4311 return show_list(caller, arg);
4312 case CMD_AITOGGLE:
4313 return toggle_ai_command(caller, arg, check);
4314 case CMD_TAKE:
4315 return take_command(caller, arg, check);
4316 case CMD_OBSERVE:
4317 return observe_command(caller, arg, check);
4318 case CMD_DETACH:
4319 return detach_command(caller, arg, check);
4320 case CMD_CREATE:
4321 return create_command(caller, arg, check);
4322 case CMD_AWAY:
4323 return set_away(caller, arg, check);
4324 case CMD_NOVICE:
4325 case CMD_EASY:
4326 case CMD_NORMAL:
4327 case CMD_HARD:
4328 case CMD_CHEATING:
4329 case CMD_EXPERIMENTAL:
4330 return set_ai_level_named(caller, arg, command_name_by_number(cmd), check);
4331 case CMD_QUIT:
4332 return quit_game(caller, check);
4333 case CMD_CUT:
4334 return cut_client_connection(caller, arg, check);
4335 case CMD_SHOW:
4336 return show_command(caller, arg, check);
4337 case CMD_EXPLAIN:
4338 return explain_option(caller, arg, check);
4339 case CMD_DEBUG:
4340 return debug_command(caller, arg, check);
4341 case CMD_SET:
4342 return set_command(caller, arg, check);
4343 case CMD_TEAM:
4344 return team_command(caller, arg, check);
4345 case CMD_RULESETDIR:
4346 return set_rulesetdir(caller, arg, check, read_recursion);
4347 case CMD_WALL:
4348 return wall(arg, check);
4349 case CMD_CONNECTMSG:
4350 return connectmsg_command(caller, arg, check);
4351 case CMD_VOTE:
4352 return vote_command(caller, arg, check);
4353 case CMD_CANCELVOTE:
4354 return cancelvote_command(caller, arg, check);
4355 case CMD_READ_SCRIPT:
4356 return read_command(caller, arg, check, read_recursion);
4357 case CMD_WRITE_SCRIPT:
4358 return write_command(caller, arg, check);
4359 case CMD_RESET:
4360 return reset_command(caller, arg, check, read_recursion);
4361 case CMD_LUA:
4362 return lua_command(caller, arg, check);
4363 case CMD_KICK:
4364 return kick_command(caller, arg, check);
4365 case CMD_DELEGATE:
4366 return delegate_command(caller, arg, check);
4367 case CMD_FCDB:
4368 return fcdb_command(caller, arg, check);
4369 case CMD_MAPIMG:
4370 return mapimg_command(caller, arg, check);
4371 case CMD_RFCSTYLE: /* see console.h for an explanation */
4372 if (!check) {
4373 con_set_style(!con_get_style());
4375 return TRUE;
4376 case CMD_CMDLEVEL:
4377 return cmdlevel_command(caller, arg, check);
4378 case CMD_FIRSTLEVEL:
4379 return firstlevel_command(caller, check);
4380 case CMD_TIMEOUT:
4381 return timeout_command(caller, allargs, check);
4382 case CMD_START_GAME:
4383 return start_command(caller, check, FALSE);
4384 case CMD_END_GAME:
4385 return end_command(caller, arg, check);
4386 case CMD_SURRENDER:
4387 return surrender_command(caller, arg, check);
4388 case CMD_IGNORE:
4389 return ignore_command(caller, arg, check);
4390 case CMD_UNIGNORE:
4391 return unignore_command(caller, arg, check);
4392 case CMD_PLAYERCOLOR:
4393 return playercolor_command(caller, arg, check);
4394 case CMD_NUM:
4395 case CMD_UNRECOGNIZED:
4396 case CMD_AMBIGUOUS:
4397 break;
4399 /* should NEVER happen! */
4400 log_error("Unknown command variant: %d.", cmd);
4401 return FALSE;
4404 /**************************************************************************
4405 End the game immediately in a draw.
4406 **************************************************************************/
4407 static bool end_command(struct connection *caller, char *str, bool check)
4409 if (S_S_RUNNING == server_state()) {
4410 if (check) {
4411 return TRUE;
4413 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4414 _("Game is over."));
4415 set_server_state(S_S_OVER);
4416 force_end_of_sniff = TRUE;
4417 cmd_reply(CMD_END_GAME, caller, C_OK,
4418 _("Ending the game. The server will restart once all clients "
4419 "have disconnected."));
4420 return TRUE;
4421 } else {
4422 cmd_reply(CMD_END_GAME, caller, C_FAIL,
4423 _("Cannot end the game: no game running."));
4424 return FALSE;
4428 /**************************************************************************
4429 Concede the game. You still continue playing until all but one player
4430 or team remains un-conceded.
4431 **************************************************************************/
4432 static bool surrender_command(struct connection *caller, char *str, bool check)
4434 if (S_S_RUNNING == server_state() && caller && NULL != caller->playing) {
4435 if (check) {
4436 return TRUE;
4438 notify_conn(game.est_connections, NULL, E_GAME_END, ftc_server,
4439 _("%s has conceded the game and can no longer win."),
4440 player_name(caller->playing));
4441 player_status_add(caller->playing, PSTATUS_SURRENDER);
4442 return TRUE;
4443 } else {
4444 cmd_reply(CMD_SURRENDER, caller, C_FAIL, _("You cannot surrender now."));
4445 return FALSE;
4449 /* Define the possible arguments to the reset command */
4450 #define SPECENUM_NAME reset_args
4451 #define SPECENUM_VALUE0 RESET_GAME
4452 #define SPECENUM_VALUE0NAME "game"
4453 #define SPECENUM_VALUE1 RESET_RULESET
4454 #define SPECENUM_VALUE1NAME "ruleset"
4455 #define SPECENUM_VALUE2 RESET_SCRIPT
4456 #define SPECENUM_VALUE2NAME "script"
4457 #define SPECENUM_VALUE3 RESET_DEFAULT
4458 #define SPECENUM_VALUE3NAME "default"
4459 #include "specenum_gen.h"
4461 /**************************************************************************
4462 Returns possible parameters for the reset command.
4463 **************************************************************************/
4464 static const char *reset_accessor(int i)
4466 i = CLIP(0, i, reset_args_max());
4467 return reset_args_name((enum reset_args) i);
4470 /**************************************************************************
4471 Reload the game settings from the ruleset and reload the init script if
4472 one was used.
4473 **************************************************************************/
4474 static bool reset_command(struct connection *caller, char *arg, bool check,
4475 int read_recursion)
4477 enum m_pre_result result;
4478 int ind;
4480 /* match the argument */
4481 result = match_prefix(reset_accessor, reset_args_max() + 1, 0,
4482 fc_strncasecmp, NULL, arg, &ind);
4484 switch (result) {
4485 case M_PRE_EXACT:
4486 case M_PRE_ONLY:
4487 /* we have a match */
4488 break;
4489 case M_PRE_AMBIGUOUS:
4490 case M_PRE_EMPTY:
4491 /* use 'ruleset' [1] if the game was not started; else use 'game' [2] */
4492 if (S_S_INITIAL == server_state() && game.info.is_new_game) {
4493 cmd_reply(CMD_RESET, caller, C_WARNING,
4494 _("Guessing argument 'ruleset'."));
4495 ind = RESET_RULESET;
4496 } else {
4497 cmd_reply(CMD_RESET, caller, C_WARNING,
4498 _("Guessing argument 'game'."));
4499 ind = RESET_GAME;
4501 break;
4502 case M_PRE_LONG:
4503 case M_PRE_FAIL:
4504 case M_PRE_LAST:
4505 cmd_reply(CMD_RESET, caller, C_FAIL,
4506 _("The valid arguments are: 'game', 'ruleset', 'script' "
4507 "or 'default'."));
4508 return FALSE;
4509 break;
4512 if (check) {
4513 return TRUE;
4516 switch (ind) {
4517 case RESET_GAME:
4518 if (!game.info.is_new_game) {
4519 if (settings_game_reset()) {
4520 cmd_reply(CMD_RESET, caller, C_OK,
4521 _("Reset all settings to the values at the game start."));
4522 } else {
4523 cmd_reply(CMD_RESET, caller, C_FAIL,
4524 _("No saved settings from the game start available."));
4525 return FALSE;
4527 } else {
4528 cmd_reply(CMD_RESET, caller, C_FAIL, _("No game started..."));
4529 return FALSE;
4531 break;
4533 case RESET_RULESET:
4534 /* Restore game settings save in game.ruleset. */
4535 if (reload_rulesets_settings()) {
4536 cmd_reply(CMD_RESET, caller, C_OK,
4537 _("Reset all settings to ruleset values."));
4538 } else {
4539 cmd_reply(CMD_RESET, caller, C_FAIL,
4540 _("Failed to reset settings to ruleset values."));
4542 break;
4544 case RESET_SCRIPT:
4545 cmd_reply(CMD_RESET, caller, C_OK,
4546 _("Reset all settings and rereading the server start "
4547 "script."));
4548 settings_reset();
4549 /* load initial script */
4550 if (NULL != srvarg.script_filename
4551 && !read_init_script_real(NULL, srvarg.script_filename, TRUE, FALSE,
4552 read_recursion + 1)) {
4553 if (NULL != caller) {
4554 cmd_reply(CMD_RESET, caller, C_FAIL,
4555 _("Could not read script file '%s'."),
4556 srvarg.script_filename);
4558 return FALSE;
4560 break;
4562 case RESET_DEFAULT:
4563 cmd_reply(CMD_RESET, caller, C_OK,
4564 _("Reset all settings to default values."));
4565 settings_reset();
4566 break;
4569 send_server_settings(game.est_connections);
4570 cmd_reply(CMD_RESET, caller, C_OK, _("Settings re-initialized."));
4572 /* list changed values */
4573 show_changed(caller, check, read_recursion);
4574 return TRUE;
4577 /* Define the possible arguments to the delegation command */
4578 #define SPECENUM_NAME lua_args
4579 #define SPECENUM_VALUE0 LUA_CMD
4580 #define SPECENUM_VALUE0NAME "cmd"
4581 #define SPECENUM_VALUE1 LUA_FILE
4582 #define SPECENUM_VALUE1NAME "file"
4583 #include "specenum_gen.h"
4585 /*****************************************************************************
4586 Returns possible parameters for the reset command.
4587 *****************************************************************************/
4588 static const char *lua_accessor(int i)
4590 i = CLIP(0, i, lua_args_max());
4591 return lua_args_name((enum lua_args) i);
4594 /*****************************************************************************
4595 Evaluate a line of lua script or a lua script file.
4596 *****************************************************************************/
4597 static bool lua_command(struct connection *caller, char *arg, bool check)
4599 FILE *script_file;
4600 const char extension[] = ".lua", *real_filename = NULL;
4601 char luafile[4096], tilde_filename[4096];
4602 char *tokens[1], *luaarg = NULL;
4603 int ntokens, ind;
4604 enum m_pre_result result;
4605 bool ret = FALSE;
4607 ntokens = get_tokens(arg, tokens, 1, TOKEN_DELIMITERS);
4609 if (ntokens > 0) {
4610 /* match the argument */
4611 result = match_prefix(lua_accessor, lua_args_max() + 1, 0,
4612 fc_strncasecmp, NULL, tokens[0], &ind);
4614 switch (result) {
4615 case M_PRE_EXACT:
4616 case M_PRE_ONLY:
4617 /* We have a match */
4618 luaarg = arg + strlen(lua_args_name(ind));
4619 luaarg = skip_leading_spaces(luaarg);
4620 break;
4621 case M_PRE_EMPTY:
4622 /* Nothing. */
4623 break;
4624 case M_PRE_AMBIGUOUS:
4625 case M_PRE_LONG:
4626 case M_PRE_FAIL:
4627 case M_PRE_LAST:
4628 /* Fall back to depreciated 'lua <script command>' syntax. */
4629 cmd_reply(CMD_LUA, caller, C_SYNTAX,
4630 _("Fall back to old syntax '%slua <script command>'."),
4631 caller ? "/" : "");
4632 ind = LUA_CMD;
4633 luaarg = arg;
4634 break;
4638 if (luaarg == NULL) {
4639 cmd_reply(CMD_LUA, caller, C_FAIL,
4640 _("No lua command or lua script file. See '%shelp lua'."),
4641 caller ? "/" : "");
4642 ret = TRUE;
4643 goto cleanup;
4646 switch (ind) {
4647 case LUA_CMD:
4648 /* Nothing to check. */
4649 break;
4650 case LUA_FILE:
4651 /* Abuse real_filename to find if we already have a .lua extension. */
4652 real_filename = luaarg + strlen(luaarg) - MIN(strlen(extension),
4653 strlen(luaarg));
4654 if (strcmp(real_filename, extension) != 0) {
4655 fc_snprintf(luafile, sizeof(luafile), "%s%s", luaarg, extension);
4656 } else {
4657 sz_strlcpy(luafile, luaarg);
4660 if (is_restricted(caller)) {
4661 if (!is_safe_filename(luafile)) {
4662 cmd_reply(CMD_LUA, caller, C_FAIL,
4663 _("Freeciv script '%s' disallowed for security reasons."),
4664 luafile);
4665 ret = FALSE;
4666 goto cleanup;;
4668 sz_strlcpy(tilde_filename, luafile);
4669 } else {
4670 interpret_tilde(tilde_filename, sizeof(tilde_filename), luafile);
4673 real_filename = fileinfoname(get_data_dirs(), tilde_filename);
4674 if (!real_filename) {
4675 if (is_restricted(caller)) {
4676 cmd_reply(CMD_LUA, caller, C_FAIL,
4677 _("No Freeciv script found by the name '%s'."),
4678 tilde_filename);
4679 ret = FALSE;
4680 goto cleanup;
4682 /* File is outside data directories */
4683 real_filename = tilde_filename;
4685 break;
4688 if (check) {
4689 ret = TRUE;
4690 goto cleanup;
4693 switch (ind) {
4694 case LUA_CMD:
4695 ret = script_server_do_string(caller, luaarg);
4696 break;
4697 case LUA_FILE:
4698 cmd_reply(CMD_LUA, caller, C_COMMENT,
4699 _("Loading Freeciv script file '%s'."), real_filename);
4701 if (is_reg_file_for_access(real_filename, FALSE)
4702 && (script_file = fc_fopen(real_filename, "r"))) {
4703 ret = script_server_do_file(caller, real_filename);
4704 goto cleanup;
4705 } else {
4706 cmd_reply(CMD_LUA, caller, C_FAIL,
4707 _("Cannot read Freeciv script '%s'."), real_filename);
4708 ret = FALSE;
4709 goto cleanup;
4713 cleanup:
4714 free_tokens(tokens, ntokens);
4715 return ret;
4718 /* Define the possible arguments to the delegation command */
4719 #define SPECENUM_NAME delegate_args
4720 #define SPECENUM_VALUE0 DELEGATE_CANCEL
4721 #define SPECENUM_VALUE0NAME "cancel"
4722 #define SPECENUM_VALUE1 DELEGATE_RESTORE
4723 #define SPECENUM_VALUE1NAME "restore"
4724 #define SPECENUM_VALUE2 DELEGATE_SHOW
4725 #define SPECENUM_VALUE2NAME "show"
4726 #define SPECENUM_VALUE3 DELEGATE_TAKE
4727 #define SPECENUM_VALUE3NAME "take"
4728 #define SPECENUM_VALUE4 DELEGATE_TO
4729 #define SPECENUM_VALUE4NAME "to"
4730 #include "specenum_gen.h"
4732 /*****************************************************************************
4733 Returns possible parameters for the 'delegate' command.
4734 *****************************************************************************/
4735 static const char *delegate_accessor(int i)
4737 i = CLIP(0, i, delegate_args_max());
4738 return delegate_args_name((enum delegate_args) i);
4741 /*****************************************************************************
4742 Handle delegation of control.
4743 *****************************************************************************/
4744 static bool delegate_command(struct connection *caller, char *arg,
4745 bool check)
4747 char *tokens[3];
4748 int ntokens, ind = delegate_args_invalid();
4749 enum m_pre_result result;
4750 bool player_specified = FALSE; /* affects messages only */
4751 bool ret = FALSE;
4752 const char *username = NULL;
4753 struct player *dplayer = NULL;
4755 if (!game_was_started()) {
4756 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Game not started - "
4757 "cannot delegate yet."));
4758 return FALSE;
4761 ntokens = get_tokens(arg, tokens, 3, TOKEN_DELIMITERS);
4763 if (ntokens > 0) {
4764 /* match the argument */
4765 result = match_prefix(delegate_accessor, delegate_args_max() + 1, 0,
4766 fc_strncasecmp, NULL, tokens[0], &ind);
4768 switch (result) {
4769 case M_PRE_EXACT:
4770 case M_PRE_ONLY:
4771 /* we have a match */
4772 break;
4773 case M_PRE_EMPTY:
4774 if (caller) {
4775 /* Use 'delegate show' as default. */
4776 ind = DELEGATE_SHOW;
4778 break;
4779 case M_PRE_AMBIGUOUS:
4780 case M_PRE_LONG:
4781 case M_PRE_FAIL:
4782 case M_PRE_LAST:
4783 ind = delegate_args_invalid();
4784 break;
4786 } else {
4787 if (caller) {
4788 /* Use 'delegate show' as default. */
4789 ind = DELEGATE_SHOW;
4793 if (!delegate_args_is_valid(ind)) {
4794 char buf[256] = "";
4795 enum delegate_args valid_args;
4797 for (valid_args = delegate_args_begin();
4798 valid_args != delegate_args_end();
4799 valid_args = delegate_args_next(valid_args)) {
4800 cat_snprintf(buf, sizeof(buf), "'%s'",
4801 delegate_args_name(valid_args));
4802 if (valid_args != delegate_args_max()) {
4803 cat_snprintf(buf, sizeof(buf), ", ");
4807 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4808 /* TRANS: do not translate the command 'delegate'. */
4809 _("Valid arguments for 'delegate' are: %s."), buf);
4810 ret = FALSE;
4811 goto cleanup;
4814 /* Get the data (player, username for delegation) and validate it. */
4815 switch (ind) {
4816 case DELEGATE_CANCEL:
4817 /* delegate cancel [player] */
4818 if (ntokens > 1) {
4819 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4820 player_specified = TRUE;
4821 dplayer = player_by_name_prefix(tokens[1], &result);
4822 if (!dplayer) {
4823 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4824 ret = FALSE;
4825 goto cleanup;
4827 } else {
4828 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4829 _("Command level '%s' or greater needed to modify "
4830 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4831 ret = FALSE;
4832 goto cleanup;
4834 } else {
4835 dplayer = conn_get_player(caller);
4836 if (!dplayer) {
4837 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4838 _("Please specify a player for whom delegation should "
4839 "be canceled."));
4840 ret = FALSE;
4841 goto cleanup;
4844 break;
4845 case DELEGATE_RESTORE:
4846 /* delegate restore */
4847 if (!caller) {
4848 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4849 _("You can't switch players from the console."));
4850 ret = FALSE;
4851 goto cleanup;
4853 break;
4854 case DELEGATE_SHOW:
4855 /* delegate show [player] */
4856 if (ntokens > 1) {
4857 player_specified = TRUE;
4858 dplayer = player_by_name_prefix(tokens[1], &result);
4859 if (!dplayer) {
4860 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4861 ret = FALSE;
4862 goto cleanup;
4864 } else {
4865 dplayer = conn_get_player(caller);
4866 if (!dplayer) {
4867 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4868 _("Please specify a player for whom the delegation should "
4869 "be shown."));
4870 ret = FALSE;
4871 goto cleanup;
4874 break;
4875 case DELEGATE_TAKE:
4876 /* delegate take <player> */
4877 if (!caller) {
4878 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4879 _("You can't switch players from the console."));
4880 ret = FALSE;
4881 goto cleanup;
4883 if (ntokens > 1) {
4884 player_specified = TRUE;
4885 dplayer = player_by_name_prefix(tokens[1], &result);
4886 if (!dplayer) {
4887 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[1], result);
4888 ret = FALSE;
4889 goto cleanup;
4891 } else {
4892 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4893 _("Please specify a player to take control of."));
4894 ret = FALSE;
4895 goto cleanup;
4897 break;
4898 case DELEGATE_TO:
4899 /* delegate to <username> [player] */
4900 if (ntokens > 1) {
4901 username = tokens[1];
4902 } else {
4903 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4904 _("Please specify a user to whom control is to be delegated."));
4905 ret = FALSE;
4906 goto cleanup;
4908 if (ntokens > 2) {
4909 if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
4910 player_specified = TRUE;
4911 dplayer = player_by_name_prefix(tokens[2], &result);
4912 if (!dplayer) {
4913 cmd_reply_no_such_player(CMD_DELEGATE, caller, tokens[2], result);
4914 ret = FALSE;
4915 goto cleanup;
4917 } else {
4918 cmd_reply(CMD_DELEGATE, caller, C_SYNTAX,
4919 _("Command level '%s' or greater needed to modify "
4920 "others' delegations."), cmdlevel_name(ALLOW_ADMIN));
4921 ret = FALSE;
4922 goto cleanup;
4924 } else {
4925 dplayer = conn_controls_player(caller) ? conn_get_player(caller) : NULL;
4926 if (!dplayer) {
4927 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4928 _("You do not control a player."));
4929 ret = FALSE;
4930 goto cleanup;
4933 break;
4936 /* All checks done to this point will give pretty much the same result at
4937 * any time. Checks after this point are more likely to vary over time. */
4938 if (check) {
4939 ret = TRUE;
4940 goto cleanup;
4943 switch (ind) {
4944 case DELEGATE_TO:
4945 /* Delegate control of player to another user. */
4946 fc_assert_ret_val(dplayer, FALSE);
4947 fc_assert_ret_val(username != NULL, FALSE);
4949 /* Forbid delegation of players already controlled by a delegate, and
4950 * those 'put aside' by a delegate.
4951 * For the former, if player is already under active delegate control,
4952 * we wouldn't handle the revocation that would be necessary if their
4953 * delegation changed; and the authority granted to delegates does not
4954 * include the ability to sub-delegate.
4955 * For the latter, allowing control of the 'put aside' player to be
4956 * delegated would break the invariant that whenever a user is connected,
4957 * they are attached to 'their' player. */
4958 if (player_delegation_active(dplayer)) {
4959 if (!player_delegation_get(dplayer)) {
4960 /* Attempting to change a 'put aside' player. Must be admin
4961 * or console. */
4962 fc_assert(player_specified);
4963 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4964 _("Can't delegate control of '%s' belonging to %s while "
4965 "they are controlling another player."),
4966 player_name(dplayer), dplayer->username);
4967 } else if (player_specified) {
4968 /* Admin or console attempting to change a controlled player. */
4969 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4970 _("Can't change delegation of '%s' while controlled by "
4971 "delegate %s."), player_name(dplayer), dplayer->username);
4972 } else {
4973 /* Caller must be the delegate. Give more specific message.
4974 * (We don't know if they thought they were delegating their
4975 * original or delegated player, but we don't allow either.) */
4976 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4977 _("You can't delegate control while you are controlling "
4978 "a delegated player yourself."));
4980 ret = FALSE;
4981 goto cleanup;
4984 /* Forbid delegation to player's original owner
4985 * (from above test we know that dplayer->username is the original now) */
4986 if (strcmp(dplayer->username, username) == 0) {
4987 if (player_specified) {
4988 /* Probably admin or console. */
4989 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4990 /* TRANS: don't translate 'delegate cancel' */
4991 _("%s already owns '%s', so cannot also be delegate. "
4992 "Use '%sdelegate cancel' to cancel an existing "
4993 "delegation."),
4994 username, player_name(dplayer), caller?"/":"");
4995 } else {
4996 /* Player not specified on command line, so they must have been trying
4997 * to delegate control to themself. Give more specific message. */
4998 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
4999 /* TRANS: don't translate '/delegate cancel' */
5000 _("You can't delegate control to yourself. "
5001 "Use '/delegate cancel' to cancel an existing "
5002 "delegation."));
5004 ret = FALSE;
5005 goto cleanup;
5008 /* FIXME: if control was already delegated to someone else, that
5009 * delegation is implicitly canceled. Perhaps we should tell someone. */
5011 player_delegation_set(dplayer, username);
5012 cmd_reply(CMD_DELEGATE, caller, C_OK,
5013 _("Control of player '%s' delegated to user %s."),
5014 player_name(dplayer), username);
5015 ret = TRUE;
5016 goto cleanup;
5017 break;
5019 case DELEGATE_SHOW:
5020 /* Show delegations. */
5021 fc_assert_ret_val(dplayer, FALSE);
5023 if (player_delegation_get(dplayer) == NULL) {
5024 /* No delegation set. */
5025 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5026 _("No delegation defined for '%s'."),
5027 player_name(dplayer));
5028 } else {
5029 cmd_reply(CMD_DELEGATE, caller, C_COMMENT,
5030 _("Control of player '%s' delegated to user %s."),
5031 player_name(dplayer), player_delegation_get(dplayer));
5033 ret = TRUE;
5034 goto cleanup;
5035 break;
5037 case DELEGATE_CANCEL:
5038 if (player_delegation_get(dplayer) == NULL) {
5039 /* No delegation set. */
5040 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5041 _("No delegation defined for '%s'."),
5042 player_name(dplayer));
5043 ret = FALSE;
5044 goto cleanup;
5047 if (player_delegation_active(dplayer)) {
5048 /* Delegation is currently in use. Forcibly break connection. */
5049 struct connection *pdelegate;
5050 /* (Can only happen if admin/console issues this command, as owner
5051 * will end use by their mere presence.) */
5052 fc_assert(player_specified);
5053 pdelegate = conn_by_user(player_delegation_get(dplayer));
5054 fc_assert_ret_val(pdelegate != NULL, FALSE);
5055 if (!connection_delegate_restore(pdelegate)) {
5056 /* Should never happen. Generic failure message. */
5057 log_error("Failed to restore %s's connection as %s during "
5058 "'delegate cancel'.", pdelegate->username,
5059 delegate_player_str(pdelegate->server.delegation.playing,
5060 pdelegate->server.delegation.observer));
5061 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5062 ret = FALSE;
5063 goto cleanup;
5065 notify_conn(pdelegate->self, NULL, E_CONNECTION, ftc_server,
5066 _("Your delegated control of player '%s' was canceled."),
5067 player_name(dplayer));
5070 player_delegation_set(dplayer, NULL);
5071 cmd_reply(CMD_DELEGATE, caller, C_OK, _("Delegation of '%s' canceled."),
5072 player_name(dplayer));
5073 ret = TRUE;
5074 goto cleanup;
5075 break;
5077 case DELEGATE_TAKE:
5078 /* Try to take another player. */
5079 fc_assert_ret_val(dplayer, FALSE);
5080 fc_assert_ret_val(caller, FALSE);
5082 if (caller->server.delegation.status) {
5083 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5084 /* TRANS: don't translate '/delegate restore'. */
5085 _("You are already controlling a delegated player. "
5086 "Use '/delegate restore' to relinquish control of your "
5087 "current player first."));
5088 ret = FALSE;
5089 goto cleanup;
5092 /* Don't allow 'put aside' players to be delegated; the invariant is
5093 * that while the owning user is connected to the server, they are
5094 * in sole control of 'their' player. */
5095 if (conn_controls_player(caller)
5096 && player_delegation_get(conn_get_player(caller)) != NULL) {
5097 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5098 /* TRANS: don't translate '/delegate cancel'. */
5099 _("Can't take player while you have delegated control "
5100 "yourself. Use '/delegate cancel' to cancel your own "
5101 "delegation first."));
5102 ret = FALSE;
5103 goto cleanup;
5106 /* Taking your own player makes no sense. */
5107 if (conn_controls_player(caller)
5108 && dplayer == conn_get_player(caller)) {
5109 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("You already control '%s'."),
5110 player_name(conn_get_player(caller)));
5111 ret = FALSE;
5112 goto cleanup;
5115 if (!player_delegation_get(dplayer)
5116 || strcmp(player_delegation_get(dplayer), caller->username) != 0) {
5117 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5118 _("Control of player '%s' has not been delegated to you."),
5119 player_name(dplayer));
5120 ret = FALSE;
5121 goto cleanup;
5124 /* If the player is controlled by another user, fail. */
5125 if (dplayer->is_connected) {
5126 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5127 _("Another user already controls player '%s'."),
5128 player_name(dplayer));
5129 ret = FALSE;
5130 goto cleanup;
5133 if (!connection_delegate_take(caller, dplayer)) {
5134 /* Should never happen. Generic failure message. */
5135 log_error("%s failed to take control of '%s' during 'delegate take'.",
5136 caller->username, player_name(dplayer));
5137 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5138 ret = FALSE;
5139 goto cleanup;
5142 cmd_reply(CMD_DELEGATE, caller, C_OK,
5143 _("%s is now controlling player '%s'."), caller->username,
5144 player_name(conn_get_player(caller)));
5145 ret = TRUE;
5146 goto cleanup;
5147 break;
5149 case DELEGATE_RESTORE:
5150 /* Delegate user relinquishes control of delegated player, returning to
5151 * previous view (e.g. observer) if any. */
5152 fc_assert_ret_val(caller, FALSE);
5154 if (!caller->server.delegation.status) {
5155 cmd_reply(CMD_DELEGATE, caller, C_FAIL,
5156 _("You are not currently controlling a delegated player."));
5157 ret = FALSE;
5158 goto cleanup;
5161 if (!connection_delegate_restore(caller)) {
5162 /* Should never happen. Generic failure message. */
5163 log_error("Failed to restore %s's connection as %s during "
5164 "'delegate restore'.", caller->username,
5165 delegate_player_str(caller->server.delegation.playing,
5166 caller->server.delegation.observer));
5167 cmd_reply(CMD_DELEGATE, caller, C_FAIL, _("Unexpected failure."));
5168 ret = FALSE;
5169 goto cleanup;
5172 cmd_reply(CMD_DELEGATE, caller, C_OK,
5173 /* TRANS: "<user> is now connected to <player>" where <player>
5174 * can also be "global observer" or "nothing" */
5175 _("%s is now connected as %s."), caller->username,
5176 delegate_player_str(conn_get_player(caller), caller->observer));
5177 ret = TRUE;
5178 goto cleanup;
5179 break;
5182 cleanup:
5183 free_tokens(tokens, ntokens);
5184 return ret;
5187 /*****************************************************************************
5188 Return static string describing what a connection is connected to.
5189 *****************************************************************************/
5190 static const char *delegate_player_str(struct player *pplayer, bool observer)
5192 static struct astring buf;
5194 if (pplayer) {
5195 if (observer) {
5196 astr_set(&buf, _("%s (observer)"), player_name(pplayer));
5197 } else {
5198 astr_set(&buf, "%s", player_name(pplayer));
5200 } else if (observer) {
5201 astr_set(&buf, "%s", _("global observer"));
5202 } else {
5203 /* TRANS: in place of player name or "global observer" */
5204 astr_set(&buf, "%s", _("nothing"));
5207 return astr_str(&buf);
5210 /* Define the possible arguments to the mapimg command */
5211 /* map image layers */
5212 #define SPECENUM_NAME mapimg_args
5213 #define SPECENUM_VALUE0 MAPIMG_COLORTEST
5214 #define SPECENUM_VALUE0NAME "colortest"
5215 #define SPECENUM_VALUE1 MAPIMG_CREATE
5216 #define SPECENUM_VALUE1NAME "create"
5217 #define SPECENUM_VALUE2 MAPIMG_DEFINE
5218 #define SPECENUM_VALUE2NAME "define"
5219 #define SPECENUM_VALUE3 MAPIMG_DELETE
5220 #define SPECENUM_VALUE3NAME "delete"
5221 #define SPECENUM_VALUE4 MAPIMG_SHOW
5222 #define SPECENUM_VALUE4NAME "show"
5223 #define SPECENUM_COUNT MAPIMG_COUNT
5224 #include "specenum_gen.h"
5226 /**************************************************************************
5227 Returns possible parameters for the mapimg command.
5228 **************************************************************************/
5229 static const char *mapimg_accessor(int i)
5231 i = CLIP(0, i, mapimg_args_max());
5232 return mapimg_args_name((enum mapimg_args) i);
5235 /**************************************************************************
5236 Handle mapimg command
5237 **************************************************************************/
5238 static bool mapimg_command(struct connection *caller, char *arg, bool check)
5240 enum m_pre_result result;
5241 int ind, ntokens, id;
5242 char *token[2];
5243 bool ret = TRUE;
5245 ntokens = get_tokens(arg, token, 2, TOKEN_DELIMITERS);
5247 if (ntokens > 0) {
5248 /* match the argument */
5249 result = match_prefix(mapimg_accessor, MAPIMG_COUNT, 0,
5250 fc_strncasecmp, NULL, token[0], &ind);
5252 switch (result) {
5253 case M_PRE_EXACT:
5254 case M_PRE_ONLY:
5255 /* we have a match */
5256 break;
5257 case M_PRE_AMBIGUOUS:
5258 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5259 _("Ambiguous 'mapimg' command."));
5260 ret = FALSE;
5261 goto cleanup;
5262 break;
5263 case M_PRE_EMPTY:
5264 /* use 'show' as default */
5265 ind = MAPIMG_SHOW;
5266 break;
5267 case M_PRE_LONG:
5268 case M_PRE_FAIL:
5269 case M_PRE_LAST:
5271 char buf[256] = "";
5272 enum mapimg_args valid_args;
5274 for (valid_args = mapimg_args_begin();
5275 valid_args != mapimg_args_end();
5276 valid_args = mapimg_args_next(valid_args)) {
5277 cat_snprintf(buf, sizeof(buf), "'%s'",
5278 mapimg_args_name(valid_args));
5279 if (valid_args != mapimg_args_max()) {
5280 cat_snprintf(buf, sizeof(buf), ", ");
5284 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5285 _("The valid arguments are: %s."), buf);
5286 ret = FALSE;
5287 goto cleanup;
5289 break;
5291 } else {
5292 /* use 'show' as default */
5293 ind = MAPIMG_SHOW;
5296 switch (ind) {
5297 case MAPIMG_DEFINE:
5298 if (ntokens == 1) {
5299 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5300 _("Missing argument for 'mapimg define'."));
5301 ret = FALSE;
5302 } else {
5303 /* 'mapimg define <mapstr>' */
5304 if (!mapimg_define(token[1], check)) {
5305 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5306 _("Can't use definition: %s."), mapimg_error());
5307 ret = FALSE;
5308 } else if (check) {
5309 /* Validated OK, bail out now */
5310 goto cleanup;
5311 } else if (game_was_started()
5312 && mapimg_isvalid(mapimg_count() - 1) == NULL) {
5313 /* game was started - error in map image definition check */
5314 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5315 _("Can't use definition: %s."), mapimg_error());
5316 ret = FALSE;
5317 } else {
5318 int id = mapimg_count() - 1;
5319 char str[MAX_LEN_MAPDEF];
5321 mapimg_id2str(id, str, sizeof(str));
5322 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Defined as map image "
5323 "definition %d: '%s'."),
5324 id, str);
5327 break;
5329 case MAPIMG_DELETE:
5330 if (ntokens == 1) {
5331 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5332 _("Missing argument for 'mapimg delete'."));
5333 ret = FALSE;
5334 } else if (ntokens == 2 && strcmp(token[1], "all") == 0) {
5335 /* 'mapimg delete all' */
5336 if (check) {
5337 goto cleanup;
5340 for (id = 0; id < mapimg_count(); id++) {
5341 mapimg_delete(id);
5343 cmd_reply(CMD_MAPIMG, caller, C_OK, _("All map image definitions "
5344 "deleted."));
5345 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5346 /* 'mapimg delete <id>' */
5347 if (check) {
5348 goto cleanup;
5351 if (!mapimg_delete(id)) {
5352 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5353 _("Couldn't delete definition: %s."), mapimg_error());
5354 ret = FALSE;
5355 } else {
5356 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map image definition %d "
5357 "deleted."), id);
5359 } else {
5360 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5361 _("Bad argument for 'mapimg delete': '%s'."), token[1]);
5362 ret = FALSE;
5364 break;
5366 case MAPIMG_SHOW:
5367 if (ntokens < 2 || (ntokens == 2 && strcmp(token[1], "all") == 0)) {
5368 /* 'mapimg show' or 'mapimg show all' */
5369 if (check) {
5370 goto cleanup;
5372 show_mapimg(caller, CMD_MAPIMG);
5373 } else if (ntokens == 2 && sscanf(token[1], "%d", &id) != 0) {
5374 char str[2048];
5375 /* 'mapimg show <id>' */
5376 if (check) {
5377 goto cleanup;
5380 if (mapimg_show(id, str, sizeof(str), TRUE)) {
5381 cmd_reply(CMD_MAPIMG, caller, C_OK, "%s", str);
5382 } else {
5383 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5384 _("Couldn't show definition: %s."), mapimg_error());
5385 ret = FALSE;
5387 } else {
5388 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5389 _("Bad argument for 'mapimg show': '%s'."), token[1]);
5390 ret = FALSE;
5392 break;
5394 case MAPIMG_COLORTEST:
5395 if (check) {
5396 goto cleanup;
5399 mapimg_colortest(game.server.save_name, NULL);
5400 cmd_reply(CMD_MAPIMG, caller, C_OK, _("Map color test images saved."));
5401 break;
5403 case MAPIMG_CREATE:
5404 if (ntokens < 2) {
5405 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5406 _("Missing argument for 'mapimg create'."));
5407 ret = FALSE;
5408 goto cleanup;
5411 if (strcmp(token[1], "all") == 0) {
5412 /* 'mapimg create all' */
5413 if (check) {
5414 goto cleanup;
5417 for (id = 0; id < mapimg_count(); id++) {
5418 struct mapdef *pmapdef = mapimg_isvalid(id);
5420 if (pmapdef == NULL
5421 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5422 srvarg.saves_pathname)) {
5423 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5424 _("Error saving map image %d: %s."), id, mapimg_error());
5425 ret = FALSE;
5428 } else if (sscanf(token[1], "%d", &id) != 0) {
5429 struct mapdef *pmapdef;
5431 /* 'mapimg create <id>' */
5432 if (check) {
5433 goto cleanup;
5436 pmapdef = mapimg_isvalid(id);
5437 if (pmapdef == NULL
5438 || !mapimg_create(pmapdef, TRUE, game.server.save_name,
5439 srvarg.saves_pathname)) {
5440 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5441 _("Error saving map image %d: %s."), id, mapimg_error());
5442 ret = FALSE;
5444 } else {
5445 cmd_reply(CMD_MAPIMG, caller, C_FAIL,
5446 _("Bad argument for 'mapimg create': '%s'."), token[1]);
5447 ret = FALSE;
5449 break;
5452 cleanup:
5454 free_tokens(token, ntokens);
5456 return ret;
5459 /* Define the possible arguments to the fcdb command */
5460 #define SPECENUM_NAME fcdb_args
5461 #define SPECENUM_VALUE0 FCDB_RELOAD
5462 #define SPECENUM_VALUE0NAME "reload"
5463 #define SPECENUM_VALUE1 FCDB_LUA
5464 #define SPECENUM_VALUE1NAME "lua"
5465 #define SPECENUM_COUNT FCDB_COUNT
5466 #include "specenum_gen.h"
5468 /**************************************************************************
5469 Returns possible parameters for the fcdb command.
5470 **************************************************************************/
5471 static const char *fcdb_accessor(int i)
5473 i = CLIP(0, i, fcdb_args_max());
5474 return fcdb_args_name((enum fcdb_args) i);
5477 /**************************************************************************
5478 Handle the freeciv database script module.
5479 **************************************************************************/
5480 static bool fcdb_command(struct connection *caller, char *arg, bool check)
5482 enum m_pre_result result;
5483 int ind, ntokens;
5484 char *token[1];
5485 bool ret = TRUE;
5486 bool usage = FALSE;
5488 #ifndef HAVE_FCDB
5489 cmd_reply(CMD_FCDB, caller, C_FAIL,
5490 _("Freeciv database script deactivated at compile time."));
5491 return FALSE;
5492 #endif
5494 ntokens = get_tokens(arg, token, 1, TOKEN_DELIMITERS);
5496 if (ntokens > 0) {
5497 /* match the argument */
5498 result = match_prefix(fcdb_accessor, FCDB_COUNT, 0,
5499 fc_strncasecmp, NULL, token[0], &ind);
5501 switch (result) {
5502 case M_PRE_EXACT:
5503 case M_PRE_ONLY:
5504 /* we have a match */
5505 break;
5506 case M_PRE_AMBIGUOUS:
5507 cmd_reply(CMD_FCDB, caller, C_FAIL,
5508 _("Ambiguous fcdb command."));
5509 ret = FALSE;
5510 goto cleanup;
5511 break;
5512 case M_PRE_EMPTY:
5513 case M_PRE_LONG:
5514 case M_PRE_FAIL:
5515 case M_PRE_LAST:
5516 usage = TRUE;
5517 break;
5519 } else {
5520 usage = TRUE;
5523 if (usage) {
5524 char buf[256] = "";
5525 enum fcdb_args valid_args;
5527 for (valid_args = fcdb_args_begin();
5528 valid_args != fcdb_args_end();
5529 valid_args = fcdb_args_next(valid_args)) {
5530 cat_snprintf(buf, sizeof(buf), "'%s'",
5531 fcdb_args_name(valid_args));
5532 if (valid_args != fcdb_args_max()) {
5533 cat_snprintf(buf, sizeof(buf), ", ");
5537 cmd_reply(CMD_FCDB, caller, C_FAIL,
5538 _("The valid arguments are: %s."), buf);
5539 ret = FALSE;
5540 goto cleanup;
5543 if (check) {
5544 ret = TRUE;
5545 goto cleanup;
5548 switch (ind) {
5549 case FCDB_RELOAD:
5550 /* Reload database lua script. */
5551 script_fcdb_free();
5552 script_fcdb_init(NULL);
5553 break;
5555 case FCDB_LUA:
5556 /* Skip whitespaces. */
5557 arg = skip_leading_spaces(arg);
5558 /* Skip the base argument 'lua'. */
5559 arg += 3;
5560 /* Now execute the scriptlet. */
5561 ret = script_fcdb_do_string(caller, arg);
5562 break;
5565 cleanup:
5567 free_tokens(token, ntokens);
5569 return ret;
5572 /**************************************************************************
5573 Send start command related message
5574 **************************************************************************/
5575 static void start_cmd_reply(struct connection *caller, bool notify, char *msg)
5577 cmd_reply(CMD_START_GAME, caller, C_FAIL, "%s", msg);
5578 if (notify) {
5579 notify_conn(NULL, NULL, E_SETTING, ftc_server, "%s", msg);
5583 /**************************************************************************
5584 Handle start command. Notify all players about errors if notify set.
5585 **************************************************************************/
5586 bool start_command(struct connection *caller, bool check, bool notify)
5588 int human_players;
5590 switch (server_state()) {
5591 case S_S_INITIAL:
5592 /* Sanity check scenario */
5593 if (game.info.is_new_game && !check) {
5594 if (0 < map_startpos_count()
5595 && game.server.max_players > map_startpos_count()) {
5596 /* If we load a pre-generated map (i.e., a scenario) it is possible
5597 * to increase the number of players beyond the number supported by
5598 * the scenario. The solution is a hack: cut the extra players
5599 * when the game starts. */
5600 log_verbose("Reduced maxplayers from %d to %d to fit "
5601 "to the number of start positions.",
5602 game.server.max_players, map_startpos_count());
5603 game.server.max_players = map_startpos_count();
5606 if (normal_player_count() > game.server.max_players) {
5607 int i;
5608 struct player *pplayer;
5610 for (i = player_slot_count() - 1; i >= 0; i--) {
5611 pplayer = player_by_number(i);
5612 if (pplayer) {
5613 server_remove_player(pplayer);
5615 if (normal_player_count() <= game.server.max_players) {
5616 break;
5620 log_verbose("Had to cut down the number of players to the "
5621 "number of map start positions, there must be "
5622 "something wrong with the savegame or you "
5623 "adjusted the maxplayers value.");
5627 human_players = 0;
5628 players_iterate(plr) {
5629 if (!plr->ai_controlled) {
5630 human_players++;
5632 } players_iterate_end;
5634 /* check min_players.
5635 * Allow continuing of savegames where some of the original
5636 * players have died */
5637 if (game.info.is_new_game
5638 && human_players < game.server.min_players) {
5639 start_cmd_reply(caller, notify,
5640 _("Not enough human players; game will not start."));
5641 return FALSE;
5642 } else if (player_count() < 1) {
5643 /* At least one player required */
5644 start_cmd_reply(caller, notify,
5645 _("No players; game will not start."));
5646 return FALSE;
5647 } else if (normal_player_count() > server.playable_nations) {
5648 start_cmd_reply(caller, notify,
5649 _("Not enough nations for all players; game will "
5650 "not start."));
5651 return FALSE;
5652 } else if (check) {
5653 return TRUE;
5654 } else if (!caller) {
5655 if (notify) {
5656 /* Called from handle_player_ready()
5657 * Last player just toggled ready-status. */
5658 notify_conn(NULL, NULL, E_SETTING, ftc_game_start,
5659 _("All players are ready; starting game."));
5661 start_game();
5662 return TRUE;
5663 } else if (NULL == caller->playing || !caller->playing->is_connected) {
5664 /* A detached or observer player can't do /start. */
5665 return TRUE;
5666 } else {
5667 /* This might trigger recursive call to start_command() if this is
5668 * last player who gets ready. In that case caller is NULL. */
5669 handle_player_ready(caller->playing, player_number(caller->playing), TRUE);
5670 return TRUE;
5672 case S_S_OVER:
5673 start_cmd_reply(caller, notify,
5674 /* TRANS: given when /start is invoked during gameover. */
5675 _("Cannot start the game: the game is waiting for all clients "
5676 "to disconnect."));
5677 return FALSE;
5678 case S_S_RUNNING:
5679 start_cmd_reply(caller, notify,
5680 /* TRANS: given when /start is invoked while the game
5681 * is running. */
5682 _("Cannot start the game: it is already running."));
5683 return FALSE;
5685 log_error("Unknown server state variant: %d.", server_state());
5686 return FALSE;
5689 /**************************************************************************
5690 Handle cut command
5691 **************************************************************************/
5692 static bool cut_client_connection(struct connection *caller, char *name,
5693 bool check)
5695 enum m_pre_result match_result;
5696 struct connection *ptarget;
5698 ptarget = conn_by_user_prefix(name, &match_result);
5700 if (!ptarget) {
5701 cmd_reply_no_such_conn(CMD_CUT, caller, name, match_result);
5702 return FALSE;
5703 } else if (check) {
5704 return TRUE;
5707 if (conn_controls_player(ptarget)) {
5708 /* If we cut the connection, unassign the login name.*/
5709 sz_strlcpy(ptarget->playing->username, ANON_USER_NAME);
5712 cmd_reply(CMD_CUT, caller, C_DISCONNECTED,
5713 _("Cutting connection %s."), ptarget->username);
5714 connection_close_server(ptarget, _("connection cut"));
5716 return TRUE;
5720 /****************************************************************************
5721 Utility for 'kick_hash' tables.
5722 ****************************************************************************/
5723 static time_t *time_duplicate(const time_t *t)
5725 time_t *d = fc_malloc(sizeof(*d));
5726 *d = *t;
5727 return d;
5730 /****************************************************************************
5731 Returns FALSE if the connection isn't kicked and can connect the server
5732 normally.
5733 ****************************************************************************/
5734 bool conn_is_kicked(struct connection *pconn, int *time_remaining)
5736 time_t time_of_addr_kick, time_of_user_kick;
5737 time_t now, time_of_kick = 0;
5739 if (NULL != time_remaining) {
5740 *time_remaining = 0;
5743 fc_assert_ret_val(NULL != kick_table_by_addr, FALSE);
5744 fc_assert_ret_val(NULL != kick_table_by_user, FALSE);
5745 fc_assert_ret_val(NULL != pconn, FALSE);
5747 if (kick_hash_lookup(kick_table_by_addr, pconn->server.ipaddr,
5748 &time_of_addr_kick)) {
5749 time_of_kick = time_of_addr_kick;
5751 if (kick_hash_lookup(kick_table_by_user, pconn->username,
5752 &time_of_user_kick)
5753 && time_of_user_kick > time_of_kick) {
5754 time_of_kick = time_of_user_kick;
5757 if (0 == time_of_kick) {
5758 return FALSE; /* Not found. */
5761 now = time(NULL);
5762 if (now - time_of_kick > game.server.kick_time) {
5763 /* Kick timeout expired. */
5764 if (0 != time_of_addr_kick) {
5765 kick_hash_remove(kick_table_by_addr, pconn->server.ipaddr);
5767 if (0 != time_of_user_kick) {
5768 kick_hash_remove(kick_table_by_user, pconn->username);
5770 return FALSE;
5773 if (NULL != time_remaining) {
5774 *time_remaining = game.server.kick_time - (now - time_of_kick);
5776 return TRUE;
5779 /****************************************************************************
5780 Kick command handler.
5781 ****************************************************************************/
5782 static bool kick_command(struct connection *caller, char *name, bool check)
5784 char ipaddr[FC_MEMBER_SIZEOF(struct connection, server.ipaddr)];
5785 struct connection *pconn;
5786 enum m_pre_result match_result;
5787 time_t now;
5789 remove_leading_trailing_spaces(name);
5790 pconn = conn_by_user_prefix(name, &match_result);
5791 if (NULL == pconn) {
5792 cmd_reply_no_such_conn(CMD_KICK, caller, name, match_result);
5793 return FALSE;
5796 if (NULL != caller && ALLOW_ADMIN > conn_get_access(caller)) {
5797 const int MIN_UNIQUE_CONNS = 3;
5798 const char *unique_ipaddr[MIN_UNIQUE_CONNS];
5799 int i, num_unique_connections = 0;
5801 if (pconn == caller) {
5802 cmd_reply(CMD_KICK, caller, C_FAIL, _("You may not kick yourself."));
5803 return FALSE;
5806 conn_list_iterate(game.est_connections, aconn) {
5807 for (i = 0; i < num_unique_connections; i++) {
5808 if (0 == strcmp(unique_ipaddr[i], aconn->server.ipaddr)) {
5809 /* Already listed. */
5810 break;
5813 if (i >= num_unique_connections) {
5814 num_unique_connections++;
5815 if (MIN_UNIQUE_CONNS <= num_unique_connections) {
5816 /* We have enought already. */
5817 break;
5819 unique_ipaddr[num_unique_connections - 1] = aconn->server.ipaddr;
5821 } conn_list_iterate_end;
5823 if (MIN_UNIQUE_CONNS > num_unique_connections) {
5824 cmd_reply(CMD_KICK, caller, C_FAIL,
5825 _("There must be at least %d unique connections to the "
5826 "server for this command to be valid."), MIN_UNIQUE_CONNS);
5827 return FALSE;
5831 if (check) {
5832 return TRUE;
5835 sz_strlcpy(ipaddr, pconn->server.ipaddr);
5836 now = time(NULL);
5837 kick_hash_replace(kick_table_by_addr, ipaddr, now);
5839 conn_list_iterate(game.all_connections, aconn) {
5840 if (0 != strcmp(ipaddr, aconn->server.ipaddr)) {
5841 continue;
5844 if (conn_controls_player(aconn)) {
5845 /* Unassign the username. */
5846 sz_strlcpy(aconn->playing->username, ANON_USER_NAME);
5849 kick_hash_replace(kick_table_by_user, aconn->username, now);
5851 connection_close_server(aconn, _("kicked"));
5852 } conn_list_iterate_end;
5854 return TRUE;
5858 /**************************************************************************
5859 Show caller introductory help about the server. help_cmd is the command
5860 the player used.
5861 **************************************************************************/
5862 static void show_help_intro(struct connection *caller,
5863 enum command_id help_cmd)
5865 /* This is formated like extra_help entries for settings and commands: */
5866 char *help = fc_strdup(
5867 _("Welcome - this is the introductory help text for the Freeciv "
5868 "server.\n"
5869 "\n"
5870 "Two important server concepts are Commands and Options. Commands, "
5871 "such as 'help', are used to interact with the server. Some commands "
5872 "take one or more arguments, separated by spaces. In many cases "
5873 "commands and command arguments may be abbreviated. Options are "
5874 "settings which control the server as it is running.\n"
5875 "\n"
5876 "To find out how to get more information about commands and options, "
5877 "use 'help help'.\n"
5878 "\n"
5879 "For the impatient, the main commands to get going are:\n"
5880 " show - to see current options\n"
5881 " set - to set options\n"
5882 " start - to start the game once players have connected\n"
5883 " save - to save the current game\n"
5884 " quit - to exit"));
5886 fc_break_lines(help, LINE_BREAK);
5887 cmd_reply(help_cmd, caller, C_COMMENT, "%s", help);
5888 FC_FREE(help);
5891 /**************************************************************************
5892 Show the caller detailed help for the single COMMAND given by id.
5893 help_cmd is the command the player used.
5894 **************************************************************************/
5895 static void show_help_command(struct connection *caller,
5896 enum command_id help_cmd,
5897 enum command_id id)
5899 const struct command *cmd = command_by_number(id);
5901 if (command_short_help(cmd)) {
5902 cmd_reply(help_cmd, caller, C_COMMENT,
5903 /* TRANS: <untranslated name> - translated short help */
5904 _("Command: %s - %s"),
5905 command_name(cmd),
5906 command_short_help(cmd));
5907 } else {
5908 cmd_reply(help_cmd, caller, C_COMMENT,
5909 /* TRANS: <untranslated name> */
5910 _("Command: %s"),
5911 command_name(cmd));
5913 if (command_synopsis(cmd)) {
5914 /* line up the synopsis lines: */
5915 const char *syn = _("Synopsis: ");
5916 size_t synlen = strlen(syn);
5917 char prefix[40];
5919 fc_snprintf(prefix, sizeof(prefix), "%*s", (int) synlen, " ");
5920 cmd_reply_prefix(help_cmd, caller, C_COMMENT, prefix,
5921 "%s%s", syn, command_synopsis(cmd));
5923 cmd_reply(help_cmd, caller, C_COMMENT,
5924 _("Level: %s"), cmdlevel_name(command_level(cmd)));
5926 char *help = command_extra_help(cmd);
5928 if (help) {
5929 fc_break_lines(help, LINE_BREAK);
5930 cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
5931 cmd_reply_prefix(help_cmd, caller, C_COMMENT, " ", " %s", help);
5932 FC_FREE(help);
5937 /**************************************************************************
5938 Show the caller list of COMMANDS.
5939 help_cmd is the command the player used.
5940 **************************************************************************/
5941 static void show_help_command_list(struct connection *caller,
5942 enum command_id help_cmd)
5944 enum command_id i;
5946 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
5947 cmd_reply(help_cmd, caller, C_COMMENT,
5948 _("The following server commands are available:"));
5949 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
5950 if(!caller && con_get_style()) {
5951 for (i=0; i<CMD_NUM; i++) {
5952 cmd_reply(help_cmd, caller, C_COMMENT, "%s", command_name_by_number(i));
5954 } else {
5955 char buf[MAX_LEN_CONSOLE_LINE];
5956 int j;
5958 buf[0] = '\0';
5959 for (i=0, j=0; i<CMD_NUM; i++) {
5960 if (may_use(caller, i)) {
5961 cat_snprintf(buf, sizeof(buf), "%-19s", command_name_by_number(i));
5962 if((++j % 4) == 0) {
5963 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
5964 buf[0] = '\0';
5968 if (buf[0] != '\0')
5969 cmd_reply(help_cmd, caller, C_COMMENT, "%s", buf);
5971 cmd_reply(help_cmd, caller, C_COMMENT, horiz_line);
5974 /**************************************************************************
5975 Send a reply to the caller listing the matched names from an ambiguous
5976 prefix.
5977 **************************************************************************/
5978 static void cmd_reply_matches(enum command_id cmd,
5979 struct connection *caller,
5980 m_pre_accessor_fn_t accessor_fn,
5981 int *matches, int num_matches)
5983 char buf[MAX_LEN_MSG];
5984 const char *src, *end;
5985 char *dest;
5986 int i;
5988 if (accessor_fn == NULL || matches == NULL || num_matches < 1) {
5989 return;
5992 dest = buf;
5993 end = buf + sizeof(buf) - 1;
5995 for (i = 0; i < num_matches && dest < end; i++) {
5996 src = accessor_fn(matches[i]);
5997 if (!src) {
5998 continue;
6000 if (dest != buf) {
6001 *dest++ = ' ';
6003 while (*src != '\0' && dest < end) {
6004 *dest++ = *src++;
6007 *dest = '\0';
6009 cmd_reply(cmd, caller, C_COMMENT, _("Possible matches: %s"), buf);
6012 /**************************************************************************
6013 Additional 'help' arguments
6014 **************************************************************************/
6015 #define SPECENUM_NAME help_general_args
6016 #define SPECENUM_VALUE0 HELP_GENERAL_COMMANDS
6017 #define SPECENUM_VALUE0NAME "commands"
6018 #define SPECENUM_VALUE1 HELP_GENERAL_OPTIONS
6019 #define SPECENUM_VALUE1NAME "options"
6020 #define SPECENUM_COUNT HELP_GENERAL_COUNT
6021 #include "specenum_gen.h"
6023 /**************************************************************************
6024 Unified indices for help arguments:
6025 CMD_NUM - Server commands
6026 HELP_GENERAL_NUM - General help arguments, above
6027 settings_number() - Server options
6028 **************************************************************************/
6029 #define HELP_ARG_NUM (CMD_NUM + HELP_GENERAL_COUNT + settings_number())
6031 /**************************************************************************
6032 Convert unified helparg index to string; see above.
6033 **************************************************************************/
6034 static const char *helparg_accessor(int i)
6036 if (i < CMD_NUM) {
6037 return command_name_by_number(i);
6040 i -= CMD_NUM;
6041 if (i < HELP_GENERAL_COUNT) {
6042 return help_general_args_name((enum help_general_args) i);
6045 i -= HELP_GENERAL_COUNT;
6046 return optname_accessor(i);
6049 /**************************************************************************
6050 Handle help command
6051 **************************************************************************/
6052 static bool show_help(struct connection *caller, char *arg)
6054 int matches[64], num_matches = 0;
6055 enum m_pre_result match_result;
6056 int ind;
6058 fc_assert_ret_val(!may_use_nothing(caller), FALSE);
6059 /* no commands means no help, either */
6061 match_result = match_prefix_full(helparg_accessor, HELP_ARG_NUM, 0,
6062 fc_strncasecmp, NULL, arg, &ind, matches,
6063 ARRAY_SIZE(matches), &num_matches);
6065 if (match_result==M_PRE_EMPTY) {
6066 show_help_intro(caller, CMD_HELP);
6067 return FALSE;
6069 if (match_result==M_PRE_AMBIGUOUS) {
6070 cmd_reply(CMD_HELP, caller, C_FAIL,
6071 _("Help argument '%s' is ambiguous."), arg);
6072 cmd_reply_matches(CMD_HELP, caller, helparg_accessor,
6073 matches, num_matches);
6074 return FALSE;
6076 if (match_result==M_PRE_FAIL) {
6077 cmd_reply(CMD_HELP, caller, C_FAIL,
6078 _("No match for help argument '%s'."), arg);
6079 return FALSE;
6082 /* other cases should be above */
6083 fc_assert_ret_val(match_result < M_PRE_AMBIGUOUS, FALSE);
6085 if (ind < CMD_NUM) {
6086 show_help_command(caller, CMD_HELP, ind);
6087 return TRUE;
6089 ind -= CMD_NUM;
6091 if (ind == HELP_GENERAL_OPTIONS) {
6092 show_help_option_list(caller, CMD_HELP);
6093 return TRUE;
6095 if (ind == HELP_GENERAL_COMMANDS) {
6096 show_help_command_list(caller, CMD_HELP);
6097 return TRUE;
6099 ind -= HELP_GENERAL_COUNT;
6101 if (ind < settings_number()) {
6102 show_help_option(caller, CMD_HELP, ind);
6103 return TRUE;
6106 /* should have finished by now */
6107 log_error("Bug in show_help!");
6108 return FALSE;
6111 /****************************************************************************
6112 List connections; initially mainly for debugging
6113 ****************************************************************************/
6114 static void show_connections(struct connection *caller)
6116 char buf[MAX_LEN_CONSOLE_LINE];
6118 cmd_reply(CMD_LIST, caller, C_COMMENT,
6119 _("List of connections to server:"));
6120 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6122 if (conn_list_size(game.all_connections) == 0) {
6123 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no connections>"));
6124 } else {
6125 conn_list_iterate(game.all_connections, pconn) {
6126 sz_strlcpy(buf, conn_description(pconn));
6127 if (pconn->established) {
6128 cat_snprintf(buf, sizeof(buf), " command access level %s",
6129 cmdlevel_name(pconn->access_level));
6131 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6132 } conn_list_iterate_end;
6134 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6137 /*****************************************************************************
6138 List all delegations of the current game.
6139 *****************************************************************************/
6140 static void show_delegations(struct connection *caller)
6142 bool empty = TRUE;
6144 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of all delegations:"));
6145 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6147 players_iterate(pplayer) {
6148 const char *delegate_to = player_delegation_get(pplayer);
6149 if (delegate_to != NULL) {
6150 const char *owner =
6151 player_delegation_active(pplayer) ? pplayer->server.orig_username
6152 : pplayer->username;
6153 fc_assert(owner);
6154 cmd_reply(CMD_LIST, caller, C_COMMENT,
6155 /* TRANS: last %s is either " (active)" or empty string */
6156 _("%s delegates control over player '%s' to user %s%s."),
6157 owner, player_name(pplayer), delegate_to,
6158 player_delegation_active(pplayer) ? _(" (active)") : "");
6159 empty = FALSE;
6161 } players_iterate_end;
6163 if (empty) {
6164 cmd_reply(CMD_LIST, caller, C_COMMENT, _("No delegations defined."));
6167 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6170 /****************************************************************************
6171 Show the ignore list of the
6172 ****************************************************************************/
6173 static bool show_ignore(struct connection *caller)
6175 char buf[128];
6176 int n = 1;
6178 if (NULL == caller) {
6179 cmd_reply(CMD_IGNORE, caller, C_FAIL,
6180 _("That would be rather silly, since you are not a player."));
6181 return FALSE;
6184 if (0 == conn_pattern_list_size(caller->server.ignore_list)) {
6185 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list is empty."));
6186 return TRUE;
6189 cmd_reply(CMD_LIST, caller, C_COMMENT, _("Your ignore list:"));
6190 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6191 conn_pattern_list_iterate(caller->server.ignore_list, ppattern) {
6192 conn_pattern_to_string(ppattern, buf, sizeof(buf));
6193 cmd_reply(CMD_LIST, caller, C_COMMENT, "%d: %s", n++, buf);
6194 } conn_pattern_list_iterate_end;
6195 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6197 return TRUE;
6200 /****************************************************************************
6201 Show the list of the players of the game.
6202 ****************************************************************************/
6203 void show_players(struct connection *caller)
6205 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of players:"));
6206 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6208 if (player_count() == 0) {
6209 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6210 } else {
6211 players_iterate(pplayer) {
6212 char buf[MAX_LEN_CONSOLE_LINE];
6213 int n;
6215 /* Low access level callers don't get to see barbarians in list: */
6216 if (is_barbarian(pplayer) && caller
6217 && (caller->access_level < ALLOW_CTRL)) {
6218 continue;
6221 /* The output for each player looks like:
6223 * <Player name> [color]: Team[, Nation][, Username][, Status]
6224 * AI/Barbarian/Human[, AI type, skill level][, Connections]
6225 * [Details for each connection]
6228 /* '<Player name> [color]: [Nation][, Username][, Status]' */
6229 buf[0] = '\0';
6230 cat_snprintf(buf, sizeof(buf), "%s [%s]: %s", player_name(pplayer),
6231 player_color_ftstr(pplayer),
6232 team_name_translation(pplayer->team));
6233 if (!game.info.is_new_game) {
6234 cat_snprintf(buf, sizeof(buf), ", %s",
6235 nation_adjective_for_player(pplayer));
6237 if (strlen(pplayer->username) > 0
6238 && strcmp(pplayer->username, "nouser") != 0) {
6239 cat_snprintf(buf, sizeof(buf), _(", user %s"), pplayer->username);
6241 if (S_S_INITIAL == server_state() && pplayer->is_connected) {
6242 if (pplayer->is_ready) {
6243 sz_strlcat(buf, _(", ready"));
6244 } else {
6245 /* Emphasizes this */
6246 n = strlen(buf);
6247 featured_text_apply_tag(_(", not ready"),
6248 buf + n, sizeof(buf) - n,
6249 TTT_COLOR, 1, FT_OFFSET_UNSET,
6250 ftc_changed);
6252 } else if (!pplayer->is_alive) {
6253 sz_strlcat(buf, _(", Dead"));
6255 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6257 /* ' AI/Barbarian/Human[, skill level][, Connections]' */
6258 buf[0] = '\0';
6259 if (is_barbarian(pplayer)) {
6260 sz_strlcat(buf, _("Barbarian"));
6261 } else if (pplayer->ai_controlled) {
6262 sz_strlcat(buf, _("AI"));
6263 } else {
6264 sz_strlcat(buf, _("Human"));
6266 if(pplayer->ai_controlled) {
6267 cat_snprintf(buf, sizeof(buf), _(", %s"), ai_name(pplayer->ai));
6268 cat_snprintf(buf, sizeof(buf), _(", difficulty level %s"),
6269 ai_level_name(pplayer->ai_common.skill_level));
6271 n = conn_list_size(pplayer->connections);
6272 if (n > 0) {
6273 cat_snprintf(buf, sizeof(buf),
6274 PL_(", %d connection:", ", %d connections:", n), n);
6276 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6278 /* ' [Details for each connection]' */
6279 conn_list_iterate(pplayer->connections, pconn) {
6280 fc_snprintf(buf, sizeof(buf),
6281 _("%s from %s (command access level %s), "
6282 "bufsize=%dkb"), pconn->username, pconn->addr,
6283 cmdlevel_name(pconn->access_level),
6284 (pconn->send_buffer->nsize >> 10));
6285 if (pconn->observer) {
6286 sz_strlcat(buf, _(" (observer mode)"));
6288 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", buf);
6289 } conn_list_iterate_end;
6290 } players_iterate_end;
6292 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6295 /****************************************************************************
6296 List scenarios. We look both in the DATA_PATH and DATA_PATH/scenario
6297 ****************************************************************************/
6298 static void show_scenarios(struct connection *caller)
6300 char buf[MAX_LEN_CONSOLE_LINE];
6301 struct fileinfo_list *files;
6303 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of scenarios available:"));
6304 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6306 files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE);
6308 fileinfo_list_iterate(files, pfile) {
6309 fc_snprintf(buf, sizeof(buf), "%s", pfile->name);
6310 cmd_reply(CMD_LIST, caller, C_COMMENT, "%s", buf);
6311 } fileinfo_list_iterate_end;
6312 fileinfo_list_destroy(files);
6314 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6317 /****************************************************************************
6318 List nation sets in the current ruleset.
6319 ****************************************************************************/
6320 static void show_nationsets(struct connection *caller)
6322 cmd_reply(CMD_LIST, caller, C_COMMENT,
6323 /* TRANS: don't translate text between '' */
6324 _("List of nation sets available for 'nationset' option:"));
6325 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6327 nation_sets_iterate(pset) {
6328 const char *description = nation_set_description(pset);
6329 int num_nations = 0;
6330 nations_iterate(pnation) {
6331 if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) {
6332 num_nations++;
6334 } nations_iterate_end;
6335 cmd_reply(CMD_LIST, caller, C_COMMENT,
6336 /* TRANS: nation set description; %d refers to number of playable
6337 * nations in set */
6338 PL_(" %-10s %s (%d playable)",
6339 " %-10s %s (%d playable)", num_nations),
6340 nation_set_rule_name(pset), nation_set_name_translation(pset),
6341 num_nations);
6342 if (strlen(description) > 0) {
6343 static const char prefix[] = " ";
6344 char *translated = fc_strdup(_(description));
6345 fc_break_lines(translated, LINE_BREAK);
6346 cmd_reply_prefix(CMD_LIST, caller, C_COMMENT, prefix, "%s%s",
6347 prefix, translated);
6349 } nation_sets_iterate_end;
6351 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6354 /****************************************************************************
6355 Show a list of teams on the command line.
6356 ****************************************************************************/
6357 static void show_teams(struct connection *caller)
6359 /* Currently this just lists all teams (typically 32 of them) with their
6360 * names and # of players on the team. This could probably be improved. */
6361 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of teams:"));
6362 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6364 teams_iterate(pteam) {
6365 const struct player_list *members = team_members(pteam);
6367 /* PL_() is needed here because some languages may differentiate
6368 * between 2 and 3 (although English does not). */
6369 cmd_reply(CMD_LIST, caller, C_COMMENT,
6370 /* TRANS: There will always be at least 2 players here. */
6371 PL_("%2d : '%s' : %d player :",
6372 "%2d : '%s' : %d players :",
6373 player_list_size(members)),
6374 team_index(pteam), team_name_translation(pteam),
6375 player_list_size(members));
6376 player_list_iterate(members, pplayer) {
6377 cmd_reply(CMD_LIST, caller, C_COMMENT, " %s", player_name(pplayer));
6378 } player_list_iterate_end;
6379 } teams_iterate_end;
6381 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6384 /****************************************************************************
6385 Show a list of all map image definitions on the command line.
6386 ****************************************************************************/
6387 static void show_mapimg(struct connection *caller, enum command_id cmd)
6389 int id;
6391 if (mapimg_count() == 0) {
6392 cmd_reply(cmd, caller, C_OK, _("No map image definitions."));
6393 } else {
6394 cmd_reply(cmd, caller, C_COMMENT, _("List of map image definitions:"));
6395 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6396 for (id = 0; id < mapimg_count(); id++) {
6397 char str[MAX_LEN_MAPDEF] = "";
6398 mapimg_show(id, str, sizeof(str), FALSE);
6399 cmd_reply(cmd, caller, C_COMMENT, _("[%2d] %s"), id, str);
6401 cmd_reply(cmd, caller, C_COMMENT, horiz_line);
6405 /****************************************************************************
6406 Show a list of all players with the assigned color.
6407 ****************************************************************************/
6408 static void show_colors(struct connection *caller)
6410 cmd_reply(CMD_LIST, caller, C_COMMENT, _("List of player colors:"));
6411 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6412 if (player_count() == 0) {
6413 cmd_reply(CMD_LIST, caller, C_COMMENT, _("<no players>"));
6414 } else {
6415 players_iterate(pplayer) {
6416 cmd_reply(CMD_LIST, caller, C_COMMENT, _("%s (user %s): [%s]"),
6417 player_name(pplayer), pplayer->username,
6418 player_color_ftstr(pplayer));
6419 } players_iterate_end;
6421 cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line);
6424 /****************************************************************************
6425 '/list' arguments
6426 **************************************************************************/
6427 #define SPECENUM_NAME list_args
6428 #define SPECENUM_VALUE0 LIST_COLORS
6429 #define SPECENUM_VALUE0NAME "colors"
6430 #define SPECENUM_VALUE1 LIST_CONNECTIONS
6431 #define SPECENUM_VALUE1NAME "connections"
6432 #define SPECENUM_VALUE2 LIST_DELEGATIONS
6433 #define SPECENUM_VALUE2NAME "delegations"
6434 #define SPECENUM_VALUE3 LIST_IGNORE
6435 #define SPECENUM_VALUE3NAME "ignored users"
6436 #define SPECENUM_VALUE4 LIST_MAPIMG
6437 #define SPECENUM_VALUE4NAME "map image definitions"
6438 #define SPECENUM_VALUE5 LIST_PLAYERS
6439 #define SPECENUM_VALUE5NAME "players"
6440 #define SPECENUM_VALUE6 LIST_SCENARIOS
6441 #define SPECENUM_VALUE6NAME "scenarios"
6442 #define SPECENUM_VALUE7 LIST_NATIONSETS
6443 #define SPECENUM_VALUE7NAME "nationsets"
6444 #define SPECENUM_VALUE8 LIST_TEAMS
6445 #define SPECENUM_VALUE8NAME "teams"
6446 #define SPECENUM_VALUE9 LIST_VOTES
6447 #define SPECENUM_VALUE9NAME "votes"
6448 #include "specenum_gen.h"
6450 /**************************************************************************
6451 Returns possible parameters for the list command.
6452 **************************************************************************/
6453 static const char *list_accessor(int i)
6455 i = CLIP(0, i, list_args_max());
6456 return list_args_name((enum list_args) i);
6459 /**************************************************************************
6460 Show list of players or connections, or connection statistics.
6461 **************************************************************************/
6462 static bool show_list(struct connection *caller, char *arg)
6464 enum m_pre_result match_result;
6465 int ind_int;
6466 enum list_args ind;
6468 remove_leading_trailing_spaces(arg);
6469 match_result = match_prefix(list_accessor, list_args_max() + 1, 0,
6470 fc_strncasecmp, NULL, arg, &ind_int);
6471 ind = ind_int;
6473 if (match_result > M_PRE_EMPTY) {
6474 cmd_reply(CMD_LIST, caller, C_SYNTAX,
6475 _("Bad list argument: '%s'. Try '%shelp list'."),
6476 arg, (caller?"/":""));
6477 return FALSE;
6480 if (match_result == M_PRE_EMPTY) {
6481 ind = LIST_PLAYERS;
6484 switch(ind) {
6485 case LIST_COLORS:
6486 show_colors(caller);
6487 return TRUE;
6488 case LIST_CONNECTIONS:
6489 show_connections(caller);
6490 return TRUE;
6491 case LIST_DELEGATIONS:
6492 show_delegations(caller);
6493 return TRUE;
6494 case LIST_IGNORE:
6495 return show_ignore(caller);
6496 case LIST_MAPIMG:
6497 show_mapimg(caller, CMD_LIST);
6498 return TRUE;
6499 case LIST_PLAYERS:
6500 show_players(caller);
6501 return TRUE;
6502 case LIST_SCENARIOS:
6503 show_scenarios(caller);
6504 return TRUE;
6505 case LIST_NATIONSETS:
6506 show_nationsets(caller);
6507 return TRUE;
6508 case LIST_TEAMS:
6509 show_teams(caller);
6510 return TRUE;
6511 case LIST_VOTES:
6512 show_votes(caller);
6513 return TRUE;
6516 cmd_reply(CMD_LIST, caller, C_FAIL,
6517 "Internal error: ind %d in show_list", ind);
6518 log_error("Internal error: ind %d in show_list", ind);
6519 return FALSE;
6522 #ifdef HAVE_LIBREADLINE
6523 /********************* RL completion functions ***************************/
6524 /* To properly complete both commands, player names, options and filenames
6525 there is one array per type of completion with the commands that
6526 the type is relevant for.
6529 /**************************************************************************
6530 A generalised generator function: text and state are "standard"
6531 parameters to a readline generator function;
6532 num is number of possible completions, or -1 if this is not known and
6533 index2str should be iterated until it returns NULL;
6534 index2str is a function which returns each possible completion string
6535 by index (it may return NULL).
6536 **************************************************************************/
6537 static char *generic_generator(const char *text, int state, int num,
6538 const char*(*index2str)(int))
6540 static int list_index, len;
6541 const char *name = ""; /* dummy non-NULL string */
6542 char *mytext = local_to_internal_string_malloc(text);
6544 /* This function takes a string (text) in the local format and must return
6545 * a string in the local format. However comparisons are done against
6546 * names that are in the internal format (UTF-8). Thus we have to convert
6547 * the text function from the local to the internal format before doing
6548 * the comparison, and convert the string we return *back* to the
6549 * local format when returning it. */
6551 /* If this is a new word to complete, initialize now. This includes
6552 saving the length of TEXT for efficiency, and initializing the index
6553 variable to 0. */
6554 if (state == 0) {
6555 list_index = 0;
6556 len = strlen(mytext);
6559 /* Return the next name which partially matches: */
6560 while ((num < 0 && name) || (list_index < num)) {
6561 name = index2str(list_index);
6562 list_index++;
6564 if (name != NULL && fc_strncasecmp(name, mytext, len) == 0) {
6565 free(mytext);
6566 return internal_to_local_string_malloc(name);
6569 free(mytext);
6571 /* If no names matched, then return NULL. */
6572 return ((char *)NULL);
6575 /**************************************************************************
6576 The valid commands at the root of the prompt.
6577 **************************************************************************/
6578 static char *command_generator(const char *text, int state)
6580 return generic_generator(text, state, CMD_NUM, command_name_by_number);
6583 /**************************************************************************
6584 The valid arguments to "set" and "explain"
6585 **************************************************************************/
6586 static char *option_generator(const char *text, int state)
6588 return generic_generator(text, state, settings_number(), optname_accessor);
6591 /**************************************************************************
6592 The valid arguments to "show"
6593 **************************************************************************/
6594 static char *olevel_generator(const char *text, int state)
6596 return generic_generator(text, state, settings_number() + OLEVELS_NUM + 1,
6597 olvlname_accessor);
6600 /**************************************************************************
6601 Accessor for values of the enum/bitwise option defined by
6602 'completion_option'.
6603 **************************************************************************/
6604 static int completion_option;
6605 static const char *option_value_accessor(int idx) {
6606 const struct setting *pset = setting_by_number(completion_option);
6607 switch (setting_type(pset)) {
6608 case SSET_ENUM:
6609 return setting_enum_val(pset, idx, FALSE);
6610 break;
6611 case SSET_BITWISE:
6612 return setting_bitwise_bit(pset, idx, FALSE);
6613 break;
6614 default:
6615 fc_assert_ret_val(0, NULL);
6619 /**************************************************************************
6620 The valid arguments to "set OPT", where OPT is the enumerated or
6621 bitwise option previously defined by completion_option
6622 **************************************************************************/
6623 static char *option_value_generator(const char *text, int state)
6625 return generic_generator(text, state, -1, option_value_accessor);
6628 /**************************************************************************
6629 Access player name.
6630 **************************************************************************/
6631 static const char *playername_accessor(int idx)
6633 const struct player_slot *pslot = player_slot_by_number(idx);
6635 if (!player_slot_is_used(pslot)) {
6636 return NULL;
6639 return player_name(player_slot_get_player(pslot));
6642 /**************************************************************************
6643 The valid playername arguments.
6644 **************************************************************************/
6645 static char *player_generator(const char *text, int state)
6647 return generic_generator(text, state, player_slot_count(),
6648 playername_accessor);
6651 /**************************************************************************
6652 Access connection user name, from game.all_connections.
6653 **************************************************************************/
6654 static const char *connection_name_accessor(int idx)
6656 return conn_list_get(game.all_connections, idx)->username;
6659 /**************************************************************************
6660 The valid connection user name arguments.
6661 **************************************************************************/
6662 static char *connection_generator(const char *text, int state)
6664 return generic_generator(text, state, conn_list_size(game.all_connections),
6665 connection_name_accessor);
6668 /**************************************************************************
6669 Extra accessor function since cmdlevel_name() takes enum argument, not int.
6670 **************************************************************************/
6671 static const char *cmdlevel_arg1_accessor(int idx)
6673 return cmdlevel_name(idx);
6676 /**************************************************************************
6677 The valid first argument to "cmdlevel"
6678 **************************************************************************/
6679 static char *cmdlevel_arg1_generator(const char *text, int state)
6681 return generic_generator(text, state, cmdlevel_max()+1,
6682 cmdlevel_arg1_accessor);
6685 /**************************************************************************
6686 Accessor for the second argument to "cmdlevel": "first" or "new" or
6687 a connection name.
6688 **************************************************************************/
6689 static const char *cmdlevel_arg2_accessor(int idx)
6691 return ((idx==0) ? "first" :
6692 (idx==1) ? "new" :
6693 connection_name_accessor(idx-2));
6696 /**************************************************************************
6697 The valid arguments for the second argument to "cmdlevel".
6698 **************************************************************************/
6699 static char *cmdlevel_arg2_generator(const char *text, int state)
6701 return generic_generator(text, state,
6702 /* "first", "new", connection names */
6703 2 + conn_list_size(game.all_connections),
6704 cmdlevel_arg2_accessor);
6707 /**************************************************************************
6708 Accessor for the second argument to "create": ai type name
6709 **************************************************************************/
6710 static const char *aitype_accessor(int idx)
6712 return get_ai_type(idx)->name;
6715 /**************************************************************************
6716 The valid arguments for the second argument to "create".
6717 **************************************************************************/
6718 static char *aitype_generator(const char *text, int state)
6720 return generic_generator(text, state, ai_type_get_count(),
6721 aitype_accessor);
6724 /**************************************************************************
6725 The valid arguments for the argument to "reset".
6726 **************************************************************************/
6727 static char *reset_generator(const char *text, int state)
6729 return generic_generator(text, state, reset_args_max() + 1, reset_accessor);
6732 /**************************************************************************
6733 The valid arguments for the argument to "vote".
6734 **************************************************************************/
6735 static char *vote_generator(const char *text, int state)
6737 return generic_generator(text, state, -1, vote_arg_accessor);
6740 /**************************************************************************
6741 The valid arguments for the first argument to "delegate".
6742 **************************************************************************/
6743 static char *delegate_generator(const char *text, int state)
6745 return generic_generator(text, state, delegate_args_max() + 1,
6746 delegate_accessor);
6749 /**************************************************************************
6750 The valid arguments for the first argument to "mapimg".
6751 **************************************************************************/
6752 static char *mapimg_generator(const char *text, int state)
6754 return generic_generator(text, state, mapimg_args_max() + 1,
6755 mapimg_accessor);
6758 /**************************************************************************
6759 The valid arguments for the argument to "fcdb".
6760 **************************************************************************/
6761 static char *fcdb_generator(const char *text, int state)
6763 return generic_generator(text, state, FCDB_COUNT, fcdb_accessor);
6766 /**************************************************************************
6767 The valid arguments for the argument to "lua".
6768 **************************************************************************/
6769 static char *lua_generator(const char *text, int state)
6771 return generic_generator(text, state, lua_args_max() + 1, lua_accessor);
6774 /**************************************************************************
6775 The valid first arguments to "help".
6776 **************************************************************************/
6777 static char *help_generator(const char *text, int state)
6779 return generic_generator(text, state, HELP_ARG_NUM, helparg_accessor);
6782 /**************************************************************************
6783 The valid first arguments to "list".
6784 **************************************************************************/
6785 static char *list_generator(const char *text, int state)
6787 return generic_generator(text, state, list_args_max() + 1, list_accessor);
6790 /**************************************************************************
6791 Generalised version of contains_str_before_start, which searches the
6792 N'th token in rl_line_buffer (0=first).
6793 **************************************************************************/
6794 static bool contains_token_before_start(int start, int token, const char *arg,
6795 bool allow_fluff)
6797 char *str_itr = rl_line_buffer;
6798 int arg_len = strlen(arg);
6800 /* Swallow unwanted tokens and their preceding delimiters */
6801 while (token--) {
6802 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6803 str_itr++;
6805 while (str_itr < rl_line_buffer + start && fc_isalnum(*str_itr)) {
6806 str_itr++;
6810 /* Swallow any delimiters before the token we're interested in */
6811 while (str_itr < rl_line_buffer + start && !fc_isalnum(*str_itr)) {
6812 str_itr++;
6815 if (fc_strncasecmp(str_itr, arg, arg_len) != 0) {
6816 return FALSE;
6818 str_itr += arg_len;
6820 if (fc_isalnum(*str_itr)) {
6821 /* Not a distinct word. */
6822 return FALSE;
6825 if (!allow_fluff) {
6826 for (; str_itr < rl_line_buffer + start; str_itr++) {
6827 if (fc_isalnum(*str_itr)) {
6828 return FALSE;
6833 return TRUE;
6836 /**************************************************************************
6837 Returns whether the text between the start of rl_line_buffer and the
6838 start position is of the form [non-alpha]*cmd[non-alpha]*
6839 allow_fluff changes the regexp to [non-alpha]*cmd[non-alpha].*
6840 **************************************************************************/
6841 static bool contains_str_before_start(int start, const char *cmd,
6842 bool allow_fluff)
6844 return contains_token_before_start(start, 0, cmd, allow_fluff);
6847 /**************************************************************************
6848 Return whether we are completing command name. This can be either
6849 command itself, or argument to 'help'.
6850 **************************************************************************/
6851 static bool is_command(int start)
6853 char *str_itr;
6855 if (contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE))
6856 return TRUE;
6858 /* if there is only it is also OK */
6859 str_itr = rl_line_buffer;
6860 while (str_itr - rl_line_buffer < start) {
6861 if (fc_isalnum(*str_itr)) {
6862 return FALSE;
6864 str_itr++;
6866 return TRUE;
6869 /**************************************************************************
6870 number of tokens in rl_line_buffer before start
6871 **************************************************************************/
6872 static int num_tokens(int start)
6874 int res = 0;
6875 bool alnum = FALSE;
6876 char *chptr = rl_line_buffer;
6878 while (chptr - rl_line_buffer < start) {
6879 if (fc_isalnum(*chptr)) {
6880 if (!alnum) {
6881 alnum = TRUE;
6882 res++;
6884 } else {
6885 alnum = FALSE;
6887 chptr++;
6890 return res;
6893 /**************************************************************************
6894 Commands that may be followed by a player name
6895 **************************************************************************/
6896 static const int player_cmd[] = {
6897 CMD_AITOGGLE,
6898 CMD_NOVICE,
6899 CMD_EASY,
6900 CMD_NORMAL,
6901 CMD_HARD,
6902 CMD_CHEATING,
6903 CMD_EXPERIMENTAL,
6904 CMD_REMOVE,
6905 CMD_TEAM,
6906 CMD_PLAYERCOLOR,
6910 /**************************************************************************
6911 Return whether we are completing player name argument.
6912 **************************************************************************/
6913 static bool is_player(int start)
6915 int i = 0;
6917 while (player_cmd[i] != -1) {
6918 if (contains_str_before_start(start, command_name_by_number(player_cmd[i]), FALSE)) {
6919 return TRUE;
6921 i++;
6924 return FALSE;
6927 /**************************************************************************
6928 Commands that may be followed by a connection name
6929 **************************************************************************/
6930 static const int connection_cmd[] = {
6931 CMD_CUT,
6932 CMD_KICK,
6936 /**************************************************************************
6937 Return whether we are completing connection name argument.
6938 **************************************************************************/
6939 static bool is_connection(int start)
6941 int i = 0;
6943 while (connection_cmd[i] != -1) {
6944 if (contains_str_before_start(start,
6945 command_name_by_number(connection_cmd[i]),
6946 FALSE)) {
6947 return TRUE;
6949 i++;
6952 return FALSE;
6955 /**************************************************************************
6956 Return whether we are completing cmdlevel command argument 2.
6957 **************************************************************************/
6958 static bool is_cmdlevel_arg2(int start)
6960 return (contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), TRUE)
6961 && num_tokens(start) == 2);
6964 /**************************************************************************
6965 Return whether we are completing cmdlevel command argument.
6966 **************************************************************************/
6967 static bool is_cmdlevel_arg1(int start)
6969 return contains_str_before_start(start, command_name_by_number(CMD_CMDLEVEL), FALSE);
6972 /**************************************************************************
6973 Commands that may be followed by a server option name
6975 CMD_SHOW is handled by option_level_cmd, which is for both option levels
6976 and server options
6977 **************************************************************************/
6978 static const int server_option_cmd[] = {
6979 CMD_EXPLAIN,
6980 CMD_SET,
6984 /**************************************************************************
6985 Returns TRUE if the readline buffer string matches a server option at
6986 the given position.
6987 **************************************************************************/
6988 static bool is_server_option(int start)
6990 int i = 0;
6992 while (server_option_cmd[i] != -1) {
6993 if (contains_str_before_start(start, command_name_by_number(server_option_cmd[i]),
6994 FALSE)) {
6995 return TRUE;
6997 i++;
7000 return FALSE;
7003 /**************************************************************************
7004 Commands that may be followed by an option level or server option
7005 **************************************************************************/
7006 static const int option_level_cmd[] = {
7007 CMD_SHOW,
7011 /**************************************************************************
7012 Returns true if the readline buffer string matches an option level or an
7013 option at the given position.
7014 **************************************************************************/
7015 static bool is_option_level(int start)
7017 int i = 0;
7019 while (option_level_cmd[i] != -1) {
7020 if (contains_str_before_start(start, command_name_by_number(option_level_cmd[i]),
7021 FALSE)) {
7022 return TRUE;
7024 i++;
7027 return FALSE;
7030 /**************************************************************************
7031 Returns TRUE if the readline buffer string is such that we expect an
7032 enumerated value at the given position. The option for which values
7033 should be completed is written to opt_p.
7034 **************************************************************************/
7035 static bool is_enum_option_value(int start, int *opt_p)
7037 if (contains_str_before_start(start, command_name_by_number(CMD_SET),
7038 TRUE)) {
7039 settings_iterate(SSET_ALL, pset) {
7040 if (setting_type(pset) != SSET_ENUM
7041 && setting_type(pset) != SSET_BITWISE) {
7042 continue;
7044 /* Allow a single token for enum options, multiple for bitwise
7045 * (the separator | will separate tokens for these purposes) */
7046 if (contains_token_before_start(start, 1, setting_name(pset),
7047 setting_type(pset) == SSET_BITWISE)) {
7048 *opt_p = setting_number(pset);
7049 /* Suppress appended space for bitwise options (user may want |) */
7050 rl_completion_suppress_append = (setting_type(pset) == SSET_BITWISE);
7051 return TRUE;
7053 } settings_iterate_end;
7055 return FALSE;
7058 /**************************************************************************
7059 Commands that may be followed by a filename
7060 **************************************************************************/
7061 static const int filename_cmd[] = {
7062 CMD_LOAD,
7063 CMD_SAVE,
7064 CMD_READ_SCRIPT,
7065 CMD_WRITE_SCRIPT,
7069 /**************************************************************************
7070 Return whether we are completing filename.
7071 **************************************************************************/
7072 static bool is_filename(int start)
7074 int i = 0;
7076 while (filename_cmd[i] != -1) {
7077 if (contains_str_before_start(start, command_name_by_number(filename_cmd[i]), FALSE)) {
7078 return TRUE;
7080 i++;
7083 return FALSE;
7086 /**************************************************************************
7087 Return whether we are completing second argument for create command
7088 **************************************************************************/
7089 static bool is_create_arg2(int start)
7091 return (contains_str_before_start(start, command_name_by_number(CMD_CREATE), TRUE)
7092 && num_tokens(start) == 2);
7095 /**************************************************************************
7096 Return whether we are completing argument for reset command
7097 **************************************************************************/
7098 static bool is_reset(int start)
7100 return contains_str_before_start(start,
7101 command_name_by_number(CMD_RESET),
7102 FALSE);
7105 /**************************************************************************
7106 Return whether we are completing argument for vote command
7107 **************************************************************************/
7108 static bool is_vote(int start)
7110 return contains_str_before_start(start,
7111 command_name_by_number(CMD_VOTE),
7112 FALSE);
7115 /**************************************************************************
7116 Return whether we are completing first argument for delegate command
7117 **************************************************************************/
7118 static bool is_delegate_arg1(int start)
7120 return contains_str_before_start(start,
7121 command_name_by_number(CMD_DELEGATE),
7122 FALSE);
7125 /**************************************************************************
7126 Return whether we are completing first argument for mapimg command
7127 **************************************************************************/
7128 static bool is_mapimg(int start)
7130 return contains_str_before_start(start,
7131 command_name_by_number(CMD_MAPIMG),
7132 FALSE);
7135 /**************************************************************************
7136 Return whether we are completing argument for fcdb command
7137 **************************************************************************/
7138 static bool is_fcdb(int start)
7140 return contains_str_before_start(start,
7141 command_name_by_number(CMD_FCDB),
7142 FALSE);
7145 /**************************************************************************
7146 Return whether we are completing argument for lua command
7147 **************************************************************************/
7148 static bool is_lua(int start)
7150 return contains_str_before_start(start,
7151 command_name_by_number(CMD_LUA),
7152 FALSE);
7155 /**************************************************************************
7156 Return whether we are completing help command argument.
7157 **************************************************************************/
7158 static bool is_help(int start)
7160 return contains_str_before_start(start, command_name_by_number(CMD_HELP), FALSE);
7163 /**************************************************************************
7164 Return whether we are completing list command argument.
7165 **************************************************************************/
7166 static bool is_list(int start)
7168 return contains_str_before_start(start, command_name_by_number(CMD_LIST), FALSE);
7171 /**************************************************************************
7172 Attempt to complete on the contents of TEXT. START and END bound the
7173 region of rl_line_buffer that contains the word to complete. TEXT is
7174 the word to complete. We can use the entire contents of rl_line_buffer
7175 in case we want to do some simple parsing. Return the array of matches,
7176 or NULL if there aren't any.
7177 **************************************************************************/
7178 char **freeciv_completion(const char *text, int start, int end)
7180 char **matches = (char **)NULL;
7182 if (is_help(start)) {
7183 matches = rl_completion_matches(text, help_generator);
7184 } else if (is_command(start)) {
7185 matches = rl_completion_matches(text, command_generator);
7186 } else if (is_list(start)) {
7187 matches = rl_completion_matches(text, list_generator);
7188 } else if (is_cmdlevel_arg2(start)) {
7189 matches = rl_completion_matches(text, cmdlevel_arg2_generator);
7190 } else if (is_cmdlevel_arg1(start)) {
7191 matches = rl_completion_matches(text, cmdlevel_arg1_generator);
7192 } else if (is_connection(start)) {
7193 matches = rl_completion_matches(text, connection_generator);
7194 } else if (is_player(start)) {
7195 matches = rl_completion_matches(text, player_generator);
7196 } else if (is_server_option(start)) {
7197 matches = rl_completion_matches(text, option_generator);
7198 } else if (is_option_level(start)) {
7199 matches = rl_completion_matches(text, olevel_generator);
7200 } else if (is_enum_option_value(start, &completion_option)) {
7201 matches = rl_completion_matches(text, option_value_generator);
7202 } else if (is_filename(start)) {
7203 /* This function we get from readline */
7204 matches = rl_completion_matches(text, rl_filename_completion_function);
7205 } else if (is_create_arg2(start)) {
7206 matches = rl_completion_matches(text, aitype_generator);
7207 } else if (is_reset(start)) {
7208 matches = rl_completion_matches(text, reset_generator);
7209 } else if (is_vote(start)) {
7210 matches = rl_completion_matches(text, vote_generator);
7211 } else if (is_delegate_arg1(start)) {
7212 matches = rl_completion_matches(text, delegate_generator);
7213 } else if (is_mapimg(start)) {
7214 matches = rl_completion_matches(text, mapimg_generator);
7215 } else if (is_fcdb(start)) {
7216 matches = rl_completion_matches(text, fcdb_generator);
7217 } else if (is_lua(start)) {
7218 matches = rl_completion_matches(text, lua_generator);
7219 } else {
7220 /* We have no idea what to do */
7221 matches = NULL;
7224 /* Don't automatically try to complete with filenames */
7225 rl_attempted_completion_over = 1;
7227 return (matches);
7230 #endif /* HAVE_LIBREADLINE */