(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.
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")
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")
#undef EMP_CONFIG_H_OUTPUT
extern char *configdir;
+extern char *schedulefil;
extern char motdfil[];
extern char downfil[];
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 */
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);
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 *);
/* 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);
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 */
+++ /dev/null
-/*
- * 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
.TH Command FORCE
.NA force "Force an update"
.LV Expert
-.SY "force <seconds>"
-Force an update in <seconds> seconds.
-.SA "enable, disable, shutdown, Deity"
+.SY "force"
+Force an update now.
+.SA "enable, disable, reload, shutdown, Deity"
.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
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;
}
* upda.c: Give the time of the next update
*
* Known contributors to this file:
- *
+ * Markus Armbruster, 2007
*/
#include <config.h>
#include "commands.h"
#include "optlist.h"
#include "server.h"
-#include "wantupd.h"
/*
* Tell what the update policy is, and when the next update
{
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)
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);
}
#include "commands.h"
#include "optlist.h"
#include "server.h"
-#include "wantupd.h"
int
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;
}
if (!checking && wantupd && demandupdatecheck()) {
pr("Here goes...\n");
- update_trigger(0);
+ update_trigger();
}
return RET_OK;
}
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
--- /dev/null
+/*
+ * 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 <config.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#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 = "<stdin>";
+ }
+
+ 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;
+}
#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)
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)
{
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) {
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) {
}
/*
- * 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
};
static struct keymatch *keylookup(char *key, struct keymatch tbl[]);
-static void set_dirs(char *);
+static void set_paths(char *);
/*
* read in empire configuration
done:
WORLD_X &= ~1; /* force even */
- set_dirs(file);
+ set_paths(file);
return -errors;
}
}
static void
-set_dirs(char *econfig)
+set_paths(char *econfig)
{
char *slash;
char *cwd = getcwd(NULL, 0);
}
#endif /* !_WIN32 */
+ schedulefil = malloc(strlen(configdir) + 10);
+ sprintf(schedulefil, "%s/schedule", configdir);
+
free(cwd);
}
#include <config.h>
-#include "wantupd.h"
+#include "optlist.h"
/* Name of the deity */
char *privname = "Deity forgot to edit econfig";
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 */
/* 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@";
{"fly <cargo-PLANES> <fighter-PLANES> <ap-SECT> <PATH|DESTINATION> <COM>",
2, fly, C_MOD, NORM + MONEY + CAP},
{"follow <leader> <SHIPS>", 1, foll, C_MOD, NORM + CAP},
- {"force <delay time>", 0, force, C_MOD, GOD},
+ {"force", 0, force, C_MOD, GOD},
{"fortify <UNITS> <MOB>", 1, fort, C_MOD, NORM},
{"fuel <s|l> <SHIP/FLEET | UNIT/ARMY> <AMOUNT> [<OILER>]",
1, fuel, C_MOD, NORM},
#include "player.h"
#include "server.h"
#include "update.h"
-#include "wantupd.h"
long money[MAXNOC];
long pops[MAXNOC];
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);
#ifdef SIGHUP
if (sig == SIGHUP) {
journal_reopen();
+ update_reschedule();
continue;
}
#endif
#if defined(_WIN32)
loc_NTInit();
#endif
- update_policy_check();
player_init();
ef_init_srv();
io_init();
#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);
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();
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