Complete.Org: Mailing Lists: Archives: freeciv-dev: February 2004:
[Freeciv-Dev] (PR#7378) Vote command for server
Home

[Freeciv-Dev] (PR#7378) Vote command for server

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#7378) Vote command for server
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Wed, 4 Feb 2004 11:51:36 -0800
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=7378 >

This implements a /vote command for the server. It elevates command
priviledge for ALLOW_INFO cmdlevel users to ALLOW_CTRL cmdlevel by means
of a consensus-based vote.

The syntax is:

        /vote new start
        /vote new 'set endy 2001'

This creates new votes. Each player can have one on-going vote at a time.
If your command has multiple words, you must enclose them in apostrophes.

        /vote yes
        /vote no
        /vote abstain

This places your vote on the latest vote.

        /vote yes 3
        /vote no 3
        /vote abstain 3

This places your vote on vote number 3.

        /vote

This shows a list of on-going votes.

Once all players have placed their votes or a full turn has passed since
the vote was suggested, whichever comes first, a vote is resolved. The
votes passes only if no player voted against.

The 'fix' and 'unfix' commands have been removed, since they no longer
have any use. I also removed the 'rename' command while I was at it, since
it has never been implemented and shows no signs of becoming.

The 'endgame' command is made CTRL cmdlevel instead of HACK cmdlevel.

Technical: Quite a bit of lines changed in stdinhand.c, very few lines
elsewhere. The checking for valid vote command makes things a little bit
ugly, but not by much. I fixed the 'I get some responses from the server
twice, first as Game: then as /command:' bug while I was at it and I will
post this as a separate patch later. I also fixed a memory leak bug which
would kick in whenever you used the /team command in error, I do not
intend to submit this as a separate patch.

This patch needs a lot more testing. I want responses on the general
design as well.

  - Per

Index: server/handchat.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/handchat.c,v
retrieving revision 1.28
diff -u -r1.28 handchat.c
--- server/handchat.c   2003/11/28 17:37:22     1.28
+++ server/handchat.c   2004/02/04 19:35:10
@@ -180,7 +180,7 @@
   */
   if (message[0] == SERVER_COMMAND_PREFIX) {
     /* pass it to the command parser, which will chop the prefix off */
-    handle_stdin_input(pconn, message);
+    (void) handle_stdin_input(pconn, message, FALSE);
     return;
   }
 
Index: server/sernet.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/sernet.c,v
retrieving revision 1.112
diff -u -r1.112 sernet.c
--- server/sernet.c     2003/11/28 17:37:22     1.112
+++ server/sernet.c     2004/02/04 19:35:11
@@ -170,7 +170,7 @@
     add_history(line);
 
   con_prompt_enter();          /* just got an 'Enter' hit */
-  handle_stdin_input((struct connection*)NULL, line);
+  (void) handle_stdin_input((struct connection*)NULL, line, FALSE);
 
   readline_handled_input = TRUE;
 }
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.150
diff -u -r1.150 srv_main.c
--- server/srv_main.c   2003/12/15 17:08:00     1.150
+++ server/srv_main.c   2004/02/04 19:35:11
@@ -530,6 +530,7 @@
   do_apollo_program();
   marco_polo_make_contact();
   make_history_report();
+  stdinhand_turn();
   send_player_turn_notifications(NULL);
 
   /* Output some ranking and AI debugging info here. */
@@ -1446,6 +1447,7 @@
   con_flush();
 
   game_init();
+  stdinhand_init();
 
   /* init network */  
   init_connections(); 
@@ -1453,7 +1455,7 @@
 
   /* load a saved game */
   if (srvarg.load_filename) {
-    load_command(NULL, srvarg.load_filename);
+    (void) load_command(NULL, srvarg.load_filename, FALSE);
   } 
 
   if(!(srvarg.metaserver_no_send)) {
@@ -1703,4 +1705,5 @@
   nation_city_names_free(misc_city_names);
   misc_city_names = NULL;
   game_free();
+  stdinhand_free();
 }
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.303
diff -u -r1.303 stdinhand.c
--- server/stdinhand.c  2004/01/09 05:55:55     1.303
+++ server/stdinhand.c  2004/02/04 19:35:12
@@ -68,20 +68,33 @@
 static enum cmdlevel_id default_access_level = ALLOW_INFO;
 static enum cmdlevel_id   first_access_level = ALLOW_CTRL;
 
-static void cut_client_connection(struct connection *caller, char *name);
-static void show_help(struct connection *caller, char *arg);
-static void show_list(struct connection *caller, char *arg);
+static bool cut_client_connection(struct connection *caller, char *name,
+                                  bool check);
+static bool show_help(struct connection *caller, char *arg);
+static bool show_list(struct connection *caller, char *arg);
 static void show_connections(struct connection *caller);
-static void set_ai_level(struct connection *caller, char *name, int level);
-static void set_away(struct connection *caller, char *name);
+static bool set_ai_level(struct connection *caller, char *name, int level, 
+                         bool check);
+static bool set_away(struct connection *caller, char *name, bool check);
 
 static bool is_allowed_to_take(struct player *pplayer, bool will_obs, 
                                char *msg);
 
-static void fix_command(struct connection *caller, char *str, int cmd_enum);
-static void observe_command(struct connection *caller, char *name);
-static void take_command(struct connection *caller, char *name);
-static void detach_command(struct connection *caller, char *name);
+static bool observe_command(struct connection *caller, char *name, bool check);
+static bool take_command(struct connection *caller, char *name, bool check);
+static bool detach_command(struct connection *caller, char *name, bool check);
+static bool start_command(struct connection *caller, char *name, bool check);
+static bool end_command(struct connection *caller, char *name, bool check);
+
+struct voting {
+  char command[MAX_LEN_CONSOLE_LINE]; /* [0] == \0 if none in action */
+  char votes_cast[MAX_NUM_PLAYERS]; /* 0-not voted, 1-blank, 2-yes, 3-no */
+  int vote_no; /* place in the queue */
+  bool full_turn; /* has a full turn begun for this vote yet? */
+  int yes, no, abstains;
+};
+static struct voting votes[MAX_NUM_PLAYERS];
+static int last_vote;
 
 static const char horiz_line[] =
 
"------------------------------------------------------------------------------";
@@ -879,12 +892,6 @@
 #define SETTINGS_NUM (ARRAY_SIZE(settings)-1)
 
 /********************************************************************
-Returns whether the specified server setting (option) has been
-explicitly protected from change at runtime with /fix.  Array of booleans.
-*********************************************************************/
-static bool sset_is_fixed[SETTINGS_NUM];
-
-/********************************************************************
 Returns whether the specified server setting (option) can currently
 be changed.  Does not indicate whether it can be changed by clients.
 *********************************************************************/
@@ -918,24 +925,6 @@
 }
 
 /********************************************************************
-Returns whether the specified server setting (option) can ever be changed
-by a client while the game is running.  Does not test whether the client
-has sufficient access or the option has been /fixed.
-*********************************************************************/
-static bool sset_is_runtime_changeable_by_client(int idx)
-{
-  struct settings_s *op = &settings[idx];
-
-  switch(op->sclass) {
-  case SSET_RULES_FLEXIBLE:
-  case SSET_META:
-    return TRUE;
-  default:
-   return FALSE;
-  }
-}
-
-/********************************************************************
 Returns whether the specified server setting (option) should be
 sent to the client.
 *********************************************************************/
@@ -996,15 +985,13 @@
   CMD_SHOW,
   CMD_SCORE,
   CMD_WALL,
+  CMD_VOTE,
   
   /* mostly non-harmful: */
   CMD_DEBUG,
   CMD_SET,
   CMD_TEAM,
-  CMD_FIX,
-  CMD_UNFIX,
   CMD_RULESETDIR,
-  CMD_RENAME,
   CMD_METAINFO,
   CMD_METACONN,
   CMD_METASERVER,
@@ -1018,9 +1005,7 @@
   CMD_EASY,
   CMD_NORMAL,
   CMD_HARD,
-#ifdef DEBUG
   CMD_EXPERIMENTAL,
-#endif
   CMD_CMDLEVEL,
   CMD_FIRSTLEVEL,
   CMD_TIMEOUT,
@@ -1128,7 +1113,19 @@
    N_("For each connected client, pops up a window showing the message "
       "entered.")
   },
-  {"debug",    ALLOW_HACK,
+  {"vote",     ALLOW_INFO,
+   N_("vote new \'<command>\'    - or -    vote yes|no|abstain [vote number]"),
+   N_("Start or participate in a vote."),
+   N_("When followed by a \"new\" and a command, starts a new vote for given " 
+      "command, which will be executed with control cmdlevel.  When followed "
+      "by \"yes\", \"no\" or \"abstain\", and optionally a vote number, "
+      "gives your vote for an ongoing voting.  If you do not add a vote "
+      "number, your vote applies to the latest voting.  You can only "
+      "suggest one vote at a time.  Voting usually ends next turn, and "
+      "you can only vote for info or control cmdlevel.  Enclose commands "
+      "with multiple words in apostrophes.")
+  },
+  {"debug",    ALLOW_CTRL,
    N_("debug <player <player> | city <x> <y> | units <x> <y> | unit <id>>"),
    N_("Turn on or off AI debugging of given entity."),
    N_("Print AI debug information about given entity and turn continous "
@@ -1147,24 +1144,12 @@
       "vision and embassies, and fight together to achieve team victory "
       "with averaged individual scores.")
   },
-  {"fix",       ALLOW_CTRL,
-   N_("fix <option-name>"),
-   N_("Make server option unchangeable during game."), NULL
-  },
-  {"unfix",      ALLOW_CTRL,
-   N_("unfix <option-name>"),
-   N_("Make server option changeable during game."), NULL
-  },
   {"rulesetdir", ALLOW_CTRL,
    N_("rulesetdir <directory>"),
    N_("Choose new ruleset directory or modpack. Calling this\n "
       "without any arguments will show you the currently selected "
       "ruleset."), NULL
   },
-  {"rename",   ALLOW_CTRL,
-   NULL,
-   N_("This command is not currently implemented."), NULL
-  },
   {"metainfo", ALLOW_CTRL,
    /* TRANS: translate text between <> only */
    N_("metainfo <meta-line>"),
@@ -1261,7 +1246,6 @@
       "sets the default level for any new AI players to 'hard'.  With an "
       "argument, sets the skill level for that player only.")
   },
