Skip to content

Commit

Permalink
Add commands to control the timeout
Browse files Browse the repository at this point in the history
Add three new timeout-related server commands:

- timeoutshow displays the current timeout
- timeoutadd adds time to the current turn
- timeoutset sets the remaining time for the current turn

They are named timeoutsomething for three reasons:

- timeoutincrease already exists
- it's easier to find them if they're together in /help commands
- sub-commands like "timeout show" are not feasible with the current
  implementation

This is heavily inspired from an earlier patch used by LT with 2.6,
41cc12b401fe4a9db8d22b78039561710fff59e3. The syntax is however more flexible,
allowing for hours or minutes to be passed directly without doing any
calculation.

Closes #1151.
  • Loading branch information
lmoureaux committed Aug 14, 2022
1 parent f4d14ad commit 2e5d3a0
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 7 deletions.
26 changes: 26 additions & 0 deletions server/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,32 @@ static struct command commands[] = {
N_("If there is none, become the game organizer with increased "
"permissions."),
nullptr, nullptr, CMD_ECHO_ADMINS, VCF_NONE, 50},
{"timeoutshow", ALLOW_INFO, SYN_ORIG_("timeoutshow"),
N_("Show timeout settings and timers."),
N_("Shows information about the timeout for the current turn, for "
"instance how much time is left."),
nullptr, CMD_ECHO_ALL, VCF_NONE, 0},
{"timeoutset", ALLOW_CTRL,
// TRANS: translate text between <> only
N_("timeoutset <time>"),
N_("Changes the timeout for the current turn."),
// TRANS: don't translate the format
N_("This command changes the remaining time for the current turn. "
"Passing a value of 0 ends the turn immediately.\n"
"The time is specified as hours, minutes, and seconds using the "
"format hh:mm:ss (minutest and hours and are "
"optional)."),
nullptr, CMD_ECHO_ALL, VCF_NONE, 50},
{"timeoutadd", ALLOW_CTRL,
// TRANS: translate text between <> only
N_("timeoutadd <time>"), N_("Adds more time to the current turn."),
// TRANS: don't translate the format
N_("This increases the timeout for the current turn, giving players "
"more time to finish their actions.\n"
"The time is specified as hours, minutes, and seconds using the "
"format hh:mm:ss (minutest and hours and are "
"optional)."),
nullptr, CMD_ECHO_ALL, VCF_NONE, 50},
{"timeoutincrease", ALLOW_CTRL,
// TRANS: translate text between <> only
N_("timeoutincrease <turn> <turninc> <value> <valuemult>"),
Expand Down
5 changes: 4 additions & 1 deletion server/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ enum command_id {
#endif
CMD_CMDLEVEL,
CMD_FIRSTLEVEL,
CMD_TIMEOUT,
CMD_TIMEOUT_SHOW, // not really harmful, info level
CMD_TIMEOUT_SET,
CMD_TIMEOUT_ADD,
CMD_TIMEOUT_INCREASE,
CMD_CANCELVOTE,
CMD_IGNORE,
CMD_UNIGNORE,
Expand Down
129 changes: 123 additions & 6 deletions server/stdinhand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

// server
#include "aiiface.h"
#include "commands.h"
#include "connecthand.h"
#include "diplhand.h"
#include "gamehand.h"
Expand Down Expand Up @@ -1596,6 +1597,116 @@ static const char *olvlname_accessor(int i)
}
}

/**
* Show the current timeout, time left before TC, etc.
*/
static bool timeout_show_command(struct connection *caller, char *str,
bool check)
{
if (check) {
// There's never a syntax error.
return true;
}

if (game.info.timeout <= 0) {
cmd_reply(CMD_TIMEOUT_SHOW, caller, C_OK,
_("The timeout is currently disabled."));
return true;
}

char buf[128];
format_time_duration(game.info.timeout, buf, 127);
cmd_reply(CMD_TIMEOUT_SHOW, caller, C_OK,
_("The 'timeout' setting is: %6d s = %s"),
game.info.timeout, buf);

if (server_state() != S_S_RUNNING) {
cmd_reply(CMD_TIMEOUT_SHOW, caller, C_OK,
_("The game is currently not running."));
return true;
}

int phase_timer = timer_read_seconds(game.server.phase_timer);
int end_time = game.tinfo.seconds_to_phasedone;

format_time_duration(phase_timer, buf, 127);
cmd_reply(CMD_TIMEOUT_SHOW, caller, C_OK,
_("The current phase has now lasted: %6d s = %s"), phase_timer,
buf);

format_time_duration(end_time - phase_timer, buf, 127);
cmd_reply(CMD_TIMEOUT_SHOW, caller, C_OK,
_("Time left: %6d s = %s"),
end_time - phase_timer, buf);

return true;
}

