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.
This commit is contained in:
Markus Armbruster 2007-07-11 22:27:29 +00:00
parent 15f8f48353
commit 71320ed67f
20 changed files with 487 additions and 448 deletions

View file

@ -101,46 +101,34 @@ EMPCFBOTH("WORLD_X", WORLD_X, int, NSC_INT, 0,
EMPCFBOTH("WORLD_Y", WORLD_Y, int, NSC_INT, 0, EMPCFBOTH("WORLD_Y", WORLD_Y, int, NSC_INT, 0,
"World size Y dimension") "World size Y dimension")
EMPCF_COMMENT("\n\n### Update policy") EMPCF_COMMENT("\n\n### Update policy\n\n"
EMPCFBOTH("update_policy", update_policy, int, NSC_INT, 0, "# Note: the update schedule is defined in the file schedule in the\n"
"Update policy") "# same directory as this file.")
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")
EMPCFBOTH("etu_per_update", etu_per_update, int, NSC_INT, 0, EMPCFBOTH("etu_per_update", etu_per_update, int, NSC_INT, 0,
"Number of ETUs per update") "Number of ETUs per update")
EMPCFBOTH("s_p_etu", s_p_etu, int, NSC_INT, 0, EMPCFBOTH("s_p_etu", s_p_etu, int, NSC_INT, 0,
"Seconds per ETU") "Seconds per ETU")
EMPCF_COMMENT("# updates under policy 0 occur every s_p_etu * etu_per_update seconds") EMPCF_COMMENT("# FIXME get rid of this!\n"
EMPCFBOTH("adj_update", adj_update, int, NSC_INT, KM_INTERNAL, "# Until then, set it so that s_p_etu * etu_per_update seconds equals\n"
"Move the update forward or backward (in seconds)") "# the time between scheduled updates.")
EMPCFBOTH("update_window", update_window, int, NSC_INT, 0, EMPCFBOTH("update_window", update_window, int, NSC_INT, 0,
"Window the update will occur in (in seconds) before and after the update time") "Time window the update will occur in after the update time, in seconds")
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.")
EMPCFBOTH("pre_update_hook", pre_update_hook, char *, NSC_STRING, KM_INTERNAL, EMPCFBOTH("pre_update_hook", pre_update_hook, char *, NSC_STRING, KM_INTERNAL,
"Shell command run right before the update.") "Shell command run right before the update.")
EMPCF_COMMENT("\n\n### Demand update policy") EMPCFBOTH("update_demand", update_demand, int, NSC_INT, 0,
EMPCFBOTH("update_demandpolicy", update_demandpolicy, int, NSC_INT, 0,
"Demand update policy") "Demand update policy")
EMPCF_COMMENT("# 0 - Votes tallied at update times under policy 0\n" EMPCF_COMMENT("# 0 - No demand updates\n"
"# 1 - Votes tallies right after a vote\n" "# 1 - Scheduled updates are demand updates\n"
"# 2 - Demand updates disabled") "# 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, EMPCFBOTH("update_wantmin", update_wantmin, int, NSC_INT, 0,
"Number of votes required for a demand update") "Number of votes required for a demand update")
EMPCFBOTH("update_missed", update_missed, int, NSC_INT, 0, EMPCFBOTH("update_missed", update_missed, int, NSC_INT, 0,
"A country vetoes further demand updates after missing that many votes") "A country vetoes further demand updates after missing that many votes")
EMPCFBOTH("update_demandtimes", update_demandtimes, char *, NSC_STRING, 0, EMPCFBOTH("update_demandtimes", update_demandtimes, char *, NSC_STRING, 0,
"Time of day ranges when demand updates can occur, separated by space.") "Times when unscheduled demand updates can occur, 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.") "# Ranges CANNOT cross midnight.")
EMPCF_COMMENT("\n\n### Game hours restrictions") 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.") EMPCF_COMMENT("# Give days as Su Mo Tu We Th Fr Sa.")
EMPCFBOTH("game_hours", game_hours, char *, NSC_STRING, 0, EMPCFBOTH("game_hours", game_hours, char *, NSC_STRING, 0,
"Time of day ranges when the game is open, separated by space.") "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.") "# Ranges CANNOT cross midnight.")
EMPCF_COMMENT("\n\n### Options") EMPCF_COMMENT("\n\n### Options")