-#ifdef DEBUG
   {"experimental",     ALLOW_CTRL,
    /* TRANS: translate text between <> only */
    N_("experimental\n"
@@ -1273,7 +1257,6 @@
       "FOR TESTING OF NEW AI FEATURES! For ordinary servers, this option "
       "has no effect.")
   },
-#endif
   {"cmdlevel", ALLOW_HACK,  /* confusing to leave this at ALLOW_CTRL */
    /* TRANS: translate text between <> only */
    N_("cmdlevel\n"
@@ -1317,7 +1300,7 @@
       "to <turn> and multiply <value> by <valuemult>.  Use this command in "
       "concert with the option \"timeout\". Defaults are 0 0 0 1")
   },
-  {"endgame",  ALLOW_HACK,
+  {"endgame",  ALLOW_CTRL,
    "endgame",
    N_("End the game."),
    N_("This command ends the game immediately.")
@@ -1392,10 +1375,103 @@
   }
 }
 
+/**************************************************************************
+  Initialize stuff related to this code module.
+**************************************************************************/
+void stdinhand_init(void)
+{
+  int i;
+
+  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+    votes[i].command[0] = '\0';
+    memset(votes[i].votes_cast, 0, sizeof(votes[i].votes_cast));
+    votes[i].vote_no = -1;
+    votes[i].full_turn = FALSE;
+  }
+  last_vote = -1;
+}
+
+/**************************************************************************
+  Check if we satisfy the criteria for resolving a vote, and resolve it
+  if these critera are indeed met. Updates yes, no, abstains variables
+  in voting struct as well.
+**************************************************************************/
+static void check_vote(struct voting *vote)
+{
+  int i, num_cast = 0, num_voters = 0;
+
+  vote->yes = 0;
+  vote->no = 0;
+
+  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+    if (game.players[i].is_alive && game.players[i].is_connected) {
+      num_voters++;
+    }
+  }
+  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+    num_cast = (vote->votes_cast[i] > 0) ? num_cast + 1 : num_cast;
+    vote->yes = (vote->votes_cast[i] == 2) ? vote->yes + 1 : vote->yes;
+    vote->no = (vote->votes_cast[i] == 3) ? vote->no + 1 : vote->no;
+  }
+
+  vote->abstains = num_cast - vote->yes - vote->no;
+
+  /* Check if we should resolve the vote */
+  if (vote->command[0] != '\0'
+      && (vote->full_turn == TRUE
+          || num_cast == num_voters)) {
+    /* Yep, resolve this one */
+    vote->vote_no = -1;
+    vote->full_turn = FALSE;
+    if (last_vote == vote->vote_no) {
+      last_vote = -1;
+    }
+    if (vote->no == 0) {
+      /* Do it! */
+      notify_player(NULL, _("Vote \"%s\" is passed %d to %d with %d "
+                    "abstentions."), vote->command, vote->yes, vote->no, 
+                    vote->abstains);
+      handle_stdin_input((struct connection *)NULL, vote->command, FALSE);
+    } else {
+      notify_player(NULL, _("Vote \"%s\" failed with %d against, %d for "
+                    "and %d abstentions."), vote->command, vote->no, 
vote->yes, 
+                    vote->abstains);
+    }
+    vote->command[0] = '\0';
+  }
+}
+
+/**************************************************************************
+  Update stuff every turn that is related to this code module. Run this
+  on turn end.
+**************************************************************************/
+void stdinhand_turn(void)
+{
+  int i;
+
+  /* Check if any votes have passed */
+  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+    check_vote(&votes[i]);
+  }
+
+  /* Update full turn info */
+  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+    votes[i].full_turn = TRUE;
+  }
+}
+
 /**************************************************************************
-  Whether the caller can use the specified command.
-  Does not see if the setting has been /fixed.  caller == NULL means console.
+  Deinitialize stuff related to this code module.
 **************************************************************************/
+void stdinhand_free(void)
+{
+  /* Nothing yet */
+}
+
+/**************************************************************************
+  Whether the caller can use the specified command. caller == NULL means 
+  console.
+**************************************************************************/
 static bool may_use(struct connection *caller, enum command_id cmd)
 {
   if (!caller) {
@@ -1419,7 +1495,7 @@
 /**************************************************************************
   Whether the caller can set the specified option (assuming that
   the state of the game would allow changing the option at all).
-  Does not see if the setting has been /fixed.  caller == NULL means console.
+  caller == NULL means console.
 **************************************************************************/
 static bool may_set_option(struct connection *caller, int option_idx)
 {
@@ -1434,12 +1510,11 @@
 
 /**************************************************************************
   Whether the caller can set the specified option, taking into account
-  access, the game state, and /fix.  caller == NULL means console.
+  access, and the game state.  caller == NULL means console.
 **************************************************************************/
 static bool may_set_option_now(struct connection *caller, int option_idx)
 {
   return (may_set_option(caller, option_idx)
-         && !sset_is_fixed[option_idx]
          && sset_is_changeable(option_idx));
 }
 
@@ -1485,7 +1560,12 @@
   }
 
   if (rfc_status == C_OK) {
-    notify_player(NULL, _("Game: %s"), line);
+    players_iterate(pplayer) {
+      /* Do not tell caller, since he was told above! */
+      if (!(caller && caller->player && caller->player == pplayer)) {
+        notify_player(pplayer, _("Game: %s"), line);
+      }
+    } players_iterate_end;
   }
 }
 
@@ -1641,7 +1721,8 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void metaconnection_command(struct connection *caller, char *arg)
+static bool metaconnection_command(struct connection *caller, char *arg, 
+                                   bool check)
 {
   if ((*arg == '\0') ||
       (0 == strcmp (arg, "?"))) {
@@ -1655,30 +1736,41 @@
   } else if ((0 == mystrcasecmp(arg, "u")) ||
             (0 == mystrcasecmp(arg, "up"))) {
     if (!server_is_open) {
-      open_metaserver_connection(caller);
+      if (!check) {
+        open_metaserver_connection(caller);
+      }
     } else {
       cmd_reply(CMD_METACONN, caller, C_METAERROR,
                _("Metaserver connection is already open."));
+      return FALSE;
     }
   } else if ((0 == mystrcasecmp(arg, "d")) ||
             (0 == mystrcasecmp(arg, "down"))) {
     if (server_is_open) {
-      close_metaserver_connection(caller);
+      if (!check) {
+        close_metaserver_connection(caller);
+      }
     } else {
       cmd_reply(CMD_METACONN, caller, C_METAERROR,
                _("Metaserver connection is already closed."));
+      return FALSE;
     }
   } else {
     cmd_reply(CMD_METACONN, caller, C_METAERROR,
              _("Argument must be 'u', 'up', 'd', 'down', or '?'."));
+    return FALSE;
   }
+  return TRUE;
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-static void metainfo_command(struct connection *caller, char *arg)
+static bool metainfo_command(struct connection *caller, char *arg, bool check)
 {
+  if (check) {
+    return TRUE;
+  }
   sz_strlcpy(srvarg.metaserver_info_line, arg);
   if (!send_server_info_to_metaserver(TRUE, FALSE)) {
     cmd_reply(CMD_METAINFO, caller, C_METAERROR,
@@ -1687,13 +1779,18 @@
     notify_player(NULL, _("Metaserver infostring set to '%s'."),
                  srvarg.metaserver_info_line);
   }
+  return TRUE;
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-static void metaserver_command(struct connection *caller, char *arg)
+static bool metaserver_command(struct connection *caller, char *arg, 
+                               bool check)
 {
+  if (check) {
+    return TRUE;
+  }
   close_metaserver_connection(caller);
 
   sz_strlcpy(srvarg.metaserver_addr, arg);
@@ -1701,6 +1798,7 @@
 
   notify_player(NULL, _("Metaserver is now [%s]."),
                meta_addr_port());
+  return TRUE;
 }
 
 /***************************************************************
@@ -1787,14 +1885,16 @@
 For command "save foo";
 Save the game, with filename=arg, provided server state is ok.
 **************************************************************************/
-static void save_command(struct connection *caller, char *arg)
+static bool save_command(struct connection *caller, char *arg, bool check)
 {
   if (server_state==SELECT_RACES_STATE) {
     cmd_reply(CMD_SAVE, caller, C_SYNTAX,
              _("The game cannot be saved before it is started."));
-    return;
+    return FALSE;
+  } else if (!check) {
+    save_game(arg);
   }
-  save_game(arg);
+  return TRUE;
 }
 
 /**************************************************************************
@@ -1819,7 +1919,7 @@
     /* Set the skill level explicitly, because eg: the player skill
        level could have been set as AI, then toggled, then saved,
        then reloaded. */ 
-    set_ai_level(caller, pplayer->name, pplayer->ai.skill_level);
+    set_ai_level(caller, pplayer->name, pplayer->ai.skill_level, FALSE);
     /* the AI can't do active diplomacy */
     cancel_all_meetings(pplayer);
     /* The following is sometimes necessary to avoid using
@@ -1844,7 +1944,7 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void toggle_ai_player(struct connection *caller, char *arg)
+static bool toggle_ai_player(struct connection *caller, char *arg, bool check)
 {
   enum m_pre_result match_result;
   struct player *pplayer;
@@ -1853,15 +1953,17 @@
 
   if (!pplayer) {
     cmd_reply_no_such_player(CMD_AITOGGLE, caller, arg, match_result);
-    return;
+    return FALSE;
+  } else if (!check) {
+    toggle_ai_player_direct(caller, pplayer);
   }
-  toggle_ai_player_direct(caller, pplayer);
+  return TRUE;
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-static void create_ai_player(struct connection *caller, char *arg)
+static bool create_ai_player(struct connection *caller, char *arg, bool check)
 {
   struct player *pplayer;
   PlayerNameStatus PNameStatus;
@@ -1870,39 +1972,42 @@
   {
     cmd_reply(CMD_CREATE, caller, C_SYNTAX,
              _("Can't add AI players once the game has begun."));
-    return;
+    return FALSE;
   }
 
   if (game.nplayers >= game.max_players) 
   {
     cmd_reply(CMD_CREATE, caller, C_FAIL,
              _("Can't add more players, server is full."));
-    return;
+    return FALSE;
   }
 
   if ((PNameStatus = test_player_name(arg)) == PNameEmpty)
   {
     cmd_reply(CMD_CREATE, caller, C_SYNTAX, _("Can't use an empty name."));
-    return;
+    return FALSE;
   }
 
   if (PNameStatus == PNameTooLong)
   {
     cmd_reply(CMD_CREATE, caller, C_SYNTAX,
              _("That name exceeds the maximum of %d chars."), MAX_LEN_NAME-1);
-    return;
+    return FALSE;
   }
 
   if ((pplayer=find_player_by_name(arg))) {
     cmd_reply(CMD_CREATE, caller, C_BOUNCE,
              _("A player already exists by that name."));
-    return;
+    return FALSE;
   }
 
   if ((pplayer = find_player_by_user(arg))) {
     cmd_reply(CMD_CREATE, caller, C_BOUNCE,
               _("A user already exists by that name."));
-    return;
+    return FALSE;
+  }
+  if (check) {
+    return TRUE;
   }
 
   pplayer = &game.players[game.nplayers];
@@ -1922,18 +2027,19 @@
   {
     cmd_reply(CMD_CREATE, caller, C_FAIL,
              _("Error creating new AI player: %s."), arg);
-    return;
+    return FALSE;
   }
 
   pplayer->ai.control = TRUE;
   set_ai_level_directer(pplayer, game.skill_level);
+  return TRUE;
 }
 
 
 /**************************************************************************
 ...
 **************************************************************************/
-static void remove_player(struct connection *caller, char *arg)
+static bool remove_player(struct connection *caller, char *arg, bool check)
 {
   enum m_pre_result match_result;
   struct player *pplayer;
@@ -1943,15 +2049,18 @@
   
   if(!pplayer) {
     cmd_reply_no_such_player(CMD_REMOVE, caller, arg, match_result);
-    return;
+    return FALSE;
   }
 
   if (!(game.is_new_game && (server_state==PRE_GAME_STATE ||
                             server_state==SELECT_RACES_STATE))) {
     cmd_reply(CMD_REMOVE, caller, C_FAIL,
              _("Players cannot be removed once the game has started."));
-    return;
+    return FALSE;
   }
+  if (check) {
+    return TRUE;
+  }
 
   sz_strlcpy(name, pplayer->name);
   team_remove_player(pplayer);
@@ -1960,23 +2069,7 @@
     cmd_reply(CMD_REMOVE, caller, C_OK,
              _("Removed player %s from the game."), name);
   }
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-static void generic_not_implemented(struct connection *caller, const char *cmd)
-{
-  cmd_reply(CMD_RENAME, caller, C_FAIL,
-           _("Sorry, the '%s' command is not implemented yet."), cmd);
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-static void rename_player(struct connection *caller, char *arg)
-{
-  generic_not_implemented(caller, "rename");
+  return TRUE;
 }
 
 /**************************************************************************
@@ -1993,7 +2086,7 @@
     char buffer[MAX_LEN_CONSOLE_LINE];
     /* the size is set as to not overflow buffer in handle_stdin_input */
     while(fgets(buffer,MAX_LEN_CONSOLE_LINE-1,script_file))
-      handle_stdin_input((struct connection *)NULL, buffer);
+      handle_stdin_input((struct connection *)NULL, buffer, FALSE);
     fclose(script_file);
     return TRUE;
   } else {
@@ -2008,10 +2101,13 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void read_command(struct connection *caller, char *arg)
+static bool read_command(struct connection *caller, char *arg, bool check)
 {
+  if (check) {
+    return TRUE; /* FIXME: no actual checks done */
+  }
   /* warning: there is no recursion check! */
-  (void) read_init_script(caller, arg);
+  return read_init_script(caller, arg);
 }
 
 /**************************************************************************
@@ -2079,14 +2175,6 @@
       }
     }
 
-    /* the 'fix'ed settings */
-
-    for (i = 0; settings[i].name; i++) {
-      if (sset_is_fixed[i]) {
-        fprintf(script_file, "fix %s\n", settings[i].name);
-      }
-    }
-    
     /* rulesetdir */
     fprintf(script_file, "rulesetdir %s\n", game.rulesetdir);
 
@@ -2102,9 +2190,12 @@
 ...
 ('caller' argument is unused)
 **************************************************************************/
-static void write_command(struct connection *caller, char *arg)
+static bool write_command(struct connection *caller, char *arg, bool check)
 {
-  write_init_script(arg);
+  if (!check) {
+    write_init_script(arg);
+  }
+  return TRUE;
 }
 
 /**************************************************************************
@@ -2189,7 +2280,7 @@
 /**************************************************************************
  Change command access level for individual player, or all, or new.
 **************************************************************************/
-static void cmdlevel_command(struct connection *caller, char *str)
+static bool cmdlevel_command(struct connection *caller, char *str, bool check)
 {
   char arg_level[MAX_LEN_CONSOLE_LINE]; /* info, ctrl etc */
   char arg_name[MAX_LEN_CONSOLE_LINE];  /* a player name, or "new" */
@@ -2226,7 +2317,7 @@
     cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
              _("Command access level for first player to take it: %s"),
              cmdlevel_name(first_access_level));
-    return;
+    return TRUE;
   }
 
   /* a level name was supplied; set the level */
@@ -2235,14 +2326,17 @@
     cmd_reply(CMD_CMDLEVEL, caller, C_SYNTAX,
              _("Error: command access level must be one of"
                " 'none', 'info', 'ctrl', or 'hack'."));
-    return;
+    return FALSE;
   } else if (caller && level > caller->access_level) {
     cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
              _("Cannot increase command access level to '%s';"
                " you only have '%s' yourself."),
              arg_level, cmdlevel_name(caller->access_level));
-    return;
+    return FALSE;
   }
+  if (check) {
+    return TRUE; /* looks good */
+  }
 
   /* find the start of the name: */
   for (; *cptr_s != '\0' && !my_isalnum(*cptr_s); cptr_s++) {
@@ -2269,6 +2363,7 @@
                  _("Command access level could not be set to '%s' for "
                    "connection %s."),
                  cmdlevel_name(level), pconn->username);
+        return FALSE;
       }
     }
     conn_list_iterate_end;
@@ -2316,10 +2411,13 @@
                _("Command access level could not be set to '%s'"
                  " for connection %s."),
                cmdlevel_name(level), ptarget->username);
+      return FALSE;
     }
   } else {
     cmd_reply_no_such_conn(CMD_CMDLEVEL, caller, arg_name, match_result);
+    return FALSE;
   }
+  return TRUE;
 }
 
 /**************************************************************************
@@ -2328,26 +2426,30 @@
  to promote one's own connection to 'first come' cmdlevel if that isn't
  already taken.
  **************************************************************************/
-static void firstlevel_command(struct connection *caller)
+static bool firstlevel_command(struct connection *caller, bool check)
 {
   if (!caller) {
     cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
        _("The 'firstlevel' command makes no sense from the server command 
line."));
+    return FALSE;
   } else if (caller->access_level >= first_access_level) {
     cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
        _("You already have command access level '%s' or better."),
                cmdlevel_name(first_access_level));
+    return FALSE;
   } else if (first_access_level_is_taken()) {
     cmd_reply(CMD_FIRSTLEVEL, caller, C_FAIL,
        _("Someone else already has command access level '%s' or better."),
                cmdlevel_name(first_access_level));
-  } else {
+    return FALSE;
+  } else if (!check) {
     caller->access_level = first_access_level;
     cmd_reply(CMD_FIRSTLEVEL, caller, C_OK,
        _("Command access level '%s' has been grabbed by connection %s."),
                cmdlevel_name(first_access_level),
                caller->username);
   }
+  return TRUE;
 }
 
 
@@ -2358,7 +2460,7 @@
 /**************************************************************************
   Set timeout options.
 **************************************************************************/