/**
* Sets the timeout for the current turn. If "add", add more time; else,
* replace the timeout.
*/
static bool timeout_set_command(struct connection *caller, char *str,
bool check, command_id self, bool add)
{
if (server_state() != S_S_RUNNING) {
cmd_reply(
self, caller, C_FAIL,
_("Cannot change the turn timeout while the game is not running."));
return false;
} else if (game.info.timeout <= 0) {
// TRANS: Don't translate "/set timeout"
cmd_reply(self, caller, C_FAIL,
_("The timeout is disabled, use /set timeout."));
return false;
}

// Parse the time as [[hours:]minutes:]seconds
const auto parts = QString(str).split(':');
if (parts.size() > 3) {
cmd_reply(self, caller, C_FAIL, _("Invalid time specified."));
return false;
}

bool ok[3] = {true, true, true};
int h = parts.size() > 2 ? parts.front().toInt(&ok[0]) : 0;
int m = parts.size() > 1 ? parts[parts.size() - 2].toInt(&ok[1]) : 0;
int s = parts.size() > 0 ? parts.back().toInt(&ok[2]) : 0;
if (!ok[0] || !ok[1] || !ok[2]) {
cmd_reply(self, caller, C_FAIL, _("Invalid time specified."));
return false;
}

int seconds = 3600 * h + 60 * m + s;
if (seconds > 86400 * 365) {
// More than a year
cmd_reply(self, caller, C_FAIL, _("Time too big."));
return false;
}

if (check) {
// Syntax is ok
return true;
}

auto timer_now = timer_read_seconds(game.server.phase_timer);

if (add) {
game.tinfo.seconds_to_phasedone += seconds;
cmd_reply(self, caller, C_OK, _("Adding %d seconds to the current timeout"),
seconds);
} else {
game.tinfo.seconds_to_phasedone = timer_now + seconds;
cmd_reply(self, caller, C_OK,
_("Setting the remaining time to %d seconds"), seconds);
}

// send everyone the updated timeout
send_game_info(nullptr);

return true;
}

/**
Set timeout options.
*/
Expand All @@ -1617,20 +1728,20 @@ static bool timeout_command(struct connection *caller, char *str, bool check)

for (i = 0; i < arg.count(); i++) {
if (!str_to_int(qUtf8Printable(arg.at(i)), timeouts[i])) {
cmd_reply(CMD_TIMEOUT, caller, C_FAIL, _("Invalid argument %d."),
i + 1);
cmd_reply(CMD_TIMEOUT_INCREASE, caller, C_FAIL,
_("Invalid argument %d."), i + 1);
}
}

if (arg.isEmpty()) {
cmd_reply(CMD_TIMEOUT, caller, C_SYNTAX, _("Usage:\n%s"),
command_synopsis(command_by_number(CMD_TIMEOUT)));
cmd_reply(CMD_TIMEOUT_INCREASE, caller, C_SYNTAX, _("Usage:\n%s"),
command_synopsis(command_by_number(CMD_TIMEOUT_INCREASE)));
return false;
} else if (check) {
return true;
}

cmd_reply(CMD_TIMEOUT, caller, C_OK,
cmd_reply(CMD_TIMEOUT_INCREASE, caller, C_OK,
_("Dynamic timeout set to "
"%d %d %d %d"),
game.server.timeoutint, game.server.timeoutintinc,
Expand Down Expand Up @@ -4628,7 +4739,13 @@ static bool handle_stdin_input_real(struct connection *caller, char *str,
return cmdlevel_command(caller, arg, check);
case CMD_FIRSTLEVEL:
return firstlevel_command(caller, check);
case CMD_TIMEOUT:
case CMD_TIMEOUT_SHOW:
return timeout_show_command(caller, arg, check);
case CMD_TIMEOUT_SET:
return timeout_set_command(caller, arg, check, CMD_TIMEOUT_SET, false);
case CMD_TIMEOUT_ADD:
return timeout_set_command(caller, arg, check, CMD_TIMEOUT_ADD, true);
case CMD_TIMEOUT_INCREASE:
return timeout_command(caller, arg, check);
case CMD_START_GAME:
return start_command(caller, check, false);
Expand Down

0 comments on commit 2e5d3a0

Please sign in to comment.