View file

@ -45,6 +45,7 @@ extern char dflt_econfig[];
#undef EMP_CONFIG_H_OUTPUT #undef EMP_CONFIG_H_OUTPUT
extern char *configdir; extern char *configdir;
extern char *schedulefil;
extern char motdfil[]; extern char motdfil[];
extern char downfil[]; extern char downfil[];
@ -60,6 +61,12 @@ enum {
KM_OPTION = 4 /* historically an option */ 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 { struct keymatch {
char *km_key; /* the key */ char *km_key; /* the key */
nsc_type km_type; /* type of associated data */ nsc_type km_type; /* type of associated data */

View file

@ -298,8 +298,6 @@ extern double hap_req(struct natstr *np);
extern int is_wday_allowed(int, char *); extern int is_wday_allowed(int, char *);
extern int is_daytime_allowed(int, char *); extern int is_daytime_allowed(int, char *);
extern int gamehours(time_t); extern int gamehours(time_t);
extern int is_daytime_near(int, char *, int);
extern int min_to_next_daytime(int, char *);
/* land.c */ /* land.c */
extern int has_units(coord, coord, natid, struct lndstr *); extern int has_units(coord, coord, natid, struct lndstr *);
extern int has_units_with_mob(coord, coord, natid); 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 *BestShipPath(char *, int, int, int, int, int);
extern char *BestAirPath(char *, int, int, int, int); extern char *BestAirPath(char *, int, int, int, int);
extern double pathcost(struct sctstr *, char *, 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 */ /* res_pop.c */
extern int max_population(float, int, int); extern int max_population(float, int, int);
extern int max_pop(float, struct sctstr *); extern int max_pop(float, struct sctstr *);
@ -343,12 +343,9 @@ extern int sectdamage(struct sctstr *, int, struct emp_qelem *);
/* type.c */ /* type.c */
extern int sct_typematch(char *); extern int sct_typematch(char *);
/* wantupd.c */ /* wantupd.c */
extern void update_policy_check(void);
extern int demand_update_want(int *, int *, int); extern int demand_update_want(int *, int *, int);
extern int demand_check(void);
extern int demandupdatecheck(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); extern int updates_disabled(void);
/* xundump.c */ /* xundump.c */
extern int xundump(FILE *, char *, int); extern int xundump(FILE *, char *, int);

View file

@ -39,14 +39,15 @@
extern int shutdown_pending; extern int shutdown_pending;
extern int update_pending; extern int update_pending;
extern empth_rwlock_t *update_lock; extern empth_rwlock_t *update_lock;
extern time_t update_time; extern time_t update_time[];
extern int updating_mob; extern int updating_mob;
void mobility_init(void); void mobility_init(void);
void market_init(void); void market_init(void);
void update_main(void); void update_main(void);
void update_init(void); void update_init(void);
int update_trigger(time_t); int update_trigger(void);
int update_reschedule(void);
int shutdown_initiate(int); int shutdown_initiate(int);
/* thread entry points */ /* thread entry points */

View file

@ -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

View file

@ -1,6 +1,6 @@
.TH Command FORCE .TH Command FORCE
.NA force "Force an update" .NA force "Force an update"
.LV Expert .LV Expert
.SY "force <seconds>" .SY "force"
Force an update in <seconds> seconds. Force an update now.
.SA "enable, disable, shutdown, Deity" .SA "enable, disable, reload, shutdown, Deity"

View file

@ -81,7 +81,8 @@ deprecated.
.B SIGHUP .B SIGHUP
Request Request
.B emp_server .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 .TP
.BR SIGINT ", " SIGTERM .BR SIGINT ", " SIGTERM
Request Request

View file

@ -49,12 +49,7 @@ force(void)
pr("Updates are disabled\n"); pr("Updates are disabled\n");
return RET_FAIL; return RET_FAIL;
} }
seconds = onearg(player->argp[1], "Time until update [in seconds]? "); if (update_trigger() < 0)
if (seconds < 0)
return RET_FAIL;
pr("Scheduling update in %d second(s)\n", seconds);
if (update_trigger(seconds) < 0)
return RET_FAIL; return RET_FAIL;
return RET_OK; return RET_OK;
} }

