On Sat, 14 Aug 2010 at 15:54:47 +0100, Simon McVittie wrote:
> When a 0.8.5 client joins a 0.8.1 server, this leads to
> the client disconnecting with
>
> ERROR: CG_RegisterItemVisuals: itemNum 70 out of range [0-60]
>
> probably due to some incompatibilities between 0.8.1 and 0.8.5 game/bg_*.c.
> I'm a bit confused by this, because the failure mode seems as though it ought
> to be the other way round: 0.8.5 has *more* items.
I think it might just be that the 0.8.5 client code, when running against an
0.8.1 server, is interpreting player_state->stats[] members wrong:
STAT_PERSISTANT_POWERUP (sic) doesn't exist in 0.8.1, so STAT_WEAPONS (a
bitfield) will be interpreted as STAT_PERSISTANT_POWERUP by 0.8.5 client code,
causing a disconnect if you pick up a high-numbered weapon and the weapons
bitfield exceeds the length of the bg_itemlist.
This is originally an incompatibility between Quake III Arena (baseq3) and
Team Arena (missionpack/#define MISSIONPACK): baseoa 0.8.1 is a mixture
of baseq3 and a small amount of missionpack, whereas 0.8.5 has more of the
missionpack changes.
The other incompatible change I've found is that items (the persistent
powerups, i.e. "runes", plus Kamikaze and Invincibility) are inserted into
the *middle* of the bg_itemlist. Indexes into bg_itemlist are part of the
network protocol, so that's A Bad Thing™.
> Upstream, the client will run bytecode from the newest available PK3 file;
> when running on a "pure server" (sv_pure 1), the client isn't allowed to
> load the PK3 for the 0.8.5 patch, and will fall back to the original 0.8.1
> version. This wouldn't work on a server with sv_pure 0, where clients would
> see exactly the same failure mode we do.
As I predicted, impure servers suffer from the same failure mode with bytecode:
http://openarena.ws/board/index.php?topic=3717.0
> We could solve this, but the solution is pretty horrible:
>
> * in the openarena binary package, ship native code for both the 0.8.5
> and 0.8.1 versions
We can actually just have a #ifdef that reverts some of the 0.8.1 changes.
I attach patches.
592965-data.diff is for openarena-data, and is not in the svn repository.
The other two patches are for openarena, and are they're also in the git
repository as the 592965-081-compat branch.
> * when redirecting from a fake QVM file to native code, embed a suffix in the
> fake QVM file ("0.8.1" and "0.8.5", say), and prefer to load
> cgame0.8.1x86_64.so, falling back to cgamex86_64.so (which would be a
> symlink to the latest version at all times)
I actually embedded a directory name instead; the build process of
openarena-data made it easiest to use the PK3 filename, so I did.
With these patches, when running on an 0.8.1 pure server, we'll load
pak0/cgamex86_64.so (or appropriate for the architecture), which is now built.
When running on an 0.8.5 pure server or on any impure server, we'll try
pak6-patch085/cgamex86_64.so (which isn't actually built), then
fall back to ./cgamex86_64.so, which I intend to keep as the latest version.
If 0.8.6 is incompatible with 0.8.5 in a similar way, we'll have to start
producing pak6-patch085/cgamex86_64.so too.
Regards,
Simon
Index: qvmbrute.c
===================================================================
--- qvmbrute.c (revision 11184)
+++ qvmbrute.c (working copy)
@@ -19,21 +19,25 @@
main (int argc, char **argv)
{
u_int32_t i;
- unsigned char qvm[] = { 'N', 'T', 'V', 'E', 0, 0, 0, 0,
- 'c', 'c', 'c', 'c', 'x', 'x', 'x', 'x' };
+ unsigned char qvm[1024];
u_int32_t target = strtoul (argv[1], NULL, 0);
+ char *prefix = argv[2];
+ size_t fixed_len = strlen (prefix) + strlen ("NTVE0000");
u_int32_t crc;
- /* calculate CRC of first 8 bytes */
- crc = crc32 (crc32 (0, NULL, 0), qvm, 8);
+ snprintf ((char *) qvm, sizeof (qvm), "NTVE%s%c%c%c%cccccxxxx",
+ prefix, 0, 0, 0, 0);
+
+ /* calculate CRC of first fixed_len bytes */
+ crc = crc32 (crc32 (0, NULL, 0), qvm, fixed_len);
/* put the complement of it, in little-endian, in the next 4 */
- qvm[8] = ~(crc & 0xFF);
- qvm[9] = ~((crc >> 8) & 0xFF);
- qvm[10] = ~((crc >> 16) & 0xFF);
- qvm[11] = ~((crc >> 24) & 0xFF);
+ qvm[fixed_len + 0] = ~(crc & 0xFF);
+ qvm[fixed_len + 1] = ~((crc >> 8) & 0xFF);
+ qvm[fixed_len + 2] = ~((crc >> 16) & 0xFF);
+ qvm[fixed_len + 3] = ~((crc >> 24) & 0xFF);
/* by mathematical properties of CRC32, the CRC of those 12 bytes is
* 0xFFFFFFFF */
- crc = crc32 (crc32 (0, NULL, 0), qvm, 12);
+ crc = crc32 (crc32 (0, NULL, 0), qvm, fixed_len + 4);
assert (crc == 0xFFFFFFFF);
/* it's possible to do the last bit by mathematics, but brute force
@@ -59,23 +63,24 @@
}
/* check our working */
- memcpy (qvm + 12, &i, 4);
- crc = crc32 (crc32 (0, NULL, 0), qvm, 16);
+ memcpy (qvm + fixed_len + 4, &i, 4);
+ crc = crc32 (crc32 (0, NULL, 0), qvm, fixed_len + 8);
assert (crc == target);
- printf ("crc32(\"NTVE\" 00000000 %.8x %.8x) == 0x%.8x\n",
- ntohl(*((u_int32_t *) (qvm + 8))),
- ntohl(*((u_int32_t *) (qvm + 12))),
+ printf ("crc32(\"NTVE%s\" 00000000 %.8x %.8x) == 0x%.8x\n",
+ prefix,
+ ntohl(*((u_int32_t *) (qvm + fixed_len))),
+ ntohl(*((u_int32_t *) (qvm + fixed_len + 4))),
crc);
- if (argc > 2) {
+ if (argc > 3) {
FILE *f;
- printf ("writing to file %s\n", argv[2]);
- f = fopen (argv[2], "w");
+ printf ("writing to file %s\n", argv[3]);
+ f = fopen (argv[3], "w");
if (f == NULL ||
- fwrite (qvm, 16, 1, f) < 1 ||
+ fwrite (qvm, fixed_len + 8, 1, f) < 1 ||
fclose (f) < 0) {
perror ("writing fake QVM");
return 1;
Index: changelog
===================================================================
--- changelog (revision 11185)
+++ changelog (working copy)
@@ -1,3 +1,10 @@
+openarena-data (0.8.5-3) UNRELEASED; urgency=low
+
+ * Encode a search prefix into the fake QVM files so we can load different
+ native code; OA 0.8.1 and 0.8.5 turn out not to be network-compatible :-(
+
+ -- Simon McVittie <[email protected]> Fri, 20 Aug 2010 23:57:11 +0100
+
openarena-data (0.8.5-2) unstable; urgency=low
* Include directory entries explicitly in the repacked PK3 files, so the
Index: rules
===================================================================
--- rules (revision 11184)
+++ rules (working copy)
@@ -12,12 +12,13 @@
set -e && \
cd debian && for c in baseoa/*.contents missionpack/*.contents; do \
pk3=$${c%.contents}; \
+ prefix=$$(basename $${pk3%.pk3}); \
dir=$${pk3%.pk3}; \
echo "adding any missing fake QVM files for $$c"; \
sed -n -e 's/#.*//; /\.qvm$$/p;' < $$c | \
while read crc filename; do \
- test -f $$crc.qvm || $(MAKE) -C .. -f debian/rules debian/$$crc.qvm; \
- cp $$crc.qvm ../$$dir/$$filename; \
+ test -f $$prefix-$$crc.qvm || $(MAKE) -C .. -f debian/rules QVMPREFIX=$$prefix QVMCRC=$$crc debian/$$prefix-$$crc.qvm; \
+ cp $$prefix-$$crc.qvm ../$$dir/$$filename; \
done; \
echo "compressing $$dir to $$pk3"; \
rm -f ../$$pk3; \
@@ -29,8 +30,8 @@
DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-debian/%.qvm: debian/qvmbrute
- ./debian/qvmbrute 0x$* $@
+debian/$(QVMPREFIX)-$(QVMCRC).qvm: debian/qvmbrute
+ ./debian/qvmbrute 0x$(QVMCRC) $(QVMPREFIX) $@
debian/qvmbrute: debian/qvmbrute.c
$(DEB_BUILD_GNU_TYPE)-gcc -Wall -lz -o debian/qvmbrute debian/qvmbrute.c
>From eabfbbba1b133a9dc8b1bd7fe8fab401e2ad1c89 Mon Sep 17 00:00:00 2001
From: Simon McVittie <[email protected]>
Date: Sat, 21 Aug 2010 16:09:09 +0100
Subject: [PATCH 1/2] Add patches to work around OA 0.8.1 to 0.8.5 network-compat breakage
---
...NA_081_COMPATIBLE-define-for-network-comp.patch | 379 ++++++++++++++++++++
...arts-with-NTVE-followed-by-a-nonempty-str.patch | 53 +++
debian/patches/series | 2 +
3 files changed, 434 insertions(+), 0 deletions(-)
create mode 100644 debian/patches/0040-Add-OPENARENA_081_COMPATIBLE-define-for-network-comp.patch
create mode 100644 debian/patches/0041-If-a-QVM-starts-with-NTVE-followed-by-a-nonempty-str.patch
diff --git a/debian/patches/0040-Add-OPENARENA_081_COMPATIBLE-define-for-network-comp.patch b/debian/patches/0040-Add-OPENARENA_081_COMPATIBLE-define-for-network-comp.patch
new file mode 100644
index 0000000..bb6bd15
--- /dev/null
+++ b/debian/patches/0040-Add-OPENARENA_081_COMPATIBLE-define-for-network-comp.patch
@@ -0,0 +1,379 @@
+From aee4d733e1e9324e8392ecc0e024b50e3176b938 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <[email protected]>
+Date: Fri, 20 Aug 2010 23:27:28 +0100
+Subject: [PATCH] Add OPENARENA_081_COMPATIBLE define for network compat with 0.8.1
+
+0.8.1 was approximately compatible with Quake III Arena, whereas 0.8.5
+is more like Team Arena. Unfortunately, these are not the same.
+
+Bug: http://openarena.ws/board/index.php?topic=3717.0
+---
+ game/Makefile | 4 ++++
+ game/code/cgame/cg_draw.c | 6 +++---
+ game/code/game/ai_dmq3.c | 2 ++
+ game/code/game/bg_misc.c | 15 ++++++++++++---
+ game/code/game/bg_pmove.c | 2 ++
+ game/code/game/bg_public.h | 11 ++++++++++-
+ game/code/game/g_active.c | 11 ++++++++++-
+ game/code/game/g_combat.c | 4 ++++
+ game/code/game/g_items.c | 9 ++++++++-
+ 9 files changed, 55 insertions(+), 9 deletions(-)
+
+diff --git a/game/Makefile b/game/Makefile
+index 1520a96..f3dd3f1 100644
+--- a/game/Makefile
++++ b/game/Makefile
+@@ -852,6 +852,10 @@ endif
+
+ BASE_CFLAGS += -DPRODUCT_VERSION=\\\"$(VERSION)\\\"
+
++ifeq ($(OPENARENA_081_COMPATIBLE),1)
++ BASE_CFLAGS += -DOPENARENA_081_COMPATIBLE
++endif
++
+ ifeq ($(V),1)
+ echo_cmd=@:
+ Q=
+diff --git a/game/code/cgame/cg_draw.c b/game/code/cgame/cg_draw.c
+index e4d2b2a..32f74dd 100644
+--- a/game/code/cgame/cg_draw.c
++++ b/game/code/cgame/cg_draw.c
+@@ -1890,7 +1890,6 @@ static void CG_DrawHoldableItem( void ) {
+ }
+ #endif // MISSIONPACK
+
+-#ifndef MISSIONPACK
+ /*
+ ===================
+ CG_DrawPersistantPowerup
+@@ -1898,6 +1897,7 @@ CG_DrawPersistantPowerup
+ */
+ #if 1 // sos001208 - DEAD // sago - ALIVE
+ static void CG_DrawPersistantPowerup( void ) {
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ int value;
+
+ value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP];
+@@ -1905,9 +1905,9 @@ static void CG_DrawPersistantPowerup( void ) {
+ CG_RegisterItemVisuals( value );
+ CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2 - ICON_SIZE, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
+ }
++#endif
+ }
+ #endif
+-#endif // MISSIONPACK
+
+
+ /*
+@@ -3243,8 +3243,8 @@ static void CG_Draw2D(stereoFrame_t stereoFrame)
+
+ #ifndef MISSIONPACK
+ CG_DrawHoldableItem();
+- CG_DrawPersistantPowerup();
+ #endif
++ CG_DrawPersistantPowerup();
+
+ CG_DrawReward();
+ }
+diff --git a/game/code/game/ai_dmq3.c b/game/code/game/ai_dmq3.c
+index 9037445..9731fdd 100644
+--- a/game/code/game/ai_dmq3.c
++++ b/game/code/game/ai_dmq3.c
+@@ -1881,10 +1881,12 @@ void BotUpdateInventory(bot_state_t *bs) {
+ bs->inventory[INVENTORY_INVISIBILITY] = bs->cur_ps.powerups[PW_INVIS] != 0;
+ bs->inventory[INVENTORY_REGEN] = bs->cur_ps.powerups[PW_REGEN] != 0;
+ bs->inventory[INVENTORY_FLIGHT] = bs->cur_ps.powerups[PW_FLIGHT] != 0;
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ bs->inventory[INVENTORY_SCOUT] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_SCOUT;
+ bs->inventory[INVENTORY_GUARD] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_GUARD;
+ bs->inventory[INVENTORY_DOUBLER] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_DOUBLER;
+ bs->inventory[INVENTORY_AMMOREGEN] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_AMMOREGEN;
++#endif
+ bs->inventory[INVENTORY_REDFLAG] = bs->cur_ps.powerups[PW_REDFLAG] != 0;
+ bs->inventory[INVENTORY_BLUEFLAG] = bs->cur_ps.powerups[PW_BLUEFLAG] != 0;
+ bs->inventory[INVENTORY_NEUTRALFLAG] = bs->cur_ps.powerups[PW_NEUTRALFLAG] != 0;
+diff --git a/game/code/game/bg_misc.c b/game/code/game/bg_misc.c
+index 767cc7e..c701434 100644
+--- a/game/code/game/bg_misc.c
++++ b/game/code/game/bg_misc.c
+@@ -655,6 +655,7 @@ Only in CTF games
+ /* sounds */ ""
+ },
+
++#ifdef HAVE_MISSIONPACK_ITEMLIST
+ /*QUAKED holdable_kamikaze (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
+ */
+ {
+@@ -702,6 +703,7 @@ Only in CTF games
+ /* precache */ "",
+ /* sounds */ ""
+ },
++#endif
+
+ /*QUAKED ammo_nails (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
+ */
+@@ -751,6 +753,7 @@ Only in CTF games
+ /* sounds */ ""
+ },
+
++#ifdef HAVE_MISSIONPACK_ITEMLIST
+ //
+ // PERSISTANT POWERUP ITEMS
+ //
+@@ -817,6 +820,7 @@ Only in CTF games
+ /* precache */ "",
+ /* sounds */ ""
+ },
++#endif
+
+
+ /*QUAKED team_CTF_neutralflag (0 0 1) (-16 -16 -16) (16 16 16)
+@@ -1214,6 +1218,9 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play
+ return qtrue;
+
+ case IT_ARMOR:
++ upperBound = ps->stats[STAT_MAX_HEALTH] * 2;
++
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
+ return qfalse;
+ }
+@@ -1222,9 +1229,7 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play
+ if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ upperBound = ps->stats[STAT_MAX_HEALTH];
+ }
+- else {
+- upperBound = ps->stats[STAT_MAX_HEALTH] * 2;
+- }
++#endif
+
+ if ( ps->stats[STAT_ARMOR] >= upperBound ) {
+ return qfalse;
+@@ -1232,12 +1237,14 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play
+ return qtrue;
+
+ case IT_HEALTH:
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ // small and mega healths will go over the max, otherwise
+ // don't pick up if already at max
+ if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ upperBound = ps->stats[STAT_MAX_HEALTH];
+ }
+ else
++#endif
+ if ( item->quantity == 5 || item->quantity == 100 ) {
+ if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) {
+ return qfalse;
+@@ -1253,6 +1260,7 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play
+ case IT_POWERUP:
+ return qtrue; // powerups are always picked up
+
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ case IT_PERSISTANT_POWERUP:
+
+ //In Double D we don't want persistant Powerups (or maybe, can be discussed)
+@@ -1273,6 +1281,7 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play
+ }
+
+ return qtrue;
++#endif
+
+ case IT_TEAM: // team items, such as flags
+ if( gametype == GT_1FCTF ) {
+diff --git a/game/code/game/bg_pmove.c b/game/code/game/bg_pmove.c
+index 8bb6a2c..2fd3ba5 100644
+--- a/game/code/game/bg_pmove.c
++++ b/game/code/game/bg_pmove.c
+@@ -1696,6 +1696,7 @@ static void PM_Weapon( void ) {
+ break;
+ }
+
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
+ addTime /= 1.5;
+ }
+@@ -1704,6 +1705,7 @@ static void PM_Weapon( void ) {
+ addTime /= 1.3;
+ }
+ else
++#endif
+ if ( pm->ps->powerups[PW_HASTE] ) {
+ addTime /= 1.3;
+ }
+diff --git a/game/code/game/bg_public.h b/game/code/game/bg_public.h
+index b0efb3e..3fcd777 100644
+--- a/game/code/game/bg_public.h
++++ b/game/code/game/bg_public.h
+@@ -25,6 +25,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ // because games can change separately from the main system version, we need a
+ // second version that must match between game and cgame
+
++#if defined(MISSIONPACK) || !defined(OPENARENA_081_COMPATIBLE)
++// MISSIONPACK and 0.8.5 break ABI and network compatibility, because
++// STAT_PERSISTANT_POWERUP wasn't added at the end of its enum. Sad times.
++# define HAVE_STAT_PERSISTANT_POWERUP
++// Adding objects to the middle of the bg_itemlist also breaks network compat.
++# define HAVE_MISSIONPACK_ITEMLIST
++#endif
++
+ #if defined(BG_PUBLIC_H)
+ #else
+ #define BG_PUBLIC_H 1
+@@ -223,13 +231,14 @@ void Pmove (pmove_t *pmove);
+
+ //===================================================================================
+
+-
+ // player_state->stats[] indexes
+ // NOTE: may not have more than 16
+ typedef enum {
+ STAT_HEALTH,
+ STAT_HOLDABLE_ITEM,
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ STAT_PERSISTANT_POWERUP,
++#endif
+ STAT_WEAPONS, // 16 bit fields
+ STAT_ARMOR,
+ STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?)
+diff --git a/game/code/game/g_active.c b/game/code/game/g_active.c
+index c905261..b66f982 100644
+--- a/game/code/game/g_active.c
++++ b/game/code/game/g_active.c
+@@ -437,10 +437,13 @@ void ClientTimerActions( gentity_t *ent, int msec ) {
+ continue;
+
+ // regenerate
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ maxHealth = client->ps.stats[STAT_MAX_HEALTH] / 2;
+ }
+- else if ( client->ps.powerups[PW_REGEN] ) {
++ else
++#endif
++ if ( client->ps.powerups[PW_REGEN] ) {
+ maxHealth = client->ps.stats[STAT_MAX_HEALTH];
+ }
+ else {
+@@ -484,6 +487,7 @@ void ClientTimerActions( gentity_t *ent, int msec ) {
+ client->ps.stats[STAT_ARMOR]--;
+ }
+ }
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
+ int w, max, inc, t, i;
+ int weapList[]={WP_MACHINEGUN,WP_SHOTGUN,WP_GRENADE_LAUNCHER,WP_ROCKET_LAUNCHER,WP_LIGHTNING,WP_RAILGUN,WP_PLASMAGUN,WP_BFG,WP_NAILGUN,WP_PROX_LAUNCHER,WP_CHAINGUN};
+@@ -520,6 +524,7 @@ void ClientTimerActions( gentity_t *ent, int msec ) {
+ }
+ }
+ }
++#endif
+ }
+
+ /*
+@@ -968,10 +973,12 @@ void ClientThink_real( gentity_t *ent ) {
+ // set speed
+ client->ps.speed = g_speed.value;
+
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
+ client->ps.speed *= 1.5;
+ }
+ else
++#endif
+ if ( client->ps.powerups[PW_HASTE] ) {
+ client->ps.speed *= 1.3;
+ }
+@@ -1278,6 +1285,7 @@ void ClientEndFrame( gentity_t *ent ) {
+ }
+
+ // set powerup for player animation
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ ent->client->ps.powerups[PW_GUARD] = level.time;
+ }
+@@ -1290,6 +1298,7 @@ void ClientEndFrame( gentity_t *ent ) {
+ if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
+ ent->client->ps.powerups[PW_AMMOREGEN] = level.time;
+ }
++#endif
+ if ( ent->client->invulnerabilityTime > level.time ) {
+ ent->client->ps.powerups[PW_INVULNERABILITY] = level.time;
+ }
+diff --git a/game/code/game/g_combat.c b/game/code/game/g_combat.c
+index 8c81284..22ce65a 100644
+--- a/game/code/game/g_combat.c
++++ b/game/code/game/g_combat.c
+@@ -227,7 +227,9 @@ void TossClientPersistantPowerups( gentity_t *ent ) {
+ powerup->r.contents = CONTENTS_TRIGGER;
+ trap_LinkEntity( powerup );
+
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ ent->client->ps.stats[STAT_PERSISTANT_POWERUP] = 0;
++#endif
+ ent->client->persistantPowerup = NULL;
+ }
+
+@@ -1071,9 +1073,11 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
+ // unless they are rocket jumping
+ if ( attacker->client && attacker != targ ) {
+ max = attacker->client->ps.stats[STAT_MAX_HEALTH];
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ max /= 2;
+ }
++#endif
+ damage = damage * max / 100;
+ }
+
+diff --git a/game/code/game/g_items.c b/game/code/game/g_items.c
+index cc003bc..1fd4619 100644
+--- a/game/code/game/g_items.c
++++ b/game/code/game/g_items.c
+@@ -118,6 +118,7 @@ int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
+ //======================================================================
+
+ int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) {
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ int clientNum;
+ char userinfo[MAX_INFO_STRING];
+ float handicap;
+@@ -185,6 +186,7 @@ int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) {
+ break;
+ }
+
++#endif
+ return -1;
+ }
+
+@@ -277,11 +279,13 @@ int Pickup_Health (gentity_t *ent, gentity_t *other) {
+ int max;
+ int quantity;
+
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ // small and mega healths will go over the max
+ if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ max = other->client->ps.stats[STAT_MAX_HEALTH];
+ }
+ else
++#endif
+ if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) {
+ max = other->client->ps.stats[STAT_MAX_HEALTH];
+ } else {
+@@ -315,10 +319,13 @@ int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
+
+ other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
+
++#ifdef HAVE_STAT_PERSISTANT_POWERUP
+ if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
+ upperBound = other->client->ps.stats[STAT_MAX_HEALTH];
+ }
+- else {
++ else
++#endif
++ {
+ upperBound = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
+ }
+
+--
+1.7.1
+
diff --git a/debian/patches/0041-If-a-QVM-starts-with-NTVE-followed-by-a-nonempty-str.patch b/debian/patches/0041-If-a-QVM-starts-with-NTVE-followed-by-a-nonempty-str.patch
new file mode 100644
index 0000000..66ce0bb
--- /dev/null
+++ b/debian/patches/0041-If-a-QVM-starts-with-NTVE-followed-by-a-nonempty-str.patch
@@ -0,0 +1,53 @@
+From 88632d58a1bb664b54621aac6bdf14b80315cfd9 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <[email protected]>
+Date: Sat, 21 Aug 2010 16:03:22 +0100
+Subject: [PATCH] If a QVM starts with NTVE followed by a nonempty string, look in that subdir
+
+This lets each version have a different substitute shared object, if
+necessary for network compatibility (OpenArena broke network
+compatibility between 0.8.1 and 0.8.5 by removing some #ifdef MISSIONPACK).
+---
+ engine/code/qcommon/vm.c | 20 ++++++++++++++++++++
+ 1 files changed, 20 insertions(+), 0 deletions(-)
+
+diff --git a/engine/code/qcommon/vm.c b/engine/code/qcommon/vm.c
+index 83832d1..2c1a7f5 100644
+--- a/engine/code/qcommon/vm.c
++++ b/engine/code/qcommon/vm.c
+@@ -372,6 +372,7 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) {
+ union {
+ vmHeader_t *h;
+ void *v;
++ char *s;
+ } header;
+
+ // load the image
+@@ -386,6 +387,25 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) {
+
+ if (LittleLong( header.h->vmMagic ) == VM_MAGIC_USE_NATIVE) {
+ Com_Printf( "...which has vmMagic VM_MAGIC_USE_NATIVE.\n" );
++
++ if (header.s[sizeof(int)] != '\0') {
++ Com_sprintf( filename, sizeof(filename), "%.*s/%s", length - sizeof(int), header.s + sizeof(int), vm->name );
++ filename[sizeof(filename)-1] = '\0';
++
++ if (FS_CheckDirTraversal( filename )) {
++ Com_Printf( "Directory traversal detected! %s", filename );
++ VM_Free( vm );
++ return NULL;
++ }
++
++ vm->dllHandle = Sys_LoadDll( filename, vm->fqpath , &vm->entryPoint, VM_DllSyscall );
++
++ if (vm->dllHandle != NULL) {
++ Com_Printf( "Loaded prefixed DLL file %s.\n", filename );
++ return header.h;
++ }
++ }
++
+ Com_Printf( "Loading DLL file %s instead.\n", vm->name );
+ vm->dllHandle = Sys_LoadDll( vm->name, vm->fqpath , &vm->entryPoint, VM_DllSyscall );
+ if ( !vm->dllHandle ) {
+--
+1.7.1
+
diff --git a/debian/patches/series b/debian/patches/series
index 4f37be5..485ad3a 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -31,3 +31,5 @@
0037-Put-a-error-marker-in-engine-s-ui_shared.h-to-make-s.patch
0038-Fix-buffer-overflow-report-and-patch-by-Eugene-C.-46.patch
0039-Used-self-enemy-before-it-was-initialized.-Might-hav.patch
+0040-Add-OPENARENA_081_COMPATIBLE-define-for-network-comp.patch
+0041-If-a-QVM-starts-with-NTVE-followed-by-a-nonempty-str.patch
--
1.7.1
>From ee37d628b1d3c225a95a3306690a93139f52acc8 Mon Sep 17 00:00:00 2001
From: Simon McVittie <[email protected]>
Date: Sat, 21 Aug 2010 16:03:55 +0100
Subject: [PATCH 2/2] Install 0.8.1-compatible binaries to a subdirectory
---
debian/changelog | 10 ++++++++++
debian/openarena-server.install | 1 +
debian/rules | 11 ++++++++++-
3 files changed, 21 insertions(+), 1 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index b2204d6..0629bba 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+openarena (0.8.5-4) UNRELEASED; urgency=low
+
+ * Build a second copy of the game logic and install it in a subdirectory.
+ It turns out that OpenArena 0.8.1 and 0.8.5 aren't network-compatible;
+ on impure servers we can't be any better than the QVMs, but on pure
+ servers we can at least load the corresponding game logic.
+ (Closes: #592965 if openarena-data is also updated)
+
+ -- Simon McVittie <[email protected]> Sat, 21 Aug 2010 00:02:54 +0100
+
openarena (0.8.5-3) unstable; urgency=medium
* Add patch from oax r203 to fix server crashes if a player joins, then
diff --git a/debian/openarena-server.install b/debian/openarena-server.install
index 714e52f..e475b7d 100644
--- a/debian/openarena-server.install
+++ b/debian/openarena-server.install
@@ -1,2 +1,3 @@
debian/tmp/usr/games/openarena-server
debian/tmp/usr/lib/games/openarena/*/*.so
+debian/tmp/usr/lib/games/openarena/*/*/*.so
diff --git a/debian/rules b/debian/rules
index e8216f8..67adff2 100755
--- a/debian/rules
+++ b/debian/rules
@@ -44,11 +44,17 @@ GAME_OPTIONS = \
BUILD_GAME_SO=1 \
CROSS_COMPILING=1
+GAME_081_OPTIONS = \
+ $(GAME_OPTIONS) \
+ BUILD_DIR=build-0.8.1-compat \
+ OPENARENA_081_COMPATIBLE=1
+
build: build-stamp
build-stamp:
dh_testdir
$(MAKE) -C engine $(ENGINE_OPTIONS)
$(MAKE) -C game $(GAME_OPTIONS)
+ $(MAKE) -C game $(GAME_081_OPTIONS)
touch $@
clean:
@@ -57,6 +63,7 @@ clean:
rm -f build-stamp
rm -rf engine/build
rm -rf game/build
+ rm -rf game/build-0.8.1-compat
rm -f debian/openarena32.xpm
dh_clean
@@ -65,7 +72,7 @@ install: build
dh_testroot
dh_clean -k
install -d debian/tmp/usr/games
- install -d debian/tmp/usr/lib/games/openarena/baseoa/
+ install -d debian/tmp/usr/lib/games/openarena/baseoa/pak0/
install -d debian/tmp/usr/lib/games/openarena/missionpack/
install engine/build/release-$(Q3PLATFORM)-$(Q3ARCH)/ioquake3.$(Q3ARCH) \
debian/tmp/usr/games/openarena
@@ -73,6 +80,8 @@ install: build
debian/tmp/usr/games/openarena-server
install -m644 game/build/release-$(Q3PLATFORM)-$(Q3ARCH)/baseq3/*.so \
debian/tmp/usr/lib/games/openarena/baseoa/
+ install -m644 game/build-0.8.1-compat/release-$(Q3PLATFORM)-$(Q3ARCH)/baseq3/*.so \
+ debian/tmp/usr/lib/games/openarena/baseoa/pak0/
install -m644 game/build/release-$(Q3PLATFORM)-$(Q3ARCH)/missionpack/*.so\
debian/tmp/usr/lib/games/openarena/missionpack/
convert -scale 32x32 debian/openarena128.png debian/openarena32.xpm
--
1.7.1