-static void timeout_command(struct connection *caller, char *str) 
+static bool timeout_command(struct connection *caller, char *str, bool check)
 {
   char buf[MAX_LEN_CONSOLE_LINE];
   char *arg[4];
@@ -2385,7 +2487,9 @@
     cmd_reply(CMD_TIMEOUT, caller, C_SYNTAX, _("Usage: timeoutincrease "
                                               "<turn> <turnadd> "
                                               "<value> <valuemult>."));
-    return;
+    return FALSE;
+  } else if (check) {
+    return TRUE;
   }
 
   cmd_reply(CMD_TIMEOUT, caller, C_OK, _("Dynamic timeout set to "
@@ -2395,6 +2499,7 @@
 
   /* if we set anything here, reset the counter */
   game.timeoutcounter = 1;
+  return TRUE;
 }
 
 /**************************************************************************
@@ -2508,7 +2613,7 @@
 /**************************************************************************
  ...
 **************************************************************************/
-static void explain_option(struct connection *caller, char *str)
+static bool explain_option(struct connection *caller, char *str, bool check)
 {
   char command[MAX_LEN_CONSOLE_LINE], *cptr_s, *cptr_d;
   int cmd;
@@ -2524,24 +2629,29 @@
     cmd=lookup_option(command);
     if (cmd==-1) {
       cmd_reply(CMD_EXPLAIN, caller, C_FAIL, _("No explanation for that 
yet."));
-      return;
+      return FALSE;
     } else if (cmd==-2) {
       cmd_reply(CMD_EXPLAIN, caller, C_FAIL, _("Ambiguous option name."));
-      return;
+      return FALSE;
     } else {
       show_help_option(caller, CMD_EXPLAIN, cmd);
     }
   } else {
     show_help_option_list(caller, CMD_EXPLAIN);
   }
+  return TRUE;
 }
+
 /******************************************************************
   Send a message to all players
 ******************************************************************/
-static void wall(char *str)
+static bool wall(char *str, bool check)
 {
-  notify_conn_ex(&game.game_connections, -1, -1, E_MESSAGE_WALL,
-                _("Server Operator: %s"), str);
+  if (!check) {
+    notify_conn_ex(&game.game_connections, -1, -1, E_MESSAGE_WALL,
+                  _("Server Operator: %s"), str);
+  }
+  return TRUE;
 }
   
 /******************************************************************
@@ -2617,14 +2727,7 @@
     case 3 : return CMD_EASY;
     case 5 : return CMD_NORMAL;
     case 7 : return CMD_HARD;
-    case 10 :
-#ifdef DEBUG
-      return CMD_EXPERIMENTAL;
-#else
-      freelog(LOG_NORMAL,
-             _("Experimental AI isn't available; using hard instead."));
-      return CMD_HARD;
-#endif
+    case 10 : return CMD_EXPERIMENTAL;
   }
   assert(FALSE);
   return CMD_NORMAL; /* to satisfy compiler */
@@ -2645,7 +2748,8 @@
 /******************************************************************
   Handle a user command to set an AI level.
 ******************************************************************/
-static void set_ai_level(struct connection *caller, char *name, int level)
+static bool set_ai_level(struct connection *caller, char *name, 
+                         int level, bool check)
 {
   enum m_pre_result match_result;
   struct player *pplayer;
@@ -2656,6 +2760,9 @@
 
   if (pplayer) {
     if (pplayer->ai.control) {
+      if (check) {
+        return TRUE;
+      }
       set_ai_level_directer(pplayer, level);
       cmd_reply(cmd_of_level(level), caller, C_OK,
                _("Player '%s' now has AI skill level '%s'."),
@@ -2663,8 +2770,12 @@
     } else {
       cmd_reply(cmd_of_level(level), caller, C_FAIL,
                _("%s is not controlled by the AI."), pplayer->name);
+      return FALSE;
     }
   } else if(match_result == M_PRE_EMPTY) {
+    if (check) {
+      return TRUE;
+    }
     players_iterate(pplayer) {
       if (pplayer->ai.control) {
        set_ai_level_directer(pplayer, level);
@@ -2679,28 +2790,33 @@
                name_of_skill_level(level));
   } else {
     cmd_reply_no_such_player(cmd_of_level(level), caller, name, match_result);
+    return FALSE;
   }
+  return TRUE;
 }
 
 /******************************************************************
   Set user to away mode.
 ******************************************************************/
-static void set_away(struct connection *caller, char *name)
+static bool set_away(struct connection *caller, char *name, bool check)
 {
   if (caller == NULL) {
     cmd_reply(CMD_AWAY, caller, C_FAIL, _("This command is client only."));
+    return FALSE;
   } else if (name && strlen(name) > 0) {
     notify_conn(&caller->self, _("Usage: away"));
-  } else if (!caller->player->ai.control) {
+    return FALSE;
+  } else if (!caller->player->ai.control && !check) {
     notify_conn(&game.est_connections, _("%s set to away mode."), 
                 caller->player->name);
     set_ai_level_directer(caller->player, 1);
     caller->player->ai.control = TRUE;
-  } else {
+  } else if (!check) {
     notify_conn(&game.est_connections, _("%s returned to game."), 
                 caller->player->name);
     caller->player->ai.control = FALSE;
   }
+  return TRUE;
 }
 
 /******************************************************************
@@ -2709,7 +2825,7 @@
 which we let overflow their columns, plus a sign character.
 Only show options which the caller can SEE.
 ******************************************************************/
-static void show_command(struct connection *caller, char *str)
+static bool show_command(struct connection *caller, char *str, bool check)
 {
   char buf[MAX_LEN_CONSOLE_LINE];
   char command[MAX_LEN_CONSOLE_LINE], *cptr_s, *cptr_d;
@@ -2729,11 +2845,11 @@
       cmd_reply(CMD_SHOW, caller, C_FAIL,
                _("Sorry, you do not have access to view option '%s'."),
                command);
-      return;
+      return FALSE;
     }
     if (cmd==-1) {
       cmd_reply(CMD_SHOW, caller, C_FAIL, _("Unknown option '%s'."), command);
-      return;
+      return FALSE;
     }
     if (cmd==-2) {
       /* allow ambiguous: show all matching */
@@ -2813,6 +2929,7 @@
     }
   }
   cmd_reply_show(horiz_line);
+  return TRUE;
 #undef cmd_reply_show
 #undef OPTION_NAME_SPACE
 }
@@ -2844,18 +2961,19 @@
 /******************************************************************
 ...
 ******************************************************************/
-static void team_command(struct connection *caller, char *str) 
+static bool team_command(struct connection *caller, char *str, bool check)
 {
   struct player *pplayer;
   enum m_pre_result match_result;
   char buf[MAX_LEN_CONSOLE_LINE];
   char *arg[2];
-  int ntokens = 0;
+  int ntokens = 0, i;
+  bool res = FALSE;
 
   if (server_state != PRE_GAME_STATE) {
     cmd_reply(CMD_TEAM, caller, C_SYNTAX,
               _("Cannot change teams once game has begun."));
-    return;
+    return FALSE;
   }
 
   if (str != NULL || strlen(str) > 0) {
@@ -2865,44 +2983,187 @@
   if (ntokens > 2 || ntokens < 1) {
     cmd_reply(CMD_TEAM, caller, C_SYNTAX,
              _("Undefined argument.  Usage: team <player> [team]."));
-    return;
+    goto cleanup;
   }
 
   pplayer = find_player_by_name_prefix(arg[0], &match_result);
   if (pplayer == NULL) {
     cmd_reply_no_such_player(CMD_TEAM, caller, arg[0], match_result);
-    return;
+    goto cleanup;
   }
 
-  if (pplayer->team != TEAM_NONE) {
+  if (!check && pplayer->team != TEAM_NONE) {
     team_remove_player(pplayer);
   }
 
   if (ntokens == 1) {
     /* Remove from team */
-    cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s is made teamless."), 
-        pplayer->name);
-    return;
+    if (!check) {
+      cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s is made teamless."), 
+          pplayer->name);
+    }
+    res = TRUE;
+    goto cleanup;
   }
 
   if (!is_sane_name(arg[1])) {
     cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Bad team name."));
-    return;
+    goto cleanup;
   }
   if (is_barbarian(pplayer)) {
     /* This can happen if we change team settings on a loaded game. */
     cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Cannot team a barbarian."));
-    return;
+    goto cleanup;
+  }
+  if (!check) {
+    team_add_player(pplayer, arg[1]);
+    cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
+             pplayer->name, team_get_by_id(pplayer->team)->name);
+  }
+  res = TRUE;
+
+  cleanup:
+  for (i = 0; i < ntokens; i++) {
+    free(arg[i]);
   }
-  team_add_player(pplayer, arg[1]);
-  cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
-           pplayer->name, team_get_by_id(pplayer->team)->name);
+  return res;
 }
 
 /******************************************************************
+  Make or participate in a vote.
+******************************************************************/
+static bool vote_command(struct connection *caller, char *str,
+                         bool check)
+{
+  char buf[MAX_LEN_CONSOLE_LINE];
+  char *arg[3];
+  int ntokens = 0, i;
+  char *usage = _("Undefined arguments. Usage: vote new <command> "
+                  "  or   vote yes|no|abstain [vote number].");
+  int idx;
+  bool res = FALSE;
+
+  if (caller == NULL || caller->player == NULL) {
+    cmd_reply(CMD_VOTE, caller, C_FAIL, _("This command is client only."));
+    return FALSE;
+  } else if (!str || strlen(str) == 0) {
+    int j = 0;
+
+    for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+      struct voting *vote = &votes[i];
+
+      if (vote->command[0] != '\0') {
+        j++;
+        cmd_reply(CMD_VOTE, caller, C_OK, _("Vote %d \"%s\": %d for, %d 
against"
+                  ", %d abstains"), vote->vote_no, vote->command, vote->yes,
+                  vote->no, vote->abstains);
+      }
+    }
+    if (j == 0) {
+      cmd_reply(CMD_VOTE, caller, C_OK, _("There are no votes going on."));
+    }
+    return FALSE; /* see below */
+  } if (check) {
+    return FALSE; /* cannot vote over having vote! */
+  }
+  idx = caller->player->player_no;
+
+  sz_strlcpy(buf, str);
+  ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
+
+  if (strcmp(arg[0], "new") == 0) {
+    if (ntokens <= 1) {
+      cmd_reply(CMD_VOTE, caller, C_SYNTAX, usage);
+      goto cleanup;
+    }
+
+    /* If we already have a vote going, cancel it in favour of the new
+     * vote command. You can only have one vote at a time. */
+    if (votes[idx].command[0] != '\0') {
+      cmd_reply(CMD_VOTE, caller, C_OK, _("Your new vote cancelled your "
+                "previous vote."));
+      votes[idx].command[0] = '\0';
+    }
+
+    /* Check if the vote command would succeed. */
+    if (handle_stdin_input(caller, arg[1], TRUE)) {
+      last_vote++;
+      notify_player(NULL, _("New vote, no. %d, by %s: %s."), last_vote, 
+                    caller->player->name, arg[1]);
+      sz_strlcpy(votes[idx].command, arg[1]);
+      votes[idx].vote_no = last_vote;
+      votes[idx].full_turn = FALSE; /* just to be sure */
+      memset(votes[idx].votes_cast, 0, sizeof(votes[idx].votes_cast));
+      votes[idx].votes_cast[idx] = 2; /* vote yes to your own suggestion */
+      check_vote(&votes[idx]); /* update vote numbers, maybe auto-accept */
+    } else {
+      cmd_reply(CMD_VOTE, caller, C_FAIL, "Your new vote (\"%s\") was not "
+                "legal or was not recognized.", arg[1]);
+      goto cleanup;
+    }
+  } else if (strcmp(arg[0], "yes") == 0
+             || strcmp(arg[0], "no") == 0
+             || strcmp(arg[0], "abstain") == 0) {
+    int which = -1;
+    struct voting *vote = NULL;
+
+    if (ntokens == 1) {
+      /* Applies to last vote */
+      if (last_vote > -1) {
+        which = last_vote;
+      } else {
+        cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote."));
+        goto cleanup;
+      }
+    } else {
+      if (sscanf(arg[1], "%d", &which) <= 0) {
+        cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be integer."));
+        goto cleanup;
+      }
+    }
+    /* Ok, now try to find this vote */
+    for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+      if (votes[i].vote_no == which) {
+        vote = &votes[i];
+      }
+    }
+    if (which > last_vote || !vote || vote->command[0] == '\0') {
+      cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d), last is "
+                "%d"), which, last_vote);
+      goto cleanup;
+    }
+    if (strcmp(arg[0], "yes") == 0) {
+      cmd_reply(CMD_VOTE, caller, C_OK, _("You voted for \"%s\""), 
+                vote->command);
+      vote->votes_cast[caller->player->player_no] = 2;
+    } else if (strcmp(arg[0], "no") == 0) {
+      cmd_reply(CMD_VOTE, caller, C_OK, _("You voted against \"%s\""), 
+                vote->command);
+      vote->votes_cast[caller->player->player_no] = 3;
+    } else if (strcmp(arg[0], "abstain") == 0) {
+      cmd_reply(CMD_VOTE, caller, C_OK, _("You abstained on \"%s\""), 
+                vote->command);
+      vote->votes_cast[caller->player->player_no] = 1;
+    }
+    check_vote(vote);
+  } else {
+    cmd_reply(CMD_VOTE, caller, C_SYNTAX, usage);
+    goto cleanup;
+  }
+
+  res = TRUE;
+  cleanup:
+  for (i = 0; i < ntokens; i++) {
+    free(arg[i]);
+  }
+  return res;
+}
+
+/******************************************************************
   ...
 ******************************************************************/
