/* initial part must match struct empobj */
short ef_type;
short com_uid;
+ unsigned com_seqno;
time_t com_timestamp;
natid com_owner;
/* end of part matching struct empobj */
*/
short ef_type;
short uid;
+ unsigned seqno;
time_t timestamp;
/* end of part matching struct emptypedstr */
natid own; /* valid if EFF_OWNER is in table's flags */
struct emptypedstr {
short ef_type;
short uid;
+ unsigned seqno;
time_t timestamp;
};
extern int ef_flush(int);
extern void ef_blank(int, int, void *);
extern int ef_write(int, int, void *);
+extern void ef_set_uid(int, void *, int);
extern int ef_extend(int, int);
extern int ef_ensure_space(int, int, int);
extern int ef_truncate(int, int);
/* initial part must match struct empobj */
short ef_type;
short game_uid;
+ unsigned game_seqno;
time_t game_timestamp;
/* end of part matching struct empobj */
char game_upd_disable; /* updates disabled? */
/* initial part must match struct empobj */
short ef_type;
short lnd_uid; /* unit id (land unit) */
+ unsigned lnd_seqno;
time_t lnd_timestamp; /* Last time this unit was touched */
natid lnd_own; /* owner's country num */
coord lnd_x; /* x location in abs coords */
/* initial part must match struct empobj */
short ef_type;
short l_uid;
+ unsigned l_seqno;
time_t l_timestamp;
/* end of part matching struct empobj */
natid l_loner; /* loan shark */
/* initial part must match struct empobj */
short ef_type;
int lost_uid;
+ unsigned lost_seqno;
time_t lost_timestamp; /* When it was lost */
natid lost_owner; /* Who lost it */
/* end of part matching struct empobj */
/* initial part must match struct empobj */
short ef_type;
short r_uid; /* realm table index */
+ unsigned r_seqno;
time_t r_timestamp; /* Last time this realm was touched */
natid r_cnum; /* country number */
/* end of part matching struct empobj */
/* initial part must match struct empobj */
short ef_type;
short nat_uid; /* equals nat_cnum */
+ unsigned nat_seqno;
time_t nat_timestamp;
natid nat_cnum; /* our country number */
/* end of part matching struct empobj */
/* initial part must match struct empobj */
short ef_type;
short nws_uid;
+ unsigned nws_seqno;
time_t nws_timestamp;
/* end of part matching struct empobj */
natid nws_ano; /* "actor" country # */
/* initial part must match struct empobj */
short ef_type;
short nuk_uid;
+ unsigned nuk_seqno;
time_t nuk_timestamp; /* Last time this nuke was touched */
natid nuk_own;
coord nuk_x, nuk_y; /* current loc of device */
/* initial part must match struct empobj */
short ef_type;
short pln_uid; /* plane unit id */
+ unsigned pln_seqno;
time_t pln_timestamp; /* Last time this plane was touched */
natid pln_own; /* owning country */
coord pln_x; /* plane x-y */
/* initial part must match struct empobj */
short ef_type;
short sct_uid; /* equals XYOFFSET(sct_x, sct_y) */
+ unsigned sct_seqno;
time_t sct_timestamp; /* Last time this sector was written to */
natid sct_own; /* owner's country num */
coord sct_x; /* x coord of sector */
/* initial part must match struct empobj */
short ef_type;
short shp_uid; /* unit id (ship #) */
+ unsigned shp_seqno;
time_t shp_timestamp; /* Last time this ship was touched. */
natid shp_own; /* owner's country num */
coord shp_x; /* x location in abs coords */
/* initial part must match struct empobj */
short ef_type;
short trd_uid;
+ unsigned trd_seqno;
time_t trd_timestamp;
natid trd_owner;
/* end of part matching struct empobj */
/* initial part must match struct empobj */
short ef_type;
short trt_uid;
+ unsigned trt_seqno;
time_t trt_timestamp;
/* end of part matching struct empobj */
natid trt_cna; /* proposer */
return RET_SYN;
sect->sct_x = newx;
sect->sct_y = newy;
- sect->sct_uid = XYOFFSET(newx, newy);
+ ef_set_uid(EF_SECTOR, §, XYOFFSET(newx, newy));
break;
case 'D':
if (!sarg_xy(p, &newx, &newy))
ship->shp_rflags = arg;
break;
case 'U':
- ship->shp_uid = arg;
+ ef_set_uid(EF_SHIP, ship, arg);
break;
case 'O':
if (ship->shp_own)
land->lnd_land = arg;
break;
case 'U':
- land->lnd_uid = arg;
+ ef_set_uid(EF_LAND, land, arg);
break;
case 'O':
if (land->lnd_own)
plane->pln_nuketype = arg;
break;
case 'U':
- plane->pln_uid = arg;
+ ef_set_uid(EF_PLANE, plane, arg);
break;
case 'l':
if (!sarg_xy(p, &newx, &newy))
/* change the location of secta to that of sectb */
secta.sct_x = sectb.sct_x;
secta.sct_y = sectb.sct_y;
- secta.sct_uid = sectb.sct_uid;
+ ef_set_uid(EF_SECTOR, §a, sectb.sct_uid);
secta.sct_dist_x = sectb.sct_x;
secta.sct_dist_y = sectb.sct_y;
secta.sct_coastal = sectb.sct_coastal;
/* change the location of sectb to where secta was */
sectb.sct_x = tmp.sct_x;
sectb.sct_y = tmp.sct_y;
- sectb.sct_uid = tmp.sct_uid;
+ ef_set_uid(EF_SECTOR, §b, tmp.sct_uid);
sectb.sct_dist_x = tmp.sct_x;
sectb.sct_dist_y = tmp.sct_y;
sectb.sct_coastal = tmp.sct_coastal;
static int ef_realloc_cache(struct empfile *, int);
static int fillcache(struct empfile *, int);
+static int do_read(struct empfile *, void *, int, int);
static int do_write(struct empfile *, void *, int, int);
+static unsigned get_seqno(struct empfile *, int);
+static void new_seqno(struct empfile *, void *);
static void do_blank(struct empfile *, void *, int, int);
/*
*/
static int
fillcache(struct empfile *ep, int id)
+{
+ int ret;
+
+ if (CANT_HAPPEN(!ep->cache))
+ return -1;
+
+ ret = do_read(ep, ep->cache, id, MIN(ep->csize, ep->fids - id));
+ if (ret >= 0) {
+ /* cache changed */
+ ep->baseid = id;
+ ep->cids = ret;
+ }
+ return ret;
+}
+
+static int
+do_read(struct empfile *ep, void *buf, int id, int count)
{
int n, ret;
char *p;
- if (CANT_HAPPEN(ep->fd < 0 || !ep->cache))
+ if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
return -1;
if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
return -1;
}
- p = ep->cache;
- n = MIN(ep->csize, ep->fids - id) * ep->size;
+ p = buf;
+ n = count * ep->size;
while (n > 0) {
ret = read(ep->fd, p, n);
if (ret < 0) {
if (errno != EINTR) {
logerror("Error reading %s elt %d (%s)",
ep->file,
- id + (int)((p - ep->cache) / ep->size),
+ id + (int)((p - (char *)buf) / ep->size),
strerror(errno));
break;
}
} else if (ret == 0) {
logerror("Unexpected EOF reading %s elt %d",
- ep->file, id + (int)((p - ep->cache) / ep->size));
+ ep->file, id + (int)((p - (char *)buf) / ep->size));
break;
} else {
p += ret;
}
}
- if (p == ep->cache)
- return -1; /* nothing read, old cache still ok */
-
- ep->baseid = id;
- ep->cids = (p - ep->cache) / ep->size;
- return ep->cids;
+ return (p - (char *)buf) / ep->size;
}
/*
ep->prewrite(id, from);
if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
return 0; /* not implemented */
+ new_seqno(ep, from);
if (ep->fd >= 0) {
if (do_write(ep, from, id, 1) < 0)
return 0;
return 1;
}
+/*
+ * Change element id.
+ * BUF is an element of table TYPE.
+ * ID is its new element ID.
+ * If table is EFF_TYPED, change id and sequence number stored in BUF.
+ * Else do nothing.
+ */
+void
+ef_set_uid(int type, void *buf, int uid)
+{
+ struct emptypedstr *elt;
+ struct empfile *ep;
+
+ if (ef_check(type) < 0)
+ return;
+ ep = &empfile[type];
+ if (!(ep->flags & EFF_TYPED))
+ return;
+ elt = buf;
+ if (elt->uid == uid)
+ return;
+ elt->uid = uid;
+ elt->seqno = get_seqno(ep, uid);
+}
+
+/*
+ * Return sequence number of element ID in table EP.
+ * Return zero if table is not EFF_TYPED (it has no sequence number
+ * then).
+ */
+static unsigned
+get_seqno(struct empfile *ep, int id)
+{
+ struct emptypedstr *elt;
+
+ if (!(ep->flags & EFF_TYPED))
+ return 0;
+ if (id < 0 || id >= ep->fids)
+ return 0;
+ if (id >= ep->baseid && id < ep->baseid + ep->cids)
+ elt = (void *)(ep->cache + (id - ep->baseid) * ep->size);
+ else {
+ /* need a buffer, steal last cache slot */
+ if (ep->cids == ep->csize)
+ ep->cids--;
+ elt = (void *)(ep->cache + ep->cids * ep->size);
+ if (do_read(ep, elt, id, 1) < 0)
+ return 0; /* deep trouble */
+ }
+ return elt->seqno;
+}
+
+/*
+ * Increment sequence number in BUF, which is about to be written to EP.
+ * Do nothing if table is not EFF_TYPED (it has no sequence number
+ * then).
+ */
+static void
+new_seqno(struct empfile *ep, void *buf)
+{
+ struct emptypedstr *elt = buf;
+ unsigned old_seqno;
+
+ if (!(ep->flags & EFF_TYPED))
+ return;
+ old_seqno = get_seqno(ep, elt->uid);
+ if (CANT_HAPPEN(old_seqno != elt->seqno))
+ old_seqno = MAX(old_seqno, elt->seqno);
+ elt->seqno = old_seqno + 1;
+}
+
/*
* Extend table TYPE by COUNT elements.
* Any pointers obtained from ef_ptr() become invalid.
void
ef_blank(int type, int id, void *buf)
{
+ struct empfile *ep;
+ struct emptypedstr *elt;
+
if (ef_check(type) < 0)
return;
- do_blank(&empfile[type], buf, id, 1);
+ ep = &empfile[type];
+ do_blank(ep, buf, id, 1);
+ if (ep->flags & EFF_TYPED) {
+ elt = buf;
+ elt->seqno = get_seqno(ep, elt->uid);
+ }
}
/*
for (j = 0; getnews(i + j, &news); j++) {
if (news.nws_vrb == 0)
break;
- news.nws_uid = j;
+ ef_set_uid(EF_NEWS, &news, j);
putnews(j, &news);
}
CANT_HAPPEN(i + j != news_tail);
for (i = 1; i < MAXNOC; i++) {
nat.ef_type = EF_NATION;
nat.nat_cnum = nat.nat_uid = i;
+ nat.nat_seqno = 0;
putnat((&nat));
}
memset(&realm, 0, sizeof(realm));
for (j = 0; j < MAXNOR; j++) {
realm.r_realm = j;
realm.r_uid = (i * MAXNOR) + j;
+ realm.r_seqno = 0;
putrealm(&realm);
}
}
sp->ef_type = EF_SECTOR;
sp->sct_uid = XYOFFSET(x, y);
+ sp->sct_seqno = 0;
sp->sct_x = x;
sp->sct_y = y;
sp->sct_dist_x = x;