View file

@ -28,7 +28,7 @@
* upda.c: Give the time of the next update * upda.c: Give the time of the next update
* *
* Known contributors to this file: * Known contributors to this file:
* * Markus Armbruster, 2007
*/ */
#include <config.h> #include <config.h>
@ -36,7 +36,6 @@
#include "commands.h" #include "commands.h"
#include "optlist.h" #include "optlist.h"
#include "server.h" #include "server.h"
#include "wantupd.h"
/* /*
* Tell what the update policy is, and when the next update * Tell what the update policy is, and when the next update
@ -47,7 +46,7 @@ upda(void)
{ {
FILE *fp; FILE *fp;
struct mob_acc_globals timestamps; struct mob_acc_globals timestamps;
time_t now, next, delta; time_t now, next, stop;
if (opt_MOB_ACCESS) { if (opt_MOB_ACCESS) {
if ((fp = fopen(timestampfil, "rb")) == NULL) if ((fp = fopen(timestampfil, "rb")) == NULL)
@ -70,66 +69,41 @@ upda(void)
pr("UPDATES ARE DISABLED!\n"); pr("UPDATES ARE DISABLED!\n");
(void)time(&now); (void)time(&now);
switch (update_policy) { next = update_time[0];
case UDP_NORMAL: if (next) {
next_update_time(&now, &next, &delta);
pr("\nUpdates occur at times specified by the ETU rates.\n\n"); pr("\nUpdates occur at times specified by the ETU rates.\n\n");
pr("The next update is at %19.19s.\n", ctime(&next)); pr("The next update is at %19.19s.\n", ctime(&next));
break; } else {
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:
pr("There are no regularly scheduled updates.\n"); 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)); pr("The current time is %19.19s.\n\n", ctime(&now));
if (update_window) { if (next && update_window) {
now = update_time - update_window;
next_update_time(&now, &next, &delta);
pr("The next update window starts at %19.19s.\n", pr("The next update window starts at %19.19s.\n",
ctime(&next)); ctime(&next));
next += update_window; stop = next + update_window;
pr("The next update window stops at %19.19s.\n", ctime(&next)); pr("The next update window stops at %19.19s.\n", ctime(&stop));
} }
switch (update_demandpolicy) { switch (update_demand) {
case UDDEM_TMCHECK: case UPD_DEMAND_NONE:
next_update_check_time(&now, &next, &delta); default:
break;
case UPD_DEMAND_SCHED:
pr("Demand updates occur at update CHECK times.\n"); pr("Demand updates occur at update CHECK times.\n");
if (next) {
pr("The next update check is at %19.19s.\n", pr("The next update check is at %19.19s.\n",
ctime(&next)); ctime(&next));
}
pr("Demand updates require %d country(s) to want one.\n",
update_wantmin);
break; break;
case UDDEM_COMSET: case UPD_DEMAND_ASYNC:
pr("Demand updates occur right after the demand is set.\n"); pr("Demand updates occur right after the demand is set.\n");
break; if (*update_demandtimes != 0) {
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)
pr("Demand updates are allowed during: %s\n", pr("Demand updates are allowed during: %s\n",
update_demandtimes); update_demandtimes);
}
pr("Demand updates require %d country(s) to want one.\n", pr("Demand updates require %d country(s) to want one.\n",
update_wantmin); update_wantmin);
} }

View file

@ -54,7 +54,6 @@
#include "commands.h" #include "commands.h"
#include "optlist.h" #include "optlist.h"
#include "server.h" #include "server.h"
#include "wantupd.h"
int int
zdon(void) zdon(void)
@ -70,7 +69,8 @@ zdon(void)
int dowant; int dowant;
char buf[1024]; 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"); pr("Demand updates are not enabled.\n");
return RET_FAIL; return RET_FAIL;
} }
@ -136,7 +136,7 @@ zdon(void)
if (!checking && wantupd && demandupdatecheck()) { if (!checking && wantupd && demandupdatecheck()) {
pr("Here goes...\n"); pr("Here goes...\n");
update_trigger(0); update_trigger();
} }
return RET_OK; return RET_OK;
} }

View file

@ -101,50 +101,6 @@ gamehours(time_t t)
return is_daytime_allowed(60 * tm->tm_hour + tm->tm_min, game_hours); 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. * Parse weekday name in STR.
* On success assign day number (Sunday is 0) to *WDAY and return * On success assign day number (Sunday is 0) to *WDAY and return

320
src/lib/common/rdsched.c Normal file
View file

@ -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;
}

View file

@ -47,26 +47,6 @@
#include "nat.h" #include "nat.h"
#include "optlist.h" #include "optlist.h"
#include "prototypes.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 static int
demand_update_time(time_t *now) demand_update_time(time_t *now)
@ -78,45 +58,6 @@ demand_update_time(time_t *now)
update_demandtimes); 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 int
demand_update_want(int *want, int *pop, int which) demand_update_want(int *want, int *pop, int which)
{ {
@ -144,12 +85,14 @@ demand_update_want(int *want, int *pop, int which)
return whichwants; return whichwants;
} }
static int /*
* Do we have sufficient votes for a demand update?
*/
int
demand_check(void) demand_check(void)
{ {
struct natstr *natp; struct natstr *natp;
int want, pop, cn, veto; int want, pop, cn, veto;
time_t now;
demand_update_want(&want, &pop, 0); demand_update_want(&want, &pop, 0);
if (want < update_wantmin) { if (want < update_wantmin) {
@ -158,13 +101,6 @@ demand_check(void)
return 0; return 0;
} }
time(&now);
if (!demand_update_time(&now)) {
logerror("no demand update, not within hours allowed.");
return 0;
}
veto = 0; veto = 0;
for (cn = 1; 0 != (natp = getnatp(cn)); cn++) { for (cn = 1; 0 != (natp = getnatp(cn)); cn++) {
if (natp->nat_stat == STAT_ACTIVE) { if (natp->nat_stat == STAT_ACTIVE) {
@ -183,118 +119,17 @@ demand_check(void)
} }
/* /*
* Check if enough countries want an update, * Can we have an unscheduled demand update now?
* and if demand updates are allowed now.
*/ */
int int
demandupdatecheck(void) demandupdatecheck(void)
{ {
if (UDDEM_COMSET != update_demandpolicy) { time_t now = time(NULL);
logerror("no demand update, not policy.");
return 0;
}
return demand_check(); return update_demand == UPD_DEMAND_ASYNC
} && !updates_disabled()
&& demand_update_time(&now)
/* && 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;
}
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;
}
}
}
} }
int int

View file

@ -70,7 +70,7 @@ struct keymatch configkeys[] = {
}; };
static struct keymatch *keylookup(char *key, struct keymatch tbl[]); static struct keymatch *keylookup(char *key, struct keymatch tbl[]);
static void set_dirs(char *); static void set_paths(char *);
/* /*
* read in empire configuration * read in empire configuration
@ -154,7 +154,7 @@ emp_config(char *file)
done: done:
WORLD_X &= ~1; /* force even */ WORLD_X &= ~1; /* force even */
set_dirs(file); set_paths(file);
return -errors; return -errors;
} }
@ -175,7 +175,7 @@ keylookup(char *command, struct keymatch *tbl)
} }
static void static void
set_dirs(char *econfig) set_paths(char *econfig)
{ {
char *slash; char *slash;
char *cwd = getcwd(NULL, 0); char *cwd = getcwd(NULL, 0);
@ -204,6 +204,9 @@ set_dirs(char *econfig)
} }
#endif /* !_WIN32 */ #endif /* !_WIN32 */
schedulefil = malloc(strlen(configdir) + 10);
sprintf(schedulefil, "%s/schedule", configdir);
free(cwd); free(cwd);
} }