-static void debug_command(struct connection *caller, char *str) 
+static bool debug_command(struct connection *caller, char *str, 
+                          bool check)
 {
   char buf[MAX_LEN_CONSOLE_LINE];
   char *arg[3];
@@ -2913,8 +3174,11 @@
   if (server_state != RUN_GAME_STATE) {
     cmd_reply(CMD_DEBUG, caller, C_SYNTAX,
               _("Can only use this command once game has begun."));
-    return;
+    return FALSE;
   }
+  if (check) {
+    return TRUE; /* whatever! */
+  }
 
   if (str != NULL || strlen(str) > 0) {
     sz_strlcpy(buf, str);
@@ -2931,7 +3195,7 @@
     }
     pplayer = find_player_by_name_prefix(arg[1], &match_result);
     if (pplayer == NULL) {
-      cmd_reply_no_such_player(CMD_TEAM, caller, arg[1], match_result);
+      cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result);
       goto cleanup;
     }
     if (pplayer->debug) {
@@ -3030,12 +3294,13 @@
   for (i = 0; i < ntokens; i++) {
     free(arg[i]);
   }
+  return TRUE;
 }
 
 /******************************************************************
   ...
 ******************************************************************/
-static void set_command(struct connection *caller, char *str) 
+static bool set_command(struct connection *caller, char *str, bool check)
 {
   char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE], *cptr_s, 
*cptr_d;
   int val, cmd;
@@ -3067,28 +3332,22 @@
   if (cmd==-1) {
     cmd_reply(CMD_SET, caller, C_SYNTAX,
              _("Undefined argument.  Usage: set <option> <value>."));
-    return;
+    return FALSE;
   }
   else if (cmd==-2) {
     cmd_reply(CMD_SET, caller, C_SYNTAX,
              _("Ambiguous option name."));
-    return;
+    return FALSE;
   }
   if (!may_set_option(caller,cmd)) {
      cmd_reply(CMD_SET, caller, C_FAIL,
               _("You are not allowed to set this option."));
-    return;
+    return FALSE;
   }
   if (!sset_is_changeable(cmd)) {
     cmd_reply(CMD_SET, caller, C_BOUNCE,
              _("This setting can't be modified after the game has started."));
-    return;
-  }
-  if (sset_is_fixed[cmd] && !(map_is_empty() || game.is_new_game)) {
-    cmd_reply(CMD_SET, caller, C_BOUNCE,
-             _("This setting is protected from being modified after the "
-                "game has started."));
-    return;
+    return FALSE;
   }
 
   op = &settings[cmd];
