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/