Take care not to supply from self
authorMarkus Armbruster <armbru@pond.sub.org>
Sun, 15 Feb 2009 07:47:43 +0000 (08:47 +0100)
committerMarkus Armbruster <armbru@pond.sub.org>
Tue, 17 Feb 2009 18:32:14 +0000 (19:32 +0100)
Change s_commod() not to use the supply sink as source.  As explained
in the message of the commit before the previous one, using the sink
as source makes it impossible for callers to safely keep a copy of the
sink across a supply call.  All current users do that.  Some were safe
anyway, some were not:

* fort_fire() was safe, because a fort draws shells only when it has
  none.

* shp_fire() was unsafe for ships with capability supply and ammo use
  greater than 1.  No such ship exists in the stock game.

* shp_dchrg() was unsafe for ships with both capabilities dchrg and
  supply.  Same for shp_torp() and capability torp, and
  shp_missile_defense() and capability anti-missile.  No such ship
  exists in the stock game.

* lnd_fire(), supp() and get_dlist() were safe, because they draw
  shells only when they have less than their ammo need, and then they
  don't supply any.

* mission_pln_equip() was unsafe when equipping planes with shells in
  supply sources.

* landmine() was unsafe for land units with both capability engineer
  and supply.  No such land units exist in the stock game.

* load() and lload() were unsafe for loadable supply units, but the
  supply use there was disabled in commit 65410d16 because of another
  bug.

* ask_olist() and att_reacting_units() were safe, because
  lnd_can_attack() excludes supply units.

In the stock game, planes flying interception or support missions,
abms intercepting ballistic missiles, launch of missiles or anti-sats
could conjure up shells, triggering a seqno mismatch oops.

In games with unusual customizations, this could also happen with
supply ships firing guns or torpedoes, dropping depth charges, or
shooting down marine missiles, and in the lmine command.

src/lib/subs/supply.c