@@ -3100,9 +3359,11 @@
   case SSET_BOOL:
     if (sscanf(arg, "%d", &val) != 1) {
       cmd_reply(CMD_SET, caller, C_SYNTAX, _("Value must be an integer."));
+      return FALSE;
     } else if (val != 0 && val != 1) {
       cmd_reply(CMD_SET, caller, C_SYNTAX,
                _("Value out of range (minimum: 0, maximum: 1)."));
+      return FALSE;
     } else {
       char *reject_message = NULL;
       bool b_val = (val != 0);
@@ -3110,7 +3371,8 @@
       if (settings[cmd].bool_validate
          && !settings[cmd].bool_validate(b_val, &reject_message)) {
        cmd_reply(CMD_SET, caller, C_SYNTAX, reject_message);
-      } else {
+        return FALSE;
+      } else if (!check) {
        *(op->bool_value) = b_val;
        my_snprintf(buffer, sizeof(buffer),
                    _("Option: %s has been set to %d."), op->name,
@@ -3122,17 +3384,20 @@
   case SSET_INT:
     if (sscanf(arg, "%d", &val) != 1) {
       cmd_reply(CMD_SET, caller, C_SYNTAX, _("Value must be an integer."));
+      return FALSE;
     } else if (val < op->int_min_value || val > op->int_max_value) {
       cmd_reply(CMD_SET, caller, C_SYNTAX,
                _("Value out of range (minimum: %d, maximum: %d)."),
                op->int_min_value, op->int_max_value);
+      return FALSE;
     } else {
       char *reject_message = NULL;
 
       if (settings[cmd].int_validate
          && !settings[cmd].int_validate(val, &reject_message)) {
        cmd_reply(CMD_SET, caller, C_SYNTAX, reject_message);
-      } else {
+        return FALSE;
+      } else if (!check) {
        *(op->int_value) = val;
        my_snprintf(buffer, sizeof(buffer),
                    _("Option: %s has been set to %d."), op->name,
@@ -3146,13 +3411,15 @@
     if (strlen(arg) >= op->string_value_size) {
       cmd_reply(CMD_SET, caller, C_SYNTAX,
                _("String value too long.  Usage: set <option> <value>."));
+      return FALSE;
     } else {
       char *reject_message = NULL;
 
       if (settings[cmd].string_validate
          && !settings[cmd].string_validate(arg, &reject_message)) {
        cmd_reply(CMD_SET, caller, C_SYNTAX, reject_message);
-      } else {
+        return FALSE;
+      } else if (!check) {
        strcpy(op->string_value, arg);
        my_snprintf(buffer, sizeof(buffer),
                    _("Option: %s has been set to \"%s\"."), op->name,
@@ -3162,11 +3429,11 @@
     break;
   }
 
-  if (strlen(buffer) > 0 && sset_is_to_client(cmd)) {
+  if (!check && strlen(buffer) > 0 && sset_is_to_client(cmd)) {
     notify_player(NULL, "%s", buffer);
   }
 
-  if (do_update) {
+  if (!check && do_update) {
     /* 
      * send any modified game parameters to the clients -- if sent
      * before RUN_GAME_STATE, triggers a popdown_races_dialog() call
@@ -3175,88 +3442,10 @@
     if (server_state == RUN_GAME_STATE) {
       send_game_info(NULL);
     }
-  }
-}
-
-/**************************************************************************
-  /fix and /unfix commands
-  allow runtime modifiable options to be [un]fixed before the game starts
-**************************************************************************/
-static void fix_command(struct connection *caller, char *str, int cmd_enum)
-{
-  char buf[MAX_LEN_CONSOLE_LINE], *arg[1];
-  int opt, ntokens;
-
-  assert(cmd_enum == CMD_FIX || cmd_enum == CMD_UNFIX);
-
-  /* can't use the command now */
-  if (!(map_is_empty() || game.is_new_game)) {
-    cmd_reply(cmd_enum, caller, C_FAIL,
-             _("This command may only be used before the game has started."));
-    return;
-  }
-
-  /* we can use it. find the option name */
-  sz_strlcpy(buf, str);
-  ntokens = get_tokens(buf, arg, 1, TOKEN_DELIMITERS);
-
-  if (ntokens == 0) {
-    cmd_reply(cmd_enum, caller, C_SYNTAX,
-              _("Need an argument.  Usage: %s <option-name>."),
-              commands[cmd_enum].name);
-    return;
-  }
-
-  opt = lookup_option(arg[0]);
-  free(arg[0]);
-
-  if (opt == -1) {
-    cmd_reply(cmd_enum, caller, C_SYNTAX, _("Unknown option name."));
-    return;
-  } else if (opt == -2) {
-    cmd_reply(cmd_enum, caller, C_SYNTAX, _("Ambiguous option name."));
-    return;
-  }
-
-  /* option's good, can we do anything with it? */
-  if (!sset_is_runtime_changeable_by_client(opt)) {
-    cmd_reply(cmd_enum, caller, C_BOUNCE,
-           _("Option '%s' can never be modified after the game has started."),
-              settings[opt].name);
-    return;
-  }
-
-  if (!may_set_option(caller, opt)) {
-    if (cmd_enum == CMD_FIX) {
-      cmd_reply(cmd_enum, caller, C_FAIL,
-               _("You are not allowed to set option '%s', so you "
-               "may not prevent it from being set, either."),
-               settings[opt].name);
-    } else {
-      cmd_reply(cmd_enum, caller, C_FAIL,
-               _("You are not allowed to set option '%s', so you "
-               "may not allow it to be set, either."),
-               settings[opt].name);
-    }
-    return;
   }
-
-  if (cmd_enum == CMD_FIX) {
-    sset_is_fixed[opt] = TRUE;
-    cmd_reply(cmd_enum, caller, C_OK,
-               _("Option: '%s' now cannot be modified "
-                "after the game has started."),
-               settings[opt].name);
-  } else {
-    sset_is_fixed[opt] = FALSE;
-    cmd_reply(cmd_enum, caller, C_OK,
-               _("Option: '%s' may now be modified "
-                "after the game has started."),
-               settings[opt].name);
-  }
+  return TRUE;
 }
 
-
 /**************************************************************************
  check game.allow_take for permission to take or observe a player
 **************************************************************************/
@@ -3339,7 +3528,7 @@
  (see detach_command()). The console and those with ALLOW_HACK can
  use the two-argument command and force others to observe.
 **************************************************************************/
-static void observe_command(struct connection *caller, char *str)
+static bool observe_command(struct connection *caller, char *str, bool check)
 {
   int i = 0, ntokens = 0;
   char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];  
@@ -3349,6 +3538,7 @@
   enum m_pre_result result;
   struct connection *pconn = NULL;
   struct player *pplayer = NULL;
+  bool res = FALSE;
   
   /******** PART I: fill pconn and pplayer ********/
 
@@ -3428,6 +3618,11 @@
     goto end;
   }
 
+  res = TRUE; /* all tests passed */
+  if (check) {
+    goto end;
+  }
+
   /* if we want to switch players, reset the client */
   if (pconn->player && server_state == RUN_GAME_STATE) {
     send_game_state(&pconn->self, CLIENT_PRE_GAME_STATE);
@@ -3441,7 +3636,7 @@
     /* if a pconn->player is removed, we'll lose pplayer */
     sz_strlcpy(name, pplayer->name);
   
-    detach_command(NULL, pconn->username);
+    detach_command(NULL, pconn->username, FALSE);
 
     /* find pplayer again, the pointer might have been changed */
     pplayer = find_player_by_name(name);
@@ -3480,6 +3675,7 @@
   for (i = 0; i < ntokens; i++) {
     free(arg[i]);
   }
+  return res;
 }
 
 /**************************************************************************
@@ -3491,7 +3687,7 @@
   Otherwise, there should be one argument, that being the player that the 
   caller wants to take.
 **************************************************************************/
-static void take_command(struct connection *caller, char *str)
+static bool take_command(struct connection *caller, char *str, bool check)
 {
   int i = 0, ntokens = 0;
   char buf[MAX_LEN_CONSOLE_LINE], *arg[2], msg[MAX_LEN_MSG];
@@ -3501,6 +3697,7 @@
   enum m_pre_result match_result;
   struct connection *pconn = NULL;
   struct player *pplayer = NULL;
+  bool res = FALSE;
   
   /******** PART I: fill pconn and pplayer ********/
 
@@ -3511,7 +3708,7 @@
   if (ntokens == 0) {
     cmd_reply(CMD_TAKE, caller, C_SYNTAX,
               _("Usage: take [connection-name] <player-name>"));
-    return;
+    return FALSE;
   } 
   
   if (!caller && ntokens != 2) {
@@ -3560,6 +3757,11 @@
     goto end;
   } 
 
+  res = TRUE;
+  if (check) {
+    goto end;
+  }
+
   /* if we want to switch players, reset the client if the game is running */
   if (pconn->player && server_state == RUN_GAME_STATE) {
     send_game_state(&pconn->self, CLIENT_PRE_GAME_STATE);
@@ -3585,7 +3787,7 @@
     /* if a pconn->player is removed, we'll lose pplayer */
     sz_strlcpy(name, pplayer->name);
 
-    detach_command(NULL, pconn->username);
+    detach_command(NULL, pconn->username, FALSE);
 
     /* find pplayer again, the pointer might have been changed */
     pplayer = find_player_by_name(name);
@@ -3630,13 +3832,14 @@
   for (i = 0; i < ntokens; i++) {
     free(arg[i]);
   }
+  return res;
 }
 
 /**************************************************************************
   Detach from a player. if that player wasn't /created and you were 
   controlling the player, remove it (and then detach any observers as well).
 **************************************************************************/
-static void detach_command(struct connection *caller, char *str)
+static bool detach_command(struct connection *caller, char *str, bool check)
 {
   int i = 0, ntokens = 0;
   char buf[MAX_LEN_CONSOLE_LINE], *arg[1];
@@ -3646,6 +3849,7 @@
   struct player *pplayer = NULL;
   bool is_newgame = (server_state == PRE_GAME_STATE || 
                      server_state == SELECT_RACES_STATE) && game.is_new_game;
+  bool res = FALSE;
 
   sz_strlcpy(buf, str);
   ntokens = get_tokens(buf, arg, 1, TOKEN_DELIMITERS);
@@ -3685,6 +3889,11 @@
     goto end;
   }
 
+  res = TRUE;
+  if (check) {
+    goto end;
+  }
+
   /* if we want to detach while the game is running, reset the client */
   if (server_state == RUN_GAME_STATE) {
     send_game_state(&pconn->self, CLIENT_PRE_GAME_STATE);
@@ -3727,34 +3936,39 @@
   for (i = 0; i < ntokens; i++) {
     free(arg[i]);
   }
+  return res;
 }
 
 /**************************************************************************
   ...
 **************************************************************************/
-void load_command(struct connection *caller, char *arg)
+bool load_command(struct connection *caller, char *arg, bool check)
 {
   struct timer *loadtimer, *uloadtimer;  
   struct section_file file;
 
   if (!arg || arg[0] == '\0') {
     cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage: load <filename>"));
-    return;
+    return FALSE;
   }
 
   if (server_state != PRE_GAME_STATE) {
     cmd_reply(CMD_LOAD, caller, C_FAIL, _("Can't load a game while another "
                                           "is running."));
-    return;
+    return FALSE;
   }
 
   /* attempt to parse the file */
 
   if (!section_file_load_nodup(&file, arg)) {
     cmd_reply(CMD_LOAD, caller, C_FAIL, _("Couldn't load savefile: %s"), arg);
-    return;
+    return FALSE;
   }
 
+  if (check) {
+    return TRUE;
+  }
+
   cmd_reply(CMD_LOAD, caller, C_COMMENT, _("Loading saved game: %s..."), arg);
 
   /* we found it, free all structures */
@@ -3787,30 +4001,33 @@
       }
     } players_iterate_end;
   } conn_list_iterate_end;
-
+  return TRUE;
 }
 
 /**************************************************************************
   ...
 **************************************************************************/
-static void set_rulesetdir(struct connection *caller, char *str)
+static bool set_rulesetdir(struct connection *caller, char *str, bool check)
 {
   char filename[512], *pfilename;
   if ((str == NULL) || (strlen(str)==0)) {
     cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
              _("Current ruleset directory is \"%s\""), game.rulesetdir);
-    return;
+    return FALSE;
   }
   my_snprintf(filename, sizeof(filename), "%s", str);
   pfilename = datafilename(filename);
   if (!pfilename) {
     cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
              _("Ruleset directory \"%s\" not found"), str);
-    return;
+    return FALSE;
   }
-  cmd_reply(CMD_RULESETDIR, caller, 
-      C_OK, _("Ruleset directory set to \"%s\""), str);
-  sz_strlcpy(game.rulesetdir, str);
+  if (!check) {
+    cmd_reply(CMD_RULESETDIR, caller, C_OK, 
+              _("Ruleset directory set to \"%s\""), str);
+    sz_strlcpy(game.rulesetdir, str);
+  }
+  return TRUE;
 }
 
 /**************************************************************************
@@ -3842,19 +4059,24 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void quit_game(struct connection *caller)
+static bool quit_game(struct connection *caller, bool check)
 {
-  cmd_reply(CMD_QUIT, caller, C_OK, _("Goodbye."));
-  server_quit();
+  if (!check) {
+    cmd_reply(CMD_QUIT, caller, C_OK, _("Goodbye."));
+    server_quit();
+  }
+  return TRUE;
 }
 
 /**************************************************************************
   Handle "command input", which could really come from stdin on console,
   or from client chat command, or read from file with -r, etc.
   caller==NULL means console, str is the input, which may optionally
-  start with SERVER_COMMAND_PREFIX character
+  start with SERVER_COMMAND_PREFIX character.
+
+  If check is TRUE, then do nothing, just check syntax.
 **************************************************************************/
-void handle_stdin_input(struct connection *caller, char *str)
+bool handle_stdin_input(struct connection *caller, char *str, bool check)
 {
   char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE],
       allargs[MAX_LEN_CONSOLE_LINE], *cptr_s, *cptr_d;
@@ -3862,7 +4084,7 @@
   enum command_id cmd;
 
   /* notify to the server console */
-  if (caller) {
+  if (!check && caller) {
     con_write(C_COMMENT, "%s: '%s'", caller->username, str);
   }
 
@@ -3870,7 +4092,7 @@
   if (may_use_nothing(caller)) {
     cmd_reply(CMD_HELP, caller, C_FAIL,
        _("Sorry, you are not allowed to use server commands."));
-     return;
+     return FALSE;
   }
 
   /* Is it a comment or a blank line? */
@@ -3879,7 +4101,7 @@
     /* nothing */
   }
   if(*cptr_s == 0 || *cptr_s == '#') {
-    return;
+    return FALSE;
   }
 
   /* commands may be prefixed with SERVER_COMMAND_PREFIX, even when
@@ -3910,13 +4132,15 @@
   } else if (cmd == CMD_UNRECOGNIZED) {
     cmd_reply(cmd, caller, C_SYNTAX,
        _("Unknown command.  Try '%shelp'."), caller?"/":"");
-    return;
+    return FALSE;
   }
-  if (!may_use(caller, cmd)) {
-    assert(caller != NULL);
+  if (caller
+      && !(check && caller->access_level >= ALLOW_INFO 
+           && commands[cmd].level == ALLOW_CTRL)
+      && caller->access_level < commands[cmd].level) {
     cmd_reply(cmd, caller, C_FAIL,
              _("You are not allowed to use this command."));
-    return;
+    return FALSE;
   }
 
   for (; *cptr_s != '\0' && my_isspace(*cptr_s); cptr_s++) {
@@ -3934,7 +4158,7 @@
   while(i>0 && my_isspace(arg[i]))
     arg[i--]='\0';
 
-  if (commands[cmd].level > ALLOW_INFO) {
+  if (!check && commands[cmd].level > ALLOW_INFO) {
     /*
      * this command will affect the game - inform all players
      *
@@ -3947,192 +4171,93 @@
 
   switch(cmd) {
   case CMD_REMOVE:
-    remove_player(caller,arg);
-    break;
-  case CMD_RENAME:
-    rename_player(caller,arg);
-    break;
+    return remove_player(caller, arg, check);
   case CMD_SAVE:
-    save_command(caller,arg);
-    break;
+    return save_command(caller,arg, check);
   case CMD_LOAD:
-    load_command(caller, arg);
-    break;
+    return load_command(caller, arg, check);
   case CMD_METAINFO:
-    metainfo_command(caller,arg);
-    break;
+    return metainfo_command(caller, arg, check);
   case CMD_METACONN:
-    metaconnection_command(caller,arg);
-    break;
+    return metaconnection_command(caller, arg, check);
   case CMD_METASERVER:
-    metaserver_command(caller,arg);
-    break;
+    return metaserver_command(caller, arg, check);
   case CMD_HELP:
-    show_help(caller, arg);
-    break;
+    return show_help(caller, arg);
   case CMD_LIST:
-    show_list(caller, arg);
-    break;
+    return show_list(caller, arg);
   case CMD_AITOGGLE:
-    toggle_ai_player(caller,arg);
-    break;
+    return toggle_ai_player(caller, arg, check);
   case CMD_TAKE:
-    take_command(caller, arg);
-    break;
+    return take_command(caller, arg, check);
   case CMD_OBSERVE:
-    observe_command(caller, arg);
-    break;
+    return observe_command(caller, arg, check);
   case CMD_DETACH:
-    detach_command(caller, arg);
-    break;
+    return detach_command(caller, arg, check);
   case CMD_CREATE:
-    create_ai_player(caller,arg);
-    break;
+    return create_ai_player(caller, arg, check);
   case CMD_AWAY:
-    set_away(caller, arg);
-    break;
+    return set_away(caller, arg, check);
   case CMD_NOVICE:
-    set_ai_level(caller, arg, 2);
-    break;
+    return set_ai_level(caller, arg, 2, check);
   case CMD_EASY:
-    set_ai_level(caller, arg, 3);
-    break;
+    return set_ai_level(caller, arg, 3, check);
   case CMD_NORMAL:
-    set_ai_level(caller, arg, 5);
-    break;
+    return set_ai_level(caller, arg, 5, check);
   case CMD_HARD:
-    set_ai_level(caller, arg, 7);
-    break;
-#ifdef DEBUG
+    return set_ai_level(caller, arg, 7, check);
   case CMD_EXPERIMENTAL:
-    set_ai_level(caller, arg, 10);
-    break;
-#endif
+    return set_ai_level(caller, arg, 10, check);
   case CMD_QUIT:
-    quit_game(caller);
-    break;
+    return quit_game(caller, check);
   case CMD_CUT:
-    cut_client_connection(caller, arg);
-    break;
+    return cut_client_connection(caller, arg, check);
   case CMD_SHOW:
-    show_command(caller,arg);
-    break;
+    return show_command(caller, arg, check);
   case CMD_EXPLAIN:
-    explain_option(caller,arg);
-    break;
+    return explain_option(caller, arg, check);
   case CMD_DEBUG:
-    debug_command(caller,arg);
-    break;
+    return debug_command(caller, arg, check);
   case CMD_SET:
-    set_command(caller,arg);
-    break;
+    return set_command(caller, arg, check);
   case CMD_TEAM:
-    team_command(caller, arg);
-    break;
-  case CMD_FIX:
-    fix_command(caller,arg, CMD_FIX);
-    break;
-  case CMD_UNFIX:
-    fix_command(caller, arg, CMD_UNFIX);
-    break;
+    return team_command(caller, arg, check);
   case CMD_RULESETDIR:
-    set_rulesetdir(caller, arg);
-    break;
+    return set_rulesetdir(caller, arg, check);
   case CMD_SCORE:
     if (server_state == RUN_GAME_STATE || server_state == GAME_OVER_STATE) {
-      report_progress_scores();
+      if (!check) {
+        report_progress_scores();
+      }
+      return TRUE;
     } else {
       cmd_reply(cmd, caller, C_SYNTAX,
                _("The game must be running before you can see the score."));
+      return FALSE;
     }
-    break;
   case CMD_WALL:
-    wall(arg);
-    break;
+    return wall(arg, check);
+  case CMD_VOTE:
+    return vote_command(caller, arg, check);
   case CMD_READ_SCRIPT:
-    read_command(caller,arg);
-    break;
+    return read_command(caller, arg, check);
   case CMD_WRITE_SCRIPT:
-    write_command(caller,arg);
-    break;
+    return write_command(caller, arg, check);
   case CMD_RFCSTYLE:   /* see console.h for an explanation */
-    con_set_style(!con_get_style());
-    break;
+    if (!check) {
+      con_set_style(!con_get_style());
+    }
+    return TRUE;
   case CMD_CMDLEVEL:
-    cmdlevel_command(caller,arg);
-    break;
+    return cmdlevel_command(caller, arg, check);
   case CMD_FIRSTLEVEL:
-    firstlevel_command(caller);
-    break;
+    return firstlevel_command(caller, check);
   case CMD_TIMEOUT:
-    timeout_command(caller, allargs);
-    break;
+    return timeout_command(caller, allargs, check);
   case CMD_START_GAME:
-    switch (server_state) {
-    case PRE_GAME_STATE:
-      /* Sanity check scenario */
-      if (game.is_new_game) {
-       if (map.fixed_start_positions
-           && game.max_players > map.num_start_positions) {
-         freelog(LOG_VERBOSE, "Reduced maxplayers from %i to %i to fit "
-                 "to the number of start positions.",
-                 game.max_players, map.num_start_positions);
-         game.max_players = map.num_start_positions;
-       }
-
-       if (game.nplayers > game.max_players) {
-         /* Because of the way player ids are renumbered during
-            server_remove_player() this is correct */
-         while (game.nplayers > game.max_players) {
-           server_remove_player(get_player(game.max_players));
-          }
-
-         freelog(LOG_VERBOSE,
-                 "Had to cut down the number of players to the "
-                 "number of map start positions, there must be "
-                 "something wrong with the savegame or you "
-                 "adjusted the maxplayers value.");
-       }
-      }
-
-      /* check min_players */
-      if (game.nplayers < game.min_players) {
-        cmd_reply(cmd, caller, C_FAIL,
-                 _("Not enough players, game will not start."));
-      } else {
-       start_game();
-      }
-      break;
-    case GAME_OVER_STATE:
-      /* TRANS: given when /start is invoked during gameover. */
-      cmd_reply(cmd, caller, C_FAIL,
-               _("Cannot start the game: the game is waiting for all clients "
-                  "to disconnect."));
-      break;
-    case SELECT_RACES_STATE:
-      /* TRANS: given when /start is invoked during nation selection. */
-      cmd_reply(cmd, caller, C_FAIL,
-               _("Cannot start the game: it has already been started."));
-      break;
-    case RUN_GAME_STATE:
-      /* TRANS: given when /start is invoked while the game is running. */
-      cmd_reply(cmd, caller, C_FAIL,
-               _("Cannot start the game: it is already running."));
-      break;
-    }
-    break;
+    return start_command(caller, arg, check);
   case CMD_END_GAME:
-    if (server_state == RUN_GAME_STATE) {
-      server_state = GAME_OVER_STATE;
-      force_end_of_sniff = TRUE;
-      cmd_reply(cmd, caller, C_OK,
-               _("Ending the game. The server will restart once all clients "
-                  "have disconnected."));
-    } else {
-      cmd_reply(cmd, caller, C_FAIL,
-               _("Cannot end the game: no game running."));
-    }
-    break;
+    return end_command(caller, arg, check);
   case CMD_NUM:
   case CMD_UNRECOGNIZED:
   case CMD_AMBIGUOUS:
@@ -4140,13 +4265,101 @@
     freelog(LOG_FATAL, "bug in civserver: impossible command recognized; 
bye!");
     assert(0);
   }
+  return FALSE; /* should NEVER happen but we need to satisfy some compilers */
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-static void cut_client_connection(struct connection *caller, char *name)
+static bool end_command(struct connection *caller, char *name, bool check)
 {
+  if (server_state == RUN_GAME_STATE) {
+    if (check) {
+      return TRUE;
+    }
+    server_state = GAME_OVER_STATE;
+    force_end_of_sniff = TRUE;
+    cmd_reply(CMD_END_GAME, caller, C_OK,
+              _("Ending the game. The server will restart once all clients "
+              "have disconnected."));
+    return TRUE;
+  } else {
+    cmd_reply(CMD_END_GAME, caller, C_FAIL, 
+              _("Cannot end the game: no game running."));
+    return FALSE;
+  }
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+static bool start_command(struct connection *caller, char *name, bool check)
+{
+  switch (server_state) {
+  case PRE_GAME_STATE:
+    /* Sanity check scenario */
+    if (game.is_new_game && !check) {
+      if (map.fixed_start_positions
+         && game.max_players > map.num_start_positions) {
+       freelog(LOG_VERBOSE, "Reduced maxplayers from %i to %i to fit "
+               "to the number of start positions.",
+               game.max_players, map.num_start_positions);
+       game.max_players = map.num_start_positions;
+      }
+
+      if (game.nplayers > game.max_players) {
+       /* Because of the way player ids are renumbered during
+          server_remove_player() this is correct */
+        while (game.nplayers > game.max_players) {
+         server_remove_player(get_player(game.max_players));
+        }
+
+       freelog(LOG_VERBOSE,
+               "Had to cut down the number of players to the "
+               "number of map start positions, there must be "
+               "something wrong with the savegame or you "
+               "adjusted the maxplayers value.");
+      }
+    }
+
+    /* check min_players */
+    if (game.nplayers < game.min_players) {
+      cmd_reply(CMD_START_GAME, caller, C_FAIL,
+               _("Not enough players, game will not start."));
+      return FALSE;
+    } else if (check) {
+      return TRUE;
+    } else {
+      start_game();
+      return TRUE;
+    }
+  case GAME_OVER_STATE:
+    /* TRANS: given when /start is invoked during gameover. */
+    cmd_reply(CMD_START_GAME, caller, C_FAIL,
+              _("Cannot start the game: the game is waiting for all clients "
+              "to disconnect."));
+    return FALSE;
+  case SELECT_RACES_STATE:
+    /* TRANS: given when /start is invoked during nation selection. */
+    cmd_reply(CMD_START_GAME, caller, C_FAIL,
+              _("Cannot start the game: it has already been started."));
+    return FALSE;
+  case RUN_GAME_STATE:
+    /* TRANS: given when /start is invoked while the game is running. */
+    cmd_reply(CMD_START_GAME, caller, C_FAIL,
+              _("Cannot start the game: it is already running."));
+    return FALSE;
+  }
+  assert(FALSE);
+  return FALSE;
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+static bool cut_client_connection(struct connection *caller, char *name, 
+                                  bool check)
+{
   enum m_pre_result match_result;
   struct connection *ptarget;
   struct player *pplayer;
@@ -4155,7 +4368,9 @@
 
   if (!ptarget) {
     cmd_reply_no_such_conn(CMD_CUT, caller, name, match_result);
-    return;
+    return FALSE;
+  } else if (check) {
+    return TRUE;
   }
 
   pplayer = ptarget->player;
@@ -4169,6 +4384,7 @@
   if (pplayer) {
     sz_strlcpy(pplayer->username, ANON_USER_NAME);
   }
+  return TRUE;
 }
 
 /**************************************************************************
@@ -4313,7 +4529,7 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void show_help(struct connection *caller, char *arg)
+static bool show_help(struct connection *caller, char *arg)
 {
   enum m_pre_result match_result;
   int ind;
@@ -4326,17 +4542,17 @@
 
   if (match_result==M_PRE_EMPTY) {
     show_help_intro(caller, CMD_HELP);
-    return;
+    return FALSE;
   }
   if (match_result==M_PRE_AMBIGUOUS) {
     cmd_reply(CMD_HELP, caller, C_FAIL,
              _("Help argument '%s' is ambiguous."), arg);
-    return;
+    return FALSE;
   }
   if (match_result==M_PRE_FAIL) {
     cmd_reply(CMD_HELP, caller, C_FAIL,
              _("No match for help argument '%s'."), arg);
-    return;
+    return FALSE;
   }
 
   /* other cases should be above */
@@ -4344,27 +4560,28 @@
   
   if (ind < CMD_NUM) {
     show_help_command(caller, CMD_HELP, ind);
-    return;
+    return TRUE;
   }
   ind -= CMD_NUM;
   
   if (ind == HELP_GENERAL_OPTIONS) {
     show_help_option_list(caller, CMD_HELP);
-    return;
+    return TRUE;
   }
   if (ind == HELP_GENERAL_COMMANDS) {
     show_help_command_list(caller, CMD_HELP);
-    return;
+    return TRUE;
   }
   ind -= HELP_GENERAL_NUM;
   
   if (ind < SETTINGS_NUM) {
     show_help_option(caller, CMD_HELP, ind);
-    return;
+    return TRUE;
   }
   
   /* should have finished by now */
   freelog(LOG_ERROR, "Bug in show_help!");
+  return FALSE;
 }
 
 /**************************************************************************
@@ -4382,7 +4599,7 @@
 /**************************************************************************
   Show list of players or connections, or connection statistics.
 **************************************************************************/
-static void show_list(struct connection *caller, char *arg)
+static bool show_list(struct connection *caller, char *arg)
 {
   enum m_pre_result match_result;
   int ind;
@@ -4395,7 +4612,7 @@
     cmd_reply(CMD_LIST, caller, C_SYNTAX,
              _("Bad list argument: '%s'.  Try '%shelp list'."),
              arg, (caller?"/":""));
-    return;
+    return FALSE;
   }
 
   if (match_result == M_PRE_EMPTY) {
@@ -4405,15 +4622,15 @@
   switch(ind) {
   case LIST_PLAYERS:
     show_players(caller);
-    return;
+    return TRUE;
   case LIST_CONNECTIONS:
     show_connections(caller);
-    return;
+    return TRUE;
   default:
     cmd_reply(CMD_LIST, caller, C_FAIL,
              "Internal error: ind %d in show_list", ind);
     freelog(LOG_ERROR, "Internal error: ind %d in show_list", ind);
-    return;
+    return FALSE;
   }
 }
 
@@ -4788,15 +5005,12 @@
 Commands that may be followed by a player name
 **************************************************************************/
 static const int player_cmd[] = {
-  CMD_RENAME,
   CMD_AITOGGLE,
   CMD_NOVICE,
   CMD_EASY,
   CMD_NORMAL,
   CMD_HARD,
-#ifdef DEBUG
   CMD_EXPERIMENTAL,
-#endif
   CMD_REMOVE,
   CMD_TEAM,
   -1
Index: server/stdinhand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.h,v
retrieving revision 1.26
diff -u -r1.26 stdinhand.h
--- server/stdinhand.h  2002/12/17 15:14:48     1.26
+++ server/stdinhand.h  2004/02/04 19:35:12
@@ -20,14 +20,18 @@
 #define SERVER_COMMAND_PREFIX '/'
   /* the character to mark chatlines as server commands */
 
-void handle_stdin_input(struct connection *caller, char *str);
+void stdinhand_init(void);
+void stdinhand_turn(void);
+void stdinhand_free(void);
+
+bool handle_stdin_input(struct connection *caller, char *str, bool check);
 void report_server_options(struct conn_list *dest, int which);
 void set_ai_level_direct(struct player *pplayer, int level);
 void set_ai_level_directer(struct player *pplayer, int level);
 bool read_init_script(struct connection *caller, char *script_filename);
 void show_players(struct connection *caller);
 
-void load_command(struct connection *caller, char *arg);
+bool load_command(struct connection *caller, char *arg, bool check);
 
 
 void toggle_ai_player_direct(struct connection *caller,

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#7378) Vote command for server, Per I. Mathisen <=