]> git.pond.sub.org Git - empserver/commitdiff
New update scheduler:
authorMarkus Armbruster <armbru@pond.sub.org>
Wed, 11 Jul 2007 22:27:29 +0000 (22:27 +0000)
committerMarkus Armbruster <armbru@pond.sub.org>
Wed, 11 Jul 2007 22:27:29 +0000 (22:27 +0000)
(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.

20 files changed:
include/econfig-spec.h
include/optlist.h
include/prototypes.h
include/server.h
include/wantupd.h [deleted file]
info/force.t
man/emp_server.6
src/lib/commands/forc.c
src/lib/commands/upda.c
src/lib/commands/zdon.c
src/lib/common/hours.c
src/lib/common/rdsched.c [new file with mode: 0644]
src/lib/common/wantupd.c
src/lib/gen/emp_config.c
src/lib/global/constants.c
src/lib/global/path.c.in
src/lib/player/empmod.c
src/lib/update/main.c
src/server/main.c
src/server/update.c

index bc86a5d5aff0b4e232c34de1f0e43edcd0206bc6..855e22567b808823550dcefbaacc340caea5f120 100644 (file)
@@ -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")
index 4effa0e0b9d48ed4e264968e399b1cda588fe277..d31568d9794bb7853456c046bf75b24118d6f241 100644 (file)
@@ -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 */
index 3829683a1e6b6bdc4dca392066bfa6765b778454..97e176a070ba3dabe913dfced8d6ce254af17828 100644 (file)
@@ -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);
index a0da6c1c9d594dff29f2fcec042384564999cbac..9f9b168452a7e224a9302b47fb8c4b2d2ee99006 100644 (file)
 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 (file)
index c483323..0000000
+++ /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
index 3be565aea5028b06559d697d8f8001ff8ed94d2c..8b98689da29ba810c80e7c4aa27003e36f45bbe4 100644 (file)
@@ -1,6 +1,6 @@
 .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"
index 1175ce59ff880c33b817b7725389744ec67da7e2..31f5730c4282adce9a3ed3a0e8ad2cc2550a949a 100644 (file)
@@ -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
index 6bb4b51794bf0154951c7e2999d0a96015fe0995..45ed523fef01c35d64f1545ef51ff3fdde74dd2f 100644 (file)
@@ -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;
 }
index 433ef4e5f124bf57dbce2c3511fe5d3615bb7104..d66a579cd4353628a3b8d5cd88878488127ea61d 100644 (file)
@@ -28,7 +28,7 @@
  *  upda.c: Give the time of the next update
  * 
  *  Known contributors to this file:
- *  
+ *     Markus Armbruster, 2007
  */
 
 #include <config.h>
@@ -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);
     }
index 8a03ef587b361ab43b490101c6f4b1bcacabfad3..0eede00f77ab3ff03fc4747b7e5e2e8f214e59c6 100644 (file)
@@ -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;
 }
index a9365bfcc5180d3a75602c7d3ac58cde5e872905..a9f09630afed137307cc392937a9ad1a8862ee78 100644 (file)
@@ -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 (file)
index 0000000..33be082
--- /dev/null
@@ -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 <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;
+}
index 400ae9c84b8d0efacaf9a89cd2fe3f16af52b116..377f07dfc96803915a1e87ea93ed1420b18f512d 100644 (file)
 #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
index 1006dd98d4e22bec07a8f3711d7d4f7d67976c9a..be2af2e931a57f14fa684f69f53d5ebf0bf2026e 100644 (file)
@@ -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);
 }
 
index 5ffd430ca559adabf1929d83053a54e9d9ab421a..7930dd85cb35fddfdd9c0c627ffcb4fd5f694f4b 100644 (file)
@@ -34,7 +34,7 @@
 
 #include <config.h>
 
-#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 */
index 8a2a3da8ee775c3da793015c038182e2a09e8893..fddee11bdef4f3d4bd9bbc708ea683d694b8911e 100644 (file)
@@ -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@";
 
index b3edc51518f190ddb0135727535a9e1a3281221d..c5ce6cd66f41e8683e69439f8bc05ab4842ce733 100644 (file)
@@ -111,7 +111,7 @@ struct cmndstr player_coms[] = {
     {"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},
index 5bd1bd4c2b1ba8b08fceddbbddedcb2474e2b9e5..790e59e733f5af8baabbe1b1f194df409a51e9e8 100644 (file)
@@ -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);
index 42023c012fd3daed60d60fadceef192d6c696534..2d3a4e66ef76ea010f1ac1ae1f30c262e675d0b3 100644 (file)
@@ -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();
index fc08c941f7ab304c1f3d2084029f2609010d2e3b..dce66cfc4e6f726b902ba0050834d25649606a0d 100644 (file)
 #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