View file

@ -34,7 +34,7 @@
#include <config.h> #include <config.h>
#include "wantupd.h" #include "optlist.h"
/* Name of the deity */ /* Name of the deity */
char *privname = "Deity forgot to edit econfig"; 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 m_m_p_d = 1440; /* max mins of play per day (per country) */
int s_p_etu = 10; /* seconds per Empire Time Unit */ int s_p_etu = 10; /* seconds per Empire Time Unit */
int etu_per_update = 60; /* # of etu's per update */ 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 update_window = 0; /* update window adjustment, in seconds */
int hourslop = 5; /* update_times matching fuzz, in minutes */ int update_demand = UPD_DEMAND_NONE;
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_missed = 999; /* demand updates missed before veto */ int update_missed = 999; /* demand updates missed before veto */
int update_wantmin = 1; /* votes required for demand update */ 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 *update_demandtimes = ""; /* demand update time ranges */
char *game_days = ""; /* days game is running */ char *game_days = ""; /* days game is running */
char *game_hours = ""; /* hours game is running */ char *game_hours = ""; /* hours game is running */

View file

@ -49,6 +49,9 @@ char *configdir;
/* User configuration tables to load (relative to configdir) */ /* User configuration tables to load (relative to configdir) */
char *custom_tables = ""; char *custom_tables = "";
/* Update schedule (absolute file name) */
char *schedulefil;
/* Where to find built-in configuration tables (relative to configdir) */ /* Where to find built-in configuration tables (relative to configdir) */
char *builtindir = "@builtindir@"; char *builtindir = "@builtindir@";

