From 71320ed67fda6360a46d51638bf4f1d8933cd68c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 11 Jul 2007 22:27:29 +0000 Subject: [PATCH] New update scheduler: (schedulefil): New. (set_dirs, set_paths): Rename. Initialize schedulfil. (read_schedule): New. Can read several updates, which will be used in later changesets. (update_time): Change to array. Will be used in later changesets. (update_schedule_anchor): New. (update_init): Initialize it. (update_get_schedule): New. (update_init): Call it to initialize update_time[]. (update_sched): Rewrite. (update_forced, update_wanted): Replace. (update_reschedule): New. (main): Call it on SIGHUP to reload the schedule. (update_trigger, update_force, force, player_coms): Drop force's capability to schedule updates in the future, because it's not worth the trouble to implement again. Deities can simply edit the schedule file to schedule updates. Remove update_force() and update_trigger()'s parameter. (upda): Update for new scheduler. Take care to keep output the same as far as possible, even though it's ugly, to avoid breaking clients. (update_policy, adj_update, update_times, hourslop, blitz_time): econfig keys removed. (update_demand, UPD_DEMAND_NONE, UPD_DEMAND_SCHED, UPD_DEMAND_ASYNC) (update_demandpolicy, UDP_NORMAL, UDP_TIMES, UDP_NORMAL, UDP_BLITZ) (UDP_MAX, UDP_DEFAULT, UDDEM_TMCHECK, UDDEM_COMSET, UDDEM_DISABLE) (UDDEM_MAX, UDDEM_DEFAULT): econfig key and values replaced. Users changed. wantupd.h is now empty, remove. (demand_check): External linkage. (update_policy_check): Now pointless, remove. (is_daytime_near, min_to_next_daytime, regular_update_time) (scheduled_update_time, next_scheduled_time, updatetime) (next_update_time, next_update_check_time): Unused, Remove. (demand_check, demandupdatecheck): Move call of demand_update_time() from demand_check(), which controls all demand updates, to demandupdatecheck(), which controls only unscheduled ones. Fixes update command not to lie about the next scheduled demand update. (demandupdatecheck): Check updates_disabled() so that zdone no longer claims to trigger an update when it can't. --- include/econfig-spec.h | 42 ++--- include/optlist.h | 7 + include/prototypes.h | 9 +- include/server.h | 5 +- include/wantupd.h | 53 ------ info/force.t | 6 +- man/emp_server.6 | 3 +- src/lib/commands/forc.c | 7 +- src/lib/commands/upda.c | 70 +++----- src/lib/commands/zdon.c | 6 +- src/lib/common/hours.c | 44 ----- src/lib/common/rdsched.c | 320 +++++++++++++++++++++++++++++++++++++ src/lib/common/wantupd.c | 185 ++------------------- src/lib/gen/emp_config.c | 9 +- src/lib/global/constants.c | 9 +- src/lib/global/path.c.in | 3 + src/lib/player/empmod.c | 2 +- src/lib/update/main.c | 4 +- src/server/main.c | 2 +- src/server/update.c | 141 +++++++++------- 20 files changed, 483 insertions(+), 444 deletions(-) delete mode 100644 include/wantupd.h create mode 100644 src/lib/common/rdsched.c diff --git a/include/econfig-spec.h b/include/econfig-spec.h index bc86a5d5a..855e22567 100644 --- a/include/econfig-spec.h +++ b/include/econfig-spec.h @@ -101,46 +101,34 @@ EMPCFBOTH("WORLD_X", WORLD_X, int, NSC_INT, 0, EMPCFBOTH("WORLD_Y", WORLD_Y, int, NSC_INT, 0, "World size Y dimension") -EMPCF_COMMENT("\n\n### Update policy") -EMPCFBOTH("update_policy", update_policy, int, NSC_INT, 0, - "Update policy") -EMPCF_COMMENT("# 0 - Schedule updates according to etu_per_update, s_p_etu, adj_update\n" - "# 1 - Update at times specified by key \"update_times\"\n" - "# 2 - Blitz update every blitz_time minute\n" - "# 3 - No regular updates, only demand ones") +EMPCF_COMMENT("\n\n### Update policy\n\n" + "# Note: the update schedule is defined in the file schedule in the\n" + "# same directory as this file.") EMPCFBOTH("etu_per_update", etu_per_update, int, NSC_INT, 0, "Number of ETUs per update") EMPCFBOTH("s_p_etu", s_p_etu, int, NSC_INT, 0, "Seconds per ETU") -EMPCF_COMMENT("# updates under policy 0 occur every s_p_etu * etu_per_update seconds") -EMPCFBOTH("adj_update", adj_update, int, NSC_INT, KM_INTERNAL, - "Move the update forward or backward (in seconds)") +EMPCF_COMMENT("# FIXME get rid of this!\n" + "# Until then, set it so that s_p_etu * etu_per_update seconds equals\n" + "# the time between scheduled updates.") EMPCFBOTH("update_window", update_window, int, NSC_INT, 0, - "Window the update will occur in (in seconds) before and after the update time") -EMPCFBOTH("update_times", update_times, char *, NSC_STRING, 0, - "Times of day when updates may occur under policy 1, separated by space.") -EMPCF_COMMENT("# Give time of day as HOUR:MINUTE, e.g. 20:00\n" - "# Times must coincide with updates under policy 0.") -EMPCFBOTH("hourslop", hourslop, int, NSC_INT, KM_INTERNAL, - "Number of minutes update check can slip to match update_times") -EMPCFBOTH("blitz_time", blitz_time, int, NSC_INT, 0, - "Number of minutes between updates under policy 2.") + "Time window the update will occur in after the update time, in seconds") EMPCFBOTH("pre_update_hook", pre_update_hook, char *, NSC_STRING, KM_INTERNAL, "Shell command run right before the update.") -EMPCF_COMMENT("\n\n### Demand update policy") -EMPCFBOTH("update_demandpolicy", update_demandpolicy, int, NSC_INT, 0, +EMPCFBOTH("update_demand", update_demand, int, NSC_INT, 0, "Demand update policy") -EMPCF_COMMENT("# 0 - Votes tallied at update times under policy 0\n" - "# 1 - Votes tallies right after a vote\n" - "# 2 - Demand updates disabled") +EMPCF_COMMENT("# 0 - No demand updates\n" + "# 1 - Scheduled updates are demand updates\n" + "# 2 - Demand updates run right after the deciding vote is cast,\n" + "# in addition to (non-demand) scheduled updates\n") EMPCFBOTH("update_wantmin", update_wantmin, int, NSC_INT, 0, "Number of votes required for a demand update") EMPCFBOTH("update_missed", update_missed, int, NSC_INT, 0, "A country vetoes further demand updates after missing that many votes") EMPCFBOTH("update_demandtimes", update_demandtimes, char *, NSC_STRING, 0, - "Time of day ranges when demand updates can occur, separated by space.") -EMPCF_COMMENT("# Give range HOUR:MINUTE-HOUR:MINUTE, e.g. 20:00-24:00\n" + "Times when unscheduled demand updates can occur, separated by space.") +EMPCF_COMMENT("# Give time ranges as HOUR:MINUTE-HOUR:MINUTE, e.g. 20:00-24:00\n" "# Ranges CANNOT cross midnight.") EMPCF_COMMENT("\n\n### Game hours restrictions") @@ -149,7 +137,7 @@ EMPCFBOTH("game_days", game_days, char *, NSC_STRING, 0, EMPCF_COMMENT("# Give days as Su Mo Tu We Th Fr Sa.") EMPCFBOTH("game_hours", game_hours, char *, NSC_STRING, 0, "Time of day ranges when the game is open, separated by space.") -EMPCF_COMMENT("# Give range HOUR:MINUTE-HOUR:MINUTE, e.g. 20:00-24:00\n" +EMPCF_COMMENT("# Give time ranges as HOUR:MINUTE-HOUR:MINUTE, e.g. 20:00-24:00\n" "# Ranges CANNOT cross midnight.") EMPCF_COMMENT("\n\n### Options") diff --git a/include/optlist.h b/include/optlist.h index 4effa0e0b..d31568d97 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -45,6 +45,7 @@ extern char dflt_econfig[]; #undef EMP_CONFIG_H_OUTPUT extern char *configdir; +extern char *schedulefil; extern char motdfil[]; extern char downfil[]; @@ -60,6 +61,12 @@ enum { KM_OPTION = 4 /* historically an option */ }; +enum { + UPD_DEMAND_NONE, /* no demand updates */ + UPD_DEMAND_SCHED, /* scheduled updates are demand updates */ + UPD_DEMAND_ASYNC, /* zdone triggers unscheduled update */ +}; + struct keymatch { char *km_key; /* the key */ nsc_type km_type; /* type of associated data */ diff --git a/include/prototypes.h b/include/prototypes.h index 3829683a1..97e176a07 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -298,8 +298,6 @@ extern double hap_req(struct natstr *np); extern int is_wday_allowed(int, char *); extern int is_daytime_allowed(int, char *); extern int gamehours(time_t); -extern int is_daytime_near(int, char *, int); -extern int min_to_next_daytime(int, char *); /* land.c */ extern int has_units(coord, coord, natid, struct lndstr *); extern int has_units_with_mob(coord, coord, natid); @@ -332,6 +330,8 @@ extern char *BestLandPath(char *, struct sctstr *, struct sctstr *, extern char *BestShipPath(char *, int, int, int, int, int); extern char *BestAirPath(char *, int, int, int, int); extern double pathcost(struct sctstr *, char *, int); +/* rdsched.c */ +extern int read_schedule(char *, time_t[], int, time_t, time_t); /* res_pop.c */ extern int max_population(float, int, int); extern int max_pop(float, struct sctstr *); @@ -343,12 +343,9 @@ extern int sectdamage(struct sctstr *, int, struct emp_qelem *); /* type.c */ extern int sct_typematch(char *); /* wantupd.c */ -extern void update_policy_check(void); extern int demand_update_want(int *, int *, int); +extern int demand_check(void); extern int demandupdatecheck(void); -extern int updatetime(time_t *); -extern void next_update_time(time_t *, time_t *, time_t *); -extern void next_update_check_time(time_t *, time_t *, time_t *); extern int updates_disabled(void); /* xundump.c */ extern int xundump(FILE *, char *, int); diff --git a/include/server.h b/include/server.h index a0da6c1c9..9f9b16845 100644 --- a/include/server.h +++ b/include/server.h @@ -39,14 +39,15 @@ extern int shutdown_pending; extern int update_pending; extern empth_rwlock_t *update_lock; -extern time_t update_time; +extern time_t update_time[]; extern int updating_mob; void mobility_init(void); void market_init(void); void update_main(void); void update_init(void); -int update_trigger(time_t); +int update_trigger(void); +int update_reschedule(void); int shutdown_initiate(int); /* thread entry points */ diff --git a/include/wantupd.h b/include/wantupd.h deleted file mode 100644 index c483323e6..000000000 --- a/include/wantupd.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * wantupd.h: Header used to check to see if an update is wanted - * and/or allowed. - * - * Known contributors to this file: - * Doug Hay, 1991 - */ - -#ifndef WANTUPD_H -#define WANTUPD_H - -/* Update policies */ -#define UDP_NORMAL 0 -#define UDP_TIMES 1 -#define UDP_NOREG 3 -#define UDP_BLITZ 2 -#define UDP_MAX 3 -#define UDP_DEFAULT UDP_NORMAL - -/* Demand update policies */ -#define UDDEM_TMCHECK 0 -#define UDDEM_COMSET 1 -#define UDDEM_DISABLE 2 -#define UDDEM_MAX 2 -#define UDDEM_DEFAULT UDDEM_DISABLE - -#endif diff --git a/info/force.t b/info/force.t index 3be565aea..8b98689da 100644 --- a/info/force.t +++ b/info/force.t @@ -1,6 +1,6 @@ .TH Command FORCE .NA force "Force an update" .LV Expert -.SY "force " -Force an update in seconds. -.SA "enable, disable, shutdown, Deity" +.SY "force" +Force an update now. +.SA "enable, disable, reload, shutdown, Deity" diff --git a/man/emp_server.6 b/man/emp_server.6 index 1175ce59f..31f5730c4 100644 --- a/man/emp_server.6 +++ b/man/emp_server.6 @@ -81,7 +81,8 @@ deprecated. .B SIGHUP Request .B emp_server -to reopen log files. This allows proper log rotation. +to reopen log files and reload its configuration. This allows proper +log rotation. Only the update schedule is reloaded at this time. .TP .BR SIGINT ", " SIGTERM Request diff --git a/src/lib/commands/forc.c b/src/lib/commands/forc.c index 6bb4b5179..45ed523fe 100644 --- a/src/lib/commands/forc.c +++ b/src/lib/commands/forc.c @@ -49,12 +49,7 @@ force(void) pr("Updates are disabled\n"); return RET_FAIL; } - seconds = onearg(player->argp[1], "Time until update [in seconds]? "); - if (seconds < 0) - return RET_FAIL; - - pr("Scheduling update in %d second(s)\n", seconds); - if (update_trigger(seconds) < 0) + if (update_trigger() < 0) return RET_FAIL; return RET_OK; } diff --git a/src/lib/commands/upda.c b/src/lib/commands/upda.c index 433ef4e5f..d66a579cd 100644 --- a/src/lib/commands/upda.c +++ b/src/lib/commands/upda.c @@ -28,7 +28,7 @@ * upda.c: Give the time of the next update * * Known contributors to this file: - * + * Markus Armbruster, 2007 */ #include @@ -36,7 +36,6 @@ #include "commands.h" #include "optlist.h" #include "server.h" -#include "wantupd.h" /* * Tell what the update policy is, and when the next update @@ -47,7 +46,7 @@ upda(void) { FILE *fp; struct mob_acc_globals timestamps; - time_t now, next, delta; + time_t now, next, stop; if (opt_MOB_ACCESS) { if ((fp = fopen(timestampfil, "rb")) == NULL) @@ -70,66 +69,41 @@ upda(void) pr("UPDATES ARE DISABLED!\n"); (void)time(&now); - switch (update_policy) { - case UDP_NORMAL: - next_update_time(&now, &next, &delta); + next = update_time[0]; + if (next) { pr("\nUpdates occur at times specified by the ETU rates.\n\n"); pr("The next update is at %19.19s.\n", ctime(&next)); - break; - case UDP_TIMES: - next_update_time(&now, &next, &delta); - pr("\nUpdates occur at scheduled times.\n\n"); - pr("The next update is at %19.19s.\n", ctime(&next)); - break; - case UDP_BLITZ: - next_update_time(&now, &next, &delta); - pr("\nBlitz Updates occur every %d minutes. \n\n", blitz_time); - pr("The next update is at %19.19s.\n", ctime(&next)); - break; - case UDP_NOREG: + } else { pr("There are no regularly scheduled updates.\n"); - break; - default: - pr("Update policy is inconsistent.\n"); } pr("The current time is %19.19s.\n\n", ctime(&now)); - if (update_window) { - now = update_time - update_window; - next_update_time(&now, &next, &delta); + if (next && update_window) { pr("The next update window starts at %19.19s.\n", ctime(&next)); - next += update_window; - pr("The next update window stops at %19.19s.\n", ctime(&next)); + stop = next + update_window; + pr("The next update window stops at %19.19s.\n", ctime(&stop)); } - switch (update_demandpolicy) { - case UDDEM_TMCHECK: - next_update_check_time(&now, &next, &delta); + switch (update_demand) { + case UPD_DEMAND_NONE: + default: + break; + case UPD_DEMAND_SCHED: pr("Demand updates occur at update CHECK times.\n"); - pr("The next update check is at %19.19s.\n", - ctime(&next)); + if (next) { + pr("The next update check is at %19.19s.\n", + ctime(&next)); + } + pr("Demand updates require %d country(s) to want one.\n", + update_wantmin); break; - case UDDEM_COMSET: + case UPD_DEMAND_ASYNC: pr("Demand updates occur right after the demand is set.\n"); - break; - case UDDEM_DISABLE: - break; - default: - CANT_REACH(); - pr("Update demand policy is inconsistent.\n"); - } - - if ((update_policy == UDP_TIMES) || - (update_demandpolicy == UDDEM_TMCHECK)) { - if (*update_times != 0) - pr("The update schedule is: %s\n", update_times); - } - - if (update_demandpolicy != UDDEM_DISABLE) { - if (*update_demandtimes != 0) + if (*update_demandtimes != 0) { pr("Demand updates are allowed during: %s\n", update_demandtimes); + } pr("Demand updates require %d country(s) to want one.\n", update_wantmin); } diff --git a/src/lib/commands/zdon.c b/src/lib/commands/zdon.c index 8a03ef587..0eede00f7 100644 --- a/src/lib/commands/zdon.c +++ b/src/lib/commands/zdon.c @@ -54,7 +54,6 @@ #include "commands.h" #include "optlist.h" #include "server.h" -#include "wantupd.h" int zdon(void) @@ -70,7 +69,8 @@ zdon(void) int dowant; char buf[1024]; - if (update_demandpolicy == UDDEM_DISABLE) { + if (update_demand != UPD_DEMAND_SCHED + && update_demand != UPD_DEMAND_ASYNC) { pr("Demand updates are not enabled.\n"); return RET_FAIL; } @@ -136,7 +136,7 @@ zdon(void) if (!checking && wantupd && demandupdatecheck()) { pr("Here goes...\n"); - update_trigger(0); + update_trigger(); } return RET_OK; } diff --git a/src/lib/common/hours.c b/src/lib/common/hours.c index a9365bfcc..a9f09630a 100644 --- a/src/lib/common/hours.c +++ b/src/lib/common/hours.c @@ -101,50 +101,6 @@ gamehours(time_t t) return is_daytime_allowed(60 * tm->tm_hour + tm->tm_min, game_hours); } -/* - * Is day time DTIME (minutes since midnight) near a time in TIMES? - * TIMES is a list of day times. See daytime() for syntax. - * DTIME is near a listed time T if its within T and T + SLOP minutes. - */ -int -is_daytime_near(int dtime, char *times, int slop) -{ - int dt; - - if (times) - while (NULL != (times = daytime(times, &dt))) - if (dt <= dtime && dtime < dt + slop) - return 1; - - return 0; -} - -/* - * Return time in minutes between DTIME and next time in TIMES. - * If TIMES doesn't give a time, return -1. - * DTIME is day time in minutes since midnight. - * TIMES is a list of day times. See daytime() for syntax. - */ -int -min_to_next_daytime(int dtime, char *times) -{ - int mindt = INT_MAX; - int dt; - - if (times) { - while (NULL != (times = daytime(times, &dt))) { - if (dt <= dtime) - dt += 24 * 60; /* tomorrow */ - if (dt < mindt) - mindt = dt; - } - } - - if (mindt == INT_MAX) - return -1; - return mindt - dtime; -} - /* * Parse weekday name in STR. * On success assign day number (Sunday is 0) to *WDAY and return diff --git a/src/lib/common/rdsched.c b/src/lib/common/rdsched.c new file mode 100644 index 000000000..33be0822f --- /dev/null +++ b/src/lib/common/rdsched.c @@ -0,0 +1,320 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * rdsched.c: Read update schedule + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#define _XOPEN_SOURCE 500 + +#include + +#include +#include +#include +#include +#include +#include +#include "prototypes.h" + +static int parse_schedule_line(char *, time_t[], int, time_t, time_t *, + char *, int); +static int time_ok(time_t, char *, int); +static char *parse_time(time_t *, char *, time_t *); +static char *parse_every(time_t *, char *); +static char *parse_until(time_t *, char *, time_t *); +static char *parse_skip(time_t *, char *, time_t *); +static int insert_update(time_t, time_t[], int, time_t); +static int delete_update(time_t, time_t[], int); + +/* + * Read update schedule from file FNAME. + * Put the first N-1 updates after T0 into SCHED[] in ascending order, + * terminated with a zero. + * Use ANCHOR as initial anchor for anchor-relative times. + * Return 0 on success, -1 on failure. + */ +int +read_schedule(char *fname, time_t sched[], int n, time_t t0, time_t anchor) +{ + FILE *fp; + int lno = 0; + char buf[1024]; + char *endp; + + if (fname) { + fp = fopen(fname, "r"); + if (!fp) { + logerror("Can't open %s for reading (%s)\n", + fname, strerror(errno)); + return -1; + } + } else { + fp = stdin; + fname = ""; + } + + sched[0] = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + ++lno; + endp = strchr(buf, '#'); + if (endp) + *endp = 0; + if (parse_schedule_line(buf, sched, n, t0, &anchor, fname, lno)) + return -1; + } + + fclose(fp); + return 0; +} + +/* + * Parse an update schedule directive from LINE. + * Update SCHED[] and ANCHOR accordingly. + * SCHED[] holds the first N-1 updates after T0 in ascending order. + * FNAME and LNO file name and line number for reporting errors. + */ +static int +parse_schedule_line(char *line, time_t sched[], int n, + time_t t0, time_t *anchor, + char *fname, int lno) +{ + char *endp, *p; + int bol; + time_t t, delta, u; + + if ((endp = parse_time(&t, line, anchor))) { + if (!time_ok(t, fname, lno)) + return -1; + *anchor = t; + insert_update(t, sched, n, t0); + } else if ((endp = parse_every(&delta, line))) { + if ((p = parse_until(&u, endp, anchor))) { + endp = p; + if (!time_ok(u, fname, lno)) + return -1; + } else + u = (time_t)-1; + t = *anchor; + do { + t += delta; + } while ((u == (time_t)-1 || t <= u) + && insert_update(t, sched, n, t0) < n - 1); + } else if ((endp = parse_skip(&t, line, anchor))) { + if (!time_ok(t, fname, lno)) + return -1; + delete_update(t, sched, n); + } else + endp = line; + + bol = endp == line; + while (isspace(*endp)) endp++; + if (*endp) { + if (bol) + logerror("%s:%d: unintelligible\n", fname, lno); + else + logerror("%s:%d: trailing junk\n", fname, lno); + return -1; + } + + return 0; +} + +/* + * Complain and return zero when T is bad, else return non-zero. + * FNAME and LNO file name and line number. + */ +static int +time_ok(time_t t, char *fname, int lno) +{ + if (t == (time_t)-1) { + logerror("%s:%d: time weird\n", fname, lno); + return 0; + } + return 1; +} + +/* + * Parse a time from S into *T. + * *ANCHOR is the base for anchor-relative time. + * Return pointer to first character not parsed on success, + * null pointer on failure. + */ +static char * +parse_time(time_t *t, char *s, time_t *anchor) +{ + static char *fmt[] = { + "%Y-%m-%d %H:%M ", /* ISO 8601 */ + "%b %d %H:%M %Y ", /* like ctime(): Dec 22 15:35 2006 */ + "%d %b %Y %H:%M ", /* 22 Dec 2006 15:35 */ + "next %a %H:%M ", /* next Fri 15:35 */ + "next %a ", /* next Fri */ + NULL + }; + char *p, *endp; + int i; + struct tm tm, nexttm; + + for (p = s; isspace(*(unsigned char *)p); ++p) ; + + for (i = 0; ; i++) { + if (!fmt[i]) + return NULL; + memset(&tm, 0, sizeof(tm)); + tm.tm_hour = -1; + endp = strptime(p, fmt[i], &tm); + if (endp) + break; + } + + if (tm.tm_mday == 0) { + /* relative to anchor */ + nexttm = *localtime(anchor); + if (tm.tm_hour >= 0) { + /* got hour and minute */ + nexttm.tm_hour = tm.tm_hour; + nexttm.tm_min = tm.tm_min; + nexttm.tm_sec = 0; + } + nexttm.tm_mday += tm.tm_wday - nexttm.tm_wday; + if (tm.tm_wday <= nexttm.tm_wday) + nexttm.tm_mday += 7; + tm = nexttm; + } + + *t = mktime(&tm); + return endp; +} + +/* + * Parse an every clause from S into *SECS. + * Return pointer to first character not parsed on success, + * null pointer on failure. + */ +static char * +parse_every(time_t *secs, char *s) +{ + int nch, delta; + + nch = -1; + sscanf(s, " every %u hours%n", &delta, &nch); + if (nch >= 0) + delta *= 60; + else + sscanf(s, " every %u minutes%n", &delta, &nch); + if (nch < 0) + return NULL; *secs = 60 * delta; + return s + nch; +} + +/* + * Parse an until clause from S into *T. + * *ANCHOR is the base for anchor-relative time. + * Return pointer to first character not parsed on success, + * null pointer on failure. + */ +static char * +parse_until(time_t *t, char *s, time_t *anchor) +{ + int nch; + + nch = -1; + sscanf(s, " until%n", &nch); + if (nch < 0) + return NULL; + return parse_time(t, s + nch, anchor); +} + +/* + * Parse an skip clause from S into *T. + * *ANCHOR is the base for anchor-relative time. + * Return pointer to first character not parsed on success, + * null pointer on failure. + */ +static char * +parse_skip(time_t *t, char *s, time_t *anchor) +{ + int nch; + + nch = -1; + sscanf(s, " skip%n", &nch); + if (nch < 0) + return NULL; + return parse_time(t, s + nch, anchor); +} + +/* + * Return the index of the first update at or after T in SCHED[]. + */ +static int +find_update(time_t t, time_t sched[]) +{ + int i; + + /* Could use binary search here, but it's hardly worth it */ + for (i = 0; sched[i] && t > sched[i]; i++) ; + return i; +} + +/* + * Insert update at T into SCHED[]. + * SCHED[] holds the first N-1 updates after T0 in ascending order. + * If T is before T0 or outside game_days/game_hours, return -1. + * If there's no space for T in SCHED[], return N-1. + * Else insert T into SCHED[] and return its index in SCHED[]. + */ +static int +insert_update(time_t t, time_t sched[], int n, time_t t0) +{ + int i; + + if (t <= t0 || !gamehours(t)) + return -1; + + i = find_update(t, sched); + memmove(sched + i + 1, sched + i, (n - 1 - i) * sizeof(*sched)); + sched[i] = t; + sched[n - 1] = 0; + return i; +} + +/* + * Delete update T from SCHED[]. + * SCHED[] holds N-1 updates in ascending order. + * Return the index of the first update after T in SCHED[]. + */ +static int +delete_update(time_t t, time_t sched[], int n) +{ + int i = find_update(t, sched); + if (t == sched[i]) + memmove(sched + i, sched + i + 1, + (n - 1 - i) * sizeof(*sched)); + return i; +} diff --git a/src/lib/common/wantupd.c b/src/lib/common/wantupd.c index 400ae9c84..377f07dfc 100644 --- a/src/lib/common/wantupd.c +++ b/src/lib/common/wantupd.c @@ -47,26 +47,6 @@ #include "nat.h" #include "optlist.h" #include "prototypes.h" -#include "wantupd.h" - -void -update_policy_check(void) -{ - if (update_policy < 0) - update_policy = UDP_DEFAULT; - if (update_policy > UDP_MAX) - update_policy = UDP_DEFAULT; - if (update_demandpolicy < 0) - update_demandpolicy = UDDEM_DEFAULT; - if (update_demandpolicy > UDDEM_MAX) - update_demandpolicy = UDDEM_DEFAULT; - if (update_wantmin < 1) - update_wantmin = 1; - if (update_wantmin > MAXNOC) - update_wantmin = MAXNOC; - if (blitz_time < 1) - blitz_time = 1; -} static int demand_update_time(time_t *now) @@ -78,45 +58,6 @@ demand_update_time(time_t *now) update_demandtimes); } -/* When is the next regularly scheduled update from now. */ -static void -regular_update_time(time_t *now, time_t *tim, time_t *delta) -{ - time_t tw; - int secs_per_update; - - tw = *now + adj_update; - secs_per_update = etu_per_update * s_p_etu; - *delta = secs_per_update - (tw % secs_per_update); - *tim = *now + *delta; -} - -/* Is this a valid time for a scheduled update. */ -static int -scheduled_update_time(time_t *now) -{ - struct tm *tm; - - tm = localtime(now); - return is_daytime_near(60 * tm->tm_hour + tm->tm_min, - update_times, hourslop); -} - -static int -next_scheduled_time(time_t *now, time_t *tim, time_t *delta) -{ - struct tm *tm; - int d; - - tm = localtime(now); - d = min_to_next_daytime(60 * tm->tm_hour + tm->tm_min, update_times); - if (d < 0) - return 0; - *delta = 60 * d; - *tim = *now + *delta - tm->tm_sec; - return 1; -} - int demand_update_want(int *want, int *pop, int which) { @@ -144,12 +85,14 @@ demand_update_want(int *want, int *pop, int which) return whichwants; } -static int +/* + * Do we have sufficient votes for a demand update? + */ +int demand_check(void) { struct natstr *natp; int want, pop, cn, veto; - time_t now; demand_update_want(&want, &pop, 0); if (want < update_wantmin) { @@ -158,13 +101,6 @@ demand_check(void) return 0; } - time(&now); - if (!demand_update_time(&now)) { - logerror("no demand update, not within hours allowed."); - return 0; - } - - veto = 0; for (cn = 1; 0 != (natp = getnatp(cn)); cn++) { if (natp->nat_stat == STAT_ACTIVE) { @@ -183,118 +119,17 @@ demand_check(void) } /* - * Check if enough countries want an update, - * and if demand updates are allowed now. + * Can we have an unscheduled demand update now? */ int demandupdatecheck(void) { - if (UDDEM_COMSET != update_demandpolicy) { - logerror("no demand update, not policy."); - return 0; - } - - return demand_check(); -} - -/* - * Is it time for a regular or scheduled update? - * As well, if none of the above, check to see if - * a demand update can occur. - */ -int -updatetime(time_t *now) -{ - if (opt_BLITZ && update_policy == UDP_BLITZ) { - logerror("BLITZ Update."); - return 1; - } - - if (UDP_NORMAL == update_policy) { - logerror("Regular update, etu type."); - return 1; - } + time_t now = time(NULL); - if (UDP_TIMES == update_policy) { - if (scheduled_update_time(now)) { - logerror("Scheduled update."); - return 1; - } - } - if (UDDEM_DISABLE != update_demandpolicy) { - if (demand_check()) { - logerror("Demand update, at check time."); - return 1; - } - } - return 0; -} - -/* - * Return the time, and delta seconds, of the next update. - * If the policy is no regular updates, return the time of - * the next possible check. - */ -void -next_update_time(time_t *now, time_t *tim, time_t *delta) - /* From when */ - /* Time of next update */ - /* Seconds till next update */ -{ - time_t stim, sdelta; - - switch (update_policy) { - case UDP_NORMAL: - regular_update_time(now, tim, delta); - break; - case UDP_TIMES: - if (!next_scheduled_time(now, tim, delta)) - regular_update_time(now, tim, delta); - break; - case UDP_BLITZ: - *delta = (blitz_time * 60) - (*now % (blitz_time * 60)); - *tim = *now + *delta; - break; - case UDP_NOREG: - default: - regular_update_time(now, tim, delta); - if (next_scheduled_time(now, &stim, &sdelta)) { - if (*delta > sdelta) { - *delta = sdelta; - *tim = stim; - } - } - break; - } -} - -void -next_update_check_time(time_t *now, time_t *tim, time_t *delta) - /* From when */ - /* Time of next update */ - /* Seconds till next update check */ -{ - time_t stim, sdelta; - - switch (update_policy) { - case UDP_NORMAL: - regular_update_time(now, tim, delta); - break; - case UDP_BLITZ: - *delta = (blitz_time * 60) - (*now % (blitz_time * 60)); - *tim = *now + *delta; - break; - case UDP_TIMES: - case UDP_NOREG: - default: - regular_update_time(now, tim, delta); - if (next_scheduled_time(now, &stim, &sdelta)) { - if (*delta > sdelta) { - *delta = sdelta; - *tim = stim; - } - } - } + return update_demand == UPD_DEMAND_ASYNC + && !updates_disabled() + && demand_update_time(&now) + && demand_check(); } int diff --git a/src/lib/gen/emp_config.c b/src/lib/gen/emp_config.c index 1006dd98d..be2af2e93 100644 --- a/src/lib/gen/emp_config.c +++ b/src/lib/gen/emp_config.c @@ -70,7 +70,7 @@ struct keymatch configkeys[] = { }; static struct keymatch *keylookup(char *key, struct keymatch tbl[]); -static void set_dirs(char *); +static void set_paths(char *); /* * read in empire configuration @@ -154,7 +154,7 @@ emp_config(char *file) done: WORLD_X &= ~1; /* force even */ - set_dirs(file); + set_paths(file); return -errors; } @@ -175,7 +175,7 @@ keylookup(char *command, struct keymatch *tbl) } static void -set_dirs(char *econfig) +set_paths(char *econfig) { char *slash; char *cwd = getcwd(NULL, 0); @@ -204,6 +204,9 @@ set_dirs(char *econfig) } #endif /* !_WIN32 */ + schedulefil = malloc(strlen(configdir) + 10); + sprintf(schedulefil, "%s/schedule", configdir); + free(cwd); } diff --git a/src/lib/global/constants.c b/src/lib/global/constants.c index 5ffd430ca..7930dd85c 100644 --- a/src/lib/global/constants.c +++ b/src/lib/global/constants.c @@ -34,7 +34,7 @@ #include -#include "wantupd.h" +#include "optlist.h" /* Name of the deity */ char *privname = "Deity forgot to edit econfig"; @@ -56,15 +56,10 @@ int TRADE_DELAY = 7200; /* Seconds to bid on units */ int m_m_p_d = 1440; /* max mins of play per day (per country) */ int s_p_etu = 10; /* seconds per Empire Time Unit */ int etu_per_update = 60; /* # of etu's per update */ -int adj_update = 0; /* update time adjustment, in seconds */ int update_window = 0; /* update window adjustment, in seconds */ -int hourslop = 5; /* update_times matching fuzz, in minutes */ -char *update_times = ""; /* update times for policy UDP_TIMES */ -int update_policy = UDP_DEFAULT; /* update policy for regular updates */ -int update_demandpolicy = UDDEM_DEFAULT; /* update policy for demand updates */ +int update_demand = UPD_DEMAND_NONE; int update_missed = 999; /* demand updates missed before veto */ int update_wantmin = 1; /* votes required for demand update */ -int blitz_time = 10; /* number of minutes between blitz updates */ char *update_demandtimes = ""; /* demand update time ranges */ char *game_days = ""; /* days game is running */ char *game_hours = ""; /* hours game is running */ diff --git a/src/lib/global/path.c.in b/src/lib/global/path.c.in index 8a2a3da8e..fddee11bd 100644 --- a/src/lib/global/path.c.in +++ b/src/lib/global/path.c.in @@ -49,6 +49,9 @@ char *configdir; /* User configuration tables to load (relative to configdir) */ char *custom_tables = ""; +/* Update schedule (absolute file name) */ +char *schedulefil; + /* Where to find built-in configuration tables (relative to configdir) */ char *builtindir = "@builtindir@"; diff --git a/src/lib/player/empmod.c b/src/lib/player/empmod.c index b3edc5151..c5ce6cd66 100644 --- a/src/lib/player/empmod.c +++ b/src/lib/player/empmod.c @@ -111,7 +111,7 @@ struct cmndstr player_coms[] = { {"fly ", 2, fly, C_MOD, NORM + MONEY + CAP}, {"follow ", 1, foll, C_MOD, NORM + CAP}, - {"force ", 0, force, C_MOD, GOD}, + {"force", 0, force, C_MOD, GOD}, {"fortify ", 1, fort, C_MOD, NORM}, {"fuel []", 1, fuel, C_MOD, NORM}, diff --git a/src/lib/update/main.c b/src/lib/update/main.c index 5bd1bd4c2..790e59e73 100644 --- a/src/lib/update/main.c +++ b/src/lib/update/main.c @@ -41,7 +41,6 @@ #include "player.h" #include "server.h" #include "update.h" -#include "wantupd.h" long money[MAXNOC]; long pops[MAXNOC]; @@ -159,7 +158,8 @@ update_main(void) mob_plane(etu); mob_land(etu); } - if (update_demandpolicy != UDDEM_DISABLE) + if (update_demand == UPD_DEMAND_SCHED + || update_demand == UPD_DEMAND_ASYNC) update_removewants(); /* flush all mem file objects to disk */ ef_flush(EF_NATION); diff --git a/src/server/main.c b/src/server/main.c index 42023c012..2d3a4e66e 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -267,6 +267,7 @@ main(int argc, char **argv) #ifdef SIGHUP if (sig == SIGHUP) { journal_reopen(); + update_reschedule(); continue; } #endif @@ -290,7 +291,6 @@ init_server(void) #if defined(_WIN32) loc_NTInit(); #endif - update_policy_check(); player_init(); ef_init_srv(); io_init(); diff --git a/src/server/update.c b/src/server/update.c index fc08c941f..dce66cfc4 100644 --- a/src/server/update.c +++ b/src/server/update.c @@ -48,14 +48,18 @@ #include "prototypes.h" #include "server.h" -static empth_t *update_thread; +#define UPDATES 16 + empth_rwlock_t *update_lock; +static empth_t *update_thread; int update_pending; -time_t update_time; -static int update_forced; +time_t update_time[UPDATES]; +static time_t update_schedule_anchor; +static int update_wanted; + +static int update_get_schedule(void); static void update_sched(void *); -static void update_force(void *); static void update_run(void); static int run_hook(char *cmd, char *name); @@ -71,6 +75,10 @@ update_init(void) logerror("setting s_p_etu to %d", s_p_etu); } + update_schedule_anchor = (time(NULL) + 59) / 60 * 60; + if (update_get_schedule() < 0) + exit(1); + update_lock = empth_rwlock_create("Update"); if (!update_lock) exit_nomem(); @@ -88,91 +96,100 @@ update_init(void) exit_nomem(); } +/* + * Get the schedule for future updates into update_time[]. + * Return 0 on success, -1 on failure. + */ +static int +update_get_schedule(void) +{ + time_t now = time(NULL); + + if (read_schedule(schedulefil, update_time, UPDATES, + now + 30, update_schedule_anchor) < 0) { + logerror("No update schedule!"); + update_time[0] = 0; + return -1; + } + logerror("Update schedule read"); + return 0; +} + /*ARGSUSED*/ static void update_sched(void *unused) { - int wind; - time_t now, delta; + time_t next_update, now; player->proc = empth_self(); player->cnum = 0; player->god = 1; for (;;) { - time(&now); - next_update_time(&now, &update_time, &delta); - if (update_window > 0) { - wind = (random() % update_window); - update_time += wind; - delta += wind; + /* + * Sleep until the next scheduled update or an unscheduled + * wakeup. + */ + next_update = update_time[0]; + if (next_update) { + if (update_window > 0) + next_update += random() % update_window; + logerror("Next update at %s", ctime(&next_update)); + /* sleep until update is scheduled to go off */ + empth_sleep(next_update); + } else { + logerror("No update scheduled"); + /* want to sleep forever, but empthread doesn't provide that */ + while (empth_sleep(60 * 60 * 24) >= 0) ; } - logerror("Next update at %s", ctime(&update_time)); - logerror("Next update in %ld seconds", (long)delta); - /* sleep until update is scheduled to go off */ - update_forced = 0; - empth_sleep(update_time); - if (!update_forced) { - time(&now); - now += adj_update; - if (!gamehours(now)) { - logerror("No update permitted (hours restriction)"); - continue; - } - if (!updatetime(&now)) { - logerror("No update wanted"); - continue; - } - if (updates_disabled()) { + + now = time(NULL); + if (next_update != 0 && now >= next_update) { + /* scheduled update time reached */ + if (now >= next_update + 60) + logerror("Missed the update!"); + else if (update_demand == UPD_DEMAND_SCHED && !demand_check()) + ; + else if (updates_disabled()) logerror("Updates disabled...skipping update"); - continue; - } + else + update_wanted = 1; + update_schedule_anchor = update_time[0]; } - update_run(); + /* else unscheduled update if update_wanted is set */ + + if (update_wanted) { + update_wanted = 0; + update_run(); + } + + update_get_schedule(); } /*NOTREACHED*/ } /* - * Trigger an update SECS_FROM_NOW seconds from now. + * Trigger an update. * Return 0 on success, -1 on failure. */ int -update_trigger(time_t secs_from_now) +update_trigger(void) { - time_t *secp; - - if (secs_from_now < 0) - return -1; - - if (secs_from_now == 0) { - update_forced = 1; - empth_wakeup(update_thread); - return 0; - } - - /* FIXME make triggers overwrite, not accumulate */ - secp = malloc(sizeof(time_t)); - if (!secp) - return -1; - *secp = secs_from_now; - if (!empth_create(PP_SCHED, update_force, 50 * 1024, 0, - "forceUpdate", secp)) - return -1; + logerror("Triggering unscheduled update"); + update_wanted = 1; + empth_wakeup(update_thread); return 0; } -static void -update_force(void *seconds) +/* + * Reload the update schedule. + * Return 0 on success, -1 on failure. + */ +int +update_reschedule(void) { - time_t now; - - time(&now); - empth_sleep(now + *(time_t *)seconds); - update_forced = 1; empth_wakeup(update_thread); - free(seconds); - empth_exit(); + return 0; } static void -- 2.43.0