[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 <=
|
|