From: Markus Armbruster Date: Sat, 6 Sep 2008 22:40:58 +0000 (-0400) Subject: Cargo lists storing lists of cargo for each carrier X-Git-Tag: v4.3.17~61 X-Git-Url: http://git.pond.sub.org/?p=empserver;a=commitdiff_plain;h=64a53c90f0522ba60bf70a7912161a082dd0b745 Cargo lists storing lists of cargo for each carrier Persistent game state encodes "who carries what" by storing the carrier uid in the cargo. Cargo lists augment that: they store lists of cargo for each carrier. They are not persistent. New unit_cargo_init() to compute the cargo lists from game state. Call it in ef_init_srv() and at the end of update_main(). New unit_onresize() to resize the cargo list data structure. Installed as units' struct empfile callback onresize to make them resize automatically with the unit files. New unit_carrier_change() to update cargo lists when carriers change in game state. Convenience wrappers pln_carrier_change(), lnd_carrier_change() and nuk_carrier_change(). Call them from prewrite callbacks to keep cargo lists in sync with game state. To make that work, unused units must not point to a carrier. Add new pln_oninit(), lnd_oninit() and nuk_oninit() take care of newly created units. Change lnd_prewrite() and nuk_prewrite() to take dead land units and nukes off their carrier. pln_prewrite() did that already. New unit_cargo_first(), unit_cargo_next() to traverse cargo lists. Convenience wrappers lnd_first_on_ship(), lnd_first_on_land(), lnd_next_on_unit(), pln_first_on_ship(), pln_first_on_land(), pln_next_on_unit() and nuk_on_plane(). The latter is disabled for now because it clashes with an existing function. --- diff --git a/include/land.h b/include/land.h index 487426305..f443f9d9e 100644 --- a/include/land.h +++ b/include/land.h @@ -159,6 +159,12 @@ extern int lnd_acc(struct lndstr *); extern int lnd_dam(struct lndstr *); extern int lnd_aaf(struct lndstr *); +/* src/lib/common/cargo.c */ +extern void lnd_carrier_change(struct lndstr *, int, int, int); +extern int lnd_first_on_ship(struct shpstr *); +extern int lnd_first_on_land(struct lndstr *); +extern int lnd_next_on_unit(int); + extern int lnd_fire(struct lndstr *); extern double lnd_fire_range(struct lndstr *); diff --git a/include/nuke.h b/include/nuke.h index 054cdb4c3..05245cfb4 100644 --- a/include/nuke.h +++ b/include/nuke.h @@ -90,4 +90,10 @@ struct nchrstr { extern struct nchrstr nchr[N_MAXNUKE + 1]; +/* src/lib/common/cargo.c */ +extern void nuk_carrier_change(struct nukstr *, int, int, int); +#if 0 /* clashes with the one in src/lib/subs/nuke.c */ +extern int nuk_on_plane(struct plnstr *); +#endif + #endif diff --git a/include/plane.h b/include/plane.h index 485ebb51e..6fb735f0f 100644 --- a/include/plane.h +++ b/include/plane.h @@ -156,6 +156,12 @@ extern int pln_acc(struct plnstr *); extern int pln_range_max(struct plnstr *); extern int pln_load(struct plnstr *); +/* src/lib/common/cargo.c */ +extern void pln_carrier_change(struct plnstr *, int, int, int); +extern int pln_first_on_ship(struct shpstr *); +extern int pln_first_on_land(struct lndstr *); +extern int pln_next_on_unit(int); + /* src/lib/subs/aircombat.c */ extern void ac_combat_headers(natid, natid); extern void ac_airtoair(struct emp_qelem *, struct emp_qelem *); diff --git a/include/prototypes.h b/include/prototypes.h index 7ddfeff29..7cb9bd305 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -450,6 +450,7 @@ extern void bitinit2(struct nstr_sect *, unsigned char *, int); extern int getele(char *, char *); /* land.c */ extern char *prland(struct lndstr *); +extern void lnd_oninit(void *); extern void lnd_postread(int, void *); extern void lnd_prewrite(int, void *, void *); /* landgun.c */ @@ -517,6 +518,7 @@ extern void init_nreport(void); extern void nreport(natid, int, natid, int); /* nuke.c */ extern char *prnuke(struct nukstr *); +extern void nuk_oninit(void *); extern void nuk_postread(int, void *); extern void nuk_prewrite(int, void *, void *); extern int nuk_on_plane(struct nukstr *, int); @@ -528,6 +530,7 @@ extern int nxtsct(struct nstr_sect *, struct sctstr *); extern int onearg(char *, char *); /* plane.c */ extern char *prplane(struct plnstr *); +extern void pln_oninit(void *); extern void pln_postread(int, void *); extern void pln_prewrite(int, void *, void *); /* plnsub.c */ diff --git a/include/unit.h b/include/unit.h index c8805e87e..0ce36545b 100644 --- a/include/unit.h +++ b/include/unit.h @@ -44,6 +44,12 @@ struct ulist { int supplied; /* LAND only */ }; +extern void unit_cargo_init(void); +extern void unit_carrier_change(struct empobj *, int, int, int); +extern int unit_cargo_first(int, int, int); +extern int unit_cargo_next(int, int); +extern int unit_onresize(int); + extern void unit_list(struct emp_qelem *); extern void unit_put(struct emp_qelem *list, natid actor); extern char *unit_path(int, struct empobj *, char *); diff --git a/src/lib/common/cargo.c b/src/lib/common/cargo.c new file mode 100644 index 000000000..4e38b3b0b --- /dev/null +++ b/src/lib/common/cargo.c @@ -0,0 +1,391 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2008, 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. + * + * --- + * + * cargo.c: Cargo lists + * + * Known contributors to this file: + * Markus Armbruster, 2008 + */ + +#include + +#include +#include +#include "file.h" +#include "unit.h" + +struct clink { + short next; + short head[EF_NUKE - EF_PLANE + 1]; +}; + +/* + * Cargo lists + * + * Persistent game state encodes "who carries what" by storing the + * carrier uid in the cargo. Cargo lists augment that: they store + * lists of cargo for each carrier. + * + * clink[TYPE] points to an array of cargo list links. The array has + * nclink[TYPE] elements. nclink[TYPE] tracks ef_nelem(TYPE). + * clink[TYPE][UID] is the cargo list link for unit UID of type TYPE. + * TYPE must be a unit file type: EF_SHIP, EF_PLANE, EF_LAND or + * EF_NUKE. Other slots of clink[] and nclink[] are unused and remain + * zero. + * + * clink[TYPE][UID].next is the uid of the next unit of the same type + * in the same carrier, -1 if none. + * + * clink[TYPE][UID].head[CARGO-EF_PLANE] is the uid of the first unit + * of type CARGO carried by this unit, -1 if none. The next unit, if + * any, is clink[CARGO][clink[TYPE][UID].head[CARGO-EF_PLANE]].next, + * and so forth. + * + * Each type of carrier can only carry certain types of cargo, but + * cargo lists know nothing about that. + */ +static struct clink *clink[EF_NUKE + 1]; +static short nclink[EF_NUKE + 1]; + +/* + * Return pointer to CL's cargo list head for file type TYPE. + */ +static short * +clink_headp(struct clink *cl, int type) +{ + static short dummy; + + if (CANT_HAPPEN(type < EF_PLANE || type > EF_NUKE)) { + dummy = -1; + return &dummy; + } + return &cl->head[type - EF_PLANE]; +} + +/* + * Initialize cargo list link CL to empty. + */ +static void +clink_init(struct clink *cl) +{ + unsigned i; + + cl->next = -1; + for (i = 0; i < sizeof(cl->head) / sizeof(*cl->head); i++) + cl->head[i] = -1; +} + +/* + * Check whether *UIDP is a valid uid for file type TYPE. + */ +static void +clink_check1(short *uidp, int type) +{ + if (CANT_HAPPEN(*uidp >= nclink[type])) + *uidp = -1; +} + +/* + * Check validity of cargo lists for file type TYPE. + */ +static void +clink_check(int type) +{ + int carr_type, i; + + /* check the heads for all carriers */ + if (type != EF_SHIP) { + for (carr_type = EF_PLANE; carr_type <= EF_NUKE; carr_type++) { + for (i = 0; i < nclink[carr_type]; i++) + clink_check1(clink_headp(&clink[carr_type][i], type), + type); + } + } + /* check the nexts */ + for (i = 0; i < nclink[type]; i++) + clink_check1(&clink[type][i].next, type); +} + +/* + * Add to CL's cargo list for type TYPE the uid UID. + * UID must not be on any cargo list already. + */ +static void +clink_add(struct clink *cl, int type, int uid) +{ + short *head = clink_headp(cl, type); + + if (CANT_HAPPEN(type < 0 || type > EF_NUKE + || uid < 0 || uid >= nclink[type])) + return; + if (CANT_HAPPEN(*head >= nclink[type])) + *head = -1; + clink[type][uid].next = *head; + *head = uid; +} + +/* + * Remove from CL's cargo list for type TYPE the uid UID. + * UID must be on that cargo list. + */ +static void +clink_rem(struct clink *cl, int type, int uid) +{ + short *head = clink_headp(cl, type); + struct clink *linkv; + int n; + short *p; + + if (CANT_HAPPEN(type < 0 || type > EF_NUKE)) + return; + linkv = clink[type]; + n = nclink[type]; + + for (p = head; *p != uid; p = &linkv[*p].next) { + if (CANT_HAPPEN(*p < 0 || *p >= n)) + return; + } + + *p = linkv[uid].next; +} + +/* + * Update cargo lists for a change of CARGO's carrier. + * Carrier is of type TYPE, and changes from uid OLD to NEW. + * Negative uids mean no carrier. + */ +void +unit_carrier_change(struct empobj *cargo, int type, int old, int new) +{ + if (CANT_HAPPEN(type < 0 || type > EF_NUKE)) + return; + if (old >= 0) + clink_rem(&clink[type][old], cargo->ef_type, cargo->uid); + if (new >= 0) + clink_add(&clink[type][new], cargo->ef_type, cargo->uid); +} + +/* + * Update cargo lists for a change of PP's carrier. + * Carrier is of type TYPE, and changes from uid OLD to NEW. + * Negative uids mean no carrier. + */ +void +pln_carrier_change(struct plnstr *pp, int type, int old, int new) +{ + unit_carrier_change((struct empobj *)pp, type, old, new); +} + +/* + * Update cargo lists for a change of LP's carrier. + * Carrier is of type TYPE, and changes from uid OLD to NEW. + * Negative uids mean no carrier. + */ +void +lnd_carrier_change(struct lndstr *lp, int type, int old, int new) +{ + unit_carrier_change((struct empobj *)lp, type, old, new); +} + +/* + * Update cargo lists for a change of NP's carrier. + * Carrier is of type TYPE, and changes from uid OLD to NEW. + * Negative uids mean no carrier. + */ +void +nuk_carrier_change(struct nukstr *np, int type, int old, int new) +{ + unit_carrier_change((struct empobj *)np, type, old, new); +} + +/* + * Initialize cargo lists from game state. + */ +void +unit_cargo_init(void) +{ + int i; + struct plnstr *pp; + struct lndstr *lp; + struct nukstr *np; + + for (i = EF_SHIP; i <= EF_NUKE; i++) { + nclink[i] = 0; + unit_onresize(i); + } + + for (i = 0; (pp = getplanep(i)); i++) { + if (!pp->pln_own) + continue; + if (CANT_HAPPEN(pp->pln_ship >= 0 && pp->pln_land >= 0)) + pp->pln_land = -1; + pln_carrier_change(pp, EF_SHIP, -1, pp->pln_ship); + pln_carrier_change(pp, EF_LAND, -1, pp->pln_land); + } + for (i = 0; (lp = getlandp(i)); i++) { + if (!lp->lnd_own) + continue; + if (CANT_HAPPEN(lp->lnd_ship >= 0 && lp->lnd_land >= 0)) + lp->lnd_land = -1; + lnd_carrier_change(lp, EF_SHIP, -1, lp->lnd_ship); + lnd_carrier_change(lp, EF_LAND, -1, lp->lnd_land); + } + for (i = 0; (np = getnukep(i)); i++) { + if (!np->nuk_own) + continue; + nuk_carrier_change(np, EF_PLANE, -1, np->nuk_plane); + } +} + +/* + * Resize clink[TYPE] to match ef_nelem(TYPE). + * Return 0 on success, -1 on error. + * This is the struct empfile onresize callback for units. + */ +int +unit_onresize(int type) +{ + int n, i; + struct clink *cl; + + if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE)) + return -1; + + n = ef_nelem(type); + cl = realloc(clink[type], n * sizeof(*clink[type])); + if (!cl) + return -1; + for (i = nclink[type]; i < n; i++) + clink_init(&cl[i]); + clink[type] = cl; + nclink[type] = n; + clink_check(type); + return 0; +} + +/* + * Find first unit on a carrier's cargo list for file type CARGO_TYPE. + * Search carrier UID of type TYPE. + * Return first unit's uid, or -1 if the carrier isn't carrying such + * units. + */ +int +unit_cargo_first(int type, int uid, int cargo_type) +{ + short *headp; + + if (CANT_HAPPEN(type < EF_SHIP || type > EF_NUKE)) + return -1; + if (CANT_HAPPEN(uid < 0 || uid >= nclink[type])) + return -1; + headp = clink_headp(&clink[type][uid], cargo_type); + if (CANT_HAPPEN(!headp)) + return -1; + return *headp; +} + +/* + * Find the next unit on a cargo list for file type CARGO_TYPE. + * Get the unit after CARGO_UID. + * Return its uid, or -1 if there are no more on this list. + */ +int +unit_cargo_next(int cargo_type, int cargo_uid) +{ + if (CANT_HAPPEN(cargo_type < EF_SHIP || cargo_type > EF_NUKE)) + return -1; + if (CANT_HAPPEN(cargo_uid < 0 || cargo_uid >= nclink[cargo_type])) + return -1; + return clink[cargo_type][cargo_uid].next; +} + +/* + * If SP carries planes, return the uid of the first one, else -1. + */ +int +pln_first_on_ship(struct shpstr *sp) +{ + return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_PLANE); +} + +/* + * If LP carries planes, return the uid of the first one, else -1. + */ +int +pln_first_on_land(struct lndstr *lp) +{ + return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_PLANE); +} + +/* + * Find the next plane on the same carrier as plane#UID. + * Return its uid, or -1 if there are no more. + */ +int +pln_next_on_unit(int uid) +{ + return unit_cargo_next(EF_PLANE, uid); +} + +/* + * If SP carries land units, return the uid of the first one, else -1. + */ +int +lnd_first_on_ship(struct shpstr *sp) +{ + return unit_cargo_first(EF_SHIP, sp->shp_uid, EF_LAND); +} + +/* + * If SP carries land units, return the uid of the first one, else -1. + */ +int +lnd_first_on_land(struct lndstr *lp) +{ + return unit_cargo_first(EF_LAND, lp->lnd_uid, EF_LAND); +} + +/* + * Find the next land unit on the same carrier as land#UID. + * Return its uid, or -1 if there are no more. + */ +int +lnd_next_on_unit(int uid) +{ + return unit_cargo_next(EF_LAND, uid); +} + +#if 0 /* clashes with the one in src/lib/subs/nuke.c */ +/* + * If PP carries a nuke, return its uid, else -1. + */ +int +nuk_on_plane(struct plnstr *pp) +{ + return unit_cargo_first(EF_PLANE, pp->pln_uid, EF_NUKE); +} +#endif diff --git a/src/lib/subs/fileinit.c b/src/lib/subs/fileinit.c index fb0e0f33f..1a63a1dd0 100644 --- a/src/lib/subs/fileinit.c +++ b/src/lib/subs/fileinit.c @@ -38,19 +38,22 @@ #include "nat.h" #include "optlist.h" #include "prototypes.h" +#include "unit.h" struct fileinit { int ef_type; - void (*postread) (int, void *); - void (*prewrite) (int, void *, void *); + void (*oninit)(void *); + void (*postread)(int, void *); + void (*prewrite)(int, void *, void *); + int (*onresize)(int); }; static struct fileinit fileinit[] = { - {EF_SECTOR, sct_postread, sct_prewrite}, - {EF_SHIP, shp_postread, shp_prewrite}, - {EF_PLANE, pln_postread, pln_prewrite}, - {EF_LAND, lnd_postread, lnd_prewrite}, - {EF_NUKE, nuk_postread, nuk_prewrite} + {EF_SECTOR, NULL, sct_postread, sct_prewrite, NULL}, + {EF_SHIP, NULL, shp_postread, shp_prewrite, unit_onresize}, + {EF_PLANE, pln_oninit, pln_postread, pln_prewrite, unit_onresize}, + {EF_LAND, lnd_oninit, lnd_postread, lnd_prewrite, unit_onresize}, + {EF_NUKE, nuk_oninit, nuk_postread, nuk_prewrite, unit_onresize} }; static void ef_open_srv(void); @@ -67,6 +70,7 @@ ef_init_srv(void) for (i = 0; i < sizeof(fileinit) / sizeof(fileinit[0]); i++) { empfile[fileinit[i].ef_type].postread = fileinit[i].postread; empfile[fileinit[i].ef_type].prewrite = fileinit[i].prewrite; + empfile[fileinit[i].ef_type].onresize = fileinit[i].onresize; } nsc_init(); @@ -74,6 +78,7 @@ ef_init_srv(void) if (ef_verify() < 0) exit(EXIT_FAILURE); global_init(); + unit_cargo_init(); } void diff --git a/src/lib/subs/land.c b/src/lib/subs/land.c index 9bc647ebf..618a294ca 100644 --- a/src/lib/subs/land.c +++ b/src/lib/subs/land.c @@ -120,6 +120,7 @@ lnd_prewrite(int n, void *old, void *new) if (llp->lnd_own && llp->lnd_effic < LAND_MINEFF) { own = 0; + llp->lnd_ship = llp->lnd_land = -1; for (i = 0; NULL != (lp = getlandp(i)); i++) { if (lp->lnd_own && lp->lnd_land == n) { @@ -149,6 +150,13 @@ lnd_prewrite(int n, void *old, void *new) item_prewrite(llp->lnd_item); } + if (CANT_HAPPEN(llp->lnd_ship >= 0 && llp->lnd_land >= 0)) + llp->lnd_land = -1; + if (oldlp->lnd_ship != llp->lnd_ship) + lnd_carrier_change(llp, EF_SHIP, oldlp->lnd_ship, llp->lnd_ship); + if (oldlp->lnd_land != llp->lnd_land) + lnd_carrier_change(llp, EF_LAND, oldlp->lnd_land, llp->lnd_land); + /* We've avoided assigning to llp->lnd_own, in case oldsp == sp */ if (oldlp->lnd_own != own) lost_and_found(EF_LAND, oldlp->lnd_own, own, @@ -157,6 +165,14 @@ lnd_prewrite(int n, void *old, void *new) llp->lnd_own = own; } +void +lnd_oninit(void *ptr) +{ + struct lndstr *lp = ptr; + + lp->lnd_ship = lp->lnd_land = -1; +} + char * prland(struct lndstr *lp) { diff --git a/src/lib/subs/nuke.c b/src/lib/subs/nuke.c index 6f38e0e89..0708ff579 100644 --- a/src/lib/subs/nuke.c +++ b/src/lib/subs/nuke.c @@ -44,8 +44,6 @@ #include "plane.h" #include "player.h" #include "prototypes.h" -#include "sect.h" -#include "xy.h" void nuk_postread(int n, void *ptr) @@ -80,8 +78,13 @@ nuk_prewrite(int n, void *old, void *new) struct nukstr *np = new; natid own = np->nuk_own; - if (np->nuk_effic == 0) + if (np->nuk_effic == 0) { own = 0; + np->nuk_plane = -1; + } + + if (oldnp->nuk_plane != np->nuk_plane) + nuk_carrier_change(np, EF_PLANE, oldnp->nuk_plane, np->nuk_plane); /* We've avoided assigning to np->nuk_own, in case oldsp == sp */ if (oldnp->nuk_own != own) @@ -106,6 +109,14 @@ nuk_on_plane(struct nukstr *np, int pluid) return -1; } +void +nuk_oninit(void *ptr) +{ + struct nukstr *np = ptr; + + np->nuk_plane = -1; +} + char * prnuke(struct nukstr *np) { diff --git a/src/lib/subs/plane.c b/src/lib/subs/plane.c index 18f9401e5..11c28104c 100644 --- a/src/lib/subs/plane.c +++ b/src/lib/subs/plane.c @@ -119,6 +119,7 @@ pln_prewrite(int n, void *old, void *new) if (pp->pln_effic < PLANE_MINEFF) { own = 0; pp->pln_effic = 0; + pp->pln_ship = pp->pln_land = -1; for (i = 0; NULL != (np = getnukep(i)); i++) { if (np->nuk_own && np->nuk_plane == n) { mpr(np->nuk_own, "%s lost!\n", prnuke(np)); @@ -129,6 +130,13 @@ pln_prewrite(int n, void *old, void *new) } } + if (CANT_HAPPEN(pp->pln_ship >= 0 && pp->pln_land >= 0)) + pp->pln_land = -1; + if (oldpp->pln_ship != pp->pln_ship) + pln_carrier_change(pp, EF_SHIP, oldpp->pln_ship, pp->pln_ship); + if (oldpp->pln_land != pp->pln_land) + pln_carrier_change(pp, EF_LAND, oldpp->pln_land, pp->pln_land); + /* We've avoided assigning to pp->pln_own, in case oldsp == sp */ if (oldpp->pln_own != own) lost_and_found(EF_PLANE, oldpp->pln_own, own, @@ -137,6 +145,14 @@ pln_prewrite(int n, void *old, void *new) pp->pln_own = own; } +void +pln_oninit(void *ptr) +{ + struct plnstr *pp = ptr; + + pp->pln_ship = pp->pln_land = -1; +} + char * prplane(struct plnstr *pp) { diff --git a/src/lib/update/main.c b/src/lib/update/main.c index 731405a4f..93fa218b1 100644 --- a/src/lib/update/main.c +++ b/src/lib/update/main.c @@ -42,6 +42,7 @@ #include "journal.h" #include "player.h" #include "server.h" +#include "unit.h" #include "update.h" long money[MAXNOC]; @@ -169,6 +170,7 @@ update_main(void) ef_flush(EF_SHIP); ef_flush(EF_PLANE); ef_flush(EF_LAND); + unit_cargo_init(); delete_old_announcements(); delete_old_news(); /* Clear all the telegram flags */