View file

@ -111,7 +111,7 @@ struct cmndstr player_coms[] = {
{"fly <cargo-PLANES> <fighter-PLANES> <ap-SECT> <PATH|DESTINATION> <COM>", {"fly <cargo-PLANES> <fighter-PLANES> <ap-SECT> <PATH|DESTINATION> <COM>",
2, fly, C_MOD, NORM + MONEY + CAP}, 2, fly, C_MOD, NORM + MONEY + CAP},
{"follow <leader> <SHIPS>", 1, foll, C_MOD, NORM + 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}, {"fortify <UNITS> <MOB>", 1, fort, C_MOD, NORM},
{"fuel <s|l> <SHIP/FLEET | UNIT/ARMY> <AMOUNT> [<OILER>]", {"fuel <s|l> <SHIP/FLEET | UNIT/ARMY> <AMOUNT> [<OILER>]",
1, fuel, C_MOD, NORM}, 1, fuel, C_MOD, NORM},

View file

@ -41,7 +41,6 @@
#include "player.h" #include "player.h"
#include "server.h" #include "server.h"
#include "update.h" #include "update.h"
#include "wantupd.h"
long money[MAXNOC]; long money[MAXNOC];
long pops[MAXNOC]; long pops[MAXNOC];
@ -159,7 +158,8 @@ update_main(void)
mob_plane(etu); mob_plane(etu);
mob_land(etu); mob_land(etu);
} }
if (update_demandpolicy != UDDEM_DISABLE) if (update_demand == UPD_DEMAND_SCHED
|| update_demand == UPD_DEMAND_ASYNC)
update_removewants(); update_removewants();
/* flush all mem file objects to disk */ /* flush all mem file objects to disk */
ef_flush(EF_NATION); ef_flush(EF_NATION);

View file

@ -267,6 +267,7 @@ main(int argc, char **argv)
#ifdef SIGHUP #ifdef SIGHUP
if (sig == SIGHUP) { if (sig == SIGHUP) {
journal_reopen(); journal_reopen();
update_reschedule();
continue; continue;
} }
#endif #endif
@ -290,7 +291,6 @@ init_server(void)
#if defined(_WIN32) #if defined(_WIN32)
loc_NTInit(); loc_NTInit();
#endif #endif
update_policy_check();
player_init(); player_init();
ef_init_srv(); ef_init_srv();
io_init(); io_init();

