For review and comment.

The purpose of this patch is to:

1.  fix the flaw in SP_GENERIC_32 caused by not byte swapping the
values, in a way that does not break existing clients that began to use
the packet, by using the next version number 'b', and by allowing the
client to state which version it desires,

2.  add Bronco t-mode and clue game support features to the packet, to
allow development of client side tools without falling back on message
parsing.

The new values in the packet include:

a.  the value of status->gameup, so that server status flags can be
displayed,

b.  the team numbers of the two teams involved in t-mode,

c.  the duration of t-mode so far, (hitherto clients have had to
calculate duration based on when they observed t-mode to begin),

d.  the remaining time in t-mode for an INL or clue game, (hitherto only
available by parsing messages to ALL or by sending TIME command),

e.  the starbase reconstruction time, (hitherto ... SBTIME),

f.  the team surrender time, for whatever team is considering surrender,

Patch below.

Fri Jul 11 20:38:10 EST 2008  quozl at us.netrek.org
  * SP_GENERIC_32 version 'b' packet support
diff -rN -u old-netrek-server/Vanilla/docs/sample_features new-netrek-server/Vanilla/docs/sample_features
--- old-netrek-server/Vanilla/docs/sample_features	2008-07-11 20:38:48.000000000 +1000
+++ new-netrek-server/Vanilla/docs/sample_features	2008-07-11 20:38:48.000000000 +1000
@@ -63,8 +63,8 @@
 LITE_SOUNDS           C       ON
 # ship stat. packets
 SHIP_CAP              S       ON
-# generic packets
-SP_GENERIC_32         S       ON
+# generic packets (plus packet version number)
+SP_GENERIC_32         S       ON       2
 # Despite using short packets, show full direction resolution for
 # ships.  The same information for players is already included in the
 # original packet format.  When a client requests this feature, the
diff -rN -u old-netrek-server/Vanilla/include/packets.h new-netrek-server/Vanilla/include/packets.h
--- old-netrek-server/Vanilla/include/packets.h	2008-07-11 20:38:48.000000000 +1000
+++ new-netrek-server/Vanilla/include/packets.h	2008-07-11 20:38:48.000000000 +1000
@@ -998,26 +998,57 @@
     u_short	s_bitmap;
 };
 
