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:
parent
15f8f48353
commit
71320ed67f
20 changed files with 487 additions and 448 deletions
|
@ -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
|
||||
|
|
320
src/lib/common/rdsched.c
Normal file
320
src/lib/common/rdsched.c
Normal 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;
|
||||
}
|
|
@ -47,26 +47,6 @@
|
|||
#include "nat.h"
|
||||
#include "optlist.h"
|
||||
#include "prototypes.h"
|
||||
#include "wantupd.h"
|
||||
|
||||
void
|
||||
update_policy_check(void)
|
||||
{
|
||||
if (update_policy < 0)
|
||||
update_policy = UDP_DEFAULT;
|
||||
if (update_policy > UDP_MAX)
|
||||
update_policy = UDP_DEFAULT;
|
||||
if (update_demandpolicy < 0)
|
||||
update_demandpolicy = UDDEM_DEFAULT;
|
||||
if (update_demandpolicy > UDDEM_MAX)
|
||||
update_demandpolicy = UDDEM_DEFAULT;
|
||||
if (update_wantmin < 1)
|
||||
update_wantmin = 1;
|
||||
if (update_wantmin > MAXNOC)
|
||||
update_wantmin = MAXNOC;
|
||||
if (blitz_time < 1)
|
||||
blitz_time = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
demand_update_time(time_t *now)
|
||||
|
@ -78,45 +58,6 @@ demand_update_time(time_t *now)
|
|||
update_demandtimes);
|
||||
}
|
||||
|
||||
/* When is the next regularly scheduled update from now. */
|
||||
static void
|
||||
regular_update_time(time_t *now, time_t *tim, time_t *delta)
|
||||
{
|
||||
time_t tw;
|
||||
int secs_per_update;
|
||||
|
||||
tw = *now + adj_update;
|
||||
secs_per_update = etu_per_update * s_p_etu;
|
||||
*delta = secs_per_update - (tw % secs_per_update);
|
||||
*tim = *now + *delta;
|
||||
}
|
||||
|
||||
/* Is this a valid time for a scheduled update. */
|
||||
static int
|
||||
scheduled_update_time(time_t *now)
|
||||
{
|
||||
struct tm *tm;
|
||||
|
||||
tm = localtime(now);
|
||||
return is_daytime_near(60 * tm->tm_hour + tm->tm_min,
|
||||
update_times, hourslop);
|
||||
}
|
||||
|
||||
static int
|
||||
next_scheduled_time(time_t *now, time_t *tim, time_t *delta)
|
||||
{
|
||||
struct tm *tm;
|
||||
int d;
|
||||
|
||||
tm = localtime(now);
|
||||
d = min_to_next_daytime(60 * tm->tm_hour + tm->tm_min, update_times);
|
||||
if (d < 0)
|
||||
return 0;
|
||||
*delta = 60 * d;
|
||||
*tim = *now + *delta - tm->tm_sec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
demand_update_want(int *want, int *pop, int which)
|
||||
{
|
||||
|
@ -144,12 +85,14 @@ demand_update_want(int *want, int *pop, int which)
|
|||
return whichwants;
|
||||
}
|
||||
|
||||
static int
|
||||
/*
|
||||
* Do we have sufficient votes for a demand update?
|
||||
*/
|
||||
int
|
||||
demand_check(void)
|
||||
{
|
||||
struct natstr *natp;
|
||||
int want, pop, cn, veto;
|
||||
time_t now;
|
||||
|
||||
demand_update_want(&want, &pop, 0);
|
||||
if (want < update_wantmin) {
|
||||
|
@ -158,13 +101,6 @@ demand_check(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
if (!demand_update_time(&now)) {
|
||||
logerror("no demand update, not within hours allowed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
veto = 0;
|
||||
for (cn = 1; 0 != (natp = getnatp(cn)); cn++) {
|
||||
if (natp->nat_stat == STAT_ACTIVE) {
|
||||
|
@ -183,118 +119,17 @@ demand_check(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* Check if enough countries want an update,
|
||||
* and if demand updates are allowed now.
|
||||
* Can we have an unscheduled demand update now?
|
||||
*/
|
||||
int
|
||||
demandupdatecheck(void)
|
||||
{
|
||||
if (UDDEM_COMSET != update_demandpolicy) {
|
||||
logerror("no demand update, not policy.");
|
||||
return 0;
|
||||
}
|
||||
time_t now = time(NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue