190
180
**************************************************************************/
191
181
void stdinhand_init(void)
195
for (i = 0; i < MAX_NUM_PLAYERS; i++) {
196
votes[i].command[0] = '\0';
197
memset(votes[i].votes_cast, 0, sizeof(votes[i].votes_cast));
198
votes[i].vote_no = -1;
203
/**************************************************************************
204
Check if we satisfy the criteria for resolving a vote, and resolve it
205
if these critera are indeed met. Updates yes and no variables in voting
209
Accepted immediately if: > 50% of votes for
210
Rejected immediately if: >= 50% of votes against
211
**************************************************************************/
212
static void check_vote(struct voting *vote)
214
int i, num_cast = 0, num_voters = 0;
219
for (i = 0; i < MAX_NUM_PLAYERS; i++) {
220
if (game.players[i].is_alive && game.players[i].is_connected) {
223
/* Disqualify already given vote (eg if disconnected after voting) */
224
vote->votes_cast[i] = VOTE_NONE;
227
for (i = 0; i < MAX_NUM_PLAYERS; i++) {
228
num_cast = (vote->votes_cast[i] > VOTE_NONE) ? num_cast + 1 : num_cast;
229
vote->yes = (vote->votes_cast[i] == VOTE_YES) ? vote->yes + 1 : vote->yes;
230
vote->no = (vote->votes_cast[i] == VOTE_NO) ? vote->no + 1 : vote->no;
233
/* Check if we should resolve the vote */
234
if (vote->command[0] != '\0'
236
&& (vote->yes > num_voters / 2
237
|| vote->no >= (num_voters + 1) / 2)) {
238
/* Yep, resolve this one */
240
if (last_vote == vote->vote_no) {
243
if (vote->yes > num_voters / 2) {
245
notify_conn(NULL, NULL, E_SETTING,
246
_("Vote \"%s\" is passed %d to %d with %d "
248
vote->command, vote->yes, vote->no,
249
num_voters - vote->yes - vote->no);
250
handle_stdin_input((struct connection *)NULL, vote->command, FALSE);
252
notify_conn(NULL, NULL, E_SETTING,
253
_("Vote \"%s\" failed with %d against, %d for "
254
"and %d abstentions."),
255
vote->command, vote->no, vote->yes,
256
num_voters - vote->yes - vote->no);
258
vote->command[0] = '\0';
262
186
/**************************************************************************
1249
1171
**************************************************************************/
1250
1172
static bool cmdlevel_command(struct connection *caller, char *str, bool check)
1252
char arg_level[MAX_LEN_CONSOLE_LINE]; /* info, ctrl etc */
1253
char arg_name[MAX_LEN_CONSOLE_LINE]; /* a player name, or "new" */
1254
char *cptr_s, *cptr_d; /* used for string ops */
1256
1177
enum m_pre_result match_result;
1257
1178
enum cmdlevel_id level;
1258
1179
struct connection *ptarget;
1260
/* find the start of the level: */
1261
for (cptr_s = str; *cptr_s != '\0' && !my_isalnum(*cptr_s); cptr_s++) {
1265
/* copy the level into arg_level[] */
1266
for(cptr_d=arg_level; *cptr_s != '\0' && my_isalnum(*cptr_s); cptr_s++, cptr_d++) {
1271
if (arg_level[0] == '\0') {
1272
/* no level name supplied; list the levels */
1274
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, _("Command access levels in effect:"));
1181
ntokens = get_tokens(str, arg, 2, TOKEN_DELIMITERS);
1184
/* No argument supplied; list the levels */
1185
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1186
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1187
_("Command access levels in effect:"));
1188
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1276
1189
conn_list_iterate(game.est_connections, pconn) {
1277
1190
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, "cmdlevel %s %s",
1278
cmdlevel_name(pconn->access_level), pconn->username);
1280
conn_list_iterate_end;
1281
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1282
_("Command access level for new connections: %s"),
1283
cmdlevel_name(default_access_level));
1284
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1285
_("Command access level for first player to take it: %s"),
1286
cmdlevel_name(first_access_level));
1191
cmdlevel_name(conn_get_access(pconn)), pconn->username);
1192
} conn_list_iterate_end;
1193
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1194
_("Command access level for new connections: %s"),
1195
cmdlevel_name(default_access_level));
1196
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT,
1197
_("Command access level for first player to take it: %s"),
1198
cmdlevel_name(first_access_level));
1199
cmd_reply(CMD_CMDLEVEL, caller, C_COMMENT, horiz_line);
1290
/* a level name was supplied; set the level */
1292
if ((level = cmdlevel_named(arg_level)) == ALLOW_UNRECOGNIZED) {
1203
/* A level name was supplied; set the level */
1204
if ((level = cmdlevel_named(arg[0])) == ALLOW_UNRECOGNIZED) {
1293
1205
cmd_reply(CMD_CMDLEVEL, caller, C_SYNTAX,
1294
_("Error: command access level must be one of"
1295
" 'none', 'info', 'ctrl', or 'hack'."));
1297
} else if (caller && level > caller->access_level) {
1206
_("Error: command access level must be one of"
1207
" 'none', 'info', 'ctrl', or 'hack'."));
1209
} else if (caller && level > conn_get_access(caller)) {
1298
1210
cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1299
_("Cannot increase command access level to '%s';"
1300
" you only have '%s' yourself."),
1301
arg_level, cmdlevel_name(caller->access_level));
1211
_("Cannot increase command access level to '%s';"
1212
" you only have '%s' yourself."),
1213
arg[0], cmdlevel_name(conn_get_access(caller)));
1305
return TRUE; /* looks good */
1308
/* find the start of the name: */
1309
for (; *cptr_s != '\0' && !my_isalnum(*cptr_s); cptr_s++) {
1313
/* copy the name into arg_name[] */
1314
for(cptr_d=arg_name;
1315
*cptr_s != '\0' && (*cptr_s == '-' || *cptr_s == ' ' || my_isalnum(*cptr_s));
1316
cptr_s++ , cptr_d++) {
1321
if (arg_name[0] == '\0') {
1322
/* no playername supplied: set for all connections, and set the default */
1218
return TRUE; /* looks good */
1222
/* No playername supplied: set for all connections */
1323
1223
conn_list_iterate(game.est_connections, pconn) {
1324
if (set_cmdlevel(caller, pconn, level)) {
1325
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1326
_("Command access level set to '%s' for connection %s."),
1327
cmdlevel_name(level), pconn->username);
1328
send_conn_info(pconn->self, NULL);
1330
cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1331
_("Command access level could not be set to '%s' for "
1333
cmdlevel_name(level), pconn->username);
1224
if (pconn != caller) {
1225
(void) set_cmdlevel(caller, pconn, level);
1227
} conn_list_iterate_end;
1229
/* Set the caller access level at last, because it could make the
1230
* previous operations impossible if set before. */
1232
(void) set_cmdlevel(caller, caller, level);
1337
conn_list_iterate_end;
1235
/* Set default access for new connections. */
1339
1236
default_access_level = level;
1340
1237
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1341
_("Command access level set to '%s' for new players."),
1342
cmdlevel_name(level));
1238
_("Command access level set to '%s' for new players."),
1239
cmdlevel_name(level));
1240
/* Set default access for first connection. */
1343
1241
first_access_level = level;
1344
1242
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1345
_("Command access level set to '%s' for first player to grab it."),
1346
cmdlevel_name(level));
1348
else if (strcmp(arg_name,"new") == 0) {
1243
_("Command access level set to '%s' "
1244
"for first player to grab it."),
1245
cmdlevel_name(level));
1249
} else if (mystrcasecmp(arg[1], "new") == 0) {
1349
1250
default_access_level = level;
1350
1251
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1351
_("Command access level set to '%s' for new players."),
1352
cmdlevel_name(level));
1252
_("Command access level set to '%s' for new players."),
1253
cmdlevel_name(level));
1353
1254
if (level > first_access_level) {
1354
1255
first_access_level = level;
1355
1256
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1356
_("Command access level set to '%s' for first player to grab it."),
1357
cmdlevel_name(level));
1257
_("Command access level set to '%s' "
1258
"for first player to grab it."),
1259
cmdlevel_name(level));
1360
else if (strcmp(arg_name,"first") == 0) {
1264
} else if (mystrcasecmp(arg[1], "first") == 0) {
1361
1265
first_access_level = level;
1362
1266
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1363
_("Command access level set to '%s' for first player to grab it."),
1364
cmdlevel_name(level));
1267
_("Command access level set to '%s' "
1268
"for first player to grab it."),
1269
cmdlevel_name(level));
1365
1270
if (level < default_access_level) {
1366
1271
default_access_level = level;
1367
1272
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1368
_("Command access level set to '%s' for new players."),
1369
cmdlevel_name(level));
1273
_("Command access level set to '%s' for new players."),
1274
cmdlevel_name(level));
1372
else if ((ptarget = find_conn_by_user_prefix(arg_name, &match_result))) {
1279
} else if ((ptarget = find_conn_by_user_prefix(arg[1], &match_result))) {
1373
1280
if (set_cmdlevel(caller, ptarget, level)) {
1374
cmd_reply(CMD_CMDLEVEL, caller, C_OK,
1375
_("Command access level set to '%s' for connection %s."),
1376
cmdlevel_name(level), ptarget->username);
1377
send_conn_info(ptarget->self, NULL);
1379
cmd_reply(CMD_CMDLEVEL, caller, C_FAIL,
1380
_("Command access level could not be set to '%s'"
1381
" for connection %s."),
1382
cmdlevel_name(level), ptarget->username);
1386
cmd_reply_no_such_conn(CMD_CMDLEVEL, caller, arg_name, match_result);
1284
cmd_reply_no_such_conn(CMD_CMDLEVEL, caller, arg[1], match_result);
1288
free_tokens(arg, ntokens);
1392
1292
/**************************************************************************
2148
/******************************************************************
2022
/**************************************************************************
2023
List all running votes. Moved from /vote command.
2024
**************************************************************************/
2025
static void show_votes(struct connection *caller)
2030
if (vote_list != NULL) {
2031
vote_list_iterate(vote_list, pvote) {
2032
if (!conn_can_see_vote(caller, pvote)) {
2035
title = vote_is_team_only(pvote) ? _("Teamvote") : _("Vote");
2036
cmd_reply(CMD_VOTE, caller, C_COMMENT,
2037
_("%s %d \"%s\" (needs %0.0f%%%s): %d for, "
2038
"%d against, and %d abstained out of %d players."),
2039
title, pvote->vote_no, pvote->cmdline,
2040
MIN(100, pvote->need_pc * 100 + 1),
2041
pvote->flags & VCF_NODISSENT ? _(" no dissent") : "",
2042
pvote->yes, pvote->no, pvote->abstain, game.info.nplayers);
2044
} vote_list_iterate_end;
2048
cmd_reply(CMD_VOTE, caller, C_COMMENT,
2049
_("There are no votes going on."));
2053
/**************************************************************************
2054
Vote command argument definitions.
2055
**************************************************************************/
2056
static const char *const vote_args[] = {
2062
static const char *vote_arg_accessor(int i)
2064
return vote_args[i];
2067
/**************************************************************************
2149
2068
Make or participate in a vote.
2150
******************************************************************/
2151
static bool vote_command(struct connection *caller, char *str,
2069
**************************************************************************/
2070
static bool vote_command(struct connection *caller, char *str, bool check)
2154
2072
char buf[MAX_LEN_CONSOLE_LINE];
2157
const char *usage = _("Undefined arguments. Usage: vote yes|no "
2074
int ntokens = 0, i = 0, which = -1;
2075
enum m_pre_result match_result;
2076
struct vote *pvote = NULL;
2077
const char *usage = _("Invalid arguments. Usage: vote "
2078
"yes|no|abstain [vote number].");
2159
2079
bool res = FALSE;
2161
if (caller == NULL || caller->player == NULL) {
2162
cmd_reply(CMD_VOTE, caller, C_FAIL, _("This command is client only."));
2164
} else if (caller->observer) {
2165
cmd_reply(CMD_VOTE, caller, C_FAIL, _("Observers cannot vote."));
2167
} else if (S_S_RUNNING != server_state()) {
2168
cmd_reply(CMD_VOTE, caller, C_FAIL, _("You can only vote in a "
2169
"running game. Use 'first' to become the game organizer "
2170
"if there currently is none."));
2172
} else if (!str || strlen(str) == 0) {
2175
for (i = 0; i < MAX_NUM_PLAYERS; i++) {
2176
struct voting *vote = &votes[i];
2178
if (vote->command[0] != '\0') {
2180
cmd_reply(CMD_VOTE, caller, C_COMMENT,
2181
_("Vote %d \"%s\": %d for, %d against"),
2182
vote->vote_no, vote->command, vote->yes,
2187
cmd_reply(CMD_VOTE, caller, C_COMMENT,
2188
_("There are no votes going on."));
2190
return FALSE; /* see below */
2192
return FALSE; /* cannot vote over having vote! */
2082
/* This should never happen, since /vote must always be
2083
* set to ALLOW_BASIC or less. But just in case... */
2195
2087
sz_strlcpy(buf, str);
2196
2088
ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS);
2198
if (strcmp(arg[0], "yes") == 0
2199
|| strcmp(arg[0], "no") == 0) {
2201
struct voting *vote = NULL;
2204
/* Applies to last vote */
2205
if (last_vote > -1) {
2208
cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote."));
2212
if (sscanf(arg[1], "%d", &which) <= 0) {
2213
cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be integer."));
2217
/* Ok, now try to find this vote */
2218
for (i = 0; i < MAX_NUM_PLAYERS; i++) {
2219
if (votes[i].vote_no == which) {
2223
if (which > last_vote || !vote || vote->command[0] == '\0') {
2224
cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), which);
2227
if (strcmp(arg[0], "yes") == 0) {
2228
cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
2230
vote->votes_cast[player_index(caller->player)] = VOTE_YES;
2231
} else if (strcmp(arg[0], "no") == 0) {
2232
cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
2234
vote->votes_cast[player_index(caller->player)] = VOTE_NO;
2093
} else if (!conn_can_vote(caller, NULL)) {
2094
cmd_reply(CMD_VOTE, caller, C_FAIL,
2095
_("You are not allowed to use this command."));
2099
match_result = match_prefix(vote_arg_accessor, VOTE_NUM, 0,
2100
mystrncasecmp, NULL, arg[0], &i);
2102
if (match_result == M_PRE_AMBIGUOUS) {
2103
cmd_reply(CMD_VOTE, caller, C_SYNTAX,
2104
_("The argument \"%s\" is ambigious."), arg[0]);
2106
} else if (match_result > M_PRE_AMBIGUOUS) {
2238
2108
cmd_reply(CMD_VOTE, caller, C_SYNTAX, "%s", usage);
2113
/* Applies to last vote */
2114
if (vote_number_sequence > 0 && get_vote_by_no(vote_number_sequence)) {
2115
which = vote_number_sequence;
2117
int num_votes = vote_list_size(vote_list);
2118
if (num_votes == 0) {
2119
cmd_reply(CMD_VOTE, caller, C_FAIL, _("There are no votes running."));
2121
cmd_reply(CMD_VOTE, caller, C_FAIL, _("No legal last vote (%d %s)."),
2122
num_votes, PL_("other vote running", "other votes running",
2128
if (sscanf(arg[1], "%d", &which) <= 0) {
2129
cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be an integer."));
2134
if (!(pvote = get_vote_by_no(which))) {
2135
cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), which);
2139
if (!conn_can_vote(caller, pvote)) {
2140
cmd_reply(CMD_VOTE, caller, C_FAIL,
2141
_("You are not allowed to vote on that."));
2145
if (i == VOTE_YES) {
2146
cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
2148
connection_vote(caller, pvote, VOTE_YES);
2149
} else if (i == VOTE_NO) {
2150
cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
2152
connection_vote(caller, pvote, VOTE_NO);
2153
} else if (i == VOTE_ABSTAIN) {
2154
cmd_reply(CMD_VOTE, caller, C_COMMENT,
2155
_("You abstained from voting on \"%s\""), pvote->cmdline);
2156
connection_vote(caller, pvote, VOTE_ABSTAIN);
2158
assert(0); /* Must never happen */
2244
for (i = 0; i < ntokens; i++) {
2164
free_tokens(arg, ntokens);
2168
/**************************************************************************
2169
Cancel a vote... /cancelvote <vote number>|all.
2170
**************************************************************************/
2171
static bool cancelvote_command(struct connection *caller,
2172
char *arg, bool check)
2174
struct vote *pvote = NULL;
2178
/* This should never happen anyway, since /cancelvote
2179
* is set to ALLOW_BASIC in both pregame and while the
2180
* game is running. */
2184
remove_leading_trailing_spaces(arg);
2186
if (arg[0] == '\0') {
2187
if (caller == NULL) {
2189
cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2190
_("Missing argument <vote number> or "
2191
"the string \"all\"."));
2194
/* The caller cancel his/her own vote. */
2195
if (!(pvote = get_vote_by_caller(caller))) {
2196
cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2197
_("You don't have any vote going on."));
2200
} else if (mystrcasecmp(arg, "all") == 0) {
2201
/* Cancel all votes (needs some privileges). */
2202
if (vote_list_size(vote_list) == 0) {
2203
cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2204
_("There isn't any vote going on."));
2206
} else if (!caller || conn_get_access(caller) >= ALLOW_ADMIN) {
2208
notify_conn(NULL, NULL, E_CHAT_MSG,
2209
_("Server: All votes have been removed."));
2212
cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2213
_("You are not allowed to use this command."));
2216
} else if (sscanf(arg, "%d", &vote_no) == 1) {
2217
/* Cancel one particular vote (needs some privileges if the vote
2219
if (!(pvote = get_vote_by_no(vote_no))) {
2220
cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2221
_("No such vote (%d)."), vote_no);
2223
} else if (caller && conn_get_access(caller) < ALLOW_ADMIN
2224
&& caller->id != pvote->caller_id) {
2225
cmd_reply(CMD_CANCELVOTE, caller, C_FAIL,
2226
_("You are not allowed to cancel this vote (%d)."),
2231
cmd_reply(CMD_CANCELVOTE, caller, C_SYNTAX,
2232
_("Usage: /cancelvote [<vote number>|all]"));
2236
assert(pvote != NULL);
2239
notify_team(conn_get_player(vote_get_caller(pvote)),
2241
_("Server: %s cancelled the vote \"%s\" (number %d)."),
2242
caller->username, pvote->cmdline, pvote->vote_no);
2245
notify_team(conn_get_player(vote_get_caller(pvote)),
2247
_("Server: The vote \"%s\" (number %d) has been cancelled."),
2248
pvote->cmdline, pvote->vote_no);
2250
/* Make it after, prevent crashs about a free pointer (pvote). */
2250
2256
/******************************************************************
2251
2257
Turn on selective debugging.
2252
2258
******************************************************************/
3612
if (S_S_INITIAL != server_state()
3615
&& !caller->observer /* don't allow observers to ask votes */
3617
&& caller->access_level == ALLOW_INFO
3618
&& commands[cmd].level == ALLOW_CTRL) {
3619
int idx = player_index(caller->player);
3665
level = command_access_level(cmd);
3667
if (conn_can_vote(caller, NULL) && level == ALLOW_CTRL
3668
&& conn_get_access(caller) == ALLOW_BASIC && !check) {
3621
3671
/* If we already have a vote going, cancel it in favour of the new
3622
3672
* vote command. You can only have one vote at a time. */
3623
if (votes[idx].command[0] != '\0') {
3673
if (get_vote_by_caller(caller)) {
3624
3674
cmd_reply(CMD_VOTE, caller, C_COMMENT,
3625
_("Your new vote canceled your previous vote."));
3626
votes[idx].command[0] = '\0';
3675
_("Your new vote cancelled your previous vote."));
3629
3678
/* Check if the vote command would succeed. */
3630
if (handle_stdin_input(caller, full_command, TRUE)) {
3632
notify_conn(NULL, NULL, E_SETTING,
3633
_("New vote (number %d) by %s: %s."),
3635
player_name(caller->player),
3637
sz_strlcpy(votes[idx].command, full_command);
3638
votes[idx].vote_no = last_vote;
3639
memset(votes[idx].votes_cast, VOTE_NONE, sizeof(votes[idx].votes_cast));
3640
votes[idx].votes_cast[idx] = VOTE_YES; /* vote on your own suggestion */
3641
check_vote(&votes[idx]); /* update vote numbers, maybe auto-accept */
3679
if (handle_stdin_input(caller, full_command, TRUE)
3680
&& (vote = vote_new(caller, allargs, cmd))) {
3681
char votedesc[MAX_LEN_CONSOLE_LINE];
3682
const struct player *teamplr;
3685
describe_vote(vote, votedesc, sizeof(votedesc));
3687
if (vote_is_team_only(vote)) {
3688
what = _("New teamvote");
3689
teamplr = conn_get_player(caller);
3691
what = _("New vote");
3694
notify_team(teamplr, NULL, E_CHAT_MSG,
3695
_("%s (number %d) by %s: %s"), what,
3696
vote->vote_no, caller->username, votedesc);
3698
/* Vote on your own suggestion. */
3699
connection_vote(caller, vote, VOTE_YES);
3644
3703
cmd_reply(CMD_VOTE, caller, C_FAIL,
3645
_("Your new vote (\"%s\") was not legal or was not recognized."),
3704
_("Your new vote (\"%s\") was not "
3705
"legal or was not recognized."), full_command);
3651
&& !(check && caller->access_level >= ALLOW_INFO
3652
&& commands[cmd].level == ALLOW_CTRL)
3653
&& caller->access_level < commands[cmd].level) {
3710
if (caller && !(check && conn_get_access(caller) >= ALLOW_BASIC
3711
&& level == ALLOW_CTRL)
3712
&& conn_get_access(caller) < level) {
3654
3713
cmd_reply(cmd, caller, C_FAIL,
3655
3714
_("You are not allowed to use this command."));
3659
cptr_s = skip_leading_spaces(cptr_s);
3660
sz_strlcpy(arg, cptr_s);
3664
/* keep this before we cut everything after a space */
3665
sz_strlcpy(allargs, cptr_s);
3666
cut_comment(allargs);
3669
while(i>0 && my_isspace(arg[i]))
3672
if (!check && commands[cmd].level > ALLOW_INFO) {
3718
if (!check && level > ALLOW_INFO) {
3674
3720
* this command will affect the game - inform all players