index 57a60a4203c75e15ed4d0dd4b3ba960270ab4746..f81ece5371f409dabea91b738e6197a59fbd9a6e 100644 (file)
@@ -130,34 +130,40 @@ s_commod(struct empobj *sink, short *vec,
        return 1;
     wanted -= vec[type];
 
-    /* try to get it from sector we're in */
     getsect(x, y, &dest);
-    getsect(x, y, &sect);
-    if (sect.sct_own == own) {
-       if (!opt_NOFOOD && type == I_FOOD)
-           minimum = 1 + (int)ceil(food_needed(sect.sct_item,
-                                               etu_per_update));
-       if (sect.sct_item[type] - wanted >= minimum) {
-           sect.sct_item[type] -= wanted;
-           if (actually_doit) {
-               vec[type] += wanted;
-               putsect(&sect);
-               put_empobj(sink->ef_type, sink->uid, sink);
-           }
-           return 1;
-       } else if (sect.sct_item[type] - minimum > 0) {
-           wanted -= sect.sct_item[type] - minimum;
-           sect.sct_item[type] = minimum;
-           if (actually_doit) {
-               vec[type] += sect.sct_item[type] - minimum;
-               putsect(&sect);
+
+    /* try to get it from sector we're in */
+    if (sink->ef_type != EF_SECTOR) {
+       getsect(x, y, &sect);
+       if (sect.sct_own == own) {
+           if (!opt_NOFOOD && type == I_FOOD)
+               minimum = 1 + (int)ceil(food_needed(sect.sct_item,
+                                                   etu_per_update));
+           if (sect.sct_item[type] - wanted >= minimum) {
+               sect.sct_item[type] -= wanted;
+               if (actually_doit) {
+                   vec[type] += wanted;
+                   putsect(&sect);
+                   put_empobj(sink->ef_type, sink->uid, sink);
+               }
+               return 1;
+           } else if (sect.sct_item[type] - minimum > 0) {
+               wanted -= sect.sct_item[type] - minimum;
+               sect.sct_item[type] = minimum;
+               if (actually_doit) {
+                   vec[type] += sect.sct_item[type] - minimum;
+                   putsect(&sect);
+               }
            }
        }
     }
+
     /* look for a headquarters or warehouse */
     lookrange = tfact(own, 10.0);
     snxtsct_dist(&ns, x, y, lookrange);
     while (nxtsct(&ns, &sect) && wanted) {
+       if (ns.curdist == 0)
+           continue;
        if (sect.sct_own != own)
            continue;
        if ((sect.sct_type != SCT_WAREH) &&
@@ -174,10 +180,8 @@ s_commod(struct empobj *sink, short *vec,
        if (!opt_NOFOOD && type == I_FOOD)
            minimum = 1 + (int)ceil(food_needed(sect.sct_item,
                                                etu_per_update));
-       if (sect.sct_item[type] <= minimum) {
-           /* Don't bother... */
+       if (sect.sct_item[type] <= minimum)
            continue;
-       }
        ip = &ichr[type];
        dp = &dchr[sect.sct_type];
        packing = ip->i_pkg[dp->d_pkg];
@@ -231,11 +235,11 @@ s_commod(struct empobj *sink, short *vec,
 
     /* look for an owned ship in a harbor */
     snxtitem_dist(&ni, EF_SHIP, x, y, lookrange);
-
     while (nxtitem(&ni, &ship) && wanted) {
+       if (sink->ef_type == EF_SHIP && sink->uid == ship.shp_uid)
+           continue;
        if (ship.shp_own != own)
            continue;
-
        if (!(mchr[(int)ship.shp_type].m_flags & M_SUPPLY))
            continue;
        getsect(ship.shp_x, ship.shp_y, &sect);
@@ -248,10 +252,8 @@ s_commod(struct empobj *sink, short *vec,
        if (!opt_NOFOOD && type == I_FOOD)
            minimum = 1 + (int)ceil(food_needed(ship.shp_item,
                                                etu_per_update));
-       if (ship.shp_item[type] <= minimum) {
-           /* Don't bother... */
+       if (ship.shp_item[type] <= minimum)
            continue;
-       }
        ip = &ichr[type];
        dp = &dchr[sect.sct_type];
        packing = ip->i_pkg[dp->d_pkg];
@@ -303,10 +305,11 @@ s_commod(struct empobj *sink, short *vec,
 
     /* look for an owned supply unit */
     snxtitem_dist(&ni, EF_LAND, x, y, lookrange);
-
     while (nxtitem(&ni, &land) && wanted) {
        int min;
 
+       if (sink->ef_type == EF_LAND && sink->uid == land.lnd_uid)
+           continue;
        if (land.lnd_own != own)
            continue;
 
@@ -443,13 +446,10 @@ lnd_could_be_supplied(struct lndstr *lp)
        food_needed = get_minimum(lp, I_FOOD);
        food = lp->lnd_item[I_FOOD];
        if (food < food_needed) {
-           lp->lnd_item[I_FOOD] = 0;
-           putland(lp->lnd_uid, lp);
            res = s_commod((struct empobj *)lp, lp->lnd_item,
                           I_FOOD, food_needed,
                           lchr[lp->lnd_type].l_item[I_FOOD], 0);
            lp->lnd_item[I_FOOD] = food;
-           putland(lp->lnd_uid, lp);
            if (!res)
                return 0;
        }
@@ -458,13 +458,10 @@ lnd_could_be_supplied(struct lndstr *lp)
     shells_needed = lchr[lp->lnd_type].l_ammo;
     shells = lp->lnd_item[I_SHELL];
     if (shells < shells_needed) {
-       lp->lnd_item[I_SHELL] = 0;
-       putland(lp->lnd_uid, lp);
        res = s_commod((struct empobj *)lp, lp->lnd_item,
                       I_SHELL, shells_needed,
                       lchr[lp->lnd_type].l_item[I_SHELL], 0);
        lp->lnd_item[I_SHELL] = shells;
-       putland(lp->lnd_uid, lp);
        if (!res)
            return 0;
     }