View file

@ -48,14 +48,18 @@
#include "prototypes.h" #include "prototypes.h"
#include "server.h" #include "server.h"
static empth_t *update_thread; #define UPDATES 16
empth_rwlock_t *update_lock;
int update_pending;
time_t update_time;
static int update_forced;
empth_rwlock_t *update_lock;
static empth_t *update_thread;
int update_pending;
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_sched(void *);
static void update_force(void *);
static void update_run(void); static void update_run(void);
static int run_hook(char *cmd, char *name); 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); 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"); update_lock = empth_rwlock_create("Update");
if (!update_lock) if (!update_lock)
exit_nomem(); exit_nomem();
@ -88,91 +96,100 @@ update_init(void)
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*/ /*ARGSUSED*/
static void static void
update_sched(void *unused) update_sched(void *unused)
{ {
int wind; time_t next_update, now;
time_t now, delta;
player->proc = empth_self(); player->proc = empth_self();
player->cnum = 0; player->cnum = 0;
player->god = 1; player->god = 1;
for (;;) { for (;;) {
time(&now); /*
next_update_time(&now, &update_time, &delta); * Sleep until the next scheduled update or an unscheduled
if (update_window > 0) { * wakeup.
wind = (random() % update_window); */
update_time += wind; next_update = update_time[0];
delta += wind; if (next_update) {
} if (update_window > 0)
logerror("Next update at %s", ctime(&update_time)); next_update += random() % update_window;
logerror("Next update in %ld seconds", (long)delta); logerror("Next update at %s", ctime(&next_update));
/* sleep until update is scheduled to go off */ /* sleep until update is scheduled to go off */
update_forced = 0; empth_sleep(next_update);
empth_sleep(update_time); } else {
if (!update_forced) { logerror("No update scheduled");
time(&now); /* want to sleep forever, but empthread doesn't provide that */
now += adj_update; while (empth_sleep(60 * 60 * 24) >= 0) ;
if (!gamehours(now)) {
logerror("No update permitted (hours restriction)");
continue;
} }
if (!updatetime(&now)) {
logerror("No update wanted"); now = time(NULL);
continue; if (next_update != 0 && now >= next_update) {
} /* scheduled update time reached */
if (updates_disabled()) { 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"); logerror("Updates disabled...skipping update");
continue; else
} update_wanted = 1;
update_schedule_anchor = update_time[0];
} }
/* else unscheduled update if update_wanted is set */
if (update_wanted) {
update_wanted = 0;
update_run(); update_run();
} }
update_get_schedule();
}
/*NOTREACHED*/ /*NOTREACHED*/
} }
/* /*
* Trigger an update SECS_FROM_NOW seconds from now. * Trigger an update.
* Return 0 on success, -1 on failure. * Return 0 on success, -1 on failure.
*/ */
int int
update_trigger(time_t secs_from_now) update_trigger(void)
{ {
time_t *secp; logerror("Triggering unscheduled update");
update_wanted = 1;
if (secs_from_now < 0)
return -1;
if (secs_from_now == 0) {
update_forced = 1;
empth_wakeup(update_thread); empth_wakeup(update_thread);
return 0; return 0;
} }
/* FIXME make triggers overwrite, not accumulate */ /*
secp = malloc(sizeof(time_t)); * Reload the update schedule.
if (!secp) * Return 0 on success, -1 on failure.
return -1; */
*secp = secs_from_now; int
if (!empth_create(PP_SCHED, update_force, 50 * 1024, 0, update_reschedule(void)
"forceUpdate", secp))
return -1;
return 0;
}
static void
update_force(void *seconds)
{ {
time_t now;
time(&now);
empth_sleep(now + *(time_t *)seconds);
update_forced = 1;
empth_wakeup(update_thread); empth_wakeup(update_thread);
free(seconds); return 0;
empth_exit();
} }
static void static void