-struct generic_32_spacket { /* SP_GENERIC_32 py-struct "!bbhh26x" #32 */
-    char	type;
-    char	version;	/* alphabetic */
-    short	repair_time;	/* server estimate of repair time in seconds */
-    short	pl_orbit;	/* what planet player orbiting, -1 if none */
-    char	pad1[26];	/* TODO: consider using a union */
+struct generic_32_spacket {
+    char        type;
+    char        pad[31];
 };
-#define GENERIC_32_VERSION 'a'
 #define GENERIC_32_LENGTH 32
 #define COST_GENERIC_32 (F_sp_generic_32 ? GENERIC_32_LENGTH : 0)
+struct generic_32_spacket_a { /* SP_GENERIC_32 py-struct "b1sHH26x" #32 */
+    char        type;
+    char        version;        /* alphabetic, 0x60 + version */
+    u_short     repair_time;    /* server estimate of repair time in seconds */
+    u_short     pl_orbit;       /* what planet player orbiting, -1 if none */
+    char        pad1[26];
+    /* NOTE: this version didn't use network byte order for the shorts */
+};
+#define GENERIC_32_VERSION_A 1
+struct generic_32_spacket_b { /* SP_GENERIC_32 py-struct "!b1sHbHBBsBsBB18x" #32 */
+    char        type;
+    char        version;        /* alphabetic, 0x60 + version */
+    u_short     repair_time;    /* server estimate of repair time, seconds  */
+    char        pl_orbit;       /* what planet player orbiting, -1 if none  */
+    u_short     gameup;                  /* server status flags             */
+    u_char      tournament_teams;        /* what teams are involved         */
+    u_char      tournament_age;          /* duration of t-mode so far       */
+    char        tournament_age_units;    /* units for above, see s2du       */
+    u_char      tournament_remain;       /* remaining INL game time         */
+    char        tournament_remain_units; /* units for above, see s2du       */
+    u_char      starbase_remain;         /* starbase reconstruction, mins   */
+    u_char      team_remain;             /* team surrender time, seconds    */
+    char        pad1[18];
+} __attribute__ ((packed));
+#define GENERIC_32_VERSION_B 2
+#define GENERIC_32_VERSION GENERIC_32_VERSION_B /* default */
+
+/* SP_GENERIC_32 versioning instructions:
+
+   we start with version 'a', and each time a structure is changed
+   increment the version and reduce the pad size, keeping the packet
+   the same size ...
+
+   client is entitled to trust fields in struct that were defined at a
+   particular version ...
+
+   client is to send CP_FEATURE with SP_GENERIC_32 value 1 for version
+   'a', value 2 for version 'b', etc ...
+
+   server is to reply with SP_FEATURE with SP_GENERIC_32 value set to
+   the maximum version it supports (not the version requested by the
+   client), ...
 
-/* versioning instructions: we start with version 'a', and each time a
-   field is added increment the version and reduce the pad size,
-   keeping the packet the same size ... client is entitled to trust
-   fields in struct that were defined at a particular version. */
-
-/*
-    packet.type = SP_GENERIC_32;
-    packet.version = GENERIC_32_VERSION;
-    if (sizeof(struct generic_32_spacket) != GENERIC_32_LENGTH) abort();
+   server is to send SP_GENERIC_32 packets of the highest version it
+   knows about, but no higher than the version the client asks for.
 */
 
 struct flags_all_spacket {
diff -rN -u old-netrek-server/Vanilla/ntserv/genspkt.c new-netrek-server/Vanilla/ntserv/genspkt.c
--- old-netrek-server/Vanilla/ntserv/genspkt.c	2008-07-11 20:38:48.000000000 +1000
+++ new-netrek-server/Vanilla/ntserv/genspkt.c	2008-07-11 20:38:48.000000000 +1000
@@ -2365,7 +2365,15 @@
 
     clientSelfShip.damage = -1;
     clientSelfShort.pnum = -1;
-    clientGeneric32.repair_time = -1;
+    clientGeneric32.type = 0;
+    if (sizeof(struct generic_32_spacket_a) != GENERIC_32_LENGTH) {
+        fprintf(stderr, "SP_GENERIC_32 size a wrong at %d bytes\n",
+                sizeof(struct generic_32_spacket_a));
+    }
+    if (sizeof(struct generic_32_spacket_b) != GENERIC_32_LENGTH) {
+        fprintf(stderr, "SP_GENERIC_32 size b wrong at %d bytes\n",
+                sizeof(struct generic_32_spacket_b));
+    }
 }
 
 /* Routine called by forceUpdate to clear local packet info, and force
@@ -2652,27 +2660,77 @@
     sendClientPacket((CVOID) &maskPacket);
 }
 
+static struct generic_32_spacket_a *
+sendGeneric32PacketA(struct player *pl, struct generic_32_spacket_a *ga)
+{
+    ga->type = SP_GENERIC_32;
+    ga->version = 'a';
+    /*! we did not use network byte order for these two fields */
+    ga->repair_time = pl->p_repair_time;
+    ga->pl_orbit = pl->p_flags & PFORBIT ? pl->p_planet : -1;
+    return ga;
+}
+
+static struct generic_32_spacket_b *
+sendGeneric32PacketB(struct player *pl, struct generic_32_spacket_b *gb)
+{
+    int v, t;
+
+    gb->type = SP_GENERIC_32;
+    gb->version = 'b';
+    gb->repair_time = ntohs(pl->p_repair_time);
+    gb->pl_orbit = pl->p_flags & PFORBIT ? pl->p_planet : -1;
+
+    gb->gameup = ntohs(status->gameup & 0xffff);
+
+    gb->tournament_teams = ((context->quorum[1] << 4) & 0xf0) |
+                            (context->quorum[0] & 0xf);
+
+    v = (context->frame - context->frame_tourn_start) / fps;
+    s2du(v, &gb->tournament_age, &gb->tournament_age_units);
+
+    v = context->inl_remaining / fps;
+    s2du(v, &gb->tournament_remain, &gb->tournament_remain_units);
+
+    v = teams[pl->p_team].te_turns;
+    if (v < 0) v = 0;
+    if (v > 255) v = 255;
+    gb->starbase_remain = v;
+
+    for (t=0;((t<=MAXTEAM)&&(teams[t].te_surrender==0));t++);
+    if (t > MAXTEAM) {
+        gb->team_remain = 0; /* no one is considering surrender now */
+    } else {
+        v = teams[t].te_surrender * 60 +
+            ((context->frame - teams[t].te_surrender_frame) / fps);
+        gb->team_remain = (v > 255) ? 255 : v;
+    }
+
+    return gb;
+}
+
 void
 sendGeneric32Packet(void)
 {
-    struct generic_32_spacket gp;
+    struct generic_32_spacket g, *gp;
     int len = GENERIC_32_LENGTH;
-    struct player *pl;
 
     if (!F_sp_generic_32) return;
+    memset(&g, 0, len);
 
-    pl = my();
-    memset(&gp, 0, len);
-    gp.type = SP_GENERIC_32;
-    gp.version = GENERIC_32_VERSION;
-    /*! @bug not using network byte order for these two fields.
-        may need to do this in next version of packet */
-    gp.repair_time = pl->p_repair_time;
-    gp.pl_orbit = pl->p_flags & PFORBIT ? pl->p_planet : -1;
-
-    if (memcmp(&clientGeneric32, &gp, len) != 0) {
-        memcpy(&clientGeneric32, &gp, len);
-        sendClientPacket(&gp);
+    gp = NULL;
+    if (A_sp_generic_32 == GENERIC_32_VERSION_A ||
+        A_sp_generic_32 == 0)
+        gp = (struct generic_32_spacket *)
+                sendGeneric32PacketA(my(), (struct generic_32_spacket_a *)&g);
+    if (A_sp_generic_32 == GENERIC_32_VERSION_B)
+        gp = (struct generic_32_spacket *)
+                sendGeneric32PacketB(my(), (struct generic_32_spacket_b *)&g);
+    if (gp == NULL) return;
+
+    if (memcmp(&clientGeneric32, gp, len) != 0) {
+        memcpy(&clientGeneric32, gp, len);
+        sendClientPacket(gp);
     }
 }
 
-- 
James Cameron    mailto:quozl at us.netrek.org     http://quozl.netrek.org/